Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[google_maps_flutter] Cloud-based map styling support #6553

Closed

Conversation

jokerttu
Copy link
Contributor

@jokerttu jokerttu commented Oct 8, 2022

Adds support for cloud-based map styling.

On Android, cloud-based maps styling works only with the new (opt-in) renderer, and feature to prefer latest renderer via AndroidManifest meta-data field is added as well.
New renderer will be rolled as default renderer in future releases of the Google Maps Android SDK. After that this feature can be still used to prefer legacy renderer, or can be obsoleted/removed from the package.

When using a cloud-based map Id, it can only be set during map initialization. Changing the map Id for an existing map instance is not supported. If the developer wants to change the map Id, this is easy to implement by changing the key of the widget, in which case the widget is re-initialized with a new map Id.

This is first step of the federated plugin multi-package PR process

Resolves flutter/flutter#67631

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the relevant style guides and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/plugins repo does use dart format.)
  • I signed the CLA.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.
  • I updated CHANGELOG.md to add a description of the change, following repository CHANGELOG style.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@jokerttu jokerttu changed the title Google maps flutter map id support [google_maps_flutter] Cloud-based map styling support Oct 8, 2022
@jokerttu
Copy link
Contributor Author

jokerttu commented Oct 8, 2022

Apparently updating the Google Maps iOS SDK to support GMMapID changes the required iOS target platform from version 9 to 12, which means this is a breaking change. I'll turn this PR into a draft for now, to find out how to proceed with this.

@jokerttu jokerttu marked this pull request as draft October 8, 2022 12:08
@stuartmorgan-g
Copy link
Contributor

Apparently updating the Google Maps iOS SDK to support GMMapID changes the required iOS target platform from version 9 to 12, which means this is a breaking change. I'll turn this PR into a draft for now, to find out how to proceed with this.

