Geofencing in iOS with Core Location Swift 3

Setting Up a Location Manager and Permissions

Before any geofence monitoring can happen, though, you need to set up a Location Manager instance and request the appropriate permissions.

Open GeotificationsViewController.swift and declare a constant instance of a CLLocationManager near the top of the class, as shown below:

class GeotificationsViewController: UIViewController {

  @IBOutlet weak var mapView: MKMapView!

  var geotifications = [Geotification]()
  let locationManager = CLLocationManager() // Add this statement

  ...
}

Next, replace viewDidLoad() with the following code:

override func viewDidLoad() {
  super.viewDidLoad()
  // 1
  locationManager.delegate = self
  // 2
  locationManager.requestAlwaysAuthorization()
  // 3
  loadAllGeotifications()
}

Let’s run through this method step by step:

  1. You set the view controller as the delegate of the locationManager instance so that the view controller can receive the relevant delegate method calls.
  2. You make a call to requestAlwaysAuthorization(), which invokes a prompt to the user requesting for Always authorization to use location services. Apps with geofencing capabilities need Always authorization, due to the need to monitor geofences even when the app isn’t running. Info.plist has already been setup with a message to show the user when requesting the user’s location under the key NSLocationAlwaysUsageDescription.
  3. You call loadAllGeotifications(), which deserializes the list of geotifications previously saved to NSUserDefaults and loads them into a local geotifications array. The method also loads the geotifications as annotations on the map view.

When the app prompts the user for authorization, it will show NSLocationAlwaysUsageDescription, a user-friendly explanation of why the app requires access to the user’s location. This key is mandatory when you request authorization for location services. If it’s missing, the system will ignore the request and prevent location services from starting altogether.

Build and run the project, and you’ll see a user prompt with the aforementioned description that’s been set:

You’ve set up your app to request the required permission. Great! Click or tap Allow to ensure the location manager will receive delegate callbacks at the appropriate times.

Before you proceed to implement the geofencing, there’s a small issue you have to resolve: the user’s current location isn’t showing up on the map view! This feature is disabled, and as a result, the zoom button on the top-left of the navigation bar doesn’t work.

Fortunately, the fix is not difficult — you’ll simply enable the current location only after the app is authorized.

In GeotificationsViewController.swift, add the following delegate method to the CLLocationManagerDelegate extension:

extension GeotificationsViewController: CLLocationManagerDelegate {
  func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    mapView.showsUserLocation = (status == .authorizedAlways)
  }
}

The location manager calls locationManager(_:didChangeAuthorizationStatus:) whenever the authorization status changes. If the user has already granted the app permission to use Location Services, this method will be called by the location manager after you’ve initialized the location manager and set its delegate.

That makes this method an ideal place to check if the app is authorized. If it is, you enable the map view to show the user’s current location.

Build and run the app. If you’re running it on a device, you’ll see the location marker appear on the main map view. If you’re running on the simulator, click Debug\Location\Apple in the menu to see the location marker:

In addition, the zoom button on the navigation bar now works. :]

Registering Your Geofences

With the location manager properly configured, the next order of business is to allow your app to register user geofences for monitoring.

In your app, the user geofence information is stored within your custom Geotification model. However, Core Location requires each geofence to be represented as a CLCircularRegion instance before it can be registered for monitoring. To handle this requirement, you’ll create a helper method that returns a CLCircularRegion from a given Geotification object.

Open GeotificationsViewController.swift and add the following method to the main body:

func region(withGeotification geotification: Geotification) -> CLCircularRegion {
  // 1
  let region = CLCircularRegion(center: geotification.coordinate, radius: geotification.radius, identifier: geotification.identifier)
  // 2
  region.notifyOnEntry = (geotification.eventType == .onEntry)
  region.notifyOnExit = !region.notifyOnEntry
  return region
}

Here’s what the above method does:

  1. You initialize a CLCircularRegion with the location of the geofence, the radius of the geofence and an identifier that allows iOS to distinguish between the registered geofences of a given app. The initialization is rather straightforward, as the Geotification model already contains the required properties.
  2. The CLCircularRegion instance also has two Boolean properties, notifyOnEntry and notifyOnExit. These flags specify whether geofence events will be triggered when the device enters and leaves the defined geofence, respectively. Since you’re designing your app to allow only one notification type per geofence, you set one of the flags to true while you set the other to false, based on the enum value stored in the Geotification object.

Next, you need a method to start monitoring a given geotification whenever the user adds one.

Add the following method to the body of GeotificationsViewController:

func startMonitoring(geotification: Geotification) {
  // 1
  if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
    showAlert(withTitle:"Error", message: "Geofencing is not supported on this device!")
    return
  }
  // 2
  if CLLocationManager.authorizationStatus() != .authorizedAlways {
    showAlert(withTitle:"Warning", message: "Your geotification is saved but will only be activated once you grant Geotify permission to access the device location.")
  }
  // 3
  let region = self.region(withGeotification: geotification)
  // 4
  locationManager.startMonitoring(for: region)
}

Let’s walk through the method step by step:

  1. isMonitoringAvailableForClass(_:) determines if the device has the required hardware to support the monitoring of geofences. If monitoring is unavailable, you bail out entirely and alert the user accordingly. showSimpleAlertWithTitle(_:message:viewController) is a helper function in Utilities.swift that takes in a title and message and displays an alert view.
  2. Next, you check the authorization status to ensure that the app has also been granted the required permission to use Location Services. If the app isn’t authorized, it won’t receive any geofence-related notifications.However, in this case, you’ll still allow the user to save the geotification, since Core Location lets you register geofences even when the app isn’t authorized. When the user subsequently grants authorization to the app, monitoring for those geofences will begin automatically.
  3. You create a CLCircularRegion instance from the given geotification using the helper method you defined earlier.
  4. Finally, you register the CLCircularRegion instance with Core Location for monitoring.

With your start method done, you also need a method to stop monitoring a given geotification when the user removes it from the app.

In GeotificationsViewController.swift, add the following method below startMonitoringGeotificiation(_:):

func stopMonitoring(geotification: Geotification) {
  for region in locationManager.monitoredRegions {
    guard let circularRegion = region as? CLCircularRegion, circularRegion.identifier == geotification.identifier else { continue }
    locationManager.stopMonitoring(for: circularRegion)
  }
}

The method simply instructs the locationManager to stop monitoring the CLCircularRegion associated with the given geotification.

Now that you have both the start and stop methods complete, you’ll use them whenever you add or remove a geotification. You’ll begin with the adding part.

First, take a look at addGeotificationViewController(_:didAddCoordinate) in GeotificationsViewController.swift.

The method is the delegate call invoked by the AddGeotificationViewController upon creating a geotification; it’s responsible for creating a new Geotification object using the values passed from AddGeotificationsViewController, and updating both the map view and the geotifications list accordingly. Then you call saveAllGeotifications(), which takes the newly-updated geotifications list and persists it via NSUserDefaults.

Now, replace the method with the following code:

func addGeotificationViewController(controller: AddGeotificationViewController, didAddCoordinate coordinate: CLLocationCoordinate2D, radius: Double, identifier: String, note: String, eventType: EventType) {
  controller.dismiss(animated: true, completion: nil)
  // 1
  let clampedRadius = min(radius, locationManager.maximumRegionMonitoringDistance)
  let geotification = Geotification(coordinate: coordinate, radius: clampedRadius, identifier: identifier, note: note, eventType: eventType)
  add(geotification: geotification)
  // 2
  startMonitoring(geotification: geotification)
  saveAllGeotifications()
}

