Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[ios] Mapbox's Location Manager new API. #12013

Merged
merged 23 commits into from Jul 20, 2018

Conversation

fabian-guerra
Copy link
Contributor

@fabian-guerra fabian-guerra commented May 25, 2018

This PR fixes #11983 and adds a new API to implement a custom location manager.

This API follows Apple's rule that is possible to have only one location manager at a given time, but opens the option to have different technologies that provides location updates such as WIFI or iBeacons.

  • Create MGLLocationManager and MGLLocationManagerDelegate.
  • Change MGLMapView to use the new API.
  • Add tests.
    • Memory usage.
    • Course following.
    • Single LM multiple usages.
  • Document how to use the API and make an example.


@optional

@end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest removing the usage of all CoreLocation types (including CLAuthorizationStatus , etc. — so that you can remove the CoreLocation import above). This has the following reasons:

  • Some of them are difficult to initialize. CLHeading has no public initializer so that I was forced to create a subclass where I was overriding the trueHeading property. This was hacky and not obvious.
  • You can only reuqest the values you really need. Instead of CLHeading cou can ask for the trueHeading:(double):trueHeading. This makes the API easier to integrate and simulate.
  • You are getting completely independent of the CoreLocation framework

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @simonseyer. Thank you for your feedback, and agree with you. When I was designing the API I consider removing completely the dependency on CoreLocation (provide our version of CLLocation, CLLocationCoordinate2D,CLHeading, etc). Although making that change will require a new semver, and migrating code.

I will follow up with a ticket related to remove CoreLocation dependency.

@fabian-guerra fabian-guerra changed the base branch from master to release-chai June 13, 2018 15:29
@required

