-1

Now I'm using UIImage sync extension.

struct PostView: View {
    let url: String
    
    var body: some View {        
        PrivateImageView(image: UIImage(url:url))
     }
}
extension UIImage {
    public convenience init(url: String) {
        let url = URL(string: url)
        do {
            let data = try Data(contentsOf: url!)
            self.init(data: data)!
            return
        } catch let err {
            print("Error : \(err.localizedDescription)")
        }
        self.init()
    }
}

When I post a image, I got the error Synchronous URL loading of http://local.host/images/123.jpeg should not occur on this application's main thread as it may lead to UI unresponsiveness. Please switch to an asynchronous networking API such as URLSession. at try Data(contentsOf: url!).

In PostView I use PrivateImageView.

To use the view I have to designate the argument like this PrivateImageView(image: UIImage(xxxxxxxxx)).

I mean I have to use UIImage() not AsyncImage.

I don't know how to change convenience init to adjust to PrivateImageView.

Please tell me how to use an async function in this context.

Takeshi
  • 59
  • 5
  • Use `try await URLSession.shared.data(from:)`, there are plenty of answers here and articles online for this. See [this answer](https://stackoverflow.com/a/54597264/9223839) for instance. But you shouldn't do it in an init though imo but in a function – Joakim Danielson Jun 01 '23 at 12:14
  • I already did like this ``` extension UIImage { public convenience init(url: String) { if let url = URL(string: url) { URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in if let data = data, let image = UIImage(data: data) { DispatchQueue.main.async { self?.init(data: data) } } }.resume() } self.init() } } ``` But it did't work. So I asked this question. – Takeshi Jun 01 '23 at 12:42
  • And I am telling you to use the new async/await function in URLSession – Joakim Danielson Jun 01 '23 at 13:55
  • @Takeshi please [edit] your question with additional code and info. Don’t put it in comments since it is difficult to read. – koen Jun 01 '23 at 15:30
  • With SwiftUI just use AsyncImage instead of UIImage extension: https://developer.apple.com/documentation/swiftui/asyncimage – timbre timbre Jun 01 '23 at 16:40

1 Answers1

1

There is no way to get a data from the inter/intranet synchronously you have to use an async method and account for the time it takes to download.

extension String {
    public func getUIImage() async throws -> UIImage {
        guard let url = URL(string: self) else {
            throw URLError(.badURL)
        }
        
        let (data, response) = try await URLSession.shared.data(from: url)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw URLError(.badServerResponse)
        }
        
        guard httpResponse.statusCode == 200 else {
            throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
        }
        
        guard let image = UIImage(data: data) else {
            throw URLError(.fileDoesNotExist)
        }
        return image
    }
}

extension UIImage {
    static public func fromURL(url: String) async throws -> UIImage {
        let image = try await url.getUIImage()
        return image
    }
}

You can rewrite PostView to something like.

struct PostView: View {
    let url: String
    @State private var uiImage: UIImage?
    var body: some View {
        Group{
            if let uiImage {
                PrivateImageView(image: uiImage)
            } else {
                ProgressView() //Show this while downloading
                    .task {
                        do {
                            self.uiImage = try await url.getUIImage()
                            // or
                            // self.uiImage = try await UIImage.fromURL(url: url)
                        } catch {
                            print(error)
                        }
                    }
            }
        }
    }
}
lorem ipsum
  • 21,175
  • 5
  • 24
  • 48
  • Thank you. In fact `PostView` has `let user: User` property. I want to use `use.profileImageUrl` as `url` in this context. And `use.profileImageUrl` is optional string. What do I add to your good code? – Takeshi Jun 02 '23 at 11:45
  • @Takeshi you have to unwrap the string somehow, a switch and put a placeholder in “none” and the current code in “some” – lorem ipsum Jun 02 '23 at 12:22