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, GitLab, or sign up with email / password to for repos on any other git server). Neat!

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!

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

 

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's 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!

Topics

#285: Generating Random Data with RandomKit 🎲

Topics

Whether we need sample values while prototyping our app's interface, or some multipliers for our game's logic, random data can be incredibly useful when programming. Today we'll check out a great library from Nikolai Vazquez called RandomKit that makes generating random data both simple and intuitive. Let's begin.

RandomKit is built on a set of Swift Protocols. We're provided Random, RandomWithinRange, RandomToValue, and many more. RandomKit then extends a bunch of types to conform to these protocols.

(This is great because it means we can easily add RandomKit-style functionality to our own types, if we ever need to. Neat.)

Let's try out the basics. Some of the most common things to generate randomly are numbers and booleans. RandomKit has us well covered here, supporting all the major numerical Foundation types (and more):

Double.random() // 0.6472946529383645
Int.random(within: 1...10) // 7
NSNumber.random(within: -5...5)  // -3
CGFloat.random(0...1)  // 0.27969591675319
Bool.random() // false

Neat. Next up, Strings:

String.random(ofLength: 5) // "$-=5t"
Character.random() // "#"

We can also easily grab a random element from any Swift Sequence or Collection:

"Little Bites of Cocoa".characters.random // "o"
["Han", "Luke", "Chewie"].random // "Luke"
NSDictionary(dictionary: [ "firstName": "Han", "lastName": "Solo" ]).random // ("lastName", "Solo")

But wait, there's more! RandomKit is incredibly comprehensive. Let's try some more advanced functionality like Dates:

Date.random(within: Date.distantPast...Date())  // "Feb 7, 472, 5:40 AM"

URLs:

URL.random() // https://stackoverflow.com

...or even UIColors and NSColors:

UIColor.random(alpha: false)  

Last but not least, we can even grab random values for CoreGraphics types:

CGPoint.random(within: 0...50, 0...50) // {x 23.284 y 45.302 }
CGSize.random(within: 0...50, 0...50) // {w 29.475 h 12.863 }
CGRect.random() // {x 3.872  y 46.15  w 8.852  h 20.201}

These examples were just a (sorry) random-sampling of RandomKit's functionality. It has a ton more to offer.

Learn more about RandomKit at git.io/randomkit

Topics

#284: More Xcode Source Editor Extensions 🤖🛠

Topics

Today we'll dive back into the world of Xcode Source Editor Extensions (Bite #239). These extensions can not only help us save time and effort, they're also a great way to customize Xcode to our exact needs. Let's dive in! 🏊

First up, Cleaner Closures. We can use CleanClosureXcode from Patrick Balestra to clean up all those unnecessary ('s and )'s from our Swift closure definitions:

Beautiful. No more manually arrow-key-ing around to get rid of those.

Next, let's look at a common feature of many IDEs and text editors: the ability to "jump" the cursor multiple lines up or down. Xcode can't really do that... until now. Thanks to Jump, we're given a few new menu items in our Editor menu to move the cursor up or down by 2 or 5 lines. Neat.

Pro Tip: We can use the Key Bindings tab of Xcode's Preferences window to customize the keyboard shortcuts for each of these movement commands (or any other commands).

Finally, let's check out QuickAdd from Sidney de Koning.

This extension allows us to select some text, and then insert a new function definition into our code, complete with documentation comment, and placeholders we can press tab to jump between.

This allows us to employ a workflow of:

1.) Call a function that doesn't yet exist when writing some code as a sort of "placeholder".
2.) When we're done with that chunk of work, select the name portion of the function call and press a keyboard shortcut to "generate" the function and insert it into our file.
3.) Profit!

Full installation instructions are here.

Know of another neat Source Editor Extension? Send it along!

Weekly Sponsor: Zendesk 💡

We're welcoming back one of our favorite sponsors again this week, it's Zendesk!

None of us have ever built a flawless app.

Chances are, no matter how good we think our app's user experience is, there's probably something users will need help with while they're in our app.

