In Bite #294 we learned about annotations in Sourcery. They're a great way to make extra metadata available on our types when accessing them in Sourcery's .stencil templates.
Today we'll take a look at one of the most powerful features of annotations in Sourcery, Key/Value Annotations.
We'll explore these by building a fictional API client powered by a simple Swift Enum. Let's begin!
First, we'll need to create our enum. Then, we'll define some cases to represent each HTTP endpoint we want to be able to call.
For us, these are a classic set of "get all", "get one", "create", "change", and "delete":
Long-time readers may recognize this enum technique from Bites such as #93 or #150.
Here's where the magic begins though. 🎩
SourceryKey/Value Annotations are similar to regular Annotations. Instead of a simple tag though, they allow us to annotate types, enums, enum cases, etc with a set of names and a values.
Key/Value Annotations work everywhere that regular Annotations do. We can annotate types, enums, enum cases, and more.
Ok, back to our API client. Now that we know we can add simple metadata like this above things in our code, let's use this technique to describe each API endpoint.
Nice! Normally, we might put things like method and path into essentially a big switch statement switching on self, and returning the correct method or path.
With SourceryKey/Value Annotations though, we can let Sourcerygenerate all of these statements for us.
Let's create a SpaceshipsAPI+Properties.stencil template that extends our enum, generating the computed properties:
"Why not just use the AttributedString API that ships with Foundation?"
This is a fair question.
Imagine building an app that had many different attributed string styles. With Attributed, we're given a strongly API, allowing us to omit a couple of types. We're also able to omit the long, verbose key names.
Finally, (and perhaps most importantly) we're using a sort of "closure composition" technique.
This involves accepting a Swift closure in an initializer, giving us the anonymous argument $0 to play with. Then, we can chain function calls on to $0 to add attributes.
All of this leads to a dramatic decrease in manual typing (even with autocomplete in Xcode, writing tons of attribute collections stops being fun, quickly).
It also allows us to work more effeciently, and accurately. We can lean on our syntactic shorthand and trust in the Swift's type system to get us to the finish line.
Inheritance
Before we go, let's talk about one of the most common hiccups we can run into when writing AttributedString-related code. Inheritance.
We've all been there. We're composing an AttributedString to go into a UILabel. We're pumped because we've got the style neatly translated from the original design into code. Then we see it. The design calls for one of the words in the label to be a different color, for emphasis.
Dun, dun dun.
No worries though, with Attributed we can solve this problem quickly:
letbase=Attributes().font(UIFont(name:"AvenirNext",size:18.0)!)lethighlighted=base.foreground(color:.red)"Han Solo is the captain of the ".attributed(with:base)+"Millennium Falcon".attributed(with:highlighted)
Very cool. We're able to define a base set of attributes in Attributes(), then compose new sets that inherit all the attributes of our base set.
Then, we're leaning on Attributed's extension to String and String's support of the + operator to write some super clean code.
Pro Tip: This functionality also allows us to easily build up a re-usable set of Attributes(), keeping them in one spot, then sprinkling them throughout our code.
In Bite #287, we built a small extension to Date to allow us to format relative date strings like "10 minutes ago". We used a bunch of if statements and built our final String. This was a nice way to learn about working with DateComponents, but it turns out 🛎Foundation actually provides a class to do something similar, and it's much more powerful.
Today we'll continue our look at Foundation's date and time-related functionality with DateComponentsFormatter. Let's dive in.
Much like its cousin DateFormatter (Bite #286), DateComponentsFormatter is all about converting a TimeInterval or DateComponents into a nicely formatted, human-readable String. (Note: It doesn't support parsing Strings yet. Conversions are a one way trip for now).
In Bite #286, we looked at the basics of using DateFormatters to transform Dates into Strings.
Today we'll explore another common use case of Dates in our apps: "relative" date strings.
Think "2 days ago", "10 minutes ago", "Yesterday", and "Just now". Representing timestamps this way has become common, and it can help our users more quickly understand how old a piece of content is.
Let's add this functionality to our app by extending Date to give it a new computed property to do this.
We'll begin by adding a new File to our Xcode Project. We'll call it Date+Relative.swift. (The Type+SomeExtensionName convention is borrowed from Objective-C.
Nice. Next, we'll need a way to calculate how much time is in between now, and self (from the extended Date's point of view). We could do a bunch of ugly math, but we've got a better way!
This is a fantastic use case for Foundation's DateComponents type. It has a function that can calculate the difference between two Dates, and provide it to us in neatly (pre-calculated) components like days, months, hours, seconds, etc.
We ask the current Calendar to please calculate the quantity of each of the components we passed in for the time between the Date we passed in as the from argument to the Date we passed into the to argument.
Beautiful. Now, the returned DateComponents type has a bunch of properties we'll use to build our relatively formatted date `String.
Finally, we'll inspect each date component (starting with most broad at the top) and return a nicely formatted String:
Doing things manually like this allows us to completely customize and control exactly how this relative date strings appear in our app.
This is fairly straightforward. We get a little fancy for the "days" component, allowing for "yesterday". We could also easily add support for future dates (i.e. "tomorrow") here as well.
All that's left to do is test it out. We can do that easily by constructing some Dates in the past, then printing our new property:
Formatting dates and times is one of those common tasks we all have to do in almost every app. Today we'll take a look at how to use Foundation's solution for this: DateFormatter.
DateFormatters are incredibly powerful. Their core purpose is transforming Dates into Strings and Strings into Dates. They handle things like localization for us under the hood. Let's try one out.
We'll create a new formatter:
letformatter=DateFormatter()
Then we'll need to set a "format" on it. This is a string of characters that represent the date we're going to try to parse or render. Often these appear as one or more repeated series of letters like:
formatter.dateFormat="MMM yyyy"
To see a rendered date string using this format, we can ask for one like this:
formatter.string(from:Date())// "Jan 2017"
We can play around with the format for different results:
The first is that DateFormatters have historically been heavy-weight objects to create. Performance has definitely improved over the years, but if we can, it's probably a good idea to keep one around instead of creating on the fly each time we need it. (For example we wouldn't want to be creating a new DateFormatter inside a UITableView or UICollectionView"cell for row" style delegate function).
The second is that these format strings are opaque and hard to understand at a glance. To solve this, friend of the show (and creator of the awesome NSScreencasts) Ben Scheirman has created a site called nsdateformatter.com.
There we can not only find easy, glance-able examples and references for all the different date format tokens, but we can also test out formats right there in the browser! Super handy.
We've only scratched the surface of what Foundation is capable of when it comes to Dates. Tune in tomorrow to learn more.
Today we're going to continue our look at the new Measurement functionality in Foundation. (Bite #276). MeasurementFormatter offers some great ways to format and print Strings of our Measurements. Let's take a look.
First we'll make define some units to work with. One of the most common ones we'll use in our apps is Length (for example when displaying how "far away" something is).
We'll leave the locale setting alone, which causes it to use the current locale of the device. In our case, en_US.
Finally, we'll print the formatted String.
print(formatter.string(from:distance))
Nice. Now we can start to try out some of the options. Let's start with the *style, we have three choices:
formatter.unitStyle=.short// "0.87mi"formatter.unitStyle=.medium// (the default) "0.87 mi"formatter.unitStyle=.long// "0.87 miles"
We'll use the .long style.
By default, our device locale being en_US is causing the distance to formatted with "miles" instead of "kilometers" (our input value). We can change this with unitOptions:
We can even drop down and configure the NumberFormatter (Bite #182) that the MeasurementFormatter uses when formatting the numerical part of our Measurements:
MeasurementFormatter is just another in a long list of wonderful little gems tucked away inside Foundation. Know of another that should be covered here? Send it along!.
Apple added something pretty neat to Foundation this year: Measurements. We can represent units, convert them to other units, compare them, and even convert them into formatted strings. Let's dive in.
Last but certainly not least, we can nicely format a measurement into a String. Apple provides a MeasurementFormatter type that is fully locale-aware. This allows us to easily display Measurements in our apps in whichever ways our users expect.
Foundation is such a powerhouse of functionality, it can be easy to miss great new additions like this. Next time, we'll learn more about formatting Measurements for display in our apps.
Attributed Strings are a fantastic way to work with rich, styled-text. (We've covered them in Bites #143 & #144 for the curious.) We've even looked at ways to improve upon how verbose the FoundationNSAttributedString API can be.
Today we'll check out another approach using a library from Eddie Kaiger called SwiftyAttributes. It dramatically improves how we work with Attributed Strings. Let's dive in.
Let's start by looking at how things work in vanilla Foundation:
Things get even nicer when we need to concatenate two NSAttributedStrings together. We've all had to do this before and it could have involved applying attributes to specific ranges, etc. Now, we can use a much more declarative and readable syntax:
letsomeString="We can easily ".withFont(.systemFont(ofSize:16))+"underline".withFont(.systemFont(ofSize:16)).withUnderlineStyle(.styleSingle)+" certain words.".withFont(.systemFont(ofSize:16))
Under the hood, SwiftyAttributes takes a pragmatic approach by extending both Swift's String type as well as NSAttributedString with a fairly comprehensive set of delicious, sugary functions:
Today we'll look at a new library from Krzysztof Zabłocki called KZFileWatchers. It allows us to easily monitor local and remote files, running code when changes occur. Let's dive in.
Watching files for changes is a common technique used when creating "live" updating interfaces.
This is a popular technique among web developers. For example, it's now common to use a tool to "live reload" a web page whenever a CSS file is changed.
Live updating techniques can dramatically change how we work, allowing us to try out changes more rapidly.
KZFileWatchers helps us build this type of functionality by providing two simple tools:
FileWatcher.Local is for observing local file changes. (It can even cross sandbox boundaries for debug simulator builds, think "config files on the desktop").
FileWatcher.Remote is for observe remote file changes. We can place a file on a server somewhere, and KZFileWatchers will use the Etag headers and Last-Modified-Date. This even works with Dropbox. Neat.
Let's use a FileWatcher.Local to make a live updating label.
First we'll create a label:
lettextLabel=UILabel()
Then we'll create our file if it doesn't already exist:
We can use this technique to drive all sorts of systems. With this we can easily enable/disable features, change content of labels in our UI, or build a full fledged theming system that updates in "real time".
We've covered GCD a few times back in Bites #85 & #35. This year, GCD is getting a huge overhaul that brings it into the modern world of Swift. Today we'll take a look at what's changed, let's dive in.
TLDR: Grand Central Dispatch now has a richly typedSwift 3 API.
Here we create a new queue, and enqueue some image processing work on it. Then, we do the same thing, but back on the main queue, enqueueing the work of setting resized image on our imageView there.
That's just the beginning. Here's a few more examples of how to use the new "swifty" GCD: