<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>Drew McCormack (@drewmccormack) is founder of The Mental Faculty, developer of Mental Case</description><title>The Mental Blog</title><generator>Tumblr (3.0; @mentalfaculty)</generator><link>http://mentalfaculty.tumblr.com/</link><item><title>Does Core Data Sync Quack?</title><description>&lt;p&gt;After a &lt;a href="/post/24009617665/under-the-sheets-with-icloud-and-core-data-working"&gt;spate of articles&lt;/a&gt; on the topic, it&amp;#8217;s been quite a while since I posted anything about iCloud/Core Data sync. To be honest, I hit some serious stumbling blocks, and ended up giving up on it entirely.&lt;/p&gt;

&lt;p&gt;I did end up shipping &lt;a href="http://mentalcaseapp.com"&gt;Mental Case&lt;/a&gt; for Mac with iCloud sync, but to do so I adopted a &lt;a href="https://github.com/drewmccormack/TICoreDataSync"&gt;customized fork&lt;/a&gt; of the &lt;a href="https://github.com/nothirst/TICoreDataSync/issues/39"&gt;TICDS framework&lt;/a&gt;. Mental Case 2 for iOS is just around the corner, and I am using the same sync solution there. (If you are interested in going that route, I recommend first giving the main repository a try. They pulled back my iCloud changes recently.)&lt;/p&gt;

&lt;p&gt;Since my &lt;a href="http://mentalfaculty.tumblr.com/post/25241910449/under-the-sheets-with-icloud-and-core-data"&gt;original series of posts&lt;/a&gt;, many well-known developers have spoken out on the issues that still plague iCloud when used in combination with Core Data. Some have &lt;a href="http://inessential.com/2013/03/27/why_developers_shouldnt_use_icloud_sy"&gt;spoken out&lt;/a&gt; against the principle of building sync into Core Data directly, and others (&lt;em&gt;eg&lt;/em&gt; &lt;a href="http://blackpixel.com/blog/2013/03/the-return-of-netnewswire.html"&gt;Black Pixel&lt;/a&gt;, &lt;a href="http://rms2.tumblr.com/post/46505165521/the-gathering-storm-our-travails-with-icloud-sync"&gt;Bare Bones&lt;/a&gt;, &lt;a href="http://www.jumsoft.com/2013/01/response-to-sync-issues/"&gt;Jumsoft&lt;/a&gt;) on their first-hand experiences.&lt;/p&gt;

&lt;p&gt;Two years after the technology was first introduced, very few apps have shipped with iCloud/Core Data support. I know of only two — &lt;a href="https://itunes.apple.com/us/app/time-butler/id460856856?mt=12"&gt;Time Butler&lt;/a&gt; and &lt;a href="https://itunes.apple.com/us/app/todomovies/id528977441?mt=8"&gt;ToDoMovies&lt;/a&gt; — each of which has a relatively simple entity model, and very little data to transfer. Perhaps most telling is that the only Apple app that seems to use the technology is &lt;a href="https://itunes.apple.com/us/app/itunes-movie-trailers/id471966214?mt=8"&gt;iTunes Movie Trailers&lt;/a&gt;, which presumably also has a very simple model and little data.&lt;/p&gt;

&lt;p&gt;With WWDC just around the corner, we again start to ponder if this will be the year Apple gets iCloud right. But the more I dig into iCloud/Core Data sync, the more I have come to realize that even if it worked as designed, it still may be quite flawed as a solution. They may have gotten it wrong from the outset, and some design failures are probably not easily addressed. What follows is a list of what I think is fundamentally wrong with iCloud/Core Data&amp;#8217;s design, leaving aside any of the practical failures that we have witnessed in the past.&lt;/p&gt;

&lt;p&gt;iCloud/Core Data issues that may never be resolved:&lt;/p&gt;

&lt;p&gt;1) Brent Simmons has already &lt;a href="http://inessential.com/2013/03/27/why_developers_shouldnt_use_icloud_sy"&gt;criticized&lt;/a&gt; the approach of coupling sync so tightly with Core Data. I don&amp;#8217;t think the problem is that Core Data is involved — after all, what use would an object graph management framework be if it didn&amp;#8217;t help you manage your objects, including synchronization — but I do agree that Core Data sync should not be so tightly coupled to a single storage mechanism, namely iCloud. By not allowing for extension to other storage facilities, Core Data sync is no option for many categories of app, including those not in an App Store, cross-platform apps, apps that require secure data storage, and apps with developers who simply don&amp;#8217;t want to be locked into a single service. In short, sync in Core Data should be an interface that can work with many backends, not just iCloud.&lt;/p&gt;

&lt;p&gt;2) Core Data sync currently offers no developer access to the process. It seems Apple expected to be able to make a completely generic sync algorithm that would work in every case, but that was not realistic. Five minutes thinking through the problems that can arise quickly tells you that it is not possible to come up with generic solutions for many cases. Developer intervention is needed in all but the most trivial examples. For example, there is no way for the developer to influence conflict resolution. If conflicts arise, Core Data either decides how to proceed itself, or halts syncing altogether. Some have endeavored to work within these constraints, using hacks to detect conflicts and recover (&lt;em&gt;eg&lt;/em&gt; &lt;a href="https://github.com/lhunath/UbiquityStoreManager"&gt;UbiquityStoreManager project&lt;/a&gt;, &lt;a href="http://www.atomicbird.com/blog/icloud-complications-part-3"&gt;Tom Harrington&lt;/a&gt;), but these attempts just make it even more evident that the API of Core Data sync is fundamentally flawed.&lt;/p&gt;

&lt;p&gt;3) Because the developer is not invited to the process, the first you get notified of sync changes is &lt;em&gt;after&lt;/em&gt; they are resident in the persistent store. In other words, in many situations, your store will actually contain data in an invalid state. Apple recommends &amp;#8216;cleaning up&amp;#8217; (&lt;em&gt;eg&lt;/em&gt; deduping, applying validation constraints) when the merge notification is fired, but I would argue that this is too late. It is OK for a managed object context to be in an invalid state, but your on-disk store should never been invalid. The fact that Core Data sync requires that your store become invalid is a fundamental design flaw of the API.&lt;/p&gt;

&lt;p&gt;4) Sync has been built into Core Data with minimal changes to the existing API. Basically, there are a few new notifications, and new metadata properties on the &lt;code&gt;NSPersistentStore&lt;/code&gt; class. As a result, many aspects of sync are arguably too tightly coupled to existing classes. For example, if the user logs out of their iCloud account while an app is running, the developer must immediately tear down their Core Data stack, or expect a crash. Not only that, they must migrate data out of the persistent store to a new store, or move to a completely different (un-synced) store. It&amp;#8217;s a big mess, and you are left wondering why. Why does the persistent store even need to know it is actively being synchronized via iCloud? Presumably, a better solution would be to have another class monitoring saves into the store, and handling that completely independently. The Core Data stack should not be so fragile, and should continue to function, with sane solutions to sync interruptions offered.&lt;/p&gt;

&lt;p&gt;5) Related to 4). Changes to iCloud availability make a developer&amp;#8217;s life very difficult, requiring reloading and migration of data stores. Apple produced code to demonstrate how to handle this a year after the technology was first introduced, but it is far from a satisfactory solution. It seems like they didn&amp;#8217;t give much thought to store management until it was too late, and then had to resort to obscure workarounds to make the system work. Offering a new persistent store manager class that handles the most important cases would be more sensible and make the technology a lot easier for developers to adopt.&lt;/p&gt;

&lt;p&gt;6) Take an app like &lt;a href="https://itunes.apple.com/us/app/todomovies/id528977441?mt=8"&gt;ToDoMovies&lt;/a&gt;, turn on iCloud, and mess with it on two devices at the same time. It doesn&amp;#8217;t take long before each device shows different data, and remains that way for all eternity, never ending up back in a fully-synced state. This points to pretty serious flaws in the algorithms being used behind the scenes. I would rather Core Data ensure exactly the same operations end up being applied to each data store, and then require developer intervention to manually massage a potentially invalid state before saving merges, than have data end up systematically out-of-sync.&lt;/p&gt;

&lt;p&gt;Who knows, maybe Apple will get iCloud working well enough without addressing these issues to make it a success. But I doubt it. I fear Core Data sync in its current form is a lame duck.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/51143164677</link><guid>http://mentalfaculty.tumblr.com/post/51143164677</guid><pubDate>Thu, 23 May 2013 08:01:00 -0400</pubDate></item><item><title>Are Prices Too Low in the Mac App Store? </title><description>&lt;p&gt;Since its inception, the Mac App Store has prompted discussion amongst Mac developers over pricing. Will there be a race to the bottom? Is it better to lower prices to reach a broader audience? Those types of questions have been — and continue to be — debated in the corridors of Apple developer conferences the World over.&lt;/p&gt;

&lt;p&gt;At the recent &lt;a href="http://%C3%A7ingleton.com/en/"&gt;Çingleton&lt;/a&gt; conference, ex-Apple employee Michael Jurewitz addressed the issue front on. I wasn&amp;#8217;t actually at the conference, so I have only heard his arguments second hand, but it seems the gist was that most of the top grossing Mac apps have high prices, so Mac developers are encouraged to price their software higher.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s certainly an interesting analysis, but are the conclusions actually well established? Or is it another case of &amp;#8216;damn lies and statistics&amp;#8217;?&lt;/p&gt;

&lt;p&gt;I decided to investigate this further in an impromptu study. I quickly realized that the results depend quite dramatically on exactly who you are. In particular, whether you are an established company like &lt;a href="http://www.apple.com"&gt;Apple&lt;/a&gt; or &lt;a href="http://www.omnigroup.com"&gt;The Omni Group&lt;/a&gt;, or a small indie startup trying to establish a foothold.&lt;/p&gt;

&lt;p&gt;My analysis was far from scientific, but it does point to some interesting conclusions. What I did was go through the Top 100 grossing apps, and I categorized each app as either&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Developed by Apple or another big company (eg Adobe, EA)&lt;/li&gt;
&lt;li&gt;Developed by a well-established company, or existing prior to the Mac App Store&lt;/li&gt;
&lt;li&gt;A new app introduced after the Mac App Store launched&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;I discarded all apps in category 1, because how well Apple and Adobe do has little influence on the rest of us. I took the remaining results, for smaller companies, and tallied them up in the ranges 1-to-50, and 51-to-100, and took an average price for each range and product category. Here is what I found.&lt;/p&gt;

&lt;table&gt;&lt;tr&gt;&lt;th&gt;Product Category&lt;/th&gt;&lt;th&gt;Top Grossing Range&lt;/th&gt;&lt;th&gt;Number in Range&lt;/th&gt;&lt;th&gt;Average Price&lt;/th&gt;
&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan="2"&gt;Established apps&lt;/td&gt;&lt;td&gt;1-50&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;$68.83&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;51-100&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;$55.91&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td rowspan="2"&gt;New apps&lt;/td&gt;&lt;td&gt;1-50&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;$11.99&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;51-100&lt;/td&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;$18.31&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;The first thing you will notice is that the results confirm Michael Jurewitz&amp;#8217;s analysis — there are more higher-priced apps in the top 50. Not only that, but the difference in average price for the ranges 1-50 and 51-100 is minimal, almost certainly within the statistical error of this very imperfect study. Conclusion: established companies have little to win by dropping their prices.&lt;/p&gt;

&lt;p&gt;But that is perhaps asking the wrong question, at least for most of us. What indie developers want to know is: &amp;#8220;If I start developing a new app, should I price it high or low?&amp;#8221; The results for the newer apps shed some light on this. The average price of new apps in the top 50 is about a third lower than the average price of new apps in the 51-100 range. In other words, it seems you could mount an argument that new apps do better when they move to a lower price point.&lt;/p&gt;

&lt;p&gt;I am not going to claim that what I have presented is scientific — for one thing, there is some subjectivity in how the apps are classified — but I think this should at least make us approach the question differently. Comparing indie developers to Apple, or even to well established developers like &lt;a href="http://www.omnigroup.com"&gt;The Omni Group&lt;/a&gt; and &lt;a href="http://www.panic.com"&gt;Panic&lt;/a&gt;, doesn&amp;#8217;t make sense, because the type of software they can produce is vastly different.&lt;/p&gt;

&lt;p&gt;A quick statistical analysis does suggest that of the newer apps, the ones that gross the most are actually priced somewhat lower. Raising the price of your new release is more likely to do harm than good.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/35766790848</link><guid>http://mentalfaculty.tumblr.com/post/35766790848</guid><pubDate>Thu, 15 Nov 2012 04:37:00 -0500</pubDate><category>macappstore</category><category>mac</category></item><item><title>How to Choose Keywords for the App Store</title><description>&lt;p&gt;When an app developer comes to release a new app, after months of development, they are inclined to relax too soon, and gloss over the submission process. But the choices you make when submitting an app can have a big impact on your visibility in the App Store. With almost a million apps vying for attention, details like screenshots, app description, title and keywords can make all the difference.&lt;/p&gt;

&lt;p&gt;Nobody knows exactly what formula Apple uses to rank search results, but it is pretty clear it is a combination of text search, download stats, and ratings. The only aspect you have direct influence on is the text you enter. The title and keywords seem to be paramount. Terms in the full description are either not scanned at all, or weighted much more lightly.&lt;/p&gt;

&lt;p&gt;My wife, Jennifer, just released her second app, &lt;a href="http://speechesapp.com"&gt;Speeches&lt;/a&gt;. It&amp;#8217;s an app for preparing and delivering slide-less presentations and pitches. Basically, speaker&amp;#8217;s notes on your iPhone.&lt;/p&gt;

&lt;p&gt;I helped her out with the submission, and came to some realizations about how to choose terms for the title and keywords. We established a small system for narrowing in on what would be most effective, and I want to outline what we came up with here.&lt;/p&gt;

&lt;p&gt;The first thing you need to realize when introducing a new app is that you are already at an enormous disadvantage. There are probably existing apps in the App Store that are similar to your app, and they have a big lead in terms of ratings and downloads. Even if your app is much better, there is a good chance you will get completely buried at the bottom of the box, and nobody will find your app. It is essential that you do what you can to raise your visibility by careful choice of search terms.&lt;/p&gt;