You’ve made two key changes to the code:

  1. You ensure that the value of the radius is clamped to the maximumRegionMonitoringDistance property of locationManager, which is defined as the largest radius in meters that can be assigned to a geofence. This is important, as any value that exceeds this maximum will cause monitoring to fail.
  2. You add a call to startMonitoringGeotification(_:) to ensure that the geofence associated with the newly-added geotification is registered with Core Location for monitoring.

At this point, the app is fully capable of registering new geofences for monitoring. There is, however, a limitation: As geofences are a shared system resource, Core Location restricts the number of registered geofences to a maximum of 20 per app.

While there are workarounds to this limitation (See Where to Go From Here? for a short discussion), for the purposes of this tutorial, you’ll take the approach of limiting the number of geotifications the user can add.

 

Finally, let’s deal with the removal of geotifications. This functionality is handled in mapView(_:annotationView:calloutAccessoryControlTapped:), which is invoked whenever the user taps the “delete” accessory control on each annotation.

Add a call to stopMonitoring(geotification:) to mapView(_:annotationView:calloutAccessoryControlTapped:), as shown below:

func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
  // Delete geotification
  let geotification = view.annotation as! Geotification
  stopMonitoring(geotification: geotification)   // Add this statement
  removeGeotification(geotification)
  saveAllGeotifications()
}

The additional statement stops monitoring the geofence associated with the geotification, before removing it and saving the changes to NSUserDefaults.

At this point, your app is fully capable of monitoring and un-monitoring user geofences. Hurray!

Build and run the project. You won’t see any changes, but the app will now be able to register geofence regions for monitoring. However, it won’t be able to react to any geofence events just yet. Not to worry—that will be your next order of business!

Reacting to Geofence Events

You’ll start by implementing some of the delegate methods to facilitate error handling – these are important to add in case anything goes wrong.

In GeotificationsViewController.swift, add the following methods to the CLLocationManagerDelegateextension:

func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
  print("Monitoring failed for region with identifier: \(region!.identifier)")
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
  print("Location Manager failed with the following error: \(error)")
}

These delegate methods simply log any errors that the location manager encounters to facilitate your debugging.

Note: You’ll definitely want to handle these errors more robustly in your production apps. For example, instead of failing silently, you could inform the user what went wrong.

Next, open AppDelegate.swift; this is where you’ll add code to properly listen and react to geofence entry and exit events.

Add the following line at the top of the file to import the CoreLocation framework:

import CoreLocation

Ensure that the AppDelegate has a CLLocationManager instance near the top of the class, as shown below:

class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  let locationManager = CLLocationManager() // Add this statement
  ...
}

Replace application(_:didFinishLaunchingWithOptions:) with the following implementation:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
  locationManager.delegate = self
  locationManager.requestAlwaysAuthorization()
  return true
}

You’ve set up your AppDelegate to receive geofence-related events. But you might wonder, “Why did I designate the AppDelegate to do this instead of the view controller?”

Geofences registered by an app are monitored at all times, including when the app isn’t running. If the device triggers a geofence event while the app isn’t running, iOS automatically relaunches the app directly into the background. This makes the AppDelegate an ideal entry point to handle the event, as the view controller may not be loaded or ready.

Now you might also wonder, “How will a newly-created CLLocationManager instance be able to know about the monitored geofences?”

It turns out that all geofences registered by your app for monitoring are conveniently accessible by all location managers in your app, so it doesn’t matter where the location managers are initialized. Pretty nifty, right? :]

Now all that’s left is to implement the relevant delegate methods to react to the geofence events. Before you do so, you’ll create a method to handle a geofence event.

Add the following method to AppDelegate.swift:

func handleEvent(forRegion region: CLRegion!) {
  print("Geofence triggered!")
}

At this point, the method takes in a CLRegion and simply logs a statement. Not to worry—you’ll implement the event handling later.

Next, add the following delegate methods in the CLLocationManagerDelegate extension of AppDelegate.swift, as well as a call to the handleRegionEvent(_:) function you just created, as shown in the code below:

extension AppDelegate: CLLocationManagerDelegate {
  
  func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    if region is CLCircularRegion {
      handleEvent(forRegion: region)
    }
  }
  
  func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
    if region is CLCircularRegion {
      handleEvent(forRegion: region)
    }
  }
}

As the method names aptly suggest, you fire locationManager(_:didEnterRegion:) when the device enters a CLRegion, while you fire locationManager(_:didExitRegion:) when the device exits a CLRegion.

Both methods return the CLRegion in question, which you need to check to ensure it’s a CLCircularRegion, since it could be a CLBeaconRegion if your app happens to be monitoring iBeacons, too. If the region is indeed a CLCircularRegion, you accordingly call handleRegionEvent(_:).

Note: A geofence event is triggered only when iOS detects a boundary crossing. If the user is already within a geofence at the point of registration, iOS won’t generate an event. If you need to query whether the device location falls within or outside a given geofence, Apple provides a method called requestStateForRegion(_:).

Now that your app is able to receive geofence events, you’re ready to give it a proper test run. If that doesn’t excite you, it really ought to, because for the first time in this tutorial, you’re going to see some results. :]

The most accurate way to test your app is to deploy it on your device, add some geotifications and take the app for a walk or a drive. However, it wouldn’t be wise to do so right now, as you wouldn’t be able to verify the print logs emitted by the geofence events with the device unplugged. Besides, it would be nice to get assurance that the app works before you commit to taking it for a spin.

Fortunately, there’s an easy way do this without leaving the comfort of your home. Xcode lets you include a hardcoded waypoint GPX file in your project that you can use to simulate test locations. Lucky for you, the starter project includes one for your convenience. :]

Open up TestLocations.gpx, which you can find in the Supporting Files group, and inspect its contents. You’ll see the following:

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
  <wpt lat="37.422" lon="-122.084058">
    <name>Google</name>
  </wpt>
  <wpt lat="37.3270145" lon="-122.0310273">
    <name>Apple</name>
  </wpt>
</gpx>

The GPX file is essentially an XML file that contains two waypoints: Google’s Googleplex in Mountain View and Apple’s Headquarters in Cupertino.

To begin simulating the locations in the GPX file, build and run the project. When the app launches the main view controller, go back to Xcode, select the Location icon in the Debug bar and choose TestLocations:

Back in the app, use the Zoom button on the top-left of the navigation bar to zoom to the current location. Once you get close to the area, you’ll see the location marker moving repeatedly from the Googleplex to Apple, Inc. and back.

Test the app by adding a few geotifications along the path defined by the two waypoints. If you added any geotifications earlier in the tutorial before you enabled geofence registration, those geotifications will obviously not work, so you might want to clear them out and start afresh.

For the test locations, it’s a good idea to place a geotification roughly at each waypoint. Here’s a possible test scenario:

  • Google: Radius: 1000m, Message: “Say Bye to Google!”, Notify on Exit
  • Apple: Radius: 1000m, Message: “Say Hi to Apple!”, Notify on Entry

Once you’ve added your geotifications, you’ll see a log in the console each time the location marker enters or leaves a geofence. If you activate the home button or lock the screen to send the app to the background, you’ll also see the logs each time the device crosses a geofence, though you obviously won’t be able to verify that behavior visually.

Note: Location simulation works both in iOS Simulator and on a real device. However, the iOS Simulator can be quite inaccurate in this case; the timings of the triggered events do not coincide very well with the visual movement of the simulated location in and out of each geofence. You would do better to simulate locations on your device, or better still, take the app for a walk!

Notifying the User of Geofence Events

You’ve made a lot of progress with the app. At this point, it simply remains for you to notify the user whenever the device crosses the geofence of a geotification—so prepare yourself to do just that.

To obtain the note associated with a triggering CLCircularRegion returned by the delegate calls, you need to retrieve the corresponding geotification that was persisted in NSUserDefaults. This turns out to be trivial, as you can use the unique identifier you assigned to the CLCircularRegion during registration to find the right geotification.

