Topics

#257: Property Observers on Local Variables 🐤

Topics

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.

Traditionally, they look like this:

class Spaceship : NSObject {
  var name: String {
    didSet { print(name) }
  }
}

Nothing too fancy here, just printing the latest value for name, anytime a ship's name changes.

This works great for properties on types, but it turns out we can actually use these same observers on local variables as well:

var name = "Tim Cook" {
  didSet { print(name) }
}

name = "Eddy Cue"
name = "Craig Federighi"

This prints:

Eddy Cue
Craig Federighi

Neat!

Kelan Champagne mentions an interesting technique for super-simple value-change tracking:

var previousStatuses = [String]()
var awayMessage: String? = nil {
  willSet {
    guard let awayMessage = awayMessage else { return }
    previousStatuses.append(awayMessage)
  }
}

awayMessage = "out to lunch, brb"
awayMessage = "eating dinner"
awayMessage = "emo song lyrics"
awayMessage = nil

print("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.

A huge thanks to both Kelan Champagne and Chris Eidhof for pointing this out!

Topics

#256: UIPasteboard Revisited 📋

Topics

UIPasteboard is a wonderfully simple little type with a ton of great functionality inside. (Covered previously in Bite #45). Today we'll check out the latest pasteboard improvements in iOS 10. Let's begin.

The biggest new user-facing feature of pasteboards in iOS 10 and macOS Sierra is the Universal Clipboard. This allows users to copy some content into their clipboard on one device, and retrieve it on another.

We don't need to do anything to get this functionality in our apps. This behavior is on by default for UIPasteboard.general, but we can configure how items behave when we add them.

Expiration Dates

let tomorrow = Date().addingTimeInterval(60 * 60 * 24)

UIPasteboard.general.setItems(
  [[ "URL": URL(string: "https://littlebitesofcocoa.com")! ]],
  options: [.expirationDate : tomorrow]
)

Preventing Universal Clipboard

Sometimes there's going to be data that doesn't make sense to sync using the Universal Clipboard. This is easy.

UIPasteboard.general.setItems(
  [[ "URL": URL(string: "https://littlebitesofcocoa.com")! ]],
  options: [.localOnly : true]
)

has* Properties

Last but not least, UIPasteboard has a few new helper properties for easily checking the current state of a pasteboard:

// we can check for images, URLs,
// strings, and colors this way
if pasteboard.hasURLs {
  print(UIPasteboard.general.urls)
}

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.

public protocol ColorConvertible {
  var colorValue: UIColor { get }
}

Now, let's try this new Protocol out by creating an enum that represents the different colors we use in our app:

public enum SpaceshipsColors : String {
  case white = "FFFFFF"
  case red = "E2574C"
  case gold = "EFC75E"
  case darkTeal = "314D5B"
  case lightTeal = "3CB39E"
  case spaceGray = "233640"
  case aluminum = "6A838D"
  case black = "000000"
}

Looking pretty good, now let's extend our enum to conform to ColorConvertible.

We're using Hue here (Bite #195) to convert our hex color code Strings into UIColors.

extension SpaceshipsColors : ColorConvertible {
  public var colorValue: UIColor {
    return UIColor.hex(rawValue)
  }
}

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 UIView subclass to show it off:

class SpaceshipNameHeaderView : UIView {
  var nameLabel: UILabel
  var nameTextColor: ColorConvertible? {
    didSet {
      nameLabel.textColor = nameTextColor?.colorValue
    }
  }
}

It'd be nice if we could still supply "one-off" UIColors as well. No problem, let's simply conform UIColor itself to be ColorConvertible. We can just return self:

extension UIColor : ColorConvertible {
  public var colorValue: UIColor {
    return self
  }
}

Finally, we can use it like this:

let hv = 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.

Testing network requests can be tricky. Generating mock data, handling HTTP routes, etc. Thing get complicated quickly. Today we'll check out a library from devlucky called Kakapo that can help us tame all this. Let's get started.

One of the best features of Kakapo is how easy it is start using. We can create a new Router for a domain, and start adding intercepted routes like this:

let router = Router.register("http://littlebitesofcocoa.com")

router.get("/bites") { request in
  return ["id" : 1, "title": "#1: View Controller Initialization 🚀"]
}

In the above example, we're returning static data. Let's kick things up a notch and return some dynamic data. This is another place Kakapo really shines:

let db = KakapoDB()
db.create(Bite.self, number: 10)

router.get("/bites") { request in
  return db.findAll(Bite.self)
}

We can use this functionality to not only test network requests, but even to "stub out" responses before fully implementing backend services. Neat!

Once we've wired up some routes, we can make our network requests like normal:

let URL = NSURL(string: "http://littlebitesofcocoa.com/1")!

session.dataTaskWithURL(URL) { (data, _, _) in
  // handle response
}.resume()

We can even wire Kakapo up to Alamofire (Bite #93) like this:

let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.protocolClasses = [KakapoServer.self]
let manager = Manager(configuration: configuration)

This barely scratches the surface of what Kakapo has to offer.

From advanced dynamic mocking and database functionality, great routing, and a squeaky clean API, it's definitely worth a look.

Learn more about Kakapo at git.io/kakapo

Weekly Sponsor: Buddybuild 🤖🛠

We've got one of our favorite sponsors back this week, it's buddybuild! Long time readers will remember that buddybuild is a fantastic mobile continuous integration, delivery, crash reporting and feedback platform, all in one.

It takes just minutes to set up, let's check it out!

We'll head over to buddybuild.com and click Get Started. We'll sign up with Github (we could also use Bitbucket or just an email/password).

Next, buddybuild will let us choose a git repo to create our first project with.

 

Once we select the repo we want to build, buddybuild will get to work building our app for the first time.

After that build is complete, our app is set up on buddybuild - it's that easy.

Now, we'll be able to trigger a new build (and run our tests with code coverage) at any time with a simple git push!

This works with repos on Github, BitBucket, GitLab or any other git server, neat!

 

With buddybuild, we won't need to wait for App Store processing time or reviews before deploying to testers.

Speaking of deployment, buddybuild's email (and Slack!) based deployment service can deploy instantly on every build, every night, or at the push of a button.

Buddybuild's deployment service also handles the process of adding new users (and their devices) by automatically managing UDIDs, iOS provisioning profiles and signing identities. (Fantastic UX for both testers and devs!)

 

Only buddybuild goes even farther, and gives us a fully integrated feedback platform. Once users have installed our app and are testing it out, they can send their feedback (along with important diagnostic details) by simply taking a screenshot.

If our app ever crashes, buddybuild will trace back and highlight the exact line of offending source code that caused the crash, telling us which users were affected, and how many times the crash happened.

Each crash is also recorded with an Instant Replay - to show you exactly what your users were doing in the moments leading up to the crash. Awesome!

Buddybuild is also a great community citizen, and has our backs as iOS developers. For example, they'll give us a heads up on any potential breaking changes in Xcode. Within 48 hours of any Xcode release (including betas!), buddybuild will automatically takes our most recent successful build, and build and run tests against it using the latest version of Xcode. Then it will email us the results. How cool is that?!

Last but certainly not least, buddybuild comes with built-in integrations with tons of services we all know and love such as GitHub, BitBucket, GitLab, Slack, JIRA Pivotal Tracker, Slack, HipChat and more!

Buddybuild is the real deal, give them a try today at buddybuild.com!

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:

let textLabel = UILabel()

Then we'll create our file if it doesn't already exist:

let filename = "label-content.txt"
let middle = FileWatcher.Local.simulatorOwnerUsername()
let path = "/Users/\(middle)/Desktop/\(filename)"

if FileManager.default.fileExists(atPath: path) == false {
  try! "Hello, world."
    .data(using: String.Encoding.utf8)?
    .write(to: NSURL.fileURL(withPath: path))
}

Finally, we'll wire up a watcher to our file's path, and update our label when the file changes:

let watcher = FileWatcher.Local(path: path)

try! watcher?.start {
  switch $0 {
  case .updated(let data):
    textLabel.text = String(
      data: data,
      encoding: String.Encoding.utf8
    )
  default: break
  }
}

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

More info about KZFileWatchers can be found at git.io/filewatchers

Topics

#252: Compiling Swift from Source 🐤🛠

Topics

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 swift directory 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.

Weekly Sponsor: Buddybuild 🤖🛠

We've got one of our favorite sponsors back this week, it's buddybuild! Long time readers will remember that buddybuild is a fantastic mobile continuous integration, delivery, crash reporting and feedback platform, all in one.

It takes just minutes to set up, let's check it out!

We'll head over to buddybuild.com and click Get Started. We'll sign up with Github (we could also use Bitbucket or just an email/password).

Next, buddybuild will let us choose a git repo to create our first project with.

 

Once we select the repo we want to build, buddybuild will get to work building our app for the first time.

After that build is complete, our app is set up on buddybuild - it's that easy.

Now, we'll be able to trigger a new build (and run our tests with code coverage) at any time with a simple git push!

This works with repos on Github, BitBucket, GitLab or any other git server, neat!

 

With buddybuild, we won't need to wait for App Store processing time or reviews before deploying to testers.

Speaking of deployment, buddybuild's email (and Slack!) based deployment service can deploy instantly on every build, every night, or at the push of a button.

Buddybuild's deployment service also handles the process of adding new users (and their devices) by automatically managing UDIDs, iOS provisioning profiles and signing identities. (Fantastic UX for both testers and devs!)

 

Only buddybuild goes even farther, and gives us a fully integrated feedback platform. Once users have installed our app and are testing it out, they can send their feedback (along with important diagnostic details) by simply taking a screenshot.

If our app ever crashes, buddybuild will trace back and highlight the exact line of offending source code that caused the crash, telling us which users were affected, and how many times the crash happened.

Each crash is also recorded with an Instant Replay - to show you exactly what your users were doing in the moments leading up to the crash. Awesome!

Buddybuild is also a great community citizen, and has our backs as iOS developers. For example, they'll give us a heads up on any potential breaking changes in Xcode. Within 48 hours of any Xcode release (including betas!), buddybuild will automatically takes our most recent successful build, and build and run tests against it using the latest version of Xcode. Then it will email us the results. How cool is that?!

Last but certainly not least, buddybuild comes with built-in integrations with tons of services we all know and love such as GitHub, BitBucket, GitLab, Slack, JIRA Pivotal Tracker, Slack, HipChat and more!

Buddybuild is the real deal, give them a try today at buddybuild.com!

When using UIImageViews, sometimes the built-in content modes can cramp our style.

Many times, we're displaying photos of people. In these cases, it'd be great if the image view could somehow be told to intelligently crop the photo around the person's face.

Today we'll check out a library from Beau Nouvelle called AspectFillFaceAware. It's super simple, let's take a look.

AspectFillFaceAware is essentially just an extension on UIImageView. It provides two ways to configure an image view to be "face aware".

The first is in Interface Builder, we can enable the feature by flipping on the feature in the Inspector. (Not seeing the option? Run your project once, then it should appear).

Here's the how it looks:

We can also enable the functionality in code by setting the image view's image using this new function:

imageView.set(image: avatar, focusOnFaces: true)

We can even throw a quick corner radius on the image view's layer to try out the "face aware" functionality on a circular view. (i.e. user avatars):

let radius = imageView.bounds.size.width / 2.0
imageView.layer.cornerRadius = radius

Under the hood, the library is using a low accuracy CIDetector with a type of CIDetectorTypeFace to handle the actual face detection. Want to dive deeper here? We covered CIDetectors way back in Bite #87.

More info about AsyncFillFaceAware can be found at git.io/faceaware

Topics

#250: Improvements to C APIs in Swift 3 🐤

Topics

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.

Here's some basic drawing code in Swift 2:

guard let context: CGContext = UIGraphicsGetCurrentContext() else { return }

CGContextSetStrokeColorWithColor(context, UIColor.redColor().CGColor)
CGContextSetLineWidth(context, 2)
CGContextDrawPath(context, .Stroke)

In Swift 3, the code to do the same would be:

guard let context: CGContext = UIGraphicsGetCurrentContext() else { return }

context.strokeColor = UIColor.red().cgColor
context.lineWidth = 2
context.drawPath(mode: .Stroke)

The mechanisms powering this conversion under the hood are pretty neat. The full (now implemented) proposal can be found here, but here's a quick overview:

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:

func CGColorCreate(space: CGColorSpace?, _ components: UnsafePointer<CGFloat>) -> CGColor?

But in Swift 3 it's an init function:

extension CGColor {
  init?(space: CGColorSpace?, components: UnsafePointer<CGFloat>)
}

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.

struct Point3D rotatePoint3D(Point3D point, float radians)
__attribute__((swift_name("Point3D.rotate(self:radians:)")));

The string parameter we pass to swift_name has a bunch of little extra syntax to import things as inits, regular functions, getters/setters, etc.

As always when talking about Swift improvements, it's a good idea to check out the full proposal for more info.

Page 10 of 38