Saving and Loading Objects From Disk Memory

Saving and Loading List Items

Even though in the previous part we built the basic functionality of the demo app, we still must add two more important features so it works as expected. This means that we must make it capable of saving the shopping list permanently to the disk, and also load the list from the disk (if exists) when the application gets launched.

As the structure of our data is a mutable array, we can easily write and read to and from the disk, using the methods that the NSMutableArray class provides. Obviously, for doing both of the above, we are going to create two separate functions. So, let’s get started with the next one, which is responsible for saving the contents of the array to a file. The file is named shopping_list:

The first two lines consist of a standard piece of code that returns the path to the documents directory of the app. Once we know that, we form the path to the file, specifying the file name at the same time. Lastly, the key to the above function is the writeToFile(_:atomically:) method of the NSMutableArray class. This is the one that actually saves the array contents to the disk.

By having the above method implemented, we can go ahead and call it where it’s needed. If you think of what we did in the previous part, you can assume that we are going to call it in two places: When a new item is added to the list, and when an existing item is removed from the list.

At first, go to the textFieldShouldReturn(textField:) delegate method of the textfield. Just right before the return command, make a call to the above method:

Nice, each new item now will be saved to the disk too. Also, let’s pay a visit to the removeItemAtIndex(index:) function too, where we actually delete an item. As we just did, let’s make a call to the save function and let’s add it as the last command as well:

With all the above we can be sure that any changes that might take place in our data, they will become permanent too.

Now, we can proceed by implementing the exact opposite functionality, the data loading. At first, let’s see the definition of the respective function:

It’s important to highlight the fact that we must always check if a file exists before we load its contents. In iOS, we do that by using the NSFileManager class exactly as shown above. If the file doesn’t exists and the condition returns false, nothing will happen at all. On the other hand, if the file exists, we initialise the shoppingList array with the contents of the file, and then we reload the tableview so it displays the loaded data.

As a last step, we must call this function. As we want the data to be loaded right after the app has been launched, we’ll do that in the viewDidLoad method:

The two functionalities we added to the application in this part, will become really helpful later, when we’ll handle the notification actions without running the app on the foreground.

 

Advertisements
Saving and Loading Objects From Disk Memory

Creating Interactive Notifications in iOS 8

Creating Notification Actions

Several times up to this point I talked about notification actions, but always generally speaking. Now, it’s time to see what they really are.

An action is an object of the UIMutableUserNotificationAction class. This class is new in iOS 8, and provides various useful properties so an action can be properly configured. These are:

  • identifier: This is a string value, that uniquely identifies an action among all in an application. Obviously, you should never define two or more actions with the same identifier. Also, using this property we’ll be able to determine the chosen action by the user upon the notification appearance. We’ll see that later.
  • title: The title is the text that is shown on the action button to the user. This can be either a simple, or a localized string. Be cautious and always set proper titles to your actions, so the user can instantly understand by reading the 1-2 title words what is going to happen by selecting it.
  • destructive: This is a bool value. When it is set to true the respective button in the notification has red background colour. Note that this happens in the banner mode only. Usually, actions regarding deletion, removal and anything else critical are marked as destructive, so they increase the user’s attention.
  • authenticationRequired: This property is a bool value also. When it becomes true, the user must necessarily authenticate himself to the device before the action is performed. It’s extremely useful in cases where the action is critical enough, and any unauthorised access can damage the application’s data.
  • activationMode: This is an enum property, and defines whether the app should run in the foreground or in the background when the action is performed. The possible values specifying each mode are two: (a) UIUserNotificationActivationModeForeground, and (b) UIUserNotificationActivationModeBackground. In background, the app is given just a few seconds to perform the action.

While I was describing this demo application, I said that we’re going to create three distinct actions:

  1. An action to just make the notification go away, without anything else to be performed.
  2. An action to edit the list (actually to add a new item).
  3. An action to delete the entire list.

