Dictionary Fictionary

See update below: it’s more complicated than I thought.

Over the last, what, decade or so? maybe more? people have been using the word “literally” to mean…not literally.

“Literally a monster.”

“Literally the worst thing ever.”

They don’t mean literally literally. They’re using it because it feels right, regardless of its actual definition. It can lead to some amusing results.

I’ve seen something similar in Objective-C.1

When array, dictionary, and number literals and object subscripting were introduced in Objective-C in 2012, people really liked them. Me too! They were literally mostly just compiler syntactic sugar, but they derived their usefulness in part from being vastly more compact and readable.

Because the new syntax is so nifty, people want to use it everywhere.

NSMutableDictionary *foo = @{}.mutableCopy;

instead of

NSMutableDictionary *foo = [NSMutableDictionary new];

for example. Are they equivalent? Well, this is what the first sample is generating:

NSMutableDictionary *foo = [NSDictionary new].mutableCopy;

Making two dictionary instances, only to throw one away immediately, just because you want to use shiny syntax, doesn’t seem like the best idea to me.

Array and dictionary literals derive the rest of their useful from not having quite the same rules as the old syntax.

Unlike for the old creation APIs, now nil is no longer used as the ending sentinel for the list of input values. So because they could, the designers of the new syntax disallowed it completely.

The amusing part? Recently, I had a coworker who swore you couldn’t set a dictionary value to nil, like this:

foo[@"key"] = nil;

What he was doing was confusing the new literal syntax rules with plain old NSDictionary APIs. It’s an easy mistake to make. The only reason I didn’t is because I have years and years of experience with the old APIs, which didn’t have the same hangups about nil.

So I could compartmentalize the new nil rules to just the new literal syntax, where they belong.

I literally needed to run example code for him to convince him that the above line wouldn’t assert.

Update: Looks like I was right for the wrong reasons.

Instead of it always working the way I thought, the current behavior is a recent addition. See Foundation Release Notes for OS X v10.11 and iOS 9, specifically this part:

NSMutableDictionary subscript syntax change
In OS X 10.11 and iOS 9, NSMutableDictionary now allows you to use its subscript syntax to assign nil for a key. Like Swift’s Dictionary type, doing so will remove an existing object for that key from the dictionary. In effect, the following lines of code are now equivalent:

[dictionary removeObjectForKey:@"Key"];
dictionary[@"Key"] = nil;

These new semantics exist only when building with the OS X 10.11 or iOS 9 SDKs. If your application’s deployment target is earlier operating system, then runtime support will be implicitly linked into your binary by Xcode to ensure the behavior works on any targeted operation system.

My mistake was assuming the subscript setter syntax was an unmodified usage of NSMutableDictionary’s setObject:forKey:, which in my recollection always allowed nil, but of course there’s no reason it would have to be.

And now, with nullability annotations, setObject:forKey: no longer allows nil at all! (Plus, on reflection, I think I was wrong about it allowing nil in the past.)

Thanks to Jordan Rose and others for the correction.


1. Somebody get me a storyboard, cuz I just made a killer segue! ↩︎

Link or Swim

I learned today that you can’t make a linked list in Swift using value types. The reason why ties into the pointers issue I was recently discussing.

Here’s how a linked list struct might look in C:

struct LinkedList {
	struct LinkedList *next;
	int data;
};

And here’s how you would do it in Swift:

struct LinkedList {
	var next: LinkedList?
	var data: Int
}

The difference is, in C, you can have a reference to a struct without making a copy — without it being the thing itself. By adding an asterisk and making it a pointer.

In Swift, you can’t do that. So a reference to another struct might as well be that other struct, even if it isn’t always, under the hood.

If I try to compile that Swift, I get the error “value type ‘LinkedList’ cannot have a stored property that references itself”.

If I change the declaration from struct to class, then it compiles fine, because the property representing the “next” instance is now a reference to it.

I wasn’t expecting this issue to come up again so soon in my work.

Poll Position

I conducted a Twitter poll recently because I was curious: when I use Objective-C for my coding examples on this blog, does that still seem normal to my readers, or does it seem like some weird ancient relic of a bygone era?

Now, my Twitter followers and my blog readers might not be exactly the same, and there were < 40 responses so it’s not a huge sample, but I still think the results are illuminating:

poll-results

There’s no clear majority. About the same number are using primarily Swift as primarily Objective-C.

That sounds about right to me.

