I am planning to use Core Data in my app If there is no internet connection.
So what i am doing is retrieving images and videos from my server. I currently download them (Images for example) from the url retrieved from the server and convert it to UIImage NSData and then populate my tableView.
Now to the CoreData:
Some may say storing files in coreData is a bad idea, But as I shall only store a maxium of 5 images/videos at once I don't thing storage or speed shall be a problem as they will not be enourmous files.
To the question:
I have to save I guess the UIImage to CoreData, so obviously I need to get the URL then download the image and finally save it to coreData.
But If I do so in my query for getting the actual posts that would slow down the query possibly a couple of seconds, and I would prefer not to do so. So I was wondering what would be the best way?
I am downloading the images async with SDWebImage each time a cell appears, so If I save the images to coreData once they are downloaded that would mean to save 5 images to CoreData I would need to scroll 5 Cell's.
But I would prefer to: Load the app, download the Images/videos from server as normal and populate the tableView, and once all that has happened I would like the post's to already be saved to coreData.
So If I immediately close the app after the tableView gets populated then disconnect from the internet or go into AirPlane mode and re-open the app I have the posts in the tableView.
I know how to do most of the saving to coreData etc... But I would like some more proffessional advice and tips on how I should approach this.
What I would like to achieve?
1) Open the app
2) Download 5 images/video from the server (Up too here all good)
(From here on I would like some advice)
3) Empty the coreData if there was any previously images/videos saved
4) Save the last 5 images/videos I downloaded to CoreData.
What would be the best solution and quickest way to do so?
Thanks to anyone that can offer some advice.
Background
iOS devices, like most computers, have persistent storage. (e.g. a hard drive or an ssd)
To persist data across system shutdown events, etc., your app can save it directly to this store using the filesystem. This is a quick and simple way of storing data.
If you need a more structured approach, you could use a database. This approach allows you to work with your data in a more organized fashion (i.e. in rows and columns, with relationships, etc.) This database would be stored on the filesystem, but your app would work directly with the database file(s) itself.
On iOS, the typical database used is SQLite.
Core Data is another layer on top of one (or rarely, multiple) databases. This approach allows you to focus on your data as entities with relationships to each other. (e.g. User -> multiple Follower, etc.) Using Core Data, you can usually ignore the databases and the filesystem underlying it.
On iOS, Core Data is typically implemented with one SQLite database as a store.
Your Use Case
If all you need is to store a few images or videos to your device, then storing directly to the filesystem can work pretty well.
Assuming you already have your file in hand, you just need to:
1 – get a directory location
var documentDirectoryUrl: URL
do {
try documentDirectoryUrl = FileManager.default.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let imageUrl = documentDirectoryUrl.appendingPathComponent("imageFileName.jpg")
} catch {
// error handling
}
2 – write your file to disk
if (try? imageData.write(to: imageUrl)) == nil { // nil == false
print("Error: could not save image to file system.")
}
Reference
iOS File System
Core Data
SQLite on iOS Tutorial
Related
My app is a mood diary and to save data I've chosen to use Core Data (for strings, images, etc.); to allow the user to restore his diary I've implemented iCloud, that works well with Core Data.
Everything works well if I have not much entries, but when the images saved are too much the app is slow to load data and encounters memory warnings.
To save my images I've chosen Transformable data type and not Binary Data; I know that is not the best way to save images (saving url is surely better), but I need to sync my data on iCloud, and saving images as Transformable allows me to sync data in a simple way (thanks to the possibility offered by Apple Api to link Core Data and iCloud).
What can I do to avoid this memory warnings and sync my app pics on iCloud?
I've considered the possibility to save my pics on a custom photo album (if iCloud is activated for Photo app my app pics would be synchronized), but I need to save them with custom name to retrieve them from camera roll to my app, and for the moment I don't find any solution to save pic with custom name in a custom photo album.
Saving photos in document directory (and saving urls in my core data entity) would be the right choice for local database, but my app pics would not be synchronized.
There are a few things you can try.
First, add an image thumbnail property which stores a smaller version of the image. Use that whenever possible. Loading a bunch of full-size photos needs a lot of memory, so change to loading smaller images whenever your UI allows smaller sizes.
Beyond that you can change how you handle images using one of the following strategies. In ascending order of complexity (and effectiveness):
Make sure "Allows external storage" is enabled for the image property in your data model. This will let Core Data put the images outside the persistent store without requiring you to manage those files. This will save on memory if, for example, you sometimes fetch data but aren't using the image property.
Change the data model so that the image is saved in a different entity, with a relationship linking it to your current entity. This should make it easier to avoid "accidentally" loading images when you're not using them.
Put the images in separate files and keep only the file names in Core Data. You can still sync the images via iCloud, because you can sync files directly via iCloud outside of Core Data. But you'll need extra code to manage uploading/downloading the images. You'll also need to make sure you can handle the case where Core Data has finished syncing but the image is not available yet.
On this list, #1 is easiest but will probably have the least effect. Using #3 should be very effective but will require the most work.
I want users to save the picked image and video within the app. So I wanted to know what is the best. Should i convert the picked images and videos to NSData and save in Core data? I search everywhere and everyone recommend to use Documents Folder to save images and videos because coredata is slower. I just started learning CoreData so I don't have much knowledge about it. Here is what I have come up.
Users pick the image or video -> App Saves it to documents folder -> Using coredata, application stores the filepath as string to access the image or video. (and User can backup anytime using icloud)
and Users can access those files in their other devices using iCloud. Please explain me if I'm incorrect or if you have better alternative or the question is wrong in any way. Thanks
I've done both ways and for me it's just a matter of how to use the database. If you'd like to to be able to backup the database and share with other, I would recommend storing files in CoreData as NSData. If it's just a local database it might be easier to store URL's to images stored on disk.
When you setup a Binery field in CoreData you can optionally select "Allows External Storage" which basically store the file on disk for you, but you can load it from the databas as if it was stored in the db. This is what I use most for images.
With this option, it is easy to create a zip file of the database and all binary files connected to it and also easy to unzip it to make a restore of the database.
So i have this situation with images. In one of app stages i get all user photos from his photo library. I get them as ALAsset's. I let him choose photo he wants. Then i save his chosen photo to applications directory as full size photo with HIGH_ prefix and a thumbnail of a photo with LOW_ prefix. I need this because photos have some properties like time etc. I save those properties to SQL database with a field of photo name that begins with HIGH_ or LOW_. When i need to get photos i get properties from db and then do [UIImage imageWithContentsOfFile:photoPath]. Can someone tell me how to do it more efficient because writing and getting photos like this takes some time. And on iPhone 4 i sometimes even get memory warnings. AND another question would be, how should i save photos fetched from web?
I stand corrected, instead of using core data, Apple writes,
It is better, however, if you are able to store BLOBs as resources on the filesystem, and to maintain links (such as URLs or paths) to those resources. You can then load a BLOB as and when necessary.
So you are doing it correctly, but maybe you should check out transformables. Just make sure you remove images you aren't using from memory if you are getting warnings
From documentation: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html
Under the section 'Large Data Objects (BLOBs)"
Another way to do it with a transformable:
Which way to store data(image)? NSData, String or Transformable
In fact, perhaps transformables are for core data at least:
How should I store UIImages within my Core Data database?
Im on iOS 7 and want to use core data with iCloud, this is working well, the issue I wanted some input on is the following.
Some of my Core Data objects have related images/large text files which I was storing as files on the file system and just setting the url of these items as string properties. Now obviously this wont just work with iCloud Core Data sync because iCloud has no notion of these files. I was wondering if you had any suggestions on how to handle this?
1) I could store the image data and large text files as core data properties directly but it does not seem to be the best way to go
2) I could use iCloud to also store the documents and then somehow try to keep sync between the files in different iOS devices, but this could potentially get messy.
3) Some other solution.
Any Suggestions?
Regards
Daniel
If you are setting up the model programmatically, add a message to setAllowsExternalBinaryDataStorage: to allow Core Data to decide when to save it as a record or separate file automatically. Core Data manages the external files for you when it decides to use them, so it should "just work" in iCloud as well.
NSAttributeDescription * imageAttribute;
//Initialise, setup, add to model, etc
[imageAttribute setAttributeType: NSBinaryDataAttributeType];
[imageAttribute setAllowsExternalBinaryDataStorage:YES];
There is a corresponding checkbox for the attribute in the model editor UI as well.
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSAttributeDescription_Class/reference.html#//apple_ref/doc/uid/TP30001175-SW26
Place the files in your iCloud document sandbox also and they will keep synced across all your devices.
You can use a relative reference to the items as they will appear in the file system at the same relative point.
<ubiquitous container URL>/Documents/something.jpg
A minor issue is that the sync isn't seamless and you will have to explicitly request the item to be downloaded from iCloud if it isn't on device already.
So at its most primitive...
NSDictionary *dict = [urlToIcloudResource resourceValuesForKeys:#[NSURLUbiquitousItemIsDownloadedKey] error:&error];
NSNumber *isDownloaded = dict[NSURLUbiquitousItemIsDownloadedKey];
if (![isDownloaded boolValue]) {
BOOL res = [[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:urlToIcloudResource error:&error];
}
So you can keep using your current strategy of dumping them in the file system and just using a string reference to them. iCloud will keep them synced.
The suitability of this solution does depend on whether those large blobs are write-once-read-many objects (very suitable) or read-write-often type objects (trickier).
One advantage is that for text objects like txt or json if you keep the extension on the file iCloud should "just work" for keeping multiple device edits in sync.
Raw data objects like jpg or pdf will be last edit wins.
My situation is that I have a universal app that talks to an sql database via odata. When the user retrieves data over the line I want to save that to the device so that if the user stops the app or the app crashes than I can rehydrate the saved device data and we will not have to re-retrieve the data when the app starts again.
My question is for this sitatuation is it more beneficial to user coredata to save the data to an sqllite db or should I save the data to the documents directory? The data can be serialized into an NSData object which could be saved straight to the device from what I have read, where as saving NSData objects to sqllite is not what it is designed for.
Im looking for the most performant of the two options and also the option that will not restrict as much on size restrictions.
Looking forward to any advice that you can give me.
Thanks in advance
If the size of the data is small enough to fit in memory with no problems, then you will probably get the best performance from serializing an NSData object.
If, however, the data reaches the point where it strains memory usage, you will want to use something like Core Data or sqlite to persist it to the disk and only load objects in memory you are using at the moment.