Send array of NSManagedObject to watchOS in sendMessage: reply handler - ios

The following is a test method for communicating between the watchOS and iOS components of my app:
- (void)session:(WCSession *)session didReceiveMessage:(NSDictionary<NSString *,id> *)message replyHandler:(void (^)(NSDictionary<NSString *,id> * _Nonnull))replyHandler {
NSArray *responseArray = #[#"hello", "world"];
NSDictionary *responseDict = #{#"response": responseArray};
replyHandler(response);
}
This works perfectly - in the reply handler on the watch I can log the contents of responseDict and see the objects #"hello" and #"world". However, if I change responseArray to contain NSManagedObject instances (for sending actual data to the watch), the sendMessage error handler is triggered with an error saying Payload could not be delivered. Before I change my database structure to include a uuid for the entities I need to send (so I can send them represented by their UUID in NSString format), I just wanted to check: is it actually possible to send NSManagedObject instances to watchOS?

No, it's not possible to send NSManagedObject instances between contexts, threads, or devices.
A managed object only exists within its managed object context. Its data would be nil, if you tried to access or copy it outside its context.
If your Core Data persistent store is on the phone, but you want to display a managed object's data on the watch, you'd first to move the data from the managed object into another type (e.g., a dictionary), and then send that data to the watch.
See this answer for more details.

Related

WCErrorCodePayloadTooLarge - Payload is too large

I'm trying to make a WatchOS app that can receive images and a title from a iPhone app in SwiftUI.
I create a object that has SecureCoding. The object is called TemplateObject that can have a String and an array of Data.
I convert the images of the iPhone to data and send to Watch as TemplateObject
let data = try! NSKeyedArchiver.archivedData(withRootObject: template, requiringSecureCoding: true)
The image that I was testing has 51kB.
Anyone know what is the problem?
for image transfer to watch always use the WCSession method
- (WCSessionFileTransfer *)transferFile:(NSURL *)file metadata:(nullable NSDictionary<NSString *, id> *)metadata;
we should transfer only a small amount of data through other methods of WCSession

PFFile and JSON?

In my chatting application, I'm using Parse for a user table, getting ID's, images, etc. I recently added this functionality, and I have encountered a problem. When I send a message, I create an NSDictionary with information about the message such as time, message, sender, sender objectId, etc. But, when I try to add the PFFile (image file) associated with the user, I get an error saying that PFFile cannot be converted to JSON (PubNub message format). How can I add PFFile as part of the NSDictionary used in the message to be compatible with JSON, or there might be another way.
I'm not familiar with asynchronous tasks, but in my code, I have a method - (NSDictionary *)parseMessageToDisplay:(NSDictionary *)message {} where the input would be message received from PubNub, and it would return a format better united to be displayed in a UITableView. If I added the ID of the file or user to my dictionary, how could I get my image in UIImage or NSData, and return it from my method in an NSDictionary. Sorry if this post seems long, just trying to provide a lot of information.
In order to use parse.com, PFFile in particular, you'll probably want that NSDictionary to be a PFObject instead. A PFFile reference can be saved as an attribute of a PFObject -- in fact that's the only way it can be saved.
Thanks to #danh for this suggestion, but you really saved me. Apparently Parse creates a URL for all PFFiles and I can just send that URL (NSString *) with my NSDictionary to PubNub, and then in my - (NSDictionary *)parseMessageToDisplay:(NSDictionary *)message method just use [NSData dataWithContentsOfURL:[NSURL URLWithString:imageURLString]]; and get data from that. YAY! No long running confusing asynchronous tasks to made my day terrible!

WatchKit Core Data Sync Up

