iOS synchronization - what is a good way for a Core Data object to know it has to be pushed to server? - objective-c

I am building sync functionality between an iPad and a web server. I'm using an approach pretty close to the one described here. I only have one type of object, let's called it a Story, that has to be synchronized. It is a Core Data entity (managed object).
The remaining problem I have to solve is knowing "whenever something changes and needs to be synchronized to the server." One approach would be to go find every piece of code that modifies a Story and have it also set some needsSyncing flag. That does not seem elegant and it seems that over time, developers could forget to update the flag for new types of modification.
Do Core Data objects have a way to observe themselves, so any time any change is made, a particular method is executed? That would make this pretty easy.
Another option might be using the isUpdated method right before doing a save operation on the managed object context. You'd either have to have save called in only one place or do this at every place you save (sounds like the first option). I guess I could make a helper method that goes through all Story objects right before saving and see if any of them need their flag to be set. The only drawback to that is that I'd be traversing all Story objects in the system for any save, even for saves that have nothing to do with a Story.
Anyway I'll stop trying to guess the solution out loud - does anyone have experience with a good way to do this?

SDK has you covered. See the NSManagedObjectContext class reference, at the very end of the page, the MOC will post notifications that you can subscribe to, including NSManagedObjectContextObjectsDidChangeNotification. You can listen for these and do the update call pretty much coincident with saving the MOC.

Related

Correct approach to Add or Edit with CoreData Managed Objects

A common scenario is to have a view that allows to Add or Edit an underlying model,
we could simply source the view with a initWithObject:(MyManagedObject*)object and
create a new one if object is nil.
Now when leaving the view, we can ask the ManagedObjectContext if it is dirty, but how do we know if it is dirty because the object was modified, or due to some other objects that may have been modified someplace else?
And if we want to provide the user with a cancel option, how would one rollback the changes on only the current object (and delete the object if it was just newly created in this view?)
Is it advised to use multiple ManagedObjectContexts? (One for each view? -- in this case synchronization may become an issue, no?) Or should one use the UndoManager? Can this be achieved using undoGroups?
There are three common ways that I am aware of. And most certainly I would love to hear other people to post how they've dealt with this.
Undo Groups
Before presenting a detailed view controller modally (with a save and cancel options), I create an undo group and then create an managed object instance in a managed object context and do the preliminary setup.
On the delegate methods for save and cancel I end undo groups and with cancel I also do a undoNestedGroup.
Good: You can undo edits with ease.
Bad: If there are certain actions beyond that you would not want to cancel, it's going to be very tricky. In other words its a cancel all or nothing approach.
Manual Handling
This is really viable when adding a new item, since it's too hard to track individual edits. It basically means that in your delegate method for cancel you delete the object you know you added before presenting the detailed view controller.
Draft Contexts
This means you'll create another managed object context instance which you either merge back to the main context or throw away with all the changes. As you said, the merging part can be somewhat pain, but it's entirely doable. Compared to undo groups approached this has the benefit of merging back only the changes that you save. So, while still a bit tricky, you can better fine-tune, what changes you want to keep and which you want to cancel.
With iOS5 Apple added nested contexts where you can create a child context easily for the detailed view controller to use and either just save or throw it away. No extra code is necessary to take care of the merging of contexts.
Good: You keep your main context untouched from all modifications until you are ready to save.
Bad: Its more complicated to implement prior to iOS5 (which introduces nested contexts which is awesome).
Best regards,
sven.

Copying pending changes between NSManagedObjectContexts with shared persistent store?

I have two instances of NSManagedObjectContext: one is used in main thread and the other is used in a background thread (via an NSOperation.) For thread safety, these two contexts only share an NSPersistentStoreCoordinator.
The problem I'm having is with pending changes in the first context (on the main thread) not being available to the second context until a -save is performed. This is understandable since the shared persistent store won't have copies of the NSManagedObjects being tracked by -insertedObjects, -updatedObjects, and -deletedObjects are persisted.
Unfortunately, this presents a problem with the user experience: any unsaved changes won't appear in the (time consuming) reports that are generated in the background thread.
The only solution I can think of is nasty: take the inserted, updated and deleted objects from the first context and graft them onto the object graph of the second context. There are some pretty complex relations in the dataset, so I'm hesitant to go in this direction. I'm hoping someone here as a better solution.
If this is under 10.7 there are some solutions: one is you can have nested ManagedObjectContexts, so you can “save” in the one being modified and it won’t save all the way to the disk, but it will make the changes available to other children of the master context.
Before 10.7 you will probably have to copy the changes over yourself. This isn’t super-hard since you can just have a single object listen for NSManagedObjectContextObjectsDidChangeNotification and then just re-apply the changes exactly from the main context. (Should be about 20 lines of code.) You never have to save this second context I assume?
Not sure if you have any OS restraints but in iOS 5 / Mac OS 10.7 you can use nested managed object contexts to accomplish this. I believe a child context is able to pull in unsaved changes in the parent by simply doing a new fetch.
Edit: Looks like Wil beat me to it but yeah, prior to iOS 5 / Mac OS 10.7 you'll have to listen for the NSManagedObjectContextDidSaveNotification and take a look at the userInfo dictionary for the added/updated/deleted objects.
An alternate solution might involve using a single managed object context and providing your own thread safety over access to it, or use the context's lock and unlock methods.
I would try to make the main thread do a normal save so the second context can just merge the changes into his context. "fighting" a APIs intended use is never an good idea.
You could mark the newly saved record with an attribute as intermediate and delete later if the user finally cancels the edit.
Solving those problems with attributes in your entities and querying in the background thread with a matching predicated would be easy...
And that would be a stable solution as well. I am coming from a database driven world (oracle) we often use such patterns (status attributes in records) to make data visible/invisible to other DB sessions (which would equal to threads in an cocoa app). Works always without problems. Other threads /sessions do always only see commited changes that's how most RDBMS work.

