summaryrefslogtreecommitdiff
path: root/platform/macos
diff options
context:
space:
mode:
Diffstat (limited to 'platform/macos')
-rw-r--r--platform/macos/CHANGELOG.md4
-rw-r--r--platform/macos/bitrise.yml58
-rw-r--r--platform/macos/sdk/Base.lproj/Localizable.strings12
-rw-r--r--platform/macos/src/MGLMapView.mm329
4 files changed, 229 insertions, 174 deletions
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 7e3eac9877..152fdc382b 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog for Mapbox macOS SDK
+## master
+
+* The error passed into `-[MGLMapViewDelegate mapViewDidFailLoadingMap:withError:]` now includes a more specific description and failure reason. ([#8418](https://github.com/mapbox/mapbox-gl-native/pull/8418))
+
## 0.4.0
### Internationalization
diff --git a/platform/macos/bitrise.yml b/platform/macos/bitrise.yml
index 2de6bce1fc..1f2495dab2 100644
--- a/platform/macos/bitrise.yml
+++ b/platform/macos/bitrise.yml
@@ -12,59 +12,24 @@ workflows:
primary:
steps:
- script:
- title: Check for skipping CI
- inputs:
- - content: |-
- #!/bin/bash
- if [[ -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_SUBJECT | sed -n '/\[skip ci\]/p')" ||
- -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_SUBJECT | sed -n '/\[ci skip\]/p')" ||
- -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_BODY | sed -n 's/\[skip ci\]/p')" ||
- -n "$(echo $GIT_CLONE_COMMIT_MESSAGE_BODY | sed -n 's/\[ci skip\]/p')" ]]; then
- envman add --key SKIPCI --value true
- else
- envman add --key SKIPCI --value false
- fi
- - script:
- title: Install Dependencies
- run_if: '{{enveq "SKIPCI" "false"}}'
+ title: Build
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
brew install cmake
gem install xcpretty --no-rdoc --no-ri
- - is_debug: 'yes'
- - script:
- title: Generate Workspace
- run_if: '{{enveq "SKIPCI" "false"}}'
- inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
- export BUILDTYPE=Debug
- make xproj
- - is_debug: 'yes'
- - script:
- title: Run Core and SDK Unit Tests
- run_if: '{{enveq "SKIPCI" "false"}}'
- inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
export BUILDTYPE=Debug
export XCPRETTY="| tee ${BITRISE_DEPLOY_DIR}/raw-xcodebuild-output.txt | xcpretty --color --report html --output ${BITRISE_DEPLOY_DIR}/xcode-test-results.html"
make run-test
- - is_debug: 'yes'
- deploy-to-bitrise-io:
title: Deploy to Bitrise.io
- run_if: '{{enveq "SKIPCI" "false"}}'
inputs:
- deploy_path: "test/fixtures"
- notify_user_groups: none
- is_compress: 'true'
- slack:
title: Post to Slack
- run_if: '{{enveq "SKIPCI" "false"}}'
inputs:
- webhook_url: "$SLACK_HOOK_URL"
- channel: "#gl-bots"
@@ -83,28 +48,13 @@ workflows:
nightly-release:
steps:
- script:
- title: Install Dependencies
+ title: Build
inputs:
- content: |-
#!/bin/bash
set -eu -o pipefail
brew install cmake
- - is_debug: 'yes'
- - script:
- title: Configure AWS-CLI
- inputs:
- - content: |-
- #!/bin/bash
- apt-get install -y python-pip python-dev build-essential
pip install awscli
- - script:
- title: Build package
- inputs:
- - content: |-
- #!/bin/bash
- set -eu -o pipefail
- export BUILDTYPE=Release
- export SYMBOLS=NO
- make xpackage
+ gem install xcpretty --no-rdoc --no-ri
+ BUILDTYPE=Release SYMBOLS=NO make xpackage
CLOUDWATCH=true platform/macos/scripts/metrics.sh
- - is_debug: 'yes'
diff --git a/platform/macos/sdk/Base.lproj/Localizable.strings b/platform/macos/sdk/Base.lproj/Localizable.strings
index b7a4a21173..68360320eb 100644
--- a/platform/macos/sdk/Base.lproj/Localizable.strings
+++ b/platform/macos/sdk/Base.lproj/Localizable.strings
@@ -1,6 +1,18 @@
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "The map failed to load because an unknown error occurred.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "The map failed to load because the style can't be loaded.";
+
/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "The map failed to load because the style is corrupted.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "The map failed to load because the style can’t be found or is incompatible.";
+
/* Label of Zoom In button */
"ZOOM_IN_LABEL" = "+";
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 8711950554..028d41ceda 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -35,8 +35,10 @@
#import <mbgl/math/wrap.hpp>
#import <mbgl/util/constants.hpp>
#import <mbgl/util/chrono.hpp>
+#import <mbgl/util/exception.hpp>
#import <mbgl/util/run_loop.hpp>
#import <mbgl/util/shared_thread_pool.hpp>
+#import <mbgl/util/string.hpp>
#import <map>
#import <unordered_map>
@@ -813,133 +815,150 @@ public:
[self.layer setNeedsDisplay];
}
-- (void)notifyMapChange:(mbgl::MapChange)change {
- // Ignore map updates when the Map object isn't set.
+- (void)cameraWillChangeAnimated:(BOOL)animated {
if (!_mbglMap) {
return;
}
- switch (change) {
- case mbgl::MapChangeRegionWillChange:
- case mbgl::MapChangeRegionWillChangeAnimated:
- {
- if ([self.delegate respondsToSelector:@selector(mapView:cameraWillChangeAnimated:)]) {
- BOOL animated = change == mbgl::MapChangeRegionWillChangeAnimated;
- [self.delegate mapView:self cameraWillChangeAnimated:animated];
- }
- break;
- }
- case mbgl::MapChangeRegionIsChanging:
- {
- // Update a minimum of UI that needs to stay attached to the map
- // while animating.
- [self updateCompass];
- [self updateAnnotationCallouts];
+ if ([self.delegate respondsToSelector:@selector(mapView:cameraWillChangeAnimated:)]) {
+ [self.delegate mapView:self cameraWillChangeAnimated:animated];
+ }
+}
- if ([self.delegate respondsToSelector:@selector(mapViewCameraIsChanging:)]) {
- [self.delegate mapViewCameraIsChanging:self];
- }
- break;
- }
- case mbgl::MapChangeRegionDidChange:
- case mbgl::MapChangeRegionDidChangeAnimated:
- {
- // Update all UI at the end of an animation or atomic change to the
- // viewport. More expensive updates can happen here, but care should
- // still be taken to minimize the work done here because scroll
- // gesture recognition and momentum scrolling is performed as a
- // series of atomic changes, not an animation.
- [self updateZoomControls];
- [self updateCompass];
- [self updateAnnotationCallouts];
- [self updateAnnotationTrackingAreas];
+- (void)cameraIsChanging {
+ if (!_mbglMap) {
+ return;
+ }
- if ([self.delegate respondsToSelector:@selector(mapView:cameraDidChangeAnimated:)]) {
- BOOL animated = change == mbgl::MapChangeRegionDidChangeAnimated;
- [self.delegate mapView:self cameraDidChangeAnimated:animated];
- }
- break;
- }
- case mbgl::MapChangeWillStartLoadingMap:
- {
- if ([self.delegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) {
- [self.delegate mapViewWillStartLoadingMap:self];
- }
- break;
- }
- case mbgl::MapChangeDidFinishLoadingMap:
- {
- [self.style willChangeValueForKey:@"sources"];
- [self.style didChangeValueForKey:@"sources"];
- [self.style willChangeValueForKey:@"layers"];
- [self.style didChangeValueForKey:@"layers"];
- if ([self.delegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) {
- [self.delegate mapViewDidFinishLoadingMap:self];
- }
- break;
- }
- case mbgl::MapChangeDidFailLoadingMap:
- {
- if ([self.delegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) {
- NSError *error = [NSError errorWithDomain:MGLErrorDomain code:0 userInfo:nil];
- [self.delegate mapViewDidFailLoadingMap:self withError:error];
- }
- break;
- }
- case mbgl::MapChangeWillStartRenderingMap:
- {
- if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) {
- [self.delegate mapViewWillStartRenderingMap:self];
- }
- break;
- }
- case mbgl::MapChangeDidFinishRenderingMap:
- case mbgl::MapChangeDidFinishRenderingMapFullyRendered:
- {
- if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) {
- BOOL fullyRendered = change == mbgl::MapChangeDidFinishRenderingMapFullyRendered;
- [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:fullyRendered];
- }
- break;
- }
- case mbgl::MapChangeWillStartRenderingFrame:
- {
- if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingFrame:)]) {
- [self.delegate mapViewWillStartRenderingFrame:self];
- }
- break;
- }
- case mbgl::MapChangeDidFinishRenderingFrame:
- case mbgl::MapChangeDidFinishRenderingFrameFullyRendered:
- {
- if (_isChangingAnnotationLayers) {
- _isChangingAnnotationLayers = NO;
- [self.style didChangeValueForKey:@"layers"];
- }
- if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) {
- BOOL fullyRendered = change == mbgl::MapChangeDidFinishRenderingFrameFullyRendered;
- [self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered];
- }
- break;
- }
- case mbgl::MapChangeDidFinishLoadingStyle:
- {
- self.style = [[MGLStyle alloc] initWithMapView:self];
- if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)])
- {
- [self.delegate mapView:self didFinishLoadingStyle:self.style];
- }
- break;
- }
- case mbgl::MapChangeSourceDidChange:
- {
- [self installAttributionView];
- self.needsUpdateConstraints = YES;
- break;
- }
+ // Update a minimum of UI that needs to stay attached to the map
+ // while animating.
+ [self updateCompass];
+ [self updateAnnotationCallouts];
+
+ if ([self.delegate respondsToSelector:@selector(mapViewCameraIsChanging:)]) {
+ [self.delegate mapViewCameraIsChanging:self];
+ }
+}
+
+- (void)cameraDidChangeAnimated:(BOOL)animated {
+ if (!_mbglMap) {
+ return;
+ }
+
+ // Update all UI at the end of an animation or atomic change to the
+ // viewport. More expensive updates can happen here, but care should
+ // still be taken to minimize the work done here because scroll
+ // gesture recognition and momentum scrolling is performed as a
+ // series of atomic changes, not an animation.
+ [self updateZoomControls];
+ [self updateCompass];
+ [self updateAnnotationCallouts];
+ [self updateAnnotationTrackingAreas];
+
+ if ([self.delegate respondsToSelector:@selector(mapView:cameraDidChangeAnimated:)]) {
+ [self.delegate mapView:self cameraDidChangeAnimated:animated];
+ }
+}
+
+- (void)mapViewWillStartLoadingMap {
+ if (!_mbglMap) {
+ return;
+ }
+
+ if ([self.delegate respondsToSelector:@selector(mapViewWillStartLoadingMap:)]) {
+ [self.delegate mapViewWillStartLoadingMap:self];
+ }
+}
+
+- (void)mapViewDidFinishLoadingMap {
+ if (!_mbglMap) {
+ return;
+ }
+
+ [self.style willChangeValueForKey:@"sources"];
+ [self.style didChangeValueForKey:@"sources"];
+ [self.style willChangeValueForKey:@"layers"];
+ [self.style didChangeValueForKey:@"layers"];
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFinishLoadingMap:)]) {
+ [self.delegate mapViewDidFinishLoadingMap:self];
+ }
+}
+
+- (void)mapViewDidFailLoadingMapWithError:(NSError *)error {
+ if (!_mbglMap) {
+ return;
+ }
+
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFailLoadingMap:withError:)]) {
+ [self.delegate mapViewDidFailLoadingMap:self withError:error];
+ }
+}
+
+- (void)mapViewWillStartRenderingFrame {
+ if (!_mbglMap) {
+ return;
+ }
+
+ if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingFrame:)]) {
+ [self.delegate mapViewWillStartRenderingFrame:self];
+ }
+}
+
+- (void)mapViewDidFinishRenderingFrameFullyRendered:(BOOL)fullyRendered {
+ if (!_mbglMap) {
+ return;
+ }
+
+ if (_isChangingAnnotationLayers) {
+ _isChangingAnnotationLayers = NO;
+ [self.style didChangeValueForKey:@"layers"];
+ }
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) {
+ [self.delegate mapViewDidFinishRenderingFrame:self fullyRendered:fullyRendered];
+ }
+}
+
+- (void)mapViewWillStartRenderingMap {
+ if (!_mbglMap) {
+ return;
+ }
+
+ if ([self.delegate respondsToSelector:@selector(mapViewWillStartRenderingMap:)]) {
+ [self.delegate mapViewWillStartRenderingMap:self];
+ }
+}
+
+- (void)mapViewDidFinishRenderingMapFullyRendered:(BOOL)fullyRendered {
+ if (!_mbglMap) {
+ return;
+ }
+
+ if ([self.delegate respondsToSelector:@selector(mapViewDidFinishRenderingMap:fullyRendered:)]) {
+ [self.delegate mapViewDidFinishRenderingMap:self fullyRendered:fullyRendered];
}
}
+- (void)mapViewDidFinishLoadingStyle {
+ if (!_mbglMap) {
+ return;
+ }
+
+ self.style = [[MGLStyle alloc] initWithMapView:self];
+ if ([self.delegate respondsToSelector:@selector(mapView:didFinishLoadingStyle:)])
+ {
+ [self.delegate mapView:self didFinishLoadingStyle:self.style];
+ }
+}
+
+- (void)sourceDidChange {
+ if (!_mbglMap) {
+ return;
+ }
+
+ [self installAttributionView];
+ self.needsUpdateConstraints = YES;
+}
+
#pragma mark Printing
- (void)print:(__unused id)sender {
@@ -2750,8 +2769,78 @@ public:
MGLMapViewImpl(MGLMapView *nativeView_)
: nativeView(nativeView_) {}
- void notifyMapChange(mbgl::MapChange change) override {
- [nativeView notifyMapChange:change];
+ void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override {
+ bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated;
+ [nativeView cameraWillChangeAnimated:animated];
+ }
+
+ void onCameraIsChanging() override {
+ [nativeView cameraIsChanging];
+ }
+
+ void onCameraDidChange(mbgl::MapObserver::CameraChangeMode mode) override {
+ bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated;
+ [nativeView cameraDidChangeAnimated:animated];
+ }
+
+ void onWillStartLoadingMap() override {
+ [nativeView mapViewWillStartLoadingMap];
+ }
+
+ void onDidFinishLoadingMap() override {
+ [nativeView mapViewDidFinishLoadingMap];
+ }
+
+ void onDidFailLoadingMap(std::exception_ptr exception) override {
+ NSString *description;
+ MGLErrorCode code;
+ try {
+ std::rethrow_exception(exception);
+ } catch (const mbgl::util::StyleParseException&) {
+ code = MGLErrorCodeParseStyleFailed;
+ description = NSLocalizedStringWithDefaultValue(@"PARSE_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style is corrupted.", @"User-friendly error description");
+ } catch (const mbgl::util::StyleLoadException&) {
+ code = MGLErrorCodeLoadStyleFailed;
+ description = NSLocalizedStringWithDefaultValue(@"LOAD_STYLE_FAILED_DESC", nil, nil, @"The map failed to load because the style can't be loaded.", @"User-friendly error description");
+ } catch (const mbgl::util::NotFoundException&) {
+ code = MGLErrorCodeNotFound;
+ description = NSLocalizedStringWithDefaultValue(@"STYLE_NOT_FOUND_DESC", nil, nil, @"The map failed to load because the style can’t be found or is incompatible.", @"User-friendly error description");
+ } catch (...) {
+ code = MGLErrorCodeUnknown;
+ description = NSLocalizedStringWithDefaultValue(@"LOAD_MAP_FAILED_DESC", nil, nil, @"The map failed to load because an unknown error occurred.", @"User-friendly error description");
+ }
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey: description,
+ NSLocalizedFailureReasonErrorKey: @(mbgl::util::toString(exception).c_str()),
+ };
+ NSError *error = [NSError errorWithDomain:MGLErrorDomain code:code userInfo:userInfo];
+ [nativeView mapViewDidFailLoadingMapWithError:error];
+ }
+
+ void onWillStartRenderingFrame() override {
+ [nativeView mapViewWillStartRenderingFrame];
+ }
+
+ void onDidFinishRenderingFrame(mbgl::MapObserver::RenderMode mode) override {
+ bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full;
+ [nativeView mapViewDidFinishRenderingFrameFullyRendered:fullyRendered];
+ }
+
+ void onWillStartRenderingMap() override {
+ [nativeView mapViewWillStartRenderingMap];
+ }
+
+ void onDidFinishRenderingMap(mbgl::MapObserver::RenderMode mode) override {
+ bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full;
+ [nativeView mapViewDidFinishRenderingMapFullyRendered:fullyRendered];
+ }
+
+ void onDidFinishLoadingStyle() override {
+ [nativeView mapViewDidFinishLoadingStyle];
+ }
+
+ void onSourceChanged(mbgl::style::Source&) override {
+ [nativeView sourceDidChange];
}
void invalidate() override {