Swift: Firebase 3 - How to Load Images from Firebase Storage and Caching (Ep 6)

Swift: Firebase 3 – How to Load Images from Firebase Storage and Caching (Ep 6)



hey what is up guys welcome back to episode 6 of our firebase tutorial on how to create our real-time chat application hope everyone is doing alright and is ready to do a little bit more coding today so why don't we quickly go into the simulator here and talk about what we did in the last episode so last time we were able to click on this little dragon puppy guy bring up a UI picker controller and click on an image right here let's select a brand start choose and upon registering a brand start as our new user here with a fake email like that we were able to register this entire user give him the image that we uploaded and associate his account with this image right here so we have bran stark and this profile image URL is the image that belongs to this user now ok so let's see slide that guy over there and what is it that we want to do today right well today's gonna be very interesting because we're going to actually go over how to implement the functionality that downloads the image from D pro Palamas URL and put it onto the left of our user right here so in other words we want to be able to click on this new message controller and download the profile for our users in this screen alright let's cancel out of there and let's bring this guy to the top right I don't run this bit of code here and all right so make sure to watch the last episode that is available in the description below that's very useful and you probably won't be able to follow along that much if you are just watching this episode right now alright and also we will be talking about how the network is affected every time we download one of these image URLs ok going into new message controller now we can actually take a look at what we need to do to download that image so let's head over to new message controller cell for row at index path let me show you what we can do here so cell dot image view image view is a default image view that is inside of one of these UI table view cells I'm going to access it directly and set an image to something perhaps image named something we will get right now so what is this image well I'm going to drag in an image from my folder here so that is Ned Stark image dragon let's get the name bat and back and then I'm going to paste it right there so if I run this code now you'll see the application load with all of our users in a table view then we'll get to see the Ned Stark image right there so I bump it up let's see that big and we get that image so pretty good and how do we download these new images from our users now well it's pretty easy if we if we get the image URL like this so if let user profile image URL equals user dot profile image oriole so this will safely unwrap this value here which is stretched from this user property or user variable and it is this property that is a string inside of our user model right here so let's go back and see how we can actually execute an nsurl session shared session and data tasks with this completion handler guy right there I'm going to execute this to download the image what do we need here for the first forever we need a URL so nsurl like this with the string constructor and resistor ring well simply profile image URL and we actually have to unwrap it like this so this is a little unsafe so I'm going to show you how to do that in just a little bit how to make it a little bit safer so completion Handler and injured data response and error so let's just be our little bit safer with our error checking it or if error equals nil not equals no we're going to simply print out the error and then return out of the download so this means that download hit and error so let's return out and here is where the download is successful so to set the image we can say sell that image view that image equals UI image data and data unwrap with a bang and now we can actually run the application so the common mistake that I always run into here is to actually run this without the resumes and if I do this brand stark or this nesstar characters does it and we actually need to hit resume here in order to actually fire off this URL session request so let's see what that does hit that guy and then wait for the images to show up but it doesn't show up because we haven't actually run this image setter on the main queue so let's do that right here dispatch async this batch get main queue and at the completion handler and so if we set the imagery here instead we'll get the images to show up right away whatever the download is complete so I'm going to show you what that looks like by launching the app right now hitting the new and right away you'll see the images load up like that so it doesn't take as long as it did previously alright so what do we want to do now well I actually want to show you a couple of things first so you see how these images are kind of squished right here the aspect ratio is a little off you have to set the cell dot imagery dot content mode equals scale aspect film and we'll see what the images look like right now does it the new message controller right here now is the images and now it's being stretched out like this and we have this inconsistent spacing between each of the images so what is happening here is that this cell image view the default image view that comes with our cell it behaves very very strangely it doesn't actually respect the spacing that we need in order to have a consistent layout like this so why don't we provide our own image view instead of using this default one right here now I'm going to show you how to do that by modifying this user cell right here remember user cell is the actual cell that we've registered it with our table view here so we can modify this cell to actually include an image ooh like this profile image view is a type image view let's use this closure block to declare it an image of you like this and we'll just return it as our closure return value now this will give us the image view right here and if we set image of view that image equals this UI image named Ned see Ned Stark is the felony we can actually now add it into our cells of sub view hierarchy like this so AB sub view profile image review like so and then we need to add some constraints to put it into the cell so in other words let me just now comment off that code right there and comment out this code right here and then we can run this application and it'll revert back to the original state where the images are not showing and the labels are flesh or flushed to the left side and here we go how do we add this profile image of you in here now well we can add it with some iOS 9 constraint says iOS 9 constraints constraint actors and we need X Y width and height anchors like this so the first value is let me just drink this guy down the x value for the profile image view will be right here so let's use a value of roll image view that left anchor that constraint to anchor with a constant and this will be self left anchor and this constant will be 8 so I want to give it 8 pixels from the left right there and then we have to see just that active Eagles true for the source 1 and now we need to set the y anchor like this so the Y anchor the Y anchor value will be Center Y that constrain to anchor this will be self dot setter Y anchor so that's going to place this image view right in the center right there and now we need the with anchor of this profile imagery let me just get rid of that project navigator and get back down here dot let's see with anchor and this is just going to constrain to some kind of constant value that I'll set to be perhaps 40 yeah 40 sounds good and for the height let's just use the exact same value of 40 and now I need to do a couple of things so first run this and it's not going to exactly show up I don't believe I'm gonna show you how to fix that right here so it doesn't show up and we get this huge massive warning message saying that constraints aren't actually working properly because we have not set this translates property here to false and then we also need to set these actives to true for each one of these anchors so quite an annoying thing to do but we have to do it nonetheless turning this now we're going to see that the imagery to show up on the left of the two labels okay so that's a good start so that's what we get there now the labels are being hidden behind the image view that we just added so let's go ahead and fix that by overriding a method up here called layout subviews and so what goes in here so first we need to call super layout subviews we're gonna have to change the text label rather easily if we just change the frame of text label frame equals cg rekt right there so remember this text label corresponds to the top label that says net start and this x value will just be a value of let's say 56 so 56 right there will give us 8 pixels from here 40 pixels from there and 8 pixels up from there now give us 56 and this value right here will be text label dot frame origin dot Y and there we go so we need to unwrap this like that and then for these next values we'll just call the width the width from the frame and this will be text label same thing frame dot height like so so let's run this now and we're going to see that this text label is going to be placed to the right of the image view right now right that we get the text label right here so that's where that text label is let's just move the bottom detail label exactly the same way with in detail text label and we just need to replace all of these values right there it's actually a little text label all right let's run this now you'll see that our table view cells are now fully customized to layout ourselves a little better than the default image view so this is above this up and that's what that looks like and I want to make it so that it has this corner radius down so I will modify profile image of used corner radius like in this image view dot layer corner radius equals perhaps 20 and we'll also have the need to set layer masks to bounds equal to true so the value of 20 comes from half of this constant constraint right here and let's click on that and we get this now how do I provide a little bit more spacing for these image views so I'm going to go back to my table view controller up here don't implement a method called height say height for Row is 256 so let's see what that does all right seems like I am missing an overwrite I really feel like Swiss should autocomplete with that override but I'm not sure why it does not now clicking there we get tolerance cells and we are pretty much closer a lot closer to this than we are before so how do we actually get these cells to load these images now well back in our cell for row method call here let's just remove this bit of comment because we don't need that anymore we can also remove the default image view code and inside of here we actually need to call cell dot profile the profile image view so reason why it's not showing up though is because our cell let's see cell right here if I click on there is a type uitableviewcell so why don't we down cast this as a user cell which is this class and right here so user cell now will contain a let's see profile see profile image view so that already comes up however you see how we get all these question marks here and we need to actually unwrap it so why don't we unwrap it with a bang right there and then we get just the cell like that and setting the image equal the see you just cut that out base out there delete that I'm going to run my application now I'll show you that the images will be fetched and put into our cell like this so so so there are you and all right it's a pretty good now I can remove the default image at net start like that and let's see what do I want to do so actually once you provide a little bit more spacing between these two labels and how do I do that from within my user cell class well to push this label up and push this label down all we need to do is to subtract perhaps two pixels from the Y value a text label and then had to pixels to the value of detail like label detail text label and so that'll give us a little bit more spacing between these two labels right there see the gap has actually widened and that makes your app a little bit more polished in my opinion spacing is rather important to me at least so let's see what we can do now okay so I actually want to show you a a problem a pretty severe issue if you can crease this height of the tableview cell by let's say twice the amount roughly twice 200 it'll go here and you see how these images are coming in and then they flash in and then they flash out then it gets reloaded like that so the issue here is that we are constantly downloading all these images over and over again and the problem occurs if you bring back your project navigator go to this I think it's called the debugger navigator you look at the network tab right here if you scroll the if you scroll the table view up and down you see that per second you're actually downloading quite a bit of data essentially we are constantly calling the bit of code that fetches the image via nsurl session you can imagine how much data you're eating up on your users data plant if you don't manage this correctly so the question is how do we actually go about managing this image fetch instead of new message controller well the proper way of doing this is to actually cache these images inside of your your code how do we cache things so caching is just a process of keeping the things in memory so that you can fetch them later and the easiest way to cache this image is to actually just create a extension so I'm going to hit new new group I don't call this group helpers and I thought it helpers belongs this see the style called extensions extensions is just a very simple file that will import UI kit first and then I'll call extension UI image view and let's just use them caching here so I've already went through this example of caching images in a another video out of my YouTube app series so why don't we just do that quickly again in this bit of code and let's call this function a load image using cash with URL string like this URL string will be this string right there now paste all that code that I just had inside of this method here so this profile image URL will just be this URL string there and now we need to fix this bit of code which we need to actually set the image upon to finishing the download of this image and we simply fix that with a self dot image equals the new image right there now I'm going to go back to my code here I'm going to comment all of this out and in here I'll just simply say cell dot profile image view Cu load you see how it doesn't come up with yet you need to build load using cache and this URL string is just simply profile image URL like so and let's just see what we can do down let it run and it builds successfully and now we can perform this exact same download and we'll see that the problem is still there where the the network is constantly firing off downloads every time we are scrolling up and down so that's the issue that we're trying to fix right here so the constant flashing is a bit annoying and we need to go into the extension file and introduce a cache right above I'm gonna say let image cache equals and as cache like this this cache we'll just serve as the memory bank for all of our images being downloaded in this line right here so how do we actually cache these images well we'll just say this if let well I'm going to show you why it doesn't work with this call for so image cache like this right here a set object and this object right here needs to be a non-optional so the problem is that if we set an image like though this UI image perhaps let's do this maybe it's more obvious and we cut that and paste that there and we need to set this object to this image with this key of URL string like so and the reason why this doesn't work is because this expects a non-optional image and to do this a little bit safer we will say if let image equals that so this image is actually a bad name for our image right here because interview also has another property called image so why don't we do this downloaded image equals this image here and we'll set download an image inside of here via this call like that so now we are good and we also need to set the image right here so it's self dot image equals downloaded image right there if we run this now all these images are going to be cached inside of this image cache right here so I'm gonna hit download or I'm hit the new message and it's going to download you see how the image cache so let me see if I can get Pio image cache uninitialized so we do that that that so we're putting things into the cache and we get our cache with some values in it so how do we get out these values well first let me show you the network tab again you see that the network is still going off like crazy here because it's constantly downloading new images from firebase storage so I'm going to simply fetch the cached images up here alright so here is what we need to do so fetch let's see check cash for image first right here we'll say this let cached image image cache and we'll get the object for key key is this URL string we pass in up here as our parameter and we'll close it off with the self image equals cached image right there and we'll just return out return and bat try to run the reason why we cannot run it right now is because it is not a UI image type so let's cast it as an optional UI image and run this application now so get rid of that space and so here is otherwise fire off a new download and then we can look at those Network tab again new a new message you'll see what happens so let's see I am constantly getting this error for my application because I actually need to log in again so test 51 gmail.com kick you kick your kid the reason why I'm getting this is because I'm I have two or three projects using firebase right now and a similar doesn't really like that in my project so see how the flashing has gone and the network is no longer being used every time I scroll up and down so it's constantly staying on zero right here so if I click on there see no network is being used this per second values staying at zero and it's cashing our images properly and not firing off the new downloads so that's how the extension works like this now there's additional things that we can do with this cache that will probably go into in a later video all right so that looks pretty good why don't we shrink the new message controller a table view cell back down to what is it 56 perhaps and we should be okay all right let's run the application now and hit this new message controller and we get all these images there so that's pretty good let's see let's see what do we want to do here so the other thing that we need to do inside of this extension it's actually set the images to nil every time we run this call in other words let me see how I can illustrate this a little better new message Euler let's bring this value back up to 100 here and run the application so what I mean is because this tableview reuses sells if I click here it'll download all these images but the moment I scroll down it's going to reuse some of these images like that so you'll see something a little minor bit of flashing occur at the bottom as well so let me just show you that one more time running the application will get the new message controller and hit that and just concentrate on the bottom couple of cells so you see how it kind of like flashes in that's because we need to set this image right here so self image equal nil then I'll blank out the image and just show a bit of white space before the image can actually download so let's come here new message controller will see the black images blank right here and we don't get the flashing anymore so one thing that's also advantageous about this cache is that every time you cancel out of the screen you cancel out you bring it back the images are already downloaded inside of our new message controller more specifically it's downloaded inside of this image cache right here and one thing that I probably needs to fix I think is the aspect ratio of these images right there so why don't we go back and to fix the ratio a little bit by going to your new message controller and then we'll go to profile an interview here image view dot content mode equals scale aspect film like that so let's run that and see how close we are so getting a good width and height aspect ratio for the user image so that looks a lot better and let's bring this value back up to let's see if if these six perhaps and running this will get something a little better so why don't we try to increase the height of the image view as well so this looks a little small for my taste why don't we bump this up to perhaps 48 let's seeyou 48 and the corner radius needs to match or properly appropriately to half of that and we now bump this up to perhaps 64 64 48 64 perhaps 72 so 72 and so this is just a slight modification to make our application look just a little bit better I'm going to click on this guy and we get the images right there now let's a lot nicer a lot more legible and we need to now push the label to the side a little bit so instead of 56 we'll use 64 and 64 and I'll push it to the right eight pixels to match the spacing that we have inside of ourselves so that's how you would kind of layout all of your cells a little bit more a little nicer in terms of spacing like that and alright that's about it alright that about wraps it up for today hope you guys enjoyed the lesson on how to monitor the network traffic on your devices and also how to re layout your sub views inside of a UI tableview so if I only make sure to hit the like button if you enjoyed today's content and also hit the subscribe button as well does really help me out alright in the next episode we'll take a look at two things first I'll show you guys how to set up your users profile image in the navbar and secondly we'll fix a bug that is occurring every time we log out log back in as a different user and then the nav bar title needs refreshed based on this new user and finally you can download the project by clicking the link on the screen you can follow me on twitter at build that app and also if you're interested in learning more about iOS development you can click on the following three links on the screen to go directly to the playlist on my youtube channel that's about it guys keep on coding and I'll see you next time