How do I share CLLocation throughout my app?

I'm developing an app which has several view controllers which make use of the users current location. Since the users current location should be the same no matter which view I'm in, I think it makes sense to share this location between view controllers. Should I define a CLLocation in my Application Delegate and then set each view controllers usersCurrentLocation to match this? When the location is updated by one view controller, how do I ensure each of the other view controllers pick up the change? To me this sounds like a job for a global variable, but I know they're not a very 'Cocoa' way of doing things. What's the correct way to do this? Code samples would be helpful.
Singletons are a perfectly Cocoa way of doing things, and a "Location Manager" singleton (or whatever you choose to call it) instantiated upon app launch can not only provide the information to anything that needs the information, but can also periodically check for a location change and send notifications to any listeners.
Matt Gallagher does a great job of explaining singletons on his blog, and includes a super-handy macro for doing the heavy lifting.
Instead of assigning a location as property, consider a CCLocationManager as property. Same amount of work and keeps itself up to date... Whenever you need the current location, just send it the location message.
It's a philosophical question. If it's only a little data, then yeah, a named property in your app delegate is a fine way to handle it. Especially if it's a property of the app (like where it's running physically). I don't mind.
More data than that, and I prefer to create a singleton data manager class, so it can be gotten to globally but has its own namespace.
If I were you, I would have all your location management code live in the app delegate itself, and think of individual viewcontrollers as "clients" of the delegate's location manager, picking up the current state from the delegate's .currentLocation (or whatever) property as needed.
If you can handle some iffy accuracy from time to time, you might also consider using the "updates on significant change" mode of CLLocationManager, rather than real live GPS tracking.

Is there a way to use the NHibernate session to figure out if changes need to be written to db?

I'm using NHibernate here with C#. I have a cache of nhibernate objects that have lazily loaded objects inside of those that might have changes written to them. I need a way to determine if there are changes that need to be saved. It's quite a lot of effort to set flags when one little thing changes and also quite annoying to compare a cache with a copy of an original (because of the lazy loading).
Just wondering if there's a way I can use the current session object to know if it has pending changes that are about to be written to the db, this is so that I can have a 'do you want to save' prompt if in fact there are changes. I can't autosave, the customer demands a save button.
NHibernate.ISession exposes an IsDirty() method so you should be able to check that.

iPhone Objective-C Placement of NSNotifications

I have a fairly complex iphone application that has many asynchronous processes occurring. To deal with this I'm making heavy use of NSNotification Center. My question is what is the "best practice", if such a thing exists", for registering the notifications? Right now they sprinkled through my code in a hap-hazard way. I'm about to go to production and I want it cleaned up. I though about having a "registerNotifications" method in viewDidLoad of the main view which obviously registers all notifications in one shot. Does this sound reasonable? If not what would be the preferred way of dealing with this. Thanks in advance for your help!!
Putting all the notifications in one object destroys encapsulation and that is bad. You make one objects notification operations dependent on another object working properly. It will actually be a nightmare tracking all that. It is also backwards to the entire of purpose of notifications which is to create a decentralized and modular messaging system.
Notifications shouldn't be "sprinkled throughout the code" but they should be managed solely by the objects that receive them. If you use a great deal of notifications you might need to create a class with dedicated methods for handling multiple notifications and then have your other classes inherit from that class. That way you get automatic management.
One of the big mistakes people make with notifications is that they register controllers when they really need to register their data model. For example, suppose you're downloading some data from a URL and you want to update the interface as it progresses and/or when it ends. If you have multiple views in your UI and you register controllers, then every view has to manage a notification (of which there may be more than one.) However, if you set up the shared datamodel to receive the notification you need only have a maximum of two notifications. One will go to the datamodel so the datamodel can update itself and then you can have the datamodel issue a generic notification for any listening views to update themselves from the datamodel.
Making your view controller dependent on the datamodel simplifies design greatly in all cases.
An object should register itself as an observer of the notifications it is interested in. The best time to do it is when the object becomes interested, otherwise you have to deal with notifications when you don't want them.