I am having a lot of trouble downloading pictures from URL for my tableview cells. I tried both synchronously and asynchronously downloading, but none of them worked.
For synchronous download, Xcode gives me purple warnings and the pictures won't show up in my tableview.
Warning:
"Synchronous URL loading of ... 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."
For asynchronous download, the code after the downloading executes right away, and the download is not able to finish and results in a nil.
What should I do?
My code:
(I am loading tableview in batches of 15 posts, this is the code of loading batches)
func reloadBatch(){
for i in currentRow...currentRow 15{
if i == posts.count{
return
}
let post = posts[i] // documens
if post.posttype == 1{
let uP = UIImage(url: URL(string: post.userphoto!)) ?? UIImage(named: "Jordan")
postCell.append(LoadedCellModel(posttype: 1, sender: post.sender, userphoto: uP, title: post.title, photo: nil, videoURL: nil, content: post.content))
}else if post.posttype == 2{
let uP = UIImage(url: URL(string: post.userphoto!)) ?? UIImage(named: "Jordan")
let pic = UIImage(url: URL(string: post.photo![0])) ?? UIImage(named: "Jordan")
// This is the picture that does not show up, "photo" is an array of pictures' URL(in string)
postCell.append(LoadedCellModel(posttype: 2, sender: post.sender, userphoto: uP, title: post.title, photo: pic, videoURL: nil, content: post.content))
print(pic)
}
}
currentRow = 15
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
extension UIImage {
convenience init?(url: URL?) {
guard let url = url else { return nil }
do {
self.init(data: try Data(contentsOf: url))
} catch {
print("Cannot load image from url: \(url) with error: \(error)")
return nil
}
}
}
CodePudding user response:
Getting data such as an image from the net is asynchronous.
That is, it takes time to do. So you need to "wait" until the data is available
before you can use it. Note the warning message ...Please switch to an asynchronous networking API such as URLSession
There are many ways to do this. Here I present a simple
way, using a function with a completion handler
.
This means, you cannot use this function (getImage(url: ..)
) like you are trying to do in:
let uP = UIImage(url: URL(string: post.userphoto!)) ?? UIImage(named: "Jordan")
You have to use the closure:
getImage(url: url) { img in
// do something with img
}
Here is an example code for downloading one image:
struct ContentView: View {
@State var uimg: UIImage?
let token = "xxxx" // <-- your secret token
var body: some View {
VStack {
if uimg == nil {
Text("downloading")
ProgressView()
} else {
Image(uiImage: uimg!).resizable().frame(width: 333, height: 333)
}
}
.onAppear {
guard let url = URL(string: "https://firebasestorage.googleapis.com/v0/b/pirateforum-f2f04.appspot.com/o/images/1663116366.2403781.png?alt=media&token=\(token)") else { return }
getImage(url: url) { img in
self.uimg = img
}
}
}
func getImage(url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data, let img = UIImage(data: data) {
completion(img)
} else {
completion(nil)
}
}.resume()
}
}
P.S. do not show your secret token in the warning message, remove it.