I can see two options:

  1. Do a breaking change and make iOS 12 the minimum supported version. This would also address the ARM issue (see [google_maps_flutter] Update minimum GoogleMaps dependency to a version that supports ARM simulators flutter#94491)
  2. Look up the new selector dynamically, and call it if present, and then document that using this functionality requires setting your app's minimum iOS version to 12. This would be uglier, but would let us keep support for older versions of iOS.

@jmagman Which way do you want to go with this?

@jmagman
Copy link
Member

jmagman commented Oct 20, 2022

Apparently updating the Google Maps iOS SDK to support GMMapID changes the required iOS target platform from version 9 to 12, which means this is a breaking change. I'll turn this PR into a draft for now, to find out how to proceed with this.

I can see two options:

  1. Do a breaking change and make iOS 12 the minimum supported version. This would also address the ARM issue (see [google_maps_flutter] Update GoogleMaps dependency to 6.0 flutter#94491)
  2. Look up the new selector dynamically, and call it if present, and then document that using this functionality requires setting your app's minimum iOS version to 12. This would be uglier, but would let us keep support for older versions of iOS.

@jmagman Which way do you want to go with this?

Upping the iOS min version is such a poor experience for the developer using the plugin since we have no helpful Flutter tooling, I think it just fails to compile, or maybe the pod install fails?. Ideally the tool would parse the podspec somehow and tell the developer how to up the minimum version in their Xcode iOS app, or better do it for them automatically, with a warning perhaps. Since we don't have any of that, I prefer the gross hack of 2. 😞

@stuartmorgan-g
Copy link
Contributor

Upping the iOS min version is such a poor experience for the developer using the plugin since we have no helpful Flutter tooling, I think it just fails to compile, or maybe the pod install fails?. Ideally the tool would parse the podspec somehow and tell the developer how to up the minimum version in their Xcode iOS app, or better do it for them automatically, with a warning perhaps.

I filed flutter/flutter#113762

@jokerttu
Copy link
Contributor Author

jokerttu commented Oct 21, 2022

When using runtime checks eg. @available(iOS 12, *) podfile lint test failed with command: pod lib lint ./packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec --configuration=Debug --skip-tests --use-modular-headers --use-libraries

Instead, using compile time #if functionality fixed linting issues, and seems like mapId can now be used when iOS platform 12+ is targeted.
#if defined(__IPHONE_12_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_12_0

Should I upgrade the iOS platform version for example projects, as those are used for integration tests. If platform target version is not 12+, cloud mapId tests are run and passed, but the code block inside the #if -block does not get ever tested.

Also if plugin is marked to be able to be run with iOS platform version 9, can the examples still target version 12, as this mean example projects cannot be run by users with iOS platform version 9 anymore?
Flutter stable now targets iOS platform version 11 by default, is it ok still to have version 12 used in integration tests?

Haven't tested the plugin with iOS platform version <11 as current flutter version forces project to use version 11.

For android, the cloud map id support is available only with LATEST renderer, which is not yet the default renderer for the Google Maps Android plugin. This PR adds also feature to target this latest renderer (and also legacy renderer if ever needed). This is now bundled with the plugin; another option would have been to point users to this documentation showing how to prefer latest renderer, but I see this would have been more complicated option for developers.

@jokerttu jokerttu marked this pull request as ready for review October 21, 2022 21:11
@stuartmorgan-g
Copy link
Contributor

When using runtime checks eg. @available(iOS 12, *) podfile lint test failed with command: pod lib lint ./packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec --configuration=Debug --skip-tests --use-modular-headers --use-libraries

What exactly was the error?

If we can't use @available we can do it the old fashioned way with respondsToSelector:.

For android, the cloud map id support is available only with LATEST renderer, which is not yet the default renderer for the Google Maps Android plugin. This PR adds also feature to target this latest renderer (and also legacy renderer if ever needed). This is now bundled with the plugin; another option would have been to point users to this documentation showing how to prefer latest renderer, but I see this would have been more complicated option for developers.

Adding this is fine; I was actually planning on doing this anyway after recent discussions with the Google Maps team. Could you split that out into a separate PR that we can land first though, to simplify reviews?

@jokerttu
Copy link
Contributor Author

jokerttu commented Oct 24, 2022

When using runtime checks eg. @available(iOS 12, *) podfile lint test failed with command: pod lib lint ./packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec --configuration=Debug --skip-tests --use-modular-headers --use-libraries

What exactly was the error?

If we can't use @available we can do it the old fashioned way with respondsToSelector:.

With simplified example:

if (args[@"options"][@"cloudMapId"] && [GMSMapView respondsToSelector:@selector(mapWithFrame:mapID:camera:)]) {
  //GMSMapID *mapID = [GMSMapID mapIDWithIdentifier:args[@"options"][@"cloudMapId"]];
  mapView = [GMSMapView mapWithFrame:frame mapID:nil camera:camera];
} else {
  mapView = [GMSMapView mapWithFrame:frame camera:camera];
}

Running this command (runned in github actions at darwin-lint_podspecs step):
pod lib lint ./packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec --configuration=Debug --skip-tests --use-modular-headers --use-libraries":

Output:
- ERROR | [iOS] xcodebuild: ./packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m:85:27: error: no known class method for selector 'mapWithFrame:mapID:camera:'

@available is giving similar results.
Compile time check is ugly, but as the GoogleMaps pod version is selected before compiling the project, this feature will only be available at runtime if project is targeted to iOS 12+

So how to bypass the pod lib lint error for unknown identifiers with runtime checks? Maybe using even older style NSClassFromString and dynamic selectors with NSSelectorFromString but this sounds even more hacky solution.

For android, the cloud map id support is available only with LATEST renderer, which is not yet the default renderer for the Google Maps Android plugin. This PR adds also feature to target this latest renderer (and also legacy renderer if ever needed). This is now bundled with the plugin; another option would have been to point users to this documentation showing how to prefer latest renderer, but I see this would have been more complicated option for developers.

Adding this is fine; I was actually planning on doing this anyway after recent discussions with the Google Maps team. Could you split that out into a separate PR that we can land first though, to simplify reviews?

Is there already issue for this?
If not, I can create one and the separate PR for preferred renderer selection tomorrow.

@stuartmorgan-g
Copy link
Contributor

stuartmorgan-g commented Oct 25, 2022

this feature will only be available at runtime if project is targeted to iOS 12+

🤦🏻 Sorry, right; this is why I had suggested dynamic lookup initially, then I forgot over the weekend.

If the only downside to #if vs dynamic lookup is example support, I think going that route and making the example 12+ is probably fine. We've talked about adding iOS 11 CI testing, but it's not something we currently do. (We could always switch to dynamic lookup later if we do add that testing.) @jmagman thoughts?

@stuartmorgan-g
Copy link
Contributor

stuartmorgan-g commented Oct 25, 2022

Is there already issue for this? If not, I can create one and the separate PR for preferred renderer selection tomorrow.

Not yet, no.

@jokerttu
Copy link
Contributor Author

jokerttu commented Oct 25, 2022

Adding this is fine; I was actually planning on doing this anyway after recent discussions with the Google Maps team. Could you split that out into a separate PR that we can land first though, to simplify reviews?

Separate PR for prefer map renderer for Android: #6619

@jokerttu
Copy link
Contributor Author

Marking as draft until #6619 is landed and this PR updated to work against it.

@jokerttu jokerttu marked this pull request as draft October 28, 2022 07:51
@jokerttu jokerttu force-pushed the google_maps_flutter_map_id_support branch from c47bb4a to 8086f9d Compare December 8, 2022 09:19
@jokerttu jokerttu marked this pull request as ready for review December 8, 2022 09:51
@jokerttu
Copy link
Contributor Author

jokerttu commented Dec 8, 2022

Marking as draft until #6619 is landed and this PR updated to work against it.

PR is now updated and implements map renderer selection for android using existing api.

@@ -17,6 +17,7 @@ const LatLng _kInitialMapCenter = LatLng(0, 0);
const double _kInitialZoomLevel = 5;
const CameraPosition _kInitialCameraPosition =
CameraPosition(target: _kInitialMapCenter, zoom: _kInitialZoomLevel);
const String _kCloudMapId = '8e0a97af9386fef';
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure what we should do with the testing map Id, since it is associated with the cloud account. We might need to inject this from ci?

@stuartmorgan

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not familiar with the details of our current testing setup for maps; do we have an account already for this? Or does this mean we need to set up and manage a new testing account, and associate our example app with that account? Is this directly connected to the API key, or orthogonal?

Copy link
Contributor

@cyanglaz cyanglaz Jan 30, 2023

Choose a reason for hiding this comment

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

We do have a testing account, whose key is encrypted in https://github.com/flutter/plugins/blob/main/.cirrus.yml#L262

On iOS it is used as

NSString *mapsApiKey = [[NSProcessInfo processInfo] environment][@"MAPS_API_KEY"];

I believe we will need to do something similar here. I think we should add the environment variable in a separate PR, update this PT after the environment variable is available on main.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

_kCloudMapId value does not need to be valid mapId at google maps platform. Google Maps Native SDK's silently fall back to normal map if mapId is not available. This test is meant to test the API and not the SDK behaviour itself. I'll comment this variable, and test if I can change the hash to something that is clearly random id like "0000000000000000", and test that it still passes the tests on each platform.

Copy link
Contributor Author

@jokerttu jokerttu Feb 2, 2023

Choose a reason for hiding this comment

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

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

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

Overall this looks great, just a couple of minor questions. We just need to figure out the testing approach here.

Worst case we could probably just not integration test this, and rely on unit tests on both sides (Dart and native) to ensure that we're plumbing the ID through. That's less robust, but there's also not a lot of code of our own here so it's less critical.

@@ -75,6 +79,34 @@ void main() {
GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) {
mapsImplementation.useAndroidViewSurface = true;
initializeMapRenderer();
Copy link
Contributor

Choose a reason for hiding this comment

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

The README should be updated to explain the requirement here, and this should have a comment referring to that to explain why it's here.

Copy link
Contributor Author

@jokerttu jokerttu Jan 31, 2023

Choose a reason for hiding this comment

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

Map renderers are already explained under google_maps_flutter_android package README.
I can update the app-facing plugin README and add information that cloud-based mapid works only on Android if latest renderer is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jokerttu jokerttu force-pushed the google_maps_flutter_map_id_support branch from fa48a28 to 7e09827 Compare February 1, 2023 12:43
@jokerttu jokerttu requested review from stuartmorgan-g and removed request for ditman and GaryQian February 2, 2023 12:25
@reidbaker reidbaker self-requested a review February 2, 2023 19:38
@stuartmorgan-g
Copy link
Contributor

We've just completed the migration of the plugin code to the flutter/packages repository, as described in https://flutter.dev/go/flutter-plugins-repo-migration, and this repository is now being archived. Unfortunately that means that all in-progress PRs here must be moved to flutter/packages.

Please see our instructions for an explanation of how to move your PR, and if you have any issues moving your PR please don't hesitate to reach out in the #hackers-ecosystem channel in Discord.

Our apologies that your PR was caught in this one-time transition. We're aware that it's disruptive in the short term, and appreciate your help in getting us to a better long-term state!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[google_map_flutter] New Cloud-based map style feature support request
4 participants