I have an app structured as follows
iOS App Writes data to Core Data which has a persistent store stored in a shared app group.
The Watch Kit extension is able to read data from Core Data that was written by the iOS app.
The issue I am having is if my iOS app writes data while my watch kit app is open I am not getting updates because the object context is not syncing with the data on the disk.
Is there a way that since my watch kit extension is only reading data to be able to refresh the context and force it to load again from the data on the disk?
My working solution was using MMWormhole to send notification (NSManagedObjectContextDidSaveNotification) from iPhone app to my watch app. In the watch app's controller i used mergeChangesFromContextDidSaveNotification: method of NSManagedObjectContext.
// in iPhone app's notification handler
MMWormhole *wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"your.group.container.identifier" optionalDirectory:nil];
[wormhole passMessageObject:notification identifier:#"your notification identifier"];
// in WKInterfaceController's awakeWithContext: method
MMWormhole *wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"your.group.container.identifier" optionalDirectory:nil];
[wormhole listenForMessageWithIdentifier:#"your notification identifier" listener:^(id messageObject) {
[self.managedObjectContext mergeChangesFromContextDidSaveNotification:messageObject];
}];
Then NSFetchedResultsController did all other work with UI updates.
You must implement initWithCoder: and encodeWithCoder: methods from NSCoding protocol for your NSManagedObject subclasses because MMWormhole uses NSKeyedArchiver as a serialization medium.
- (id)initWithCoder:(NSCoder *)decoder {
NSManagedObjectContext *context = ... // use your NSManagedObjectContext
NSPersistentStoreCoordinator *coordinator = ...; //use your NSPersistentStoreCoordinator
NSURL *url = (NSURL *)[decoder decodeObjectForKey:#"URIRepresentation"];
NSManagedObjectID *managedObjectID = [coordinator managedObjectIDForURIRepresentation:url];
self = [context existingObjectWithID:managedObjectID error:nil];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:[[self objectID] URIRepresentation] forKey:#"URIRepresentation"];
}
I ran into the same issue. I used - (void)refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag in the NSManagedObjectContext to get the latest data for the managed object.
Ran into similar issues. Despite creating a shared Fetched Results Controller in the App Group observing Managed Object Context changes and refreshing Managed Object Context were not feasible.
Managed Object Contexts cache a certain level of the object graph for retrieval without reading from the actual SQLite store on the disk. The only potential way to actually get live sync between the two would be sending messages across iOS app to Extension when the MOC changes and destroying/rebuilding the Core Data stack from the disk every single time, not a great solution at all.
I believe the use case for live sync between iOS and Extension at initial launch isn't a necessity. Hopefully we get a more deliberate solution to this problem in a future release.

iOS Core Data data insert

I have an app which asynchronously downloads a JSON file and then it should insert those objects within Core Data for persistent storage. Regarding the insert, is it a good idea to do it from the main thread? What if there are thousands of objects? Should I do the inserts on a different thread? Could you provide me with some snippets regarding this matter? Regarding the fetching of the objects after I've saved them, should I also use a different thread?
My code for inserting into Core Data is:
- (void) insertObjects:(NSArray*)objects ofEntity:(NSString *)entityName
{
NSString *key;
NSManagedObject *managedObject;
NSError *error;
for(NSDictionary *dict in objects){
managedObject = [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:_managedObjectContext];
for(key in dict){
[managedObject setValue:dict[key] forKey:key];
}
}
[_managedObjectContext save:&error];
}
PS: The objects are of the same entity. The project runs on iOS 7.0 or higher.
Since I can't comment yet..
What iOS Versions do you plan to support? If 5 and higher, this might help Concurrency stack
Summary of the link:
you create a context of private concurreny type to access your physical data
based on this you create a context of main concurreny type
on top of this you use private concurrency type stores again.
Don't forget so save in every store, otherwise, the data seems to be saved while the app is running, but after restart it is lost.
And yes, you want to do it an extra thread, since it would otherwise block the UI if there are to many items.

Having trouble saving image to entity field on IOS

On my server I have a table that I want to get over to IOS sqllite table.
My server table has a field called data which is of type Image. The way I populated the field is that I wrote a C# app that converts an image to byte array and then write this byte array to sql Image column.
In IOS, I make a soap request to my wcf service and get all data from my table. I made sure data is received. My problem is writing received image data to my entity's binary data field. I use the following code for that.
NSString *key = (NSString *) [keys objectAtIndex:i]; // I made sure key is valid
NSData *data = (NSData *) [rowData GetValue:key]; // I made sure data is retrieved
[tblRow setValue:data forKey:key]; // After calling this, data for the key is nil.
Portion of Image Data Content
/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAMdA2oDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDn7DRkuEtmmnETXUqww5UkM7dF4HGffAq/L4QC3VzaRyGW6tmjWeNUY7C4JXJAxyFP074qx4aa11nTbGKS5jjjYyBFLAOzNEygqO7KWDf8Brobi3ggWWS91aCG4ZIbi6mJ2vuWSYtIF6hS06qvptA9KmxVzh5fBt3dfuHbCBvmCnqQfu/yH403UPCGoTQeWbYSxbvLOMAJ2/Ou2i00K9jbyWlqv2SKSMNbRY3Myoqy4P8AECpOfpzxVWfS/
I am not the one writing the WCF Service that sends me the image data and I learned that the service applies base 64 encoding to the data and setting base 64 string to NSData object and trying to save it was failing. Once I decoded the data, everything worked fine.

Resources