In AppDelegate.swift, add the following helper method at the bottom of the class:

func note(fromRegionIdentifier identifier: String) -> String? {
  let savedItems = UserDefaults.standard.array(forKey: PreferencesKeys.savedItems) as? [NSData]
  let geotifications = savedItems?.map { NSKeyedUnarchiver.unarchiveObject(with: $0 as Data) as? Geotification }
  let index = geotifications?.index { $0?.identifier == identifier }
  return index != nil ? geotifications?[index!]?.note : nil
}

This helper method retrieves the geotification note from the persistent store, based on its identifier, and returns the note for that geotification.

Now that you’re able to retrieve the note associated with a geofence, you’ll write code to trigger a notification whenever a geofence event is fired, using the note as the message.

Add the following statements to the end of application(_:didFinishLaunchingWithOptions:), just before the method returns:

application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil))
UIApplication.shared.cancelAllLocalNotifications()

The code you’ve added prompts the user for permission to enable notifications for this app. In addition, it does some housekeeping by clearing out all existing notifications.

Next, replace the contents of handleRegionEvent(_:) with the following:

func handleEvent(forRegion region: CLRegion!) {
  // Show an alert if application is active
  if UIApplication.shared.applicationState == .active {
    guard let message = note(fromRegionIdentifier: region.identifier) else { return }
    window?.rootViewController?.showAlert(withTitle: nil, message: message)
  } else {
    // Otherwise present a local notification
    let notification = UILocalNotification()
    notification.alertBody = note(fromRegionIdentifier: region.identifier)
    notification.soundName = "Default"
    UIApplication.shared.presentLocalNotificationNow(notification)
  }
}

If the app is active, the code above simply shows an alert controller with the note as the message. Otherwise, it presents a location notification with the same message.

Build and run the project, and run through the test procedure covered in the previous section. Whenever your test triggers a geofence event, you’ll see an alert controller displaying the reminder note:

Send the app to the background by activating the Home button or locking the device while the test is running. You’ll continue to receive notifications periodically that signal geofence events:

And with that, you have a fully functional, location-based reminder app in your hands. And yes, get out there and take that app for a spin!

Note: When you test the app, you may encounter situations where the notifications don’t fire exactly at the point of boundary crossing.

This is because before iOS considers a boundary as crossed, there is an additional cushion distance that must be traversed and a minimum time period that the device must linger at the new location. iOS internally defines these thresholds, seemingly to mitigate the spurious firing of notifications in the event the user is traveling very close to a geofence boundary.

In addition, these thresholds seem to be affected by the available location hardware capabilities. From experience, the geofencing behavior is a lot more accurate when Wi-Fi is enabled on the device.

Geofencing in iOS with Core Location Swift 3

Creating iOS Frameworks

Have you ever wanted to share a chunk of code between two or more of your apps, or wanted to share a part of your program with other developers?

Maybe you wanted to modularize your code similarly to how the iOS SDK separates its API by functionality, or perhaps you want to distribute your code in the same way as popular 3rd parties do.

In this iOS frameworks tutorial you’ll learn how to do all of the above!

In iOS 8 and Xcode 6, Apple provided a new template, Cocoa Touch Framework. As you’ll see, it makes creating custom frameworks much easier than before.

Frameworks have three major purposes:

  • Code encapsulation
  • Code modularity
  • Code reuse

You can share your framework with your other apps, team members, or the iOS community. When combined with Swift’s access control, frameworks help define strong, testable interfaces between code modules.

In Swift parlance, a module is a compiled group of code that is distributed together. A framework is one type of module, and an app is another example.

In this iOS frameworks tutorial, you’ll extract a piece of an existing app and set it free, and by doing so, you’ll learn the ins and outs of frameworks by:

  • Creating a new framework for the rings widget
  • Migrating the existing code and tests
  • Importing the whole thing back into the app.
  • Packing it up as an uber-portable CocoaPods
  • Bonus: Setting up a repository for your framework

By the time you’re done, the app will behave exactly as it did before, but it will use the portable framework you developed! :]

Getting Started

Download the Phonercise Starter Project.

Phonercise is a simple application that replicates the Apple Watch Activity app, except it measures your phone’s physical activity. The three rings on the main view represent movement, standing and exercise.

To get the most out of the project, you’ll need to build and run on an actual iOS device, and turn on the volume. Go ahead now, build and run!

Move the phone around to get credit for movement, and “exercise” the phone by shaking it vigorously — that’ll get its heartrate up. To get credit for standing up, just hold the phone upright.

The logic for the app is pretty simple:

  • ActionViewController.swift contains the view lifecycle and motion logic.
  • All the view logic is in the files in the Three Ring View folder, where you’ll find ThreeRingView.swift, which handles the view, and Fanfare.swift, which handles the audio. The other files handle the custom drawing of the shading, gradient and shapes.

The ring controls are pretty sweet. They’ve got an addictive quality and they’re easy to understand. Wouldn’t it be nice to use them in a number of applications beyond this fun, but completely silly app? Frameworks to the rescue!

Creating a Framework

Frameworks are self-contained, reusable chunks of code and resources that you can import into any number of apps and even share across iOS, tvOS, watchOS, and macOS apps.

If you’ve programmed in other languages, you may have heard of node modules, packages, gems, jars, etc. Frameworks are the Xcode version of these. Some examples of common frameworks in the iOS SDK are: Foundation, UIKit, AVFoundation, CloudKit, etc.

Framework Set Up

In Xcode 6, Apple introduced the Cocoa Touch Framework template along with access control, so creating frameworks has never been easier. The first thing to do is to create the project for the framework.

    1. Create a new project. In Xcode, go to File/New/Project.
    2. Choose iOS/Framework & Library/Cocoa Touch Framework to create a new framework.
  1. Click Next.
  2. Set the Product Name to ThreeRingControl. Use your own Organization Name and Organization Identifier. Check Include Unit Tests. That’s right! You’re going to have automated tests ensure your framework is bug free
  3. Click Next.
  4. In the file chooser, choose to create the project at the same level as the Phonercise project.
  5. Click Create.

Now you have a project (albeit a boring one) that creates a framework!

Add Code and Resources

Your current state is a framework without code, and that is about as appealing as straight chocolate without sugar. In this section, you’ll put the pod in CocoaPods by adding the existing files to the framework.

From the Phonercise source directory, drag the following eight files into the ThreeRingControl project in Xcode:

  • CircularGradient.swift
  • coin07.mp3
  • Fanfare.swift
  • RingLayer.swift
  • RingTip.swift
  • ThreeRingView.swift
  • Utilities.swift
  • winning.mp3

Make sure to check Copy items if needed, so that the files actually copy into the new project instead of just adding a reference. Frameworks need their own code, not references, to be independent.

Double-check that each of the files has Target Membership in ThreeRingControl to make sure they appear in the final framework. You can see this in the File Inspector for each file.

Build the framework project to make sure that you get Build Succeeded with no build warnings or errors.

Add the Framework to the Project

Close the ThreeRingControl project, and go back to the Phonercise project. Delete the six files under the Three Ring View group as well as the two MP3 files in Helper Files. Select Move to Trash in the confirmation dialog.

Build the project, and you’ll see several predictable errors where Xcode complains about not knowing what the heck a ThreeRingView is. Well, you’ll actually see messages along the lines of “Use of undeclared type 'ThreeRingView'“, among others.

Adding the Three Ring Control framework project to the workspace is the solution to these problems.

Add the Framework to the Project

Right-click on the root Phonercise node in the project navigator. Click Add Files to “Phonercise”. In the file chooser, navigate to and select ThreeRingControl.xcodeproj. This will add ThreeRingControl.xcodeproj as a sub-project.

