Category Archives: Swift

Unified Swift Playgrounds

The announcement of Swift Playgrounds 2.0 has me thinking again about Xcode Playgrounds: both about what a revelation they are, and about how disappointing they continue to be.

When Xcode Playgrounds were first introduced (as “Swift Playgrounds”) in 2014, they were received as a groundbreaking new way for developers to write Swift code interactively. There were lots of rough edges on the feature, but it seemed reasonable to expect that because they were released in tandem with the Swift programming language itself, those rough edges would be smoothed out on a parallel pace with the language itself.

Two years later, Apple announced Swift Playgrounds again, immediately introducing a nomenclature confusion. This time the name referred to a dedicated, closed-source iOS app designed for interactively teaching programming concepts with Swift. The previous Xcode-coupled technology, now known as “Xcode Playgrounds” or simply “Playgrounds,” had seen modest improvements over the years but continued to be frustratingly slow, unpredictable, and crash-prone.

Today, in early 2018, the release of Swift Playgrounds 2.0 for iOS appears to represent Apple’s commitment to driving that product forward into the future. The latest version of Xcode Playgrounds, on the other hand, offers a lackluster interface, slow responsiveness, and a tendency to crash both within the Playground, and in ways that take down the entire Xcode app. In short: they’re not a very fun place to play.

I propose that Apple eliminate Xcode playgrounds, and invest all of their work in the field of interactive coding into the Swift Playgrounds app. It has been a nagging shortcoming that Swift Playgrounds is only available for iOS. Many people who would benefit from the educational opportunities of Swift Playgrounds could do so from Macs, whether in schools, homes, or workplaces. Porting Swift Playgrounds to the Mac would address that problem.

Where does that leave developers? After eliminating Xcode Playgrounds as we know them today, I envision adapting the Mac version of Swift Playgrounds so that playgrounds can be run either independently in a Swift Playgrounds app, or in “developer mode” within Xcode. In effect, Xcode would become a dedicated Swift Playgrounds authoring app, where such authoring capabilities would incidentally provide all the benefits that standalone Xcode Playgrounds currently provide.

Taking this course would allow Apple to maximize the output of its engineering and design efforts while eliminating the naming confusion that currently exists between Swift Playgrounds and Xcode Playgrounds.

For students and educators, it would broaden device requirements for Swift Playground materials, opening up learning opportunities for people who have access to Macs but not to iOS devices.

Finally, and perhaps most importantly for the developer ecosystem as a whole, it would eliminate the frustratingly problematic Xcode Playgrounds and hopefully provide developers with something more inspiring, more functional, and more reliable.

Radar #36910249.

Intrinsic String Encoding

I was baffled today while investigating a bug in MarsEdit, which a customer reported as only seeming to affect the app when writing in Japanese.

I pasted some Japanese text into the app and was able to reproduce the bug easily. What really confused me, though was that the bug persisted even after I replaced the Japanese text with very straight-forward ASCII-compatible English. I opened a new editor window, copied and pasted the English text in, and the bug disappeared. I copied and pasted back into the problematic editor, and the bug returned. What the heck? Two windows with identical editors, containing identical text, exhibiting varying behavior? I knew this was going to be good.

It turns out there’s a bug in my app where I erroneously ask for a string’s “fastestEncoding” in the process of converting it. The bug occurs when fastestEncoding returns something other than ASCII or UTF8. For example, with a string of Japanese characaters, the fastestEncoding tends to be NSUnicodeStringEncoding.

But why did the bug continue to occur even after I replaced the text with plain English? Well…

The documentation for NSString encourages developer to view it as a kind of encoding-agnostic repository of characters, which can be used to manipulate arbitrary strings, converting a specific encoding only as needed:

An NSString object encodes a Unicode-compliant text string, represented as a sequence of UTF—16 code units. All lengths, character indexes, and ranges are expressed in terms of 16-bit platform-endian values, with index values starting at 0.

This might lead you to believe that no matter how you create an NSString representation of “Hello”, the resulting objects will be identical both in value and in behavior. But it’s not true. Once I had worked with Japanese characters in my NSTextView, the editor’s text storage must have graduated to understanding its content as intrinsically unicode based. Thus when I proceeded to copy the string out of the editor and manipulate it, it behaved differently from a string that was generated in an editor that had never contained non-ASCII characters.

In a nutshell: NSString’s fastestEncoding can return different values for the same string, depending upon how the string was created. An NSString constant created from ASCII-compatible bytes in an Objective-C source file reports NSASCIIStringEncoding (1) for both smallest and fastest encoding:

