Back in Bite #292 we looked at Sourcery and how we could use it to add handy properties like count to our Swift enums. Sourcery is still just as awesome but Swift 4.2 actually removed the need for using it in this... case. 🤭 Let's take a look.
We'll start with the problem we're trying to solve. Let's imagine a simple enum:
enumSpaceships{casetantiveIVcaseghostcasefalcon}
We'll be adding to the list of cases in our enum frequently as new spaceships are added, and we'd like to display the number of spaceships in our app. We could extend our enum, adding a count property:
Maintaining this code will not be fun. As we add new enum cases, we'll need to never forget to always also update this extension property's implementation as well. Yuck.
In Swift 4.2 we can solve this quite elegantly, by ditching our extension and instead conforming our enum to the new CaseIterable protocol:
Now, we can access the number of spaceships anywhere in our code:
print(Spaceships.allCases.count)
Neat!
We're using count for simplicity here, but as the name suggests, the allCases static property that CaseIterable adds to our enums of course allows us to easily iterate through our enum's cases.
In Bites #316 and #317, we began looking at Swift's new Codable (Encodable & Decodable) protocols. Today we'll continue by learning more about how Swift Enums work can work with these protocols. Let's get started.
First off, lets try a basic example. Here's an enum:
We'll get an error: Type 'SpaceshipKind' does not conform to protocol 'Decodable'.
We can get around this by making our enum a "raw" value type like a String:
enumSpaceshipKind:String,Codable{
Nice. Since Strings are Codable, this works great. Same goes for Int and other basic Codable types.
This is a fine solution for simple use cases, but things get a little (alright, a lot) more involved when one or more of our enum cases has associated values.
If we compile now, we get that same does not conform... error.
Let's fix that by extending our enum. First, since we'll be taking on more of the encoding and decoding logic ourselves, we'll need our own Swift Error type to throw when things go wrong.
Then, we'll need to tell Swift the keys we'll be using to encode or decode our data. We'll use another enum for this, this one adopting the built-in CodingKeyprotocol:
First we pull all the values out of the decoder's container.
Then we check for each of our two possible cases, configuring ourselves and returning if successful. Finally, if we didn't return successfully and reach the end, we throw one of those CodingErrors we defined earlier.
This time we grab the encoder's container up front, then simply switch on ourselves to see which value to encode.
Now our associated value enum can be encoded and decoded all we want. Nice!
Pro Tip: Writing all of this by hand can get extremely tedious for anything more than a few simple cases. Try something like Sourcery (Bites #292, #294, and #295) to generate this more boilerplate-ish Codable implementation.
In Bite 315 we started looking at the new Codable protocol in Swift 4. Today we'll learn how to work with Date types when encoding and decoding. Let's dive in.
Swift 4 has brought with it a ton of great improvements. Today we'll cover one of them, it's the new Codable protocol! Codable ships with Swift 4 as part of the Swift standard library and allows us to conform our own types to it to get encoding and decoding functionality "for free".
Encoding and decoding types has never been easier.
Let's dive in!
Before we begin lets back up a second and look at what we're trying to accomplish when it comes to encoding and decoding.
Essentially we want to take an instance of an object or struct and convert (or "encode") it into some other, (usually "machine-readable") format. Then, later, we want to be able to convert (or "decode") the information in that format back into our object or struct.
These days the most common format for encoding/decoding is JSON. It's supported just about everywhere and has the advantage of being both human and machine readable.
For this reason, we'll be discussing JSON in this Bite. It's important to note though, that Codable isn't specific to just JSON. It's built to support encoding and decoding into just about any format we can think of.
In the past, we might have used something like the JSONSerialization class in Foundation to do encode our types into JSON.
This works great, but the encoding input and decoding output was either a Dictionary<String: Any>, or an Array of them. Then we'd need to manually convert to/from our "real" types.
With Codable, we can do much better!
Let's start with a simple struct conforming to Codable:
Nice. Now when we encode a Spaceship, the system notices that the pilot property's value is also Codable and does the right thing for us automatically:
Today we're going to begin looking at the lazy keyword in Swift. It describes some functionality in the language for what Apple calls " just-in-time calculation" of work.
This is great for times when we have expensive work to perform, or maybe the work is just some ugly complex code we'd like to tuck away. Perhaps we simply want to better compartmentalize the bits of our code that initialize UI elements, the use cases are plenty.
Today we'll start with Lazy Variables. Let's dive in!
Say we have a View Controller with a titleLabel. Pretty old school standard stuff.
We need to create the titleLabel, save it to a property, configure some stuff about it, then add it into the view heirarchy.
We want this work to be done after the view is loaded, and not when the view controller is first created, so we are forced to put it in viewDidLoad.
Additionally, we're forced to use a UILabel! force-unwrapped type on our titleLabel property to make the compiler happy.
It ends up looking something like this:
classViewController:UIViewController{vartitleLabel:UILabel!overridefuncviewDidLoad(){super.viewDidLoad()titleLabel=UILabel()titleLabel.font=UIFont(name:"Source Sans Pro",size:18.0)titleLabel.textColor=UIColor.brownview.addSubview(titleLabel)}
Let's make our titleLabellazy and clean this junk up:
classViewController:UIViewController{lazyvartitleLabel:UILabel={letlabel=UILabel()label.font=UIFont(name:"Source Sans Pro",size:18.0)label.textColor=UIColor.brownreturnlabel}()overridefuncviewDidLoad(){super.viewDidLoad()view.addSubview(titleLabel)}}
Neat!
We've now dramatically improved our viewDidLoad implementation. Cool.
We've also neatly compartmentalized the bit of code that sets up that label. Cool cool.
Perhaps most importantly though, we've now better expressed our intentions using language keywords (and their resulting behaviors), Cool cool cool.
Note: We have to use var instead of let when defining lazy properties like this. Our property might not (likely won't) have an initial value until after the instance of our view controller is created and its view is loaded. This completely breaks the rules of lets (constants).
That's all for now, in future Bites we'll look at other lazy behaviors in Swift!
We first looked at Swift's property observers back in Bite #179. They're a great way to know when a property's value is about to change, or just finished changing.
Kelan Champagne mentions an interesting technique for super-simple value-change tracking:
varpreviousStatuses=[String]()varawayMessage:String?=nil{willSet{guardletawayMessage=awayMessageelse{return}previousStatuses.append(awayMessage)}}awayMessage="out to lunch, brb"awayMessage="eating dinner"awayMessage="emo song lyrics"awayMessage=nilprint("Previous: ",previousStatuses)
This prints:
Previous: ["out to lunch, brb", "eating dinner", "emo song lyrics"]
This technique might not make sense in many situations, but is a nice feature to know about in case we can ever benefit from it.
Swift Protocols are great. They allow us to express the idea of expecting a set of functionality, without needing to expect a specific concrete type. Today we'll look at creating a Protocol to make working with colors in our apps a bit more flexible. Let's dive in. 🏊
We'll start by creating a new Protocol, giving it a conventional name, and require just one read-only property from our conforming types. This property will always return a UIColor.
Now we can reference colors in our code by name, and get full autocompletion when typing them. Using our new Protocol is super simple, let's make a quick UIViewsubclass to show it off:
It'd be nice if we could still supply "one-off" UIColors as well. No problem, let's simply conform UIColoritself to be ColorConvertible. We can just return self:
lethv=SpaceshipNameHeaderView()// works!hv.nameTextColor=UIColor.black// works just as well!hv.nameTextColor=SpaceshipsColors.aluminum
Swift Protocols can help us write code that is both expressive and quite flexible. They can take a while to get a handle on, but understanding them is a key step towards unlocking the full power of Swift.
Today we'll take our first step towards contributing to the Swift language. We'll learn how to download the codebase, compile it, and run tests. Let's get started!
First, we'll prepare our environment. We'll be doing all of this on a Mac today, but all of this is also possible on Linux (and a bunch of other platforms).
We'll begin by using homebrew to install a couple of build tools:
brew install cmake ninja
Once that's done, we'll make a new directory somewhere sensible where we can put everything:
mkdir -p ~/projects/swift
Next, we'll do the first clone:
git clone git@github.com:apple/swift.git
Once this is done, we'll pull down all the dependencies and other projects involved with building Swift:
./swift/utils/update-checkout --clone-with-ssh
We have now cloned and checked out a lot of code, it's time to get building. We'll head into the swiftdirectory and kick off a standard build, then run basic tests like this:
cd swift
./utils/build-script -r -t
That's it! At this point we can go get a cup of coffee (no really, this is going to take a while, even on a fast machine).
With these skills in hand though, we can now edit code, and see what effects our changes have on tests.
We can run basic tests at anytime with:
./utils/build-script --test
Or run more intensive validation tests like this:
./utils/build-script --validation-test
This is just the beginning, in the future we'll look at fixing a Swift bug, and contributing our work back to the project.
We’ve covered a little about working with C APIs in Swift back in Bite #189. Swift 3 brings us a ton of new goodies and improvements around C APIs, let's dive right in and take a look.
Swift 3 improves upon a number of areas of C API usage in Swift, but the biggest one is importing functions as members. Essentially, taking free floating C functions, renaming them, and shoving them onto a type as functions.
One place where this behavior really shines is when using CoreGraphics to draw into a view.
The first is a bit of automatic inference. Many APIs (like CoreGraphics and CoreFoundation) use consistent (albeit often verbose) naming schemes for their functions. The Swift compiler can now exploit this to (for example) detect functions returning a specific type and convert them into init functions in Swift.
So this C function would come in to Swift 2 like this:
There's tons more of these automatic inferences for things like detecting getter/setter pairs of functions, converting Boolean functions into Bool properties, etc.
Finally, authors of C libraries can customize exactly how their functions are imported into Swift using the new swift_name macro.
Here's an example from the proposal that demonstrates how we can define which type our function gets imported on to.
We've been looking at some the changes coming in Swift 3 recently in Bites #243 and #244. We'll continue our tour today by checking out what is new the world of Swift Enums. Let's dive right in.
We'll start with the obvious, in Swift 2 (and earlier) enums have been PascalCased. In Swift 3, they are camelCase.
In Swift 2, there was some inconsistency about the requirements around when the "dot" in the "dot notation" was required.
In Swift 2, all of this was valid:
enumAdvertisementKind{caseSmall,Medium,Large,GiantfuncdebugDescription()->String{switchself{caseSmall:return"Small Ad"case.Medium:return"Medium Ad"// leading dot}ifself==Large{return"Large Ad"}ifself==.Giant{return"Giant Ad"}}}
Note how enum cases are used interchangeable both with and without dots.
In Swift 3, this has been cleaned up and dots are now required when using this shorthand style to access enum cases. There's one exception and that is static functions on enums, which still infer self:
Watching Swift evolve in these ways is a great way to understand not just the syntactic structural changes being made, but also the reasoning and goals behind them. Those interested should definitely read over the proposal that prompted this change here.