The Powers of Observation

Apple and tech and me

Archive for the ‘Uncategorized’ Category

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.

Written by subjectiveobserver

June 21, 2014 at 5:48 am

Posted in Uncategorized

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.

Written by subjectiveobserver

March 30, 2014 at 9:01 pm

Posted in Uncategorized

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.

Written by subjectiveobserver

March 29, 2014 at 1:13 am

Posted in Uncategorized

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.

Written by subjectiveobserver

January 29, 2014 at 1:33 am

Posted in Uncategorized

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.

Written by subjectiveobserver

January 20, 2014 at 4:12 am

Posted in Uncategorized

Sync or Swim

Previously: An introduction to my series of blog posts on iCloud document syncing, and issues with provisioning and entitlements.

Part 2: The Documentation for Documentary

When last we left our heroine, she had configured her ADC accounts properly to allow her to run the Documentary iOS and Mac sample apps. (Not a very exciting afternoon matinée, I’ll admit.)

Great, so they run, but what do they do?

The short answer is: in order to share documents between iOS and Mac, they do what the documentation says to do.

Shared Ubiquity Containers

Apple’s produced some good iCloud documentation (PDF), so go read that first if you’re interested (remember, this is not a full tutorial).

The first thing from the documentation that’s important to our specific project is that we want to share a ubiquity container for both of our apps, Documentary for iOS and Documentary for Mac. Normally, you’d use the unique, separate bundle IDs of your apps as their primary ubiquity container identifiers, but here we’re going to use the iOS bundle ID for both, so all their contents are shared. This is not a hack: Apple recommends it for precisely this case.

Just like for your app ID, this ubiquity container ID is actually prepended with your team ID. So make sure you use the same team for your iOS and Mac projects (this should be the default). Normally, therefore, you’d need to hard-code your team ID into your code to retrieve the URL for this ubiquity container. But Apple makes it easy by returning the first container in your app if you pass in nil:

    [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];

In our case, we only have one ubiquity container, so nil it is!

I was a little surprised to read that you can put your own folders inside the ubiquity container, really whatever you want to do there, and it will be replicated for you. It’s not just one level deep. The Documents folder is special because its contents are user-visible on the Mac side (for a certain value of “user visible”, keep reading), but other than that, anything goes, as long as you keep in mind that your free space is limited by your iCloud account’s storage quota.

Turning iCloud On and Off

The second thing that struck me in the documentation is all the work you need to do to handle turning on and off iCloud support. You’re supposed to call this method in your application:didLaunch... code:

    [[NSFileManager defaultManager] ubiquityIdentityToken]

and if it returns a non-nil value, then the user is signed in to iCloud and your app can use it…well, maybe you can use it. You should also call the above-mentioned URLForUbiquityContainerIdentifier:—on a background thread, mind—before doing anything. In theory, a non-nil result from the former should be enough to assume iCloud availability. But what if the first returns non-nil but the second returns nil?

In any case, if iCloud is available but your app isn’t using it, you should ask the user whether they want to use it, and cache that choice so you don’t ask it again the next time. If you were using it, but now it’s off, you should…tell the user they just lost all their data? “Sorry, fuck off”? The documentation isn’t forthcoming. Wenderlich’s tutorial makes it sound like you can still access the local cache of the iCloud files, despite iCloud being off. Even if that’s true, presumably anything that was “in the cloud” and not downloaded can’t now be downloaded after you’ve logged out. Same if you’ve switched accounts.

But if the user agrees to start using iCloud, you’re supposed to copy all your local documents to the ubiquity container, where they will then be synced with iCloud. This is entirely on you, the developer. If there are naming conflicts, you need to resolve them in your own custom code. This is the first of many places where I wish Apple had done more to help us out. Making each app do this individually means that all apps will do it inconsistently, and many will miss the edge cases that don’t show up in casual testing.

(The Documentary project punt on this entirely by assuming iCloud is always on, sorry.)

Mac Versus iOS File Philosophies

