My images are not loading in order in my table view - swift

I have an iOS app that has an events section where users can post events and attach a flyer image. everything works except two mages show up on top no matter what I do. And I have programmed the app to show the most current post at the top of the table view. Any help would be greatly appreciated.
eventsDatabaseHandle = eventsRef?.child("Church Events").observe(.childAdded, with: { (snaphot) in
let eventPost = snaphot.value as! [String: Any]
var event = Event(title: eventPost["eventtitle"] as! String,
timestamp: eventPost["eventdate"] as! String,
location: eventPost["eventlocation"] as! String,
image: nil)
let task = URLSession.shared.dataTask(with: URL(string: eventPost["ImageUrl"] as! String)!) { data, _, error in
if let image: UIImage = UIImage(data: data!) {
event.image = image
DispatchQueue.main.async {
self.events.append(event)
self.tableView.reloadData()
}
}
}
task.resume()
})

Have you tried this method?
event.image = UIImage(data: image)
If you tried this method, there may be an error :
eventPost["ImageUrl"]
Check with the name in the database.

Related

How to download and show a list of images in a collection view so to avoid flickering and images in wrong spots?

I'm struggling to solve the very common problem of loading images inside a collection view given a list of urls. I have implemented a UIImageView extension. In there I defined a cache variable:
static var imageCache = NSCache<NSString, UIImage>()
In addition I have created a loadImage method that takes as input the image cache key, the url itself, a placeholder image and an error image and works as follows:
public func loadImage(cacheKey: String?, urlString: String?, placeholderImage: UIImage?, errorImage: UIImage?) {
image = nil
if let cacheKey = cacheKey, let cachedImage = UIImageView.imageCache.object(forKey: cacheKey as NSString) {
image = cachedImage
return
}
if let placeholder = placeholderImage {
image = placeholder
}
if let urlString = urlString, let url = URL(string: urlString) {
self.downloadImage(url: url, errorImage: errorImage)
} else {
image = errorImage
}
}
The download image function proceeds to create a data task, download the image and assign to the image view:
private func downloadImage(url: URL, errorImage: UIImage?) {
let dataTask = URLSession.shared.dataTask(with: url) { [weak self] (data, response, error ) in
if error != nil {
DispatchQueue.main.async {
self?.image = errorImage
}
return
}
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
if !(200...299).contains(statusCode) {
DispatchQueue.main.async {
self?.image = errorImage
}
return
}
}
if let data = data, let image = UIImage(data: data) {
UIImageView.imageCache.setObject(image, forKey: url.absoluteString as NSString)
DispatchQueue.main.async {
self?.image = image
}
}
}
dataTask.resume()
}
I call the loadImage method inside cellForItemAt, so that I don't have to download all the data at once, but only the images that are effectively displayed on screen. The way I call the function is the following:
cell.albumImage.loadImage(
cacheKey: bestQualityImage,
urlString: bestQualityImage,
placeholderImage: UIImage(named: "Undefined"),
errorImage: UIImage(named: "Undefined")
)
The main problem I face with the current implementation is that sometimes the images are not displayed in the correct spot. In other words, if I have three elements on screen I would sometimes see all three elements with the same image instead of their respective image.
I believe that the problem is that by the time the download is complete for a specific cell, the cellForItemAt for that cell has already ended and the cell gets the wrong image instead.
Is there a way I can modify my function to fix this bug or should I completely change my approach?
EDIT.
At the beginning I thought the problem was that I was using an extension and I tried to use a function with a closure returning the downloaded image but that didn't solve my problem.

swift firebase get Image from Storage and show in JSQMessage Controller

I want to get a saved Image in Firebase Storage.
I have the url saved in an string value in the Firebase database:
mediaUrl = url!.absoluteString
Now I want to get this image.
I observe the messages.
func observeMessages() {
let query = Constants.refs.databaseChats.child(chatId).queryLimited(toLast: 50)
_ = query.observe(.childAdded, with: { [weak self] snapshot in
if let data = snapshot.value as? [String: String],
let id = data["sender_id"],
let name = data["name"],
let timestamp = data["timestamp"],
let media = data["media"],
let text = data["text"]?.encode(),
!text.isEmpty
{
if media == "text" {
if let message = JSQMessage(senderId: id, senderDisplayName: name, date: self!.dateFormatter.date(from: timestamp), text: text) {
self?.messages.append(message)
self?.finishReceivingMessage()
}
}
else if media == "image" {
let storageRef = Storage.storage().reference(withPath: text)
storageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) -> Void in
if data != nil {
let image = UIImage(data: data!)
if let imageMessage = JSQMessage(senderId: id, senderDisplayName: name, date: self!.dateFormatter.date(from: timestamp), media: image as! JSQMessageMediaData) {
self?.messages.append(imageMessage)
print("image message")
self?.finishReceivingMessage()
}
}
}
}
}
})
}
If the media is an image, I want to create an image bubble, and when text a normal text bubble.
But I get no Image in the JSQMEssageViewController.
What went wrong? Can someone help me there?
I get the url in console:
https://firebasestorage.googleapis.com/v0/b/emmessenger-22.appspot.com/o/media%2FJzl0EUmjSvZZcpCd8Mdi8X9q87G2%2F03.06.2019%2021:26:39?alt=media&token=1c45feaf-0ed8-4f88-9c1f-13f4c6a8f3d4
And after that I get the following at console:
2019-06-03 21:28:21.092580+0200 EMMessenger[38502:554129] [] nw_proxy_resolver_create_parsed_array PAC evaluation error: NSURLErrorDomain: -1003
Thanks
Actually you don't need to call getData function for this case. As you're using tableView so I would recommend to use SDWebImage library to load the images on cells asynchronously. Once you have the imageURL just call below method
yourImageView.sd_setImage(with: URL(string: "https://firebasestorage.googleapis.com/v0/b/emmessenger-22.appspot.com/o/media%2FJzl0EUmjSvZZcpCd8Mdi8X9q87G2%2F03.06.2019%2021:26:39?alt=media&token=1c45feaf-0ed8-4f88-9c1f-13f4c6a8f3d4"), placeholderImage: UIImage(named: "placeholder.png"))
Rest the library will do for you.