Note: It isn’t strictly necessary to add the framework project to the app project; you could just add the ThreeRingControl.framework output.

However, combining the projects makes it easier to develop both the framework and app simultaneously. Any changes you make to the framework project are automatically propagated up to the app. It also makes it easier for Xcode to resolve the paths and know when to rebuild the project.

Even though the two projects are now together in the workspace, Phonercise still doesn’t get ThreeRingControl. It’s like they’re sitting in the same room, but Phonercise can’t see the new framework.

Try linking the framework to the app’s target to fix this problem. First, expand the ThreeRingControl project to see the Products folder, and then look for for ThreeRingControl.framework beneath it. This file is the output of the framework project that packages up the binary code, headers, resources and metadata.

Select the top level Phonercise node to open the project editor. Click the Phonercise target, and then go to the General tab.

Scroll down to the Embedded Binaries section. Drag ThreeRingControl.framework from the Products folder of ThreeRingControl.xcodeproj onto this section.

You just added an entry for the framework in both Embedded Binaries and Linked Frameworks and Binaries.

Now the app knows about the framework and where to find it, so that should be enough, right?

Build the Phonercise project. More of the same errors.

Access Control

Your problem is that although the framework is part of the project, the project’s code doesn’t know about it — out of sight, out of mind.

Go to ActionViewController.swift, and add the following line to the list of imports at the top of the file.

import ThreeRingControl

It’s critical, but this inclusion won’t fix the build errors. This is because Swift uses access control to let you determine whether constructs are visible to other files or modules.

By default, Swift makes everything internal or visible only within its own module.

To restore functionality to the app, you have to update the access control on two Phonercise classes.

Although it’s a bit tedious, the process of updating access control improves modularity by hiding code not meant to appear outside the framework. You do this by leaving certain functions with no access modifier, or by explicitly declaring them internal.

Swift has three levels of access control. Use the following rules of thumb when creating your own frameworks:

  • Public: for code called by the app or other frameworks, e.g., a custom view.
  • Internal: for code used between functions and classes within the framework, e.g., custom layers in that view.
  • Fileprivate: for code used within a single file, e.g., a helper function that computes layout heights.
  • Private: for code used within an enclosing declaration, such as a single class block. Private code will not be visible to other blocks, such as extensions of that class, even in the same file, e.g., private variables, setters, or helper sub-functions.

Ultimately, making frameworks is so much easier than in the past, thanks to the new Cocoa Touch Framework template. Apple provided both of these in Xcode 6 and iOS 8.

Update the Code

When ThreeRingView.swift was part of the Phonercise app, internal access wasn’t a problem. Now that it’s in a separate module, it must be made public for the app to use it. The same is true of the code in Fanfare.swift.

Open ThreeRingView.swift inside of ThreeRingControlProject.

Make the class public by adding the public keyword to the class definition, like so:

public class ThreeRingView : UIView {

ThreeRingView will now be visible to any app file that imports the ThreeRingControl framework.

Add the public keyword to:

  • Both init functions
  • The variables RingCompletedNotification, AllRingsCompletedNotification, layoutSubviews()
  • Everything marked @IBInspectable — there will be nine of these

Note: You might wonder why you have to declare inits as public. Apple explains this and other finer points of access control in their Access Control Documentation.

The next step is to do essentially the same thing as you did for ThreeRingView.swift, and add the public keyword to the appropriate parts of Fanfare.swift. For your convenience, this is already done.

Note that the following variables are public: ringSound, allRingSound, and sharedInstance. The function playSoundsWhenReady() is public as well.

Now build and run. The good news is that the errors are gone, and the bad news is that you’ve got a big, white square. Not a ring in sight. Oh no! What’s going on?

Update the Storyboard

When using storyboards, references to custom classes need to have both the class name and module set in the Identity Inspector. At the time of this storyboard’s creation, ThreeRingView was in the app’s module, but now it’s in the framework.

Update the storyboard, as explained below, by telling it where to find the custom view; this will get rid of the white square.

  1. Open Main.Storyboard in the Phonercise project.
  2. Select the Ring Control in the view hierarchy.
  3. In the Identity Inspector, under Custom Class, change the Module to ThreeRingControl.

Once you set the module, Interface Builder should update and show the control in the editor area.

Build and run. Now you’ll get some rings.

Creating iOS Frameworks

When Should You Use Implicitly Unwrapped Optionals

When should you use implicitly unwrapped optionals? The short answer to this question is “Never.” But the answer is more nuanced than that. Implicitly unwrapped optionals were added to the Swift language for a reason.

What Is an Implicitly Unwrapped Optional?

The concept underlying implicitly unwrapped optionals is easy to understand. As the name implies, an implicitly unwrapped optional behaves as an optional. I write behave because optionals and implicitly unwrapped optionals are defined differently in the Swift standard library. What’s important is that an implicitly unwrapped optional is allowed to have no value. It shares this trait with optionals.

We declare an implicitly unwrapped optional by appending an exclamation mark to the type.

var text: String!

Remember that the exclamation mark is a warning sign in Swift. It indicates that we can access the value of the implicitly unwrapped optional without unwrapping it using optional binding or optional chaining.

I need to mention that we can still use optional binding and optional chaining to safely access the value of the implicitly unwrapped optional.

var text: String!

if let text = text {
    ...
}
var text: String!

let startIndex = text?.startIndex

We can also check it for nil. But this shouldn’t be necessary since an implicitly unwrapped optional promises the compiler it has a value when it is accessed.

var text: String!

if text == nil {
    ...
}

You could see an implicitly unwrapped optional as an optional whose value is always accessed by forced unwrapping. This also means that a runtime error is thrown if you access an implicitly unwrapped optional that has no value.

Why Would I Need Implicitly Unwrapped Optionals?

Like I said, it is perfectly fine and possible to never use implicitly unwrapped optionals. Implicitly unwrapped optionals are a compromise between safety and convenience. Whenever you use an implicitly unwrapped optional instead of an optional, you trade safety for convenience. If safety is more important to you, then don’t use implicitly unwrapped optionals.

Implicitly unwrapped optionals are a compromise between safety and convenience.

If you declare a property as optional, then you need to use optional binding or optional chaining every time you access the value of the optional. While this isn’t a problem, it makes the code you write more verbose and the result may be less readable. This isn’t true if you use implicitly unwrapped optionals.

When Should You Use Implicitly Unwrapped Optionals?

The Swift Programming Language states the following.

Sometimes it is clear from a program’s structure that an optional will always have a value, after the value is first set. In these cases, it is useful to remove the need to check and unwrap the optional’s value every time it is accessed, because it can be safely assumed to have a value all of the time. – The Swift Programming Language

Outlets are a common example when discussing implicitly unwrapped optionals. Why does Apple use implicitly unwrapped optionals to declare outlets? The reason is simple.

Every stored property of a class needs to have a valid value before the initialization of the instance is complete. If you don’t know the value of a stored property during initialization, you can assign a sensible default value or declare it as an optional, which has a default value of nil. But there is a third option, use an implicitly unwrapped optional.

The value of an outlet is set after the instance of the class is initialized. This is inconvenient since you know that the outlet will have a valid value when it is later accessed. For these reasons, outlets are often declared as implicitly unwrapped optionals. The only reason for doing so is convenience. That is important to understand. It is perfectly fine to declare outlets as optionals.

import UIKit

class ViewController: UIViewController {

    @IBOutlet var titleLabel: UILabel!

