So we are all excited about all the new features those Swift betas bring us, but we also lose some things. From the Swift 1.2 release notes:
• Swift now detects discrepancies between overloading and overriding in the Swift type system and the effective behavior seen via the Objective-C runtime. (18391046, 18383574)
What this also takes away is overriding Objective-C methods in extensions, a technique that I use in my CLI test runner xctester:
extension XCTestCase {
[...]
func recordFailureWithDescription(description: String!, inFile filePath: String!, atLine lineNumber: UInt, expected: Bool) {
[...]
}
}
The -recordFailureWithDescription:inFile:lineNumber:expected:
selector is already declared in XCTestCase
, but up until now, Swift was kind enough to let us do this anyway.
With 1.2, this gets significantly more verbose. I am utilizing an old ObjC runtime friend imp_implementationWithBlock()
to do this, but where to we put that code and how do we create a compatible block?
Global variables are lazy in Swift, so they're not an option, neither is +load
in a dummy class. I opted for the unelegant solution of using +initialize
and instantiating a dummy object like this:
class LolSwift: NSObject {
override class func initialize() {
let recordFailure_IMP = imp_implementationWithBlock(unsafeBitCast(recordFailure_block, AnyObject.self))
let recordFailure_method = class_getInstanceMethod(XCTestCase.self, "recordFailureWithDescription:inFile:atLine:expected:")
let recordFailure_old_IMP = method_setImplementation(recordFailure_method, recordFailure_IMP)
}
}
and then later
let l = LolSwift()
This works, but how do we actually form that recordFailure_block
? A simple Swift closure converted will crash at runtime 😭. What we need is an @objc_block
, defined like this:
let recordFailure_block : @objc_block (sself: XCTestCase, description: String!, filePath: String!, lineNumber: UInt, expected: Bool) -> Void = { (sself, description, filePath, lineNumber, expected) -> (Void) in
[...]
}
Let there be much rejoicing, the tests are running again \o/.