printf("%ld\n", [@"Hello" fastestEncoding]);	// ASCII (1)

And a Swift string constant coerced to NSString at creation behaves exactly the same way:

let helloAscii =  "Hello" as NSString
helloAscii.fastestEncoding			// ASCII (1)

But here’s the same plain string constant, left as a native Swift String and only bridged to NSString when calling the method:

let helloUnicode = "Hello"
helloUnicode.fastestEncoding		// Unicode (10)

As confusing as I found this at first, I have to concede that the behavior makes sense. The high level documentation describing NSString representing “a sequence of UTF-16 code units” says nothing about the implementation details. It’s a conceptual description of the class, and for the most part all methods operating on an NSString comprising the same characters should be heave the same way. But the documentation for fastestEncoding is actually pretty clear:

“Fastest” applies to retrieval of characters from the string. This encoding may not be space efficient.

As I said earlier, my usage of fastestEncoding was erroneous, so the solution to my bug involves removing the call to the method completely. In fact, I don’t expect most developers will ever have a legitimate needs to call this method. Forthose who do, be very aware that it can and does behave differently, depending on the provenance of your string data!

Selective Selector Mapping

I ran into an interesting challenge while porting some Objective-C code to Swift. The class in question served both as an NSTableView delegate and data source, meaning that it implemented methods both for controlling the table view’s behavior and for supplying its content.

Historically in Cocoa, most delegate relationships were established as informal protocols. If you wanted a particular class to be a table view data source, you simply implemented the required methods. For example, to populate a cell based table view, a data source would implement various methods, including one to indicate how many rows the view should have:

- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView;

In recent years, Apple has increasingly converted these informal protocols to formal Objective-C protocols. These give the compiler the opportunity to generate errors if a particular class declares compliance, but neglects to implement a required method. At runtime, however, the compliance-checking is still pretty loose. NSTableView consults its data source, checks to see that it implements a required subset of methods, and dynamically dispatches to them if it does.

The dynamic nature of NSTableView hasn’t changed with Swift. An @objc class in Swift that complies with NSTableViewDataSource must still implement the required methods such that Apple’s Objective-C based NSTableView can dynamically look up and dispatch to the required delegate methods. Swift’s method rewriting “magic” even ensures that a delegate method can be written in modern Swift style, yet still appear identically to older Objective-C code:

class MyDataSource: NSObject {
	@objc func numberOfRows(in tableView: NSTableView) -> Int {
		return 0
	}
}

Given an instance of MyDataSource, I can use the Objective-C runtime to confirm that a the legacy “numberOfRowsInTableView:” selector is actually implemented by the class above:

let thisSource = MyDataSource()
thisSource.responds(to: Selector("numberOfRowsInTableView:")) // false

Or can I? False? That’s no good. I’m using the discouraged “Selector” initializer here to ensure I get Swift to look for a very specific Selector, even if it doesn’t appear to be correct to the Swift-adapted side of the runtime.

I was scratching my head, trying to figure out why Objective-C could not see my method. Did I forget an @objc marker? No. Did I forget to make MyDataSource a subclass of NSObject? No. I finally discovered that I could second-guess the default Swift selector mapping to obtain a result that “worked”:

class MyDataSource: NSObject {
	@objc func numberOfRowsInTableView(_ tableView: NSTableView) -> Int {
		return 0
	}
}

let thisSource = MyDataSource()
thisSource.responds(to: Selector("numberOfRowsInTableView:")) // true

Instances of MyDataSource will get the job done for Objective-C calls to “numberOfRowsInTableView:”, but I’ve lost all the pretty formatting that I expected to be able to use in Swift.

There’s something else I’m missing out in my Swift implementation: type checking of MyDataSource’s compliance with the NSTableViewDataSource protocol. Old habits die hard, and I had initially ported my class over with an old-fashioned, informal approach to complying with NSTableViewDataSource: I declared a plain NSObject that happens to implement the informal protocol.

It turns that adding that protocol conformance onto my class declaration not only gains me Swift’s protocol type checking, but changes the way key functions are mapped from Swift to Objective-C:

class MyDataSource: NSObject, NSTableViewDataSource {
	func numberOfRows(in tableView: NSTableView) -> Int {
		return 0
	}
}

let thisSource = MyDataSource()
thisSource.responds(to: Selector("numberOfRowsInTableView:")) // true

