I did a small project when Swift was released.
Now, a year later, I want to start the project back up again.
When I fired up the project I got a stack of 10 compiler errors and warnings. Here’s what I ran into, how I fixed it, and why the change was made if I could figure that out.
Array types are now written with the brackets around the element type
Instead of let emptyArray = String[]()
, we use let emptyArray = [String]()
. This one is pretty easy, and the error message explicit and helpful.
Use of unresolved identifier ..
Looks like the range syntax was changed. You can no longer use ..
to indicate a half-open range.
Closed Range Operator
...
– makes a range including the last number, so0...3
is a range containing 0, 1, 2, and 3. Half-Open Range Operator..<
– makes a range excluding the last number, so0 ..< 3
is a range containing 0, 1, and 2.
This makes sense, it’s more explicit. I never could remember in Ruby or CoffeeScript which set of dots was which.
IBOutlet
property has non-optional type UILabel
This was a change in Beta 4. UILabels are optional and should now be marked as such. Just add !
after the UILabel
to create an “implicitly unwrapped optional”:
@IBOutlet var timeDisplay : UILabel!
From Apple’s “Using Swift with Cocoa and Objective-C”:
“When you declare an outlet in Swift, you should make the type of the outlet an implicitly unwrapped optional. This way, you can let the storyboard connect the outlets at runtime, after initialization. When your class is initialized from a storyboard or xib file, you can assume that the outlet has been connected.”
More on implicitly unwrapped optionals.
required
modifier must be present on all overrides of a required initializer
This was the broken line:
init(coder aDecoder: NSCoder) {
Swift compiler now strictly enforces the presence of required initializers in subclasses…. if you subclass a Cocoa class that conforms to NSCoding (e.g. UIView) and add your own designated initializer, you must also define init(coder:). If you don’t want to actually implement it, you can simply make it fail at runtime, like so:
The compiler now requires overrides of designated initializers to be explicitly marked with override and implementations of required initializers — with required
The fix is:
required init(coder aDecoder: NSCoder) {
To require that subclasses implement a superclass’s initializer, mark the superclass’s initializer with the
required
declaration modifier. The subclass’s implementation of that initializer must also be marked with therequired
declaration modifier.
CGFloat
is not convertible to Float
I was doing some math on the window height. Pulling self.frame.height
gives you the height property of a CGRect
class, which is a CGFloat
type.
I am doing math with some other values, and for them to play together they need to be the same type. The best solution for me is to declare all values as CGFloat
:
let interval: CGFloat = 1.0
CGFloat is a typealias for either Float or Double depending on whether you’re building for 32 or 64-bits. This is exactly how Objective-C works, but is problematic in Swift because Swift doesn’t allow implicit conversions. We’re aware of this problem and consider it to be serious: we are evaluating several different solutions right now and will roll one out in a later beta. As you notice, you can cope with this today by casting to Double. This is inelegant but effective
Extra argument selector
in call (to NSTimer.scheduledTimerWithTimeInterval
)
At first I thought the signature had changed, but that is not the case. It turns out it wasn’t seeing the first param correctly, because I was sending an interval of type CGFloat
, which doesn’t automatically cast to NSTimeInterval
. The solution:
var timer = NSTimer.scheduledTimerWithTimeInterval(
NSTimeInterval(interval),
target: self,
selector: "tick",
userInfo: nil,
repeats: true)
Resources
If you liked this post, share it on Twitter
« Previous: Book: How to Fail and Still Win Big • Next: Create an Infinite Scroll List with Bacon.js »