    ...
}

Being Bitten by Implicitly Unwrapped Optionals

If you use implicitly unwrapped optionals for anything else but outlets, I can assure you that you will be bitten at some point. When I first started working with Swift, I noticed (looking back) how easily implicitly unwrapped optionals made their way into my code. These implicitly unwrapped optionals were always properties that I couldn’t give a value during initialization. This is a common issue when working with storyboards and segues, for example.

A few months ago, I took over a project that makes ample use of implicitly unwrapped optionals and that has backfired several times since I started working on the project. While I can appreciate the above statement in The Swift Programming Language, it may be obvious that an optional will always have a value when you write the code … but code changes and assumptions change.

Someone might take over the project and not know about the assumptions you made when you wrote the code that uses implicitly unwrapped optionals.

Be Careful

I continue to declare outlets as implicitly unwrapped optionals, but I never use implicitly unwrapped optionals for anything else. Be careful when you make assumptions or promises you may not be able to keep. The code you write today won’t look the same a year from now.

Don’t try to bypass Swift’s type safety. It is a cornerstone of the language and that is for good reason. Implicitly unwrapped optionals were added to the language for convenience, but that problem can also be solved with optionals.

When Should You Use Implicitly Unwrapped Optionals

Swift 3, iOS and Xcode 8: UIPageController and UIPageControl

These two elements allow you to swipe through UIViewControllers with little dots at the bottom of the screen.

First drag a UIPageController onto your storyboard. And give it a custom class. That custom class will look like this:

class MyPagesViewController : UIPageViewController, UIPageViewControllerDelegate, UIPageViewControllerDataSource {

    let pages = ["PagesContentController1", "PagesContentController2"]

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        self.dataSource = self

        ...
    }

    func pageViewController(_ pageViewController: UIPageViewController,
                            viewControllerBefore viewController: UIViewController) -> UIViewController? {
      ...
    }

    func pageViewController(_ pageViewController: UIPageViewController,
                            viewControllerAfter viewController: UIViewController) -> UIViewController? {
      ...
    }

    func presentationCount(for pageViewController: UIPageViewController) -> Int {
      ...
    }

    func presentationIndex(for pageViewController: UIPageViewController) -> Int {
      ...
    }
}

We’re extending from UIPageViewController and we have a delegate and data source protocols for our page view controller. We’re setting ourself as the delegate and datasource.

The pages array references view controller restoration identifiers. So create two new view controllers in your story board and ensure they have those identifiers.

The first thing we want to do is initialise our first view controller to show in viewDidShow.

  let vc = self.storyboard?.instantiateViewController(withIdentifier: "PagesContentController1")
  setViewControllers([vc!], // Has to be a single item array, unless you're doing double sided stuff I believe
                     direction: .forward,
                     animated: true,
                     completion: nil)

If you start the app now, you’ll get your first screen. Our first two delegate methods will allow us to page to the next screen, however:

func pageViewController(_ pageViewController: UIPageViewController,
                        viewControllerBefore viewController: UIViewController) -> UIViewController? {
    if let identifier = viewController.restorationIdentifier {
        if let index = pages.index(of: identifier) {
            if index > 0 {
                return self.storyboard?.instantiateViewController(withIdentifier: pages[index-1])
            }
        }
    }
    return nil
}

func pageViewController(_ pageViewController: UIPageViewController,
                        viewControllerAfter viewController: UIViewController) -> UIViewController? {
    if let identifier = viewController.restorationIdentifier {
        if let index = pages.index(of: identifier) {
            if index < pages.count - 1 {
                return self.storyboard?.instantiateViewController(withIdentifier: pages[index+1])
            }
        }
    }
    return nil
}

In both, we look for the restoration identifiers we set previously, then get get the index of such in our pages array, and we then return the previous view controller, if we’re in the method that says viewControllerAfter, otherwise we attempt to go forwards.

The other two delegate methods deal with the UIPageControl, which is automatically given to us when we created the UIPageViewController, although it does not appear in the storyboard.

func presentationCount(for pageViewController: UIPageViewController) -> Int {
    return pages.count
}

func presentationIndex(for pageViewController: UIPageViewController) -> Int {
    if let identifier = viewControllers?.first?.restorationIdentifier {
        if let index = pages.index(of: identifier) {
            return index
        }
    }
    return 0
}

We’re returning the number of pages, and in the latter we look for our current view controller, get its restoration id, and return the index of that to designate the page we’re on currently.

Finally, in your storyboad, on the UIPageViewController set the transition type to ‘scroll’, thereby showing those little dots on the bottom of the screen. To make them transparent this voodoo code that I found in a youtube video seems to work:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    for view in view.subviews {
        if view is UIScrollView {
            view.frame = UIScreen.main.bounds // Why? I don't know.
        }
        else if view is UIPageControl {
            view.backgroundColor = UIColor.clear
        }
    }
}

And voila.

Swift 3, iOS and Xcode 8: UIPageController and UIPageControl

Core Data Delete Rules Swift 3

Delete Rules

What happens if a note is deleted? Should the category the note belongs to also be deleted? No. But what happens if a category is deleted? Should it be possible to have notes without a category?

This brings us to delete rules. Every relationship has a delete rule. A delete rule defines what happens when the record that owns the relationship is deleted.

A delete rule defines what happens when the record that owns the relationship is deleted.

Select the notes relationship of the Category entity and open the Data Model Inspector on the right. By default, the delete rule of a relationship is set to nullify. Core Data supports four delete rules:

  • No Action
  • Nullify
  • Cascade
  • Deny

No Action Delete Rule

If the delete rule of a relationship is set to No Action, nothing happens. Let me illustrate this with an example. We have a category that contains several notes. If the category is deleted, the notes are not notified of this event. The notes on the other end of the relationship believe that they are still associated with the deleted category.

I have never had a need to use this delete rule in a project. In most situations, you want to take some action when a record is deleted. And that is where the other delete rules come into play.

Nullify Delete Rule

If the delete rule of a relationship is set to Nullify, the destination of the relationship is nullified when the record is deleted.

For example, if a category has several notes and the category is deleted, the relationships pointing from the notes to the category are nullified. This is the default delete rule and the delete rule you will find yourself using most often.

Cascade Delete Rule

The Cascade delete rule is useful if the data model includes one or more dependencies. Let me give you an example. If a note should always have a category, the deletion of a category should automatically delete the notes associated with that category. In other words, the deletion of the category cascades or trickles down to the notes linked to the category. Even though this may make sense on paper, the user probably won’t like it when you automatically delete its notes. The Deny delete rule is a better option in this scenario (see below).

If you are dealing with a Many-To-Many relationship, this is often not what you want. If a note can have several tags and a tag can be linked to several notes, deleting a tag should not result in the deletion of every note with that tag. The notes could be associated with other tags, for example.

Deny Delete Rule

Remember the previous example in which the deletion of a category resulted in the deletion of every note that belonged to that category. It may be better to apply the Deny delete rule.

Deny is another powerful and useful pattern. It is the opposite of the Cascade delete rule. Instead of cascading the deletion of a record, it prevents the deletion of the record.

For example, if a category is associated with several notes, the category can only be deleted if it is no longer tied to any notes. This configuration prevents the scenario in which notes are no longer associated with a category.

Core Data Delete Rules Swift 3

GCD(Grand Central Dispatch) Threading for Swift 3

GCD Concepts

To understand GCD, you need to be comfortable with several concepts related to concurrency and threading.

Concurrency

In iOS a process or application is made up of one or more threads. The threads are managed independently by the operating system scheduler. Each thread can execute concurrently but it’s up to the system to decide if this happens and how it happens.

Single-core devices can achieve concurrency through time-slicing. They would run one thread, perform a context switch, then run another thread.

Concurrency_vs_Parallelism

Multi-core devices on the other hand, execute multiple threads at the same time via parallelism.