Armed with the knowledge that my class intends to comply with NSTableViewDataSource, Swift generates the expected mapping to Objective-C. Notice in this final case, I don’t even have to remember to mark the function as @objc. I guess when Swift is creating the selector mapping for a function, it does so in a few phases, prioritizing more explicit scenarios over more general:

  1. First, it defers to any explicit annotation with the @objc attribute. If I tag my “numberOfRows…” func above with “@objc(numberOfDoodads:)” then the method will be made available to Objective-C code dynamically looking for “numberOfDoodads:”.
  2. If there’s no @objc specialization, it tries to match function implementations with declarations in superclasses or protocols the class complies with. This is what gives us the automatic mapping of Swift’s “numberOfRows(in:)” to Objective-C’s “numberOfRowsInTableView:”.
  3. Finally it resorts to a default mapping based on Swift API Design Guidelines. This is what yielded the default “numberOfRowsIn:” mapping that I first encountered.

This is an example of a Swift growing pain that is particularly likely to affect folks who are adapting older source bases (and older programming mindsets!) to Swift. If you run across a completely vexing failure of Objective-C to acknowledge your Swift class’s protocol compliance, start by making sure that you’ve actually declared the compliance in your class declaration!

Better Swift Completion

Apple released Xcode 9 earlier this week, and in spite of a few glitches here and there, I have found the update to be an overall improvement over Xcode 8. It’s nice that Apple continues to invest in the core tools for Mac and iOS developers.

I’ve been dabbling in more and more Swift development lately, and it’s brought to light a shortcoming in Xcode’s code completion which has unfortunately not improved in Xcode 9: completion of Swift function calls when there is a large quantity of candidates.

