iCloud with Core Data and File sync - ios

I'm working on an Application that uses Core Data with iCloud (with the great improvement given by iOS7). This application stores data to describe a task with this information:
name a NSString
date a NSDate
image a NSString which describes a path to the picture
The pictures could be stored in Documents or Library Directory (I have to decide which is the more convenient folder), by the way, in the same folder with a unique name.
Now I'd like to activate iCloud sync for the images too otherwise the experience of the user will be incomplete (I just sync DB data.. no images, a strange/wrong behaviour for an app).
I'm really confused by Apple Documentation. I can't find a way to understand exactly how iCloud data works for this kind of needs. I just want to sync every file of a folder as soon as they will be created. So my questions are:
Could you share some good resources to learn how to use iCloud for file sync
Have I to use UIDocument and other iCloud API? or is there something "automatic". Quite a new bye/stupid question, I know :(
Are there any problem using Core Data and Document based iCloud synch in the same app?
Note: I know that I can sync data just by adding file in the document folder and hoping that users activate document sync... but this is not what I want obviously.

It is pretty straight forward to use both Core Data transaction log synchronisation and file based synchronisation in the same app to achieve what you want to achieve.
So you would set up your Core Data stack to use iCloud options and synchronise data changes via iCloud. At the same time you would store your images in the Apps iCloud container so they get synchronised as well. Just remember you need to use a relative reference to the images in your Core Data fileURL because the full pathname will vary depending on the device the app is running on. So for example you would just store the image filename in Core Data and use a standard directory such as 'iCloudContainer/Documents/Images/' to store them. 'iCloudContainer' being the URL you get by calling the [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:] API.
I have uploaded sample Core Data apps that use iCloud for transaction log synchronisation (i.e. synchronising data in Core Data) and that use file synchronisation for storing Core Data backup files in iCloud which can then be accessed by any device. You should be able to use the same code for moving backup files to and from iCloud for your images. Just remember you have to trigger download of files from iCloud before you can use them either by doing a coordinated read or by initialising the download using NSFileManager.
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/sample-apps-explanations/
Download and run the sample apps and use the built in Backup File Manager to make backup files and to copy them to and from iCloud using different devices. Then just use the same code when storing your image files.
Your App does have to handle things like the user changing iCloud account, logging into or out of iCloud etc. and them move the core data file and image files accordingly.

The only way to have this happen automatically is to create a binary data attribute in your model for the images. If you do this, you will probably want to check the external binary storage allowed option, so the photos end up stored as files and not in the database.
If you would rather store the photos external to your store, you will have to do more work. You will need to migrate the photos into the iCloud container yourself, using the NSFileManager methods, for example. You could also use a class like iCloud Access if you find that easier.
The downside to handling the photos yourself is that you can never be sure that they have all arrived on your device when the Core Data store syncs, so it could be one or more photos are missing, even though there are entries for them in the store. You would have to make sure your app could handle this scenario, perhaps showing a placeholder image until the real photo was accessible.
There are no issues using Core Data and Document syncing in one app. In fact, they are exactly the same under the covers. From iClouds point of view, they are all just files to be transferred.

Related

How to create sharable Core Data .sqlite backups locally and in iCloud using current NSPersistentStore methods

I have been having quite a time of figuring out the correct way to create a backup of a Core-Data backed .sqlite file and storing that backup, both locally and/or in iCloud (whatever the user prefers), for download, restore or sharing. Let me state up front that I am not talking about moving the persistent store to iCloud to be used by the app as its datasource. I am simply asking about creating backup files in this question.
In 2014, Apple changed their default journaling mode for Core Data SQLite stores to WAL.
https://developer.apple.com/library/content/qa/qa1809/_index.html
With that change, they recommended:
To safely back up and restore a Core Data SQLite store, you can do the
following:
Use the following method of NSPersistentStoreCoordinator class, rather
than file system APIs, to back up and restore the Core Data store:
- (NSPersistentStore *)migratePersistentStore:(NSPersistentStore *)store toURL:(NSURL *)URL options:(NSDictionary *)options withType:(NSString *)storeType error:(NSError **)error
Note that this is the option we recommend.
Prior to this, I had been using NSFileManager to create backups. With this recommendation, I believe that the correct way to create a backup locally is to add a new persistent store and then to migrate that persistent store to the desired backup location, using
the NSPersistentStoreCoordinator methods addPersistentStoreWithType:configuration:URL:options:error, and migratePersistentStore:toURL:options:withType:error, respectively.
My questions are two-fold:
I previously would zip my data to NSData before exporting it, and
would write the NSData directly to a file. My file extension would
be custom to my app, for sharing the zipped data via email or other
iOS sharing methods. With the
migratePersistentStore:toURL:options:withType:error method, I now
end up with a .sqlite file (and its corresponding WAL file, etc) in
the desired location, but I cannot figure out how to share this file
now. If I zip the file, won't I be in danger of losing the data I
worked hard to preserve by using
migratePersistentStore:toURL:options:withType:error in the first
place? I believe so, but I do want to enable my users to share/save their
backups vie email, etc, and I don't know how to best approach this now.
I am having a hard time understanding how
migratePersistentStore:toURL:options:withType:error can be used to
backup the file to iCloud. Much like the sharing example above, I
see that I can use
addPersistentStoreWithType:configuration:URL:options:error, and
migratePersistentStore:toURL:options:withType:error to get the
desired .sqlite copy locally, but if I then try to upload that local
file to iCloud, I fear I will lose the data I worked to preserve by
using migratePersistentStore:toURL:options:withType:error in the
first place. I have been trying to see if there is a way that I
could/should use
migratePersistentStore:toURL:options:withType:error to move the
newly created persistentStore directly to iCloud, but I haven't been
able to find any documentation on how to do that or if it should be
done at all. Is there a specific url I would need to use to indicate that the destination for the persistentStore is iCloud?
I would greatly appreciate any insights that can be shared regarding the answers to these questions.
ZIP should be safe way IMO since it has CRC data for data integrity validation.
You have to be careful though regarding shared CoreData store version. Suppose two users run different versions of the app and share the CoreData store between each other. CoreData doesn't support progressive migrations out of box. https://www.objc.io/issues/4-core-data/core-data-migration/
Maybe sharing a portion of data in JSON and re-creating CoreData entities from it would be safer and easier strategy for sharing data as opposed to sharing entire graph.
You can only copy file into iCloud container and share it across devices but you can't really use it directly from container or have incremental updates coming via iCloud. NSFileManager has setUbiquitous that allows to move file into iCloud container.

save image and videos within the app iOS

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.

iOS app: Pre-populated large database not allowed - alternatives?

So my iOS app has just been rejected because it's storing too much non-user-created data in the SQLite database, which lives in the Documents folder.
The app basically involves a relatively large library of images -- around 60-or-so megabytes of them to start, and there are also in-app purchases which each add an additional 60-or-so mb of images. Furthermore, the user can add their own images to the library.
Right now everything (images and all) is stored in an SQLite database, which is generated when the app is first launched. As the user adds more images, or purchases image packs, those images are added to the database. To the user, all the images (user-generated or not) behave the same in the app.
But Apple won't allow this: I can't have all that data stored in the SQLite database in the Documents folder unless I set it specifically NOT to back up to iCloud, as it's all recreatable data.
But if I set it to not back up, then the user-generated data won't back up either, which I definitely don't want.
Any suggestions how I might "split up" the database, such that all the user-generated stuff can be backed up, but the included-or-purchased stuff isn't?
The reason you are being rejected is not following the Data Storage Guidelines. Data created or edited by the user belongs within NSDocumentsDirectory, while application data should be stored elsewhere (i.e., the Application Support directory). These requirements are a result of how iCloud backup and disk space purging work on iOS.
For a Core Data application, this means your persistent stores must be split into two different sets of files, in two different locations. This, in turn, ends up driving much of the application architecture and data model. To have relationships between the user data and application data, for instance, you must use two different managed object model configurations and the relationship must be a fetched property.
There is more detail on how to implement this in this answer.
An alternative for your specific case would be to save the images on the file system, in the caches directory or elsewhere. User images could exist in NSDocumentsDirectory while application images could exist in NSCachesDirectory. This would remove the images from Core Data and instead your model objects would have the path to the image on the file system. This would be a short term fix to get you through submission, and would probably work.
One option will be, storing your data to a server and calling is using web service. During first launch. Or as per requirement.

Best place for save image downloaded from web in iOS devices?

My application download images from web, and for obviously performance reason i need to cache it on my devices. So my question is: where is the best place for store that pictures?
I saw a lot of people use NSDocumentDirectory but apple say:
Critical data should be stored in the /Documents
directory. Critical data is any data that cannot be recreated by your
app, such as user documents and other user-generated content.
so i think the image are not in this category. apple also say:
Support files include files your application downloads or generates and that your application can recreate as needed. The location for storing your application’s support files depends on the current iOS version.
so maybe the NSApplicationSupportDirectory is the best place for store it.
but i saw also NSDownloadsDirectory and NSPicturesDirectory. The cache directory is not good for me because it is deleted after app update.
All of this stuff make me a lot of confusion. What is the right place and why?
Good question! Since you don't want to use NSCachesDirectory, NSApplicationSupportDirectory would be a better option, I think.
According to Apple's File System Programming Guide:
Where You Should Put Your App’s Files
To prevent the syncing and backup processes on iOS devices from taking a long time, be selective about where you place files inside your app’s home directory. Apps that store large files can slow down the process of backing up to iTunes or iCloud. These apps can also consume a large amount of a user's available storage, which may encourage the user to delete the app or disable backup of that app's data to iCloud. With this in mind, you should store app data according to the following guidelines:
Put user data in the <Application_Home>/Documents/. User data is any data that cannot be recreated by your app, such as user documents and other user-generated content.
Handle support files—files your application downloads or generates and can recreate as needed—in one of two ways:
In iOS 5.0 and earlier, put support files in the <Application_Home>/Library/Caches directory to prevent them from being backed up
In iOS 5.0.1 and later, put support files in the <Application_Home>/Library/Application Support directory and apply the com.apple.MobileBackup extended attribute to them. This attribute prevents the files from being backed up to iTunes or iCloud. If you have a large number of support files, you may store them in a custom subdirectory and apply the extended attribute to just the directory.
Put data cache files in the <Application_Home>/Library/Caches directory. Examples of files you should put in this directory include (but are not limited to) database cache files and downloadable content, such as that used by magazine, newspaper, and map apps. Your app should be able to gracefully handle situations where cached data is deleted by the system to free up disk space.
Put temporary data in the <Application_Home>/tmp directory. Temporary data comprises any data that you do not need to persist for an extended period of time. Remember to delete those files when you are done with them so that they do not continue to consume space on the user’s device.
NSApplicationSupportDirectory: Location of application support files (Library/Application Support).
NSDownloadsDirectory: Location of the user’s downloads directory.
NSPicturesDirectory: Location of user's Pictures directory (~/Pictures)
My understanding of the question is that the user won't deal with the photos you're caching. So they're the files that are supporting your app, rather than user's photos or downloads. Those folders would be suitable to use when let's say you had a download manager app where you categorized user's downloaded files into pictures, music, videos, etc.

Simperium for syncing binary data using Core Data?

I'm using Simperium to sync instances of my app's data between each other. I went with this over iCloud because I've heard iCloud has a lot of issues with Core Data and as a novice to iOS, I believe Apple makes you pay to use their services (in a form of developer membership). Since my project is academic, there isn't a need for this.
I've set up Simperium to sync data from my app to their servers, and it's syncing well, but it seems it doesn't have support for binary data. My application syncs items and wishlists, and an item always has an image associated to it. The thumbnail I store in Core Data, since it's small (75x75), and the large image using NSFileManager. None of these sync.
Is it possible to enable sync for binary data and/or images stored in the app's sandboxed documents folder using Simperium? I've heard it's experimental, but if it works decently, I'll be glad to enable it. If not, are there any other frameworks that use sync and don't require iCloud to sync Core Data with binary data?
Syncing binary data isn't yet officially supported by Simperium, but you can track this GitHub issue for it. At the moment it won't work experimentally, either.
One strategy that some other developers are using is to sync filenames using Simperium (since they're just strings), and then deal with the corresponding data files on your own, either manually or with the help of another system like Amazon S3.

Resources