Search code examples
iosswiftfirebasefirebase-realtime-databasejsqmessagesviewcontroller

Swift - JSQMessagesViewController with Swift


I'm developing a chat app, I'm having problem showing the Avatar to my JSQMessagesViewController

override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! {

    var avatar = UIImage()

    let people = FIRDatabase.database().reference().child("people").child(senderId)
    people.observeEventType(.Value, withBlock: {
        snapshot -> Void in
        let dict = snapshot.value as! Dictionary<String, AnyObject>
        let imageUrl = dict["profileImage"] as! String
        if imageUrl.hasPrefix("gs://") {
            FIRStorage.storage().referenceForURL(imageUrl).dataWithMaxSize(INT64_MAX, completion: { (data, error) in
                if let error = error {
                    print("Error downloading: \(error)")
                    return
                }
                avatar = UIImage.init(data: data!)!

            })
        }
    })

    let AvatarJobs = JSQMessagesAvatarImageFactory.avatarImageWithPlaceholder(avatar, diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault))

    return AvatarJobs
}

The problem here is, when I'm trying to pull the image of the sender from firebase, I'm getting a blank image, but when i try to use this let AvatarJobs = JSQMessagesAvatarImageFactory.avatarImageWithPlaceholder(UIImage(named: "icon.png"), diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault)) it's working fine, What do you think is the problem here? Thanks!


Solution

  • If I may suggest an alternative? Why don't you have a dictionary:

    var avatars = [String: JSQMessagesAvatarImage]()
    
    let storage = FIRStorage.storage()
    

    And use the following function:

    override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource!
    {
        let message = messages[indexPath.row]
        return avatars[message.senderId]
    }
    

    And create the avatars in viewDidLoad (or where ever )

    createAvatar(senderId, senderDisplayName: senderDisplayName, user: currentUser,  color: UIColor.lightGrayColor())
    

    with a function

    func createAvatar(senderId: String, senderDisplayName: String,  user: FBUser, color: UIColor)
    {
        if self.avatars[senderId] == nil
        {
            //as you can see, I use cache
            let img = MyImageCache.sharedCache.objectForKey(senderId) as? UIImage
    
            if img != nil
            {
                self.avatars[senderId] = JSQMessagesAvatarImageFactory.avatarImageWithImage(img, diameter: 30)
    
                // print("from cache")
            }
            else if let photoUrl = user.pictureURL where user.pictureURL != ""
            {
           // the images are very small, so the following methods work just fine, no need for Alamofire here
    
                if photoUrl.containsString("https://firebasestorage.googleapis.com")
                {
                    self.storage.referenceForURL(photoUrl).dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
                        if (error != nil)
                        {
                            //deal with error
                        }
                        else
                        {
                            let newImage = UIImage(data: data!)
    
                            self.avatars[senderId] = JSQMessagesAvatarImageFactory.avatarImageWithImage(newImage, diameter: 30)
    
                            MyImageCache.sharedCache.setObject(newImage!, forKey: senderId, cost: data!.length)
                        }
                    }
                }
                else if let data = NSData(contentsOfURL: NSURL(string:photoUrl)!)
                {
                    let newImage = UIImage(data: data)!
    
                    self.avatars[senderId] = JSQMessagesAvatarImageFactory.avatarImageWithImage(newImage, diameter: 30)
    
                    MyImageCache.sharedCache.setObject(newImage, forKey: senderId, cost: data.length)
                }
                else
                {
                    //etc. blank image or image with initials
                }
            }
        }
        else
        {
            //etc. blank image or image with initials
        }
    }
    

    for Cache I have a custom class

    import Foundation
    
    class MyImageCache
    {
        static let sharedCache: NSCache =
        {
            let cache = NSCache()
            cache.name = "MyImageCache"
            cache.countLimit = 200 // Max 200 images in memory.
            cache.totalCostLimit = 20*1024*1024 // Max 20MB used.
            return cache
        }()
    }
    

    Let me know if that helps