And it means I don’t need to change my ways this second.

But I do plan on switching over to using Swift for my coding examples at some point during the year.

Entirely Missing the Pointer

Whenever I read about Swift, I read about the distinction between reference types and value types.

In the C-based languages I used (and still use), I never thought about it like that. Instead, I thought in terms of pointers1 and everything else, which here I’ll call non-pointers.

You could have a pointer to anything, but in Objective-C they are used especially for class instances. And you could have a “non-pointer” to anything, including scalars, structs, and (in C++) class instances.

And it’s always been visually easy to distinguish between the two: one has an asterisk, and one doesn’t.2

Check it out. The ones with the asterisks have reference-based semantics, and the ones without the asterisks have value-based semantics:

Reference:

@interface Foo : NSObject
@property int bar;
@end

@implementation Foo
@end

struct Bar {
	int foo;
};

void referenceTest() {
	int intStorage = 0;
	int *myInt1 = &intStorage;
	int *myInt2 = myInt1;
	*myInt1 = 10;
	printf("%d\n", *myInt2); // Result: 10, same as myInt1
	
	struct Bar barStorage = { 0 };
	struct Bar *bar1 = &barStorage;
	struct Bar *bar2 = bar1;
	bar1->foo = 10;
	printf("%d\n", bar2->foo); // Result: 10, same as bar1
	
	Foo *foo1 = [Foo new];
	Foo *foo2 = foo1;
	foo1.bar = 10;
	printf("%d\n", foo2.bar); // Result: 10, same as foo1
}

Value:

// Same declarations/definitions as above

void valueTest() {
	int myInt1 = 0;
	int myInt2 = myInt1;
	myInt1 = 10;
	printf("%d\n", myInt2); // Result: 0, not same as myInt1
	
	struct Bar bar1 = { 0 };
	struct Bar bar2 = bar1;
	bar1.foo = 10;
	printf("%d\n", bar2.foo); // Result: 0, not same as bar1
}

Note in Objective-C we can’t have a value-based version of the class. (Though we could in C++.)

Swift has a completely different philosophy. Reference vs. value isn’t syntax-based, it’s identity-based. The exact same syntax will produce different results depending on the original definition of what you’re working on.

Reference:

class Foo {
	var bar: Int = 0
}

var foo1 = Foo()
var foo2 = foo1

foo1.bar = 10
foo2.bar // Result: 10, same as foo1

Value:

struct Foo {
	var bar: Int = 0
}

var foo1 = Foo()
var foo2 = foo1

foo1.bar = 10
foo2.bar // Result: 0, not same as foo1

The only difference in the two samples above is the class vs. struct keyword.

Because that’s such a stark philosophical gap, and for me, an unexamined one, it was quite hard for me to get my mind around it at first.

Notes:

  • For me, Java was the first mainstream language that removed the asterisk for reference types and stopped calling them “pointers”. Though amusingly, they still have a java.lang.NullPointerException, which I’ve always assumed has to be confusing to newbies!
  • Swift further muddies my concept of reference-as-pointer and value-as-non-pointer by allowing multiple value type instances to actually point to the same memory as long as you don’t modify their contents, which C-based value types never did. So Swift value types can now actually be implemented by C-style pointers under the hood.
  • For Objective-C users, our first taste of this kind of thing was with blocks. A newly-created block is kind of a “value” type, created on the stack like all other non-pointer C types. But then you copy it, and it becomes a kind of “reference” type you can pass around outside of the function scope. Same syntax for either type, just like Swift.

1. Here, I define “pointer” as an explicit C reference to memory, detached from its management. Non-pointers are still referencing memory locations, but the runtime manages their creation and destruction as part of something larger: the stack, a class or struct, etc. ↩︎
2. C++ muddied this distinction a bit by introducing references, though they had the decency to give it a difference punctuation mark. ↩︎

Keyboard out of My Mind

Four years ago (!) I wrote up the best practices I could find for dealing with the appearance and disappearance of the keyboard on iOS.

Since then, did they break? Any guesses?

Anyone?

Actually, they mostly still work.

If you’ll remember, the big problem was that iOS simply wouldn’t tell us when we were rotating. Instead, the news was delivered in separate, unconnected keyboard-will-hide and keyboard-will-show notifications.

We got around that by, in the will-show notification, starting our animation from the current point (via UIViewAnimationOptionBeginFromCurrentState), instead of from all the way at the bottom of the screen.