Swift SDWebImage in a closure block

I am attempting to use SDWebImage in a closure block after fetching userInfo from Firebase. However, doing so will result in the images and cell labels to blink as I scroll the tableView when the cells are attempting to redraw themselves.
let expiredConversationsCell = tableView.dequeueReusableCell(withIdentifier: "expiredConversationsCell", for: indexPath) as! ExpiredConversationsTableViewCell
let conversation = allConversations[0][indexPath.row]
guard let recipientID = conversation.recipientID else {return UITableViewCell()}
FirebaseClient.shared.getUserInfo(recipientID, { (results, error) in
if let error = error {
print(error.localizedDescription)
} else if let results = results {
let username = results["username"] as! String
let profileImageUrl = results["profileImageUrl"] as! String
DispatchQueue.main.async {
expiredConversationsCell.profileImageView.sd_setImage(with: URL(string: profileImageUrl), completed: nil)
expiredConversationsCell.recipientNameLabel.text = username
}
}
})
return expiredConversationsCell
Is there a way to implement SDWebImages under such circumstances? Any advice would be great. Thanks.

Using Cloudkit Assets as a UIimage

I have images saved in CloudKit as an asset. There are other attributes for each record as well. I can gather the record and use the other attributes, but I'm unable to use the asset in my ImageView. I'm new to Swift programming, therefore the error I receive does not make any sense.
let container = CKContainer.default()
let publicDB = container.publicCloudDatabase
let query1 = CKQuery(recordType: "movieArray", predicate: predicate2)
publicDB.perform(query1, inZoneWith: nil) {(results:[CKRecord]?, error:Error?) in
if error != nil {
DispatchQueue.main.async {
print("Cloud Query Error - Fetch Establishments: \(String(describing: error))")
}
return
}
for record in results! {
DispatchQueue.main.async {
let asset = record["myImageKey"] as? CKAsset
let data = NSData(contentsOf: (asset?.fileURL)!)
let image = UIImage(data: data! as Data)
print("Test")
self.detailImage.image = image
}
self.movieCast.text = record.object(forKey: "Actors") as? String
self.detailDescriptionLabel.text = record.object(forKey: "Description") as? String
let youLink = record.object(forKey: "youtubeTag") as! String
self.loadYoutube(videoID: youLink)
}
}
The error I get is on this line:
let data = NSData(contentsOf: (asset?.fileURL)!)
it says:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1020527c4
I attempted to remove it from the main thread, but I receive the same error.
Maybe the problem is that the CKAsset record is nil, but you are forcing to have a fileURL value.
Try to obtain CloudKit image with this snippet
if let asset = record["myImageKey"] as? CKAsset,
let data = try? Data(contentsOf: (asset.fileURL)),
let image = UIImage(data: data)
{
self.detailImage.image = image
}

How to prevent an app crash or freeze due to a slow connection when retrieving a remote photo in Swift?

I want to display avatar image in my table view cell by the url string. and it will crash when the phone is not connect to the internet, so I added Reachability swift to it. but now I face another problem, when the user leaving the wifi zone or the internet connection is not stable, the app will freeze, I'm not able to tap anything until I walk back the strong wifi zone. why?
let imageData:NSData = try! NSData(contentsOf: imageUrl)
this code will crash so I try add do & catch but still not working. is it because the app can't connect to the url string and get the data so that the app will be freeze? how to prevent an app crash or freeze due to a slow connection when retrieving a remote photo?
if Reachability.shared.isConnectedToNetwork(){
if let crew = user!["crew"] as? [String:Any], let crewAva = crew["crew_avatar"] as? String {
let imageUrlString = crewAva
let imageUrl:URL = URL(string: imageUrlString)!
DispatchQueue.main.async(execute: {
do{
let imageData:NSData = try NSData(contentsOf: imageUrl)
let image = UIImage(data: imageData as Data)
self.avaImg.image = image
}
catch{
print("error")
}
})
}
}else{
print("Not reachable")
}
From the NSData documentation:
init?(contentsOf url: URL)
Important
Don't use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated.
Instead, for non-file URLs, consider using the dataTask(with:completionHandler:) method of the NSURLSession class. See URL Session Programming Guide for details.
Solution
func getDataFromUrl(url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
completion(data, response, error)
}.resume()
}
func downloadImage(url: URL) {
getDataFromUrl(url: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
DispatchQueue.main.async() {
self.avaImg.image = UIImage(data: data)
}
}
}
override func viewWillAppear(_ animated: Bool) {
if let crew = user!["crew"] as? [String:Any], let crewAva = crew["crew_avatar"] as? String {
let imageUrlString = crewAva
let url = URL(string: imageUrlString)!
downloadImage(url: url)
}
}
Try only updating the UI on the main thread.
if Reachability.shared.isConnectedToNetwork(){
if let crew = user!["crew"] as? [String:Any], let crewAva = crew["crew_avatar"] as? String {
let imageUrlString = crewAva
let imageUrl:URL = URL(string: imageUrlString)!
let imageData:NSData = try NSData(contentsOf: imageUrl)
let image = UIImage(data: imageData as Data)
DispatchQueue.main.async(execute: {
do{
self.avaImg.image = image
}
catch{
print("error")
}
})
}
}else{
print("Not reachable")
}