Let’s see how each one is written in code. For every action, we will set the values to all the aforementioned properties. The first one:

The identifier of this action is “justInform”. As you see, this action will be performed in the background, and as there’s nothing dangerous with it we set to false the destructive and the authenticationRequired properties.

The next one:

Apparently, this action requires the app to run in the foreground so we can edit the shopping list. Also, we don’t want nobody else to mess with our list, so we set the authenticationRequired to true.

And the third and last one:

With this action we’ll allow the user to delete the entire shopping list without launching the application in the foreground. However, this is a dangerous action for the data, therefore we tell the app that it’s destructive and that authentication is required to proceed.

By looking at the above actions setup, you understand that configuring them is an easy task.

Now that we have done all the above, let’s add them to the setupNotificationSettings function we started to implement in the previous part:

Action Categories

When the actions of a notification have been defined, they can then be grouped together in categories. Actually, categories are what is written in the settings, and not the actions themselves, so you should always create them, unless of course you present a notification without actions. Usually, a category matches to a notification, so supposing that all notifications in an application support actions, there should be as many categories as the notifications.

In this demo application, we are going to create just one notification, so we are going to have one category only. Programmatically speaking now, a category is an object of the UIMutableUserNotificationCategory class, which is also new in iOS 8. This class has just one property and one method. The property is a string identifier that uniquely identifies a category (similarly to actions), and the method is used to group the actions together.

Let’s see a few more things about that method, and let’s start by its signature (taken by the Apple documentation directly):

The first parameter regards the actions that should be grouped for the category. It’s an array containing all the actions, and the order of the of the action objects in the array specifies the order they will appear in the notification.

The second parameter is quite important. The context is an enum type, and describes the context of the alert that the notification will appear into. There are two possible values:

  1. UIUserNotificationActionContextDefault: It matches to a full alert that is appeared to the centre of the screen (when the device is unlocked).
  2. UIUserNotificationActionContextMinimal: It matches to a banner alert.

In the default context, the category can accept up to four actions, which will be displayed in the predefined order in the notification alert (at the screen centre). In the minimal context, up to two actions can be set to appear in the banner alert. Notice that in the second case, you must choose what the most important actions are among all so they appear in the banner notification. In our implementation we’ll specify the actions for both contexts.

As I said, the first parameter in the above method must be an array. For this reason, our initial step is to create two arrays with the actions for each context. Let’s go back to our function:

Next, let’s create a new category for which we will set an identifier, and then we will provide the above arrays for the two contexts:

And… that’s all it takes to create a category regarding the actions of a notification.

Registering Notification Settings

In the last three parts we configured all the new features of the local notification, and now we have only left to write everything to settings. For this purpose, we will use the UIUserNotificationSettings class (new in iOS 8), and through the following init method we’ll provide the types and the category of the notification:

The first parameter is the types we defined for the notification. The second parameter is a NSSet object, in which all the categories for all the existing notifications in an application must be set. In this example, we have just one category, however we’ll create a NSSet object no matter what.

Let’s continue the function implementation with that:

Now, we can create a new object of the UIUserNotificationSettings class and pass the required arguments:

Lastly, let’s write (register) the settings in the Settings app using the next line:

The first time the above code will work, it will create a new record for our application in the Settings app.

Now, before I present you the whole setupNotificationSettings() after all the previous additions, let me say something more. This function will be called in the viewDidLoad method, and that means that its content will be executed every time the app runs. However, as the notification settings are not going to change and therefore it’s pointless to set them again and again, it would be wise to contain all the above code in an if statement. In the condition of this statement, we will check if the notification types have been specified or not, where in the second case the if body will be executed. This is translated to code as shown next:

At first, we get the existing notification settings using the currentUserNotificationSettings() method of the UIApplication class. This method returns a settings object, through which we can check the current value of the types property. Remember that this property is an enum type. If its value equals to None, meaning that no notification types have been set, then we’ll allow it to register the notification settings by doing all the above in the previous parts, otherwise will do nothing.