On iPhone for iOS 10, as far as I can tell, that trick doesn’t work anymore. (It still does on iPad.) Your animation begins at the bottom of the screen, out of sync with the actual keyboard movement, no matter what options you set.

But in the meantime, iOS has introduced an API that does give us access points both for the start and end of a rotation animation, viewWillTransitionToSize:​withTransitionCoordinator:.

This method is called at the start of the rotation, so that’s one access point accounted for.

And if you call the transition coordinator’s animateAlongsideTransition:​completion: method with a completion block inside it, per Apple’s best practices, the completion block is where you can do things guaranteed to occur after the rotation is over. So that’s the second access point accounted for.

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    // BEGINNING OF ROTATION
    [coordinator animateAlongsideTransition:^(id  _Nonnull context) {
    } completion:^(id  _Nonnull context) {
        // END OF ROTATION
    }];
}

Now, this API is meant to be more general-purpose than just rotation: it’s called for every multitasking size change on iPad. But we can find out whether it is a rotation transition by checking whether the new size’s width and height are the same as the old size’s height and width. If it is, now we set a flag that, in our will-hide logic, can be used to not animate at all under these circumstances.

So that’s it! One fairly small fix.

However, since it has been four years (!), I’ve made a number of changes beyond that in the GitHub project:

  • A new project, Corrected Keyboard Schmeeboard2, has been updated to Xcode 8 standards, Auto Layout, and more, without otherwise changing the code.
  • A second new project, Corrected Keyboard Schmeeboard3, has the iOS 10 iPhone rotation fix on top of the changes from 2.
  • And the last new project, Corrected Keyboard Schmeeboard4, converts the whole thing into Swift.

I’ll have more to say about Swift in future posts.

Framing Your Work

1/21/2014: Update #1: forgot a step! New step 8 at end. Update #2: updated step 5.

As of iOS 8, Apple is allowing dynamic frameworks in iOS applications submitted to the App Store.

Hey, it’s easy! Just make a new Cocoa Touch Framework target in your project. And you’re done!

(Hint: you’re not done.)

There’s actually a bunch of mistakes you (read: I) can make while converting your Objective-C code to use iOS 8 frameworks, so I’m writing it all down while I’m thinking about it.

1. Move all your files over to the framework.
It’s easy to miss a couple. And it may look like everything’s working if you also forget to remove them from your app (see 2), except you’ll get linker errors when you later try to compile your app extension.

.m files go in Compile Sources, .h files go in Headers (see 3), .xib and .storyboard files go in Copy Bundle Resources. This is all the same as for apps, except for Headers, which I’ll get to in a moment.

If you do it through the Target Membership pane of the utility area to the right of the Xcode window, adding them to the right Build Phase is done for you automatically when you click the checkbox for the framework target.

2. Remove all your files from other targets.
The Target Membership pane is the easiest way to do this, as you can both add and remove files from targets there. And you can multiple-select files in the Project Navigator on the left-hand side of the Xcode window, and do it all at once.

What I would expect, if you compile and link a .m file for both your app and your framework, is some sort of duplicate symbol error.

What you get, in Xcode 6 beta 2 at least, is “Definition of ‘DuplicateSymbol’ must be imported from module ‘MyFramework.DuplicateSymbol’ before it is required.” Which is confusing, to say the least.

3. Set the right visibility for your headers.
Like with OS X frameworks, with iOS frameworks, if you add a header to the framework target, it is be default put in the “Project” visibility group. This, like “Private”, means it will not be available to apps or extensions that link against the framework. If you want your header to be visible to them, you must change its visibility to “Public”.

For the curious: (a) “Project” headers aren’t shipped with the framework binary. (b) “Private” headers are shipped, but in a separate directory, called “PrivateHeaders”, which, as far as I know, is only visible if you’re Apple.

4. Rename your imports.
Here’s where things get fun.

First of all, keep in mind that, as far as linking is concerned, the name of your framework is the Product Name, not the name of your target. Normally, these are the same, since the PRODUCT_NAME build setting is set to $(TARGET_NAME). But it’s something to keep an eye on if you get build errors.

With that out of the way: if you moved any header files to your framework, it’s a good idea (if not required) that you change their imports.

Local headers in your app are imported like this:

    #import "MyHeader.h"

But framework headers, at least before all this module business, were referred to like this:

    #import <MyFramework/MyHeader.h>