Yet in many apps, getting help is reduced to a "contact us" button that launches an compose email screen, or a just drops the user on a web page.

By forcing folks out of the app to get help, small problems can turn into big annoyances, and those will almost certainly turn into negative App Store reviews and poor ratings.

With Zendesk's Mobile SDKs, we can bring native, in-app support functionality to our apps quickly and easily.

Our users can view help and support content, and even submit tickets to support without ever leaving our app. Neat!

Tickets go into Zendesk and can include technical details about the user and their device, history with our apps, and more.

Best of all, it's included with Zendesk at no extra charge.

We can use Zendesk's "out-of-the-box" iOS UI components to get up and running quickly, or we can build your own UI with SDK API Providers.

A huge thanks to Zendesk for sponsoring!

Topics

#283: Generating Models from JSON with json2swift ⚒

Topics

Integrating our apps with HTTP APIs often involves a fair amount of "busy work". Writing models to match API responses, manually iterating each field in a JSON object, and typing each in as a Swift property can be a bummer. Today we'll check out a great new tool from Josh Smith called json2swift that can help us here. It can generate Swift model code from a JSON object. Let's give it a try.

After we've installed json2swift, we can run it like this:

json2swift Spaceship.json

This will create a new file called Spaceship.swift in the same directory as our .json.

This means if our Spaceship.json file looked like this:

{
  "name": "Tantive IV",
  "topSpeed": 950
}

The resulting json2swift-generated Swift model would look like this:

struct RootType: CreatableFromJSON {
  let name: String
  let topSpeed: Int
}

Neat!

json2swift has generated an immutable Swift struct from our JSON file. Pro Tip: We can also run this on a directory full of JSON files, and it will process all of them.

json2swift will even try to determine which properties should be optional, and which are required. It will then generate the appropriate init code.

We're even provided some special handling for things like Date parsing. If we put a special String like this in our original JSON:

{
  "name" : "Tantive IV".
  "buildDate" : "DATE_FORMAT=yyyy-MM-dd"
}

This will give us a:

let buildDate: Date

property, as well as generate the appropriate Date format/parsing code needed to make it work. Neat!

We've only scratched the surface, json2swift has great support for intelligenty inferring types for things like numbers, and even URLs. Learn more about json2swift at git.io/json2swift.

Properly responding integrating with the software keyboard is a big part of building a solid iOS app. Traditionally, this would involve observing NSNotificationCenter notifications and some pretty lengthy boilerplate code. Today we'll look at a great little library from Toto Tvalavadze called Typist that can improve all this. Let's check it out.

Typist works by exposing a way for us to easily "opt-in" to certain keyboard events:

Typist.shared
  .on(event: .didShow) { (options) in
    // TODO
  }
  .start()
}

Yep. That's it. Gone are the wildly verbose notifications and complicated setup.

(Note: We're not required to use Typist's singleton instance, but it's provided for convenience).

Typist provides this same interface for a bunch of useful events:

Typist.shared
  .on(event: .willShow) { _ in /* TODO */ }
  .on(event: .didShow) { _ in /* TODO */ }
  .on(event: .willHide) { _ in /* TODO */ }
  .on(event: .didHide) { _ in /* TODO */ }
  .on(event: .willChangeFrame) { _ in /* TODO */ }
  .on(event: .didChangeFrame) { _ in /* TODO */ }
  .start()
}

Finally, inside each event closure, we're given a Typist.KeyboardOptions type that contains strongly-typed properties describing the event:

Typist.shared
  .on(event: .willShow) { (options) in
    guard options.belongsToCurrentApp else { return }

    updateLayout(for: options.endFrame)
  }
  .on(event: .willHide) { (options) in
    guard options.belongsToCurrentApp else { return }

    updateLayout(for: options.endFrame)
  }
  .start()
}

Here we're guard-ing to make sure the keyboard belongs to our app, and returning early if not.

Then, we use the endFrame property to adjust our own views to accomodate the keyboard.

Neat!

