Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Start a free Basic trial
to watch this video
The first step to getting the user's location is making a location request. Like permission requests, a location request can fail as well. In this video, we'll start by covering our bases for a failure case
-
0:00
We've successfully asked the user for permission to monitor their location.
-
0:04
But we haven't actually started monitoring their location or
-
0:08
requested a location fix.
-
0:10
With permissions taken care of though,
-
0:12
now we can actually work on the main logic of this component of the app.
-
0:15
There are several ways we can monitor location given our choice of
-
0:19
authorization.
-
0:20
A GPS app, for example,
-
0:22
will constantly monitor the location until you tell it to stop.
-
0:26
If we did this,
-
0:27
every time the user moves to a new location, we are continuously updated.
-
0:31
We want something much simpler.
-
0:33
We just want to know the user's current location and
-
0:35
once we have that fixed, we don't care for any more information.
-
0:39
To do this, we'll simply call the request location method on the location manager.
-
0:45
This method requests the one time delivery of the users location.
-
0:50
Typically, we would need to call start updating location, which is one method,
-
0:54
and then stop updating location, which is its counterpart.
-
0:57
So that we can both begin and end the location request when we need to.
-
1:03
But since the system knows that we only want a single location fix, start and
-
1:08
stop are automatically going to be called for us in the background.
-
1:12
Now remember that we created the location manager, this underlying one,
-
1:16
the CLLocationManager instance as a private object in this class.
-
1:21
So let's add a public method to expose this functionality.
-
1:25
And we'll do that right after the request location authorization method.
-
1:31
So I'll say func requestLocation.
-
1:37
And in here, we'll just say manager.requestLocation.
-
1:41
When we call this method, just like the permission request,
-
1:44
we're not going to get a response immediately.
-
1:46
There is no return value from this function because getting a location fix
-
1:50
is no simple task.
-
1:52
It involves receiving information from a minimum of four different satellites
-
1:57
orbiting the Earth.
-
1:58
And using a bunch of math and physics to find a user's position.
-
2:02
If you're interested in understanding that process at a high level,
-
2:05
you should check out the video linked in the teacher's notes.
-
2:08
It's pretty amazing.
-
2:09
It's actually really amazing because this device that you're holding in your hand is
-
2:13
communicating with some other flying metal object in space.
-
2:18
Okay, because asking for a location fix isn't instantaneously executed,
-
2:23
we're again going to be dealing with callbacks through delegate methods.
-
2:27
One of the reasons the response time varies is because we
-
2:30
can specify the accuracy of a location fix.
-
2:34
Location managers have a property named desired accuracy.
-
2:37
So for example, I can type manager.desiredAccuracy here.
-
2:41
And this takes a value of type CLLocation accuracy.
-
2:46
By default the setting is kCLLocationAccuracyBest,
-
2:52
which is the highest level of accuracy.
-
2:54
But you can see from the values here that you can also set it to a value like
-
2:58
a single kilometer, three kilometers, a hundred meters, or
-
3:01
even the nearest ten meters.
-
3:03
This means that if the core location framework gets a location fix
-
3:07
to the value you that specified.
-
3:09
So let's say three kilometers away from you,
-
3:11
it will accept that as the current location.
-
3:14
But if you have a higher accuracy setting, it's going to keep searching.
-
3:17
So the higher the accuracy, the longer it takes to get a location fix.
-
3:20
And obviously, the more battery life it consumes on your phone.
-
3:24
We're gonna get rid of this, because we're gonna stick to the default,
-
3:26
which is that best value.
-
3:28
As a somewhat related aside, if you're wondering why
-
3:31
delegates are used in some places for callbacks, and closures in others.
-
3:36
Like URL session, for example, with our networking code, it's mostly for
-
3:40
historic reasons.
-
3:41
All of these underlying classes are written in Objective-C.
-
3:44
Xcode is just bridging things over to swift for us automatically.
-
3:49
What are closures to us, in Swift, are known as blocks in Objective-C.
-
3:53
So Objective-C does have the ability to create units of code that can be
-
3:57
passed around, but this is a relatively recent addition to the language.
-
4:02
Objective-C was developed in the early 1980s.
-
4:05
Objective-C 2.0 was released in 2006.
-
4:10
So compare that to Swift, where we got Swift 1.0 in 2014 and 2.0 in 2015.
-
4:16
So here we have Objective-C 2.0 coming out in 2006.
-
4:19
And then in 2010, blocks where introduced as a language feature.
-
4:24
Essentially, when writing a lot of these classes,
-
4:27
Apple's developers could only implement callbacks via delegates.
-
4:31
Newer classes like URL session, or NsURL session,
-
4:35
as the actual Objective-C class is named, were written in a post-block world.
-
4:40
And therefore, could use completion handlers for better encapsulation and for
-
4:44
a different pattern with callbacks.
-
4:46
CLLocationManager, on the other hand, was written prior to that, so
-
4:49
we use delegates.
-
4:51
To get the location information back, we need to implement a few delegate methods,
-
4:55
which we'll do inside the locationManager class.
-
4:59
The first delegate method we need to implement is locationManager
-
5:03
didFailWithError.
-
5:05
locationManager didFailWithError is called if we ask for
-
5:09
a location fix, but for some reason or another, cannot obtain one.
-
5:13
Now this method is marked as optional, but if your location request does fail.
-
5:19
And there's no implementation for this method, the app is going to crash.
-
5:23
So let's go ahead and provide an implementation.
-
5:25
Now, when we were working on the permissions portion, just a video or
-
5:29
two ago, we decided that we didn't want to handle the delegate
-
5:33
events inside this class, locationManager.
-
5:36
Instead, we handled the events in the same object that requested permission.
-
5:40
Or in this case,
-
5:41
we want to handle the events in the same object that requested the location.
-
5:46
So just like we did with the permission stuff, we'll use the delegate pattern
-
5:49
here as well to kinda defer handling these events and
-
5:51
communicate it on to another object.
-
5:54
We could simply stuff these additional events
-
5:57
into the LocationPermissionsDelegate protocol we defined earlier.
-
6:00
But that wouldn't make much sense.
-
6:03
Requesting permission to use location and
-
6:05
then using the location itself are two different bits of functionality.
-
6:09
We don't want the same delegate to communicate permission and
-
6:12
location events.
-
6:13
Because then our permissions controller would have to
-
6:16
implement the location-based methods, even though it will never use them.
-
6:20
Now in Objective-C, the pattern is to include optional protocol requirements.
-
6:25
So you generally tend to see these large protocols, like UITableViewDataSource or
-
6:29
UITableViewDelegate that have a bunch of requirements where you choose what
-
6:33
you want to implement.
-
6:36
Since we can't do that in Swift, we're going to create smaller,
-
6:39
focused delegate protocols.
-
6:40
And ultimately, that's better than the Objective-C approach.
-
6:44
You know exactly what this delegate protocol is doing.
-
6:48
So let's create a new protocol, and we'll name this LocationManagerDelegate.
-
6:54
Again, this is going to be class bound.
-
6:56
And the reason for that is we want to set it to a weak property, which we
-
7:00
can't do if this is not class bound because we could assign a struct there.
-
7:04
Okay, the protocol is going to communicate a success event and
-
7:08
provide the location obtained.
-
7:10
So here, we'll say func obtainedCoordinates,
-
7:15
coordinate: Coordinate.
-
7:19
Now core location defines a location as a CLLocation object.
-
7:23
But we're going to return a coordinate value to keep it consistent
-
7:26
with the rest of the API.
-
7:28
We'll also communicate a failure event, so func failedWithError.
-
7:35
And we'll pass along an error value from the type we defined earlier,
-
7:39
which is LocationError.
-
7:43
Okay, next we need to add a delegate stored properties.
-
7:46
So we'll say a weak var delegate, LocationManagerDelegate.
-
7:53
Make it optional and then we'll make a slight modification to the init method so
-
7:57
that we can pass the delegate in on initialization.
-
8:01
So delegate LocationManagerDelegate, and
-
8:06
then inside just like we did earlier, we'll say self.delegate = delegate.
-
8:12
So far so good.
-
8:14
Now again, this is going to raise an error in the permissions controller where
-
8:17
we're creating an instance of the type.
-
8:20
So really quick, let's jump to the Permissions Controller.
-
8:23
And over here, under the creation of our LocationManager object,
-
8:27
we'll specify the delegate parameter, and we'll pass in nil.
-
8:31
And then, this way, we can easily say that, hey,
-
8:34
this class is not going to act as a delegate for the location update events.
-
8:38
But just for the permission stuff.
-
8:40
Now, back in the CLLocationManagerDelegate method, which we added at the bottom.
-
8:47
So that's this first one, DidFailWithError.
-
8:50
We need to respond to this.
-
8:52
Now, you'll notice here that this is a generic error that we're getting
-
8:55
through and not a more specific error type.
-
8:58
If you check the core location framework reference however, you'll see that there
-
9:02
is indeed a more specific error type when working with location.
-
9:05
And that's the type CLError.
-
9:07
So what we're going to do here is, we'll try and
-
9:10
cast this generic error to CLError first.
-
9:13
So guard let error = error as?
-
9:17
CLError else, if this doesn't work, we'll call our delegate.
-
9:23
And just for the sake of ease, we'll say we have no idea what's going on here
-
9:28
because we really only expect a CLError back.
-
9:30
I mean, we could get some other type of error, but let's keep this course easy.
-
9:36
So here we'll say this is an unknown error.
-
9:38
And then, because this is guard statement, we need to return.
-
9:42
Now, in the event that we do have a CLError object, we'll use the error code
-
9:47
that's defined on the type to communicate more specific error through our delegate.
-
9:51
So we'll switch on the errors.code property.
-
9:55
And despite being named code, this property actually returns enum values
-
9:59
defined for each code, and we can match against those.
-
10:03
So here we'll say, if the error has a value, locationUnknown,
-
10:08
or a network error, then we'll tell the delegate that we
-
10:13
failed because we were unableToFindLocation.
-
10:17
If the reason for failing it is because we attempted to get the location despite
-
10:21
the user saying no, we'll communicate that as well.
-
10:25
So I'll say case.denied deligate.failwithError.disallowedbyUser.
-
10:35
Now, I don't expect to reach this because we won't be
-
10:38
calling request location if we failed with error, but just in case, who knows?
-
10:43
Now, for the rest of the error codes, we'll just use a default statement and
-
10:47
do nothing, purely because this is just a tutorial, folks.
-
10:53
Now, if you're using any of this code in a real app,
-
10:56
definitely make this more robust and cover all your error cases.
-
10:59
Let's take a break here.
-
11:01
In the next video, we'll handle the success case.
You need to sign up for Treehouse in order to download course files.
Sign up