Now, nobody imports the individual headers, of, say, a framework like Foundation. They use the umbrella header, like this:

    #import <Foundation/Foundation.h>

The same is true for your new framework. Xcode makes a so-called “umbrella” header for you with the same name as the framework, and then complains if you don’t import all your public headers there.

The neat thing is, using the new module import syntax, you don’t even need to refer to the umbrella header at all, you can just leave it off completely and the compiler knows what you mean:

    @import Foundation;

    @import MyFramework;

Though you still can refer to individual public headers within framework the if you really want:

    @import Foundation.NSArray;

    @import MyFramework.MyHeader;

One place you can’t use the new syntax, however, is the umbrella header itself! The compiler will complain that “Import of module ‘MyFramework’ appears within same top-level module ‘MyFramework'” and “No submodule named ‘MyHeader’ in module ‘MyFramework'”.

So for now, sadly, you need to use the old format when adding all the necessary public header inclusions to your framework’s umbrella header. (You can see this in Apple’s umbrella headers as well.)

5. Don’t add module names to your storyboard and xib files for Objective-C.
If you look at the Identity Inspector pane in Xcode’s utility area with, say, the File’s Owner entry selected in your xib file, in Xcode 6 you’ll see a Custom Class pane with a second field for module name. (Actually, sometimes you’ll see two Custom Class panes, but never mind that now.)

Updated: (Thanks, Jordan!) This is needed for Swift classes, which I won’t be describing in this post.

Adding the module name for Objective-C classes leads to runtime errors: “Unknown class _TtC11MyFramework18MyClass in Interface Builder file.” When that happens, the instance of the class, instead of being of the right type, is of type UICustomObject.

So don’t do it!

6. Runtime IB errors indicate missing .m files.
Note, if you leave out a .m file from your framework that’s only instantiated from a xib or storyboard, the only error you’ll get (apart from things not working) is a log message, “Unknown class MyClass in Interface Builder file.”

You get this, instead of a linker error, presumably because there’s no compile-time linking going on at all; instead, the runtime instantiates the object entirely via an arbitrary class name string.

Actually, scratch that. I see this problem with classes whose absence should cause framework linker errors. So I don’t know what’s going on, exactly. But if you see that error, go over step 2 again.

7. Fix the dylib warning in your app extension.
If I make a brand-new iOS app extension, then make a brand-new framework, then link the framework against the app extension, without any other changes, I got a linker warning:

    Linking against dylib not safe for use in application extensions: /Path/To/Framework/MyFramework.framework/MyFramework

Now, from WWDC, I know that there are certain UIKit APIs, marked with a NS_EXTENSION_UNAVAILABLE_IOS macro, that are not allowed in app extensions.

This is a more general warning, however, and you fix it by going to the General tab of your framework in the Project Editor, disclosing the Deployment Info section, and clicking the “Allow app extension API only” checkbox.

After that, poof, no warning, and if you try to use any APIs that are marked with the macro NS_EXTENSION_UNAVAILABLE_IOS, you’ll get a build error that specifically mentions the call site. Everybody wins!

8. (NEW!) Fix your bundle references.
In iOS, all your resources are located in your app’s main bundle. So for methods where you have to specify a bundle to use for resource loading, such as -[UIViewController initWithNibName:bundle:] you can safely pass nil, meaning use the app’s main bundle.

If your framework code is running in your app, however, that won’t work. The resource isn’t in the app’s main bundle, it’s in the framework’s main bundle. Specifying the app’s main bundle via nil means your resource won’t be found.

There’s an easy way to fix this, however: [NSBundle bundleForClass:[MyClass class]]. As long as MyClass was compiled into your framework’s binary, not your app binary, the framework’s main bundle will be returned, without your having to hardcode its name or path. This is how OS X developers have been doing forever, and it works just peachy.

Rolling Forward

Now that I’ve talked about the old blogs I subscribed to back in 2010, what about the new blogs I’ve found since then?

New Blogs
Here are the blogs that, for the most part, started around or after my 2010 post, so I didn’t include them then:

