Swift 2
The overview
As everyone is no doubt aware Swift 2 was announced at WWDC. I'm going to do a series of posts on the changes but for now let's just cover the major points:
General changes
- Global and free-standing functions now follow the same parameter label rules as methods. The
#
external name syntax is gone. - Multi-payload enums basically allow you to declare
enum SomeEnum<T,U,V>
and it just works properly. This used to give you a compiler error about unimplemented IR. - Exit-condition loop Syntax now uses
repeat { } while(cond)
instead ofdo
- The
do
keyword is now used to introduce a new scope (and is important for the new error handling anddefer
support). In C you could do this with braces but in Swift that would be interpreted as a closure, sodo
allows you to introduce arbitrary scopes. guard
blocks to explicitly declare that you want a condition to be true or you want to exit the function. The benefit is that the variables bound in theguard
are available to the remainder of the function. This lets you avoid nesting or surrounding everything with anif
just to unwrap an optional. Theelse
of aguard
must exit the function, throw an error, or call a@noreturn
function.- Doc comments switch to markdown syntax to unify with Playgrounds (from the limited reStructured Text)
- The compiler now warns or errors on things like redundant protocol conformance, unused bound values, and variables that could be made into constants
- The Swift conversion tool is smarter now and understands API changes and certain Swift warnings and can upgrade them (it isn't perfect though and definitely misses some things)
find
was renamedindexOf
,sort
becomessortInPlace
, andsorted
becomessort
.String
no longer directly conforms toSequenceType
, presumably to avoid making a bunch of the new protocol extensions available on it. The goal is to force you to uses.characters
,s.utf8
, ors.utf16
to be explicit about the unicode encoding you want to deal with.- Public extensions of generic types are allowed now
- Non-generic classes can inherit from generic classes (basically forcing the type parameters to be fixed)
- Failable convenience initializers can just return nil early instead of being forced to call self .init first. This helps but designated initializers are still forced to initialize all fields to garbage values before returning nil so there is still room for improvement.
Internal Visibility
This solves a major pain point for unit tests. Previously you had to choose from bad options:
- Include the Swift files in your test target; now you have duplicate definitions of the class in different modules, giving awesome yet horrible errors about not being able to convert 'X' to 'X' and sometimes breaking the ability to perform certain tests
- Import the main program as a module in the test. Now everything must be
public
so it is visible to the test, sometimes including internal details that should beprivate
Now you can enable testability and it behaves exactly like InternalsVisibleTo
in C#. The internals of the main app target module become visible to the test module.
- In your Test configuration for your app or framework, enable testability
- In your unit tests, use
@testable import {ModuleName}
This causes your test builds to skip certain optimizations and keep internal symbols around which are then imported into your test module. The docs warn that this should only be used for debug/test builds because it prevents certain optimizations
Pattern Matching
The switch
pattern matching syntax and the if let ..., .... where
syntax have been generalized. You can use the comma operator and where
conditions in any control flow statement. You can also use a new case
conditional, e.g. if case .Silly(let a) { }
. There's a special form of this for Optional<T>
: if case let a? = anOptional { }
.
Pattern matching also works in loops: for case let thing? in array { }
.
This is another feature that deserves its own post.
Objective-C Generics and __kindof
What is this doing in a post about Swift? It makes some kinds of bridging much cleaner and easier. I can't hope to do it justice in this post so I'll save it for a separate article.
Error Handling
This is not exceptions the way we usually think about them! This is an early return operation that makes your function return a Result<T, Error>
but it hides all the early returning, error unwrapping, etc.
let systemAttributes: [NSObject: AnyObject]?
do {
systemAttributes = try NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last!)
} catch _ {
systemAttributes = nil
}
It seamlessly interoperates with Objective-C, exposing Swift throws
methods as selectors ala -(BOOL or nullable type)someMethodTakingParam:(type)param error:(NSError **)
. Framework methods that follow this pattern automatically import as throws
methods.
I should be clear though: This is not checked exceptions like Java. Swift doesn't care about the types, only that you either handle the exceptions or pass them along. This is another feature that deserves an entire article of its own.
Defer
Defer is also important because it lets you replace the traditional C-style if(err) goto cleanup
. Immediately after acquiring a resource just place defer { release_resource() }
. Then no matter how the function returns (explicitly early or from a do/try/catch) it the resource will be cleaned up. This also means the releasing of the resource lives next to the acquiring of it. That may seem like a small thing but it really does matter.
NS_OPTIONS and OptionSetType
Bitwise enumerations now use array style syntax for combining instead of |
and have the full range of set operations available. Check if a flag is set by contains
, or perform other set operations like isSubsetOf
or isDisjointWith
, etc. This is a huge improvement in expressing intent rather than manipulating bits directly.
This change means bitwise options aren't actually enumerations anymore; declare them as a struct implementing the OptionSetType
protocol, provide a rawValue
property, and create the values as static members of the struct. Swift will handle the rest, automatically giving you all the set operations. This is something I would expect to gain more explicit syntax in the future.
Protocol Extensions
Protocols can now be extended, including generic protocols with associated type constraints. You can also provide default implementations of the protocol.
Previously there was no way to say "I want to extend CollectionType
with a method X but only if the types in the collection satisfy certain conditions". Now you can and a lot of the old global functions like map
, filter
, and sort
have been converted to be extensions.
This solves a lot of pain points and is worthy of an entire blog post of its own. In the mean time, check out the WWDC session Protocol Oriented Programming for some details.
API auditing
A lot of APIs have been further audited and make more sense. A few examples:
UITableView
'sdequeueReusableCellWithIdentifier
now returns aUITableViewCell?
- UIKit properties are now declared as actual properties;
translatesAutoresizingMaskToConstraints = false
instead ofsetTranslatesAutoresizingMaskToConstraints(false)
.
Availability
The @available
attribute was already usable from Swift 1.2 but the improved support is nice. A new strange syntax if #available()
was added and provides a supported way of handling version checks instead of insert your favorite ad-hoc method here.
Unfortunately you can't just declare a property as UISearchController
and target iOS 7, then guard access to that property within the class. Swift wants the entire class definition to be OK or not OK.
You can also no longer adopt protocols unless they are supported on all OS versions you are targeting unless you mark the entire class as available only on the newer OS version.
This means having separate subclasses and guarding the creation of the appropriate instance with if #available()
checks.
Nevertheless I've personally shipped a bug that crashed my app on iOS 4.0-4.1 because the compiler failed to warn me that a method was only introduced in iOS 4.2 so I'll take the workarounds to the alternative of silent time bombs.
C Function Pointers
Swift can now work with C function pointers and CFunctionPointer
no longer exists. Any global function, nested function, or closure that doesn't capture state can be passed as a C function pointer directly. You can also call a function received from C code.
You can explicitly denote this with a new attribute @convention(c)
to indicate the function should use a C calling convention. Easy and painless. As part of this change @objc_block
was also removed and replaced with @convention(block)
, though given the automatic bridging support for blocks I can't think of a use for it. The default for all functions and closures is @convention(swift)
.
Libraries
Not specifically a language feature but it appears that iOS 9 will ship with various versions of the Swift standard library and future OS updates will add future revisions. Combined with the new App Thinning support and the app store can strip the Swift standard library out of your download. I'm still trying to source more details about exactly how this will work.
Omissions
One somewhat glaring omission is dealing with asynchronous code.
Apple provides us with GCD, which is a powerful foundation on which to build many asynchronous operations and concurrency primitives.
Yet every single thing we do these days in building both user interfaces and APIs requires thinking about asynchronicity and concurrency. The days of making the user's world stop while we block for a file read died a long time ago.
It's a constant pain point. Not a large one, but even paper cuts can be deadly if repeated daily and frequently.
Both C# and JavaScript have moved to async/await to give us first-class language support for asynchronous code. I think a lot of us are interested to see what sort of sugar Swift can offer to help us ensure correctness in this area. I don't know if we'll see anything in the Swift 2.0 timeframe but I'm crossing my fingers.
Open Source
The announcement that got the largest response in the room was definitely the open-sourcing of Swift. Apple has promised this is coming before the end of the year and I have no reason to doubt them; having discussed this with a member of the compiler team they seem really excited about it and dead-set on making it happen no matter what. (I'm mildly disappointed they didn't pull a classic Apple and announce that it was being open-sourced today but I'm still really happy with the announcement)
Conclusion
There's a lot to like in Swift 2. The Swift team at Apple promised us they'd move quickly and so far they've been delivering on those promises. It's an exciting time to be a developer for Apple platforms!
This blog represents my own personal opinion and is not endorsed by my employer.