Wait for existing iCloud values before setting a value? - objective-c

In my app, I need to share a setting between different devices running the app. I want the first device that install the app to set the master value of the setting, then all other devices should get that setting and not overwrite it.
How do I make sure I first check if iCloud has a value before setting the value? So I don't overwrite an existing one.
Should I wait for NSUbiquitousKeyValueStoreInitialSyncChange event to be sent, and then I can check for an eventual existing value and otherwise set it for the first time? If yes, can I rely on receiving the NSUbiquitousKeyValueStoreInitialSyncChange event? If not, then it might turn out that it don't set the iCloud value at all with this approach.
If I try to set a value before NSUbiquitousKeyValueStoreInitialSyncChange is triggered for the first time, will it be discarded and then the NSUbiquitousKeyValueStoreInitialSyncChange will be triggered with the existing data in the store?
I've heard NSUbiquitousKeyValueStoreInitialSyncChange is not triggered if there is no values in the store when it synces the first time?
I have read the Apple documentation about this and seen answers here on Stack Overflow, but don't understood how to do exactly this.
How can I make sure I don't overwrite an existing value the first time I launch/install the app?

There is no way for you to surely know you have effectively synchronized with the distant store at least once and you should not count on it (imagine there is no iCloud account set up, or no connectivity or iCloud servers are down, etc.: you don't want your user to wait for you to be sure you are in sync with the cloud as it can take forever or even never happen).
What you should do:
when you start, check the store to see if there is a value.
If there is no value, just push your own value.
If the initial sync with the server did not happen yet and there is, in fact, a value in the cloud, this will be considered a conflict by the NSUbiquitousKeyValueStore. In this precise case (initial sync), the automatic policy is to revert your local value and prefer the one in the cloud instead. So your application will be notified by the NSUbiquitousKeyValueStoreDidChangeExternallyNotification of this revert with the NSUbiquitousKeyValueStoreInitialSyncChange reason.
If there was in fact no value in the cloud, your local value will be pushed and everyone will be happy.

Related

Reliability of Google Calendar Push Notifications

I have encountered a problem, that Google notifications quite often do not arrive without any apparent reason. This makes them almost unusable for me, since it seems like they appear just in 60% of cases.
Is this common? Should I stop relying on them and set up a one minute scheduler for syncing event insted?
Thanks for your opinion
AFAIK, notification is not 100% reliable also stated in the document. As an alternative you could use Incremental sync:
Incremental sync allows you to retrieve all the resources that have been modified since the last sync request. To do this, you need to perform a list request with your most recent sync token specified in the syncToken field. Keep in mind that the result will always contain deleted entries, so that the clients get the chance to remove them from storage.
Hope this helps.

Shared CoreData between apps

My question somewhat similar to How to have multiple apps - one Core Data?. But I am not unable to replicate as suggested in the answer.
I have two applications. One applications (1st App) allows user to do all sort of things and save in coredata.
Other application (2nd App) is a service application. Here I want the service to get notified everytime the coredata is updated(any changes like create, delete, update done).
If I use following notification in 2nd App, this notification does not get fired:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(modelUpdated)
name:NSManagedObjectContextDidSaveNotification
object:nil];
If I had an UI based application or even a background application in that case I can use NSDistributedNotification.
But I want something better than distributed notifications.
Please give me some hints so that I can move ahead.
NOTE: This app is not going through AppStore, so Sandboxing doesn't come under consideration.
If you are going to distribute your app yourself, you can do almost anything you want.
You can certainly attach a PSC in each application to the same SQLite file. No problem there.
As for knowing when the file has been modified, you can use kqueue or dispatch_source to monitor when the file has changed. However, just knowing that it has changed does not tell you what has changed, meaning your watcher process will need to refetch to get its MOC updated.
If this is sufficient, then it is probably the easiest route to take.
If you need more granular notification, then there is no getting around having to notify any interested parties what specifically has changed in the store. You can roll your own, use XPC, or simply use NSDistributedNotification (remember the latter is neither safe, nor guaranteed).
What you should do is observe NSManagedObjectContextDidSaveNotification on any MOC that is directly attached to the PSC. In that handler, you will create a notification similar to the one you received. Except, you should merely send sets of object IDs in URI representation.
Now, your observers can get detailed information about what objects were inserted, updated, and deleted.
Again, if you don't want to go through all this, you can use traditional OS mechanisms to observe that the file changed, and just refetch. If you choose this route, one thing you can do to help... Keep a "last modified" date for each object, and index on that attribute. Then, you can at least query objects that have changed since the last time you loaded the database. There are a number of other options to use here... the basic idea is that if you monitor via the OS, you only get told that something changed... you have to figure out what... if that matters.
For sandboxed apps, one of the few solutions available is to share data via an XPC launched daemon.
EDIT
distributedNotification I don't want to use, actually this wont work
for a deomon/service app. And your other point MOCDidSaveNoti is not
observed at all. I tried both before posting this question. – Anoop
Vaidya
Of course NSManagedObjectContextDidSaveNotification will not tell you what has changed in another process. It has no idea what changed, and has no idea about MOCs in other processes. I am sorry I wrote so poorly to make you think it would.
That notification is to be observed in any MOC that is changing the data store, for the sole purpose of propagating that information to other processes. The notification handler should then send a remote notification (however you want to do it... SHM, pipe, message queue, XPC, smoke signal, etc), with the object IDs of all the objects that have changed.
Any process that wants to know about the changed data store will then need to watch for the remote notification (however you choose to send it).
It really does not matter what you want to do... you are limited by what's available.
You have two basic choices:
observe a general change notification that the store has changed (kqueue, dispatch_source, etc). However, all you know is that the store changed, meaning that you will have to perform a complete refetch.
Send a remote notification whenever the store is saved, passing the object IDs of what has changed, and have other processes watch for that remote notification and update their MOC accordingly.

