iCloud syncing of Core Data with Ensembles - swift

My plan is to purchase Ensembles 2 (to take advantage of speed/efficiencies etc) but am trying to make sure that I will be able to get it to work (in a test Swift project) first. To do this I am experimenting with v1.
Using the Simple Sync with Swift as a guide I have incorporated ensembles into my xcode project.
The data from the app does appear to be getting stored in iCloud as when I delete the app and then re-add it, leeching and then syncing does restore the correct data from iCloud. My trouble is that testing with a second device (signed in to the same apple/iCloud account) does the same thing with its own data. The data from the 2 devices is never merged. However the data created on each device is restored to its own device after reloading the app.
Does anyone know how this could be?
Am wondering if the problem might be the store url that I am generating. A lot of the Core Data Stack set up is now done automatically in Swift 3+ (NSPersistentContainer) and so these things do not need to be generated by the user. Here is how i am generating the variables for store url and model url to use when setting up my ensemble:
var storeDirectoryURL: URL {
return try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
}
var storeURL: URL {
return storeDirectoryURL.appendingPathComponent("VsSyncTest.sqlite")
}
// Setup Ensemble
let modelURL = Bundle.main.url(forResource: "VsSyncTest", withExtension: "momd")

The only way I can imagine that would happen is if either you are using different iCloud accounts, or you have a different ensemble identifier for each device.
Note also that the iCloud document sharing can be stubborn. Just because the files get added to the container locally does not mean they will immediately transfer to the other device. (CloudKit backend is much better in that respect, but only in E2.)
If you are testing with the simulator, using Debug > iCloud > Trigger Sync in Simulator may help.

Related

Can you share data with NSPersistentCloudKitContainer between multiple apps?

So my question is as written in the title if you could use NSPersistentCloudKitContainer to share data between different apps like if you have a different app for iPad, a different one for iPhone and a different one for Mac and if possible how would you do that?
Thanks in advance!
Yes, you can, even across different platforms (macOS, iOS). I have done that several times, actually. All it takes is the same iCloud bundle ID. Click on Capability to select iCloud. Turn on the CloudKit checkbox button. Then select a specific bundle ID below.
If you are going to use the same cloud container, you must be careful with the data types you use. For example, you shouldn't save UIImage or NSImage there because they are not exactly compatible with each other. In this case, you should save an image as Data.
Addition
When you want to use a specific cloud container other than the (default) one that your project has automatically created, make sure you specify it in accessing the cloud database. That's the same for macOS and iOS.
let cloudContainer = CKContainer(identifier: "iCloud.com.tomato.Eltomato")
let publicDB = cloudContainer.publicCloudDatabase

CloudKit automatic background backup

I'm currently working on a project that uses CoreData for data saving in swift, and for the purpose of synchronization I wanted to use iCloud, and the first thing I thought was on the CoreData iCloud implementation, but since it's now marked as deprecated I started using CloudKit, and everything works fine until I try to make a backup of the information after an internet connection is stablished (in case of failure when the information should be updated), the app does not store files, just data, all CKRecords are working fine.
My questions are:
Can you synchronize the information after the application is terminated?
Does CloudKit connect to the automatic iCloud synchronization, the one that happens when the device is plugged and has an internet connection.
The current structure is:
CoreData for local saving and withdrawal of data, and CloudKit for Cloud synchronization.
So far I understand that: CloudKit does not interact directly with CoreData and all actions involving data synchronization must be done with the Api calls.
Thank you for your help and if I forgot something please let me know.
1) When your app is terminated it isn't running so you can't sync anything. You would need to re-launch your app (possibly into the background?), but there are some serious limits to what you can do to have that happen without user intervention. Here's a couple threads that might shed more light: Launch app in background automatically? and Will iOS launch my app into the background if it was force-quit by the user?
2) Out of the box CloudKit doesn't do anything 'automatically', it does what you tell it to do. You will need to set it up to fire at appropriate times in your code (when something changes), and in response to push notification for changes from other devices.

Swift UserDefault :sharing data via share group doesn't work