&lt;p&gt;The first step is just to write down all the terms you can think of that people might enter in a search for your app. Use a Thesaurus and Google searches for inspiration in order to expand on the terms you can think of immediately yourself. Don&amp;#8217;t just come up with general terms, but seek out terms for very specific uses of your app. For example, obvious terms for Speeches include &amp;#8216;public speaking&amp;#8217;, &amp;#8216;speech&amp;#8217;, and &amp;#8216;cue cards&amp;#8217;, but terms like &amp;#8216;wedding speech&amp;#8217; and &amp;#8216;debating&amp;#8217; could be just as valuable.&lt;/p&gt;

&lt;p&gt;The second stage is to test the words. How? It&amp;#8217;s easy — just search for them in the App Store. Contrary to what you might expect, you are searching for terms that produce few hits. When your app enters the fray, it will likely be way down any list of results, so it is important to have short lists. Less than 10 results is ideal. Less than 20 is still acceptable, but more than that is probably a waste of valuable letters in your keyword field. People will simply not scroll through to your app if it is at number 36, and certainly not if it is at 413.&lt;/p&gt;

&lt;p&gt;In the case of Speeches, we initially had terms like &amp;#8216;keynote&amp;#8217; and &amp;#8216;powerpoint&amp;#8217; on the list, but seeing these produced hundreds of hits, dismissed them as a lost cause. We focussed instead on terms, and combinations of terms that gave us the most chance of being visible to the user, even if the app was ranked at the back. At least then the app is visible, and it&amp;#8217;s up to the icon and screenshots to convince a potential customer to look further.&lt;/p&gt;

&lt;p&gt;With terms chosen, it&amp;#8217;s just a question of filling the keyword field with what you think is most important. And don&amp;#8217;t be afraid to include a byline in the App Store title, together with your App&amp;#8217;s name. These terms are also weighted heavily. We went with &amp;#8220;Speeches — Speaker&amp;#8217;s notes for public speaking&amp;#8217;. If the title gets truncated, at least the app&amp;#8217;s name is still visible, and the byline both describes the app in one short sentence, and boosts those terms in the search results.&lt;/p&gt;

&lt;p&gt;There&amp;#8217;s no telling if Speeches will have any degree of success — the App Store is a hard mistress these days — but at least we have given it the best chance of being found by potential customers. And it&amp;#8217;s an ongoing process too, of course; if you think of better keywords, you can rotate them in with any update.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/34476925606</link><guid>http://mentalfaculty.tumblr.com/post/34476925606</guid><pubDate>Sun, 28 Oct 2012 06:00:00 -0400</pubDate><category>speeches</category><category>keywords</category><category>App Store</category></item><item><title>Feeding the Main Queue with Grand Central Dispatch</title><description>&lt;p&gt;So here&amp;#8217;s the thing: you want to be a good citizen, and dispatch your expensive operations to a background queue, but it is impossible or difficult to do so because your code uses frameworks that must be accessed from the main thread. You could go to the effort of copying data into dictionaries and arrays, and passing those around, in order to avoid accessing the framework in the background, but it is a major headache.&lt;/p&gt;

&lt;p&gt;Instead, you think, I could slice the problem up into small chunks of work, and run them on the main queue. That shouldn&amp;#8217;t interrupt the user interface too much, which is why you need to avoid the main queue in the first place.&lt;/p&gt;

&lt;p&gt;So you submit a few hundred blocks of work to the main queue, each of which does a very short calculation and completes. But to your horror, when you run the app, it beach balls regardless. The blocks you are submitting get queued up one after another, but then event handling blocks have to queue up and wait for all of your blocks to complete. Back to square one.&lt;/p&gt;

&lt;p&gt;What you really need to do is feed one block to the main queue, and only feed the next one when the first is finished. That way, event handling can interleave with your work blocks. But how do you do that?&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve wrestled with this scenario a few times over the years, but I think I have finally found a good solution, and one which leaves the user interface flowing like soft butter. Not only that, it&amp;#8217;s really straightforward.&lt;/p&gt;

&lt;p&gt;The idea is to create a background serial queue, and enqueue blocks that submit your original blocks synchronously to the main queue.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)enqueueBlock:(void (^)(void))block
{
    static dispatch_queue_t feederQueue = NULL;
    if ( !feederQueue ) feederQueue = 
        dispatch_queue_create("com.mentalfaculty.feederqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(feederQueue, ^{
        dispatch_sync(dispatch_get_main_queue(), block);
    });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, you enqueue work like this&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[self enqueueBlock:^{
    // Do some work on the main thread
    // Not too much!
    ...
}];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It&amp;#8217;s simple, and best of all, it works a treat.&lt;/p&gt;

&lt;p&gt;The next logical step would be a simple feeder class that can perform this sort of task for any queue you choose.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/30992605698</link><guid>http://mentalfaculty.tumblr.com/post/30992605698</guid><pubDate>Thu, 06 Sep 2012 08:57:00 -0400</pubDate></item><item><title>Babbleze and the 80/20 Rule</title><description>&lt;p&gt;You won&amp;#8217;t find a software developer in the World today who hasn&amp;#8217;t heard of the &lt;a href="http://en.wikipedia.org/wiki/Pareto_principle"&gt;80/20 rule&lt;/a&gt;, but I&amp;#8217;m guessing many don&amp;#8217;t realize you can apply it to nearly anything. I first learnt this lesson from the &lt;a href="http://click.linksynergy.com/fs-bin/stat?id=c9FoqbAZ0BI&amp;amp;offerid=146261&amp;amp;type=3&amp;amp;subid=0&amp;amp;tmpid=1826&amp;amp;RD_PARM1=http%253A%252F%252Fitunes.apple.com%252Fus%252Fbook%252F4-hour-workweek-expanded-updated%252Fid419259551%253Fmt%253D11%2526uo%253D4%2526partnerId%253D30"&gt;The 4-Hour Work Week&lt;/a&gt;. If you can look past some of the hype, there are a lot of useful tips in the book.&lt;/p&gt;

&lt;p&gt;One thing I am really bad at is applying 80/20 to my projects. For example, I&amp;#8217;ve now spent many months trying to get my app to sync reliably via iCloud. That&amp;#8217;s months of expensive, intense, and somewhat stressful engineering time. Luckily I don&amp;#8217;t have to pay the bill, because it would be expensive if I did.&lt;/p&gt;

&lt;p&gt;The idea of 80/20 as told in The 4-Hour Work Week is that you should pick the low hanging fruit when it comes to growing your business, the stuff that will give the most bang for your buck. For example, I could have spent the same time as I have spent on iCloud syncing developing a whole new iPhone app, and that may well have brought in more income than an incremental improvement to my existing app.&lt;/p&gt;

&lt;p&gt;My wife, Jennifer, has just released her first app (or series of apps to be precise). &lt;a href="http://www.babbleze.com"&gt;Babbleze&lt;/a&gt; is an audio flashcard app for learning languages. Unlike my flagship app &lt;a href="http://www.mentalcaseapp.com"&gt;Mental Case&lt;/a&gt;, which is the powertool of flashcard apps, Babbleze is much simpler, but because it is focussed on one task, in my opinion it succeeds as a product.&lt;/p&gt;

&lt;p&gt;Jennifer doesn&amp;#8217;t have the years of programming experience that I have, and yet she found a category of apps that was within her range of skills, and then made a polished app in that category. To me, it&amp;#8217;s a great example of 80/20. You don&amp;#8217;t have to be the World&amp;#8217;s best programmer to make a good app. You just have to think carefully about design, and make a polished product to solve a problem. In short, make the most of what you have.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/26546507128</link><guid>http://mentalfaculty.tumblr.com/post/26546507128</guid><pubDate>Thu, 05 Jul 2012 03:30:22 -0400</pubDate></item><item><title>Under the Sheets with iCloud and Core Data: Troubleshooting</title><description>&lt;p&gt;I want to finish off this series with a post on troubleshooting, and I&amp;#8217;m not going to sugar coat it — there&amp;#8217;s lots of trouble to shoot.&lt;/p&gt;

&lt;p&gt;You could find much of the material I covered earlier in the series elsewhere, but this post is based on the nitty-gritty, day-to-day issues you only encounter when you try to retrofit a shipping Core Data app with a cool new exhaust in the form of iCloud.&lt;/p&gt;

&lt;p&gt;Today we&amp;#8217;ll go through the stuff they didn&amp;#8217;t teach you in Cocoa school. For many, it may well be the most useful post of the whole series.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Singletons&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many Core Data apps include what I will call &lt;em&gt;singleton entities&lt;/em&gt;. Like singleton classes, these are entities for which there should only be one instance in the store. For example, many apps store certain settings or metadata in a single instance of an entity.&lt;/p&gt;

&lt;p&gt;If you have entities like this, you will need to develop a strategy for ensuring uniqueness. If an instance is created on two different devices, you will end up with two instances on each device after iCloud merges changes.&lt;/p&gt;

&lt;p&gt;There are two ways to approach this. You could simply check for extra instances after a merge, and use a deterministic system for removing them so that the same instances get removed on each device. For example, you could have a globally unique identifier or creation date attribute in the entity, such that you can sort instances and guarantee that the app is retaining the same instance each time.&lt;/p&gt;

&lt;p&gt;Another approach is to create the singleton instances as soon as a new store is setup, and, on those devices seeded from iCloud, do not allow user interaction until the singletons have been merged and can be fetched. In this way, you ensure the singleton entities only get created once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side Effects in Accessors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I first began to integrate iCloud into my Core Data apps, I held to a serious misconception which ended up wasting a considerable amount of time.  That misconception was that my source code had no bearing on the Core Data transaction log import process. I thought that the import process was a private implementation detail; while that is largely true, you can in fact influence it, and sometimes that can have a negative impact.&lt;/p&gt;

&lt;p&gt;The import takes place in a private context using a private persistent store coordinator, but it still makes use of your managed object classes, and uses key-value coding (KVC) to access properties. That means that if you have any side effects in your custom accessor methods, you could end up undermining the import.&lt;/p&gt;