Typist's options type also provides startFrame, animationCurve, and animationDuration properties to help us match our app's animation to the keyboard's.

Learn more about Typist at http://git.io/typist

Topics

#281: Touch Bar Basics 🚥

Topics

During yesterday's MacBook Pro event, Apple announced a fantastic new piece of hardware called the Touch Bar. It's a 1085 x 30 point matte-finish Retina screen that sits above the keyboard on the new MacBook Pros. It can dynamically alter its controls and content as we work in each app on the main screen.

Today we'll look at the basics of adding Touch Bar support to a macOS app. Let's dive in.

First some pre-requisites. You'll need the latest version of Xcode (8.1 at publish time), and the latest version of macOS. Even for those running 10.12.1, this updated 10.12.1 build still needs to be installed to be able to test Touch Bars using Xcode.

After we've made sure we've got the latest versions of everything, we can launch Xcode 8.1 and select Window > Show Touch Bar to see a simulated Touch Bar on screen while we're work on our Mac.

It even floats above all other windows to make it feel a tiny bit more realistic. Neat!

Whew! With that out of the way, we can fire up Xcode and create a new macOS app.

We'll make a new WindowController for our app's initial window, and set its class in Interface Builder.

// WindowController.swift
class WindowController: NSWindowController {
  override func windowDidLoad() {
    super.windowDidLoad()

  }
}

We'll add an extension to WindowController to adopt the NSTouchBarDelegate protocol. We'll also add an @available statement to make sure it's only available on the latest macOS:

@available(OSX 10.12.1, *)
extension WindowController : NSTouchBarDelegate {
  override func makeTouchBar() -> NSTouchBar? {
    // TODO
  }

  func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
    // TODO
  }
}

Next, we need to create our Touch Bar. We'll do this with an instance of NSTouchBar.

override func makeTouchBar() -> NSTouchBar? {
  let touchBar = NSTouchBar()
  touchBar.delegate = self
  return touchBar
}

We'll use another extension to add some identifiers for the Touch Bar Items we want to appear to NSTouchBarItemIdentifier:

extension NSTouchBarItemIdentifier {
  static let launch = NSTouchBarItemIdentifier("com.magnus.spaceships.launch")
  static let hyperspace = NSTouchBarItemIdentifier("com.magnus.spaceships.hyperspace")
}

Then, we'll set them in the order we want on our touchBar's defaultItemIdentifiers property:

touchBar.defaultItemIdentifiers = [.launch, .hyperspace]

Then, we'll implement the sort of "main" function of NSTouchBarDelegate (The "cell for item at index path" of Touch Bars so-to-speak).

We'll switch on our identifiers from earlier, and return an NSTouchBarItem for each one:

func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
  switch identifier {

  case NSTouchBarItemIdentifier.launch:
    let item = NSCustomTouchBarItem(identifier: identifier)
    item.view = NSButton(title: "🚀 Launch", target: self, action: #selector(launch))
    return item

  case NSTouchBarItemIdentifier.hyperspace:
    let item = NSCustomTouchBarItem(identifier: identifier)
    item.view = NSButton(title: "😱 Hyperspace", target: self, action: #selector(hyperspace))
    return item

  default: return nil
  }
}

Success!

Last but not least, we can allow our Touch Bar items to be customized by assigning it a customizationIdentifier:

touchBar.customizationIdentifier = NSTouchBarCustomizationIdentifier("com.magnus.spaceships")
if #available(OSX 10.12.1, *) {
  if ((NSClassFromString("NSTouchBar")) != nil) {
    NSApplication.shared().isAutomaticCustomizeTouchBarMenuItemEnabled = true
  }
}

We'll also need to define which items can be customized:

touchBar.customizationAllowedItemIdentifiers = [.launch, .hyperspace]

We can test out customization by selecting View > Customize Touch Bar... from our app's menu.

(Pro Tip: Hold Option to change the menu item to Customize Control Strip... instead, which allows us to customize the persistent NSTouchBarItems that are always available on the right side of the Touch Bar). Neat!