GCD is built on top of threads. Under the hood it manages a shared thread pool. With GCD you add blocks of code or work items to dispatch queues and GCD decides which thread to execute them on.

As you structure your code, you’ll find code blocks that can run simultaneously and some that should not. This then allows you to use GCD to take advantage of concurrent execution.

Note that GCD decides how much parallelism is required based on the system and available system resources. It’s important to note that parallelism requires concurrency, but concurrency does not guarantee parallelism.

Basically, concurrency is about structure while parallelism is about execution.

Queues

GCD provides dispatch queues represented by DispatchQueue to manage tasks you submit and execute them in a FIFO order guaranteeing that the first task submitted is the first one started.

Dispatch queues are thread-safe which means that you can access them from multiple threads simultaneously. The benefits of GCD are apparent when you understand how dispatch queues provide thread safety to parts of your own code. The key to this is to choose the right kind of dispatch queue and the right dispatching function to submit your work to the queue.

Queues can be either serial or concurrent. Serial queues guarantee that only one task runs at any given time. GCD controls the execution timing. You won’t know the amount of time between one task ending and the next one beginning:

Serial-Queue-Swift

Concurrent queues allow multiple tasks to run at the same time. Tasks are guaranteed to start in the order they were added. Tasks can finish in any order and you have no knowledge of the time it will take for the next task to start, nor the number of tasks that are running at any given time.

See the sample task execution below:Concurrent-Queue-Swift

 

Notice how Task 1, Task 2, and Task 3 start quickly one after the other. On the other hand, Task 1 took a while to start after Task 0. Also notice that while Task 3 started after Task 2, it finished first.

The decision of when to start a task is entirely up to GCD. If the execution time of one task overlaps with another, it’s up to GCD to determine if it should run on a different core, if one is available, or instead to perform a context switch to run a different task.

GCD provides three main types of queues:

  1. Main queue: runs on the main thread and is a serial queue.
  2. Global queues: concurrent queues that are shared by the whole system. There are four such queues with different priorities : high, default, low, and background. The background priority queue is I/O throttled.
  3. Custom queues: queues that you create which can be serial or concurrent. These actually trickle down into being handled by one of the global queues.

When setting up the global concurrent queues, you don’t specify the priority directly. Instead you specify a Quality of Service (QoS) class property. This will indicate the task’s importance and guide GCD into determining the priority to give to the task.

The QoS classes are:

  • User-interactive: This represents tasks that need to be done immediately in order to provide a nice user experience. Use it for UI updates, event handling and small workloads that require low latency. The total amount of work done in this class during the execution of your app should be small. This should run on the main thread.
  • User-initiated: The represents tasks that are initiated from the UI and can be performed asynchronously. It should be used when the user is waiting for immediate results, and for tasks required to continue user interaction. This will get mapped into the high priority global queue.
  • Utility: This represents long-running tasks, typically with a user-visible progress indicator. Use it for computations, I/O, networking, continous data feeds and similar tasks. This class is designed to be energy efficient. This will get mapped into the low priority global queue.
  • Background: This represents tasks that the user is not directly aware of. Use it for prefetching, maintenance, and other tasks that don’t require user interaction and aren’t time-sensitive. This will get mapped into the background priority global queue.

Synchronous vs. Asynchronous

With GCD, you can dispatch a task either synchronously or asynchronously.

A synchronous function returns control to the caller after the task is completed.

An asynchronous function returns immediately, ordering the task to be done but not waiting for it. Thus, an asynchronous function does not block the current thread of execution from proceeding on to the next function.

Handling Background Tasks

 

DispatchQueue.global(qos: .userInitiated).async { // 1
  let overlayImage = self.faceOverlayImageFromImage(self.image)
  DispatchQueue.main.async { // 2
    self.fadeInNewImage(overlayImage) // 3
  }
}

Here’s what the code’s doing step by step:

  1. You move the work to a background global queue and run the work in the closure asynchronously. This lets viewDidLoad() finish earlier on the main thread and makes the loading feel more snappy. Meanwhile, the face detection processing is started and will finish at some later time.
  2. At this point, the face detection processing is complete and you’ve generated a new image. Since you want to use this new image to update your UIImageView, you add a new closure to the main queue. Remember – you must always access UIKit classes on the main thread!
  3. Finally, you update the UI with fadeInNewImage(_:) which performs a fade-in transition of the new googly eyes image.

Here’s a quick guide of how and when to use the various queues with async:

  • Main Queue: This is a common choice to update the UI after completing work in a task on a concurrent queue. To do this, you’ll code one closure inside another. Targeting the main queue and calling async guarantees that this new task will execute sometime after the current method finishes.
  • Global Queue: This is a common choice to perform non-UI work in the background.
  • Custom Serial Queue: A good choice when you want to perform background work serially and track it. This eliminates resource contention since you know only one task at a time is executing. Note that if you need the data from a method, you must inline another closure to retrieve it or consider using sync.

Delaying Task Execution

DispatchQueue allows you to delay task execution. Care should be taken not to use this to solve race conditions or other timing bugs through hacks like introducing delays. Use this when you want a task to run at a specific time.

let delayInSeconds = 1.0 // 1
DispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) { // 2
  let count = PhotoManager.sharedManager.photos.count
  if count > 0 {
    self.navigationItem.prompt = nil
  } else {
    self.navigationItem.prompt = "Add photos with faces to Googlyify them!"
  }
}

Here’s what’s going on above:

  1. You specify a variable for the amount of time to delay.
  2. You then wait for the specified time then asynchronously run the block which updates the photos count and updates the prompt.

Handling the Readers-Writers Problem

GCD provides an elegant solution of creating a read/write lock using dispatch barriers. Dispatch barriers are a group of functions acting as a serial-style bottleneck when working with concurrent queues.

When you submit a DispatchWorkItem to a dispatch queue you can set flags to indicate that it should be the only item executed on the specified queue for that particular time. This means that all items submitted to the queue prior to the dispatch barrier must complete before the DispatchWorkItem will execute.

When the DispatchWorkItem‘s turn arrives, the barrier executes it and ensures that the queue does not execute any other tasks during that time. Once finished, the queue returns to its default implementation.

The diagram below illustrates the effect of a barrier on various asynchronous tasks:Dispatch-Barrier-Swift

 

Notice how in normal operation the queue acts just like a normal concurrent queue. But when the barrier is executing, it essentially acts like a serial queue. That is, the barrier is the only thing executing. After the barrier finishes, the queue goes back to being a normal concurrent queue.

Use caution when using barriers in global background concurrent queues as these queues are shared resources. Using barriers in a custom serial queue is redundant as it already executes serially. Using barriers in custom concurrent queue is a great choice for handling thread safety in atomic of critical areas of code.

You’ll use a custom concurrent queue to handle your barrier function and separate the read and write functions. The concurrent queue will allow multiple read operations simultaneously.

fileprivate let concurrentPhotoQueue =
  DispatchQueue(
    label: "com.raywenderlich.GooglyPuff.photoQueue", // 1
    attributes: .concurrent) // 2

This initializes concurrentPhotoQueue as a concurrent queue.

  1. You set up label with a descriptive name that is helpful during debugging. Typically you’ll use the reversed DNS style naming convention.
  2. You specify a concurrent queue.

Next, replace addPhoto(_:) with the following code:

func addPhoto(_ photo: Photo) {
  concurrentPhotoQueue.async(flags: .barrier) { // 1
    self._photos.append(photo) // 2
    DispatchQueue.main.async { // 3
      self.postContentAddedNotification()
    }
  }
}

Here’s how your new write function works:

  1. You dispatch the write operation asynchronously with a barrier. When it executes, it will be the only item in your queue.
  2. You add the object to the array.
  3. Finally you post a notification that you’ve added the photo. This notification should be posted on the main thread because it will do UI work. So you dispatch another task asynchronously to the main queue to trigger the notification.