just started swifting recently and got an issue by using app group to share data between iOS devices.
basically I have setup the project followed the steps below:
[iPhone]
Enabled App Group for the iPhone target
initialed data below(groupID is matched what I set in the project):
sharedDefaults = NSUserDefaults.init(suiteName: groupID)
sharedDefaults?.setObject("User Default Shared String", forKey: "test")
sharedDefaults?.synchronize()
double checked the test string by loading user default locally, which is able to display in the Log.
let t_sharedDefaults = NSUserDefaults.init(suiteName: groupID);
t_sharedDefaults?.synchronize();
let str = t_sharedDefaults?.valueForKey("test") as! String;
print(str);
[watch extension]
Create a new watch extension target.
Enable App Group.
Load the User default data under awake with context via the code below:
sharedDefaults = NSUserDefaults.init(suiteName: groupID);
sharedDefaults?.synchronize();
let str = sharedDefaults?.valueForKey("test")
print(str);
I have run the iPhone target first, and then the watch app.
However, the watch app is not able to read expected data from the user default.
I have also uploaded the test project in Github, please let me know if you have any thought on this issue.
[Github]
https://github.com/mattcn/WatchOS_DataSharing
Thanks for your help in advance.
In Watch OS1 this worked, but in Watch OS2 there has been some changes. You need to use something called WatchConnectivity to send the data you want to save to the watch.
so even if you use shared user defaults or app groups the data you put inside them will be on the watch and so not accessible from the iOS app.
To send data to the watch you can use the WatchConnectivity framework.
Thanks Ashish to bring the difference between OS1 and OS2 up.
just found a good reference from:
NSUserDefaults(suiteName:) on iOS 9 and WatchOS 2 - not working?
In watchOS 2 you need to keep in mind that there is 2 different processes running:
Apple Watch Process
iPhone Process
Both of these processes have their own sandbox that's why they call it "native", so if you try to use the shared NSUserDefaults it will not work because the Apple watch app has a completely different sandbox than the host iPhone app.
If you want to save something from your phone to the NSUserDefaults on the Apple watch Target:
Use WatchConnectivity to send the data you want to save to the watch. Then when the watch receives the data you sent to it, save it to the Apple watch's default NSUserDefaults.

Core Data (Magical Record) + WatchKit Extension + Cocoa Touch Framework

Here's what I'm up to:
I now have
An iPhone app
A WatchKit Extension
A Cocoa Touch Framework that holds all my shared classes
What I would like to accomplish, is having a persistent storage (Core Data) that is shared between my iPhone app and WatchKit Extension.
So this is what I've done so far
Create an app group to have a shared container.
Add a Core Data Model (Model.xcdatamodeld) to my Cocoa Touch Framework.
Created one Entity in this model
Created an NSMangedObject subclass for this entity and added is to my Cocoa Touch Framework
Added a DataManager class to my Cocoa Touch Framework
Here what the initializer in my DataManager looks like
public init() {
let sharedContainerURL: NSURL? = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.com.company.Project")
if let sharedContainerURL = sharedContainerURL {
let storeURL = sharedContainerURL.URLByAppendingPathComponent("Model.sqlite")
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreAtURL(storeURL)
let station: Station? = Station.MR_createEntity()
}
}
The issue I'm running into
When I init my DataManager from the iPhone app's AppDelegate, no crash occures, but station will be nil.
When I replace the last line with let stations: [Station]? = Station.MR_findAll() as? [Station] the app crashes and shows the following error: A fetch request must have an entity.
I've searched all of SO and Magical Records issues on GitHub, but couldn't find anything to push me in the right direction. All help is much appreciated.
Thanks to Leo Natan's comment I now realize that I should store my core data in both my iPhone app's sandbox as well as in my WatchKit app's sandbox. And not inside a shared container, like I was trying to.
When building for Watch OS 2, I'll be able to use the WatchKit Connectivity Framework to keep both databases in sync. In the meanwhile I could use a solution like MMWormhole to achieve the same thing.
I have done my task in my live app with watch and iPhone both. i not needs 2 stores. and MMWormhole is good one to help for instant call back both the sides. also i have handled watch's event by handleWatchKitExtensionRequest.
The sync is proper and works well.
I have followed this forum. - http://www.makeandbuild.com/blog/post/watchkit-with-shared-core-data
Hope this may help you.

Icloud Sync between 2 app having different app identifier

I have an app which has different app identifier for iphone and ipad and i want to sync both of them using icloud, is it posible to use different app identifer for same container in icloud.
Yes, it is possible to sync data between different apps using iCloud (different apps always have different app IDs, don't they?). Use the entitlements of your apps to specify to which containers they should have access. Then use a common container to store and sync your files.
Update on 10/30/2013 on request of Dinesh Kaushik:
Open your project in Xcode and select the project in the ProjectNavigator
Go to the "Capabilities" Tap (Xcode 5) of the project and find the iCloud section (should be right on the top)
Here you can specify all Ubiquity Container the app should have access to.
To share data between apps they need to share a common Ubiquity Container. Thus add at least one Ubiquity Container that is used by
all participating apps. The easiest way to just one Ubiquity
Container (of course the same in each app)
When "connecting" to iCloud you use the following code to get the Ubiquity URL:
NSURL *cloudURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
Using nil as identifier will return the URL of the default Ubiquity Container. Use a concrete ID to get the URL of another Ubiquity Container.
The Ubiquity Container is basically just a folder on your device. The iCloud Service will observe this folder and transfer all changes to the same folder on other devices.
From my experience there is one golden rule about iCloud:
DONT USE IT!
I am serious. iCloud is unstable and unreliable. Your app might crash from time to time and you will never find out the concrete source of the problem. Users will ask why syncing does not work from time to time and blame you and your app for the poor performance.
iCloud might be OK for simple KeyValueStore but anything else is a mess. Do not wast your time. Use another Cloud service like Dropbox instead.