Florian Kugler: iOS, Mac, and Web Development by Florian Kugler (floriankugler on Twitter). Wrote some interesting performance posts back in April-May of last year. Posting has been light recently.
iOS Unit Testing by Ron Lisle (rlisle on Twitter). Explores TDD for iOS.
The Mental Blog: Software with Intellect by Drew McCormack (drewmccormack on Twitter), author of ICACloud referenced in Edge Cases 77.
The Shape of Everything by Gus Mueller (ccgus on Twitter), author of Acorn. Gives monthly “status reports” on his life as an indie developer.
iOS Development by Ole Begemann (olebegemann on Twitter). Wrote NSProgress post referenced in Edge Cases 84.
The UI Observatory by Jochen Wolters (jochenwolters on Twitter). Interesting analysis and details of user interfaces on (mostly) OS X.

Old-Timers I Missed?
Here are the blogs that were around then, or if they weren’t, are by old-timers who were.

carpeaqua by indie developer Justin Williams (justin on Twitter), now owner of the Glassboard sharing platform. Posts frequently.
Cocoa Is My Girlfriend by Marcus Zarra and others. Terrible name, great technical posts.
Structure and Interpretation of Computer Programmers by Graham Lee (secboffin on Twitter), previously called Secure Mac Programming (the change is explained here). Unusual, unique, smart philosophical musings about development.

Aggregators
There are quite a few new resources that exist to tell you about Cocoa development articles and other resources. This is by no means an exhaustive list, nor necessarily the best, just the ones I’ve found so far.

iOS Dev Weekly weekly email newsletter by Dave Verwer (daveverwer on Twitter)
iOS Dev Tools by Adam Swinden (iOSDevTools on Twitter)
Cocoa Snippets by Guilherme Rambo (_inside on Twitter)
iOS Developer Tips by John Muchow (iOS_Dev_Tips on Twitter)

Full-Featured Articles
The next two aren’t blogs, exactly.

objc.io is a self-described “periodical about best practices and advanced techniques in Objective-C”. It is professionally written (in part by Florian Kugler, mentioned above) and well edited. You can subscribe to it.

NSHipster calls itself a “journal” and comes out every week (I believe). It is also very well written and professional, and it’s by Mattt Thompson (mattt on Twitter).

As I discuss in Edge Cases 86, these feel more to me like tutorial books than blogs, and feel in part like they’re trying to take the place of Apple’s documentation by introducing topics not as “here’s an interesting thing that I found”, but rather as “this is all you need to know about X”.

Maybe, with so many people new to the community, that’s what we need now, because Apple surely isn’t going to be putting out weekly tutorials anytime soon. But it’s going to take me a while to get used to.

Rolling Back

Back in August of 2010, when I wrote a post about my blogroll, I wanted to update that post frequently to keep it in sync with my blogroll as it evolved….

So much for that.

But it turns out, letting it lay fallow for almost three years means that I can do a “Where Are They Now” post.

So where are they now?

Truly Dead Blogs
ChrisAshworth.org/blog, by Chris Ashworth (Chris_Ashworth on Twitter) deleted about a year ago, sort of (?) replaced by his company blog, Notes from Figure 53
ranchero.com by Brent Simmons (brentsimmons on Twitter), dead, in favor of inessential.com
Well-Placed Pixels no author listed, server not responding
iPhone & iPad UX Reviews by Suzanne Ginsburg (suzanneginsburg on Twitter), per last post, shutting down 4/2014, replaced by Touchy Talk
Sharing the truth one thread at a time no author listed, deleted by author

Slumbering Blogs, Last Updated…
3/2013 Alan Quatermain by Jim Dovey, now working at Apple (alanQuatermain on Twitter)
2011 Dave Dribin’s Blog by Dave Dribin (ddribin on Twitter)
2010 Lap Cat Software Blog by Jeff Johnson (lapcat on app.net, no longer on Twitter)
2011 Cocoa with Love by Matt Gallagher (cocoawithlove on Twitter)
6/2013 venomous porridge by Dan Wineman (dwineman on Twitter)

Transformed Blogs
iPhone Development by Jeff LaMarche (jeff_lamarche on Twitter), since 7/2013 exclusively about developing his company’s game “Turncoat”
Matt Legend Gemmell by Matt Gemmell (mattgemmell on Twitter) since 1/2014 switched from development to fiction (and non-?) writing full time
Red Sweater Blog by Daniel Jalkut (danielpunkass on Twitter), now solely a company blog, personal topics at Bitsplitting.org (which was briefly a podcast)
ignore the code by Lukas Mathis (LKM on Twitter), recent very long post on Windows Metro, signaling possible change in focus?

Surviving Categories