To ensure thread safety with your writes, you need to perform reads on the concurrentPhotoQueue queue. You need return data from the function call so an asynchronous dispatch won’t cut it. In this case, syncwould be an excellent candidate.

Use sync to keep track of your work with dispatch barriers, or when you need to wait for the operation to finish before you can use the data processed by the closure.

You need to be careful though. Imagine if you call sync and target the current queue you’re already running on. This will result in a deadlock situation.

Two (or sometimes more) items — in most cases, threads — are said to be deadlocked if they all get stuck waiting for each other to complete or perform another action. The first can’t finish because it’s waiting for the second to finish. But the second can’t finish because it’s waiting for the first to finish.

In your case, the sync call will wait until the closure finishes, but the closure can’t finish (it can’t even start!) until the currently executing closure is finished, which can’t! This should force you to be conscious of which queue you’re calling from — as well as which queue you’re passing in.

Here’s a quick overview of when and where to use sync:

  • Main Queue: Be VERY careful for the same reasons as above; this situation also has potential for a deadlock condition.
  • Global Queue: This is a good candidate to sync work through dispatch barriers or when waiting for a task to complete so you can perform further processing.
  • Custom Serial Queue: Be VERY careful in this situation; if you’re running in a queue and call sync targeting the same queue, you’ll definitely create a deadlock.
var photos: [Photo] {
  var photosCopy: [Photo]!
  concurrentPhotoQueue.sync { // 1
    photosCopy = self._photos // 2
  }
  return photosCopy
}

Here’s what’s going on step-by-step:

  1. Dispatch synchronously onto the concurrentPhotoQueue to perform the read.
  2. Store a copy of the photo array in photosCopy and return it.

Dispatch Groups

With dispatch groups you can group together multiple tasks and either wait for them to be completed or be notified once they are complete. Tasks can be asynchronous or synchronous and can even run on different queues.

DispatchQueue.global(qos: .userInitiated).async { // 1
  var storedError: NSError?
  let downloadGroup = DispatchGroup() // 2
  for address in [overlyAttachedGirlfriendURLString, 
                  successKidURLString,
                  lotsOfFacesURLString] {
    let url = URL(string: address)
    downloadGroup.enter() // 3
    let photo = DownloadPhoto(url: url!) {
      _, error in
      if error != nil {
        storedError = error
      }   
      downloadGroup.leave() // 4
    }   
    PhotoManager.sharedManager.addPhoto(photo)
  }   
      
  downloadGroup.wait() // 5
  DispatchQueue.main.async { // 6
    completion?(storedError)
  }   
}

Here’s what the code is doing step-by-step:

  1. Since you’re using the synchronous wait method which blocks the current thread, you use async to place the entire method into a background queue to ensure you don’t block the main thread.
  2. This creates a new dispatch group.
  3. You call enter() to manually notify the group that a task has started. You must balance out the number of enter() calls with the number of leave() calls or your app will crash.
  4. Here you notify the group that this work is done.
  5. You call wait() to block the current thread while waiting for tasks’ completion. This waits forever which is fine because the photos creation task always completes. You can use wait(timeout:) to specify a timeout and bail out on waiting after a specified time.
  6. At this point, you are guaranteed that all image tasks have either completed or timed out. You then make a call back to the main queue to run your completion closure.

Dispatch groups are a good candidate for all types of queues. You should be wary of using dispatch groups on the main queue if you’re waiting synchronously for the completion of all work since you don’t want to hold up the main thread. However, the asynchronous model is an attractive way to update the UI once several long-running tasks finish, such as network calls.

Dispatching asynchronously to another queue then blocking work using wait is clumsy.

DispatchGroup manages dispatch groups. You’ll first look at its wait method. This blocks your current thread until all the group’s enqueued tasks have been completed.

 

// 1
var storedError: NSError?
let downloadGroup = DispatchGroup()
for address in [overlyAttachedGirlfriendURLString,
                successKidURLString,
                lotsOfFacesURLString] {
  let url = URL(string: address)
  downloadGroup.enter()
  let photo = DownloadPhoto(url: url!) {
    _, error in
    if error != nil {
      storedError = error
    }   
    downloadGroup.leave()
  }   
  PhotoManager.sharedManager.addPhoto(photo)
}   
    
downloadGroup.notify(queue: DispatchQueue.main) { // 2
  completion?(storedError)
}

Here’s what’s going on:

  1. In this new implementation you don’t need to surround the method in an async call since you’re not blocking the main thread.
  2. notify(queue:work:) serves as the asynchronous completion closure. It is called when there are no more items left in the group. You also specify that you want to schedule the completion work to be run on the main queue.

This is a much cleaner way to handle this particular job as it doesn’t block any threads.

Concurrency Looping

You might notice that there’s a forloop in there that cycles through three iterations and downloads three separate images. Your job is to see if you can run this for loop concurrently to try and speed things up.

This is a job for DispatchQueue.concurrentPerform(iterations:execute:). It works similarly to a for loop in that it executes different iterations concurrently. It is sychronous and returns only when all of the work is done.

Care must be taken when figuring out the optimal number of iterations for a given amount of work. Many iterations and a small amount of work per iteration can create so much overhead that it negates any gains from making the calls concurrent. The technique known as striding helps you out here. This is where for each iteration you do multiple pieces of work.

When is it appropriate to use DispatchQueue.concurrentPerform(iterations:execute:)? You can rule out serial queues because there’s no benefit there – you may as well use a normal for loop. It’s a good choice for concurrent queues that contain looping, especially if you need to keep track of progress.

var storedError: NSError?
let downloadGroup = DispatchGroup()
let addresses = [overlyAttachedGirlfriendURLString,
                 successKidURLString,
                 lotsOfFacesURLString]
let _ = DispatchQueue.global(qos: .userInitiated)
DispatchQueue.concurrentPerform(iterations: addresses.count) {
  i in
  let index = Int(i)
  let address = addresses[index]
  let url = URL(string: address)
  downloadGroup.enter()
  let photo = DownloadPhoto(url: url!) {
    _, error in
    if error != nil {
      storedError = error
    }
    downloadGroup.leave()
  }
  PhotoManager.sharedManager.addPhoto(photo)
}
downloadGroup.notify(queue: DispatchQueue.main) {
  completion?(storedError)
}

The former for loop has been replaced with DispatchQueue.concurrentPerform(iterations:execute:) to handle concurrent looping.

Running this new code on the device will occasionally produce marginally faster results. But was all this work worth it?

Actually, it’s not worth it in this case. Here’s why:

  • You’ve probably created more overhead running the threads in parallel than just running the for loop in the first place. You should use DispatchQueue.concurrentPerform(iterations:execute:) for iterating over very large sets along with the appropriate stride length.

Cancelling Dispatch Blocks

Thus far, you haven’t seen code that allows you to cancel enqueued tasks. This is where dispatch block objects represented by DispatchWorkItem comes into focus. Be aware that you can only cancel a DispatchWorkItem before it reaches the head of a queue and starts executing.

Let’s demonstrate this by starting download tasks for several images from Le Internet then cancelling some of them.

var storedError: NSError?
let downloadGroup = DispatchGroup()
var addresses = [overlyAttachedGirlfriendURLString,
                 successKidURLString,
                 lotsOfFacesURLString]
addresses += addresses + addresses // 1
var blocks: [DispatchWorkItem] = [] // 2

