SDWebImage caching fails when loading big number of images - ios

I'm using SDWebImageDownloader.shared().downloadImage to download images, then
if let i = image {
let key = SDWebImageManager.shared().cacheKey(for: product.imageURL)
SDImageCache.shared().store(i, forKey: key, completion: nil)
}
My code iterates on a product array, and downloads all the images for them (so it will appear while the user is only even if the product's page wasn't opened before)
My problem is, that it doesn't save the images at all, I see a bunch of these in the log:
CFNetwork internal error (0xc01a:/BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork/CFNetwork-811.5.4/Loading/URLConnectionLoader.cpp:304)
Could you please give me a hint where to start?
Thanks!

Related

Cache Image from Firebase

I have multiple cells that currently hold different photos from Firebase. Every time a user loads these images then scrolls, they are re-downloaded which eats up data fast. I find this concerning to any user who has a metered data plan. What could I do to solve this? Does Firebase offer any options to cache downloaded images?
This is how I am currently calling an image into a cell:
if let imageName = post["image"] as? String {
let imageRef = FIRStorage.storage().reference().child("images/\(imageName)")
imageRef.data(withMaxSize: 25 * 1024 * 1024, completion: { (data, error) -> Void in if error == nil {
let image = UIImage(data: data!)
cell.postImageView.image = image
Use Kingfisher to cache images. It's light and very easy to use. Just pass your url from firebase and it will automatically cache it.
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
You might use Alamofire too. It does not handle caching automatically though, but against Kingfisher, it has the ability to handle almost all kinds of networking needs.
PS: Yes I know that -generally- I do not need any networking capabilities if I'm using Firebase.
But, for example; since Firebase Database and Firestore cannot handle full-text search, you need to use third-party solutions, so, you might be in need of full-featured networking utility sometime.
Firebase already does cache the database locally, before it fetch real-time data from the server, so the problem is not as severe. But if you want to do better caching, use Glide, Glide caches images and you can specify time signatures so it re-fetches images only if they are updated.
This is super easy, but you do need to host your images in google cloud services, or aws, or anywhere even
ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
Glide.with(this).load("url").into(imageView);

iOS/Swift: How to manage image downloading from Firebase in a feed

I'm trying to implement a feed for images using Firebase Storage, think of it like the Instagram photo feed.
My Problem:
I don't know how many images there are in my Firebase reference folder
I want only the images to be shown that have been taken, let's say in the last week
I tried downloading the images from the reference like this:
let storageRef = storage.reference()
for i in 0...10 {
let imageRef = storageRef.child("images/" + String(i) + ".jpg")
imageRef.dataWithMaxSize((10 * 1024 * 1024), completion: { (data, error) -> Void in
if (error != nil) {
print(error)
} else {
let image: UIImage! = UIImage(data: data!)
self.downloadedPhotos.append(image) //downloadedPhotos is an array of UIImages
self.configureFeed() //takes care of some UI
}
})
}
Here I run into the obvious problem: I've only downloaded 10 images, called "1","2",..., "10"
What kind of way to name the images when users upload them would you suggest?
How could I keep track of how many images there are?
How could I delete those images from the reference that are older than a week?
Should I use Image Cashing Libraries like Kingfisher or would you go with the above style?
Thank you guys really much for any help!
Let's handle these one at a time:
What kind of way to name the images when users upload them would you
suggest?
I'd take a look at the many other questions like this: Retrieving image from Firebase Storage using Swift
How could I keep track of how many images there are?
See above.
How could I delete those images from the reference that are older than a
week?
You can use Object Lifecycle Management to delete images after a certain time. Deleting them from the database would be harder--maybe an integration with Google Cloud Functions GCS notifications could sync this.
Should I use Image Cashing Libraries like Kingfisher or would
you go with the above style?
I totally recommend using an image loader like SDWebImage or PINRemoteImage after pulling the download image from the realtime database.

Delete a single photo from a burst from the camera roll in Swift

So I have code that will delete images from the camera roll. It works fine, and can delete single images from a burst, however one of the images, if deleted, will delete the entire batch and I can't figure out how to stop that. It usually seems to be the last image in the burst group. And in my request options, I turn on includeAllBurstAssets.
func deletePhotos(assetsToDelete: [PHAsset]){
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
PHAssetChangeRequest.deleteAssets(assetsToDelete)
return
}, completionHandler: { success, error in
guard let error = error else {
return
}
print(error)
}
})
}
I can confirm this behavior. I guess the API is designed to delete the entire batch, if one burst photo is deleted.
Please note, that the Apple Photos app also has no method to delete single burst photos.
It would make sense to make that behavior customizable and I would suggest you fill a bug report /enhancement request.

Cloudkit fetch data (strings and image asset) take a long time to appear after call

I was hoping that someone can help a coding newbie with what might be considered a stupid question. I'm making a blog type app for a community organization and it's pretty basic. It'll have tabs where each tab may be weekly updates, a table view with past updates and a tab with general information.
I setup cloudkit to store strings and pictures, and then created a fetchData method to query cloud kit. In terms of the code (sample below) it works and gets the data/picture. My problem is that it takes almost 5-10 seconds before the text and image update when I run the app. I'm wondering if that's normal, and I should just add an activity overlay for 10 seconds, or is there a way to decrease the time it takes to update.
override func viewDidLoad() {
fetchUpcoming()
}
func fetchUpcoming() {
let container = CKContainer.defaultContainer()
let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: "Upcoming", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicData.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil { // There is no error
println(results)
for entry in results {
self.articleTitle.text = entry["Title"] as? String
self.articleBody.text = entry["Description"] as? String
let imageAsset: CKAsset = entry["CoverPhoto"] as! CKAsset
self.articlePicture.image = UIImage(contentsOfFile: imageAsset.fileURL.path!)
self.articleBody.sizeToFit()
self.articleBody.textAlignment = NSTextAlignment.Justified
self.articleTitle.adjustsFontSizeToFitWidth = true
}
}
else {
println(error)
}
}
}
Another question I had is about string content being stored on cloud kit. If I want to add multiple paragraphs to a blood entry (for example), is there a way to put it in one record, or do I have to separate the blog entry content into separate paragraphs? I may be mistaken but it seems like CloudKit records don't recognize line breaks. If you can help answer my questions, I'd be really appreciative.
It looks like you might be issuing a query after creating the data, which isn't necessary. When you save data, as soon as your completion block succeeds (with no errors) then you can be sure the data is stored on the server and you can go ahead and render it to the user.
For example, let's say you're using a CKModifyRecordsOperation to save the data and you assign a block of code to the modifyRecordsCompletionBlock property. As soon as that block runs and no errors are passed in, then you can render your data and images to your user. You have the data (strings, images, etc.) locally because you just sent them to the server, so there's no need to go request them again.
This provides a quicker experience for the user and reduces the amount of network requests and battery you're using on their device.
If you are just issuing normal queries when your app boots up, then that amount of time does seem long but there can be a lot of factors: your local network, the size of the image you're downloading, etc. so it's hard to say without more information.
Regarding the storage of paragraphs of text, you should consider using a CKAsset. Here is a quote from the CKRecord's documentation about string data:
Use strings to store relatively small amounts of text. Although
strings themselves can be any length, you should use an asset to store
large amounts of text.
You'll need to make sure you're properly storing and rendering line break characters between the user input and what you send to CloudKit.