In-Depth Technical (new, see below for explanation)
rentzsch.tumblr.com by Jonathan “Wolf” Rentzsch (rentzsch on Twitter)
inessential.com by Brent Simmons (brentsimmons on Twitter)
NSBlog by Mike Ash (mikeash on Twitter)
ridiculous_fish by “an engineer on the AppKit team”
Hamster Emporium by Greg Parker (gparker‎ on Twitter)

Commentary
Daring Fireball by John Gruber (gruber on Twitter)
Marco.org by Marco Arment (marcoarment on Twitter)
Apple Outsider by Matt Drance (drance on Twitter)
Waffle, by Jesper, back after a two-year hiatus

Explanation!
In my original post, I had five categories, Well-Rounded, In-Depth Coding, Low-Level, Design Blogs, and Commentary.

Well-Rounded took the biggest beating, with only two blogs coming out of it completely unchanged. Both blogs, the one from my podcast cohost Jonathan “Wolf” Rentzsch and the one from the exceptionally prolific Brent Simmons, I would now put in a new In-Depth Technical category; it just seems more fitting given their primarily technical content.

One of my two In-Depth Coding blogs is also slumbering, but otherwise it and the two Low-Level blogs are intact, probably because they just keep posting at long intervals. (How would I even know if they were on hiatus?) Again, now I’m lumping them into the new In-Depth Technical category.

Design Blogs is also gone, with one blog dead, one slumbering, and one potentially transformed. That leaves the clear winner, the Commentary blogs: six out of eight are still going strong. Though, it turns out, I personally no longer subscribe to either asymco or Smoking Apples, so I’ve removed those from my list. (Perhaps they were going a little too strong for my taste?)

Even with many of the “dead” or “slumbering” blogs, their authors are still active on twitter, so I still follow them there.

Rolling Forward
Now that I’ve gotten the old blogs out of the way, my next post will talk about all the new stuff.

iOS Frameworks

I always believed you couldn’t use your own frameworks in iOS development like you could on the Mac.

There are a bunch of benefits of OS X-style frameworks. Here’s a partial list:

1. Dynamic linking. Your app could link against the current version of a framework, such as a third-party framework installed in a standard location, and when that framework was upgraded and your app was launched again, you got any bugfixes or improvements of the new version for free. This is unlike static linking, where you’re stuck with whatever version you build with.

2. Project and compilation convenience. A single reference to that project is all Xcode needs to be able to link against its binary and find its headers. This is unlike free-standing libraries and their headers, which must have separate, individual Xcode references.

3. Resource convenience. Any other free-floating files associated with the framework can also tag along inside the framework’s folder. Pictures, text files, even xibs or storyboards. This is unlike etc. etc.

In iOS apps, for security reasons, you aren’t allowed to link against anything except system frameworks dynamically, so 1. has always been impossible.

I thought 2. was also impossible, but it turns out, I was wrong.

The Dropbox SDK has a sample project in it which just has a single reference to the Dropbox.framework, both in the Project Navigator and in the “Link Binaries with Libraries” Xcode build phase. And while there is a custom framework search path in the project’s build settings, there’s no custom library search path or header search path. And the headers are available via the usual #import <Framework/Header.h> format.

How does it do that?

Turns out, Xcode and iOS have had informal support for iOS frameworks, possibly going back to 2010, but definitely available as of April 2013.

What does “informal” mean?

It means it’s not documented, and you can’t make an “iOS framework” target. Indeed, the steps to actually build one of these frameworks are not entirely trivial. (The extra script in the linked-to web page doesn’t seem to be necessary, though.)

But it does work. And since it does, you can get all the benefits of 2., even in an iOS app.

That’s pretty cool, and something I’m going to be taking advantage of.

And note that 2. is all you get. The resulting framework binary is still statically-linked in to your app, and does not appear separately in the app binary. Nor do the headers. Nor do any other resources—meaning, you don’t get 3., either.

Everything but the Kitchen Sync

Previously: part 1, provisioning and entitlements and part 2, iCloud syncing documentation.

Part 3: Some Bad, Some Good

When last we left our hero, he had read Apple’s iCloud documentation (PDF) and found it both helpful for the details it included, and frustrating for the difficult edge cases it left as “an exercise for the reader”. (Thus mirroring the opinions of this blog’s author!)

Listing and Syncing

One of those edge cases, surprisingly enough, appears to be just trying to maintain on ongoing list of documents.

