Kingfisher how it checks if need to refresh image? - ios

I'm using a Kingfisher lib to manage images. The server refresh image every hour. Kingfisher not refreshing the image. Why?
MY INVESTIGATION: I've read cheat sheet and wiki and found out that the cache is marking as expired after 5 minutes for memory and 7 days for disk. When app did enter background and receive UIApplicationDidEnterBackgroundNotification the library performs backgroundCleanExpiredDiskCache that cleans the disk cache. Also there is a .forceRefresh option to load the image from web every time.
But i don't need to load image every time. I just need to refresh it when the new image appear on the same image url. How this works? I think it should be some field that received from the server, that can tell - you need to update your image, and the lib should manage this field, isn't it?

Related

Difference between three firebase storage download methods

I couldn't find resources discussing the difference between the three download methods in the firebase storage documentation and pros/cons of each. I would like some clarification about the firebase storage documentation.
My App
Displays 100 images ranging from 10 KB-500 KB in size on a table view
Will be used in a location where internet connection and/or phone service could be very weak
Could be used by many users
3 methods for downloading from Firebase storage
Download to NSData in memory
This is the easiest way to quickly download a file, but it must load entire contents of your file into memory. If you request a file larger than your app's available memory, your app will crash. To protect against memory issues, make sure to set the max size to something you know your app can handle, or use another download method.
Question: I tried this method to display 100 images that were 10KB-500KB in size on my table view cells. Although my app didn't crash, as I scrolled through my table, my memory usage increased to 268 mb. Would this method not be recommended for displaying a lot of images?
Download to an NSURL representing a file on device
The writeToFile:completion: method downloads a file directly to a local device. Use this if your users want to have access to the file while offline or to share in a different app.
Question: Does that mean all images from firebase storage will be downloaded on user's phone? Does that mean that the app will be taking up a large percentage of the available storage on the phone?
Generate an NSURL representing the file online
If you already have download infrastructure based around URLs, or just want a URL to share, you can get the download URL for a file by calling the downloadURLWithCompletion: method on a storage reference.
Question: Does this method require a strong internet connection and/or phone service connection to work?
Generally, your memory usage should not be affected by the method of retrieval. As long as you're displaying the 100 images, their data will be stored in the memory and should have the same size if they're identically formatted/compressed.
Either way you go with, I suggest you implement pagination (for your convenience, this question's answer might serve as a good implementation reference/guide) to possibly decrease the memory and network usage.
Now, down to comparing the methods:
Method 1
...but it must load entire contents of your file into memory.
This line might throw some people off thinking it's a
memory-inefficient solution, when all it really means is that you
cannot retrieve parts of the data, you can only download the entire
file. In the case of storing images, you probably would want that for
the data to make sense.
If your application needs to download the images every time the users
access it (i.e if your images are regularly updated), then this
method will probably suit you best. The images will be downloaded
every time the application starts, then they'll get discarded when
you kill it.
You stated that a part of your user base might have a weak internet
connection and so the next method might be more efficient and
user-friendly
Method 2
First off, the answers to your questions:
Yes. The images downloaded using this method will be stored on the users' devices.
The images should take up about the same size they're taking on Firebase storage.
Secondly, if you plan to use this method, then I suggest you store a
timestamp (or any sort of marker) in your database for when the last
change to the images occurred. Then, every time the app opens up, do
the following flow:
If no images are downloaded -> download images and store the database timestamp locally
If the local timestamp does not equal the timestamp on the database -> download images and store the new timestamp locally
Else -> use the images you already have, they should be identical to the ones in Firebase storage
That would be the best way to go if your network usage priority is
higher than that of the local storage.
And finally...
Method 3 (not really)
This is not a data download method, this simply generates a
download URL given a reference to the child. You can then use that
URL to download the data in your app or elsewhere as long as the used
app or API is authorized to access your Firebase storage.
Update:
The URL is generated from a Firebase reference (FIRDatabase.database().reference().child("exampleReference")) and would look like this: (Note: this is a fake link that will not actually work, just used for illustration purpose)
https://firebasestorage.googleapis.com/v0/b/projectName.appspot.com/o/somePathHere%2FchildName%2FsomeOtherChildName%2FimageName.jpg?alt=media&token=1a8f83a7-95xf-4d3s-nf9b-99a274927bcb
If you simply try to access that link you generate through any regular web-browser (assuming you don't have any Firebase rule that conflicts with that in your project), you can directly download that image from anywhere, not just through your app.
So in conclusion, this "Method" does not download data from Firebase storage, it just returns a download URL for your data in case you want a direct link.

Downloading images and display on table cell (Concurrent)

My app displays images in tableView cells What I want to achieve is to load the image in sequence even if the cell has disappeared off the screen. For example, I have cell 1 to 100, the cell is displayed and the images starts to download in background from 1 to 100 even if the user have already scrolled to cell 78. I'd also want to only download one to two image at a time so I don't bog up the network. With these criteria in mind, I was wondering what sort of setup would be most appropriate?
At the moment, I am using AlamoFire to download image. Would a combination of Alamofire with some sort of NSOperationQueue be suitable. I am very un familiar with NSOperationQueue at this stage. So I thought I'd try to find a recommended industry practice before I start going deep into setting NSOperationQueue
I'd also like to combine the functionality to continue the download even when the app is in background
Note. Any alternative library or framework solution is also welcome
The best solution out there is SDWebImage. The link to this repository is here.
If you wanna do it your own way without having any dependency then you have to follow the same things that SDWebImage does.
In short,
Asynchronously start download in thread, which downloads the images from the URL concurrently.
Then after downloading, use NSCache, to store the image and use the
imageURL as the key.
After storing the image in NSCache, also write the image in NSCacheDirectory, with the imagename same as that of its URL.
Now while fetching you have to check, whether the image exists in NSCache, using the imageURL as the key you can easily search that. If found, return image or then search NSCacheDirectory, whether the image exists there or not. If not then you must download the image and follow the steps once again.
Now NSCache is like the RAM. The data stored there will remain there as long as the Application is active. Once terminated, the data in NSCache will get cleaned.
Hence we also write the file in NSCacheDirectory, as data over here is persistent to as long as the app is not deleted from the device.
I hope I could make you understand as to how this thing works.
Cheers.

Delete unused persistent data without reference

I have an app which communicates with a server. In this app I have a tableview in which I display several people from my company (their first and last name and their profile image).
Every time the tableview opens or needs to refresh, I fetch the user list from my server. These users will all have an image_name, which I try to look up in an array on the app itself. If I can't find it there, I load it from the documents dir, if I can't find it there either I download it from my server and save in locally on the device to prevent future downloads.
This works very well and it's a very easy way to manage the users and their images, it also makes sure that I download an image only once if several users have the same image (e.g. the company logo when they haven't uploaded an image yet).
The problem is that I don't keep a reference to these users so the app has no clue which user uses which image OR even if an image is still in use.
So when person A has image X it will be downloaded to the iPhone. If user A then changes his image to Y, the app will download and display image Y correctly. However, image X will never get deleted from the persistent data.
I ask you, the stackoverflow community, what's the best way to handle this?
Should I start keeping a reference to my users so I can also keep a reference to the old image?
Is there any way to find the timestamp of the last time and image was read from the documents dir?
Should I store the image names in coreData and all the references to them? (some kind of custom ARC logic)
...
At some point in time you have the list of used images, at this point in time you also have a list of images saved to disk. Once per day you can take this information and, on a background thread, do a comparison of the used and saved and delete them. This shouldn't require any additional data storage.
If you wanted to allow images to hang around for a while after they stop being used you can 'touch' the file (update the fileModificationDate) each time you use the file and then later you can check the modification dates of all images and delete on that basis.
You could add a prefix to the image that you download and when you fetch images, check all images in persistent storage for this prefix and then remove if there are any. You should only need to delete (maximum) one image every time your client fetches, which wouldn't be too heavy on the client.

UIImageView UIKit setImageWithURL memory usage

Trying to debug my app, but my memory keeps increasing .
I have an app that pulls JSON from a web service with AFNetworking. I use UIKit + AFNetworking to load the URL string that I got from the JSON in UIImageView which are in a custom XIB table cell. I use setImageForURL.
When I load new JSON and hence new image URL's, my memory usage jumps by 2-7 mb each time. The old images are replaced in the UI by new ones. Live memory usage goes up and up. I've looked into the arrays holding the JSON and so forth - no leaks.
I suspect there is some image caching going on but I've never seen any information regarding image caching when using UIImageView with setImageWithURL. I've seen memory issues using imageNamed though.
Does setImageWithURL have the same memory issues as imageNamed?
setImageWithURL: from AFNetworking handles the caching of the images downloaded with this method. The next time you call this for the same URL it will check the cache first to see if the image is cached (so no need to go network again).
From AFNetworking API:
Asynchronously downloads an image from the specified URL, and sets it
once the request is finished. Any previous image request for the
receiver will be cancelled. If the image is cached locally, the
image is set immediately, otherwise the specified placeholder image
will be set immediately, and then the remote image will be set once
the request is finished. By default, URL requests have a Accept
header field value of "image / *", a cache policy of
NSURLCacheStorageAllowed and a timeout interval of 30 seconds, and
are set not handle cookies. To configure URL requests differently, use
setImageWithURLRequest:placeholderImage:success:failure:
So there is indeed caching, hence memory usage.
Hope it helps.

Downloading lots of Images in an iPad App

I am working on an app where I need to download lots of images locally (so that they are available offline). The number of images can be 100 - 10,000. Each image may vary from 100K- 250K
I can do this via a NSOperationQueue and I have the code to make this work already but this question is more of a conceptual nature. I am not sure what is the best approach to take here.
1) Do I download all images as soon as the user logs in for the first time ? Based on the number of images, this could take a long time and what if the user closes the app meanwhile. I understand there is a limit on the time that can be spent by a background process in this case? Honestly, I dont want to do anything in the background (ie when app is closed)
2) Do I download images when a particular category is selected by the user? If a category has 800 images, then what happens if the user selects another category before all of those 800 images are finished downloading? I can always start threaded downloaded but will the thread keep on running if the user selects another category ?
3) Put something in "Settings" to let the user decide this themselves. Something like "Total Images: 8000" Images Available : 2000 and a button to say "Download All" which would display a UIProgressView of what's going on....so the user will probably wait till it's all done.
Or some other approach?
Thoughts?
As far as I understand from your description, including comments, I think that the best approach would be downloading all of your images in a thread and make the download process resumable. In this way, you are going to mirror a remote database of images for offline use.
What this entails is:
you keep track of which images you have downloaded;
each time the app starts/resumes, you start the downloading thread exactly from where you left;
you should also provide a mechanism so that the user is suitably informed when he is trying to access an image which has not been downloaded yet, but I think this should be no problem (you might also provide a progress indicator somewhere).
I would only download the image when the user actually needs to do something with the image. Your users will hate you if you download all the images upfront. :)

Resources