Blog

bevkjbvkbdkdxoeoziejdoiehz fiugebfuyegwik

Detecting Swift versions

Screenshot of Xcode Swift conversion

So we are all familiar with the "Convert to latest Swift syntax..." assistant Xcode provides. Yesterday, I wondered if that couldn't be leveraged to detect if a piece of Swift code is written for the version the current Xcode provides or if it is for an older version.

First, I ventured into the Xcode headers - the functionality in question is implemented by IDEKit:

$ find . -name 'IDESwift*'
./IDESwiftMigrationAssistantContext.h
./IDESwiftMigrationGeneratingPreviewAssistant.h
./IDESwiftMigrationOverviewAssistant.h
./IDESwiftMigrationReviewChangesAssistant.h
./IDESwiftMigrationReviewChangesFile.h
./IDESwiftMigrationTarget.h
./IDESwiftMigrationTargetChooserAssistant.h
./IDESwiftMigrationTargetChooserItem.h
./IDESwiftMigrationTargetChooserTargetItem.h

But it quickly turned out that this was only UI. After some more poking, I ended up discovering a new commandline tool in Xcode 6.3 called swift-update.

$ xcrun swift-update
<unknown>:0: error: error opening '' for output: Error opening output file '': No such file or directory

Unfortunately, it was a bit unfriendly, like most of Apple's "private" CLI tools. In the end, its usage is quite similar to regular swift, a successful invocation looks like this:

$ xcrun -sdk iphoneos swift-update -sdk /Applications/Xcode-6.3.1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.3.sdk Stargate.swift -target arm64-apple-ios8.3 -o out.yaml

The output is some YAML that looks like this:

[
 {
  "file": "JaroWinkler.swift",
  "offset": 80,
  "remove": 13,
  "text": "count",
 },
]

So it is an array of locations in the given files that need to be changed to update to the latest Swift syntax. From there on, it was quite trivial to build a little Ruby library around this which set up the correct flags for swift-update and provides its results in a convenient form.

The tool is called Grenouille and its basic usage looks like this:

$ grenouille Stargate.swift 
1.2

More information can be found in the project's README.

Swift access modifiers

This Twitter exchange encouraged me to write a few words on what effects the Swift access modifiers have.

Let's consider this example and that we want to use it in a target/action context:

import Foundation

class Thing : NSObject {
  func tap() {
    println("tap!")
  }
}

Using an internal function or even a public one will simply just work, but if we change the visibility to private it will no longer work. The first naive theory would be that this is tied to symbol mangling being different for private methods.

Little sidebar on what symbol mangling is: it is the mechanism which takes a Swift construct and transforms it to a string that is valid as a symbol in the resulting binary and also unique. If you want some more information on this, read Mike Ash's excellent article on the topic.

So, let's check out the difference in symbol mangling between different access modifiers. First the internal one:

$ xcrun swiftc test.swift 
$ nm test|grep tap
0000000100001050 t __TFC4test5Thing3tapfS0_FT_T_
[...]
$ xcrun swift-demangle __TFC4test5Thing3tapfS0_FT_T_
_TFC4test5Thing3tapfS0_FT_T_ ---> test.Thing.tap (test.Thing)() -> ()

Now the private variant:

$ nm test|grep tap
00000001000010a0 t  __TFC4test5ThingP33_83378C430F65473055F1BD53F3ADCDB73tapfS0_FT_T_

OK, this must be it - the selector mechanism knows nothing about that extra UUID-looking stuff in the private symbol. Turns out that adding the @objc attribute (or @IBAction for that matter) will make the private function work as an action, though, while not affecting the way symbols are mangled, so this cannot be the reason for the difference in behaviour. It's still interesting to know and actually makes it much harder to somehow manually call a private Swift function even if we know that it exists, because only its own compilation unit knows about the exact value of that extra UUID.

Next route of investigation is using the trusty old Objective-C runtime APIs, considering that target/action is an ObjC mechanism. With the code from this snippet, we can list all methods on our class which are known to the runtime:

import ObjectiveC.runtime

var methodCount : UInt32 = 0
var methods : UnsafeMutablePointer<Method> = class_copyMethodList(self.dynamicType, &methodCount)

for i in 0..<methodCount {
    let name = NSStringFromSelector(method_getName(methods[Int(i)]))
    println(name)
}

The result is that private methods do not show up there at all, so they are invisible to the ObjC runtime. This is nice, because it eliminates another way to bypass the access modifiers at runtime. I guess this excessive hiding is also the reason for private constructs not being testable at the moment.

To conclude, common Objective-C mechanisms will not work on private functions, because they are hidden from the runtime. In case you want to limit the privateness to Swift only, you can utilize the @objc, @IBAction or @dynamic attributes.

UPDATE: Turns out the reason why public/internal methods are not hidden from the ObjC runtime is that @objc gets implicitly added for those in Objective-C compatible Swift classes.

The compiler does not automatically insert the @objc attribute for declarations marked with the private access-level modifier.

(source)