loading user images works in simulator but not on the iphone xcode 6 Swift

Im trying to load(and upload) images in my app(by picture path).
This is working in my simulator. everything works there. only when im trying to do this on the iphone itself it won't work and i see just the empty UIImageviews.
The paths are loaded the same as in the simulator. And originate from:
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: nil)
{
imageData,dataUTI,orientation,info in
cell.setString(info["PHImageFileSandboxExtensionTokenKey"] as String,name:dataUTI as String)
}
And the PHImageFileSandboxExtentionTokenKey is split into the data and the url when loading the image.
this results in the simulator as :
/Users/Twizzler/Library/Developer/CoreSimulator/Devices/3E671415-8B83-44DA-870F-19BF2BC11F8F/data/Containers/Data/Application/8872109F-3784-40EB-BEB6-4E9FDABE013D/Documents/1613945_10200645161051698_4122753901212984922_n.jpg
and in the iphone as:
/private/var/mobile/Media/DCIM/102APPLE/IMG_2607.JPG
Im loading the image like this:
let image = UIImage(named: "/private/var/mobile/Media/DCIM/102APPLE/IMG_2607.JPG")
cell.imageView.image = image
in this case i put the image url hardcoded (this is in the final app an array of images)
I don't get an error or stack trace. When placeing a breakpoint im seeing the image information in the same way as the simulator
as suggested by the answer im now trying to load them as follows:
let image = UIImage(contentsOfFile: "/private/var/mobile/Media/DCIM/102APPLE/IMG_2607.JPG")
cell.imageView.image = image
This isn't working and i can't upload the files
That’s not how imageNamed works—as the documentation states, that looks for an image with the given name inside your app’s bundle. Try imageWithContentsOfFile.
Well i fixed it! after some puzzling days im now using this way to access upload and
cell.imageView.image = UIImage(data: array[indexPath.row][0] as NSData)
This shows the image. I can save the NSData in the CoreData and reload the image on this way.
To lighten the load on the system im using this:
cellData.checked = 1
var imageData = UIImageJPEGRepresentation(cell.imageView.image, 1)
self.array.append([imageData,cellData.imageData] )
let myObj : ViewControllerImagePicker = self.parentViewController as ViewControllerImagePicker
let textfield: UILabel = myObj.amountLabel! as UILabel
textfield.text = String(self.array.count )
cell.textLabel.alpha = 1
this code is being called when there is clicked on a cell. by setting the cellData.imageData i can recall the cells (when clicking back to the collection view)
I do not know if the way im doing it is correct or works with more that 10 files because of data usage problems. But in my case with a max of 6 selected pictures it works like a charm
The simulator works completely different than a real device when it comes to files, the app bundle and sandboxing.
For starters the simulator will let you write to and add or change files in your app bundle. iOS running on a real device won't let you do this.
You can't hardcode any paths in your app. Even if it works today or on your device in test mode that doesn't mean it will work on a released version. And even if it does it could stop working on any update. Apple several times has changed where things are stored and the path structure in the phone. And where you are allowed to write.
Make bundle calls to get the path locations of standard folders.
Use the assets library to load images from the photo library, or to save them there.
There are tons of resources and tutorials online on how to do this.

Resources