My Documentary sample apps, for the sake of simplicity (and, once I started running into problems, a certain sense of orneriness) allow for the creation, deletion, and renaming of documents directly from their list, without requiring individual per-document UI. This in particular allows me to put all this logic in a platform-agnostic shared class, in my case called iCloudManager, instead of duplicating it in UIDocument for iOS and NSDocument for Mac.

Creating a new document involves calling the NSString method writeToURL:atomically:encoding:error: on a blank string instance, and this works, even in a ubiquity container, but it only works if you wrap it in a NSFileCoordinator call:

    NSError *coordinateError = nil;
            
    __block BOOL writeResult = NO;
    __block NSError *writeError = nil;
            
    [fileCoordinator coordinateWritingItemAtURL:newDocumentURL 
options:0 error:&coordinateError byAccessor:^(NSURL *newURL) {
        writeResult = [@"" writeToURL:newURL atomically:YES encoding:NSUTF8StringEncoding error:&writeError];
        NSLog(@"Write %@ success: %ld, error: %@", newDocumentNamePlusExtension, (long)writeResult, writeError);
    }];            

    if (coordinateError == nil && writeResult == YES) {
        succeeded = YES;
    }

The NSFileCoordinator is synchronous, but doesn’t return a boolean, unlike most such Apple APIs—if the NSError is nil, it succeeded…or did it? In fact, it seems to only return coordination errors. If there was an actual file writing error, you can see that there is no way for the custom block to return any value. My code gets around this by saving the results from within the block, and checking that as well. I have not had a chance to check what the UI/NSDocument APIs do; one would hope they would have similar workarounds. The deletion code is similar.

The bad news is, if you’re relying on NSMetadataQuery to maintain your list of files, it’s not going to update your list instantaneously. The documentation for NSMetadataQuery says, “By default, notification of updated results occurs at 1.0 seconds.” I’ve found that even if I set notificationBatchingInterval to a very low value, it still takes about a second. And the update, when it occurs, won’t say anything as helpful as, “Yes, we deleted a file.” Instead, it will just tell you there’s been some sort of change.

So, for example, if you wanted to highlight the row of the newly-created file, you need to wait for the next update, then try to find that URL in the latest results list, and select it then. That should work, but it’s awfully fiddly. What if a network update comes through right then? Should you keep your conditional code running for the next several updates, till you find the URL you want? Deletion and renaming updates are similarly delayed and contextless.

For Documentary, I just punted and only updated the UI once the NSMetadataQuery results came in. But for a shipping app, I’d have to do a lot more polish work.

Putting the ”Document” in ”Documentary”

But finally, there was no more putting it off, I had to create per-platform Document subclasses if I wanted to implement in-app text editors. Actually, for Mac, I made it so that Cmd-O opened your document via Launch Services, by default with TextEdit. But I also included an “Open Document Internally” option, for reasons I’ll get to below.

And the editors, really just simple text views, worked pretty well! I had to hook up my text views to the undo manager to get iCloud to work properly, but that was described in the documentation and pretty easy. Here’s the change method on iOS:

    - (void)textDidChange:(NSNotification *)notification {
        // In a real app, we would only register a change after a certain amount of typing, or after a certain time. But not for this sample app.
    
        [[self.document.undoManager prepareWithInvocationTarget:self.document] setText:self.document.text];
        [self.document.undoManager setActionName:NSLocalizedString(@"Typing", @"Undo/redo label")];
    
        self.document.text = self.textView.text;
    }

I really should have coalesced the text changes, but didn’t have time, sorry.

And while I punted on conflict resolution on the iOS side due to time issues (again, sorry), I knew that on the Mac, it should all be taken care of if I used NSDocument.

So…was it?

I’m happy to report that it was. When I opened a document that had been modified locally and on two other devices, the document window presented a sheet with the three options, clearly labeling where they came from, and showing you a preview of their contents, all without my having to do any extra work. You could even choose to keep several of the conflicting versions around. Neat! Note: it won’t do any of that work for you until you open a document, but it’s very nice to have it then.

Conclusions

I spent about a week of free time putting all this together, and I have a much better sense of the contours of iCloud document syncing than I used to. The good: the syncing itself, Xcode 5, the documentation, and NSDocument. The bad: edge cases and lots of developer-required logic.

To play around with it, feel free to clone and build my Documentary applications, but keep in mind I did this in a week, and that you’d have to add a lot of extra error-handling and edge case-handling code before you’d want to ship anything based on it.

Still, I had fun, and I hope you had fun reading about it, too.