With the above condition we avoid unneeded settings registration. Note though that if you want to modify any settings or add more actions, categories, etc for new notifications, you should comment out the if opening and closing lines and run the app once, so the new additions to be applied and so you can test it with the new settings. Then, remove the comments to revert your code to its previous state.

With that, all the work in the notification details is over. Right next you can see the setupNotificationSettings() function fully implemented and in one part:

Don’t forget to call this function in the viewDidLoad body:

Handling Notification Actions

There is one last aspect of the local notification that we haven’t worked with yet, and that is to handle the actions that get received by the application when the user taps on the respective buttons. As usual, there are certain delegate methods that should be implemented.

Before we see what actions we’ll handle and how, let me introduce you a couple of other delegate methods that can become handy when developing your own applications. Note that in this demo we won’t really use them; we’ll just print a message to the console. Also, make sure to open the AppDelegate.swift file now.

So, the first one regards the notification settings. This delegate method is called when the app is launched (either normally or due to a local notification’s action) and contains all the settings configured for the notifications of your app. Right next you can see its definition. What we only do, is to print the current notification types:

Regardless of the above simple implementation, you can use it to access all kind of settings supported by the UIUserNotificationSettings class. This method can become useful in cases you need to check the existing settings and act depending on the values that will come up. Don’t forget that users can change these settings through the Settings app, so do never be confident that the initial configuration made in code is always valid.

When scheduling a local notification, this will be fired no matter whether your application is currently running or not. Usually, developers imagine how the notifications are going to appear when the app is in the background or suspended, and all the implementation is focused on that. However, it’s your duty to handle a notification in case it will be fired when the app is running. Thankfully, iOS SKD makes things pretty straightforward, as there’s another delegate method that is called when the app is in the foreground:

There will be cases where you won’t need to handle the notification if your app is running. However, in the opposite scenario, the above method is the place to deal with it and perform the proper actions.

Now let’s focus on the delegate method that is called when an action button is tapped by the user. Based on the identifier values we set to the actions, we’ll determine which one has been performed, and then we will make the app execute the proper part of code. We configured three actions in total:

  1. One to make the notification simply go away (identifier: justInform).
  2. One to add a new item to the list (identifier: editList).
  3. One to delete entire the list (identifier: trashAction).

From all the above, we don’t need to do anything when the first action is performed. However, we’ll handle the other two. Depending on the identifier value, we will post a notification (NSNotification) for each one, and then in the ViewController class we’ll observe for those notifications, and of course, we’ll handle them.

Let’s get started with the new delegate method:

In both cases we post a notification (a NSNotification), specifying a different name each time. Notice that at the end we call the completion handler, and that’s mandatory to do, so the system knows we handled the received action. This is the most important delegate method when working with local notifications, as this is the place where you’ll route your code depending on the action the user performed.

Now, let’s go back to the ViewController.swift file. There, let’s make that class observe for the notifications we set before. In the viewDidLoad method add the next lines:

The modifyListNotification() and deleteListNotification() are both custom functions that we’ll implement right next.

Let’s begin with the first one. What we want is to make the textfield the first responder when the app is launched due to the edit list action, so we’re just talking about one line of code only:

With this, the keyboard will automatically be shown and the user will be able to type a new item immediately.

For the delete list action, we want to remove all existing objects from the array that contains them. So, we’ll do exactly that, and then we’ll save the (empty) array back to file. Lastly, we’ll reload the tableview data, so when the user launches the app will find no items at all:

With the above implemented, our demo application is finally ready!

Creating Interactive Notifications in iOS 8

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

Cocoa Touch Static Library vs. Cocoa Touch Framework

At AddThis we have the best geeks on the planet, and it’s our duty to share their knowledge and passion with the world. From time to time we hand the reins over to one of our teammates in a series we call, Talk Nerdy to Me.