I show the documents on both Mac and iOS in my own list UI, which is…frowned upon in OS X. The documentation makes clear, both in explicit and implied ways, that on the Mac they really want you to rely on the user employing the standard Open dialog to find files, and they want all file interaction to go through NSDocument, which handles downloading and uploading file content, file version conflicts, and files being moved or modified elsewhere.

On iOS, there is no such functionality, so you must find the files yourself using NSMetadataQuery. The iCloud document makes mention of NSMetadataQuery but doesn’t explain how to use it in any way. It just refers to a second document, File Metadata Search Programming Guide (PDF). This is the danger of using what my Edge Cases cohost refers to as “collaborating objects”, where all sorts of framework objects tie together to make a more capable whole; learning to do even the simplest things from scratch means hopscotching from one long document to another, never quite learning the exact steps you need. Frustration with that approach led to my finding Ray Wenderlich’s tutorial, where he had a very helpful example of NSMetadataQuery usage that I copied wholesale.

The long and short of it is, if you keep the query going, you’ll be notified when there are changes, and you can update your list to match. I do this on iOS, but I also do this in the Mac app, because even though it’s not recommended, the functionality is in Foundation and shared between both platforms.

But to manipulate files directly, instead of through a UI/NSDocument class, requires using a new class, NSFileCoordinator. It’s interesting how little change was made to low-level systems to get this to work; instead, new functionality and restrictions were piled on top, just like NSFileCoordinator was piled on top of preexisting NSFileManager methods. One doesn’t replace the other, both are still needed.

Also keep in mind that unlike on iOS, all your documents are fully reachable by the user on Mac, if they know how to look. The way to look is to do a Spotlight search for a specifically-named document, which will find the iCloud Documents folder for your app. It won’t be labeled anything specific, just iCloud. But once you’re there, you can delete files, move files, rename files, and even drag them into different apps to edit them there, all without your app being able to do anything about it. Is this a feature? A bug Apple just didn’t get around to fixing? Dunno.

Further Mac Versus iOS Difficulties

The documentation mentions, but has no overarching solution for, the fact that many of what you would consider to be foundation-level classes are actually quite different on Mac vs. iOS. For one, think about colors: NSColor (Mac) and UIColor (iOS) are not transferable.

For two, the coordinate systems is different.

For three, the file system is different, or probably so. On iOS, the file system is case-sensitive, and you can’t change that. On the Mac, by default it’s case-insensitive, and while a diehard Unix neckbeard might change that, most users will not, so it’s a scenario you have to be ready to deal with.

The docs blithely say, “To make your document file format cross-platform compatible, you must read and write files in a case-insensitive manner.”

Sure, sure, is that all? Remember, I spent a whole episode of Edge Cases talking about how complicated and crazy Unicode is. What does case even mean for non-ASCII, non-Western characters? I couldn’t tell you.

And even if you set that aside, let’s say you want to create a new file with a certain name, “Document 1.txt”. How do you tell, on iOS, that there’s no file in that location already with any case-only variant of that name, such as “document 1.txt” or “Document 1.TXT”?

Well, you could iterate through every file in the directory, and compare its name in a case-insensitive fashion with your new name string. (Hard if you’ve got thousands of files.) Or you could keep your own dictionary of actual names mapped to, say, uppercase versions of those same strings, and so look up an uppercase version of your new name in a dictionary of them. Either way, it’s extra, fiddly, work, that’s still not guaranteed to be completely full proof.

Remember, Apple has said more than once that you should just go ahead with file operations, instead of trying to preflight them, because the situation could change out from under you between your test and your actual file system work. That’s still true here, and there’s no way to fully compensate for it.

Next up: Some bad (NSMetadataQuery issues), some good (NSDocument conflict resolution), and conclusions.

Written by subjectiveobserver

January 20, 2014 at 4:11 am

Posted in Uncategorized

That Syncing Feeling

I’ve been interested in Mac/iOS app communication for some time now. While there are other options, most recently I’ve been exploring iCloud document syncing.

Apple has comparatively verbose documentation for iCloud (I prefer downloading the PDF), and Ray Wenderlich has not one but two tutorials, an introductory one (parts 1 and 2) and a more in-depth one (parts 1, 2, 3, and 4).