for i in 0 ..< addresses.count {
  downloadGroup.enter()
  let block = DispatchWorkItem(flags: .inheritQoS) { // 3
    let index = Int(i)
    let address = addresses[index]
    let url = URL(string: address)
    let photo = DownloadPhoto(url: url!) {
      _, error in
      if error != nil {
        storedError = error
      }
      downloadGroup.leave()
    }
    PhotoManager.sharedManager.addPhoto(photo)
  }
  blocks.append(block)
  DispatchQueue.main.async(execute: block) // 4
}

for block in blocks[3 ..< blocks.count] { // 5
  let cancel = arc4random_uniform(2) // 6
  if cancel == 1 {
    block.cancel() // 7
    downloadGroup.leave() // 8
  }
}

downloadGroup.notify(queue: DispatchQueue.main) {
  completion?(storedError)
}

Here’s a step-by-step walk through the code above:

  1. You expand the addresses array to hold three copies of each image.
  2. You initialize a blocks array to hold dispatch block objects for later use.
  3. You create a new DispatchWorkItem. You pass in a flags parameter to specify that the block should inherit its Quality of Service class from the queue it is dispatched to. You then define the work to be done in a closure.
  4. You dispatch the block asynchronously to the main queue. For this example, using the main queue makes it easier to cancel select blocks since it’s a serial queue. The code that sets up the dispatch blocks is already executing on the main queue so you are guaranteed that the download blocks will execute at some later time.
  5. You skip the first three download blocks by slicing the blocks array.
  6. Here you use arc4random_uniform() to randomly pick a number between 0 and 1. It’s like a coin toss.
  7. If the random number is 1 you cancel the block. This can only cancel blocks that are still in a queue and haven’t began executing. Blocks can’t be canceled in the middle of execution.
  8. Here you remember to remove the canceled block from the dispatch group.

Other GCD Fun

Semaphores

 

let url = URL(string: urlString)
let semaphore = DispatchSemaphore(value: 0) // 1
let _ = DownloadPhoto(url: url!) {
  _, error in
  if let error = error {
    XCTFail("\(urlString) failed. \(error.localizedDescription)")
  }   
  semaphore.signal() // 2
}
let timeout = DispatchTime.now() + .seconds(defaultTimeoutLengthInSeconds)
if semaphore.wait(timeout: timeout) == .timedOut { // 3
  XCTFail("\(urlString) timed out")
} 

Here’s how the semaphore works in the code above:

  1. You create a semaphore and set its start value. This represents the number of things that can access the semaphore without needing the semaphore to be incremented (note that incrementing a semaphore is known as signaling it).
  2. You signal the semaphore in the completion closure. This increments the semaphore count and signals that the semaphore is available to other resources that want it.
  3. You wait on the semaphore, with a given timeout. This call blocks the current thread until the semaphore has been signaled. A non-zero return code from this function means that the timeout was reached. In this case, the test is failed because it is deemed that the network should not take more than 10 seconds to return — a fair point!

Dispatch Sources

Dispatch sources are a particularly interesting feature of GCD. A dispatch source can basically be used to monitor for some type of event. Events can include Unix signals, file descriptors, Mach ports, VFS Nodes, and other obscure stuff.

When setting up a dispatch source, you tell it what type of events you want to monitor and the dispatch queue on which its event handler block should be executed. You then assign an event handler to the dispatch source.

Upon creation, dispatch sources start off in a suspended state. This allows for additional configuration steps to take place, for example setting up the event handler. Once you’ve configured your dispatch source, you should resume it to start processing events.

In this tutorial, you’ll get a small taste of working with dispatch sources by using it in a rather peculiar way: to monitor when your app is put into debug mode.

#if DEBUG // 1
  var signal: DispatchSourceSignal? // 2
  private let setupSignalHandlerFor = { (_ object: AnyObject) -> Void in // 3
    let queue = DispatchQueue.main
    signal =
      DispatchSource.makeSignalSource(signal: Int32(SIGSTOP), queue: queue) // 4
    signal?.setEventHandler { // 5
      print("Hi, I am: \(object.description!)")
    }
    signal?.resume() // 6
  }
#endif

The code is a little involved, so walk through it step-by-step:

  1. You compile this code only in DEBUG mode to prevent “interested parties” from gaining a lot of insight into your app. :] DEBUG is defined by adding -D DEBUG under Project Settings -> Build Settings -> Swift Compiler – Custom Flags -> Other Swift Flags -> Debug. It should be set already in the starter project.
  2. You declare a signal variable of type DispatchSourceSignal for use in monitoring Unix signals.
  3. You create a block assigned to the setupSignalHandlerFor global variable that you’ll use for one-time setup of your dispatch source.
  4. Here you set up signal. You indicate that you’re interested in monitoring the SIGSTOP Unix signal and handling received events on the main queue — you’ll discover why shortly.
  5. If the dispatch source is successfully created, you register an event handler closure that’s invoked whenever you receive the SIGSTOP signal. Your handler prints a message that includes the class description.
  6. All sources start off in the suspended state by default. Here you tell the dispatch source to resume so it can start monitoring events.

Add the following code to viewDidLoad() just below the call to super.viewDidLoad():

#if DEBUG
  _ = setupSignalHandlerFor(self)
#endif

This code invokes the dispatch source’s initialization code.

Build and run the app. Pause the program execution and resume the app immediately by tapping the pause then play buttons in Xcode’s debugger:

grand central dispatch tutorial

Check out the console. You should see something like this:

Hi, I am: <GooglyPuff.PhotoCollectionViewController: 0x7fbf0af08a10>

You app is now debugging-aware! That’s pretty awesome, but how would you use this in real life?

You could use this to debug an object and display data whenever you resume the app. You could also give your app custom security logic to protect itself (or the user’s data) when malicious attackers attach a debugger to your application.

An interesting idea is to use this approach as a stack trace tool to find the object you want to manipulate in the debugger.

Think about that situation for a second. When you stop the debugger out of the blue, you’re almost never on the desired stack frame. Now you can stop the debugger at anytime and have code execute at your desired location. This is very useful if you want to execute code at a point in your app that’s tedious to access from the debugger. Try it out!

Put a breakpoint on the print() statement inside the setupSignalHandlerFor block that you just added.

Pause in the debugger, then start again. The app will hit the breakpoint you added. You’re now deep in the depths of your PhotoCollectionViewController method. Now you can access the instance of PhotoCollectionViewController to your heart’s content. Pretty handy!

Note: If you haven’t already noticed which threads are which in the debugger, take a look at them now. The main thread will always be the first thread followed by libdispatch, the coordinator for GCD, as the second thread. After that, the thread count and remaining threads depend on what the hardware was doing when the app hit the breakpoint.

In the debugger console, type the following:

(lldb) expr object.navigationItem.prompt = "WOOT!"

The Xcode debugger can sometimes be uncooperative. If you get the message:

error: use of unresolved identifier 'self'

Then you have to do it the hard way to work around a bug in LLDB. First take note of the address of objectin the debug area:

(lldb) po object

Then manually cast the value to the type you want:

(lldb) expr let $vc = unsafeBitCast(0x7fbf0af08a10, to: GooglyPuff.PhotoCollectionViewController.self)
(lldb) expr $vc.navigationItem.prompt = "WOOT!"

With this method, you can make updates to the UI, inquire about the properties of a class, and even execute methods — all while not having to restart the app to get into that special workflow state. Pretty neat.

 

GCD(Grand Central Dispatch) Threading for Swift 3

Higher Order Functions  Map, filter, reduce and flatMap in Swift 3.0

Map

Loops over a collection and applies the same operation to each element in the collection.

Map

Filter

Loops over a collection and returns an array that contains elements that meet a condition.

Filter

Reduce

Combines all items in a collection to create a single value.

Reduce

FlatMap

When implemented on sequences : Flattens a collection of collections.

FlatMap

Higher Order Functions  Map, filter, reduce and flatMap in Swift 3.0