ios8 Core Data iCloud Today Widget not synchronizing - core-data

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.

Related

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
}

files in server root not loading properly in Azure

I'm working on an app that instead of a database uses file system in the server's root directory. It's basically a note application that allows me to save notes. Each note is a serialized object of Note class represented by following structure \Data\Notes\MyUsername\Title.txt
When I'm testing this on localhost through IIS Express everything works fine and I can easily go step by step there.
However, once I publish the app to Azure, the folder structure is still there (made a test Controller that uses Directory.GetFiles() and .GetDirectories() to simulate folder browsing so I'm sure that the files are there) but the file simply doesn't get loaded.
Loading script that's being called:
public T Load<T>(string filePath) where T : new()
{
StreamReader reader = null;
try
{
reader = new StreamReader(filePath);
var RawDB = reader.ReadToEnd();
return JsonConvert.DeserializeObject<T>(RawDB);
}
catch
{
return default(T);
}
finally
{
if (reader != null)
reader.Dispose();
}
}
Since I can't normally debug the app on Azure I tried to dump as much info as I can through ViewData and even there, everything looks okay and the paths match, but the deserialized object is still null, and this is only when trying to open an existing note WITHOUT creating a new one first (more on that later)
Additionally, like I said, those new notes get saved in the folder structure, and there's a Note sidebar on the left that allows users to switch between notes. The note browser is nothing more but a list that's collected with a .GetFiles() of that folder.
On Azure, this works normally and if I were to delete one manually it'd be removed from the sidebar as well.
Now here's the kicker. On localhost, adding a note adds it to the sidebar and I can switch between them normally.
Adding a note on Azure makes all Views only display that new note regardless of which note I open and the new note does NOT get stored in the structure (I don't know where it ended up at all!) even though the path is defined at that point normally and it should save just like it does on localhost.
var model = new ViewNoteModel()
{
Note = Load<Note>($#"{NotePath}\{Title}.txt"), //Works on localhost, fails on Azure on many levels. Title is a URL param.
MyNotes = GetMyNotes() //works fine, reads right directory on local and Azure
};
To summarize:
Everything works fine on localhost, Important part doesn't work on Azure.
If new note is not created but an existing note is opened, Correct note gets loaded (based on URL Param) on Localhost, it breaks on Azure and loads default Note object (not null, just the default constructor data since it's required by JsonConvert)
If a new note is created, you'll see it on Localhost and you'll be able to open all other notes regardless, you will see only the new note on Azure regardless of note picked.
It's really strange and I have no idea what could cause this? I thought it had something to do with Azure requests being handled differently so maybe controller pushes the View before the model is initialized completely but that doesn't make sense since there's nothing async here.
However the fact that it loads a note that doesn't exist on the server it's even more apsurd and I have no explanation for that.
Additionally this issue is not linked with a session. I logged in through my phone and it showed the fake note there as well right away.
P.S. Before you say anything about storage, please note this. Our university grants us a very limited Azure subscription. Simple lowest tier App service and 5DTU SQL server and 99% of the rest is locked out of our subscription. This is why I'm storing stuff on the server, not because I believe it's the smart thing to do.

Using same iCloud enabled CoreData store across Watch Extension and iPhone

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.

Getting EXC_BAD_ACCESS(code=EXC_i386_GPFLT) when migrating existing tests to XCTest

I have been using GHUnit with it without any problem to test a library.
This library basically perform calls to an API and use CoreData to decrease the number of API calls.
Then, I decided to switch to XCTest:
Created the test Target
Created the test class
Tests runs fine! wheeee!!!
However anything I try to do that involves CoreData I get a error: EXC_BAD_ACCESS(code=EXC_i386_GPFLT)
It does not happens with GHUnit, it is the same code!
Also, it runs if I set the "Host Application".
I know that the error is related to trying to access an address that the code is not suppose to, but there's no more details.
I see that in the DatabaseManager the line:
NSString *modelPath = [bundle pathForResource:bundlePath ofType:#"momd"];
Returns nil, but, as I said it runs fine when using GHUnit
or simply using the library.
I feel like I am missing some property in the project settings.
I have added the log here:
https://gist.githubusercontent.com/wilsolutions/96e3ae1310ccae86d344/raw/03ea1dfcdab75fc215baaba9d07123bd2e915617/gistfile1.txt
tkx

Why doesn't my onsubmit callback work in Linux?

I have created a file upload system which allows the user to
upload .txt,.xls and .csv formats.When I executed in windows it is
running fine but now when I deployed the application in linux(Debian)
using tomcat server I am facing this issue:
The onsubmit method is called for all the file formats.
onSubmitComplete method is called only when text files are uploaded.I
want to use onSubmitComplete event as I am using progress bar and
wanted to perform necessary action on this event.
Here is my code:
file = new FileUpload();
form = new FormPanel();
file.setName("file");
file.setTitle("select a file");
submit = new Button("Upload");
submit.setTitle("upload file");
form.setEncoding(FormPanel.ENCODING_MULTIPART);
form.setMethod(FormPanel.METHOD_POST);
submit.addClickHandler(this);
form.addSubmitHandler(this);
form.addSubmitCompleteHandler(this);
}
Please tell me why the same code is working fine in windows and not in
linux.
It was resolved as it was not platform dependency it was browser dependency.I now installed google chrome and now it is running fine in linux.

Resources