"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.
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).
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:
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:
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:
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).
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).
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.
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:
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.