35 thoughts on “Swift: Firebase 3 – How to Load Images from Firebase Storage and Caching (Ep 6)”

  1. NSURLErrorDomain in Xcode, change your firebase storage permissions. If you try to download the image using the url you'll get this
    "code": 403,
    "message": "Permission denied. Could not perform this operation"

    Change the Firebase storage rules
    allow read, write: if true;

  2. getting "Ambiguous reference to member 'dataTask(with:completionHandler:)'" error

    if let wasteImageUrl = wastes.waste_image{
    let url = NSURL(string: wasteImageUrl)
    URLSession.shared.dataTask(with: url) { (data, response, error) in
    if error != nil {
    print(error)
    return
    }
    DispatchQueue.main.async {
    cell.wasteImage.image = UIImage(data: data!)
    }

    }.resume()
    }

  3. any reason why it compiles but does not display user images but only displays the image that I originally gave the program?

  4. Thanks Brian, such a great tutorial even after 2 years!

    About the constraints of the cells (Swift 5):
    instead of using a constant x value for textLabel, we could use the following: 
    "textLabel?.frame = CGRect(x: profileImageView.frame.maxX + 8, y: textLabel!.frame.origin.y – 2, width: textLabel!.frame.width, height: textLabel!.frame.height)"

    Code above will automatically adjust the distance between profileImageView and textLabel to 8 pixels. Of course, same method can be applied to detailTextLabel constraint.

  5. Hi! This series really helped me to understand soo many topics that I didn't understand before. Thanks for that! Now, my question is: Do you already talk about how to somehow "save" the fetched users and/or chats, so they don't have to be downloaded again everytime I run the application?

  6. Use "cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage" not "cachedImage = imageCache.object(forKey: urlString) as? UIImage" if you have error for type.

  7. Firebase already does cache the database locally, before it fetch real-time data from the server, so the problem is not as severe.

  8. Hey great videos but when i was following your tutorial i ran into a problem as firebase was not returning the values for whenever i call a single value and i found out that the uid on my side and on firebase database are not matching can you please help

  9. Thanks for your video and here is one question. How to refresh the Cache if the user changes their image?

  10. I'm trying to do something similar, but how would I write my app such that when another user changes their profile picture, my app should be notified and download the new profile picture (i.e. not show the previously cached photo, but instead show the new photo)?

  11. Hellow Sir,
    I have followed your whole video it was a great tutorial, But lastly when I try to scroll my data up and down, Upper side cell reloaded with wrong Image, every time it shows different images for the same person. Please help me to figure it out what the issue is and how I can resolve it

  12. Instead of downloading the images every time you pull up the message VC, is there a way you could do it once, and save them so you don't have to do it every time? Then, if there's a new one, call it again?

  13. hi brian…i am getting "nil" on my profileImageURL though there is a valid profileImageURL in firebase database…any suggestion? thanks man…

  14. Hi Brian!
    I use swift 4 and here is my code everything works perfectly.
    Profile image uploads in firebase Storage and the url saves in database but there is something wrong with downloading code because profile image does not appear when i run my app. i don't get any error from Xcode. Please help!

    if let profileImageUrl = user.profileImageUrl{
    let url = URL(string: profileImageUrl)
    URLSession.shared.dataTask(with: url!,
    completionHandler:
    {(data, response, error) in

    //download hit error
    if error != nil {
    print(error)
    return
    }

    DispatchQueue.main.async() {
    cell?.imageView?.image = UIImage(data: data!)
    }
    }).resume()
    }
    return cell!
    }

  15. I am not too experienced with iOS experience, but what is the consensus on using constants for constraints? Do they scale well on all devices?

  16. When I write this, an error pops up. How do I fix it?
    let profileImageView: UIImageView = {
    let imageView = UIImageView()
    imageView.image = UIImage(named: "background")
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.layer.cornerRadius = 20
    imageView.layer.masksToBounds = true
    return imageView
    }
    // <-Error: Cannot convert type '() -> _' to specified type, UIImageView

  17. hi sir
    can we load images direct from firebase storage ?
    (without using firebase real-time database use )
    if yes ( how many device ?)

Leave a Reply

Your email address will not be published. Required fields are marked *