So while I’m going to talk about it at some length (and several blog posts), I don’t need to duplicate all that.

Instead, I’m going to fill in the gaps I found, and describe my own experiences.

I’ll mostly be talking about a sample app I wrote, called Documentary, that lets you make simple unformatted text documents and share them through iCloud, either to another device of the same type, or between Mac and iOS.

Part 1: Feeling Entitled

To run the iOS app on an iOS device, or the Mac app on your Mac, you’ll need to be able to support iCloud entitlements in your apps. To do that, you’ll need a paid $99/year iOS ADC account or a $99/year Mac ADC account, respectively

But what’s interesting is that, as far as I know, once you have your account(s) set up, you can download my sample code and run it, and with no other effort or changes on your part, the iCloud syncing portion of it will work right away.

Go ahead, build the iOS version (for example), and run it on two different devices, say your iPhone and an iPod touch. You can create a document on one, and it will show up a few seconds later (if you’re lucky) on the other.

For one, hey, that’s cool! Apple allows you to use its server storage even when you’re just playing around with an app. It doesn’t have to be an app you’ve registered to ship.

For two, notice that you’re using the same bundle identifier that, say, I am when I run my app, and yet there’s no worries about conflicting IDs on the iCloud servers.

This may seem strange to old Mac developers. But in the brave no-longer-so-new world of provisioning, the app ID is prefixed with your team ID (which Xcode 5 seems curiously reticent to ever show you, except in obscure error messages). Xcode handles the prefixing part of it without needing to include it in your project, and so you can use the same project that I use, while still getting your own app ID unique to you.

For three, if you build and run iOS, then Mac (or vice versa), you may get a complaint from Xcode about needing to add the iCloud entitlement again each time you build.

I’m not quite sure what’s going on under the hood, but I believe it might be because there’s no dedicated provisioning profile for each app yet. Remember that you didn’t register the app’s ID with Apple’s Dev Center website? That was a plus when you were just playing around. But it might come bite you here, if there’s some sort of temporary, fit-all-sizes profile that’s being used for both apps, and that keeps getting changed each time you build.

I was able to make the build error go away by registering those app IDs in, respectively, the iOS and Mac Dev Centers. That gives each of them an individual provisioning profile.

What’s curious is that even after I deleted the app entries again (and thus their profiles) and updated Xcode (and quit and restarted Xcode, for good measure), the builds continued to work repeatedly with no errors. So I’m not sure what’s going on under the hood. Makes it more likely it’s just a straight-up bug, eh? But keep an eye out for these problems if you try this for yourself.

Next up: What Apple’s documentation does and doesn’t say.

Written by subjectiveobserver

January 20, 2014 at 4:10 am

Posted in Uncategorized

My Lists from 2010

If you have blogging software, you’ll often have a bunch of old draft posts, which just keep getting more and more outdated, greet you every time you open the app.

My version of this is two posts, “10 Things I Was Proud of as an Apple Engineer” and “10 Things I Wasn’t Proud of as an Apple Engineer”, started in 2010 after I left Apple.

I finished the first one, but the references in it are too old to post now. And I didn’t even get to 10 on the second; I was only able to think of 9. So I’m just going to publish the lists as-is, for a bit of nostalgia:

Proud:

99-cent songs
No-DRM music
WebKit
Environmentally-friendly progress on hardware
Infrastructure-improvements-only Snow Leopard
OS X-based mobile strategy
iOS itself
iOS’s delayed, done-right multitasking introduction
iPad
Clang

Not Proud:

“100 features” nonsense
Increasingly cheap WWDC amenities
Killing WWDC feedback sessions
Breaking own HI guidelines
Ongoing BugReporter crappiness
Suing bloggers
Suing competitors
App Store review arbitrariness
Corporate war with Google

Written by subjectiveobserver

January 16, 2014 at 7:25 pm

Posted in Uncategorized

A Mergin’ Behavior

Now that I’ve merged the storyboard file format (which is also basically the new xib format) in a real-world project, I can say the following:

It is…more than just easier, it’s now possible.

Which is a great relief.

The trick (the one weird tip, as it were) is that the file format stores pieces of data that should be single, undivided snippets of the file, as single undivided snippets of the file, in a human readable format.

For example, objects are represented by a single XML node, whose name is that object’s type. You can tell what a tableViewCell node is just by its name. And you know what it means when it contains a tableViewCellContentView node, and you know what it means when that node contains a subviews node. And so on.

This leads to several benefits:

  1. A single change to the file, say adding a background color in a view, only affects one spot in the file, unlike previous formats, where a single user-initiated change to the file might wind up modifying multiple places in the file itself. So unrelated changes to different objects won’t wind up being merge conflicts.
  2. When you’re reviewing the changes from a merge, it’s easy to see that they’re the ones you expect. To use the same example as above, if you’ve added a background color to a view, that will add a color node inside that view’s node.
  3. Because you can identify the changes you want, you can also identify the changes you may not want. For example, you can easily reset the changes Interface Builder makes to the xib/storyboard’s header to reflect the current version of Interface Builder and the operating system, if you don’t want to see such noise as part of your commits, without worrying that you’re accidentally modifying something you don’t understand.
  4. You can work around Interface Builder bugs. For example, currently in Xcode 5, if you refer to the same image in multiple places in a file, it will add several entries representing the image to the resources node at the bottom of the file. Then, it will complain to you in a sheet about “internal inconsistencies” (while leaving in the duplicate entries). You can merely refuse to commit the IB changes that introduce those duplicates, which will prevent the problem (though not the alerts).

The last point can be expanded to a broader theme: you should use formats and architectures in your application that don’t try to fight the way the world works. If your architecture requires a perfect app to work well, then your architecture isn’t very well suited to the current world of software development, where there will always be bugs.

As described above, the current storyboard/xib format does not require a perfect app, and in fact allows end users broad powers to work around app bugs and peculiarities. This is in contrast to the previous format, which was not particularly human-readable, and therefore required the end user to trust that the app was doing the right thing in all cases.

Written by subjectiveobserver

November 18, 2013 at 1:28 am

Posted in Uncategorized

Laying It All Out

My first encounter with Auto Layout in a real-world project didn’t go as smoothly as I would have liked.

Why not? Because it was a special case. (An…Edge Case, as it were.)

I was trying to customize the title view in a navigationItem. I changed the view’s width after I’d set it to be the title view, making it shorter. But I couldn’t get the change to “stick.” It remained the old width no matter how many times I modified it. (On iOS 7, at least. On iOS 6, worked fine.)

Since the storyboard I was working on had Auto Layout turned on, I thought maybe this was because I hadn’t set the constraints correctly. So I tried adding a constraint in code.

Bad idea.

First of all, that constraint conflicted with the constraint generated from the view’s autoresizing mask. In order to solve that, I turned off my custom view’s translatesAutoresizingMaskIntoConstraints property.

That led to a crash due to an NSInternalInconsistencyException. Reason?

Auto Layout still required after executing -layoutSubviews. UINavigationBar’s implementation of -layoutSubviews needs to call super.

Huh? Why was I being asked to fix UINavigationBar’s implementation of layoutSubviews? I went up and down the view hierarchy, used every Auto Layout debug trick I could find, added every constraint I could think of. I still couldn’t avoid this error.

The end.

Not really, but almost. I had to conclude the following: even if the rest of your storyboard uses Auto Layout, do not use Auto Layout in your own custom title view. Just don’t. Use springs and struts and be happy about it!

Also, if you want a different title view size after you’ve add it to your navigation item, well, remove it from the navigation item, change the size, then add it again. Then, and only then, does it work.

See what I mean about it being a special case?

But because I was new to the Ways of Auto Layout, I spent hours checking and double-checking that I wasn’t missing anything, that there wasn’t some special rule I was overlooking.

Nope.

Written by subjectiveobserver

October 20, 2013 at 3:31 am

Posted in Uncategorized

Follow

Get every new post delivered to your Inbox.