/**
The delegate to recive updates.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: recive -> receive


- (void)setHeadingOrientation:(CLDeviceOrientation)headingOrientation
{
_locationManager.headingOrientation = headingOrientation;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not self.locationManager... here?

self.locationManager = [[CLLocationManager alloc] init];
// If no custom location manager is provided will use the internal implementation.
if (!self.locationManager) {
self.locationManager = [[MGLCLLocationManager alloc] init];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will call -validateLocationServices again, won't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect :) Looks like I was commenting on an earlier version.

@julianrex
Copy link
Contributor

Just picking up from @simonseyer's comment (#12013 (comment)) about removing CoreLocation references (I'm assuming here that you mean only removing CoreLocation in terms of the location manager, not from the wider SDK).

This should be our ideal goal, so

  • what code would need to change that requires a major version change?
  • if we can't go straight to a CoreLocation-less option, what are our options to help get us there?
  • for example, should we consider typedefs or subclassing?

In addition, I wonder if it's worth discussing if and how we could improve the user-authorization dance that developers have to go through.

@fabian-guerra
Copy link
Contributor Author

about removing CoreLocation references (I'm assuming here that you mean only removing CoreLocation in terms of the location manager, not from the wider SDK).

I meant the wider SDK.

what code would need to change that requires a major version change?

If we replace CLXXXX with our own versions — MGLLocation, MGLHeading, MGLLocationCoordinate — will break the current apps and will require a simple(?) migration.

if we can't go straight to a CoreLocation-less option, what are our options to help get us there?

We may not want to go full without CL but we can replace some classes/structs/enums that makes sense in the line of allowing customization — MGLHeading? MGLAuthorizationStatus? etc —

for example, should we consider typedefs or subclassing?

As I outlined above. I am thinking redefinition.

In addition, I wonder if it's worth discussing if and how we could improve the user-authorization dance that developers have to go through.

We can make a ticket to follow along the discussion just as I am waiting to release this to followup with a ticket for going full and replace CL.

@julianrex
Copy link
Contributor

We may not want to go full without CL but we can replace some classes/structs/enums that makes sense in the line of allowing customization — MGLHeading? MGLAuthorizationStatus? etc —

As I outlined above. I am thinking redefinition.

Sounds good. Protocols for the classes (e.g CLLocation), re-def for the structs (oh where are you Swift), and some lightweight "conversions".

If we drop the CL dependency for this location manager protocol - is it worth considering tweaking the interface?

@fabian-guerra
Copy link
Contributor Author

I performed an allocations and time profiler test iterating over the options in

typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) {

I used an iPhone X iOS 11.4.

Allocations.

CLLocationManager (used master as benchmark)

nlm_memory

MGLCLLocationManager (Internal API).

mgllm_memory

The internal implementation added 32 bytes to the memory growth.

Time Profiler.

CLLocationManager (used master as benchmark)

cl_execution

MGLCLLocationManager (Internal API).

mgllm_execution

It was almost the same time of execution. I performed more tests with 1-2 s deltas. I think is due to the inconsistent location updates I get at our San Francisco office.

@fabian-guerra fabian-guerra changed the base branch from release-chai to master June 28, 2018 22:52
Copy link
Contributor

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR takes a first step towards making the map view’s built-in location-related features more customizable, which is a laudable goal.

However, I’m uneasy with the idea of eliminating our dependency on Core Location. While doing so could be a boon for a relative few developers with specialized needs, it could potentially complicate most developers’ ability to use the SDK. That would be akin to Apple’s new Natural Language framework shipping with a custom NLString type that you have to use instead of String.

Even with just this more limited refactoring of CLLocationManager usage, I think we should give a lot of thought to the tradeoffs involved and whether the use cases we’d be unblocking would be worth the trouble we’d cause the general user base. Consider the impact these changes will have on someone migrating from MapKit or the Google Maps SDK.

What do these changes mean for MGLMapViewDelegate’s location-related methods? Do we need to document any changes there?

What do these changes mean for telemetry? Would it be problematic if the map view uses one location manager while the telemetry subsystem uses another? On the flip side, would it be problematic to allow the developer to override the location manager used for telemetry?

Remember to update the iOS changelog to note the addition of MGLMapView.locationManager.


@interface MGLCLLocationManager()<CLLocationManagerDelegate>

@property (nonatomic) CLLocationManager *locationManager;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why create a default location manager that owns a CLLocationManager? Why not extend CLLocationManager to conform to MGLLocationManager?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Te reason for MGLCLLocationManager to implement MGLLocationManager is that MGLMapView uses the latter for handling authorization/start/stop events and breaks the dependency on CLLocationManager.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suppose we instead declare a category on CLLocationManager that conforms to MGLLocationManager. Wouldn’t that accomplish the same thing as this class, except with less indirection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I changed the implementation accordingly.

@@ -184,7 +185,7 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {

@interface MGLMapView () <UIGestureRecognizerDelegate,
GLKViewDelegate,
CLLocationManagerDelegate,
MGLLocationManagerDelegate,
Copy link
Contributor

@1ec5 1ec5 Jun 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the navigation SDK currently depends on MGLMapView conforming to CLLocationManagerDelegate. I’m unsure whether the navigation SDK still takes advantage of that assumption following mapbox/mapbox-navigation-ios#402.

/cc @bsudekum @frederoni

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The navigation SDK’s redeclaration of this conformance was vestigial: mapbox/mapbox-navigation-ios#1549.

events.

If no custom manager is provided or setting this property to `nil` will default
to the internal `CLLocationManager` based implementation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the default use of CLLocationManager is an internal implementation detail, then we shouldn’t mention it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we communicate this behavior? We should let devs know that this is a specialized API and if nothing is provided then the default option is used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can certainly say that a typical implementation comes with MGLMapView by default, but if the goal is to reduce dependency on CLLocationManager, then we need to couple any mention of CLLocationManager in the documentation with a note that the default location manager may change in the future.

- (void)setLocationManager:(id<MGLLocationManager>)locationManager
{
_locationManager = locationManager;
_locationManager.delegate = self;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation comment for this property states that setting the property to nil causes the map view to use the CLLocationManager implementation. Does that happen immediately, or upon the next location update?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the location manager is set to nil is going to use the default option the next time the showsUserLocation is set to yes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So MGLMapView would continue to serve as the old location manager’s delegate until then, without stopping that location manager? That could result in MGLMapView and MGLMapViewDelegate responding to doubled-up or contradictory location updates.


Set the custom location manager before calling `showUserLocation`.
*/
@property (nonatomic, nullable) id<MGLLocationManager> locationManager;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the developer sets this property to nil or leaves it as nil, should the getter return nil or should it return the default implementation? If the latter, then the property should be null_resettable instead of nullable.