Take for example NSAttributedString. If I want to initialize a new instance in Swift, I type “NSAttributedString(” to bring up the list of compatible init methods I can choose from:

SwiftCompletion

The problem at this point is that I have to navigate the menu by hand. I can’t narrow down the list of completions any further by typing, because the very next character I type will be interpreted as the manual filling out of parameters of the NSAttributedString initializer.

BadCompletion

This is a situation where Objective-C gets much nicer treatment in the editor. Because completion in Objective-C begins when I start typing “init”, and because the named first parameter is part of the init message name, I can winnow down the results quite a bit:

Pasted Image 9 22 17 11 24 AM

Better still, because Xcode performs a fuzzy match on the typing, I can proceed to type the names of additional parameters to zero in completely on the variation I want:

MEAppController AppDelegate m Edited

When I accept the completion, all of my typing is replaced with the expected, templated parameter placeholders for the chose initializer.

I filed Radar #34594940 requesting better completion for Swift.

Debugging Swift: Error in Auto-Import

Have you ever tried debugging Swift code in an embedded framework, and met resistance from lldb in the form of a cryptic AST context error?

error: in auto-import:
failed to get module 'RSAppKit' from AST context:

<module-includes>:1:9: note: in file included from <module-includes>:1:
#import "Headers/RSAppKit.h"
        ^
error: [...]/RSAppKit.h:1:9: error: 'RSAppKit/SomeHeader.h' file not found
#import <RSAppKit/SomeHeader.h>
        ^

error: could not build Objective-C module 'RSAppKit'

After hours of trying to unravel this mystery, I discovered the root cause: the framework that is embedded in my app does not, in fact, contain any headers. They were stripped by Xcode when it copied the framework into the app.

In my opinion, Xcode and/or lldb should be smart enough to handle this situation, by preferring the version of the framework in the “Built Products” directory, which still has its header files in-tact. Radar #31502879 requests this, hopefully Apple will fix it.

In the mean time, you can work around the problem by setting the REMOVE_HEADERS_FROM_EMBEDDED_BUNDLES build setting to NO in the app that embeds the framework:

Xcode build settings showing REMOVE_HEADERS_FROM_EMBEDDED_BUNDLES set to NO for DEBUG builds.

You probably want to make sure it remains set to YES for Release builds, so that you don’t ship your framework’s header files to your customers.

System Level Breakpoints in Swift

Any great software developer must inevitably become a great software debugger. Debugging consists largely of setting breakpoints, then landing on them to examine the state of an app at arbitrary points during its execution. There are roughly two kinds of breakpoints: those you set on your own code, and those you set on other people’s code.

Setting a breakpoint on your own code is simple. Just find the line of source code in your Xcode project, and tap the area in the gutter next to the pertinent line:

MotoAppSwift

But what if you need to set a breakpoint on a system API, or a method implemented in a drop-in library for which you don’t have source code? For example, imagine you are hunting down a layout bug and decide it might be helpful to observe any calls to Apple’s own internal layoutSubviews method on UIView. Historically, to an Objective-C programmer, this is not a huge challenge. We know the form for expressing such a method symbolically and to break on it, we just drop into Xcode’s lldb console (View -> Debug Area -> Activate Console), and set a breakpoint manually by specifying its name. The “b” shorthand command in lldb does a bit of magic regex matching to expand what we type to its full, matching name:

(lldb) b -[UIView layoutSubviews]
Breakpoint 3: where = UIKit`-[UIView(Hierarchy) layoutSubviews], address = 0x000000010c02f642
(lldb) 

If you’re intimidated by the lldb console, or you want the breakpoint to stick around longer than the current debug session, you can use Xcode’s own built-in symbolic breakpoint interface (Debug -> Breakpoints -> Create Symbolic Breakpoint) to achieve the same thing:

Image of Xcode's symbolic breakpoint editor

In fact, if you add this breakpoint to your iOS project and run your app, I am pretty sure you will run into a breakpoint on Apple’s layoutSubviews method. Pop back into the lldb console and examine the object that is being sent the message:

(lldb) po $arg1
<UIClassicWindow: 0x7f8e7dd06660; frame = (0 0; 414 736); userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x60000004b7c0>; layer = <UIWindowLayer: 0x600000024260>>

Now, continue and break on the symbol again. And again. Examine the target each time by typing “po $arg1” into the lldb console. You can imagine how handy it might be to perform this kind of analysis while tracking down a tricky bug.

But what about the poor Swift programmers who have come to our platforms, bright-eyed and full of enthusiasm for Swift syntax? They who have read Apple’s documentation, and for whom “-[UIView layoutSubviews]” is impossible to parse, whereas “UIView.layoutSubviews” not only looks downright obvious, but is correct for Swift?

Unfortunately, setting a breakpoint on “UIView.layoutSubviews” simply doesn’t work:

(lldb) b UIView.layoutSubviews
Breakpoint 3: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) 

This fails because there is no Swift type named UIView implementing a method called layoutSubviews. It’s implemented entirely in Objective-C. In fact, a huge number of Objective-C methods that are exposed to Swift get compiled down to direct Objective-C message sends. If you type something like “UIView().layoutIfNeeded()” into a Swift file, and compile it, no Swift method call to layoutIfNeeded ever occurs.

This isn’t the case for all Cocoa types that are mapped into Swift. For example, imagine you wanted to break on all calls to “Data.write(to:options:)”. You might try to set a breakpoint on “Data.write” in the hopes that it works:

(lldb) b Data.write
Breakpoint 11: where = libswiftFoundation.dylib`Foundation.Data.write (to : Foundation.URL, options : __ObjC.NSData.WritingOptions) throws -> (), address = 0x00000001044edf10

And it does! How about that? Only it doesn’t, really. This will break on all calls that pass through libswiftFoundation on their way to -[NSData writeToURL:options:error:], but it won’t catch anything that calls the Objective-C implementation directly. To catch all calls to the underlying method, you need to set the breakpoint on the lower level, Objective-C method.

So, as a rule, Swift programmers who want to be advanced debuggers on iOS or Mac platforms, also need to develop an ability for mapping Swift method names back to their Objective-C equivalents. For a method like UIView.layoutSubviews, it’s a pretty direct mapping back to “-[UIView layoutSubviews]”, but for many methods it’s nowhere near as simple.

To map a Swift-mapped method name back to Objective-C, you have to appreciate that many Foundation classes are stripped of their “NS” prefix, and the effects of rewriting method signatures to accommodate Swift’s API guidelines. For example, a naive Swift programmer may not easily guess that in order to set a breakpoint on the low-level implementation for “Data.write(to:options)”, you need to add back the “NS” prefix, explicitly describe the URL parameter, and add a mysterious error parameter, which is apparently how cranky greybeards used to propagate failures in the bad old days:

(lldb) b -[NSData writeToURL:options:error:]
Breakpoint 13: where = Foundation`-[NSData(NSData) writeToURL:options:error:], address = 0x00000001018328c3

Success!

For those of you mourn the thought of having to develop this extensive knowledge of Objective-C message signatures and API conventions, I offer a little hack that will likely get you through your next challenge. If the API has been rewritten using one of these rules, it’s almost certain that the Swift name of the function is a subset of the ObjC method name. You can probably leverage the regex matching ability of lldb to zero in on the method you want to set a breakpoint on:

(lldb) break set -r Data.*write
Breakpoint 14: 107 locations.

Now type “break list” and see the massive number of likely matches lldb has presented at your feet. Among them are a number of Swift cover methods that are part of libswiftFoundation, but you’ll also find the target method in question. In fact, you’ll also see a few other low-level Objective-C methods that you may want to break on as well.

To make the list more manageable, given your knowledge that the target methods are in a given Objective-C framework, add the “-s” flag to limit matches to a specific shared library by name:

(lldb) break set -s Foundation -r Data.*write
Breakpoint 17: 8 locations.

Among these breakpoints there are a few false hits on the NSPageData class, but the list is altogether more manageable. The single breakpoint “17” has all of its matches identified by sub-numbers. Prune the list of any breakpoints that get in your way, and you’re good to go:

(lldb) break disable 17.6 17.7 17.8
3 breakpoints disabled.
(lldb) c

Apple’s mapping of Objective-C API to Swift creates an altogether more enjoyable programming experience for Swift developers, but it can lead to great confusion if you don’t understand some of the implementation details, or how to work around lack of understanding. I hope this article gives you the tools you need to debug your Swift apps, and the Objective-C code that you are unavoidably leveraging, more effectively.

Update: I filed two related bugs: Radar #31115822 requesting automatic mapping from Swift method format back to underlying Objective-C methods, and Radar #31115942 requesting that lldb be more intuitive about evaluating terse Swift method signatures.

Implicit Swift Dependencies

If you’re developing in Swift for Mac or iOS, you need to ensure that any standard Swift libraries are also copied into your app bundle. Typically this is handled automatically by Xcode when it detects the presence of any Swift files in your app. If your app is entirely Objective-C, but you link against your own frameworks that themselves depend on Swift, you have to ensure the required libraries are embedded. This can be done by setting the “Always Embed Swift Standard Libraries” checkbox in your target’s build settings to “Yes”:

AlwaysEmbed

When this option is set, Xcode scans the bundled frameworks in your app and, if any of them contains Swift code, it copies the pertinent libraries into your app’s bundle.

This approach falls down a bit when it comes to Unit Testing targets. Because a Unit Test bundle doesn’t typically copy the framework it is testing into its own bundle, Xcode seems to deduce dependencies by examining the list of frameworks the test bundle links to. If you enable the “Always Embed” option above for a Unit Testing bundle, then any framework the bundle links against will be examined for Swift code, and the required libraries copied in.

But what if none of the linked frameworks themselves require Swift, but they depend upon other frameworks that do? In projects of a substantial size, you may have higher-level frameworks implemented entirely in Objective-C, that depend upon lower level frameworks that use Swift. In this scenario, Xcode’s CopySwiftLibs build phase does not identify the deeper dependency, and neglects to embed the required Swift libraries. When your unit tests run, you’ll see a runtime error like this:

The bundle "RSTextFilteringTests" couldn’t be loaded because it is damaged or missing necessary resources. Try reinstalling the bundle.
(dlopen_preflight([...]/RSTextFilteringTests): Library not loaded: @rpath/libswiftAppKit.dylib
  Referenced from: [...]/RSAppKit.framework/Versions/A/RSAppKit
  Reason: image not found)
Program ended with exit code: 82

So “Always Embed Swift Standard Libraries” doesn’t always embed them. I’ve filed Radar #30832923, requesting that the CopySwiftLibs build phase look harder for Swift dependencies. In my opinion it should look not only at the directly linked frameworks, but at any developer-supplied frameworks that those in turn depend upon. This would cause “Always Embed” to behave as expected when a developer adds Swift dependencies to frameworks at any level of their code base.

There are many possible workarounds to the problem, and each has its own drawbacks. Here are a few:

  1. Add a Swift-based unit test file to your test bundle. This simple workaround will work in most situations, because the test itself will impose many Swift library dependencies on your bundle. There is a shortcoming, though: if the test code doesn’t generate all the Swift dependencies that your frameworks do, you’ll still be left with missing Swift libraries. For example if a dependent library requires the Swift library for Apple’s Contacts.framework, your Swift test file is not sufficient to cue Xcode to copy the required libSwiftContacts library.
  2. Manually link to Swift-dependent frameworks. Suppose your unit tests target HighLevel.framework, and LowLevel.framework depends upon Swift. By adding LowLevel.framework to the list of linked libraries for your testing bundle, you will cue Xcode to also embed the required Swift libraries for that framework. This has the advantage of ensuring that whatever Swift libraries are required get copied, but it has the drawback of requiring explicit dependency on a lower level framework from your unit test bundle.
  3. Stop! Abort mission! Revert to Objective-C only. Many Swift dabblers probably feel this emotion from time to time. The unexpected costs of adding a modest amount of Swift to a project are frustrating, and one can’t help but wonder whether they would be better off waiting until Swift support is more baked in to the system. The obvious downside of this approach is that Apple’s progress with Swift is moving quickly, and the longer you put off testing the waters, the harder it may be for you to catch up when ignoring it is no longer a viable option.

The workaround I’ve settled on for now is #2. Adding unnecessary direct dependencies to my test bundle feels a little inappropriate, but it is both easily undone in the future, and unlikely to cause any real problems in practice.

In the course of investigating this problem, it also occurred to me that unit test bundles in particular are such a special case, that Apple should probably give them special treatment to eliminate a number of pitfalls. I filed Radar #30832983 suggesting that unit test bundles should default to “Always Embed Swift Standard Libraries,” on the assumption that Swift code may be present in some library.

Thinking further on that point, it seems that Unit Tests could be safely run in an environment in which all Swift support libraries are available at runtime to be resolved by DYLD. Because test bundles are not built to be shipped to customers, it’s of primary that they link and run successfully. Such an approach would do away with the need for Unit Test bundles to either specify that Swift libraries be bundled, or to fret about which of those libraries in particular need to be copied.

Swift Maturity

Ted Kremenek of Apple announced on the Swift evolution announcements mailing list that the team will no longer accept source-breaking changes for Swift 3. That is, changes that would require developers’ own Swift code to change. He notes that this means many desirable features will not make the cut, and will have to be pushed to Swift 3.1 or beyond:

The challenge of course is reconciling these diametrically opposing goals: maintaining source stability while having the ability to incorporate more core (and important) language changes that are possibly source-breaking.

How will they balance this going forward? He hints that the team wants to support a mechanism whereby developers can specify a version of Swift as a parameter to the compiler. Your code builds against Swift 3.1? The Swift 4 compiler will be able to handle that:

Our goal is to allow app developers to combine a mix of Swift modules (e.g., SwiftPM packages), where each module is known to compile with a specific version of the language (module A works with Swift 3, module B works with Swift 3.1, etc.), then combine those modules into a single binary.

This is great news for developers, but only strengthens my argument that Swift needs a mechanism for SDK-conditional compilation. At this point, a developer who wishes to maintain source code that compiles against, say, iOS 9 and iOS 10, must conditionalize on the version of Swift:

#if swift(>=2.3)
	// iOS 10 only code
#else
	// iOS 9 friendly code
#endif

When and if Ted Kremenek’s promise of a multiversioned Swift compiler comes to pass, it will presumably mean multiple versions of Swift can compile against the same SDK, so this fragile workaround will no longer … work.

Update: It occurs to me, multiple versions of Swift already do build against the same SDK. Currently we have Swift 2.3 and Swift 3 building against Apple’s latest beta SDKs. It’s the “>=” in the workaround that guarantees a suitable SDK match for now.

Swifty SDK Changes

I’m not quite ready to leap to Swift 3.0, so when the Xcode 8 beta asked me to migrate my existing Swift code, I opted to switch to Swift 2.3.

Swift 2.3 is notable for its utter dearth of new behaviors, compared to Swift 2.2.1. Since no code changes are required, you should be able to opt in to 2.3 on Xcode 8, while continuing to build and distribute with Swift 2.2.1 on Xcode 7.

On the other hand, you may run into some system SDK changes that force you to accommodate distinctions between, for example, the macOS 10.11 and 10.12 SDKs. Here’s an example of a build failure I ran into when I set to building my app on Xcode 8:

Image of Xcode exhibiting a build-time error because of a Swift method call

“invalid use of ‘()’ to call a value of non-function type ‘Selector'”

What does it mean? It means that in the 10.11 SDK, “action” is defined as a method:

@protocol NSValidatedUserInterfaceItem
- (nullable SEL)action;
- (NSInteger)tag;
@end

While in the 10.12 SDK, it’s been upgraded to a property:

@protocol NSValidatedUserInterfaceItem
@property (readonly, nullable) SEL action;
@property (readonly) NSInteger tag;
@end

Objective-C doesn’t care whether you access the value as a property, or by invoking the method that implements the property. Swift … does.

So, the fix in Xcode 8, with the 10.12 SDK, is to delete those parentheses so Swift appreciates that it’s accessing a property. But doing so will break the build back on the 10.11 SDK. What’s a forward-looking, backward-looking developer to do? You might think you could take advantage of Swift’s #available operator, and that in fact best expresses what we’re trying to do: to behave differently depending on the specific SDK that is being used:

if #available(OSX 10.12, *) {
	thisAction = anObject.action
}
else {
	thisAction = anObject.action()
}

But this still produces a build error, because Swift compiles both code paths of an #available block. Unfortunately, I think the only way to convince Swift to absolutely ignore the problematic line is to use a Swift compiler check, instead. Note that this is a bad solution because we are fundamentally not concerned about the version of Swift being used. If Swift 2.3 were made available in Xcode 7, against earlier SDKs, then the logic of our test would fail:

#if swift(>=2.3)
	thisAction = anObject.action
#else
	thisAction = anObject.action()
#endif

In my opinion, we need a compile time, conditional code exclusion that operates on the SDK, similarly to #available. Something like “#if sdk(OSX 10.12)”. As far as I know, nothing like this exists.

I’ll report a bug to Apple requesting such functionality, but I wonder if I’m overlooking something obvious that already fits the bill. Any ideas?

Update: Radar #26895983.

Nullable Edge Cases

I’ve recently embarked on the tedious process of annotating my source code for nullability. This is a good idea in theory, because it adds information about assumptions in your code, which may have been previously held in arcane comments, or worse yet your mind, in a format that can be readily understood by the compiler. If you’re bridging your Objective C to Swift, the idea goes from good to essential, as the nullability information is critical to Swift working safely with your existing classes.

Does nil make sense here or not? If you decide to take on this task, you’ll ask yourself that question again, and again, and again. It sounds like such an easy thing to answer, and it certainly is in many cases, but the edge cases are deeply tied to one of several impedance mismatches between Objective C and Swift.

The easiest answers apply to methods where you can state with absolute certainty that programmers should never pass nil as a method. For example, I’m confident in annotating this method’s single parameter nonnull because the functionality of the method would be inherently undefined were a nil value supplied:

- (void) presentMessageToUser:(NSString*)theMessage;

The implications of marking a method parameter as nonnull are less worrisome than marking a return value, at least with respect to bridging the gap between Swift and Objective C. Because a method parameter will almost always represent the mapping of a Swift variable into Objective C, where handling of nil is traditionally safer, it doesn’t matter as much if a Swift variable passed to a nonnull Objective C parameter actually an optional. Marking method parameters optimistically nonnull is a fairly safe move.

Marking return values accurately is more important, because it maps possibly null values into Swift variable classes that won’t allow it. The annotation of a return value can be very straightforward, if inspection of the method indicates a 100% likelihood of a null response:

- (NSString*) importantString
{
	NSLog(@"Not implemented! Subclass responsibility.");
	return nil;
}

Or a nonnull one:

- (NSString*) errorMessage
{
	return @"Welp!";
}

For this method to return nil, the statically allocated string that the compiler creates and compiles into your binary would have to somehow turn to nil at runtime. If this were to somehow occur, you’d be facing much worse problems than the nonnull method managing to return nil.

It’s only slightly more complicated when the code path for a method can be audited to a degree that a nil result seems exceedingly unlikely:

- (NSString*) localizedErrorMessage
{
	return NSLocalizedString(@"Welp!", @"Error message of last resort");
}

For this method to return nil, NSLocalizedString, which is a macro that maps to -[NSBundle localizedStringForKey:value:table:], would have to return nil. This method is not only annotated by Apple as nonnull, but supported in its documentation as returning sensible, nonnull values in error cases:

This method returns the following when key is nil or not found in table:

  • If key is nil and value is nil, returns an empty string.
  • If key is nil and value is non-nil, returns value.
  • If key is not found and value is nil or an empty string, returns key.

So, any method whose return value is derived from a nonnull method, can also be annotated as nonnull. Right? Yes, for the most part.

The question of whether a method should return nil vs. whether it may return nil gets very fuzzy around the edges, owing to Objective C’s traditionally very nil-friendly (some might say nil-happy) programming paradigm. Let’s take a core method, one of the most fundamental in all of Objective C, the NSObject init method. Barring an override in any Objective C class, this method will be used to initialize and return a usable instance of any just-allocated class:

- (instancetype) init;

But is it nullable? In practice, NSObject’s root implementation, at least, should never return nil. It’s safer even than -[NSObject alloc], which should also never return nil, but theoretically could if you for example had completely exhausted virtual memory. Want to absolutely convince yourself that -[NSObject init] cannot return nil? Break in the debugger and examine its disassembly:

libobjc.A.dylib`-[NSObject init]:
    0x7fff8909ebf0 <+0>:  pushq  %rbp
    0x7fff8909ebf1 <+1>:  movq   %rsp, %rbp
    0x7fff8909ebf4 <+4>:  movq   %rdi, %rax
    0x7fff8909ebf7 <+7>:  popq   %rbp
    0x7fff8909ebf8 <+8>:  retq   

To summarize the behavior of this simple method, literally all it does, apart from the probably unnecessary stack-manipulating boilerplate, is to move the parameter in register %rdi (the first parameter to objc_msgSend, the object instnace itself) to register %rax (the return value of the method). It’s just “return self”, and self has to be nonnull or else this method wouldn’t have been reached.

Yet if you examine the objc/NSObject.h header file, where -[NSObject init] is declared, you’ll find something curious: it’s not annotated for nullability at all. And although I showed by disassembly above that it is guaranteed 100% to be nonnull, here’s what Apple’s own documentation says:

Return Value
An initialized object, or nil if an object could not be created for some reason that would not result in an exception.

Although this is declared on the documentation for NSObject, it’s no doubt based on the Cocoa convention that any class’s own particular -init method may and in fact should return nil if it cannot be initialized:

In a custom implementation of this method, you must invoke super’s designated initializer then initialize and return the new object. If the new object can’t be initialized, the method should return nil.

NSObject’s init method is one example of many situations in Objective C where nil is an expected return value in edge cases, but where as a general rule, a nonnull value will be returned. This is at huge odds with Swift’s emphasis on variable values being either nonnull or nullable by design.

In recent Xcode releases, Apple has added a useful clang analysis option, off by default, called CLANG_ANALYZER_NONNULL. When enabled, the analyzer goes the extra mile identifying situations where your code contains paths that will violate the spirit of your nonnull annotations. This has been very useful to me in identifying some spots where I missed a nuanced behavior of a method when adding nullability annotations. However, it also identifies a lot of defensive coding techniques that I’m frankly not prepared to abandon. The Objective C, it is strong in my family. Here is an example of a category class method I’ve defined on NSImage (explicit nonnull annotations added for emphasis):

+ (nonnull NSImage*) rsImageWithCGImage:(nonnull CGImageRef)srcImage
{
	NSImage *newImage = nil;
	if (srcImage != NULL)
	{
		NSBitmapImageRep *bitmapImageRep = [[NSBitmapImageRep alloc] initWithCGImage:srcImage];
		if (bitmapImageRep != nil)
		{
			newImage = [[NSImage alloc] init];
			[newImage addRepresentation:bitmapImageRep];
		}
	}

	return newImage;
}

Clang analysis rightly reveals that although I’ve marked this method as having both a nonnull parameter and a nonnull result, the implementation of the method behaves otherwise. But this method is exceedingly unlikely to return nil. The srcImage parameter is marked nonnull, so that first line of defense should always be breached, and the -[NSBitmapImageRep initWithCGImage:] is so likely to return a nonnull value, it is annotated as nonnull (even though the documentation claims it may return nil, Radar #26621826).

I struggle with methods like these: do I mark them optimistically as nonnull, or do I suck it up and concede that they may in fact return nil? Do I remove the defensive checks for nil that I’m so accustomed to, and migrate to the Swift-style assumption that nonnull values will be as they say they will? I clearly have trust issues.

The consequences of vending a null value to a Swift non-optional are dire: the app crashes immediately either unwrapping an implicitly unwrapped optional, or directly accessing a nonnull value. Because of this, I think that in annotating Objective C code for nullability, one must err heavily towards marking return values as nullable, even if such a result is unlikely. How heavily will I err? Will I assume that every -init method may return nil, as indeed Apple has documented? I … well, no. That way lies madness. But wherever I felt compelled to put up overt safeguards in Objective C to handle possible nils from Apple’s frameworks, there is probably still cause for skepticism, even if the method has been marked as nonnull.

The down side of course is that my Swift code will have to jump through mostly unnecessary optional chaining, or other protections, to work safely with what is 99.9% guaranteed to be safe. But in the rare instances where nil is returned, especially when the circumstances that lead to it are out of my control in Apple’s own frameworks, I’d rather my Swift code behave a little bit more like my beloved old Objective C code, and fail gracefully on null.