NEW: Learn SwiftUI with my free YouTube video series! >>

< Previous: What you learned   Next: Challenge >

Key points

Before you continue to the next milestone, there are two things I’d like to discuss briefly.

First, project 22 introduced Core Location to enable scanning for iBeacons. That’s just one of several things that Core Location does, and I couldn’t possibly continue without at least giving you a taste of the others. For example, Core Location’s functionality includes:

  • Providing co-ordinates for the user’s location at a granularity you specify.
  • Tracking the places the user has visited.
  • Indoor location, even down to what floor a user is on, for locations that have been configured by Apple.
  • Geocoding, which converts co-ordinates to user-friendly names like cities and streets.

Using these things starts with what you have already: modifying the Info.plist to provide a description of how you intend to use location data, then requesting permission. If you intend to use visit tracking you should request the “always” permission because visits are delivered to you in the background.

Once you have permission, try using this to get the user’s location just once, rather than ongoing:

locationManager = CLLocationManager()
manager.delegate = self

    // request the user's coordinates
locationManager.requestLocation()

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    if let location = locations.first {
            print("Found user's location: \(location)")
    }
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    print("Failed to find user's location: \(error.localizedDescription)")
}

You can also request visit monitoring, like this:

// start monitoring visits
locationManager.startMonitoringVisits()

When the user arrives or departs from a location, you’ll get a callback method if you implement it. The method is the same regardless of whether the user arrived or departed at the location, so you need to check the departureDate property to decide.

Here’s an example to get you started:

func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
    if visit.departureDate == Date.distantFuture {
        print("User arrived at location \(visit.coordinate) at time \(visit.arrivalDate)")
    } else {
        print("User departed location \(visit.coordinate) at time \(visit.departureDate)")
    }
}

Note: the definition of a “visit” is pretty vague because iOS can’t tell whether the user has walked into a store or is just standing at a bus stop or sitting in traffic.

The second thing I’d like to discuss further is Swift extensions. These are extraordinarily powerful, because you can extend specific types (e.g. Int and String) but also whole protocols of types (e.g. “all collections”.) Protocol extensions allow us to build up functionality extremely quickly, and using it extensively – a technique known as protocol-oriented programming – is common.

We just wrote several extensions on String, which is what we call a concrete data type – a thing you can actually make. We can write extensions for other concrete types like Int, like this:

extension Int {
    var isOdd: Bool {
        return !self.isMultiple(of: 2)
    }

    var isEven: Bool {
        return self.isMultiple(of: 2)
    }
}

However, that will only extend Int – Swift has a variety of different sizes and types of integers to handle very specific situations. For example, ‌Int8 is a very small integer that holds between -128 and 127, for times when you don’t need much data but space is really restricted. Or there’s UInt64, which holds much larger numbers than a regular Int, but those numbers must always be positive.

Making extensions for whole protocols at once adds our functionality to many places, which in the case of integers means we can add isOdd and isEven to Int, Int8, UInt64, and more by extending the BinaryInteger protocol that covers them all:

extension BinaryInteger {
    var isOdd: Bool {
        return !self.isMultiple(of: 2)
    }

    var isEven: Bool {
        return self.isMultiple(of: 2)
    }
}

However, where things get really interesting is if when we want only a subset of a protocol to be extended. For example, Swift has a Collection protocol that covers arrays, dictionaries, sets, and more, and if we wanted to write a method that counted how many odd and even numbers it held we might start by writing something like this:

extension Collection {
    func countOddEven() -> (odd: Int, even: Int) {
        // start with 0 even and odd
        var even = 0
        var odd = 0

        // go over all values
        for val in self {
            if val.isMultiple(of: 2) {
                // this is even; add one to our even count
                even += 1
            } else {
                // this must be odd; add one to our odd count                
                odd += 1
            }
        }

        // send back our counts as a tuple
        return (odd, even)
    }
}

However, that code won’t work. You see, we’re trying to extend all collections, which means we’re asking Swift to make the method available on arrays like this one:

let names = ["Arya", "Bran", "Rickon", "Robb", "Sansa"]

That array contains strings, and we can’t check whether a string is a multiple of 2 – it just doesn’t make sense.

What we mean to say is “add this method to all collections that contain integers, regardless of that integer type.” To make this work, you need to specify a where clause to filter where the extension is applied: we want this extension only for collections where the elements inside that collection conform to the BinaryInteger protocol.

This is actually surprisingly easy to do – just modify the extension to this:

extension Collection where Element: BinaryInteger {

As you’ll learn, these extension constraints are extraordinarily powerful, particularly when you constrain using a protocol rather than specific type. For example, if you extend Array so that your methods only apply to arrays that hold Comparable objects, the methods in that extension gain access to a whole range of built-in methods such as firstIndex(of:), contains(), sort(), and more – because Swift knows the elements must all conform to Comparable.

If you want to try such a constraint yourself – and trust me, you’ll need it for one of the challenges coming up! – write your extensions like this:

extension Array where Element: Comparable {
    func doStuff(with: Element) {
    }
}

Inside the doStuff() method, Swift will ensure that Element automatically means whatever type of element the array holds.

That’s just a teaser of what’s to come once your Swift skills advance a little further, but I hope you’re starting to see why Swift is called a protocol-oriented programming language – you can extend specific types if you want to, but it’s far more efficient – and powerful! – to extend whole groups of them at once.

SPONSOR Meet the new Instabug – more than just bug reporting! We help you build better apps and minimize your debugging time. With each bug report, we automatically capture details like network requests, repro steps, and session details. Get real-time crash reports with stack trace details and session data to help you catch and fix issues easily. And with our customizable in-app surveys, you’ll gather insightful user feedback and much more. Instabug is the fastest and easiest way to release with confidence. Start your free trial now! Start your free trial now!

< Previous: What you learned   Next: Challenge >
MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Average rating: 5.0/5

Click here to visit the Hacking with Swift store >>