&lt;p&gt;In particular, I have found that creating or deleting objects in an accessor method leads to errors that prevent transaction log imports from completing. For example, take the following setter method.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)setAppearsInSlideshows:(NSNumber *)yn
{
    [self willChangeValueForKey:@"appearsInSlideshows"];
    [self setPrimitiveValue:yn forKey:@"appearsInSlideshows"];
    [self updateFacetPermutations];
    [self didChangeValueForKey:@"appearsInSlideshows"];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;updateFacetPermutations&lt;/code&gt; method creates and deletes objects in order to keep the object graph in a valid state in normal app operation, but these side effects were causing the transaction log imports to fail. Creation and deletion of objects independent of the deltas in the change set is very likely to conflict with the objects being imported.&lt;/p&gt;

&lt;p&gt;The solution to this problem is to ensure that your accessors remain as simple as possible. Use the vanilla methods provided by Core Data. If you need more advanced functionality in order to enforce validity in other parts of your code, either create a secondary accessor using a naming scheme not recognized by KVC, or introduce dependent properties.&lt;/p&gt;

&lt;p&gt;To demonstrate these alternatives, consider again the setter shown above. The &lt;code&gt;appearsInSlideshows&lt;/code&gt; property could be reverted to use the default accessor methods provided by Core Data. To ensure validity of the object graph when mutating in other parts of the source code, a second setter-like method, eg &lt;code&gt;changeAppearsInSlideshowsTo:&lt;/code&gt;, could be introduced to take over the function of the original setter. The application code could use this method when changing the property, and the iCloud import would use the standard, unadulterated setter.&lt;/p&gt;

&lt;p&gt;If you are developing for the Mac, and using Cocoa Bindings, you may be better off introducing a second, dependent property instead. Your interface can then bind to the dependent property, which includes any side effects to ensure object graph validity, while the iCloud import again adopts the standard accessors.&lt;/p&gt;

&lt;p&gt;To demonstrate this option, here is the code I used in my own app for the &lt;code&gt;appearsInSlideshows&lt;/code&gt; property.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+(NSSet *)keyPathsForValuesAffectingBindableAppearsInSlideshows
{
    return [NSSet setWithObject:@"appearsInSlideshows"];
}

-(void)setBindableAppearsInSlideshows:(NSNumber *)yn
{
    [self willChangeValueForKey:@"bindableAppearsInSlideshows"];
    self.appearsInSlideshows = yn;
    [self updateFacetPermutations];
    [self didChangeValueForKey:@"bindableAppearsInSlideshows"];
}

-(NSNumber *)bindableAppearsInSlideshows
{
    [self willAccessValueForKey:@"bindableAppearsInSlideshows"];
    id result = self.appearsInSlideshows;
    [self didAccessValueForKey:@"bindableAppearsInSlideshows"];
    return result;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;bindableAppearsInSlideshows&lt;/code&gt; property is used where previously the &lt;code&gt;appearsInSlideshows&lt;/code&gt; property would have been used, including in bindings. The &lt;code&gt;setBindableAppearsInSlideshows:&lt;/code&gt; method includes the side effects previously in the &lt;code&gt;setAppearsInSlideshows:&lt;/code&gt; method, but these will be avoided during the iCloud import.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validation Failures&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your app&amp;#8217;s source can influence the import process in another way too. The validation rules of your entity model are applied during the import, as are validation checks in your managed object classes. If any fail, the import fails, and will never recover, leaving the app&amp;#8217;s data in an inconsistent state across devices.&lt;/p&gt;

&lt;p&gt;&amp;#8220;But why would the validation ever fail?&amp;#8221;, you might ask. Unfortunately, it turns out that it is much easier to cause a validation failure than you might expect. Any time you have two devices working simultaneously on the same data, the potential for conflict arises, and that could lead to a validation failure. (Note also that simply setting a merge policy is no guarantee that your managed object context will be left in a valid, savable state. This seems to be a common misconception.)&lt;/p&gt;

&lt;p&gt;To demonstrate, consider the following simple entity model: Entity &lt;code&gt;A&lt;/code&gt; has a to-one relationship to Entity &lt;code&gt;B&lt;/code&gt; called &lt;code&gt;b&lt;/code&gt;. Entity &lt;code&gt;B&lt;/code&gt; has an inverse to-one relationship called &lt;code&gt;a&lt;/code&gt;. Neither relationship is optional.&lt;/p&gt;

&lt;p&gt;Assume we have two devices (1) and (2) that begin fully synced. Each has one object of class &lt;code&gt;A&lt;/code&gt;, and one object of class &lt;code&gt;B&lt;/code&gt;, and they are associated with one another. On device 1, we have objects A1 and B1, and on device 2 we have the same logical objects A1 and B1.&lt;/p&gt;

&lt;p&gt;Now assume that simulateous changes are made on each device:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;On device 1, we delete B1, insert B2, and associate A1 with B2. Then save changes.&lt;/li&gt;
&lt;li&gt;On device 2, we also delete B1, insert B3, and associate A1 with B3. Then save changes.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Device 1 now attempts to import the transaction logs from device 2. B3 will be inserted, and A1 will be associated with B3. So far so good, but B2 is now left with relationship &lt;code&gt;a&lt;/code&gt; equal to &lt;code&gt;nil&lt;/code&gt;. This relationship is non-optional, so a validation error occurs.&lt;/p&gt;

&lt;p&gt;Something similar will occur on device 2, because there are two &lt;code&gt;B&lt;/code&gt; objects, and only one &lt;code&gt;A&lt;/code&gt; object to associate with. There must thus always be a validation error, because one of the &lt;code&gt;B&lt;/code&gt; objects must have its relationship set to &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even worse, any future change will always leave an errant &lt;code&gt;B&lt;/code&gt; object hanging around, and will thus fail validation. In effect, the user cannot fix the problem themselves by resetting the relationship. It is permanently broken.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m5q54ijXvM1rpzb44.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;This is more than a theoretical exercise. If you want to see for yourself, download the &lt;a href="https://github.com/drewmccormack/iCloudCoreDataTester"&gt;test app&lt;/a&gt; I introduced in previous posts, and carry out the following experiment.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Fill in your team identifier in the source code, and setup provisioning.&lt;/li&gt;
&lt;li&gt;Build the test app on two Macs or virtual machines running Lion.&lt;/li&gt;
&lt;li&gt;Add a new note on one machine, and start syncing.&lt;/li&gt;
&lt;li&gt;Wait for the note to appear on the other machine.&lt;/li&gt;
&lt;li&gt;Select the note on each machine, and press the &amp;#8216;Change Schedule&amp;#8217; button on each.&lt;/li&gt;
&lt;li&gt;Now press Save on each machine. Do this at almost the same time, so iCloud cannot sync in between.&lt;/li&gt;
&lt;li&gt;Wait for the errors to appear in the console on each Mac.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;The errors should look something like this (edited for readability):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;2012-06-04 13:09:31.450 iCloudCoreDataTester[72072:239b] 
    -[_PFUbiquityRecordImportOperation main](435): CoreData: Ubiquity:  
    CoreData: Ubiquity: Error saving managed object context changes for transaction log: 
    &amp;lt;PFUbiquityTransactionLog: 0x7fd55ad06920&amp;gt;   
    transactionLogLocation: &amp;lt;PFUbiquityLocation: 0x7fd55ac33a00&amp;gt;: ...

Error: Error Domain=NSCocoaErrorDomain Code=1570 
    "Property/permutation/Entity/ChildSchedule is a required value." 
    UserInfo=0x7fd55ad318d0 
    {NSValidationErrorObject=&amp;lt;NSManagedObject: 0x7fd55ac3fa10&amp;gt; 
    (entity: ChildSchedule; id: 0x7fd55ac40a70 &amp;lt;x-coredata://01B641DC-E42D-4876-BB6E-7C9F33FEB704/ChildSchedule/p5&amp;gt; ; 
        data: {
            permutation = nil;
            title = "30AD7F4F-1109-44F9-996A-AAE9915150FD-955-0000051A8";
            }), 
        NSLocalizedDescription=Property/permutation/Entity/ChildSchedule is a required value., 
        NSValidationErrorKey=permutation}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and later&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;2012-06-04 13:09:31.451 iCloudCoreDataTester[72072:239b] 
    -[_PFUbiquityRecordsImporter operation:failedWithError:](824): 
    CoreData: Ubiquity:  Import operation encountered a corrupt log file, 
    Error Domain=NSCocoaErrorDomain Code=134302 
    "The operation couldn’t be completed. (Cocoa error 134302.)" UserInfo=0x7fd55ab035d0 
    {underlyingError=Error Domain=NSCocoaErrorDomain Code=1570 
    "Property/permutation/Entity/ChildSchedule is a required value." 
    UserInfo=0x7fd55ad318d0 {NSValidationErrorObject=&amp;lt;NSManagedObject: 0x7fd55ac3fa10&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These errors are telling you about exactly the scenario I described above: two new objects (&lt;code&gt;ChildSchedule&lt;/code&gt; class) are vying for one vacancy in a to-one relationship. One of the objects is left with a &lt;code&gt;nil&lt;/code&gt; value for the non-optional &lt;code&gt;permutation&lt;/code&gt; relationship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Living with Validation Failures&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is difficult to see how Apple could come up with a general solution to the problem of validation. In the scenario above, for example, Core Data could just delete the left over object, but that is a pretty extreme measure. The object may not even be new; it may be an existing object that was added to the relationship, so deleting it may have drastic consequences. The ultimate solution is probably that Apple needs to provide more hooks for the developer to resolve validation issues during import, but what do we do in the meantime?&lt;/p&gt;

&lt;p&gt;One solution is to simply make all of the relationships in your model optional, or use weak relationships by storing the unique identifiers of related objects, rather than using an explicit relationship in the entity model. This will avoid the validation errors, but at a considerable cost to ease of coding. You will need to add a lot of code to take over the role that Core Data was playing, keeping your object graph in a valid state. At the very least, you will have to scan for stray objects after each merge, and delete or relocate them.&lt;/p&gt;

&lt;p&gt;The solution I have chosen is to maintain the same Core Data entity model, with non-optional relationships, but to avoid any validation at all during the transaction log import. It is difficult for me to oversee all of the risks of this without intimate knowledge of the Core Data framework, so please be cautious if you decide to follow my lead. There may be unforeseen issues, but so far at least, it has been working OK for my app.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s how it works: In order to disable validation during the transaction log import, I have overridden the &lt;code&gt;NSManagedObject&lt;/code&gt; &lt;code&gt;validateValue:forKey:error:&lt;/code&gt; method in a subclass used for all of my entities. The method will only validate if the managed object context class corresponds to custom subclass (&lt;code&gt;MCManagedObjectContext&lt;/code&gt;) of my main context. If a standard &lt;code&gt;NSManagedObjectContext&lt;/code&gt; instance is used, such as during the iCloud import, the method just returns &lt;code&gt;YES&lt;/code&gt; whether the object is valid or not.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(BOOL)validateValue:(__autoreleasing id *)value forKey:(NSString *)key error:(NSError *__autoreleasing *)error 
{
    if ( ![self.managedObjectContext isKindOfClass:[MCManagedObjectContext class]] ) {
        return YES;
    }
    else {
        return [super validateValue:value forKey:key error:error];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The benefit of this is that validation errors are ignored during the import phase. They are effectively postponed, arising when you try to save your app&amp;#8217;s primary context. At this point you have the means to address the errors and retry the save.&lt;/p&gt;

&lt;p&gt;To give you some idea how this works, here is the &lt;code&gt;save&lt;/code&gt; method used in my app.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)save
{    
    [managedObjectContext performBlock:^{
        if ( managedObjectContext.hasChanges ) {
            NSUInteger attempts = 0;
            NSError *error = nil;
            while ( ![managedObjectContext save:&amp;amp;error] &amp;amp;&amp;amp; ++attempts &amp;lt;= MCMaximumSaveAttempts ) {
                [self repairForSaveError:error];
            }

            if ( attempts &amp;gt; MCMaximumSaveAttempts ) {
                NSString *question = NSLocalizedString(@"A problem arose. Could not save changes.", @"Save fail");
                NSString *info = NSLocalizedString(@"You should quit as soon as possible, "
                    @"because continuing could cause other problems.", @"");
                [self runModalAlertWithMessage:question information:info];
            }
        }
    }];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If necessary, several attempts are made to save the context. If a save fails, a method is called to attempt to repair the validation problems.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)repairForSaveError:(NSError *)error
{
    [managedObjectContext processPendingChanges];
    [managedObjectContext.undoManager disableUndoRegistration];

    if ( error.code != NSValidationMultipleErrorsError ) {
        id object = [error.userInfo objectForKey:@"NSValidationErrorObject"];
        [object repairForError:error];
    }
    else {
        NSArray *detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
        for ( NSError *error in detailedErrors ) {
            NSDictionary *detailedInfo = error.userInfo;
            id object = [detailedInfo objectForKey:@"NSValidationErrorObject"];
            [object repairForError:error];
        }
    }

    [managedObjectContext processPendingChanges];
    [managedObjectContext.undoManager enableUndoRegistration];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This method iterates over the validation errors and retrieves the object responsible for each. The violating object itself is then given the opportunity to address the error. The following code comes from the custom managed object class.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+(BOOL)deletesInvalidObjectsAfterFailedSave
{
    return NO;
}

-(void)repairForError:(NSError *)error
{
    if ( [self.class deletesInvalidObjectsAfterFailedSave] ) {
        [self.managedObjectContext deleteObject:self];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The default repair is to do nothing. If an entity class overrides the &lt;code&gt;deletesInvalidObjectsAfterFailedSave&lt;/code&gt; method and returns &lt;code&gt;YES&lt;/code&gt;, any invalid object is simply deleted. Classes with more advanced requirements can override the &lt;code&gt;repairForError:&lt;/code&gt; method to instigate repairs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One reason getting iCloud working in your Core Data app is so difficult is that debugging is torturously slow and frustrating. You build and run on two machines. Then you make a change on one machine, and wait&amp;#8230;and wait&amp;#8230;and&amp;#8230; It&amp;#8217;s even worse if iCloud decides to throttle syncing back, which is quite common. If that happens, you may be better off taking the rest of the day off.&lt;/p&gt;

&lt;p&gt;To try to ease the pain a little, I&amp;#8217;ve gathered some tips here to help you debug Core Data/iCloud apps. It won&amp;#8217;t make it pleasant, but will hopefully save you a bit of time.&lt;/p&gt;

&lt;p&gt;First, you need to decide what your debugging setup will be. You need two running OSes to test. You can&amp;#8217;t run two apps in the same account, and share a single iCloud container. It would be nice, but you can&amp;#8217;t. You also can&amp;#8217;t really run in two separate user accounts and test using fast user switching. In my experience, you can get some strange locking issues with file coordinators. So you are left with two options: two different devices, or one device running a virtual machine.&lt;/p&gt;

&lt;p&gt;I started testing between my iMac and MacBook Air, but after a few weeks elected to purchase &lt;a href="http://www.vmware.com/nl/products/desktop_virtualization/fusion/overview.html"&gt;VMware Fusion&lt;/a&gt; for my iMac, and do all my testing on that. I don&amp;#8217;t regret that decision. It also makes it possible to test future OSes like Mountain Lion, so the virtual machine solution has many advantages.&lt;/p&gt;

&lt;p&gt;Once you have a good testing setup, you will need a way to see what is going on. Core Data logs are extremely verbose when using iCloud, maybe too verbose. You will see a lot of messages; many of them will look extremely worrying, but are in fact completely harmless.&lt;/p&gt;

&lt;p&gt;If you are debugging a specific problem, you may want to see every detail of the import process. In that case, you can make the console messages even more verbose by using the argument&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-com.apple.coredata.ubiquity.logLevel 3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;when launching your app. You set this in the Run section of your scheme, but be warned — you will get an awful lot of output.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m5q55wLtKE1rpzb44.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Glossary of Innoculous Errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because the console output of Core Data can be scary to the newcomer, I&amp;#8217;ve gathered together a few of the innocuous error messages to help you discern the forest from the trees.&lt;/p&gt;

&lt;p&gt;The following occur all the time, and just mean the import couldn&amp;#8217;t proceed due to missing files and the like. Core Data will try again in a minute or so, so just ignore these and wait.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;2012-06-04 13:09:01.295 iCloudCoreDataTester[72072:6f23] +[PFUbiquityTransactionLog loadPlistAtLocation:withError:](378): 
CoreData: Ubiquity:  Encountered an error trying to open the log file at the location: &amp;lt;PFUbiquityLocation: 0x7fd55ac373a0&amp;gt;: ...
    Error: Error Domain=NSCocoaErrorDomain Code=256 "The file “67BFAE7B-9CE3-432A-AD1F-6EA23C648017.1.cdt” couldn’t be opened." 
    UserInfo=0x7fd55ac3b210 {NSURL=file://localhost...cdt, NSDescription=The item failed to download.}

2012-06-04 13:09:01.298 iCloudCoreDataTester[72072:6f23] -[PFUbiquityTransactionLog loadComparisonMetadataWithError:](244): 
CoreData: Ubiquity:  Error encountered while trying to load the comparison metadata for transaction 
log: &amp;lt;PFUbiquityTransactionLog: 0x7fd558e68a60&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This one seems to arise when Core Data is trying to resolve certain conflicts. It seems to be harmless too.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;2012-06-06 17:13:05.913 Mental Case[854:ae53] CoreData: warning: An NSManagedObjectContext delegate overrode fault handling 
behavior to silently delete the object with ID '0x7fdae4dcd3c0 &amp;lt;x-coredata://367CA83F-D0FE-4E19-A52F-873A45EC954C/MCFacetPermutation/p5166&amp;gt;' 
and substitute nil/0 for all property values instead of throwing.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And lastly, the next one is actually a sign that an import was successful. At least, it seems to appear just after a successful import. I actually use it as a notification that the import has gone through.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;2012-06-06 16:20:38.711 Mental Case[3631:750f] -[_PFUbiquityStack initWithLocalPeerID:andUbiquityRootLocation:](83): 
CoreData: Ubiquity:  Error encountered while trying to connect to the metadata store: Error Domain=NSCocoaErrorDomain Code=512 
"The file couldn’t be saved." UserInfo=0x7f9b7b41d8f0 {}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Where to Now?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s it for this series. What can we conclude? Unfortunately, the most apt conclusion is probably that iCloud syncing of Core Data is not really ready for prime time, at least not for any app with a complex data model. If you have a simple model, and patience, it is doable, even if very few have achieved a shipping app at this point.&lt;/p&gt;

&lt;p&gt;Is the future brighter? We have Mountain Lion and iOS 6 just around the corner, and while Apple seems to have addressed some concerns, I still have plenty of reservations. Maybe I&amp;#8217;m wrong, I even hope that I am, but only time will tell.&lt;/p&gt;

&lt;p&gt;The promise of iCloud is great, but syncing is a very difficult problem, and the complexity of the incremental updates that Core Data introduces doesn&amp;#8217;t make it any easier. I think Apple will eventually crack it, but it could be a bumpy ride in the meantime. I hope this series has at least taken some of the rough edges off.&lt;/p&gt;

&lt;p&gt;Go forth and sync!&lt;/p&gt;

&lt;p&gt;Drew McCormack&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/25241910449</link><guid>http://mentalfaculty.tumblr.com/post/25241910449</guid><pubDate>Sat, 16 Jun 2012 15:34:07 -0400</pubDate></item><item><title>Under the Sheets with iCloud and Core Data: Sentinels</title><description>&lt;p&gt;In the previous posts I alluded to some exceptional circumstances that can arise and need to be handled in a production app. In particular,&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;If an app is running, and the user disables iCloud, or deletes the iCloud container on any device, the app will crash.&lt;/li&gt;
&lt;li&gt;If an app has been syncing at some point in the past, and undergoes a stoppage — a period of time where it does not sync — it can&amp;#8217;t pick up again from where it left off. When enabling syncing, there needs to be a way to determine if a device ever contributed to the transaction logs present in the iCloud container.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;One solution to these problems is to add a file or files to the iCloud container to track which devices have contributed to the transaction logs. When a device starts syncing, an entry is made in the container. When the device stops syncing, the device entry is left in place as a record that it contributed to the iCloud logs.&lt;/p&gt;

&lt;p&gt;If there is an attempt to enable syncing on a device which was syncing in the past, it will be clear from the device entries that a stoppage has arisen, and the existing transaction logs can be removed. And if the container is deleted altogether, the device records will also get deleted. The app can detect the missing files, tear down its Core Data stack, and, for example, setup again with a non-ubiquitous store, avoiding a crash and/or data corruption.&lt;/p&gt;

&lt;p&gt;I will refer to the files used to track syncing devices as &amp;#8216;sentinels&amp;#8217;. This term was introduced by Daniel Pasco in a presentation at &lt;a href="http://ideveloper.tv/nsconference/"&gt;NSConference 2012&lt;/a&gt;. Daniel used a very similar technique to what I will describe here, and has posted &lt;a href="https://github.com/blackpixel/NSConferenceiPhoneCoreDataRecipes"&gt;his code&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sentinel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Daniel&amp;#8217;s solution involves adding a file for each device that contributes to the transaction logs. My solution, which was developed independently, is similar in nature, but uses just a single properly list file holding an array of device identifiers. Each time a device starts syncing, it is added to this plist file.&lt;/p&gt;

&lt;p&gt;The plist can be used to check whether a device for which syncing is about to be enabled has contributed to the transaction logs in iCloud at some point in the past. And if the plist file is deleted, or a device with syncing enabled suddenly no longer appears in the list of identifiers, it can be concluded that a reset has occurred, and evasive action can be taken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Sentinel Class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have developed a class that creates and tracks a sentinel file. To get the source code, visit the test &lt;a href="https://github.com/drewmccormack/iCloudCoreDataTester"&gt;project&lt;/a&gt; introduced last time on GitHub, or — if you cloned the source last time — pull down the latest changes using git.&lt;/p&gt;

&lt;p&gt;The sentinel class is called &lt;code&gt;MCCloudResetSentinel&lt;/code&gt;. To create an instance, you use the &lt;code&gt;initWithCloudStorageURL:cloudSyncEnabled:&lt;/code&gt; initializer. The first argument should be a URL for a directory in the app&amp;#8217;s iCloud container. The class will create the sentinel plist file in this directory.&lt;/p&gt;

&lt;p&gt;The second argument indicates whether syncing is currently enabled. This relates to the in-app sync setting, and not to whether iCloud is enabled globally for the device. The &lt;code&gt;MCCloudResetSentinel&lt;/code&gt; class always assumes iCloud is globally enabled; if that is not the case, the class serves no useful purpose, as it has no access to the iCloud container.&lt;/p&gt;

&lt;p&gt;The sentinel class is immutable. Once you initialize it, either for the syncing or non-syncing state, you can&amp;#8217;t change it. If the syncing state does need to change, simply release the existing sentinel instance, and make a new one.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;MCCloudResetSentinel&lt;/code&gt; has a delegate, and will invoke the delegate method &lt;code&gt;cloudResetSentinelDidDetectReset:&lt;/code&gt; if the current device identifier disappears from the list of syncing devices while syncing is enabled. It will only invoke this method once; a new sentinel instance should be created after the reset has been addressed.&lt;/p&gt;

&lt;p&gt;The class also posts a &lt;code&gt;MCCloudResetSentinelDidDetectResetNotification&lt;/code&gt; notification when a reset occurs, so objects other than the delegate can also take appropriate action, such as release references to invalidated managed objects.&lt;/p&gt;

&lt;p&gt;When a device starts syncing, the sentinel object can be requested to add it to the list of devices using the method &lt;code&gt;updateDevicesList:&lt;/code&gt;. Because this method has to ensure it has the very latest version of the devices property list, which may involve downloading from iCloud servers, the method is asynchronous, and takes a completion handler block as sole argument.&lt;/p&gt;

&lt;p&gt;The last piece of major functionality in the &lt;code&gt;MCCloudResetSentinel&lt;/code&gt; class is the ability to check if the current device is in the property list, ie, has at some point contributed to the iCloud transaction logs. The asynchronous &lt;code&gt;checkCurrentDeviceRegistration:&lt;/code&gt; method is used for this purpose. It invokes a completion handler, passing &lt;code&gt;YES&lt;/code&gt; if the device is listed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;MCCloudResetSentinel&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;AppDelegate&lt;/code&gt; class includes a number of changes to make use of the sentinel class, and the UI of the test app has also been improved somewhat. For example, the interface now shows icons to indicate whether iCloud syncing is active, and whether the Core Data stack is currently setup.&lt;/p&gt;

&lt;p&gt;The sentinel is used in a few places. First, when the app launches, a check is done to see whether a cloud reset has occurred while the app was not running. This occurs before the Core Data stack is initialized.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[self checkIfCloudDataHasBeenReset:^(BOOL hasBeenReset) {
    if ( hasBeenReset ) [self disableCloudAfterResetAndWarnUser];
    [self setupCoreDataStack:self];
}];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;checkIfCloudDataHasBeenReset:&lt;/code&gt; method uses a sentinel to test for a reset.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)checkIfCloudDataHasBeenReset:(void (^)(BOOL hasBeenReset))completionBlock
{
    dispatch_queue_t completionQueue = dispatch_get_current_queue();
    dispatch_retain(completionQueue);

    BOOL usingCloudStorage = 
        [[NSUserDefaults standardUserDefaults] boolForKey:MCUsingCloudStorageDefault];
    if ( usingCloudStorage &amp;amp;&amp;amp; !self.cloudStoreURL ) {
        dispatch_async(completionQueue, ^{
            completionBlock(YES);
            dispatch_release(completionQueue);
        });
        return;
    }

    // Use a temporary sentinel to determine if a reset of cloud data has occurred
    MCCloudResetSentinel *tempSentinel = 
        [[MCCloudResetSentinel alloc] initWithCloudStorageURL:self.cloudStoreURL 
                                             cloudSyncEnabled:usingCloudStorage];
    if ( usingCloudStorage ) {
        [tempSentinel checkCurrentDeviceRegistration:^(BOOL deviceIsPresent) {
            dispatch_async(completionQueue, ^{
                completionBlock(!deviceIsPresent);
                dispatch_release(completionQueue);
            });
        }];
    }
    else {
        dispatch_async(completionQueue, ^{
            completionBlock(NO);
            dispatch_release(completionQueue);
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This code is asynchronous and makes heavy use of Grand Central Dispatch (GCD), but the idea is fairly straightforward: if cloud syncing is enabled, which is determined by a user default, a temporary sentinel is created to check if the current device is in the device list. The result is passed back to the completion block.&lt;/p&gt;

&lt;p&gt;Whenever the Core Data stack is setup, a sentinel is created to update the devices list, and then monitor it continuously for iCloud container resets.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;__weak AppDelegate *weakSelf = self;
[self addStoreToPersistentStoreCoordinator:^(BOOL success, NSError *error) {
    if ( !success ) {
        [weakSelf tearDownCoreDataStack:weakSelf];
        [[NSApplication sharedApplication] presentError:error];
    }
    else {
        [weakSelf makeManagedObjectContext];

        // Setup a sentinel
        BOOL usingCloudStorage = 
            [[NSUserDefaults standardUserDefaults] boolForKey:MCUsingCloudStorageDefault];
        if ( usingCloudStorage ) {
            weakSelf-&amp;gt;sentinel = 
                [[MCCloudResetSentinel alloc] initWithCloudStorageURL:weakSelf.cloudStoreURL 
                                                     cloudSyncEnabled:usingCloudStorage];
            weakSelf-&amp;gt;sentinel.delegate = self;
            [weakSelf-&amp;gt;sentinel updateDevicesList:NULL];
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The other place a sentinel is used is when the user enables syncing. At this point, the sentinel is used to check if the device ever contributed transaction logs, and act accordingly.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Use sentinel to determine if device was previously syncing.
// In that case, the only option is to replace the whole cloud container.
// If the device never synced before, the user can choose to keep the 
// local or the cloud data.
MCCloudResetSentinel *tempSentinel = 
    [[MCCloudResetSentinel alloc] initWithCloudStorageURL:self.cloudStoreURL 
                                         cloudSyncEnabled:NO];
[tempSentinel stopMonitoringDevicesList];
[tempSentinel checkCurrentDeviceRegistration:^(BOOL deviceIsPresent) {
    if ( deviceIsPresent ) {
        // Only choice is to move data to the cloud, replacing the existing cloud data.
        // In a production app, you should warn the user, and give them a chance
        // to back out.
        [self migrateStoreToCloud];
    }
    else {
        // Can keep either the cloud data, or the local data at this point
        // In a production app, you could ask the user what they want to keep.
        // Here we will just see if there is cloud data present, and if there is,
        // use that. If there is no cloud data, we'll keep the local data.
        BOOL migrateDataFromCloud = 
            [[NSFileManager defaultManager] fileExistsAtPath:self.cloudStoreURL.path];
        if ( migrateDataFromCloud ) {
            // Already cloud data present, so replace local data with it
            [self removeLocalFiles:self];
        }
        else {
            // No cloud data, so migrate local data to the cloud
            [self migrateStoreToCloud];
        }
    }
    [[NSUserDefaults standardUserDefaults] setBool:YES 
                                            forKey:MCUsingCloudStorageDefault];
    [self setupCoreDataStack:self];
}];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The comments in the code describe the various branches that can be taken. In short, if the device is in the list already, the only way to get a consistent set of data is to replace the iCloud container with data from the local store. If the device is not in the list, the option arises to either replace the iCloud data, or replace the local store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metadata — Faster than Some Speeding Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I will use the rest of this post to go through the code of the sentinel class itself, because it is useful to understanding iCloud and how you interact with it.&lt;/p&gt;

&lt;p&gt;One aspect of iCloud&amp;#8217;s implementation that is useful to grasp is that it pushes metadata between devices and the cloud much faster than it moves file data itself. An app on one device can learn of a change on another device long before the file change itself has propagated over.&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;NSMetadataQuery&lt;/code&gt; class to monitor changes in file metadata, and Apple have extended the API to support ubiquitous data in the iCloud container. This is the same class you use to do Spotlight searches, and also underpins Time Machine backups.&lt;/p&gt;

&lt;p&gt;You can do asynchronous searches with &lt;code&gt;NSMetadataQuery&lt;/code&gt;, but also be notified of file metadata changes as they occur. The latter is where our interest lies here. The &lt;code&gt;MCCloudResetSentinel&lt;/code&gt; class sets up a query in its initializer that matches the filename of the devices property list, and then listens for &lt;code&gt;NSMetadataQueryDidUpdateNotification&lt;/code&gt; notifications from the query.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Listen for changes in the metadata of the devices list
devicesListMetadataQuery = [[NSMetadataQuery alloc] init];
devicesListMetadataQuery.searchScopes = 
    [NSArray arrayWithObject:NSMetadataQueryUbiquitousDataScope];
devicesListMetadataQuery.predicate = 
    [NSPredicate predicateWithFormat:@"%K like %@", NSMetadataItemFSNameKey, MCSyncingDevicesListFilename];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devicesListDidUpdate:) 
    name:NSMetadataQueryDidUpdateNotification object:devicesListMetadataQuery];
if ( ![devicesListMetadataQuery startQuery] ) NSLog(@"Failed to start devices list NSMetadataQuery");
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The predicate of the query is used to restrict notifications to files named the same as the devices property list file, and the scope of the query — &lt;code&gt;NSMetadataQueryUbiquitousDataScope&lt;/code&gt; — means only files in the iCloud outside of the &lt;code&gt;Documents&lt;/code&gt; folder will be monitored. (If you want to monitor files inside the iCloud &lt;code&gt;Documents&lt;/code&gt; folder, use &lt;code&gt;NSMetadataQueryUbiquitousDocumentsScope&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Downloading Files&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because metadata moves faster than file data, there may be times where your app knows about a change before it has happened, and needs to get hold of the new data as quickly as possible, rather than just waiting for iCloud to do its thing. This is the case, for example, with the devices list: if a reset has occurred, we want to know about it as soon as possible, so that we can take evasive action.&lt;/p&gt;

&lt;p&gt;Luckily, Apple have built a few features into &lt;code&gt;NSURL&lt;/code&gt; and &lt;code&gt;NSFileManager&lt;/code&gt; to help with this. First, you can query the download state of a particular file using &lt;code&gt;NSURL&lt;/code&gt;&amp;#8217;s &lt;code&gt;getResourceValue:forKey:error:&lt;/code&gt;, passing in &lt;code&gt;NSURLUbiquitousItemIsDownloadedKey&lt;/code&gt; as the key. The value returned by reference contains an &lt;code&gt;NSNumber&lt;/code&gt; holding a &lt;code&gt;BOOL&lt;/code&gt; to indicate if the file is downloaded, or still has changes in the cloud.&lt;/p&gt;

&lt;p&gt;If a file is not downloaded, you can force it to download using the &lt;code&gt;startDownloadingUbiquitousItemAtURL:error:&lt;/code&gt; method. While it is downloading, the resource key &lt;code&gt;NSURLUbiquitousItemIsDownloadingKey&lt;/code&gt; can be used with &lt;code&gt;getResourceValue:forKey:error:&lt;/code&gt; to check if it is still downloading.&lt;/p&gt;

&lt;p&gt;A lot of the time, you only care to ensure that you have the latest version of a file, and don&amp;#8217;t need to know the details of how it is retrieved. For that reason, I added a category method to &lt;code&gt;NSURL&lt;/code&gt; that makes sure the latest version of an iCloud file is present on the local disk.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)syncWithCloud:(void (^)(BOOL success, NSError *error))completionBlock
{
    NSError *error;
    NSNumber *downloaded;
    BOOL success = [self getResourceValue:&amp;amp;downloaded forKey:NSURLUbiquitousItemIsDownloadedKey error:&amp;amp;error];
    if ( !success ) {
        // Resource doesn't exist
        completionBlock(YES, nil);
        return;
    }

    if ( !downloaded.boolValue ) {
        NSNumber *downloading;
        BOOL success = [self getResourceValue:&amp;amp;downloading forKey:NSURLUbiquitousItemIsDownloadingKey error:&amp;amp;error];
        if ( !success ) {
            completionBlock(NO, error);
            return;
        }

        if ( !downloading.boolValue ) {
            BOOL success = [[NSFileManager defaultManager] startDownloadingUbiquitousItemAtURL:self error:&amp;amp;error];
            if ( !success ) {
                completionBlock(NO, error);
                return;
            }
        }

        // Download not complete. Schedule another check. 
        double delayInSeconds = 0.1;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
        dispatch_queue_t queue = dispatch_get_current_queue();
        dispatch_retain(queue);
        dispatch_after(popTime, queue, ^{
            [self syncWithCloud:[completionBlock copy]];
            dispatch_release(queue);
        });
    }
    else {
        completionBlock(YES, nil);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This uses the methods discussed above to download the latest version, and waits until it is finished, before calling back to a completion block.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;File Presenters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, I have found that sometimes the metadata query notification does not fire when the device list file is deleted. I suspect this may have something to do with the file deletion taking place at the same time as the metadata update arriving, but I&amp;#8217;m not certain.&lt;/p&gt;

&lt;p&gt;To make doubly sure that the sentinel class observes all changes to the device list file, I have made it conform to the &lt;code&gt;NSFilePresenter&lt;/code&gt; protocol.&lt;/p&gt;

&lt;p&gt;File presenters are the flip side of file coordinators, which we met last time. File coordinators provide a locking mechanism for safely manipulating files, and notify any file presenters when things change.&lt;/p&gt;

&lt;p&gt;So in order to monitor changes to a file, you can register a file presenter, which is an object conforming to the &lt;code&gt;NSFilePresenter&lt;/code&gt; protocol. The protocol requires the following methods.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(NSURL *)presentedItemURL
{
    return self.syncedDevicesListURL;
}

-(NSOperationQueue *)presentedItemOperationQueue
{
    return filePresenterQueue;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;presentedItemURL&lt;/code&gt; returns the URL of the presented file, and &lt;code&gt;presentedItemOperationQueue&lt;/code&gt; should provide the queue that is used for callbacks when the file changes.&lt;/p&gt;

&lt;p&gt;The methods for observing changes are as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)presentedItemDidChange
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [self devicesListDidUpdate:nil];
    }];
}

-(void)accommodatePresentedItemDeletionWithCompletionHandler:(void (^)(NSError *errorOrNil))completionHandler
{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [self devicesListDidUpdate:nil];
    }];
    completionHandler(nil);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first is for a change to the file, and the second is for a deletion. In both cases, we queue up a device list check on the main queue.&lt;/p&gt;

&lt;p&gt;The only other aspect of &lt;code&gt;NSFilePresenter&lt;/code&gt; that should be considered is how it starts and stops monitoring. To begin monitoring the file, you actually need to use the &lt;code&gt;NSFileCoordinator&lt;/code&gt; class.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    // Register as file presenter
    [NSFileCoordinator addFilePresenter:self];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And to stop monitoring, you remove the presenter.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)stopMonitoringDevicesList
{
    [NSFileCoordinator removeFilePresenter:self];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Last Throw&amp;#8230;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s it for sentinels. Next time I will finish off the series by discussing troubleshooting and debugging.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/24692275202</link><guid>http://mentalfaculty.tumblr.com/post/24692275202</guid><pubDate>Fri, 08 Jun 2012 14:53:14 -0400</pubDate></item><item><title>Under the Sheets with iCloud and Core Data: Working Code</title><description>&lt;p&gt;Having covered Core Data syncing via iCloud from a high level, in this post I want to introduce a simple test app, which will be extended over the coming weeks. The app should eventually contain most of the code snippets you will need to get your own app up and running with iCloud.&lt;/p&gt;

&lt;h2&gt;Installing the Test App&lt;/h2&gt;

&lt;p&gt;The test app is &lt;a href="https://github.com/drewmccormack/iCloudCoreDataTester"&gt;available&lt;/a&gt; on github. You can download the source code as a zip archive, but it is probably best to use git to clone the project to your Mac, so that you can pull future changes as we go.&lt;/p&gt;

&lt;p&gt;Once you have the source code, there are a few modifications you will need to make if you want to sync via iCloud. First, go into the &lt;code&gt;AppDelegate.m&lt;/code&gt; file, and enter your Team ID at the top.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;static NSString * const TeamIdentifier = @"XXXXXXXXXX";
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Simply replace the placeholder string with your Team ID.&lt;/p&gt;

&lt;p&gt;Next, you will need to go to the Developer Certificate Utility from the Mac Dev Center, and create a provisioning profile, as described in the &lt;a href="http://mentalfaculty.tumblr.com/post/23163747823/under-the-sheets-with-icloud-and-core-data-the-basics"&gt;first post&lt;/a&gt;. It is probably easiest to create the profile for a wildcard app. To do this, just use an asterisk (*) as the app bundle identifier. Once you download the provisioning profile, install it on your Mac by double clicking the file in Finder. Also add it to the Provisioning Profiles section under the Devices tab in the Xcode Organizer. Lastly, go into the iCloudCoreDataTester Target&amp;#8217;s Build Settings and set the Code Signing Identity to use the new profile.&lt;/p&gt;

&lt;h2&gt;Introduction to iCloudCoreDataTester&lt;/h2&gt;

&lt;p&gt;The app is structurally very simple: all the interesting code is in the &lt;code&gt;AppDelegate.m&lt;/code&gt; file. The Core Data entity model is based on parts of the model from my app, &lt;a href="http://www.mentalcaseapp.com"&gt;Mental Case&lt;/a&gt;, but is otherwise nonsense. Best not to try to understand what any of it means; just note the types of relationships involved, the deletion rules, and other aspects that may be of interest. The objective in designing the entity model was to mimic the sort of complex relationships that you might find in a production app.&lt;/p&gt;

&lt;p&gt;The UI of the app is very straightforward. The list on the left shows a list of Note objects, which you can add and remove using the buttons underneath. If you select a note, a child object appears in the list on the right. This &amp;#8216;schedule&amp;#8217; object can be switched using the Change Schedule button, which deletes the existing instance, and inserts a new one.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m4ssvyBd1L1rpzb44.png" alt=""/&gt;&lt;/p&gt;

&lt;p&gt;The model includes various hidden entities, which don&amp;#8217;t appear in the UI, and are mainly there to setup different types of relationships, to thoroughly test iCloud.&lt;/p&gt;

&lt;p&gt;At this point, the app does not handle stoppages. It is more a developer test apparatus than a user-friendly, end-user app. There are buttons to manually setup and tear down the Core Data stack, delete the local store, delete the cloud container, save the managed object context, and turn on iCloud syncing. (An entry in &lt;code&gt;NSUserDefaults&lt;/code&gt; is used to persist whether iCloud syncing is enabled or not.)&lt;/p&gt;

&lt;h2&gt;The Core Data Stack&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;AppDelegate&lt;/code&gt; begins with a number of simple methods for retrieving URLs for the local store and cloud container. Local data is located in the user&amp;#8217;s Application Support folder&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(NSURL *)localStoreURL
{
    return [self.applicationFilesDirectory URLByAppendingPathComponent:@"iCloudCoreDataTester.storedata"];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and the cloud container is setup in a subpath (&lt;code&gt;MainStore&lt;/code&gt;) of the main container.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(NSURL *)cloudStoreURL
{
    NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
    NSString *ubiquityId = [NSString stringWithFormat:@"%@.%@", TeamIdentifier, bundleId];
    NSURL *ubiquitousURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:ubiquityId];
    NSURL *storeURL = [ubiquitousURL URLByAppendingPathComponent:@"MainStore"];
    return storeURL;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each of the buttons in the UI is connected to a basic action. You will probably want similar methods in your own app, so that it is trivial to setup and teardown the Core Data stack, remove the cloud container, or perform other fundamental tasks at will.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(IBAction)tearDownCoreDataStack:(id)sender
{
    if ( !stackIsSetup ) return;

    NSError *error;
    if ( ![self.managedObjectContext save:&amp;amp;error] ) {
        [NSApp presentError:error];
    }
    stackIsSetup = NO;

    [self.managedObjectContext reset];
    self.managedObjectContext = nil;
    self.managedObjectModel = nil;
    self.persistentStoreCoordinator = nil;
}

-(IBAction)setupCoreDataStack:(id)sender
{
    if ( stackIsSetup ) return;
    stackIsSetup = YES;
    [self willChangeValueForKey:@"managedObjectContext"];
    [self didChangeValueForKey:@"managedObjectContext"];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The iCloudCoreDataTester app uses Cocoa Bindings in the user interface, so setting up the Core Data stack just involves firing KVO notifications, which cause the table views to reload, and thereby lazy creation of the Core Data stack objects (eg &lt;code&gt;NSPersistentStoreCoordinator&lt;/code&gt;, &lt;code&gt;NSManagedObjectContext&lt;/code&gt;, etc).&lt;/p&gt;

&lt;p&gt;Creation of the Core Data stack objects is fairly standard, and follows the prescription given in the first post. The persistent store coordinator adds the local store with ubiquity options if iCloud syncing is enabled.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Use cloud storage if iCloud is enabled, and the user default is set to YES.
NSURL *storeURL = self.cloudStoreURL;
BOOL usingCloudStorage = [[NSUserDefaults standardUserDefaults] boolForKey:UsingCloudStorageDefault];
usingCloudStorage &amp;amp;= storeURL != nil;

// Basic options
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObjectsAndKeys:
    (id)kCFBooleanTrue, NSMigratePersistentStoresAutomaticallyOption, 
    (id)kCFBooleanTrue, NSInferMappingModelAutomaticallyOption, 
    nil];

// iCloud options
if ( usingCloudStorage ) {
    [options addEntriesFromDictionary:[NSDictionary dictionaryWithObjectsAndKeys:
         MCCloudMainStoreFileName, NSPersistentStoreUbiquitousContentNameKey,
         storeURL, NSPersistentStoreUbiquitousContentURLKey, 
         nil]];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Merging Changes&lt;/h2&gt;

&lt;p&gt;When the persistent store coordinator is created, the &lt;code&gt;AppDelegate&lt;/code&gt; registers to receive notifications of iCloud merges.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// Register as observer for iCloud merge notifications
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(persistentStoreCoordinatorDidMergeCloudChanges:) 
    name:NSPersistentStoreDidImportUbiquitousContentChangesNotification object:coordinator];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When the notification is sent, it is passed directly to the &lt;code&gt;mergeChangesFromContextDidSaveNotificaton:&lt;/code&gt; method of the &lt;code&gt;NSManagedObjectContext&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)persistentStoreCoordinatorDidMergeCloudChanges:(NSNotification *)notification
{
    [self.managedObjectContext performBlock:^{        
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 

        NSError *error;
        if ( ![self.managedObjectContext save:&amp;amp;error] ) {
            [NSApp presentError:error];
        }
    }];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;File Coordination&lt;/h2&gt;

&lt;p&gt;When Apple developed iCloud, they came across issues that don&amp;#8217;t affect an isolated desktop app. One of those issues was that, with iCloud in the mix, files often need to be accessed from multiple processes at once. Apple needed to develop a robust locking and notification mechanism for file access, and that became the &lt;a href="http://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSFileCoordinator_class/Reference/Reference.html#//apple_ref/doc/uid/TP40010585"&gt;file coordination&lt;/a&gt; and &lt;a href="http://developer.apple.com/library/mac/#documentation/Foundation/Reference/NSFilePresenter_protocol/Reference/Reference.html#//apple_ref/occ/intf/NSFilePresenter"&gt;presentation&lt;/a&gt; classes.&lt;/p&gt;

&lt;p&gt;Whenever you need to perform any action at all on a file (or folder) in the iCloud container, you should use an &lt;code&gt;NSFileCoordinator&lt;/code&gt;, to make sure you play nicely with the iCloud processes that are monitoring and modifying the same files. In iCloudCoreDataTester, an &lt;code&gt;NSFileCoordinator&lt;/code&gt; is used when removing the iCloud container.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(IBAction)removeCloudFiles:(id)sender
{
    [self tearDownCoreDataStack:self];

    [[NSUserDefaults standardUserDefaults] setBool:NO forKey:UsingCloudStorageDefault];

    NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
    NSURL *storeURL = self.cloudStoreURL;
    if ( !storeURL ) return;
    [coordinator coordinateWritingItemAtURL:storeURL options:NSFileCoordinatorWritingForDeleting error:NULL byAccessor:^(NSURL *newURL) {
        [[NSFileManager defaultManager] removeItemAtURL:newURL error:NULL];
    }];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The method &lt;code&gt;coordinateWritingItemAtURL:options:error:byAccessor:&lt;/code&gt; is responsible for retreiving a lock on the container&amp;#8217;s folder URL, in order to perform our file operation, in this case a deletion (&lt;code&gt;NSFileCoordinatorWritingForDeleting&lt;/code&gt;). In doing so, it informs any &lt;code&gt;NSFilePresenter&lt;/code&gt; objects that are monitoring the container what is about to happen. In this way, iCloud will notice that the data has been deleted.&lt;/p&gt;

&lt;p&gt;The actual removal is carried out using the default &lt;code&gt;NSFileManager&lt;/code&gt; in a block passed as last argument to the method. You may think that this happens asynchrously, as is usually the case with block arguments, but the &lt;code&gt;NSFileCoordinator&lt;/code&gt; methods are synchronous — the block has been executed, and the container deleted, when the method returns.&lt;/p&gt;

&lt;p&gt;Note also that you should operate on the URL passed into the block, rather than the original &lt;code&gt;storeURL&lt;/code&gt;. This is a good habit to get into, because with some file operations, it is possible the file&amp;#8217;s URL may have been modified by previous operations.&lt;/p&gt;

&lt;h2&gt;Migration&lt;/h2&gt;

&lt;p&gt;Migrating an existing local store to iCloud is the task of the &lt;code&gt;migrateStoreToCloud&lt;/code&gt; method. It begins by moving the local store to a new URL.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-(void)migrateStoreToCloud
{
    NSError *error;
    NSURL *storeURL = self.localStoreURL;
    NSURL *oldStoreURL = [[self applicationFilesDirectory] URLByAppendingPathComponent:@"OldStore"];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // If there is no local store, no need to migrate
    if ( ![fileManager fileExistsAtPath:storeURL.path] ) return;

    // Remove any existing old store file left over from a previous migration
    [fileManager removeItemAtURL:oldStoreURL error:NULL];

    // Move existing local store aside
    if ( ![fileManager moveItemAtURL:storeURL toURL:oldStoreURL error:&amp;amp;error] ) {
        [[NSApplication sharedApplication] presentError:error];
        return;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Store options are then generated for the existing non-ubiquitous store, and the new ubiquitious store.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    // Options for new cloud store
    NSDictionary *localOnlyOptions = [NSDictionary dictionaryWithObjectsAndKeys:
        (id)kCFBooleanTrue, NSMigratePersistentStoresAutomaticallyOption, 
        (id)kCFBooleanTrue, NSInferMappingModelAutomaticallyOption, 
        nil];
    NSDictionary *cloudOptions = [NSDictionary dictionaryWithObjectsAndKeys:
        (id)kCFBooleanTrue, NSMigratePersistentStoresAutomaticallyOption, 
        (id)kCFBooleanTrue, NSInferMappingModelAutomaticallyOption, 
        MCCloudMainStoreFileName, NSPersistentStoreUbiquitousContentNameKey,
        self.cloudStoreURL, NSPersistentStoreUbiquitousContentURLKey, 
        nil];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;An &lt;code&gt;NSPersistentStoreCoordinator&lt;/code&gt; is created, and the non-ubiquitious store added.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
    id oldStore = [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:oldStoreURL options:localOnlyOptions error:&amp;amp;error];
    if ( !oldStore ) {
        // Move back the old store and present the error
        [fileManager moveItemAtURL:oldStoreURL toURL:storeURL error:NULL];
        [[NSApplication sharedApplication] presentError:error];
        return;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;migratePersistentStore:toURL:options:withType:error:&lt;/code&gt; method is used to carry out the migration to the ubiquitous store, which is located at the original store URL.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    // Migrate existing (old) store to new store
    if ( ![coordinator migratePersistentStore:oldStore toURL:storeURL options:cloudOptions withType:NSSQLiteStoreType error:&amp;amp;error] ) {
        [fileManager removeItemAtURL:storeURL error:NULL];
        [fileManager moveItemAtURL:oldStoreURL toURL:storeURL error:NULL];
        [[NSApplication sharedApplication] presentError:error];
        return;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If all goes well, the old non-ubiquitious store is deleted.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    else {
        [[NSFileManager defaultManager] removeItemAtURL:oldStoreURL error:NULL];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Care should be taken to ensure no data is lost in this process. It might even be better not to delete the old store outright, but to simply leave it in place or move it to the trash, so that if a problem arises, the user has some recourse to retrieve the old data.&lt;/p&gt;

&lt;h2&gt;What&amp;#8217;s Next&amp;#8230;&lt;/h2&gt;

&lt;p&gt;iCloudCoreDataTester is already useful to test various syncing scenarios. You should install it on two Macs, or virtual machines, and try to get it syncing.&lt;/p&gt;

&lt;p&gt;At this point, it is not very robust. In particular, it has no facilities for handling &lt;a href="http://mentalfaculty.tumblr.com/post/23788055417/under-the-sheets-with-icloud-and-core-data-seeding"&gt;stoppages&lt;/a&gt;, so if you remove the iCloud container on one machine, you will probably find the app crashes on the other machine after iCloud has synced the change.&lt;/p&gt;

&lt;p&gt;In the next post, I&amp;#8217;ll introduce a so-called &lt;em&gt;sentinel file&lt;/em&gt;, which will be used to notify the app of unexpected changes to the iCloud container, and allow it to respond graciously, rather than just crashing.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/24009617665</link><guid>http://mentalfaculty.tumblr.com/post/24009617665</guid><pubDate>Tue, 29 May 2012 15:19:49 -0400</pubDate></item><item><title>Under the Sheets with iCloud and Core Data: Seeding iCloud</title><description>&lt;p&gt;Before you think about the fine details of syncing your Core Data via iCloud, you need to answer some big questions: How are you going to enable and disable iCloud in your app, and how are you going to seed iCloud with data when syncing gets enabled?&lt;/p&gt;

&lt;p&gt;In an ideal world, it would work like this&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;You bring a new app to market&lt;/li&gt;
&lt;li&gt;An end-user installs the app on one device with iCloud activated&lt;/li&gt;
&lt;li&gt;The app creates a new store with ubiquity options enabled&lt;/li&gt;
&lt;li&gt;Core Data writes all changes to the iCloud container&lt;/li&gt;
&lt;li&gt;The end-user installs the app on a second device with iCloud active&lt;/li&gt;
&lt;li&gt;The app imports transaction logs from the first device&lt;/li&gt;
&lt;li&gt;The user never turns off iCloud on any device&lt;/li&gt;
&lt;li&gt;The user never removes the iCloud Data for your app&lt;/li&gt;
&lt;li&gt;The user never switches iCloud accounts&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Unfortunately, this utopia is far from the reality that most developers will have to face. There are lots of corner cases, and a reasonable chance that you could end up being the dunce. For example,&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;What if the user disables iCloud for a while?&lt;/li&gt;
&lt;li&gt;What if the user wipes iCloud data for the app?&lt;/li&gt;
&lt;li&gt;What if they switch iCloud accounts for a bit, perhaps to allow someone else to lend their device?
&lt;/li&gt;&lt;li&gt;What if your app is not new, but has existed for some time, and the user could have existing data, perhaps across multiple devices?&lt;/li&gt;

&lt;li&gt;What if your app is not new, and you have a pre-existing method of sync (eg, Wi-Fi, Dropbox, MobileMe), such that — unbeknownst to iCloud — there is logically-equivalent data on multiple devices.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;When dealing with user data, you can’t be blaise. As an argument, “it seems unlikely, so let’s just ignore these issues” is not going to cut it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iCloud Does Not Repeat Itself&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When making a decision about how to handle your iCloud setup, it is important to grasp a fundamental implementation detail of iCloud: it will only move a piece of data in or out at most once on any device — it never repeats.&lt;/p&gt;

&lt;p&gt;For example, if someone is entering or making changes on a device where iCloud is turned off, any changes they make will not generate change logs, and those changes will be lost forever to iCloud. They will not automatically appear when iCloud is switched back on.&lt;/p&gt;

&lt;p&gt;Also, once Core Data has imported some iCloud data on a device, that data will not be imported again, even if a new local store is created. You might think that if you delete the local store, then bring up the Core Data stack, it would see that the store is empty and reimport all changes from iCloud. That does not happen. As far as Core Data is concerned, the store already has the changes, and your store will remain empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stoppages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve already hinted that changes made after iCloud is disabled, either via a switch in your app, or via the System Preferences, are permanently lost as far as syncing is concerned. This has some important consequences for your app.&lt;/p&gt;

&lt;p&gt;For a start, any objects inserted while iCloud is disabled, and other changes made to the data, will not appear on other devices, even after iCloud is re-engaged. That means the user will see two distinct sets of data. They will wonder why the objects they see on one device are not appearing on the other. But that will likely be the least of the problems…&lt;/p&gt;

&lt;p&gt;With inconsistent data sets on different devices, transaction logs may no longer import properly. Imagine that an object was created on Device A while iCloud was disabled, and saved. The user then enables iCloud again, and makes a change to the object. Device B will now receive a transaction log for an update to an object which — as far as it is concerned — does not exist. Import of the log will fail, including any other changes that happen to be committed during that save transaction.&lt;/p&gt;

&lt;p&gt;The more entangled the ‘missing’ objects are with the rest of your object graph, the more likely they will be involved in an update, and cause transaction log imports to fail. To the user, it just looks like iCloud is not syncing properly.&lt;/p&gt;

&lt;p&gt;Stoppages are bad news, even if you happen to have an app that otherwise fits perfectly with the utopian workflow described earlier. The only way to get the user’s data consistent again is to destroy iCloud’s data container, allow one device to refill it with data, remove local stores on other devices, and allow them to re-import the new data from the iCloud container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iCloud Seeding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the picture painted above doesn’t seem very rosy, what can you do about it?
The first decision you will need to make is how you will handle data inconsistencies, and what your policy will be for seeding iCloud with data when they arise. You need a way to coordinate devices such that eventually they are all working on the same logical data set.&lt;/p&gt;

&lt;p&gt;To try to make the decision easier, I’m going to layout the options as explicitly as possible in the coming sections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vanilla Seeding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first option is to follow Apple’s basic prescription entirely. You simply setup all your stores with the ubiquitous options enabled. If your app is new, users don’t have pre-existing data, and they never log out of iCloud, this should work quite well.&lt;/p&gt;

&lt;p&gt;But if a stoppage occurs, and syncing becomes unreliable, the fix requires considerable manual user intervention, and is not very palatable. To end up with consistent data across all devices, the user will have to&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Quit/Kill the app on all devices&lt;/li&gt;
&lt;li&gt;Delete the app’s iCloud container via System Preferences on all devices&lt;/li&gt;
&lt;li&gt;Remove the app’s local data store on all but one device&lt;/li&gt;
&lt;li&gt;Launch the app on the device with the intact local store, allowing it to seed the iCloud container.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;When the app is next launched on the other devices, it should import the newly seeded data, and all devices should have a consistent set. But this is far from user friendly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sophie’s Choice Seeding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you don’t want your users to have to go through this, or you have a more complex scenario where there may be existing data on multiple devices, you will probably need a more sophisticated approach. In the Sophie’s Choice approach, whenever inconsistencies arise, the user will get to choose whether they want to keep the data from the cloud, or the data from the local store. This should always result in a consistent data set across devices.&lt;/p&gt;

&lt;p&gt;It is not quite as straightforward as just giving the user a free choice of data set though. If the device has not previously been syncing, and thus has no transaction logs in the iCloud container, they can indeed choose to keep either the local or the cloud data. But if the device has been syncing, and has logs in iCloud, the only option is to replace the iCloud data with the local data set. This is because of the constraints discussed earlier, namely that Core Data will not reimport data it has already imported from iCloud. Deleting the local store and re-enabling ubiquitous options will not lead to all of the iCloud data being imported.&lt;/p&gt;

&lt;p&gt;The Sophie’s Choice variant of seeding requires that your app knows if the device has previously been synced, and if the container has been reset, in order to offer the user the appropriate options when inconsistencies arise. At the time of writing, Core Data gives you no notification of these state changes. In a future post, I will introduce so-called &lt;em&gt;sentinel files&lt;/em&gt;, which will reside in the iCloud container, and through continuous monitoring can notify of changes to the set of syncing devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every-Drop-is-Sacred Seeding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the idea of requiring the user to choose between data sets is not attractive, the last option you have is to attempt to merge data sets whenever a new device is added to iCloud. Unfortunately, if a stoppage occurs, the user will still be forced to fully replace their cloud data in order to guarantee a consistent set of data, but at least it will be possible to merge existing data when devices first start to sync.&lt;/p&gt;

&lt;p&gt;One problem with this approach is that you may end up with duplicate data. To fix this, you will want to have a globally unique identifier for your main entities. You can then weed out and delete the duplicates. You will need to do a sweep for duplicates whenever iCloud data is merged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Migrating Data into iCloud&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Getting data into iCloud is generally not as straightforward as just enabling the ubiquitous store options. You have to be very aware of what data is already in iCloud, and which devices have contributed to that data. Once you know that, you may have to explicitly migrate data from a non-ubiquitous store to a ubiquitous store, to allow Core Data to generate transaction logs.&lt;/p&gt;

&lt;p&gt;If the iCloud container is empty, such that the current device will be the first to sync, you can simply engage the ubiquity options, and the data will be added to iCloud, no migration needed. Core Data generates a &lt;em&gt;baseline&lt;/em&gt; in iCloud, which is effectively a copy of your local store. There has been some discussion as to whether the baseline is actually used, or is just in place for future functionality, but my tests seem to show the baseline is in use. (&lt;b&gt;Update:&lt;/b&gt; Although the baseline is used — the initial data is transferred to other devices — future changes to that data do not seem to propagate properly. At this point, I advise against relying on the baseline for seeding initial data.)&lt;/p&gt;

&lt;p&gt;The baseline is only generated once, for the first iCloud-enabled device. If you have existing data on other devices, it will have to be migrated in, or it will not be synced between devices. Because of this limitation, you may be better off migrating data in on all devices, even the first to sync.&lt;/p&gt;

&lt;p&gt;So how do you actually perform the migration? We will consider this in more detail in the next post, but there are basically two approaches:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Use the persistent store coordinator’s &lt;code&gt;migratePersistentStore:toURL:options:withType:error:&lt;/code&gt; method, together with the appropriate ubiquity options, to perform a wholesale migration of the entire non-ubiquitous local store to a ubiquitous one.&lt;/li&gt;
&lt;li&gt;Add the ubiquitous and non-ubiquitious stores to the persistent coordinator, and copy objects from one to the other in code.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;The later gives you more control over what is imported, but obviously requires more effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To Switch or Not to Switch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One question that I have not addressed in this discussion is whether your app should have a switch that allows iCloud syncing to be disabled independent of the global iCloud settings.&lt;/p&gt;

&lt;p&gt;If you have very little data to sync, I would suggest not bothering the user with a switch. Just assume that if the user is logged into iCloud, they want the app to sync. On the other hand, if your app needs to sync a lot of data, eg. media files, it is probably best to give the option to disable syncing.&lt;/p&gt;

&lt;p&gt;There is a third way: if your app does generate a lot of data, but most of it does not need to be synced, you could include the entities that do need to be synced in one ubiquitous store, and the rest in a non-ubiquitous store. Cross store relationships have to be weak — you have to fetch objects using some identifying property such as a URL or UUID — but this is a relatively small inconvenience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next Time…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This has been a very high level discussion of some of the issues you encounter when seeding iCloud with data. Next time, I will introduce a test app, and dig down into the code that you will need. If you want to get an early look, the test app is already on &lt;a href="https://github.com/drewmccormack/iCloudCoreDataTester"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/23788055417</link><guid>http://mentalfaculty.tumblr.com/post/23788055417</guid><pubDate>Sat, 26 May 2012 05:38:00 -0400</pubDate><category>iCloud</category><category>Coredata</category><category>software</category><category>Mac</category></item><item><title>Under the Sheets with iCloud and Core Data: How it Works</title><description>&lt;p&gt;Apple&amp;#8217;s documentation for Core Data syncing via iCloud offers very little insight into how it actually works. This is probably quite deliberate: Apple sees this as an implementation detail that developers should not concern themselves with. Unfortunately, if you have no concept of what is happening behind the scenes, at least in vague terms, it is difficult to grasp what you should expect during the syncing process.&lt;/p&gt;

&lt;p&gt;To give a concrete example, I was initially under the impression that importing of transaction logs took place at a low level, independent of my application code. This is not actually the case. The import uses the same model classes that the rest of the  app uses, and calls the same custom accessor methods. If those accessors cause side effects, such as the creation of new objects, the import fails. Not understanding this &amp;#8216;implementation detail&amp;#8217; ended up costing me a couple of weeks of head scratching.&lt;/p&gt;

&lt;p&gt;We will return to side effects in a future post, but today we will focus on how iCloud syncing works behind the scenes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pushing Bits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At its heart, iCloud is not much more than a folder — the iCloud container — that syncs its contents between devices, just like &lt;a href="http://dropbox.com"&gt;Dropbox&lt;/a&gt;. The folder is not readily visible to an end-user — it is hidden away at ~/Library/Mobile Documents — but it is there, and if you drop a file in, it will magically appear on your other iCloud devices.&lt;/p&gt;

&lt;p&gt;So how does Core Data fit in? At first you may be tempted to think Core Data is talking to an intelligent server in the cloud, one that understands your entity relationships. This is how Core Data syncing worked under MobileMe. But iCloud syncing is an altogether simpler affair on the server side: the server has no understanding of iCloud at all, it just pushes bits from one device to another.&lt;/p&gt;

&lt;p&gt;The intelligence lies entirely in the Core Data framework on each client device. The framework registers changes when your app saves, and writes them away as transaction log files in the iCloud container. iCloud copies the files to the cloud, and then to your other devices. The Core Data framework on these devices notices the new log files, and imports them.&lt;/p&gt;

&lt;p&gt;In other words, where with MobileMe there was a single truth database in the cloud, there is now just a collection of transaction logs from different devices, and a number of clients doing their best to independently reconcile those logs. As long as the updates are reasonably distinct, it works well, but when changes overlap considerably, you can get quite tricky situations. A lot of the discussion in this series of posts revolves around working around the conflicts that can arise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction Logs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you hear the term &lt;em&gt;transaction log&lt;/em&gt;, you may be led to think that it is some low level log file generated by the underlying SQLite database, but the logs are actually just property lists. If you dig into the iCloud container of your app, you will find a number of Core Data Transaction files with the extension .cdt. Each one is a zipped plist file; if you unzip one of them, you should find it contains a file called &lt;em&gt;contents&lt;/em&gt;, and this can be opened as a property list in a text editor.&lt;/p&gt;

&lt;p&gt;The iCloud container is a subfolder of ~/Library/Mobile Documents; it has the same reverse-DNS name used in the application code, but with all periods replaced by tildes (e.g. &lt;team id&gt;~com~mentalfaculty~mentalcase). Inside the container, you should find one subfolder for each user-device combination. If you dig even deeper into the folder of one of the devices, you will see a folder for each Core Data store that is being synced, and — a few levels deeper — the CDT files themselves.&lt;/team&gt;&lt;/p&gt;

&lt;p&gt;It is not really necessary to understand the contents of a CDT file, because it is an implementation detail that can change, but in my view it does help to break down some of the mystery of how iCloud—Core Data syncing does its thing, so I want to go through it in some detail.&lt;/p&gt;

&lt;p&gt;The structure of the CDT properly list is reasonably transparent. It begins with an array mapping each object in the log file to an entity, and a primary key in the local database.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; {  compressedGlobalIDs = ( "0:0:0", "1:1:0", "2:2:0", "3:3:0", "4:4:0", "5:0:0", "4:0:0", "1:5:0", "0:6:0",
        "0:7:0", "5:8:0", "0:8:0", "5:4:0", "5:9:0", "1:4:0", "2:10:0", "3:11:0", "4:8:0",
        "1:12:0", "0:13:0", "4:14:0", "1:15:0", ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first number in each tuple is the index of an entity in the entityNames array.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;entityNames = ( "MCNoteFacet", "MCNote", "MCNoteFacetRole", "MCScalableText", "MCFacetPermutation",
    "MCMediaFile", "MCNoteOccurrence", "MCManualCase", "MCCollectionCase", "MCNoteTemplate",
    "MCSlideshow", "MCScheduleManager"
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The second number in each tuple is the primary key in that entity&amp;#8217;s SQLite table. (I have not been able to determine what exactly the third number is for.)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;primaryKeys = ( "p1", "p8", "p4", "p32", "p27", "p12", "p48", "p18", "p2", "p46", "p10", "p52", "p9",
    "p15", "p3", "p26", "p45", "p58", "p6", "p5", "p24", "p71", "p11", "p29", "p41", ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Core Data keeps track of objects across different stores by assigning unique identifiers to inserted objects.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;externalDataReferencesInfo = { inserted = ( "1EB4B045-FD76-4791-937B-96BEF29462AD", "BEDDFF4B-6C77-43D3-977E-D1B6734CCFAE", 
"29E8179F-A896-4775-9782-597C2ED92B82", "EF4733C4-C317-41B8-91D8-A4E64BC5D067", ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Dictionaries are included for all deletions, insertions, and updates. The keys for the dictionaries are the indexes of the corresponding objects in the compressedGlobalIDs array. These indexes are also used to refer to objects that are the target of relationships.&lt;/p&gt;

&lt;p&gt;For example, in the insertion dictionary that follows the key is 0, which means the inserted object corresponds to the first item in the compressedGlobalIDs array above. That object has tuple &amp;#8220;0:0:0&amp;#8221;. The first 0 refers to the index of the entity in the entityNames array, which is MCNoteFacet. The second 0 is the primary key in the MCNoteFacet SQLite table, which is &amp;#8220;p1&amp;#8221;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;inserted = {
    0 = {
        appearsInSlideshows = 1;
        backgroundColor = &amp;lt;312E3030 30303030 20312E30 30303030 3020312E 30303030 30302031 2E303030 303030&amp;gt;;
        canBeCombinedOnSlide = 0;
        canBePrompt = 0;
        isFactual = 1;
        note = "1";
        noteFacetRole = "2";
        orderIndex = 1;
        promptFacetPermutations = ( );
        responseFacetPermutations = ( "4" );
        scalableText = "3";
        slideFacetOccurrences = ( );
        uniqueId = "2BAC1A44-77AA-466C-A64D-4C0426CCCA07-44703-000132BEDC7A394E";
    };
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see that the value corresponding to the key is a dictionary of properties, with relationships given as indexes. For example, the property &lt;em&gt;note&lt;/em&gt; is a to-one relationship, and has the value &amp;#8220;1&amp;#8221;. The related object is thus the second object in the compressedGlobalIDs array. To-many relationships are given as arrays of indexes.&lt;/p&gt;

&lt;p&gt;(A big thanks to Christian Beer for helping me understand the property list structure.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Importing Transactions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When import logs are transferred to a device, Core Data attempts to import them into the local store. The exact details of this are very vague. Basically, the process is private, and Apple do not provide any hooks into the import procedure.&lt;/p&gt;

&lt;p&gt;It seems that Core Data sets up a private NSPersistentStoreCoordinator, and applies the changes in an associated private NSManagedObjectContext. The managed object context saves, pushing the changes into the store, before posting a notification to alert other contexts to refresh the updated objects. (Thanks to Marcus Zarra for clarifying aspects of this procedure for me.)&lt;/p&gt;

&lt;p&gt;You would think, with the import process being a private affair, that your own code could play no role, but it is important to realize that custom accessors and validation methods from your custom NSManagedObject subclasses are used in the background context, and thus do have a very important influence on the success or failure of transaction imports. We will discuss the practicalities of this in a future post.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/23231176783</link><guid>http://mentalfaculty.tumblr.com/post/23231176783</guid><pubDate>Thu, 17 May 2012 11:32:19 -0400</pubDate><category>icloud</category><category>coredata</category><category>sync</category><category>software</category><category>mac</category><category>ios</category></item><item><title>Under the Sheets with iCloud and Core Data: The Basics  </title><description>&lt;p&gt;There can be no doubt that iCloud is an important part of Apple’s future. It’s now less than a year old, but it has been a mixed start. The service itself seems up to the enormous data traffic it has to push, but some aspects of client-side integration are still very rough around the edges.&lt;/p&gt;

&lt;p&gt;You don’t have to look far in Apple’s developer forums to know that Core Data syncing is one of those rough spots. An appeal on Twitter, and extensive Googling, led me to just one shipping app that currently offers Core Data syncing over iCloud: &lt;a href="http://itunes.apple.com/us/app/time-butler/id460856856?mt=12"&gt;Time Butler&lt;/a&gt;. I know of no other shipping app that is using it, and certainly no apps from well-known, established developers.&lt;/p&gt;

&lt;p&gt;(All eyes are currently on &lt;a href="http://barebones.com/"&gt;Bare Bones&lt;/a&gt;, who have vowed to offer iCloud syncing in their shoebox app &lt;a href="http://barebones.com/products/yojimbo/"&gt;Yojimbo&lt;/a&gt; before MobileMe syncing is turned off in a few months. Time will tell if they achieve that goal.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mental Case&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve spent the last 3-4 months integrating Core Data syncing over iCloud into &lt;a href="http://mentalcaseapp.com"&gt;Mental Case&lt;/a&gt; for Mac. It is not in the wild yet, but we have started beta testing with a limited audience. &lt;/p&gt;

&lt;p&gt;To say it has been a challenge would be an understatement — it has probably been one of the hardest tasks I have ever undertaken for a Mac or iOS app. And so, in an effort to move the state of Core Data/iCloud syncing forward, I have decided to pen a number of posts, in the hope that others will be able to cross the same hurdles faster.&lt;/p&gt;

&lt;p&gt;Apple has very little in the way of documentation on Core Data syncing. They do explain the basics of setting up your app, loading your Core Data store, and handling merge notifications, but they don’t go into any detail about how syncing works, and certain scenarios that you will need to deal with in a shipping app. That’s what I plan to cover in these posts.&lt;/p&gt;

&lt;p&gt;(Before getting started, it is worth pointing out that Daniel Pasco of &lt;a href="http://blackpixel.com/"&gt;Black Pixel&lt;/a&gt; delivered an excellent presentation at &lt;a href="http://www.nsconference.com/"&gt;NSConference&lt;/a&gt; 2012, covering many of the issues that I’ll be discussing. You can purchase a &lt;a href="http://ideveloper.tv/nsconference/"&gt;video&lt;/a&gt; of his talk from the NSConference site.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some Restrictions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s start with a few gotchas, some of which wasted days of my time, and are completely undocumented.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Core Data syncing via iCloud does not work in apps using Garbage Collection. This is not documented, it simply does not work. If you have a GC app, time to move to ARC.&lt;/li&gt;
&lt;li&gt;iCloud syncing only works for apps sold through the Mac App Store. If you plan to sell your app outside the Mac App Store, you will have to come up with creative ways to enable iCloud syncing. (PDFpen managed to get iCloud syncing enabled for off-store copies by selling a small &lt;a href="http://itunes.apple.com/nl/app/pdfpen-cloud-access/id494133864?mt=12"&gt;cloud sync enabler&lt;/a&gt; app via the Mac App Store.)&lt;/li&gt;
&lt;li&gt;You can only use automatic migration for your iCloud data. If you need to do more extreme migrations, you will need to setup some sort of store versioning, and copy your data to a whole new store.&lt;/li&gt;
&lt;li&gt;You cannot use ordered relationships.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;If you can cope with these restrictions, you are ready to face iCloud.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting Up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the next few sections, I’ll quickly skim through what Apple covers in its &lt;a href="https://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/iCloud/iCloud.html"&gt;own documentation&lt;/a&gt;. This is the bare minimum that you need to get iCloud Core Data syncing working. Unfortunately, it is far from enough to get your app to the production stage.&lt;/p&gt;

&lt;p&gt;The first step is to login at the &lt;a href="https://developer.apple.com/devcenter/mac/index.action"&gt;Mac Dev Center&lt;/a&gt; and click the link to go to the Developer Certificate Utility. (I will discuss iCloud from the perspective of Mac app development, but much of the information will apply equally to an iOS app.)  Select the App IDs, and configure your app to use iCloud. Having done that, you will have to regenerate any provisioning profiles for your app, and install them in Xcode.&lt;/p&gt;

&lt;p&gt;Now in Xcode, you should select your app’s target, and in the Summary tab, enable entitlements. You can add one or more iCloud containers here; these correspond to folders in iCloud. The first one should have the same name as the bundle identifier of your app (e.g. com.mentalfaculty.mentalcase.mac). Additional containers can be added with other names. You should use the reverse-DNS naming convention for these containers. Extra containers could be used, for example, to share data between multiple apps, such as when you want to support cross-platform syncing of Macs and iOS devices.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.tumblr.com/tumblr_m446y32fFH1rpzb44.png"/&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iCloud Containers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your code, you can request a file URL for your iCloud container using the new NSFileManager method URLForUbiquityContainerIdentifier:. If you only have one container, you can pass in nil. If you have more than one, you need to pass in an identifier.&lt;/p&gt;

&lt;p&gt;The identifier is comprised of your Team Identifier Prefix, followed by the container id you entered in the entitlements section (e.g. “&amp;lt;Team ID&amp;gt;.com.mentalfaculty.mentalcase”). So where do you find the Team Identifier Prefix? If you go to the &lt;a href="https://developer.apple.com/membercenter/"&gt;Member Center&lt;/a&gt; of the Apple Developer site, and you click the organization link at the top, you should see an entry called ‘Company/Organization ID’. That is the Team Identifier Prefix you need to use.&lt;/p&gt;

&lt;p&gt;You should call URLForUbiquityContainerIdentifier: early on in an app’s launch. If it returns nil, it means iCloud is not enabled on the current device. If it returns a file URL, iCloud is active, and the URL gives you a path to the iCloud container, which is a folder in ~/Library/Mobile Documents. Anything you put in there will get synced to other devices using the same container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting Up the Core Data Stack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having tested whether iCloud is turned on, you are ready to setup your Core Data stack. This is almost the same as when you aren’t using iCloud, with some small differences. If iCloud is on, i.e. if URLForUbiquityContainerIdentifier: returned a URL, you should add extra options for your persistent store.&lt;/p&gt;

&lt;pre&gt;    NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObjectsAndKeys:
        [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
        [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, 
        nil];

    // Add cloud options    
    if ( storeURL ) {
        [options addEntriesFromDictionary:
            [NSDictionary dictionaryWithObjectsAndKeys:
                @"com.mentalfaculty.mentalcase.mainstore.1", NSPersistentStoreUbiquitousContentNameKey,
                storeURL, NSPersistentStoreUbiquitousContentURLKey, 
                nil]];
&lt;/pre&gt;

&lt;p&gt;The value for NSPersistentStoreUbiquitousContentNameKey should be a unique name for the store. It gets mapped to a subfolder in iCloud. The container URL you retrieved above should be passed in with the key NSPersistentStoreUbiquitousContentURLKey.&lt;/p&gt;

&lt;p&gt;With the options all ready, you can add your persistent store to the persistent store coordinator.&lt;/p&gt;

&lt;pre&gt;    dispatch_queue_t backgroundQueue = dispatch_get_global_queue(0,0);
    dispatch_async(backgroundQueue, ^{
        NSError *error;
        [persistentStoreCoordinator lock];
        diskStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:localURL options:options error:&amp;amp;error];
        [persistentStoreCoordinator unlock];
&lt;/pre&gt;

&lt;p&gt;The localURL is simply the file URL that you are using for the store. This will usually be in the user’s Library folder somewhere (e.g. Application Support folder).&lt;/p&gt;

&lt;p&gt;When you invoke the addPersistentStore… method, it is important to do it on a background thread. With iCloud enabled, this method can block execution for some time, while iCloud retrieves any files it might need from the servers.&lt;/p&gt;

&lt;p&gt;With a persistent store coordinator setup, you can now create an NSManagedObjectContext. You probably don’t usually pay much attention to the merge policy it is using, but when integrating with iCloud, you should change the mergePolicy property to something other than the default. A reasonable choice is NSMergeByPropertyObjectTrumpMergePolicy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observing Merge Notifications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The last part of a basic iCloud/Core Data setup is to listen for notifications that iCloud has merged changes from another device. Each merge corresponds to a save performed on another device, so it is possible that several notifications fire off in short succession, as iCloud imports the changes one save at a time.&lt;/p&gt;

&lt;p&gt;To be notified of merges, simply observe the NSPersistentStoreDidImportUbiquitousContentChangesNotification notification (Is that the longest name in Cocoa?), which is sent by your persistent store coordinator. When you receive the notification, you need to merge the changes into your managed object context in much the same way that you would merge save changes when using multiple contexts.&lt;/p&gt;

&lt;pre&gt;-(void)persistentStoreCoordinatorDidMergeCloudChanges:(NSNotification *)notification
{
    [managedObjectContext performBlock:^{        
        [managedObjectContext mergeChangesFromContextDidSaveNotification:notification]; 
&lt;/pre&gt;

&lt;p&gt;You will also need to consider whether managed objects that are retained in your app could be invalidated by a merge, and perhaps need to be re-fetched when the notification fires.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Devil in the Details&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s basically all that Apple will tell you about Core Data syncing with iCloud, and also all you will tend to find in tutorials on the subject. You could mistakenly be led to believe that integrating iCloud in your Core Data app will be a walk in the park. Unfortunately, at this juncture, nothing could be farther from the truth.&lt;/p&gt;

&lt;p&gt;In the coming posts, we will consider the devil in the details, including…&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;How iCloud Core Data syncing works under the hood.&lt;/li&gt;
&lt;li&gt;How you get existing data into iCloud from multiple devices.&lt;/li&gt;
&lt;li&gt;Migrating your app from an existing, legacy sync scheme.&lt;/li&gt;
&lt;li&gt;What happens when a device has iCloud temporarily disabled.&lt;/li&gt;
&lt;li&gt;How you can handle conflicts and validation failures.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Stay tuned.&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/23163747823</link><guid>http://mentalfaculty.tumblr.com/post/23163747823</guid><pubDate>Wed, 16 May 2012 12:51:00 -0400</pubDate><category>coredata</category><category>icloud</category><category>mac</category><category>ios</category><category>software</category></item><item><title>Fallacies of the Indie Software Business</title><description>&lt;p&gt;&lt;span class="Apple-style-span"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="Apple-style-span"&gt;There’s a lot of talk about upgrade pricing on the Mac App Store. Unfortunately, a lot of it is based on guess-work about how the software market works, by people who don’t actually sell software themselves. Here are some of the bigger fallacies being propagated.&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class="Apple-style-span"&gt;The market is completely efficient. As soon as an app is released, every user has noticed this, and considered whether to purchase it. &lt;/span&gt;&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Once someone has considered purchasing a piece of software, and rejected it, they will never consider it again.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Only people new to a platform buy software.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;A small indie developer can easily saturate their market.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Mac App Store customers are used to paying for software upgrades.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Software developers can only survive without upgrade pricing if the platform has hockey stick growth.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Software developers rely on upgrade spikes to survive, because — at other times — they have no sales.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Software developers earn most of their income from sales spikes associated with an upgrade.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Writing a new app is just as fast and easy as adding features to an existing app, so there is no motivation to upgrade software without upgrade pricing.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;Upgrading an app can only generate income via existing users. There are no other advantages to upgrading an app.&lt;/p&gt;
&lt;/li&gt;
&lt;li class="full-width"&gt;
&lt;p class="paragraph_style"&gt;It is more useful for a developer to spend their whole life adding features to one app than to develop multiple apps.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description><link>http://mentalfaculty.tumblr.com/post/23156668232</link><guid>http://mentalfaculty.tumblr.com/post/23156668232</guid><pubDate>Mon, 16 Apr 2012 20:12:00 -0400</pubDate><category>macappstore</category><category>software</category><category>business</category></item><item><title>Paid Upgrades and the Mac App Store</title><description>&lt;p&gt;&lt;p class="paragraph_style"&gt;If you hang around iOS and Mac developer circles enough, you&amp;#8217;re sure to hear some complaints about how Apple runs its App Stores, and one of the most common is that they don&amp;#8217;t support paid upgrades. Yesterday, Wil Shipley &lt;a href="http://blog.wilshipley.com/2012/03/mac-app-store-needs-paid-upgrades.html"&gt;presented the case&lt;/a&gt; for paid upgrades on his blog, with some supporting analysis of Delicious Library 2 sales through the years.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;Shipley has had success redirecting Apple in the past, most recently with a &lt;a href="http://blog.wilshipley.com/2011/11/real-security-in-mac-os-x-requires.html"&gt;campaign&lt;/a&gt; to introduce Apple-issued developer certificates, which will make their debut via the Gatekeeper feature in Mountain Lion. But on this one, I think he has it wrong, and I doubt very much that Apple will be shifted. Here&amp;#8217;s why.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;Apple has spent the last 5 years or so cultivating certain expectations in how people purchase digital goods via their stores. If you buy a song in iTunes, it&amp;#8217;s yours — forever. If you buy an app in the iOS App Store, it&amp;#8217;s yours — forever. And now, if you buy an app in the Mac App Store, it&amp;#8217;s yours — (you guessed it) forever. It&amp;#8217;s part of what has made Apple so successful in this area. One click purchase, instant download, yours forever.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;What Wil is asking Apple to do is to break this implied contract with its customers. Effectively, he wants customers to pay twice for the same product, and I don&amp;#8217;t see Apple going down that route. It could actually be hurtful to developers in some ways. One of the reasons people so readily part with their cash in the App Stores is that they know they get to keep what they buy, and even transfer it to any new devices they acquire. They own their software, they don&amp;#8217;t rent it.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;Wil&amp;#8217;s blog post goes into some reasonably detailed analysis of Delicious Library sales trends, concluding that offering free upgrades would result in his company ‘&amp;#8230;losing a quarter of our revenue&amp;#8217;. Fortunately, his arguments are oversimplified and based on pre-App Store sales models. Some of the analysis is also a bit disingenuous. For example, he shows that for the 18 months after the release of Delicious Library 2, 24% of sales revenue came from upgrades. A closer look shows that most of that probably resulted from the spike immediately following release, and that in fact, taken over the full four years since the app was released, upgrade sales would be much less than 24%.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;But even if upgrade sales did account for a quarter of all revenue, it is not valid to conclude that revenue would suffer an equivalent drop if upgrade pricing was not available. The Mac App Store does not offer this stream of income, but does offer other streams that in my experience more than balance any losses. I already mentioned that customers are encouraged to buy apps by the ease with which they can do it in the App Store, and by the knowledge that whatever they purchase becomes a digital possession. It&amp;#8217;s difficult to put a number on how large this effect is, but I am convinced by my own app sales that it is significant.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;Giving away major upgrades to existing customers has a second positive effect on sales. Because all of your existing customers get the upgrade free, most will download it, and many will tweet about it, and generally encourage friends to take a look at the app. Word of mouth marketing is an enormous boost to any new release, and the more existing customers actually install an upgrade, the better. This could easily make up for the 24% loss in upgrade sales on its own, especially if you take into account the non-linear sales effects you see as you push up the charts.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;Shipley implies throughout his post that making major upgrades free would effectively discourage developers from updating their software, because it would not lead to any extra revenue, in the form of a spike in sales. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p class="paragraph_style"&gt;But, with free major upgrades, we have this unhappy choice: for the next two years we can work on a brand-new product (call it “Delicious D”) or we can work the next version of “Delicious Library ∞”. If we write “Delicious D” we know you’re going to give us another $40 of your money, because it’ll be so awesome. But if we write “Delicious Library ∞” (and we give away major upgrades) then we’re going to get $0 of your money when we’re done.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p class="paragraph_style"&gt;This is patently false, and any developer who has ever released a major upgrade in the Mac App Store knows it. There is a very large spike in sales, which comes from the combined effect of increased word of mouth from existing customers, featuring by Apple, and increased visibility when the app begins to climb the charts. When I released Mental Case 2 — exclusively on the Mac App Store — I saw an order of magnitude more sales in the days after the release than previous to that. It is simply not true that free upgrades do not generate any additional income.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;Ironically, it was Wil who once explained how the pool of potential customers was virtually infinite for any indie developer, that you will never even come close to saturating your market. This is exactly the reason why the App Store works, and does not need to include upgrade pricing. There are always many more shoppers out there than you currently have customers. Nickel and dime-ing your existing customers with paid upgrades means they will resent you, and you are taking your eyes off the bigger prize, the enormous body of potential new customers which should be your target.&lt;/p&gt;
&lt;p class="paragraph_style"&gt;Wil Shipley has been making money in this industry longer than most of us, and frankly, I think he is falling into the same trap that the music industry fell into when Napster came to town. What worked yesterday will not necessarily work tomorrow. The game has changed, and Wil wants to keep doing things the way he always has, when his audience is moving on. &lt;/p&gt;
&lt;p class="paragraph_style"&gt;By insisting old sales models get incorporated into the App Store, he is prolonging the day when customers forget there ever was such a thing as a paid upgrade, and that is hurting the transition for all of us. Rather than trying to move Apple, he should be adapting his software to the new way of doing things. No paid upgrades, but plenty of upgrade revenue.&lt;/p&gt;&lt;/p&gt;</description><link>http://mentalfaculty.tumblr.com/post/23156117883</link><guid>http://mentalfaculty.tumblr.com/post/23156117883</guid><pubDate>Wed, 28 Mar 2012 20:12:00 -0400</pubDate><category>macappstore</category><category>software</category><category>upgrades</category></item></channel></rss>
