UITableView & UICollectionView have some fantastic animation capabilities. We can animate items and sections being inserted, removed, moved, etc. We've all been there though, reloadData is sitting right there, and is conceptually so simple. Wouldn't it be great if we could enable those fancy animations in our views, with the conceptually simplicity of reloadData?

Today we'll check out Dwifft, a library from Jack Flintermann that can help us achieve this. Let's dive in.

We'll begin with a regular UITableViewController. (Dwifft works just as well on UICollectionViews).

First we'll need a TableViewDiffCalculator. This is a generic type, so we'll tell it that we're going to be display a bunch of Strings.

class ExampleViewController: UITableViewController {
  var diffCalculator: TableViewDiffCalculator<String>?

Next we'll initialize our data with some random items (just a randomly ordered array of Strings). We'll also use a property observer to let our diff calculator know whenever our items change.

var items: [String] = ExampleViewController.randomItems() {
  didSet { self.diffCalculator?.rows = items }
}

We're almost there. We'll initialize the diff calculator in our view controller's viewDidLoad function:

override func viewDidLoad() {
  super.viewDidLoad()

  tableView.register(UITableViewCell.self, forCellReuseIdentifier: "ItemCell")

  diffCalculator = TableViewDiffCalculator<String>(
    tableView: tableView,
    initialRows: items
  )
}

Lastly, we'll implement a couple of standard UITableViewDataSource functions:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return items.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath)
  cell.textLabel?.text = selfitems[(indexPath as NSIndexPath).row]
  return cell
}

Success! Here's how this ends up looking:

The "Shuffle" button was wired up off-camera, but it simply sets self.items = ExampleViewController.randomItems().

Now we can simply update the diff calculator's rows and we'll get all those beautiful, scroll-position preserving animations automatically.

A couple of final notes, we can customize the individual animation types like this:

diffCalculator?.insertionAnimation = .fade
diffCalculator?.deletionAnimation = .fade

And finally for fun the science behind Dwifft is pretty neat to read about. Read more about the Longest Common Subsequence Problem here.

Learn more about Dwifft at git.io/dwifft

