Using same iCloud enabled CoreData store across Watch Extension and iPhone - core-data

I'm witnessing some strange behaviour when opening iCloud Enabled CoreData store from Apple Watch Extension.
I'm using the same iCloud Container across all targets.
Here is a picture that shows what folder (ubiquity container) structure looks like inside the ubiquity container :
It looks like it creates different stores for iPhone & Watch
I'm sharing the same CoreData Stack between iPhone app & Watch Extension. Any ideas why this is happening ?
If I understand this correctly it treats iPhone app & Watch Extension as a separate users ?
I would really appreciate if someone could give an advice.

You should use app groups to share the same Core Data store between Watch and iPhone. Enable app groups for both targets, configure it in your provisioning profiles and then get your persistent store URL like this:
NSURL *storeURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:appGroupIdentifier];
The watch would be accessing the Core Data store via a WatchKit extension also enabled for app groups. See e.g. Figure 4.1 in Apple's App Extension Programming Guide.

Consider having your WatchKit Extension use openParentApplication to communicate with the parent app. Using openParentApplication is simple to implement and helps keep the code in the WatchKit extension simple and fast.
From the WatchKit Extension InterfaceController, call openParentApplication.
NSDictionary *requst = #{#"request":#"myRequest"};
[InterfaceController openParentApplication:requst reply:^(NSDictionary *replyInfo, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
// DO STUFF
}
}];
Then, reply from the app using
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply{
Consider also using JSON data (NSJSONSerialization) in the main app to respond to the watch extension.

Related

Use public CloudKit database (NSPersistentCloudkitContainer) - macOS

I’ve written an iOS app in SwiftUI (using the SwiftUI life cycle in Xcode 12) that uses my CloudKit container’s public database with the following lines of code:
let container = NSPersistentCloudKitContainer(name: "my_ios_container_name")
guard let description = container.persistentStoreDescriptions.first else {
print("Can't set description")
fatalError("Error")
}
description.cloudKitContainerOptions?.databaseScope = .public
I’m in the process of writing a macOS app in SwiftUI that accesses the same CloudKit container’s public database. But when I try to build the code below in my mac app's AppDelegate.swift file, Xcode gives me an error saying that it’s unable to find the ‘databaseScope’ member. Is this member variable only available for iOS apps? If this is the case, how do you access a Cloudkit container's public database in a macOS app?
Try adding "import CloudKit" to the top of the file.
Resolved this issue for me.

Core Data and CloudKit integration issue when renaming relationship (code 134110)

I currently have an app using Core Data in the App Store: the app allows people to record their water and sailing activities (think of it like Strava for sailors). I have not updated the app for 3 years, the app seems to be still working fine on latest iOS versions but I recently planned to improve the app.
I am currently working on an update for this app, and need to change the data model and schema. I would like to have an automatic lightweight migration. I renamed some entities, properties and relationships, but I made sure to put the previous ids in the Renaming ID field in the editor.
I want to take advantage of the opportunity to sync the updated schema on CloudKit. I followed the instruction on Apple Developer documentation to setup the sync. I also made sure to initialize the schema using initializeCloudKitSchema(). When I visit the dashboard, I see the correct schema. The container is only in development mode, not pushed into production.
When I launch the app with a sqlite file generated by the available app, it seems the migration works well because the data is still here and correct. I can navigate in the app normally and when I visit the CloudKit dashboard, the data is correctly saved.
But sometimes, the app crashes at launch with the following error:
UserInfo={reason=CloudKit integration forbids renaming 'crewMembers' to 'sailors'.
Older devices can't process the new relationships.
NSUnderlyingException=CloudKit integration forbids renaming 'crewMembers' to 'sailors'.
Older devices can't process the new relationships.}}}
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration."
The concerned entities were renamed, as the relationships and the relationship is a many-to-many, optional on both sides. This is occurring even if I reset the CloudKit development container. I don’t really have a clear idea of when this is appearing (seems random, after I updated some data or after I update the Core Data model). Any idea why the app is crashing? I would like as much as possible to keep the new naming for my entities and relationships.
SKPRCrewMemberMO renamed to Sailor
SKPRTrackMO renamed to Activity
crewMembers <<--->> tracks renamed sailors <<--->> activities
Here are some screenshots of the previous and updated data model for the entity at the origin of the migration issue, as well as some code regarding my Core Data stack initialization and the console error il getting.
PS: the app is used by few hundreds of people. That’s not a lot, but still, some of them have dozens of recorded activities and I don’t want to break anything and lose or corrupt data. I could launch a new app but users would lose their progress as it’s only saved locally in a shared container (app group was used as I wanted to share the Core Data with an Apple Watch extension). And I would lose the user base and App Store related things.
private init() {
container = NSPersistentCloudKitContainer(name: "Skipper")
guard let description = container.persistentStoreDescriptions.first else {
fatalError("###\(#function): Failed to retrieve a persistent store description.")
}
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
let id = "iCloud.com.alepennec.sandbox20201013"
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: id)
description.cloudKitContainerOptions = options
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
do {
try container.initializeCloudKitSchema()
} catch {
print("Unable to initialize CloudKit schema: \(error.localizedDescription)")
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
}

ios8 Core Data iCloud Today Widget not synchronizing

I have been unsuccessful in getting core data to work on an app and today widget on my device.
let url = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.mygroup.name").URLByAppendingPathComponent("fileName.sqllite")
var error: NSError? = nil
let options = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true,NSPersistentStoreUbiquitousContentNameKey:"SharedContainerName"
]
let s = coordinator?.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options
, error: &error)
I have added a group container that I use for the URL of the stores. I have noticed on the simulators that my persistent coordinator points to the same sqllite file
(URL: file:///Users/xxxx/Library/Developer/CoreSimulator/Devices/6Cxxxxxx/data/Containers/Shared/AppGroup/E65xxxxxx/fileName.sqllite))
This seems to work fine on the simulator and I can store data in my main app and fetch it in today widget. When I run the code on my device the files are at different locations and the databases are not synchronized (no data on the today widget).
My Main App
(URL: file:///private/var/mobile/Containers/Shared/AppGroup/2CCXXX/CoreDataUbiquitySupport/mobile~F74XXX/SharedContainerName/0E8XXXX/store/fileName.sqllite))
Today Widget
(URL: file:///private/var/mobile/Containers/Shared/AppGroup/2CCXXX/CoreDataUbiquitySupport/mobile~F74XXX/SharedContainerName/2FBYYYY/store/fileName.sqllite))
I am assuming this should be fine as they should be synchronized by iCloud. The widget runs fine, however it has no data (like it has not been synchronized). Now debugging this has been tricky as I am unable to get console output while running the today widget. When I run the widget from Xcode as opposed to attaching to the running process (The only way I can get any output on the console) I receive an error core data iCloud: Error: initial sync notification returned an error BRCloudDocsErrorDomain error 12. I receive no notifications. Maybe iCloud and Core Data do not work at this time with a today widget? The core data code in my app and extension are identical so I do not think I have a bug.
According to this Apple Developer Forum message from an Apple employee:
None of iCloud is accessible from within an Extension in iOS 8.0
and he adds in another message:
Document syncing, I should clarify, or anything which requires file coordination. I'm not sure about KVS or CloudKit
The recommendation is to expose the application state to extensions using some other method (plist, separate files, etc), which is a bit of a bummer.

How to Upload images from local folder to Sitecore

`webClient.UploadFile("http://www.myurl.com/~/media/DCF92BB74CDA4D558EEF2D3C30216E30.ashx", #"E:\filesImage\Item.png");
I'm trying to upload images to sitecore using webclient.uploadfile() method by sending my sitecore address and the path of my local images.But I'm not able to upload it.I have to do this without any API's and Sitecore Instances.
The upload process would be the same as with any ASP.net application. However, once the file has been uploaded you need to create a media item programtically. You can do this from an actual file in the file system, or from a memory stream.
The process involves using a MediaCreator object and using its CreateFromFile method.
This blog post outlines the whole process:
Adding a file to the Sitecore Media Library programatically
If you're thinking simply about optimizing your developer workflow you could use the Sitecore PowerShell Extensions using the Remoting API as described in this this blog post
If you want to use web service way than you can use number of ways which are as follows:
a) Sitecore Rocks WebService (If you are allowed to install that or it is already available).
b) Sitecore Razl Service(It is third party which need license).
c) Sitecore Powershell Remoting (This needs Sitecore PowerShell extensions to be installed on Sitecore Server).
d) You can also use Sitecore Service which you can find under sitecore\shell\WebService\Service.asmx (But this is legacy of new SitecoreItemWebAPI)
e) Last is my enhanced SitecoreItemWebAPI (This also need SitecoreItemWebApi 1.2 as a pre-requisite).
But in end except option d you need to install some or other thing in order to upload the image using HTTP, you should also know the valid credentials to use any of above stated methods.
If your customers upload the image on the website, you need to create the item in your master database. (needs access and write right on the master database) depend on your security you might consider not build it with custom code.
But using the Sitecore webforms for marketers module With out of the box file upload. Create a form with upload field and using the WFFM webservices.
If you dont want to use Sitecore API, then you can do the following:
Write a code that uploads images into this folder : [root]/upload/
You might need to create folder structure that represent how the images are stored in Sitecore, eg: your images uploaded into [root]/upload/Import/ will be stored in /sitecore/media library/Import
Sitecore will automatically upload these images into Media library
Hope this helps
Option: You can use Item Web API for it. No reference to any Sitecore dll is needed. You will only need access to the host and be able to enable the Item Web API.
References:
Upload the files using it: http://www.sitecoreinsight.com/how-create-media-items-using-sitecore-item-web-api/
Enable Item Web Api: http://sdn.sitecore.net/upload/sdn5/modules/sitecore%20item%20web%20api/sitecore_item_web_api_developer_guide_sc66-71-a4.pdf#search=%22item%22
I guess that is pretty much what you need, but as Jay S mentioned, if you put more details on your question helps on finding the best option to your particular case.
private void CreateImageIteminSitecore()
{
filePath = #"C:\Sitecore\Website\ImageTemp\Pic.jpg;
using (new SecurityDisabler())
{
Database masterDb = Sitecore.Configuration.Factory.GetDatabase("master");
Sitecore.Resources.Media.MediaCreatorOptions options = new Sitecore.Resources.Media.MediaCreatorOptions();
options.FileBased = true;
options.AlternateText = Path.GetFileNameWithoutExtension(filePath);
options.Destination = "/sitecore/media library/Downloads/";
options.Database = masterDb;
options.Versioned = false; // Do not make a versioned template
options.KeepExisting = false;
Sitecore.Data.Items.MediaItem mediaitemImage = new Sitecore.Resources.Media.MediaCreator().CreateFromFile(filePath, options);
Item ImageItem = masterDb.GetItem(mediaitemImage.ID.ToString());
ImageItem.Editing.BeginEdit();
ImageItem.Name = Path.GetFileNameWithoutExtension(filePath);
ImageItem.Editing.EndEdit();
}
}

how to sync core data icloud with ios and macOs app

I'm developping an iPad application that uses CoreData with iCloud. It works great!
I'm able to open ~/Library/Mobile Documents" with the folder that matches my Team ID and iCloud container.
I'm making also a macOS app that needs to access the iCloud. In macOS app, I added the iOS app container ID. So i have two container ID: ca.company.MacContainerID and ca.company.IPadContainerID which works for iPad app.
When i execute the following code in MacOS app, URLForUbiquityContainerIdentifier: returns nil.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *containerID = #"A1B2C3E4F5.ca.company.IPadContainerID";
NSURL *url = [fileManager URLForUbiquityContainerIdentifier:containerID];
url is nil
I don't know what to do to access to iCloud with core data inside from iPad app.
Do you have an idea ?
It must be a problem with your containerID. Did you double-check the ID string?
I found the solution! I was building the Mac application with an iOS provisionning file. So i put a Mac provisioning and it works; URLForUbiquityContainerIdentifier return me a non nul value.

Resources