Weekly Sponsor: Zendesk 💡

We're welcoming back one of our favorite sponsors again this week, it's Zendesk!

None of us have ever built a flawless app.

Chances are, no matter how good we think our app's user experience is, there's probably something users will need help with while they're in our app.

Yet in many apps, getting help is reduced to a "contact us" button that launches an compose email screen, or a just drops the user on a web page.

By forcing folks out of the app to get help, small problems can turn into big annoyances, and those will almost certainly turn into negative App Store reviews and poor ratings.

With Zendesk's Mobile SDKs, we can bring native, in-app support functionality to our apps quickly and easily.

Our users can view help and support content, and even submit tickets to support without ever leaving our app. Neat!

Tickets go into Zendesk and can include technical details about the user and their device, history with our apps, and more.

Best of all, it's included with Zendesk at no extra charge.

We can use Zendesk's "out-of-the-box" iOS UI components to get up and running quickly, or we can build your own UI with SDK API Providers.

A huge thanks to Zendesk for sponsoring!

Text is a huge part of iOS apps. Today we'll look at one way we can polish up the text in our apps, by allowing it to naturally "flow" around objects. Let's begin.

We'll start with a "before" picture. Here we've got a basic setup with some text and an image in the top right corner. Nothing too fancy here, just a regular UITextView and UIImageView:

let imageView = UIImageView(image: UIImage(named: "steve"))

let textView = UITextView(frame: .zero)  
textView.text = "Here's to the crazy ones..."

This looks fine. But our text is needlessly narrow. It'd be great if we could make the text expand to the full width of the device. We'll change our Text View's layout so it does:

Well, we're getting closer. Now we just need a way to make the text "flow" or "wrap" around our image. We'll start by getting the frame of the image view, then we'll create a path from it:

let imagePath = UIBezierPath(rect: imageView.frame)

Finally, we'll set our new path as one of the exclusionPaths of our Text View's textContainer:

textView.textContainer.exclusionPaths = [imagePath]

Success! Since exclusionPaths is an Array, we can flow text around as many shapes as we want!

Validating data is usually one of the "chores" we encounter while building apps. It can be annoying and boring code to write, and can be a huge source of bugs. Today we'll check out a great library called FormValidatorSwift that can help us avoid all these issues. Let's take a look.

The core of FormValidatorSwift is built around checking if a String passes some sort of test to be deemed 'valid'.

We'll use individual Conditions or combined sets of Conditions called Validators. We'll attach these to controls like Text Fields and Text Views to allow them to be validated.

Let's try out the included ValidatorTextField to validate an email address:

let field = ValidatorTextField(validator: EmailValidator())

Then, we'll configure it to allow the user to begin entering an email address that's invalid like "hello@", and specify that it should only be validated when it loses focus.

field.shouldAllowViolation = true
field.validateOnFocusLossOnly = true

This sets us up perfectly for our final steps: Displaying the validation info.

First, we'll set the validatorDelegate:

field.validatorDelegate = self

Then, we'll implement one function to update the visual state of our field when the validation state changes:

func validatorControl(validatorControl: ValidatorControl, changedValidState validState: Bool) {
  guard let view = validatorControl as? UIView else { return }

  if validState {
    view.backgroundColor = UIColor.white
    errorLabel.hidden = true
  } else {
    view.backgroundColor = UIColor.red
    errorLabel.hidden = false
  }
}

And finally, we'll add one more function to display the actual reason that validation failed (if it failed):

func validatorControl(validatorControl: ValidatorControl, violatedConditions conditions: [Condition]) {
  var errorText = ""

  for condition in conditions {
    errorText += condition.localizedViolationString
  }

  errorLabel.text = errorText
  errorLabel.hidden = false
}

Last but not certainly not least, we can group our fields into a provided Form type, then ask if the whole thing is valid:

var form = ControlForm()

form.addEntry(field)
form.addEntry(anotherField)

print(form.isValid) // true

Learn more about FormValidatorSwift at git.io/formvalidator

Page 1 of 33