Most of the time we’ve received requests to expose the location manager, it wasn’t because the developer wanted to replace the location manager with their own implementation. Rather, it was because they wanted to tweak the built-in location manager’s settings. For that to be possible, this property would always need to return a non-nil value.

@fabian-guerra
Copy link
Contributor Author

@1ec5

However, I’m uneasy with the idea of eliminating our dependency on Core Location. While doing so could be a boon for a relative few developers with specialized needs, it could potentially complicate most developers’ ability to use the SDK. That would be akin to Apple’s new Natural Language framework shipping with a custom NLString type that you have to use instead of String.

I understand your worries. Reducing our direct dependency on Core Location may bring new problems. In one of my previous comments I stated that we may not replace entirely our dependency but that is up to a conscious analysis. I also don't believe that radical changes are good for devs but I do believe on evolutionary changes. Enabling greater customization will require some evolutionary changes. It's familiar for devs to remove prefixes from Classes and what would be the worst scenario besides renaming CLLocation to MGLLocation? — please help me understand —

Even with just this more limited refactoring of CLLocationManager usage, I think we should give a lot of thought to the tradeoffs involved and whether the use cases we’d be unblocking would be worth the trouble we’d cause the general user base. Consider the impact these changes will have on someone migrating from MapKit or the Google Maps SDK.

What kind of problems do you think it may arise? MGLLocationManager and MGLLocationManagerDelegate are thought for customers with different location providers needs. We do provide a default implementation for the ones that doesn't care about location.

What do these changes mean for MGLMapViewDelegate’s location-related methods? Do we need to document any changes there?

This does not affect MGLMapViewDelegate beyond the calling stack.

What do these changes mean for telemetry? Would it be problematic if the map view uses one location manager while the telemetry subsystem uses another? On the flip side, would it be problematic to allow the developer to override the location manager used for telemetry?

I don't have a clear answer for this. The telemetry parts that are used inside MGLMapView remind unaffected but I will sync with @rclee and @bsudekum to determine the impact of this change.

@1ec5
Copy link
Contributor

1ec5 commented Jun 30, 2018

It's familiar for devs to remove prefixes from Classes and what would be the worst scenario besides renaming CLLocation to MGLLocation? — please help me understand —

That change alone is benign, given that we don’t currently use CLLocation anywhere in the public API. I was responding to the suggestion that we remove the dependency on Core Location, which would require replacing important types like CLLocationCoordinate2D and CLLocationDirection with types specific to this SDK. Unlike on Android, our various iOS libraries don’t share a common dependency that vends core types the way the Mapbox Java SDK does: mapbox/mapbox-events-ios#1.

What kind of problems do you think it may arise? MGLLocationManager and MGLLocationManagerDelegate are thought for customers with different location providers needs. We do provide a default implementation for the ones that doesn't care about location.

My understanding is that the set of developers who’d need to use this SDK with a different location manager is a relatively small subset. They may be an important subset, but perhaps that’s a discussion to have elsewhere. Most developers would at most need to configure a few CLLocation parameters rather than creating their own. I think making the locationManager property null-resettable could address that need without unnecessary complexity.

@fabian-guerra fabian-guerra force-pushed the fabian-location-manager-11983 branch from 5af4080 to cc4a2b2 Compare July 3, 2018 22:07
@fabian-guerra fabian-guerra force-pushed the fabian-location-manager-11983 branch 2 times, most recently from 6221531 to 38306ba Compare July 11, 2018 22:31
@fabian-guerra fabian-guerra added feature needs changelog Indicates PR needs a changelog entry prior to merging. labels Jul 11, 2018
@friedbunny friedbunny removed the needs changelog Indicates PR needs a changelog entry prior to merging. label Jul 20, 2018
@fabian-guerra fabian-guerra merged commit 681e014 into master Jul 20, 2018
@fabian-guerra fabian-guerra deleted the fabian-location-manager-11983 branch July 20, 2018 21:14
@friedbunny friedbunny added this to the ios-v4.3.0 milestone Jul 26, 2018
jmkiley added a commit that referenced this pull request Aug 14, 2018
* [ios] Cherry-pick 12013

* [ios] Updated Changelog.md

* [ios] Fix MBXCustomLocationViewController iOS 8 compatibility.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature iOS Mapbox Maps SDK for iOS
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MGLMapView's location manager should be configurable.
5 participants