Principal Software Engineer Kirk Elliott’s first post, 7 Things to Consider When Making iOS and Android Apps with Cordova or PhoneGap, got a lot of love, so we’re excited to have him back. Kirk enjoys DJing in the Baltimore Club Music scene, tidy code frameworks, and definitely being called Cappin’ Kirk by his co-workers.

  • Should you use a Framework or a Static Library?
  • Can you use Swift or Objective-C?
  • How can you export your final builds?
  • How will you provide instructions to use your library in a manner that will be easy to understand?

This post will attempt to answer these questions based on your project’s concerns and provide additional information and resources to make your static library or framework project go smoothly.

Know Your Supported Platforms

The first thing to know about is the difference between a Cocoa Touch Static Library (aka static library) and a Cocoa Touch Framework (aka framework). From a practical perspective, frameworks are only supported from iOS 8 onwards, whereas static libraries go “all the way back” to iOS 6, which is as far back as you can build an Xcode project using Xcode 7. If you need to support iOS versions before iOS 8, you should consider using a Cocoa Touch Static Library and writing your code in Objective-C.

Be Mindful of Swift Support

The next thing you should be aware of is that Cocoa Touch Static Libraries do not support Swift. So if you decide to use a Cocoa Touch Static Library, you need to use Objective-C. On that topic, Swift is supported back to iOS 7. If you need to support iOS 6 then Swift is definitely “off the table”. If you need to support iOS 7 and you want to use Swift, you will need to provide your source files to be included as raw files in the host app project. Otherwise if you only need to support iOS 8 and onwards, then you can create a Cocoa Touch Framework in Swift.

Plan Out Your Exporting

Platforms
The main thing to consider thus far is the base deployment target of apps that will be using your framework. If you are creating a framework for large scale distribution, you should be aware that many big publishers are still (at the time of this writing) supporting iOS 7 as a base deployment target. Generally the base deployment target is two versions below the latest one, so when iOS 10 is released (likely around 14-09-2016), we can expect the base to shift to iOS 8 for many publishers. At the time of writing this iOS 9 has 84% adoption, iOS 8 has 11%, and earlier versions of iOS comprise the remaining 5%.

Distribution
After you have built your framework, distribution will become your most important concern. Even though you have an Xcode project of type framework or static library, how do you provide your resource to others? Additionally, how do you provide instructions that are simple and easy to understand for the users of your product? If you are going to create a Cocoa Touch Framework, kodmunki has the best guide out there.

If you go with a Cocoa Touch Static Library instead, you should consider the Ray Wenderlich guide. Start with the section titled “Universal Frameworks”. Both of these are excellent guides that I have personally built frameworks from and these have been selected from all of the other pages on the Internet as the best sources have found to date.

In both of these approaches you will want to make sure that your Archive project target Build Settings has Build Active Architecture Only set to NO and Defines Module set to YES. Aside from that you should probably also define a User-Defined Build Setting of “BITCODE_GENERATION_MODE” with a value of “bitcode”, especially if you have bit code-related errors or warnings in your host app. It is not recommended “bitcode”, especially if you have bit code-related errors or warnings in your host app. It is not recommended that you disable bitcode generation in your host app in order to remove errors generated from your framework/static library product.

Prepare For Testing on Simulator

After you build and export your framework, it is important that you are able to include it in a host app for testing. You should be able to build and run on Simulator, physical device, and for “Generic iOS Device” without error. Beyond that you should also be certain that you can Archive your project with the framework included and export the app as well as submit to the App Store without error. I have seen failures occur at various stages in this process without previous error or warning and you should not consider your product complete until all of these boxes have been checked. Cocoa Touch Framework aka dynamic library Swift or Objective-C only supported on iOS 8 and later, fails during app submission if target <= iOS7 compiles to .framework Cocoa Touch Static Library aka static library only Objective-C (Swift is not supported) compiles to .lib supported “all the way down” to iOS 6.

Hopefully these resources will save you time and effort in your next iOS project!

Cocoa Touch Static Library vs. Cocoa Touch Framework

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