Is this possible to check back which application paste the NSPasteboardItem on the Pasteboard?

I would like to know how can I get this information? I can keep polling, and checking which application is running, and see the pasteboard count at the same time. But I am afraid this way is not 100% accurate. For example, if the user go to app A, and copy things, in the pasteboard, the pasteboard count increase. But the second poll request did not started yet, and the user switch to to app B, then, the poll start, it may mess up the logic. So, any way suggested to find out the NSPasteboard is from which app? Thanks.
No, this is not supported. One hacky way would be to have a system wide keyboard hook to detect copy (ctrl+c) operation. When user presses this key, you can check the foreground process.

How to discard the change set once delivered in RTC

I accidentally delivered the change set which include some additional config files having local system specific configuration in RTC. Is there any way to discard those changes once delivered? I mean the changes should not be come as incoming changes to other team members.
Please provide any pointer if you have.
Is there any way to discard those changes once delivered?
Not exactly: once delivered, that change set will come to the other team members as incoming.
There are two solutions:
revert the stream configuration to a state previous to your deliver. That is easy only if you are delivering baselines in addition of change sets, because you can then open the stream, and in the "component" section click on "Replace with", and replace the delivered baseline with the previous one.
But... if you never delivered baselines (and delivered only change sets), this isn't easy at all.
You can try and follow "Is there a way to create a RTC snapshot or baseline based on a past date?", but that is quite tedious.
Plus, if your colleagues already accepted your change set and started delivering change sets of their own, this solution isn't recommended at all.
Or, much simpler, you create a new changeset which will cancel the one you just deliver.
Right-click on your component, and select show > history, then right click on the latest change set you incorrectly delivered, and select revert.
That will create a patch.
Right-click on that patch, and select "apply to your workspace": that will create a change set which is the negative image of the one already delivered.
Deliver that new change set.
That means your colleagues will have to accept both change sets: the incorrect one, and the new one which cancels it.
This thread introduces a variation of the first alternative:
you can really remove the change set from the stream you delivered it to.
You can do this by:
discarding the change set from your local workspace
and then replacing the content of the stream with the content of your workspace for the particular component that's affected.
This is a more risky solution, because it really replaces the content of the stream with whatever you have in your workspace... it will remove anything in the stream that you don't have in your workspace. To do this:
a. Accept any incoming changes from the stream you are working with (to prevent losing anyone else's work).
b. Right click on the owning component in the Pending Changes view and select Show->History. The change set will appear in the History view.
c. Right click on the change set and choose Discard... This will discard the change set from your workspace.
So your workspace should now have all change from the stream except the one you want to remove. You can verify this by checking that your bad change set is the only thing you see Incoming.
d. Right click on the component and choose "Replace in [your stream name]..."

iCloud KeyValue store not recognized on first launch

My app uses iCloud (key-value store) to sync an unique id between multiple devices. This works accept at the point were it really has to work, at the first launch of the app. It looks like the device isn't yet familiar with the values from iCloud at first launch, only after the app is installed and has been running for a while.
I check for the iCloud value in the viewDidLoad function on main view of the app.
So, my questions:
Is this the expected behavior?
If yes, is there another solution?
Could it be the case this is only a problem when running from Xcode, not the shipping version? If so, how to test?
Thanks!
Jasper
I had a similar issue where the NSUbiquitousKeyValueStoreDidChangeExternallyNotification wasn't triggering on the first launch after install, no matter how long I waited. Setting an initial key in the NSUBiquitousKeyValueStore seemed to solve this.
Immediately after adding the observer to the default store, I call:
[[NSUbiquitousKeyValueStore defaultStore] setString:#"testValue" forKey:#"testKey"];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
I use different keys (i.e. not testKey) for the actual data I want to sync.
When you first run an iCloud enabled App it has to pull all the data from Apple servers. The time it takes to do so depends on many things like what kind of network you're currently on (Edge, 3G, GPRS, WLAN). It also depends on how much traffic the iCloud server currently has to handle so the request to the iCloud server may take a few more seconds, no matter what type of network connectivity you have.
To sum it up: Yes, it sounds absolutely normal.
If running your App depends on those settings consider implementing a "Wait" or "Loading" view that stays on the screen as long as it takes for the App to perform a initial synch and load all needed data from the cloud. To not block the UI forever also implement a timeout for this view (if iCloud data not loaded within X seconds, dismiss the view and notify user).
You have no guarantee NSUbiquitousKeyValueStore would have finished its first synchronization as your app is launched for the first time. (In fact, on iOS 5, it's often starting to sync as you launch your app for the first time).
There is no way to know if the first initial sync has already happened, is ongoing or has finished. You can (in fact should) subscribe to NSUbiquitousKeyValueStoreDidChangeExternallyNotification. The trick is that if your store is empty on the iCloud server, you won't get any notification after the initial sync (since your local store is empty and the cloud store is empty as well, nothing really changed "externally" after the initial sync…)
Normally, you should not rely on the initial sync being done.
Be optimist at launch, even if your store is empty, push your values (e.g. your unique id) right away. If the initial sync happens concurrently or after and there is already some value on the server, your local values will be reset to the server values and you will get the NSUbiquitousKeyValueStoreDidChangeExternallyNotification notification with the NSUbiquitousKeyValueStoreInitialSyncChange reason.
Finally, if you really think knowing about the initial sync being completed or not, please file a bug to bugreport.apple.com and explain why you really need that.