Learn how Swift can help you define away some common pitfalls in app development, allowing your apps to benefit from safer runtime behavior while enjoying strong guarantees provided by Swift at compile-time. Hear about how API availability checking in Swift allows you to easily take advantage of new APIs while guaranteeing safe deployment to earlier OS releases. See how enumerations and protocols can help not only maintain compile-time invariants between your app's code and assets but also reduce boilerplate.
Thank you for joining us.
My name is Ted Kremenek.
I manage the Swift team at Apple.
Alex and I have the real honor and privilege to talk
to you this afternoon about some great ways to use Swift
in practice to find more issues in your code at compile time.
This is a pretty broad theme, so we decided
to focus on two topics.
The first is about taking advantage
of the new language affordances in Swift 2
to easily allow your application to take advantage
of new APIs while deploying to earlier releases.
We talked about this briefly in earlier sessions.
We are going to really dive into the philosophy
of how it's designed, the problems it was solving,
and how best to use it in your code.
Afterwards, Alex is going to talk about how
to use the rich type system, protocols, enums,
and even protocol extensions to enforce application invariance
in your own code and to define away a lot of boilerplate.
So let's jump into our first topic,
taking advantage of new APIs.
This is a well-told story that many of you are familiar with.
As Apple, we continue to roll out rich,
new APIs in every OS release that gives you the opportunity
to build great features in your application.
Right? This is part of the reason why we do it; right?
The conundrum here is that apps have existing users; right?
They are currently...
they are not necessarily using the newest OS.
We have a rapid adoption rate on iOS,
but not necessarily everybody adopts instantaneously,
and some of them can't adopt at all for a variety of reasons.
So you're faced with a series of choices.
What do you do?
Should you just go ahead
and require your app to use the latest OS?
Right, so you get all the new APIs, but this really sucks
because you are going to lose...
you know, you lose users.
These are the people who are buying your app.
Should you, on the opposite extreme,
hold back on adopting new APIs at all?
Right, this just goes to the least common denominator
of the earliest OS that you support.
This is also bad because you are holding back
on the possible rich features you could be delivering
to your users.
And of course, you know, there's the "have your cake
and eat it too," where you can adopt new APIs while still
deploying back to earlier releases.
So this is something that we have supported technically,
you know, for a very long time.
Right, you can do this in both Objective-C and Swift.
But the reality is this is an extremely painful
But in Swift 2, we tried to make it as pain-free as possible,
and we've done that by looking at the current problems
that developers have told us about, you know, about deploying
to earlier releases and trying to solve
that problem in the language.
Now, the fundamental model hasn't changed; right?
To develop, just for our platform, we always want you
to use the latest SDK.
Right? That essentially gives you the full grab bag
of all the APIs you could potentially use
in your application.
Then you toggle the deployment target of your app
to say how far back in time you want to go.
Now, pictorially, this is pretty simple.
It's like a sliding window of releases.
You set the base SDK to the latest,
set the deployment target back to the earliest release
in that window; right?
Conceptually very simple.
So before I talk about how we do adopt new features and APIs
in Swift 2, let's take a look at the current problems we have
in the existing approach.
So fundamentally, you have to write your app so it can contend
with the absence of APIs on an earlier host OS; right?
So there are several things you have to separately consider --
the absence of entire frameworks, classes, methods,
functions, and even certain enum values are not legal to use
on an earlier release.
And the thing that really is the bummer is you have to reason
about each one of these independently.
For frameworks, you have to tediously decide, you know,
say hey, this framework is going to be optional
when I link it into my application.
If you don't do this, your app is just going to fail a load
when it launches on an earlier OS.
And then comes the actual usage of the API itself.
Let's start with classes.
So fundamentally, you are writing your app
so it behaves differently.
There's going to be conditional behavior here,
like when new APIs are available, your app is going
to do something different.
So conditional logic isn't really the problem.
The problem is how you actually conditionalize your behavior.
Here on this slide, here is the canonical way
that you check availability.
You query the Objective-C runtime, say hey,
is this class present at runtime?
The problem is that this is a bit of a lie.
Right? I mean, so the class may be available,
but that doesn't mean that it's available for you to use.
Frequently, APIs start their life as internal APIs, right,
in the OS, and they go through some time where they are baked
and their evolved, and then by the time they are released
for public use, the behavior
of the API may have completely changed.
Even though this check may succeed at runtime,
it's not the actual truth of whether
or not you can actually safely use this API.
What that means is if you use the API too early, you know,
on an OS release too early before you are supposed
to be using it, you essentially have a time bomb
in your application.
The behavior of your app that assumed
that that API would behave a certain way is now
This has bitten developers many times.
So this is a huge problem.
The other problem is it's so easy to make mistakes.
Take this new API.
With a few characters' difference,
I have completely valid code, and it's doing the wrong thing.
And this data was introduced long before NS data asset.
Your code will compile, it will even run correctly
if you are testing on the latest OS.
So the failure occurs only when you happen to run this code
on an older OS device.
Right? And that's not going to be in your common test scenario,
and in some cases it's only
up to your users to find this problem.
And what will happen is you will have a crash at runtime
when you try and use this class.
So easy to make mistakes just by having simple typos.
Methods essentially suffer from the same problems as classes.
You can type them wrong.
If you are checking for the availability of a property,
now you have to know what the selector was for that property
and you have to spell it correctly,
and also the syntax is completely different; right?
You are checking for APIs, but the syntax is different.
Functions suffer from the same problems.
You can make the same mistakes, but yet you have
yet another third way to write something.
And then if you have enums, you are just completely hosed.
You don't really have a good solution.
There is no respond to selector for enums,
so you are stuck doing a manual OS version check.
And just looking at this table, like there's a whole assortment
of book keeping that you have to have
in your head to get this right.
And this is just a very sad story indeed.
So our observations was this is just a really broken
Right? It technically works, but it's just hard to do.
And we want you to take advantage
of new APIs while continuing to support all of your users.
So we need to fix these problems.
So in Swift 2, things have changed.
It's built into the language.
You focus on how you want to structure your app so that
when it makes sense to have alternate behavior,
you just go and use those APIs.
You understand there's going to be conditional behavior there,
but you focus primarily on that.
Then the compiler has your back.
It will emit an error if you use the API in an unsafe way.
You also have unified syntax, so you don't have to think
about classes, methods, functions, even enums.
All of this is handled under one syntax that you just use.
And the compiler knows the syntax for you to use,
so if you don't get it right, it will tell you what to do.
And because the compiler is all involved all along,
and in Swift we are using modules,
all that optional linking, that just gets handled for you.
So how does this work?
So here are some APIs from core location.
Now, let's say I am deploying to iOS 9,
so I am using the iOS 9 SDK
and set my deployment target to iOS 9.
The compiler sees this information in the SDK,
so this is literally in the Objective-C headers,
which you could also view as the generated interfaces in Swift.
So the class was introduced in iOS 2,
and the method was later introduced in iOS 8.
Since I am running on iOS 9,
there's no problems just using this API unconditionally.
If I deploy back to iOS 8, still no problems.
But if I slide all the way back to deploying to iOS 7,
the compiler can see, just as we can read on this slide,
that it's unsafe to use this method,
request when in use authorization.
And the compiler will tell you that this is just unsafe code.
And it's an error.
It will literally prevent you from building this code.
And it will give you a nice safety check.
It will give you the option
of different ways you can solve this problem.
There is this note, you want to guard it with a safety check?
There is a fix it attached to it, and if you accept it,
your code is rewritten as this.
Now, there's a combination of compilation, static enforcement,
and runtime enforcement here.
What's happening is this hash available syntax.
Basically, everything within that block
of code the compiler scans to see if, you know,
what is the most recent iOS or OS X
or whatever release that's needed
to execute those APIs safely?
And then it will use that version that's mentioned
in the hash available to do the appropriate runtime check
The compiler will insert that in.
You don't have to guess how it's done, it's done efficiently,
it's cached, so you can just use it very safely.
So just by using the information in the SDK,
we get this perfect fidelity, so you get a really safe model.
So a few people have asked why are we checking
against OS version?
Isn't this something we have provided guidance
against doing in the past?
The reason is the bookkeeping is so hard to do.
At least when you are querying runtime, you get some kind
of truth, even though that truth turns
out to be a lie in many cases.
Well, logically, when we talk to app developers -- you --
you think about, you know, the experiences you want to build
in your app, it's all staged in by, like,
what host OS your users are running; right?
In each OS release, there's like a wave of new APIs.
Those basically define the set of features
that you could implement, and your users are
on different versions of the OS, and so they --
you know, they logically break into categories of the behaviors
that your apps, you know, can have; right?
And so this all just varies logically consistent.
Also, it's not checking for just the existence of one API.
It doesn't even really make sense because you usually intend
to use a bunch of APIs together.
And it's not always true that the existence
of one API implies the other.
That information is in the SDK, and the compiler can do
that bookkeeping for you.
And the compiler being involved is the real game changer here.
It makes it so the availability checks are just reliable,
and you can assume the compiler is doing the right thing.
So you get that peace of mind
that you are really defining away the whole class of problems
because you have this compile enforcement.
It also naturally goes to multiple platforms as well.
So let's say I have this NSData asset example from before.
If I want this code to target both OS X and iOS,
I can simply extend the syntax to say I am also checking
for this minimum availability on this other platform.
The star indicates essentially, you know, the all other cases,
which in this case would be Watch OS.
We put it there explicitly to call
out that there's potential control flow here.
For other platforms that aren't explicitly mentioned,
this if condition will still be executed.
Essentially, there is a true and a false.
We wanted to call out that those branches,
they still will be taken.
So we wanted to kind of explicitly call
out that behavior for readability.
Now, the availability check also naturally composes
with other affordances in Swift 2 for control flow.
So let's say you have structured your app so that you want
to do this check, and then implement some feature
and otherwise do nothing, just bail out.
Well, this composes nicely with the new guard statement,
and you can just reshuffle your code like this.
Everything below the guard is guaranteed
to have the availability provided
by the hash available tag.
And so this naturally composes to find a nice way
to factor your application.
So let's say you are deploying back to iOS 7.
So I am going to color code the APIs that are available at iOS 7
in green, and the green bar represents within this block
of code, it's safe to use anything from iOS 7 or earlier,
so this is basically the compilers,
you know, view of the world.
If I want to use an iOS 8 API, which I have color coded
in orange, you have to do the availability check; otherwise,
you get an error from the compiler.
You can think of it that within this block of code,
you are raising the privileges
of what APIs are allowed to be called.
Once I get out of that block, my privileges drop again,
where I can only call iOS 7 APIs.
If I want to call iOS 9 APIs, I can do a different check
which then gives a different range
of privileges within that block.
It's a very composable, readable model.
Let's say I am building an app
that has a whole new different set of features depending
on whether certain APIs are available.
I want to factor it out.
I am not just loading a bunch of code into these if statements.
I want to factor it out into separate functions.
This is easily done.
I can declare another function.
Let's say for pedagogical reasons, my function uses iOS 8.
I am going to call that from my conditional block.
And the problem here is the compiler doesn't know
that you are going -- that you know --
that you are only going to call this once you've done
So by default, the compiler is going to go okay,
you are targeting iOS 7.
I am going to assume you can only use iOS 7 APIs
within this function.
So if you want to use iOS 8 APIs, then you have
to do the check there.
This is not really great; right?
This does not provide a way for you to really factor your app.
It also results in redundant checks.
You can tell the compiler your intentions.
So the SDK itself has these add available adaptations
on all the methods and classes,
saying this is the minimum OS you can use for this API.
You can use those same annotations on your own code.
What that means is you can't even call this function unless
you've done the appropriate availability check.
And once that happens,
the compiler sees your code in a different light.
You can then completely eliminate that check, you know,
that extra availability check, and safely use iOS 8 APIs.
This is nicely composable
because you have other functions similarly annotated,
and you can just go directly call them,
if the functions essentially have the same API privileges.
And if you wanted to call APIs with even more privileges,
that's when you have to do the availability check.
So this is very nice, composable, easy to reason
about way about how to factor your code.
This applies, as you would expect, to methods as well,
so you can mark -- a class can be available,
but individual methods might not be.
So if you want, you can possibly instantiate the class before you
can call a specific method at a higher availability,
you would have to do the check.
This also naturally works if you want to mark the entire class
as requiring a certain minimum availability.
If you did that, can't even instantiate the class unless
you've done the availability check.
So you get this really, you know, total transitive closure
of completeness of the API availability.
And this naturally leads to some really great tricks you can do.
So let's say you had some custom, you know, blurring view
that you had on earlier iOS releases, and then Apple rolls
out a more specific subclass of UI view that you want to use
on host OSes using a new release.
You can do this kind of runtime polymorphism
with the availability guard so that if you are running
on your OS, use the native, you know, UI.
Otherwise, use your custom one.
And then the client that goes
and gets this object back doesn't need to care
about which OS version you are running on.
You have completely provided this separation of concerns.
And this works really nicely for you can do the same thing
with protocols, providing different implementations,
you can have closures, different functions.
It gives you completely new ways in which to factor your code out
and get the safety that you expect.
So we think the availability checking is pretty awesome.
It -- I think it really provides a cohesive,
safe way to adopt new APIs and deploy back to earlier releases.
The unified syntax provides a really safe programming model,
but more importantly, it gives you a way
to naturally factor your apps.
You can read your apps.
You can read the code and understand how, you know,
the invariance that you are expecting.
And I think that's incredibly powerful.
And on that note, I am going to hand it over to Alex,
who will talk about other ways you can use Swift's powerful
type system to enforce your own invariance
in your own application.
ALEX MIGICOVSKY: Thanks, Ted.
My name is Alex Migicovsky,
and I am a sample coder here at Apple.
For the past year and a half,
I have been teaching developers how
to write Cocoa apps with Swift.
I have started developing lucid dreams about Swift and Cocoa
and how you can use the two together to have safe --
compiled time safe applications.
What I want to do today is go through some
of these visions I've had and explain some
of the paradigms you can use from the visions
and apply those concepts in your own applications
so you can have compile time safe code as well.
Now, I haven't told you this yet, but in these visions,
I also dream about unicorns.
I have developed a unicorn app that lets me go see and browse
through the different unicorns I have seen in my visions.
And the first thing that I want to talk
about in my application is Asset Catalog identifiers.
This is something that everyone uses in their UIKit apps.
Now, my unicorn browsing app is pretty simple.
I have three unicorn images
that I have added to my Asset Catalog.
But what I want to do now is take a look
and see what the code looks
like when I actually create the images from the Asset Catalog.
And you'll notice that I am --
here I have three images that I am constructing.
Each of them I am passing a string
to the UIKit UI image API.
And UIKit doesn't know what assets I have actually provided
in my Asset Catalog, so I have to unwrap these images
if I actually want to use them in my application.
This is really unfortunate
because I have already defined the Asset Catalog identifiers
in my Asset Catalog.
I don't want to have this duplicate information here.
Furthermore, right here I am only using three images,
but throughout my application, I could use many, many more.
The problem is it's really hard to find in your code
where you might have typos, and you might want
to change these based on the reaction that you see
on the slide with all these typos.
I know I do.
But it's really hard to go back and find all that.
So a classic solution to that would be
to have a global constant
where you would use the same constant name everywhere
in your application.
And so if you use that correctly,
you will get your unicorn image back like you expected,
but you still have to unwrap the image
because the compiler doesn't know
and the framework doesn't know
if you are providing a valid constant name or not.
And furthermore, you can provide totally random streams
to the API and get a fatal error at runtime
because it's the NSUbiquity identity change node
notification is still a string and is valid to pass
in UI image named API.
Let's talk about how we can solve this problem.
What we have right now is a stringly typed solution.
We are passing strings everywhere in our code.
What we really want is a strongly typed solution.
What we want to be able to do is map strings
to a new kind of type.
This is going to allow us to encode information
about how we structured our application right
into the compiler's knowledge
so that we can return non-optional UI images
everywhere in our code.
The solution to that is going
to be application-specific enumerations,
enums that we define in our own application.
If we take a look at the code that we defined before,
this is what I really want to have my code look like.
I want to pass an enumeration every time I create a UI image
object so that I don't have
to unwrap the return value in my code.
So how do we implement that?
Well, the first thing that I want
to do is define a nested type on UI image that's going
to provide the mapping between enum cases
and the string representation we have defined
in our Asset Catalog.
We defined it as a nested type so other assets
that can be stored in Asset Catalog can have this,
can use this approach as well.
Once I have done that, it's pretty straightforward.
I can provide a case mapping between an enum case
and string representation.
I can do that for all my other cases as well.
Now, one really beneficial thing about this approach is
that if I accidentally have typos, if I copy
and paste a string accidentally from somewhere
and have a duplicate, the compiler is going
to give me a warning or an error telling me
that I have a duplicate case in my enumeration.
So that's really great that the compiler can help me with that.
Now, once we've defined this new type,
all we have to do is go back and write a convenience initializer
that takes in this enumeration instead of the string
and forwards the enumeration's raw value
to the UIKit defined UI image named initializer.
If we go back and look at our code,
we get unicorns everywhere we wanted them, and if we look
and see what happens when we have a typo like we had before,
the compiler now can tell us that we've had a typo
since we've encoded that information
of our application structure into our code.
So if we fix that, the compiler error goes away.
So let's talk about the benefits of this approach.
The first thing is we have centrally located constants.
If I were to add a new image to my Asset Catalog, I know exactly
where I need to go to add another image constant case.
The other benefit is this doesn't pollute the
I could have multiple objects that can be defined
in an Asset Catalog that I use this approach for.
One of the best things is that you can only use one
of these enum cases in your application
when you are constructing your UI image object now,
so the compiler can help you with that,
and now you can return non-optional images everywhere
in your code, so you don't have to worry
about forced unwrapping.
So this was a very specific approach that we're using
in this unicorn browser app, but I want you to think
about how you can use enumerations in your own code
to provide other kinds of rich mappings.
You can use more than just strings as well.
You can use integers and even selectors.
So there's a lot of opportunity
to define these mappings in your own code.
Now, that was a deep dive into enumerations, but what I want
to talk about now are segue identifiers,
since this is something that you also use
in your code all the time.
Now, recently, my visions have been getting stronger,
and I've had to develop an app that lets me actually keep track
of unicorns and download them on the fly.
So I have a more complex application.
And if we take a look at the storyboard,
it ends up being pretty simple.
I have a single view controller that can segue
to two other view controllers.
And for each of these view controllers,
I've defined a segue identifier.
What I want to do now is take a look and see what the code looks
like when we override prepare for segue
to configure the view controller
that the unicorn browser view controller would present.
So we've overridden this method, and the classic way
of implementing this would be something like this,
where we switch on the segue identifier string.
Now, you saw before I am using the same exact strings
that I defined as my segues that I had in my storyboard,
but the compiler doesn't know this.
So when I switch on these two strings only,
the compiler is going to tell me
that this is not an exhaustive check, and so I have
to add a default case because the compiler doesn't know
that I have actually provided valid mappings.
But what happens if I were to add a new view controller
and I have to -- I have a brand-new segue?
How do I know where in my code
that I actually need to change this logic?
Well, let's take a look and see how we can solve this problem
by using enums again.
So I've defined a nested type on unicorn browser view controller
which is going to represent the mapping
between segue identifier cases
and the string representation in our storyboard.
So let's see how we can implement the prepare
for segue method with more stronger typing.
So the first thing that I am going
to do is grab the segue identifier string
from the storyboard segue object
and construct the segue identifier enum
from that raw value.
And I am going to provide a little bit
of runtime checking debugging just
in case I haven't provided a valid enum case
for that new segue identifier.
Now, from there I can switch on the enum instead of the string.
And that's really great because all I have to do now is switch
on two cases, the compiler knows that I've only defined two cases
in my enumeration, so that's all I have to switch on in my code.
Now, if we add a new segue identifier in our enumeration,
the compiler is going to actually tell us
that we don't have an exhaustive switch, and so everywhere
in our code that we switch on this enumeration,
the compiler is going to tell us exactly
where we need to update our logic.
Now, this is a huge benefit over the string solution.
So this is how you can override prepare for segue,
but sometimes you need to invoke perform segue
with identifier manually.
And in our case, I might want to import a bunch of unicorns,
download them, download images for them on the Web,
and then present a new view controller.
So let's take a look and see how that code might look.
So a classic solution to this would be to pass strings
in the perform segue with identifier method.
But we've already defined this enumeration.
We really just want to use this mapping
that we've already provided.
So we want to use enums instead.
How do we do this?
Well, it's actually pretty straightforward.
We can define an overload on the UIKit define perform segue
with identifier method that now takes enumeration instead
of a string and then call the UIKit define method
with the raw value of our enumeration.
And if we go back to the code that calls this method
with the enumeration, it works exactly like we want it.
So this is a really great solution specific
to unicorn browser view controller.
But what happens when we scale this up a bit?
I want to take a look now at the structure of what we just did
to see how we can apply this to more
than just the unicorn browser view controller.
So what we've really done is provided a mapping
between enum cases and their string representations
in the storyboard.
And we've also added implementation that uses
that mapping to have a stronger --
a more strongly typed system in our application.
But if we add a new view controller and we want
to do the same thing, we are going to have to duplicate all
of that work, and I don't want to do that.
What I really want to do is extract the implementation
from the view controller and define a loose mapping for what
that enumeration of segue identifiers is going
to be specific to a view controller, and by doing that,
we can use this implementation for many different kinds
of view controllers, regardless of their class hierarchy.
So you can avoid a lot of awkward class hierarchies
by reusing this implementation.
And we are going to do this by using protocols.
And so I have defined a new protocol.
We are going to call this segue handler type.
And this is what our view controllers are going
to conform to.
And now we want to define the mapping that we just mentioned,
and now it's going to be our segue identifier enumeration.
And we want to make sure that the segue identifier conforms
to the raw representable protocol.
This protocol is an underlying implementation detail
of how enumerations that are backed by other types work.
And the compiler can synthesize this automatically for you.
So that's all we have for the protocol definition.
What I want to do now is use a Swift 2.0 feature called
constrained protocol extensions
to actually add the implementation that's going
to be our generic reusable code.
So we are going to extend this segue handler type,
and then we are going to constrain it,
so we only want implementation to be added
if these constraints are met.
And first constraint is that the type that conforms
to this protocol is a UI view controller subclass,
and this is going to now allow us
to call UI view controller methods
in our protocol extension.
And the second thing is that we want
to make sure the segue identifier mapping is
between enum cases and strings.
Now that we've defined this constrained protocol extension,
all we have to do is add our implementation.
All we need to do is take the existing implementation
of perform segue with identifier that we defined previously
and slap the exact same implementation right
into the code.
And if we go back to our unicorn browser view controller,
all we have to do is add this conformance to the new type,
and we've already satisfied all the associated type constraints
of the protocol because we already identified this segue
So if we go to our handle action method, we can call the code
in the exact same way, but we are reusing the implementation
in our segue handler type.
So that was how you can reuse the perform segue
with identifier method, but what about calling prepare for segue
or handling prepare for segue?
What I want to do now is define a convenience method that's
going to take in a storyboard object
and return a segue identifier enum, and this is going to be
in our protocol extension.
All I need to do again is take the exact implementation we
defined before and return the segue identifier enum
that we created out of that code.
And so if we take a look at our prepare for segue method,
it ends up being really straightforward.
All we have to do is switch on the result return
by segue identifier for segue, which is the method
that we just defined, and we only need to provide two cases
that we are switching
on the same exact way that we did before.
But we have this generic solution.
So let's talk about the benefits of this approach.
So the compiler knows exactly the segues that we've defined
in our storyboard now when we add them
to our segue identifier enum.
So it can make sure we handle all
of the possible cases in our code.
We have a reusable solution by using this protocol extension,
we can use this implementation on any view controller
that conforms to this new protocol.
And we also have convenience syntax.
We can use method syntax on these different --
on these different view controllers.
It doesn't have to be free functions.
So this is a really specific way that you can use protocols
and constraint protocol extensions
in this unicorn browser app, but you all have a lot
of other interesting applications,
and what I want you to do is think
about how you can use protocols and associated type constraints
in your own applications to encode some of the structure
of your application to the compiler so it can help you
with compile time safety.
I want you to also think
about how you can use protocol extensions
to share implementation throughout your application
and avoid a bunch of awkward class hierarchy problems.
So Ted and I spoke about a lot today, but what want you to get
out of this is that the compiler is here to help you.
Ted spoke to you about how you can safely take advantage
of new APIs.
Now, this is largely done by the compiler,
and so the compiler knows what is available and what is not
for specific OS versions.
And I spoke to you about strong typing in your applications
and forcing application invariance
and leveraging the compiler,
letting it know what the constraints are
of your application and encoding that information into your code
so that the compiler can help you reason about problems
at compile time and not at runtime.
So for more information,
I suggest you go watch the Protocol-Oriented Programming
in Swift session online, and if you want to have lucid dreams
about Cocoa and Swift yourself, I suggest you go check
out these two samples.
The lister sample has an example
of the segue handler type protocol,
and DemoBots uses a bunch
of interesting ways it use enumerations in protocols
for compile time safety.
Now for more information, you can check
out the Swift Language Documentation.
We have the Dev Forums, and you can contact Stefan
if you have any questions.
Thanks, everyone, and I am looking forward
to seeing you at the labs.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.