Alerts have been around since iOS was iPhone OS. First in the form of UIAlertView, and now through UIAlertController (Bite #44). The built-in system alerts work great, but sometimes we want something fancier 🎩. Today we'll check out PMAlertController from Paolo Musolino, a library that allows us to easily display great looking alerts in our app. Let's take a look.

PMAlertController is essentially a fancier replacement for the styles that UIKit's UIAlertController already provides.

PMAlertController two styles of alerts: "Alert" and "Walkthrough".

The "Alert" Style is 270 points wide (matching the system's alerts):

The "Walkthrough" Style is almost the full width of the screen and is appropriate for more in-depth alerts/dialogs (such as those for explaining why our app is requesting permission for some of the user's data):

Using PMAlertController is almost just like UIAlertController.

We're given a title, description and image to work with:

let alertVC = PMAlertController(
  title: "Locate your device", 
  description: "Enables access to your...",
  image: UIImage(named: "flag.png"),
  style: .alert
)

alertVC.addAction(PMAlertAction(
  title: "Cancel", style: .cancel,
  action: { in }
))

alertVC.addAction(PMAlertAction(
  title: "Allow", style: .default,
  action: { in }
))

present(alertVC, animated: true, completion: nil)

We instantiate the view controller, add some actions (each with their own closure handler that will be called when the action is tapped), and present it.

Neat!

Libraries like PMAlertController can help us easily add some polish to our apps, but they can also be a great way to learn about creating custom controls in general.

Learn more about PMAlertController at git.io/pmalert

We've looked at protocols in Swift in Bite #232, but we haven't really seen a ton of "real world" examples. Today we'll check out a library called CostumeKit. It's a set of base protocols that can assist us in visually styling or "themeing" our apps. Along the way we'll use some protocols "for real". Let's jump in.

Author's Note: Full disclosure, CostumeKit is written by me. I'm a huge fan of conventions, also it's a nice basic example of Protocols. I use it in all my apps.

To try this out, we'll be working on an imaginary Little Bites of Cocoa app.

We'll start by adding CostumeKit to our project with Carthage:

github "jakemarsh/CostumeKit"

Next, we'll start implementing our types. First up is the colors we'd like to use.

We'll make an enum and paste in all the colors we want to use, giving each a friendly name:

public enum LittleBitesColors : Color, ColorPalette {
  case beige = "FEFAF1"
  case lightBrown = "AB9372"
  case brown = "8C5637"
  case lightGray = "ECECEC"
  case darkGray = "6A6A6A"
  case sponsorRowGray = "F5F5F5"
}

Sharped-eyed-readers will actually recognize this technique from Bite #255 on Creating a ColorConvertible Protocol.

Moving along, LBOC uses the Source Sans Pro font. Let's implement CostumeKit's Font protocol for this. We'll add the font files to our app then implement the protocol:

public struct LittleBitesFont : Font {
  public init(size: FontSize = .textStyle(.body)) {
    self.size = size
  }

  // Font

  public var size: FontSize

  // FontConvertible

  public var FontValue: UIFont {
    return UIFont(name: "SourceSansPro", size: pointSize)!
  }
}

We're almost there, next we need to create the actual Costume our app will wear. For this we'll implement one more protocol. This protocol has no requirements. It serves as more of a convention really.

open class LittleBitesCostume : Costume {
  let spacing = CGFloat(8)

  public func wearRootBackground(_ view: UIView) {
    view.backgroundColor = Color.white.colorValue
  }

  public func wearHeadline(_ label: UILabel) {
    label.font = LittleBitesFont(size: .textStyle(.title1)).fontValue
    label.textColor = contentTextColor().colorValue
  }
  public func contentTextColor() -> Color {
    return LittleBitesColors.darkGray
  }

  public var name: String { return "Default" }
  public var description: String { return "The default costume." }

  public init() { }
}

For good measure, we'll define a night-mode costume as well:

open class LittleBitesNightCostume : LittleBitesCostume {
  public override func contentTextColor() -> Color {
    return Color.white
  }

  override public var name: String { return "Night Mode" }
  override public var description: String { return "For reading in the dark." }
}

That's where CostumeKit stops. We get a super-tiny bit of functionality "for free" (fonts, color parsing), but the idea is that we're merely following a set of conventions to help guide us through writing this code.

Let's finish up by looking at how we might actually use costumes in our app. Everything from this point on would live inside our app's code (and isn't part of CostumeKit).

We'll make a Wardrobe type and static instance to manage all our costumes:

import CostumeKit

public class Wardrobe {
  private(set) var current: LittleBitesCostume

  init(initialCostume: LittleBitesCostume) {
    current = initialCostume
  }

  public func change(costume: LittleBitesCostume, animated: Bool = true) {
    // TODO: In the future, we'll animate this change using UIView transitions and RxSwift.

    current = costume
  }
}

public let wardrobe = Wardrobe(initialCostume: LittleBitesCostume())
public var costume: LittleBitesCostume { return wardrobe.current }

Now in our regular UIKit code, we can use our current costume:

let headlineLabel = UILabel()
headlineLabel.text = "Hello World".
costume.wearHeadline(headlineLabel)

Learn more about CostumeKit at git.io/costumekit

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

#269: Taptic Engine Basics πŸ‘‹

Topics

With the iPhone 7 and iPhone 7 Plus, Apple added a remarkable new piece of hardware to our devices. It's called the Taptic Engine and it is a big change from the old vibration feedback mechanisms we're used to. Today we'll look at how we can use it in our apps. Let's get started.

Haptic feedback on iOS is all about subtlety.

We want to help inform and/or guide the user, not annoy them. For these reasons, Apple has provided a few different classes we can use to generate haptic feedback that matches what we find around the system.

First up, UIImpactFeedbackGenerator. This is great for more prominent user interactions like when a part of our UI "snaps" into place (for example, imagine a drawing app that snaps when you drag over the exact center of the document).

Next, UISelectionFeedbackGenerator. These are perfect for small, subtle changes in selection. We can preview this one by 3D Touching on an app's icon on the home screen (one with a few 3D Touch shortcuts). If we keep our finger down and drag between the menu items, we'll feel the slightest "click" as we change selection. Neat!

Last, we have UINotificationFeedbackGenerator. These are pretty heavy and are meant for things like errors or warnings. (Imagine a login form that uses this generator when the user enters an incorrect password).

Now, lets try this out in code. First we need to instantiate a generator, and call the prepare function on it.

let generator = UISelectionFeedbackGenerator()
generator.prepare()

The prepare function is crucial here. It has to do with the physical hardware inside the device.

Basically, prepare will "wake up" the Taptic Engine hardware and put it into a state where it's ready to generate feedback immediately when we ask it to. iOS is relentless about saving battery and power. The Taptic Engine isn't always using "full" power, and only does so for a few seconds after the prepare function is called. Neat.

Things will still work if we don't call prepare, but they might not perfectly match up with the changes on screen (another crucial part of crafting effective Haptic feedback).

Now all we need to do trigger the actual haptic to "play" is:

generator.selectionChanged()

Each generator has its own function for "playing" the haptic. (i.e. impactOccurred, selectionChanged, and notificationOccurred, respectively).

Finally, let's look at a "complete" example using a gesture recognizer:

var generator : UISelectionFeedbackGenerator? = nil

func panned(_ sender: UIPanGestureRecognizer) {
  switch(sender.state) {

  case .began:
    generator = UISelectionFeedbackGenerator()
    generator?.prepare()
  case .changed:
    if selectionChanged(translationPoint: sender.translation(in: view)) {
      generator?.selectionChanged()

      // we call prepare again right after "playing",
      // to make sure the taptic engine is "ready" if the user
      // moves their finger again quickly.
      generator?.prepare()
    }

  case .cancelled, .ended, .failed: generator = nil

  default: break
  }
}

Success!

Topics

#268: What's New in Tab Bar Customization 🎨

Topics

Tab Bars have been around since the very first iPhone. They are a fantastic way to organize the top level screens in our app, and give users a quick way to get to each. Today we'll check out the latest improvements to how e can customize the look and feel of UITabBars in our app. Let's begin.

First up: Badge Customization.

For years we've had to resort to custom drawing or other methods to change how the badge would appear on a tab bar in our app.

In iOS 10, Apple has added a couple of features to allow us to completely customize how this looks with only a few lines of code.

Let's try this out. First, we'll set a badge value on one of the tabs:

func configureTabBar() {
  guard let tabBarItem = self.tabBarItem else { return }

  tabBarItem.badgeValue = "268"
}

Nice. Now let's customize that badge. All we need is one function and some attributes:

tabBarItem.setBadgeTextAttributes([
  NSFontAttributeName : UIFont(name: "AvenirNextCondensed-Medium", size: 14.0)
], for: .normal)

Neat.

Next, we can now easily change the background color of the badge:

Lastly, we'll customize the tabs themselves using one final new feature, unselectedItemTintColor (Finally):

guard let tabBar = self.tabBarController?.tabBar else { return }

tabBar.tintColor = UIColor(white: 0.1, alpha: 1.0)
tabBar.unselectedItemTintColor = UIColor.lightGray

Success!

Weekly Sponsor: Zendesk πŸ’‘

This week we're welcoming back a fantastic sponsor, 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!

We first looked at Handoff and NSUserActivity all the way back in Bite #29. Today we'll take a look at a new addition to NSUserActivity in iOS 10 that allows our apps to share more about the user's current context with other parts of the system. Let's dive in. 🏊

Let's imagine our app lets users view a list of Robot Stores. We'll start by re-using a technique we first learned about in Bite #47, MKLocalSearch.

First we'll create an MKLocalSearchRequest to find all the Robot Store locations within a reasonable radius of the user's current location (acquired off-camera via standard Core Location mechanisms):

let request = MKLocalSearchRequest()

request.naturalLanguageQuery = "Robot Store"
request.region = MKCoordinateRegionMakeWithDistance(
  usersCurrentLocation, 1600, 1600)

Next, we'll start an MKLocalSearch using our request, and load a bunch of mapItems representing Robot Store locations near us.

If we were building a complete app, this is the part where we'd grab the response.mapItems from above, map their values into some business objects, then display them probably in UITableView, etc.

For now, let's simply try out the new feature of NSUserActivity.

MKLocalSearch(request: request).start { (response, error) in
  guard error == nil else { return }
  guard let response = response else { return }

  guard let exampleMapItem = response.mapItems.first else { return }

  let activity = NSUserActivity(activityType: "com.robots-store.shopping-locally")

  activity.title = exampleMapItem.name
  activity.mapItem = exampleMapItem

  self.userActivity = activity
}

Finally, we can run our app, double tap our home button and see the results:

Success!

We're finishing up our extensive look into the new Notifications improvements in iOS 10 today with Notification Service Extensions. These fill out the notifications functionality by allowing us to intercept remote notifications as they are received on the device, take some action(s), and then modify the notification before it's shown to the user. Let's get started!

We'll begin by looking at the broad concept going on here. Until iOS 10, Remote Notifications were presented to the user as soon as they arrived, without any interaction from their app.

In iOS 10, Apple has provided a mechanism for us to actually "intercept" Remote Notifications when they arrive, do some work, modify the notification's content, then send it along to be displayed to the user.

The whole process now goes like this:

This new step opens up a ton of possibilites and gives us a chance to do things like download media, or perform some other short work to enrich the notification before the user sees it.

Let's try this out.

First, we'll add another new target to our app, and choose a Notification Service Extension:

In the new group that was created we'll find one file, a subclass of UNNotificationServiceExtension.

Notification Service Extensions run in the background, and never show any kind of interface themselves. Instead, they override one function that's called whenever a notification is received.

We're passed in a notification request and a completion closure.

The request is the same UNNotificationRequest type we first learned about in Bite #258, only with its trigger property set to a UNPushNotificationTrigger, neat!

We'll perform whatever work we need, grab the content from the the notification request's, modify its properties, and then send it along in the completion closure:

class NotificationService: UNNotificationServiceExtension {
  override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    guard let asteroidID = request.content.userInfo["asteroid-id"] as? String else { return }
    guard let mutableContent = request.content.mutableCopy() as? UNMutableNotificationContent else { return }

    AsteroidService.details(asteroidID: asteroidID) {
      mutableContent.body
        .append(" Estimated Mass: \($0.massInTons) tons")

      contentHandler(mutableContent)
    }
  }
}

Here we take advantage of the custom "userInfo" dictionary our server adds to the push notification payload. In this case, we're grabbing an asteroidID from it. We also grab a mutable instance of the notification's content so we can modify it later.

Then we load the details for the asteroid, and append a new piece of information to the notification's body, the estimated mass.

Finally, we send the modified content along in the contentHandler.

We don't need it here, but there's one more function we can override, and it's all about expiration.

Unlike some other more generous extension points, Notification Service Extensions are given an extremely short window to perform work and complete by the system.

We can override:

override func serviceExtensionTimeWillExpire() {

}

This will tell us when the system is about to forcibly stop our extension, if this happens, the system will display the notification using whatever push notification payload our server sent through, with no modifications.

In our example above, we lean on this fallback behavior, so we didn't need to override this function.

Finally, we'll push an example notification through using Knuff, (covered in Bite #177), and try this all out:

Success!

Weekly Sponsor: Bugsee 🐞

Thsi week we're welcoming back one of our favorite sponsors, it's Bugsee! Allowing our users to report bugs is a crucial part of building our app. We can release builds through TestFlight, but what about allowing our users to report issues they encounter? Today we'll check out Bugsee's SDK and how it can help us super-charge this process. Let's take a look.

What is Bugsee?

Bugsee is a free SDK. It gives us truly effective debugging. It changes how we receive bug and crash reports from our users. We're going to be using it on iOS, but it works just as well on Android and on the Web!

How Does it Work?

After we integrate Bugsee into our app, our users can simply snap a screenshot to report a bug. (We can also configure different methods of reporting like shaking the device, etc.)

Bug reports include video of the last minute, full events log, console logs and network traffic logs along with all environment details.

For crash reports, Bugsee will include the full video leading up to the crash, including the reason, method, as well as the file and number that caused the crash.

Bugsee's viewer will allow us to review the video synchronized with all system events and logs. We can essentially "play back" a complete recreation of what was going on right before a crash or bug report occurred. Super neat!

Installation

Bugsee is straightforward to install, it's just one line of code (and it passes all App Store checks):

Bugsee.launchWithToken("app_token_goes_here")

Bugsee also integrates with our bug tracking systems, dramtically speeding up QA and debugging round trips, saving hours for both engineering and QA teams alike.

Bugsee doesn't impact the performance of our UI, and doesn't consume network bandwidth (no streaming). The SDK also automatically obscures secure fields in our app. Nice!

Mobile developers have needed a complete solution like Bugsee for quite some time. Signup right now at bugsee.com/jake for a free lifetime account.

A huge thanks to Bugsee for sponsoring this week's Bites!

Page 8 of 38