diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 9ba7cf03f476..000000000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,1348 +0,0 @@ ---- -version: 2.1 - -parameters: - turf-revision: - type: string - default: "" - -orbs: - # https://circleci.com/orbs/registry/orb/circleci/slack - slack: circleci/slack@4.5 - - -### Shared anchors -release-precheck: &release-anchor - filters: - tags: - only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/ - branches: - ignore: /.*/ - requires: - - release-precheck - -executors: - xcode-latest: - macos: - xcode: 13.3.0 - # Note that `create-github-draft-release.sh` depends on this structure to parse the minimum supported Xcode version. - # Please keep them in sync if this structure changes. - xcode-sdk-min: - macos: - xcode: 13.0.0 - -### Workflows - -workflows: - - # Runs daily at midnight UTC - nightly: - triggers: - - schedule: - cron: "0 6 * * *" - filters: - branches: - only: - - main - jobs: - - swiftlint - - depsvalidator: - xcode: "13.3.0" - - build-sdk: - configuration: "Release" - report_failure: true - matrix: - parameters: - xcode: ["13.0.0", "13.2.1"] - - unit-test-sdk: - configuration: "Release" - report_failure: true - matrix: - parameters: - xcode: ["13.0.0", "13.2.1"] - - build-tests: - name: Build MapboxTestHost tests - - run-tests: - name: Run MapboxTestHost tests - scheme: MapboxTestHost - requires: - - Build MapboxTestHost tests - - build-examples-tests: - name: Build Examples tests - - run-tests: - name: Run Examples tests - scheme: Examples - requires: - - Build Examples tests - - create-xcframework: - create-xcframework-always-run: true - report_failure: true - xcode: "13.0.0" - - optional-tests: - unless: << pipeline.parameters.turf-revision >> - jobs: - - run-tests?: - type: approval - filters: - branches: - ignore: main - - build-tests: - name: Build MapboxTestHost tests [non-main] - requires: - - run-tests? - - run-tests: - name: Run MapboxTestHost tests [non-main] - scheme: MapboxTestHost - requires: - - Build MapboxTestHost tests [non-main] - devices: --device model=iphone8,version=14.7 - - - run-examples-tests?: - type: approval - filters: - branches: - ignore: main - - build-examples-tests: - name: Build Examples tests [non-main] - requires: - - run-examples-tests? - - run-tests: - name: Run Examples tests [non-main] - scheme: Examples - requires: - - Build Examples tests [non-main] - devices: --device model=iphone8,version=14.7 - - - trigger-compatibility-verification?: - type: approval - - trigger-compatibility-verification: - requires: - - trigger-compatibility-verification? - - steve: - unless: << pipeline.parameters.turf-revision >> - jobs: - - swiftlint - - depsvalidator: - xcode: "13.3.0" - - build-sdk: - xcode: "13.0.0" - configuration: "Release" - - - build-sdk-for-api-compatibility-check - - check-api-compatibility: - requires: - - build-sdk-for-api-compatibility-check - - create-xcframework - - validate-integrations-for-branch - - # Trigger mobile-metrics workflows - - trigger-metrics-v2 - - trigger-binary-size: - requires: - - build-sdk - - trigger-metrics-build: - requires: - - build-sdk - - trigger-metrics-collection: - requires: - - build-sdk - filters: - branches: - only: main - - # Run unit tests on CI simulator - - unit-test-sdk: - name: "Run Unit tests" - xcode: "13.0.0" - - - unit-test-sdk: - name: "Run Unit Tests with min Turf" - xcode: "13.0.0" - turf-revision: v2.0.0 - - # Build all tests with host application - # Run on main by default - # Can be approved to run on other branches - - build-tests: - name: Build MapboxTestHost tests - - run-tests: - name: Run MapboxTestHost tests - scheme: MapboxTestHost - requires: - - Build MapboxTestHost tests - filters: - branches: - only: main - - # Build and Run tests on Examples scheme. - # Enabled by-default on 'main' branch - # Can be approved on purpose for others - - build-examples-tests: - name: Build Examples tests - - run-tests: - name: Run Examples tests - scheme: Examples - requires: - - Build Examples tests - filters: - branches: - only: main - - release: - jobs: - - release-precheck: - <<: *release-anchor - requires: [] - - trigger-metrics-v2: - <<: *release-anchor - - build-dynamic: - <<: *release-anchor - - build-static: - <<: *release-anchor - - build-docs: - <<: *release-anchor - - build-studio-preview: - <<: *release-anchor - - check-api-compatibility: - <<: *release-anchor - requires: - - build-dynamic - - create-registry-pr: - <<: *release-anchor - requires: - - build-static - - build-docs - - build-studio-preview - - check-api-compatibility - - wait-registry-pr: - <<: *release-anchor - requires: - - create-registry-pr - type: approval - - check-registry: - <<: *release-anchor - requires: - - wait-registry-pr - - create-docs-prs: - <<: *release-anchor - requires: - - check-registry - - update-examples: - <<: *release-anchor - requires: - - check-registry - - publish-podspec: - <<: *release-anchor - requires: - - check-registry - - validate-integrations: - <<: *release-anchor - requires: - - publish-podspec - - create-draft-release: - <<: *release-anchor - requires: - - create-docs-prs - - update-examples - - validate-integrations - - turf-integration: - when: << pipeline.parameters.turf-revision >> - jobs: - - turf-integration: - name: "Run Unit Tests" - turf-revision: << pipeline.parameters.turf-revision >> - -# ============================================================================== - -jobs: - - # This is a base job that can be merged into specific jobs. It uses anchors - # and aliases as described here - # https://circleci.com/docs/2.0/writing-yaml/#merging-maps - base-job: &base-job - parameters: - xcode: - type: string - default: "13.0.0" - create-xcframework-always-run: - type: boolean - default: false - configuration: - type: string - default: "Debug" - report_failure: - type: boolean - default: false - scheme: - type: string - default: "MapboxTestHost" - turf-revision: - type: string - default: "" - macos: - xcode: << parameters.xcode >> - environment: - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - CONFIGURATION: << parameters.configuration >> - steps: - # Don't run the default job - - run: exit 1 - - swiftlint: - executor: xcode-latest - steps: - - checkout - - run: brew install swiftlint - - run: swiftlint lint --strict --reporter junit | tee result.xml - - store_artifacts: - path: result.xml - - store_test_results: - path: result.xml - - depsvalidator: - <<: *base-job - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - run: - name: Build depsvalidator - command: swift build --package-path scripts/depsvalidator - - run: - name: Run depsvalidator - command: swift run --package-path scripts/depsvalidator depsvalidator - - build-sdk: - <<: *base-job - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: - name: Building MapboxMaps for simulator - command: make build-sdk-for-simulator - no_output_timeout: 5m - - run: - name: Building MapboxMaps for device - command: make build-sdk-for-device - no_output_timeout: 5m - - locate-derived-data-directory: - base_name: $(basename $(pwd)) - - store-logs: - derived_data_path: $DERIVED_DATA_PATH - - run: - name: Building scheme DebugApp for simulator - command: make build-app-for-simulator SCHEME=DebugApp - no_output_timeout: 5m - - run: - name: Building scheme DebugApp for device - command: make build-app-for-device SCHEME=DebugApp - no_output_timeout: 5m - - locate-derived-data-directory: - base_name: Apps - - store-logs: - derived_data_path: $DERIVED_DATA_PATH - - report-failure: - report_failure: << parameters.report_failure >> - message: "build-sdk" - - store-default-artifacts - - trigger-binary-size: - <<: *base-job - steps: - - run: - name: Trigger binary size test - command: | - if [ -n "${CIRCLECI_API_TOKEN_MOBILE_METRICS}" ]; then - if [[ $CIRCLE_BRANCH == main ]]; then - curl --fail -X POST --header "Content-Type: application/json" --data "{\"build_parameters\":{\"CIRCLE_JOB\":\"ios-maps-v10-binary-size\",\"SOURCE_HASH\":\"${CIRCLE_SHA1}\",\"SOURCE_NAME\":\"ios-maps-v10\"}}" https://circleci.com/api/v1.1/project/github/mapbox/mobile-metrics/tree/master?circle-token=${CIRCLECI_API_TOKEN_MOBILE_METRICS} - else - curl --fail -X POST --header "Content-Type: application/json" --data "{\"build_parameters\":{\"CIRCLE_JOB\":\"ios-maps-v10-binary-size\",\"SOURCE_HASH\":\"${CIRCLE_SHA1}\",\"SOURCE_NAME\":\"ios-maps-v10\",\"SOURCE_COMPARE_BASELINE\":\"YES\"}}" https://circleci.com/api/v1.1/project/github/mapbox/mobile-metrics/tree/master?circle-token=${CIRCLECI_API_TOKEN_MOBILE_METRICS} - fi - else - echo "CIRCLECI_API_TOKEN_MOBILE_METRICS not provided" - fi - - trigger-metrics-build: - <<: *base-job - steps: - - run: - name: Build metrics tests - command: | - if [ -n "${CIRCLECI_API_TOKEN_MOBILE_METRICS}" ]; then - if [[ $CIRCLE_BRANCH == main ]]; then - curl --fail -X POST --header "Content-Type: application/json" --data "{\"build_parameters\":{\"CIRCLE_JOB\":\"ios-maps-v10-build\",\"SOURCE_HASH\":\"${CIRCLE_SHA1}\",\"SOURCE_NAME\":\"ios-maps-v10\"}}" https://circleci.com/api/v1.1/project/github/mapbox/mobile-metrics/tree/master?circle-token=${CIRCLECI_API_TOKEN_MOBILE_METRICS} - else - echo "Trying $CIRCLE_BRANCH first:" - if ! curl --fail -X POST --header "Content-Type: application/json" --data "{\"build_parameters\":{\"CIRCLE_JOB\":\"ios-maps-v10-build\",\"SOURCE_HASH\":\"${CIRCLE_SHA1}\",\"SOURCE_NAME\":\"ios-maps-v10\"}}" https://circleci.com/api/v1.1/project/github/mapbox/mobile-metrics/tree/$CIRCLE_BRANCH?circle-token=${CIRCLECI_API_TOKEN_MOBILE_METRICS}; then - echo "Falling back to `master` branch:" - curl --fail -X POST --header "Content-Type: application/json" --data "{\"build_parameters\":{\"CIRCLE_JOB\":\"ios-maps-v10-build\",\"SOURCE_HASH\":\"${CIRCLE_SHA1}\",\"SOURCE_NAME\":\"ios-maps-v10\"}}" https://circleci.com/api/v1.1/project/github/mapbox/mobile-metrics/tree/master?circle-token=${CIRCLECI_API_TOKEN_MOBILE_METRICS} - fi - fi - else - echo "CIRCLECI_API_TOKEN_MOBILE_METRICS not provided" - fi - - trigger-metrics-v2: - docker: - - image: cimg/base:stable - steps: - - run: - name: Run Workflow - command: | - if [[ $CIRCLE_BRANCH == main ]]; then - curl -X POST "https://circleci.com/api/v2/project/github/mapbox/mapbox-maps-ios-internal/pipeline" \ - -H 'Content-Type: application/json' \ - -H "Circle-Token: $CIRCLECI_API_TOKEN_MAPBOX_MAPS_IOS_INTERNAL" \ - -d $'{ - "branch": "main", - "parameters": { - "maps-ios-checkout-reference": "<< pipeline.git.revision >>", - "run-tests": true - } - }' - else - curl -X POST "https://circleci.com/api/v2/project/github/mapbox/mapbox-maps-ios-internal/pipeline" \ - -H 'Content-Type: application/json' \ - -H "Circle-Token: $CIRCLECI_API_TOKEN_MAPBOX_MAPS_IOS_INTERNAL" \ - -d $'{ - "branch": "main", - "parameters": { - "maps-ios-checkout-reference": "<< pipeline.git.revision >>" - } - }' - fi - - trigger-metrics-collection: - <<: *base-job - steps: - - run: - # TODO: main by default (this is set as a filter), other commits on-demand - name: Trigger metrics - command: | - if [ -n "${CIRCLECI_API_TOKEN_MOBILE_METRICS}" ]; then - curl --fail -X POST --header "Content-Type: application/json" --data "{\"parameters\":{\"run_ios_maps_v10_benchmark\":true,\"ci_ref\":${CIRCLE_BUILD_NUM},\"mapbox_hash\":\"${CIRCLE_SHA1}\",\"target_branch\":\"${CIRCLE_BRANCH}\"},\"branch\":\"master\"}" https://circleci.com/api/v2/project/github/mapbox/mobile-metrics/pipeline?circle-token=${CIRCLECI_API_TOKEN_MOBILE_METRICS} - else - echo "CIRCLECI_API_TOKEN_MOBILE_METRICS not provided" - fi - - report-failure: - report_failure: << parameters.report_failure >> - message: "metrics" - - trigger-compatibility-verification: - docker: - - image: cimg/python:3.10.4 - steps: - - checkout - - run: - name: Install python depedencies - command: | - pip3 install PyYAML - pip3 install GitPython - pip3 install requests - - run: - name: Trigger compatibility test - command: | - python3 scripts/ci-trigger/ci-compatibility-start-pipeline.py --target-slug mapbox/mapbox-sdk --token ${CIRCLECI_API_TOKEN_MAPBOX_MAPS_IOS_INTERNAL} --config mapbox-maps-ios=${CIRCLE_SHA1} --origin-slug mapbox/mapbox-maps-ios --platform ios - - unit-test-sdk: - <<: *base-job - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - when: - condition: << parameters.turf-revision >> - steps: - - run: - name: Set Turf Revision - command: | - sed -i '' 's/turf-swift.git", from: "2.0.0"/turf-swift.git", .revision("<< parameters.turf-revision >>")/' Package.swift - # Building and testing are split into 2, with the aim that we'll be able to reuse - # the build product and test on multiple simulators - - run: - name: Building MapboxMaps for simulator - command: make build-sdk-for-testing-simulator - no_output_timeout: 5m - - run: - name: Testing MapboxMaps with simulator - command: make test-sdk-without-building-simulator - - run: - name: Compress XCResult - command: zip -r MapboxMapsTests.xcresult.zip MapboxMapsTests.xcresult - when: always - - store_artifacts: - path: MapboxMapsTests.xcresult.zip - - locate-derived-data-directory: - base_name: $(basename $(pwd)) - - run: - name: Converting and uploading coverage - command: | - pip3 install awscli gitpython - make update-codecov-with-profdata SCHEME=MapboxMaps BUILD_DIR="$DERIVED_DATA_PATH" - - store-logs: - artifact_name: MapboxMaps - derived_data_path: $DERIVED_DATA_PATH - - report-failure: - report_failure: << parameters.report_failure >> - message: "unit-test-sdk" - - build-tests: - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: bundle install - - run: - name: Build MapboxTestHost for testing - command: bundle exec fastlane build_tests - - store_artifacts: - path: output/build_products.zip - - persist_to_workspace: - root: output/ - paths: - - build_products.zip - - slack-notify-failure-if-any - - run-tests: - parameters: - devices: - default: | - --device model=iphone11,version=13.6 \ - --device model=iphone11pro,version=14.7 \ - --device model=iphone8,version=12.4 \ - type: string - scheme: - type: string - executor: xcode-latest - steps: - - checkout - - install-gcloud - - configure-environment - - attach_workspace: - at: output - - run: - name: Run << parameters.scheme >> tests on devices - command: | - gcloud firebase test ios run --test output/build_products.zip \ - << parameters.devices >> 2>&1 | tee firebase_test_lab_run.log - no_output_timeout: 30m - - run: - name: Download XCResults - command: | - TEST_LAB_PATH=$(cat firebase_test_lab_run.log | grep -o "test-lab.*/") - - mkdir testlab_results - gsutil -m cp -r "gs://${TEST_LAB_PATH}i*" testlab_results - zip -r testlab_results.zip testlab_results - when: always - - run: - name: Symbolicate crash logs - command: | - ( - unzip output/build_products.zip -d testlab_results/ - rsync -R testlab_results/*/CrashLogs/*.ips CrashLogs/ - make symbolicate \ - BUILD_DIR=CrashLogs \ - BUILT_DEVICE_PRODUCTS_DIR=testlab_results/Release-iphoneos/ \ - APP_NAME=<< parameters.scheme >> - zip -r crashlogs.zip CrashLogs - ) || true - when: always - - run: - name: Update coverage - command: | - make update-codecov-with-profdata \ - COVERAGE_ARCH=arm64 \ - COVERAGE_ROOT_DIR="$(pwd)/testlab_results" \ - COVERAGE_MAPBOX_MAPS="$(pwd)/testlab_results/Release-iphoneos/MapboxMaps.o" SCHEME=<< parameters.scheme >> - - run: - name: Parsing xcresults for errors - command: | - find "testlab_results" -name '*.xcresult' -exec swift run --package-path scripts/xcparty xcparty {} \; - when: on_fail - - store_artifacts: - path: firebase_test_lab_run.log - - store_artifacts: - path: crashlogs.zip - - store_artifacts: - path: testlab_results.zip - - store_test_results: - path: testlab_results - - slack-notify-failure-if-any - - build-examples-tests: - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: - name: Build Examples tests - command: | - bundle install - bundle exec fastlane build_examples_tests - - store_artifacts: - path: output/Examples.zip - - persist_to_workspace: - root: output/ - paths: - - build_products.zip - - slack-notify-failure-if-any - - create-xcframework: - <<: *base-job - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - ensure-text-exists-in-commit: - commit-text: "[create xcframework]" - always-run: << parameters.create-xcframework-always-run >> - - install-dependencies - - make-xcframework-bundle: - bundle_style: "dynamic" - - when: - # Do not run Check Compatibility API for Xcode 12 - condition: - not: - matches: - pattern: "^13.0.*$" - value: << parameters.xcode >> - steps: - - check-breaking-api: - bundle_path: MapboxMaps.zip - - store_artifacts: - path: MapboxMaps.zip - - make-xcframework-bundle: - bundle_style: "static" - - when: - # Do not run Check Compatibility API for Xcode 12 - condition: - not: - matches: - pattern: "^13.0.*$" - value: << parameters.xcode >> - steps: - - check-breaking-api: - bundle_path: MapboxMaps-static.zip - - store_artifacts: - path: MapboxMaps-static.zip - - report-failure: - report_failure: << parameters.report_failure >> - message: "create-xcframework" - - build-sdk-for-api-compatibility-check: - executor: xcode-sdk-min - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - make-xcframework-bundle - - persist_to_workspace: - root: . - paths: - - MapboxMaps.zip - - store_artifacts: - path: MapboxMaps.zip - - store-default-artifacts - - check-api-compatibility: - macos: - xcode: 13.2.1 - steps: - - checkout - - configure-environment - - attach_workspace: - at: workspace - - check-breaking-api: - bundle_path: workspace/MapboxMaps.zip - - store-default-artifacts - - validate-integrations-for-branch: - description: < - This job is designed to be called on daily bases. - It is validating dependency managers integration per branch version rule. - However, this job would run exclusively for open PRs to the release branches (aka "release/v.*"") - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: brew install xcodegen - - run: - name: Validate SPM and CocoaPods integrations for current branch - command: scripts/validate-integrations/validate-integrations.sh -b $CIRCLE_BRANCH - - store-default-artifacts - - validate-integrations: - description: < - This job is designed to be called after SDK Registry PR merge. - It requires binaries to be publicly available and downloadable. - SPM, Cocoapods and DirectDownload tests would be called. - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: - name: Inject new Podspec version to CDN cache - description: | - Force adding new version to the cache to avoid long running clone operation - The following command would take the latest CocoaPods version from cache. - - Alternative is to replace CDN with Specs repository and checkout it for the latest changes. It usually takes 3+ minutes on CircleCI - command: | - source scripts/utilities/cocoapods.sh - pod_inject_cdn_version MapboxMaps "$VERSION" - - run: - name: Test direct download integrations - command: scripts/validate-integrations/validate-integrations.sh -v "$VERSION" - - store-default-artifacts - - slack-notify-failure-if-any - - release-precheck: - executor: xcode-latest - steps: - - checkout - - parse-release-version-if-available - - run: - name: Podspec points to the release version - command: '[[ $(pod ipc spec MapboxMaps.podspec | jq -r .version) == "$VERSION" ]]' - - inject-netrc-credentials - - run: - name: General podspec check - command: pod spec lint --verbose --allow-warnings - - slack-notify-failure-if-any - - build-dynamic: - executor: xcode-sdk-min - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: - name: Build dynamic SDK - command: ./package-mapbox-maps.sh dynamic - working_directory: scripts/release/packager - - store_artifacts: - path: scripts/release/packager/MapboxMaps.zip - - persist_to_workspace: - root: scripts/release/packager - paths: - - MapboxMaps.zip - - run: - name: Upload to S3 - command: scripts/release/upload-to-registry.sh scripts/release/packager/MapboxMaps.zip mobile-maps-ios "$VERSION" MapboxMaps.zip - - slack-notify-failure-if-any - - build-static: - executor: xcode-sdk-min - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: - name: Build dynamic SDK - command: ./package-mapbox-maps.sh static - working_directory: scripts/release/packager - - store_artifacts: - path: scripts/release/packager/MapboxMaps-static.zip - - run: - name: Upload to S3 - command: scripts/release/upload-to-registry.sh scripts/release/packager/MapboxMaps-static.zip mobile-maps-ios-static "$VERSION" MapboxMaps-static.zip - - slack-notify-failure-if-any - - build-docs: - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - make-docs - - slack-notify-failure-if-any - - build-studio-preview: - executor: xcode-latest - steps: - - checkout - - configure-environment - - run: - name: Build Studio Preview with new branch - command: scripts/validate-integrations/validate-studio-preview.sh -c "$CIRCLE_SHA1" - - slack-notify-failure-if-any - - create-registry-pr: - executor: xcode-latest - steps: - - checkout - - configure-environment - - attach_workspace: - at: workspace - - run: ls -lGA workspace - - run: - name: Create SDK Registry PR - command: scripts/release/create-api-downloads-pr.sh mobile-maps-ios "$VERSION" - - slack-notify-failure-if-any - - check-registry: - docker: - - image: cimg/base:stable - steps: - - parse-release-version-if-available - - inject-netrc-credentials - - run: - name: Wait binaries public access - command: | - echo $VERSION - timeout 3600 bash -c 'while [[ "$(curl -n -s -o /dev/null -w ''%{http_code}'' https://api.mapbox.com/downloads/v2/mobile-maps-ios/releases/ios/$VERSION/MapboxMaps.zip)" != "200" ]]; do sleep 5; done' || false - no_output_timeout: 60m - - slack-notify-failure-if-any - - create-docs-prs: - executor: xcode-latest - steps: - - checkout - - configure-environment - - attach_workspace: - at: workspace - - run: - name: Create documentation pull requests - command: | - unzip workspace/api-docs.zip -d . - scripts/release/create-docs-prs.sh -p api-docs - - store-default-artifacts - - slack-notify-failure-if-any - - update-examples: - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: - name: Release Examples App to TestFlight - command: | - bundle install - bundle exec fastlane beta - - slack-notify-failure-if-any - - - publish-podspec: - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: - name: Publish CocoaPods Podspec - command: pod trunk push --allow-warnings - - slack-notify-failure-if-any - - create-draft-release: - executor: xcode-latest - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - run: brew install taiki-e/tap/parse-changelog - - run: - name: Create draft release on GitHub - command: ./scripts/release/create-github-draft-release.sh "$VERSION" - - slack/notify: - event: pass - template: success_tagged_deploy_1 - - wait-for-binaries: - docker: - - image: cimg/base:stable - steps: - - parse-release-version-if-available - - inject-netrc-credentials - - run: - name: Wait binaries public access - command: | - echo $VERSION - timeout 1800 bash -c 'while [[ "$(curl -n -s -o /dev/null -w ''%{http_code}'' https://api.mapbox.com/downloads/v2/mobile-maps-ios/releases/ios/$VERSION/MapboxMaps.zip)" != "200" ]]; do sleep 5; done' || false - no_output_timeout: 30m - - turf-integration: - parameters: - turf-revision: - type: string - default: "" - macos: - xcode: "13.0.0" - environment: - HOMEBREW_NO_AUTO_UPDATE: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - CONFIGURATION: "Debug" - resource_class: macos.x86.medium.gen2 - steps: - - checkout - - configure-environment - - turf-report-pending: - turf-revision: << parameters.turf-revision >> - - run: - name: Set Turf Revision - command: | - sed -i '' 's/turf-swift.git", from: "2.0.0"/turf-swift.git", .revision("<< parameters.turf-revision >>")/' Package.swift - # Building and testing are split into 2, with the aim that we'll be able to reuse - # the build product and test on multiple simulators - - run: - name: Building MapboxMaps for simulator - command: make build-sdk-for-testing-simulator - no_output_timeout: 5m - - run: - name: Testing MapboxMaps with simulator - command: make test-sdk-without-building-simulator - - run: - name: Compress XCResult - command: zip -r MapboxMapsTests.xcresult.zip MapboxMapsTests.xcresult - when: always - - store_artifacts: - path: MapboxMapsTests.xcresult.zip - - locate-derived-data-directory: - base_name: $(basename $(pwd)) - - store-logs: - artifact_name: MapboxMaps - derived_data_path: $DERIVED_DATA_PATH - - turf-report-success: - turf-revision: << parameters.turf-revision >> - - turf-report-failure: - turf-revision: << parameters.turf-revision >> - -# ============================================================================== -# Reusable commands -commands: - - configure-environment: - steps: - # CircleCI's checkout step configures git so that it will always clone - # from github.com via SSH. It also seeds the known_hosts file, however the - # value it uses doesn't seem to work for xcodebuild when it is resolving - # the Swift package graph. This step reverts CircleCI's git configuration - # change so that xcodebuild can clone Swift packages via HTTPS. - - run: - name: Allow cloning from github.com via HTTPS - command: git config --global --unset url."ssh://git@github.com".insteadOf - - inject-netrc-credentials - - inject-mapbox-public-token - - declare-default-artifacts-path - - parse-release-version-if-available - - install-mbx-ci - - add-mapbox-submodules-key - - install-dependencies - - inject-netrc-credentials: - steps: - - run: - name: Configure .netrc - command: | - echo "machine api.mapbox.com login mapbox password $SDK_REGISTRY_TOKEN" >> ~/.netrc - chmod 0600 ~/.netrc - - inject-mapbox-public-token: - steps: - - run: - name: Injecting Mapbox public token - command: | - echo "MAPBOX_ACCESS_TOKEN = ${MAPBOX_ACCESS_TOKEN}" > ./developer.xcconfig - echo "${MAPBOX_ACCESS_TOKEN}" > ~/.mapbox - echo "${MAPBOX_ACCESS_TOKEN}" > ./Tests/MapboxMapsTests/Helpers/MapboxAccessToken - - add-mapbox-submodules-key: - steps: - - add_ssh_keys: - fingerprints: - - "3b:cd:47:bf:57:9c:e5:36:b0:4d:5f:12:5e:d3:b3:3e" - - install-mbx-ci: - parameters: - platform: - type: enum - enum: - - darwin - - linux - default: darwin - arch: - type: enum - enum: - - amd64 - - arm64 - default: amd64 - version: - type: string - default: latest - install_path: - type: string - default: /usr/local/bin - steps: - - run: - name: Install latest mbx-ci - command: | - curl -Ls https://mapbox-release-engineering.s3.amazonaws.com/mbx-ci/<>/mbx-ci-<>-<> > <>/mbx-ci - chmod 755 <>/mbx-ci - mbx-ci aws setup - - ensure-text-exists-in-commit: - parameters: - commit-text: - type: string - always-run: - type: boolean - steps: - - run: - name: Check for "<< parameters.commit-text >>" in commit message - command: | - export RUN_JOB="$(git log -1 --pretty=%B | fgrep "<< parameters.commit-text >>" | wc -l)" - if << parameters.always-run >>; then - echo "Job configured to always run" - elif [[ "$RUN_JOB" -ne "0" ]]; then - echo "<< parameters.commit-text >> found." - else - echo "Skipping job" - circleci-agent step halt - fi - - setup-google-cloud-account: - steps: - - run: - name: Setup Google Cloud default application credentials - command: | - mkdir -p ~/.config/gcloud/ - echo "${GOOGLE_CLOUD_KEY_FILE}" > ~/.config/gcloud/application_default_credentials.json - - login-google-cloud-platform: - parameters: - project: - type: string - default: mapbox-maps-ios-dc24c - steps: - - run: - name: Log in to Google Cloud Platform - command: gcloud auth activate-service-account --key-file ~/.config/gcloud/application_default_credentials.json --project << parameters.project >> - - get-test-results-from-firebase: - parameters: - scheme: - type: string - default: "" - steps: - - run: - name: Retrieve TestResult from gcloud for << parameters.scheme >> - command: | - testResult=$(gsutil ls gs://${TEST_LAB_EXAMPLES}/<< parameters.scheme >>-${CIRCLE_BUILD_NUM}/iphone11-13.6-en_US-portrait) - gsutil cp -r $testResult $DERIVED_DATA_PATH - when: always - - store-logs: - parameters: - artifact_name: - type: string - default: "build" - derived_data_path: - type: string - default: "build" - description: "Save Logs as Artifacts" - steps: - - run: - name: Zipping logs - command: | - export ZIP_FILE="$PWD/<< parameters.artifact_name >>.zip" - cd "<< parameters.derived_data_path >>" - zip -r "$ZIP_FILE" Build/Products || echo "Skipping Build/Products" - zip -r "$ZIP_FILE" Build/ProfileData || echo "Skipping Build/ProfileData" - zip -r "$ZIP_FILE" Logs || echo "Skipping Logs" - when: always - - store_artifacts: - path: << parameters.artifact_name >>.zip - - store-firebase-artifacts: - parameters: - artifact_name: - type: string - default: "firebase" - derived_data_path: - type: string - default: "build" - description: "Save firebase Results as Artifacts" - steps: - - run: - name: Zipping firebase results - command: | - export ZIP_FILE="$PWD/<< parameters.artifact_name >>.zip" - cd "<< parameters.derived_data_path >>" - zip -r "$ZIP_FILE" xcodebuild_output.log || echo "Skipping xcodebuild_output.log" - zip -r "$ZIP_FILE" video.mp4 || echo "Skipping video.mp4" - zip -r "$ZIP_FILE" test_result_0.xml || echo "Skipping test_result_0.xml" - zip -r "$ZIP_FILE" syslog.txt || echo "Skipping syslog.txt" - zip -r "$ZIP_FILE" TestLogs || echo "Skipping TestLogs" - zip -r "$ZIP_FILE" ProfileData || echo "Skipping ProfileData" - zip -r "$ZIP_FILE" CrashLogs || echo "Skipping CrashLogs" - when: always - - store_artifacts: - path: << parameters.artifact_name >>.zip - - install-gcloud: - # Link for gcloud versions: https://cloud.google.com/sdk/docs/downloads-versioned-archives#installation_instructions - parameters: - version: - type: string - default: "google-cloud-sdk-370.0.0-darwin-x86_64.tar.gz" - checksum: - type: string - default: "e08541e2e319457698b9154cd064edc5efa95653f5a84b94a1f2f3d0f87ee196" - description: "Install gcloud" - steps: - - run: - name: Install gcloud - command: | - mkdir /tmp/gcloud && cd /tmp/gcloud - - curl -OL https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/<< parameters.version >> - echo '<< parameters.checksum >> *<< parameters.version >>' >> checksumfile - shasum -a 256 -c checksumfile - - tar zxvf << parameters.version >> - ./google-cloud-sdk/install.sh -q --install-python false - - echo "source /tmp/gcloud/google-cloud-sdk/path.bash.inc" >> $BASH_ENV - echo "source /tmp/gcloud/google-cloud-sdk/completion.bash.inc" >> $BASH_ENV - - setup-google-cloud-account - - login-google-cloud-platform - - install-dependencies: - description: "Install Dependencies" - steps: - - run: - name: Install basic dependencies - command: | - python3 -m pip install awscli gitpython requests python-dateutil - brew install xcodegen gh ccache - - make-docs: - description: "Generate Documentation" - steps: - - run: git submodule update --init -- scripts/doc-generation/jazzy-theme - - run: scripts/doc-generation/generate-maps-docs.sh -v - - run: zip -r api-docs.zip api-docs - - store_artifacts: - path: api-docs.zip - - persist_to_workspace: - root: . - paths: - - api-docs.zip - - open-docs-pull-requests: - steps: - - run: - name: Create documentation pull requests - command: scripts/release/create-docs-prs.sh -p api-docs - - make-xcframework-bundle: - parameters: - bundle_style: - type: string - default: "dynamic" - description: "Create << parameters.bundle_style >> xcframework bundle" - steps: - - run: - name: Creating << parameters.bundle_style >> bundled artifact - command: | - echo "Navigating to packager script" - cd scripts/release/packager - ./package-mapbox-maps.sh << parameters.bundle_style >> - echo "Moving zip to project root" - mv MapboxMaps*.zip /Users/distiller/project/ - - check-breaking-api: - parameters: - bundle_path: - type: string - default: scripts/release/packager/MapboxMaps.zip - description: Compare public interface with pinned baseline with swift-api-digester - steps: - - run: - name: Breaking API check - command: scripts/api-compatibility-check/breaking-api-check.sh -p << parameters.bundle_path >> - - slack-notify-failure-if-any: - steps: - - slack/notify: - event: fail - mentions: '@maps-ios' - template: basic_fail_1 - branch_pattern: ^(main|release.*)$ - - report-failure: - parameters: - report_failure: - type: boolean - default: false - message: - type: string - default: "" - steps: - - run: - name: Detected failure, report to Slack? << parameters.report_failure >> - command: | - if ! << parameters.report_failure >> ; then - echo "Skipping Slack notification" - circleci-agent step halt - fi - when: on_fail - - slack/notify: - branch_pattern: main - event: fail - custom: | - { - "text": "CircleCI job failed.", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": "Job Failed. :red_circle:", - "emoji": true - } - }, - { - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*Job*: ${CIRCLE_JOB}" - } - ] - }, - { - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*Project*: $CIRCLE_PROJECT_REPONAME" - }, - { - "type": "mrkdwn", - "text": "*Branch*: $CIRCLE_BRANCH" - }, - { - "type": "mrkdwn", - "text": "*Author*: @${CIRCLE_USERNAME}" - } - ], - "accessory": { - "type": "image", - "image_url": "https://assets.brandfolder.com/otz5mn-bw4j2w-6jzqo8/original/circle-logo-badge-black.png", - "alt_text": "CircleCI logo" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*Message*: << parameters.message >>" - } - }, - { - "type": "actions", - "elements": [ - { - "type": "button", - "text": { - "type": "plain_text", - "text": "View Job" - }, - "url": "${CIRCLE_BUILD_URL}" - } - ] - } - ] - } - - locate-derived-data-directory: - parameters: - base_name: - type: string - steps: - - run: - name: Locate derived data directory - command: | - echo "export DERIVED_DATA_PATH=\"$(find ~/Library/Developer/Xcode/DerivedData -name "<< parameters.base_name >>*" -depth 1)\"" >> $BASH_ENV - - parse-release-version-if-available: - steps: - - run: - name: Parse SDK version from release tag - command: | - if [[ -n "$CIRCLE_TAG" ]]; then - echo "export VERSION=${CIRCLE_TAG#v}" >> $BASH_ENV - fi - - declare-default-artifacts-path: - steps: - - run: - name: Create default artifacts folder - command: | - echo "export DEFAULT_ARTIFACTS_DIR='$(mktemp -d)'" >> $BASH_ENV - cat $BASH_ENV - - store-default-artifacts: - steps: - - run: - name: Compress default artifacts - command: | - CURRENT_DIR=$(pwd) - cd $DEFAULT_ARTIFACTS_DIR - zip -yr "$CURRENT_DIR/artifacts.zip" . || true - when: always - - store_artifacts: - path: artifacts.zip - - turf-report-pending: - parameters: - turf-revision: - type: string - steps: - - run: - name: Set Turf commit status to pending - command: | - curl --request POST \ - --url https://api.github.com/repos/mapbox/turf-swift/statuses/<< parameters.turf-revision >> \ - --header 'Accept: application/vnd.github.v3+json' \ - --header "Authorization: token $(mbx-ci github notifier token)" \ - --data "{\"state\":\"pending\",\"target_url\":\"$CIRCLE_BUILD_URL\",\"description\":\"MapboxMaps SDK integration tests are running\",\"context\":\"Maps SDK Integration\"}" - - turf-report-success: - parameters: - turf-revision: - type: string - steps: - - run: - name: Set Turf commit status to success - command: | - curl --request POST \ - --url https://api.github.com/repos/mapbox/turf-swift/statuses/<< parameters.turf-revision >> \ - --header 'Accept: application/vnd.github.v3+json' \ - --header "Authorization: token $(mbx-ci github notifier token)" \ - --data "{\"state\":\"success\",\"target_url\":\"$CIRCLE_BUILD_URL\",\"description\":\"MapboxMaps SDK integration tests passed\",\"context\":\"Maps SDK Integration\"}" - - turf-report-failure: - parameters: - turf-revision: - type: string - steps: - - run: - when: on_fail - name: Set Turf commit status to failure - command: | - curl --request POST \ - --url https://api.github.com/repos/mapbox/turf-swift/statuses/<< parameters.turf-revision >> \ - --header 'Accept: application/vnd.github.v3+json' \ - --header "Authorization: token $(mbx-ci github notifier token)" \ - --data "{\"state\":\"failure\",\"target_url\":\"$CIRCLE_BUILD_URL\",\"description\":\"MapboxMaps SDK integration tests failed\",\"context\":\"Maps SDK Integration\"}" diff --git a/.depsvalidator.yml b/.depsvalidator.yml index 95ed70dfbca5..120d6b8f5910 100644 --- a/.depsvalidator.yml +++ b/.depsvalidator.yml @@ -2,25 +2,58 @@ manifests: - type: Package.swift - type: Package.resolved + omit_for: + # Temporarily disabled since we use .binaryTarget instead of SPM dependency for MapboxCoreMaps + - MapboxCoreMaps + - MapboxCommon - type: Package.resolved - path: Apps/Apps.xcworkspace/xcshareddata/swiftpm/Package.resolved + path: Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved + omit_for: + # Temporarily disabled since we use .binaryTarget instead of SPM dependency for MapboxCoreMaps + - MapboxCoreMaps + - MapboxCommon - type: Podspec - type: packager + - type: Cross-check Common SDK version with GL Native + omit_for: + - MapboxCoreMaps + - MapboxCommon + - Turf dependencies: - name: MapboxCoreMaps variations: Package.swift: mapbox-core-maps-ios + Package.resolved: mapbox-core-maps-ios - name: MapboxCommon variations: Package.swift: mapbox-common-ios + Package.resolved: mapbox-common-ios - name: Turf variations: Package.swift: turf-swift - - name: MapboxMobileEvents - variations: - Package.swift: mapbox-events-ios + Package.resolved: turf-swift manifest_definitions: - name: packager type: SemanticVersion default_path: scripts/release/packager/versions.json command: jq -r ".$DEPSVALIDATOR_DEPENDENCY_NAME" "$DEPSVALIDATOR_MANIFEST_PATH" + - name: Cross-check Common SDK version with GL Native + type: SemanticVersion + default_path: gl-native-common-version(virtual_manifest) + command: | + set -e + export GITHUB_TOKEN=$(mbx-ci github reader token) + if [[ -z "$GITHUB_TOKEN" ]]; + then + exit 1 + fi + COREMAPS_VERSION=$(jq -r .MapboxCoreMaps scripts/release/packager/versions.json) + if [[ "$COREMAPS_VERSION" == *"SNAPSHOT"* ]]; + then + # take commit hash from a snapshot string, e.g. 11.0.0-SNAPSHOT.1012T1154Z.cad0c70 + GL_NATIVE_GITHUB_REF=$(echo "$COREMAPS_VERSION" | cut -d "." -f 5) + else + GL_NATIVE_GITHUB_REF="maps-v$COREMAPS_VERSION" + fi + + gh api -H "Accept: application/vnd.github+json" "/repos/mapbox/mapbox-gl-native-internal/contents/common-version.txt?ref=$GL_NATIVE_GITHUB_REF" --jq ".content" | base64 --decode diff --git a/.fastlane/Fastfile b/.fastlane/Fastfile index 6a40005d7a78..4c9a92e87461 100644 --- a/.fastlane/Fastfile +++ b/.fastlane/Fastfile @@ -17,26 +17,57 @@ ENV["SPACESHIP_SKIP_2FA_UPGRADE"] = "1" platform :ios do + lane :setup_distribution_cert do + setup_ci + match(type: "appstore") + end + + private_lane :api_key_if_needed do + # We recommend to have api key on local machines to improve sign-in experience + # While on the CI we will use api key components for the authentication + # You can setup the api key by following the guide: https://github.com/mapbox/apple-internal/blob/master/guides/Modern%20authentification.md + file_path = ENV['APP_STORE_CONNECT_API_KEY_PATH'] + + if file_path.nil? || !File.exist?(file_path) + app_store_connect_api_key + end + end + lane :build_examples_tests do + api_key_if_needed setup_circle_ci sync_code_signing update_code_signing_settings( use_automatic_signing: false, - path: "Apps/Examples/Examples.xcodeproj", + path: "Examples.xcodeproj", + team_id: "GJZR2MEM28", # Developer Portal Team ID, + profile_name: lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING]["mapbox.ExamplesUITests.xctrunner"], + targets: ["ExamplesUITests"], + code_sign_identity: "Apple Development: Created via API", + ) + update_code_signing_settings( + use_automatic_signing: false, + path: "Examples.xcodeproj", team_id: "GJZR2MEM28", # Developer Portal Team ID, profile_name: lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING]["com.mapbox.examples"], targets: ["Examples"], - code_sign_identity: "Apple Development: Machiney McMachineface", + code_sign_identity: "Apple Development: Created via API", ) run_tests( - workspace: 'Apps/Apps.xcworkspace', + project: 'Examples.xcodeproj', scheme: 'Examples', # XCTest scheme clean: true, # Recommended: This would ensure the build would not include unnecessary files - configuration: "Release", - xcargs: "ENABLE_TESTABILITY=YES SWIFT_TREAT_WARNINGS_AS_ERRORS=NO", + # Use the Debug mode here to avoid the compiler crash + # https://github.com/apple/swift/issues/72117#issuecomment-1999619388 + # using the SWIFT_COMPILATION_MODE=incremental doesn't for this scheme. + # configuration: "Release", + configuration: "Debug", + testplan: "Examples no unit tests", + xcargs: "ENABLE_TESTABILITY=YES SWIFT_TREAT_WARNINGS_AS_ERRORS=NO COMPILER_INDEX_STORE_ENABLE=NO", skip_detect_devices: true, # Required build_for_testing: true, # Required sdk: 'iphoneos', # Required + destination: "generic/platform=iOS", # Required should_zip_build_products: true, # Must be true to set the correct format for Firebase Test Lab, result_bundle: true, output_directory: "output/", @@ -65,10 +96,10 @@ platform :ios do end lane :beta do - app_store_connect_api_key # Generate API Token + api_key_if_needed # Generate API Token increment_build_number( build_number: latest_testflight_build_number + 1, - xcodeproj: 'Apps/Examples/Examples.xcodeproj' + xcodeproj: 'Examples.xcodeproj' ) build_and_submit end @@ -80,16 +111,18 @@ platform :ios do sync_code_signing(type: "development") update_code_signing_settings( use_automatic_signing: false, - path: "Apps/Examples/Examples.xcodeproj", + path: "Examples.xcodeproj", team_id: "GJZR2MEM28", # Developer Portal Team ID, profile_name: lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING]["com.mapbox.examples"], - targets: ["Examples"] + targets: ["Examples"], + code_sign_identity: "Apple Development: Created via API", + entitlements_file_path: "Sources/Examples/Examples_CarPlay.entitlements" ) sync_code_signing(type: "appstore") build_app( scheme: "Examples", - workspace: "Apps/Apps.xcworkspace", - xcargs: "SWIFT_TREAT_WARNINGS_AS_ERRORS=NO" # Disable to bypass Deprecated error on OfflineManager example + project: "Examples.xcodeproj", + xcargs: "SWIFT_TREAT_WARNINGS_AS_ERRORS=NO COMPILER_INDEX_STORE_ENABLE=NO" # Disable to bypass Deprecated error on OfflineManager example ) upload_to_testflight( beta_app_feedback_email: "applemachine@mapbox.com", @@ -101,7 +134,7 @@ platform :ios do lane :build_tests do sh("cd .. && xcodegen") - app_store_connect_api_key # Generate API Token + api_key_if_needed # Generate API Token setup_circle_ci sync_code_signing(app_identifier: "com.mapbox.MapboxMapsTestHost") update_code_signing_settings( @@ -110,14 +143,16 @@ platform :ios do team_id: "GJZR2MEM28", # Developer Portal Team ID, profile_name: lane_context[SharedValues::MATCH_PROVISIONING_PROFILE_MAPPING]["com.mapbox.MapboxMapsTestHost"], targets: ["MapboxTestHost"], - code_sign_identity: "Apple Development: Machiney McMachineface", + code_sign_identity: "Apple Development: Created via API", ) run_tests( project: 'MapboxMaps.xcodeproj', scheme: 'MapboxTestHost', clean: true, configuration: "Release", - xcargs: "ENABLE_TESTABILITY=YES", + # Use here SWIFT_COMPILATION_MODE=incremental to avoid the compiler crash + # https://github.com/apple/swift/issues/72117#issuecomment-1999619388 + xcargs: "ENABLE_TESTABILITY=YES COMPILER_INDEX_STORE_ENABLE=NO SWIFT_COMPILATION_MODE=incremental", build_for_testing: true, destination: 'generic/platform=iOS', should_zip_build_products: true, diff --git a/.fastlane/Matchfile b/.fastlane/Matchfile index 5698edb2af6c..ab0f3114dab7 100644 --- a/.fastlane/Matchfile +++ b/.fastlane/Matchfile @@ -1,3 +1,4 @@ git_url("git@github.com:mapbox/apple-certificates.git") type("development") # The default type, can be: appstore, adhoc, enterprise or development -app_identifier(["com.mapbox.examples"]) +app_identifier(["com.mapbox.examples", "mapbox.ExamplesUITests.xctrunner"]) +template_name("CarPlay Navigation App (Distribution)") diff --git a/.gitattributes b/.gitattributes index 4f42a566db5b..74553376fac0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,4 +3,5 @@ CHANGELOG.md merge=union # Mark files as generated in Github PRs -Sources/MapboxMaps/Style/Generated/** linguist-generated=true \ No newline at end of file +**/Generated/** linguist-generated=true +LICENSE.md linguist-generated=true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 510385eb4a11..d77220d04f9e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @mapbox/maps-ios \ No newline at end of file +* @mapbox/maps-ios diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index 8e1d904bfa13..b09c06acda93 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -1,5 +1,5 @@ --- -name: Bug +name: Bug about: This template should be used for reporting bugs and defects. labels: 'bug :beetle:' assignees: '' diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md index f9867e5ea80e..31b78e4d3fbc 100644 --- a/.github/ISSUE_TEMPLATE/feature.md +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -13,4 +13,3 @@ assignees: '' ## Why <- 1-2 sentence description of why you're requesting this feature. What problem does it solve? Why is it valuable? -> - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3cb5adab83d8..ad3b02ccdad3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -16,10 +16,21 @@ Describe the changes in this PR here. --> ## Pull request checklist: - - [ ] Write tests for all new functionality. If tests were not written, please explain why. - - [ ] Add documentation comments for any added or updated public APIs. - - [ ] Add any new public, top-level symbols to the Jazzy config's `custom_categories` (scripts/doc-generation/.jazzy.yaml) - [ ] Describe the changes in this PR, especially public API changes. + - [ ] Include before/after visuals or gifs if this PR includes visual changes. + + - [ ] Write tests for all new functionality. Put tests in correct [Test Plan](https://github.com/mapbox/mapbox-maps-ios/tree/main/Tests/TestPlans) (Unit, Integration, All) + - [ ] If tests were not written, please explain why. + - [ ] Add documentation comments for any added or updated public APIs. + - [ ] Add any new public, top-level symbols to the DocC custom catatlog (Sources/MapboxMaps/Documentation.docc/API Catalogs) - [ ] Add a changelog entry to to bottom of the relevant section (typically the `## main` heading near the top). - [ ] Update the guides (internal access only), README.md, and DEVELOPING.md if their contents are impacted by these changes. - - [ ] Review and agree to the Contributor License Agreement ([CLA](https://github.com/mapbox/mapbox-maps-ios/blob/main/CONTRIBUTING.md#contributor-license-agreement)). + - [ ] If this PR is a `v10.[version]` release branch fix / enhancement, merge it to `main` first and then port to `v10.[version]` release branch. + +PRs must be submitted under the terms of our Contributor License Agreement [CLA](https://github.com/mapbox/mapbox-maps-ios/blob/main/CONTRIBUTING.md#contributor-license-agreement). diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..0d08e261a2ae --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "github-actions" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/stale.yml b/.github/stale.yml index 8c56ac58aee5..407883d5ce5d 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -13,7 +13,7 @@ exemptLabels: pulls: daysUntilStale: 60 - daysUntilClose: -1 + daysUntilClose: 7 markComment: false closeComment: > This pull request has been automatically detected as stale because it has not had diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml new file mode 100644 index 000000000000..d6ab1aa35055 --- /dev/null +++ b/.github/workflows/analyze.yml @@ -0,0 +1,85 @@ +on: + workflow_call: + inputs: + project-path: + required: true + type: string + scheme: + type: string + default: MapboxMaps + destination: + type: string + default: generic/platform=iOS Simulator + executor: + type: string + default: '["ubuntu-latest"]' + secrets: + SDK_REGISTRY_TOKEN: + required: true + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze + runs-on: ${{ matrix.executor }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["javascript", "python", "ruby"] + executor: ${{ fromJson(inputs.executor) }} + include: + - language: "swift" + executor: "macos-13" + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Allow cloning public repositories from github.com via SSH + run: | + git config --global url.https://github.com/.insteadOf git@github.com: + + - name: Inject tokens + if: matrix.language == 'swift' + run: | + echo "machine api.mapbox.com login mapbox password ${{ secrets.SDK_REGISTRY_TOKEN }}" >> ~/.netrc + chmod 0600 ~/.netrc + + echo "test_token" > ~/.mapbox + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - name: Build Examples + if: matrix.language == 'swift' + run: set -o pipefail && xcodebuild build -project '${{ inputs.project-path }}' -scheme ${{ inputs.scheme }} -destination '${{ inputs.destination }}'| tee xcodebuild.log | xcpretty + + - name: Archive xcodebuild.log + uses: actions/upload-artifact@v4 + if: matrix.language == 'swift' + with: + name: xcodebuild.log + path: xcodebuild.log + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000000..4de81291321b --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,16 @@ +name: "CodeQL" + +on: + push: + branches: ['main', 'publisher-production', 'release/*', 'lts/*'] + pull_request: + branches: ['main', 'publisher-production', 'release/*', 'lts/*'] + schedule: + - cron: "16 20 * * 4" + +jobs: + analyze: + uses: ./.github/workflows/analyze.yml + with: + project-path: Examples.xcodeproj + secrets: inherit diff --git a/.gitignore b/.gitignore index 0a0c6ab66c5d..e254c01cfd87 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,13 @@ xcuserdata node_modules developer.xcconfig Carthage -api-docs +Cartfile* MapboxAccessToken scripts/release/packager/artifacts scripts/release/packager/build scripts/xcparty/.swiftpm MapboxMaps.xcodeproj +*.doccarchive +Sources/MapboxMaps/Documentation.docc/*.html -.vscode \ No newline at end of file +.vscode diff --git a/.gitmodules b/.gitmodules index ac277cf41727..e69de29bb2d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "jazzy-theme"] - path = scripts/doc-generation/jazzy-theme - url = https://github.com/mapbox/jazzy-theme.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..f57a80ea340e --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + exclude: ^(LICENSE.md|.*\.swiftinterface)$ + - id: end-of-file-fixer + exclude: ".*.list" + - id: check-yaml + - id: check-json + exclude: .vscode/.*.json + - id: check-byte-order-marker + - id: check-merge-conflict +- repo: "https://github.com/realm/SwiftLint" + rev: 0.55.1 + hooks: + - id: swiftlint + entry: swiftlint --fix --strict +- repo: https://github.com/psf/black + rev: 24.4.2 + hooks: + - id: black + language_version: python3.12 +- repo: https://github.com/gitleaks/gitleaks + rev: v8.16.1 + hooks: + - id: gitleaks diff --git a/.swiftlint.yml b/.swiftlint.yml index 2152dc29b81e..f5ee866dcf81 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,22 +1,21 @@ --- allow_zero_lintable_files: true +included: + - Sources + - Tests excluded: - - Carthage - - Pods - - Sources/MapboxMaps/Style/Generated - - Tests/MapboxMapsTests/Style/Generated - - "**/.build" + - "**/Generated" disabled_rules: - comment_spacing - computed_accessors_order - force_try - - function_body_length - identifier_name - line_length - shorthand_operator - todo - trailing_comma - type_name + - type_body_length custom_rules: trojan_source: regex: "[\u202A\u202B\u202D\u202E\u2066\u2067\u2068\u202C\u2069]" diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/MapboxMaps.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/MapboxMaps.xcscheme index e3ebb1d4e485..a1cb25afc888 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/MapboxMaps.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/MapboxMaps.xcscheme @@ -1,6 +1,6 @@ + title = "Inject Tests/MapboxMapsTests/Helpers/MapboxAccessToken " + scriptText = "TOKEN_FILE=~/.mapbox TOKEN_FILE_2=~/mapbox TOKEN="$(cat $TOKEN_FILE 2>/dev/null || cat $TOKEN_FILE_2 2>/dev/null)" if [ "$TOKEN" ]; then echo "Build pre-action step using ${TOKEN:0:5}" echo "$WORKSPACE_PATH" if [ $(basename "$WORKSPACE_PATH") = "package.xcworkspace" ]; then MBX_ROOT=$(echo "$WORKSPACE_PATH" | rev | cut -d'/' -f4- | rev) elif [ $(basename "$WORKSPACE_PATH") = "project.xcworkspace" ]; then MBX_ROOT=$(echo "$WORKSPACE_PATH" | rev | cut -d'/' -f5- | rev) else echo "Unknown workspace" exit 1 # Unknown workspace fi echo "$TOKEN" > "$MBX_ROOT/Tests/MapboxMapsTests/Helpers/MapboxAccessToken" else echo "Build pre-action step: Could not find access token" fi "> - - - - - - + shouldUseLaunchSchemeArgsEnv = "YES" + enableAddressSanitizer = "YES" + enableASanStackUseAfterReturn = "YES"> + + + + + + + + diff --git a/Apps/Apps.xcworkspace/contents.xcworkspacedata b/Apps/Apps.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 893f46ec8bb9..000000000000 --- a/Apps/Apps.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - diff --git a/Apps/Apps.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Apps/Apps.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/Apps/Apps.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Apps/Apps.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Apps/Apps.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index b03b4ccfe3c6..000000000000 --- a/Apps/Apps.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,52 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "CocoaImageHashing", - "repositoryURL": "https://github.com/ameingast/cocoaimagehashing", - "state": { - "branch": null, - "revision": "ad01eee3c3f91bd181f7f4eba7f48f7cb211b51c", - "version": "1.9.0" - } - }, - { - "package": "MapboxCommon", - "repositoryURL": "https://github.com/mapbox/mapbox-common-ios.git", - "state": { - "branch": null, - "revision": "25794d3d3a0dab1763b7973ca217feb5e073806c", - "version": "22.1.0-rc.1" - } - }, - { - "package": "MapboxCoreMaps", - "repositoryURL": "https://github.com/mapbox/mapbox-core-maps-ios.git", - "state": { - "branch": null, - "revision": "6640d6d46a6ceee6b50c451f38975d66ff5ba291", - "version": "10.7.0-rc.1" - } - }, - { - "package": "MapboxMobileEvents", - "repositoryURL": "https://github.com/mapbox/mapbox-events-ios.git", - "state": { - "branch": null, - "revision": "5e130fba637f5bbb0bb8bca0dec8d38a06ce07bf", - "version": "1.0.8" - } - }, - { - "package": "Turf", - "repositoryURL": "https://github.com/mapbox/turf-swift.git", - "state": { - "branch": null, - "revision": "569e0f0e96fda86e1a1dcefaefa68cfa9491ffc1", - "version": "2.4.0" - } - } - ] - }, - "version": 1 -} diff --git a/Apps/Configurations/base.xcconfig b/Apps/Configurations/base.xcconfig deleted file mode 100644 index aa65ab25eadd..000000000000 --- a/Apps/Configurations/base.xcconfig +++ /dev/null @@ -1,132 +0,0 @@ -// Common configurations used across projects and targets -// Sorted alphabetically within sections - -// ----------------------------------------------------------------------------- -// Build System - -ALWAYS_SEARCH_USER_PATHS = NO - -DEVELOPMENT_TEAM = GJZR2MEM28 - -ENABLE_TESTABILITY[config=Release] = NO -ENABLE_TESTABILITY[config=Debug] = YES - -IPHONEOS_DEPLOYMENT_TARGET = 11.0 -MACOSX_DEPLOYMENT_TARGET = - -ONLY_ACTIVE_ARCH[config=Release] = NO -ONLY_ACTIVE_ARCH[config=Debug] = YES - -SDKROOT = iphoneos -SKIP_INSTALL = YES -SUPPORTS_MACCATALYST = NO - -// ----------------------------------------------------------------------------- -// Swift - -SWIFT_VERSION = 5.0 - -SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = DEBUG -SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Release] = RELEASE - -SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone -SWIFT_OPTIMIZATION_LEVEL[config=Release] = -O - -// ----------------------------------------------------------------------------- -// Clang - -CLANG_ANALYZER_GCD_PERFORMANCE = YES -CLANG_ANALYZER_LOCALIZABILITY_EMPTY_CONTEXT = YES -CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES -CLANG_ANALYZER_NONNULL = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES -CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE - -CLANG_CXX_LANGUAGE_STANDARD = compiler-default -CLANG_CXX_LIBRARY = compiler-default - -CLANG_ENABLE_MODULES = YES -CLANG_ENABLE_OBJC_ARC = YES -CLANG_ENABLE_OBJC_WEAK = YES - -// Static analysis doesn't include Swift, so no harm enabling for those cases -// where there may be some Obj-C -CLANG_STATIC_ANALYZER_MODE[config=Debug] = shallow -CLANG_STATIC_ANALYZER_MODE[config=Release] = deep - -CLANG_USE_OPTIMIZATION_PROFILE = NO - -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_ASSIGN_ENUM = YES -CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES -CLANG_WARN_BOOL_CONVERSION = YES -CLANG_WARN_COMMA = YES -CLANG_WARN_CONSTANT_CONVERSION = YES -CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES -CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR -CLANG_WARN_DOCUMENTATION_COMMENTS = YES -CLANG_WARN_EMPTY_BODY = YES -CLANG_WARN_ENUM_CONVERSION = YES -CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES -CLANG_WARN_INFINITE_RECURSION = YES -CLANG_WARN_INT_CONVERSION = YES -CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -CLANG_WARN_OBJC_INTERFACE_IVARS = YES -CLANG_WARN_OBJC_LITERAL_CONVERSION = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR -CLANG_WARN_RANGE_LOOP_ANALYSIS = YES -CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES -CLANG_WARN_SUSPICIOUS_MOVE = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN_UNREACHABLE_CODE = YES - -ENABLE_STRICT_OBJC_MSGSEND = YES - -GCC_C_LANGUAGE_STANDARD = compiler-default - -GCC_DYNAMIC_NO_PIC = NO -GCC_ENABLE_CPP_RTTI = NO - -GCC_NO_COMMON_BLOCKS = YES - -GCC_OPTIMIZATION_LEVEL[config=Debug] = 0 -GCC_OPTIMIZATION_LEVEL[config=Release] = s - -GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG=1 $(inherited) -GCC_PREPROCESSOR_DEFINITIONS[config=Release] = RELEASE=1 $(inherited) - -GCC_TREAT_WARNINGS_AS_ERRORS = YES - -GCC_WARN_64_TO_32_BIT_CONVERSION = YES -GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES -GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR -GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES -GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES -GCC_WARN_SHADOW = YES -GCC_WARN_SIGN_COMPARE = YES -GCC_WARN_UNDECLARED_SELECTOR = YES -GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE -GCC_WARN_UNKNOWN_PRAGMAS = YES -GCC_WARN_UNUSED_FUNCTION = YES -GCC_WARN_UNUSED_LABEL = YES -GCC_WARN_UNUSED_VARIABLE = YES - -LD_GENERATE_MAP_FILE = YES - -RUN_CLANG_STATIC_ANALYZER = YES - -SWIFT_TREAT_WARNINGS_AS_ERRORS[config=Debug] = NO -SWIFT_TREAT_WARNINGS_AS_ERRORS[config=Release] = YES - -// ----------------------------------------------------------------------------- -// Custom - -CURRENT_COMMIT_HASH = deadbeef -#include? "../../developer.xcconfig" - diff --git a/Apps/Configurations/unitTests.xcconfig b/Apps/Configurations/unitTests.xcconfig deleted file mode 100644 index 71ac8d1795d5..000000000000 --- a/Apps/Configurations/unitTests.xcconfig +++ /dev/null @@ -1,6 +0,0 @@ - -#include "base.xcconfig" - -LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/../lib - -IPHONEOS_DEPLOYMENT_TARGET = 12.2 diff --git a/Apps/DebugApp/DebugApp.xcodeproj/project.pbxproj b/Apps/DebugApp/DebugApp.xcodeproj/project.pbxproj deleted file mode 100644 index 0439fcab72d0..000000000000 --- a/Apps/DebugApp/DebugApp.xcodeproj/project.pbxproj +++ /dev/null @@ -1,324 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 35F08DDC2347987700768B9F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35F08DDB2347987700768B9F /* AppDelegate.swift */; }; - 35F08DE02347987700768B9F /* DebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35F08DDF2347987700768B9F /* DebugViewController.swift */; }; - 35F08DE52347987900768B9F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 35F08DE42347987900768B9F /* Assets.xcassets */; }; - 35F08DE82347987900768B9F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 35F08DE62347987900768B9F /* LaunchScreen.storyboard */; }; - 7473C3BA288995B40022279E /* DebugViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7473C3B9288995B40022279E /* DebugViewController.xib */; }; - B569C20125FEBC2E008B0D92 /* MapboxMaps in Frameworks */ = {isa = PBXBuildFile; productRef = B569C20025FEBC2E008B0D92 /* MapboxMaps */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 1F11C1502421BADF00F8397B /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 35F08DD82347987700768B9F /* DebugApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DebugApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 35F08DDB2347987700768B9F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 35F08DDF2347987700768B9F /* DebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugViewController.swift; sourceTree = ""; }; - 35F08DE42347987900768B9F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 35F08DE72347987900768B9F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 35F08DE92347987900768B9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7473C3B9288995B40022279E /* DebugViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DebugViewController.xib; sourceTree = ""; }; - CA56651F24479764006F392C /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 35F08DD52347987700768B9F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B569C20125FEBC2E008B0D92 /* MapboxMaps in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 35F08DCF2347987700768B9F = { - isa = PBXGroup; - children = ( - CA5665212447A658006F392C /* Configurations */, - 35F08DDA2347987700768B9F /* DebugApp */, - 35F08DD92347987700768B9F /* Products */, - ); - sourceTree = ""; - }; - 35F08DD92347987700768B9F /* Products */ = { - isa = PBXGroup; - children = ( - 35F08DD82347987700768B9F /* DebugApp.app */, - ); - name = Products; - sourceTree = ""; - }; - 35F08DDA2347987700768B9F /* DebugApp */ = { - isa = PBXGroup; - children = ( - 35F08DE92347987900768B9F /* Info.plist */, - 35F08DDB2347987700768B9F /* AppDelegate.swift */, - 35F08DDF2347987700768B9F /* DebugViewController.swift */, - 7473C3B9288995B40022279E /* DebugViewController.xib */, - 35F08DE42347987900768B9F /* Assets.xcassets */, - 35F08DE62347987900768B9F /* LaunchScreen.storyboard */, - ); - path = DebugApp; - sourceTree = ""; - }; - CA5665212447A658006F392C /* Configurations */ = { - isa = PBXGroup; - children = ( - CA56651F24479764006F392C /* base.xcconfig */, - ); - name = Configurations; - path = ../Configurations; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 35F08DD72347987700768B9F /* DebugApp */ = { - isa = PBXNativeTarget; - buildConfigurationList = 35F08E022347987900768B9F /* Build configuration list for PBXNativeTarget "DebugApp" */; - buildPhases = ( - 35F08DD42347987700768B9F /* Sources */, - 35F08DD52347987700768B9F /* Frameworks */, - 35F08DD62347987700768B9F /* Resources */, - 1F61E3E423AC170100FAE7C2 /* Insert Mapbox Token */, - 1F11C1502421BADF00F8397B /* Embed Frameworks */, - FE98B42328816F490060C509 /* Run swiftlint */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = DebugApp; - packageProductDependencies = ( - B569C20025FEBC2E008B0D92 /* MapboxMaps */, - ); - productName = DebugApp; - productReference = 35F08DD82347987700768B9F /* DebugApp.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 35F08DD02347987700768B9F /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1100; - LastUpgradeCheck = 1340; - ORGANIZATIONNAME = "Mapbox Inc"; - TargetAttributes = { - 35F08DD72347987700768B9F = { - CreatedOnToolsVersion = 11.0; - }; - }; - }; - buildConfigurationList = 35F08DD32347987700768B9F /* Build configuration list for PBXProject "DebugApp" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 35F08DCF2347987700768B9F; - productRefGroup = 35F08DD92347987700768B9F /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 35F08DD72347987700768B9F /* DebugApp */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 35F08DD62347987700768B9F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 35F08DE82347987900768B9F /* LaunchScreen.storyboard in Resources */, - 35F08DE52347987900768B9F /* Assets.xcassets in Resources */, - 7473C3BA288995B40022279E /* DebugViewController.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 1F61E3E423AC170100FAE7C2 /* Insert Mapbox Token */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)", - ); - name = "Insert Mapbox Token"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "$SRCROOT/../../scripts/insert_access_token.sh\n"; - }; - FE98B42328816F490060C509 /* Run swiftlint */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run swiftlint"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "$SRCROOT/../../scripts/run_swiftlint.sh\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 35F08DD42347987700768B9F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 35F08DE02347987700768B9F /* DebugViewController.swift in Sources */, - 35F08DDC2347987700768B9F /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 35F08DE62347987900768B9F /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 35F08DE72347987900768B9F /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 35F08E002347987900768B9F /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CA56651F24479764006F392C /* base.xcconfig */; - buildSettings = { - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_TESTABILITY = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = "$(GCC_WARN_64_TO_32_BIT_CONVERSION)"; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - }; - name = Debug; - }; - 35F08E012347987900768B9F /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CA56651F24479764006F392C /* base.xcconfig */; - buildSettings = { - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = "$(GCC_WARN_64_TO_32_BIT_CONVERSION)"; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 35F08E032347987900768B9F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = DebugApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.DebugApp; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 35F08E042347987900768B9F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - INFOPLIST_FILE = DebugApp/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.DebugApp; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 35F08DD32347987700768B9F /* Build configuration list for PBXProject "DebugApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 35F08E002347987900768B9F /* Debug */, - 35F08E012347987900768B9F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 35F08E022347987900768B9F /* Build configuration list for PBXNativeTarget "DebugApp" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 35F08E032347987900768B9F /* Debug */, - 35F08E042347987900768B9F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCSwiftPackageProductDependency section */ - B569C20025FEBC2E008B0D92 /* MapboxMaps */ = { - isa = XCSwiftPackageProductDependency; - productName = MapboxMaps; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 35F08DD02347987700768B9F /* Project object */; -} diff --git a/Apps/DebugApp/DebugApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Apps/DebugApp/DebugApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 0814658adb7d..000000000000 --- a/Apps/DebugApp/DebugApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Apps/DebugApp/DebugApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Apps/DebugApp/DebugApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/Apps/DebugApp/DebugApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Apps/DebugApp/DebugApp.xcodeproj/xcshareddata/xcschemes/DebugApp.xcscheme b/Apps/DebugApp/DebugApp.xcodeproj/xcshareddata/xcschemes/DebugApp.xcscheme deleted file mode 100644 index fea36e5ee414..000000000000 --- a/Apps/DebugApp/DebugApp.xcodeproj/xcshareddata/xcschemes/DebugApp.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/DebugApp/DebugApp/AppDelegate.swift b/Apps/DebugApp/DebugApp/AppDelegate.swift deleted file mode 100644 index 875a3376fe05..000000000000 --- a/Apps/DebugApp/DebugApp/AppDelegate.swift +++ /dev/null @@ -1,14 +0,0 @@ -import UIKit - -@UIApplicationMain -final class AppDelegate: UIResponder, UIApplicationDelegate { - - var window: UIWindow? - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - window = UIWindow(frame: UIScreen.main.bounds) - window?.rootViewController = DebugViewController() - window?.makeKeyAndVisible() - return true - } -} diff --git a/Apps/DebugApp/DebugApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Apps/DebugApp/DebugApp/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d8db8d65fd79..000000000000 --- a/Apps/DebugApp/DebugApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "20x20", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "83.5x83.5", - "scale" : "2x" - }, - { - "idiom" : "ios-marketing", - "size" : "1024x1024", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Apps/DebugApp/DebugApp/Assets.xcassets/star.imageset/Contents.json b/Apps/DebugApp/DebugApp/Assets.xcassets/star.imageset/Contents.json deleted file mode 100644 index 80fb0f24a9d1..000000000000 --- a/Apps/DebugApp/DebugApp/Assets.xcassets/star.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "star.pdf", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/DebugApp/DebugApp/Assets.xcassets/star.imageset/star.pdf b/Apps/DebugApp/DebugApp/Assets.xcassets/star.imageset/star.pdf deleted file mode 100644 index e3b51002e365..000000000000 Binary files a/Apps/DebugApp/DebugApp/Assets.xcassets/star.imageset/star.pdf and /dev/null differ diff --git a/Apps/DebugApp/DebugApp/Assets.xcassets/triangle.imageset/Contents.json b/Apps/DebugApp/DebugApp/Assets.xcassets/triangle.imageset/Contents.json deleted file mode 100644 index 5c913c996aa4..000000000000 --- a/Apps/DebugApp/DebugApp/Assets.xcassets/triangle.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "triangle.pdf", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/DebugApp/DebugApp/Assets.xcassets/triangle.imageset/triangle.pdf b/Apps/DebugApp/DebugApp/Assets.xcassets/triangle.imageset/triangle.pdf deleted file mode 100644 index 68b1d6d8c086..000000000000 Binary files a/Apps/DebugApp/DebugApp/Assets.xcassets/triangle.imageset/triangle.pdf and /dev/null differ diff --git a/Apps/DebugApp/DebugApp/Base.lproj/LaunchScreen.storyboard b/Apps/DebugApp/DebugApp/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e9329f376..000000000000 --- a/Apps/DebugApp/DebugApp/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/DebugApp/DebugApp/DebugViewController.swift b/Apps/DebugApp/DebugApp/DebugViewController.swift deleted file mode 100644 index 87610dc75550..000000000000 --- a/Apps/DebugApp/DebugApp/DebugViewController.swift +++ /dev/null @@ -1,20 +0,0 @@ -import UIKit -import MapboxMaps - -/** - NOTE: This view controller should be used as a scratchpad - while you develop new features. Changes to this file - should not be committed. - */ - -final class DebugViewController: UIViewController { - - var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.insertSubview(mapView, at: 0) - } -} diff --git a/Apps/DebugApp/DebugApp/DebugViewController.xib b/Apps/DebugApp/DebugApp/DebugViewController.xib deleted file mode 100644 index 2eb260ef0b46..000000000000 --- a/Apps/DebugApp/DebugApp/DebugViewController.xib +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/DebugApp/DebugApp/Info.plist b/Apps/DebugApp/DebugApp/Info.plist deleted file mode 100644 index 44bc1f46db53..000000000000 --- a/Apps/DebugApp/DebugApp/Info.plist +++ /dev/null @@ -1,56 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - MBXAccessToken - - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSLocationWhenInUseUsageDescription - The map will display your location. - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - NSLocationTemporaryUsageDescriptionDictionary - - LocationAccuracyAuthorizationDescription - Debug App temporarily requires your precise location - - MMEDebugLogging - - MMECollectionEnabledInSimulator - - - diff --git a/Apps/DebugApp/DebugApp/polygons.geojson b/Apps/DebugApp/DebugApp/polygons.geojson deleted file mode 100644 index 5372466e916c..000000000000 --- a/Apps/DebugApp/DebugApp/polygons.geojson +++ /dev/null @@ -1,6 +0,0 @@ -{ - "type": "MultiPolygon", - "coordinates": [ - - ] -} diff --git a/Apps/DebugApp/DebugApp/third_party_vector_style.json b/Apps/DebugApp/DebugApp/third_party_vector_style.json deleted file mode 100644 index c41bb2b57342..000000000000 --- a/Apps/DebugApp/DebugApp/third_party_vector_style.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "version": 8, - "sources": { - "mapillary": { - "type": "vector", - "tiles": ["https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt"], - "attribution": "© Mapillary, CC BY", - "maxzoom": 14 - } - }, - "layers": [{ - "id": "background", - "type": "background", - "paint": { - "background-color": "#485E77" - } - }, { - "id": "mapillary-sequences", - "type": "line", - "source": "mapillary", - "source-layer": "mapillary-sequences", - "filter": ["==", "$type", "LineString"], - "paint": { - "line-color": "#F56745" - } - }] -} - diff --git a/Apps/Examples/Examples.xcodeproj/project.pbxproj b/Apps/Examples/Examples.xcodeproj/project.pbxproj deleted file mode 100644 index 620bff10e155..000000000000 --- a/Apps/Examples/Examples.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1094 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 0333B84F25ED942600D667C9 /* SceneKitExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0333B84E25ED942600D667C9 /* SceneKitExample.swift */; }; - 03BB33F925EDA19200109B28 /* 34M_17.dae in Resources */ = {isa = PBXBuildFile; fileRef = 03BB33F825EDA19200109B28 /* 34M_17.dae */; }; - 0706C4A625B1181A008733C0 /* TerrainExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0706C4A525B1181A008733C0 /* TerrainExample.swift */; }; - 073475D725AFAE520049B0B8 /* CustomLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 073475D625AFAE520049B0B8 /* CustomLayerExample.swift */; }; - 077C4EFA252F7E89007636F1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 077C4EF8252F7E89007636F1 /* LaunchScreen.storyboard */; }; - 077C4F05252F7E89007636F1 /* ExamplesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077C4F04252F7E89007636F1 /* ExamplesTests.swift */; }; - 077C4F10252F7E89007636F1 /* ExamplesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077C4F0F252F7E89007636F1 /* ExamplesUITests.swift */; }; - 07B071D32547CFC3007F2865 /* GeoJSONSourceExample.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 07B071D22547CFC3007F2865 /* GeoJSONSourceExample.geojson */; }; - 07DC84422538B1F100F4AF14 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 07DC84412538B1F100F4AF14 /* Assets.xcassets */; }; - 0C52BA9825AF8C880054ECA8 /* Custom3DPuckExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C52BA9725AF8C880054ECA8 /* Custom3DPuckExample.swift */; }; - 0C784D1126D002DC004AE7D0 /* FeatureStateExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C784D1026D002DC004AE7D0 /* FeatureStateExample.swift */; }; - 0C78AC2925BF70E40057F570 /* LineGradientExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C78AC2825BF70E40057F570 /* LineGradientExample.swift */; }; - 0C78AC2F25BF72C70057F570 /* GradientLine.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 0C78AC2C25BF71E40057F570 /* GradientLine.geojson */; }; - 0CC4ECEA25B8AD3000F998B8 /* Custom2DPuckExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC4ECE925B8AD3000F998B8 /* Custom2DPuckExample.swift */; }; - 177C9D47269CA61100D13A2D /* ShowHideLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177C9D46269CA61100D13A2D /* ShowHideLayerExample.swift */; }; - 17AF784026837E91006EA30D /* Fire_Hydrants.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 17AF783F26837E91006EA30D /* Fire_Hydrants.geojson */; }; - 17B40D2426A85500000887EF /* LiveDataExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17B40D2326A85500000887EF /* LiveDataExample.swift */; }; - 17B4805A2684FB2300CF0D5E /* AnimateImageLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17B480592684FB2300CF0D5E /* AnimateImageLayerExample.swift */; }; - 17B4806126851C6E00CF0D5E /* radar4.gif in Resources */ = {isa = PBXBuildFile; fileRef = 17B4805C26851C6E00CF0D5E /* radar4.gif */; }; - 17B4806226851C6E00CF0D5E /* radar2.gif in Resources */ = {isa = PBXBuildFile; fileRef = 17B4805D26851C6E00CF0D5E /* radar2.gif */; }; - 17B4806326851C6E00CF0D5E /* radar3.gif in Resources */ = {isa = PBXBuildFile; fileRef = 17B4805E26851C6E00CF0D5E /* radar3.gif */; }; - 17B4806426851C6E00CF0D5E /* radar1.gif in Resources */ = {isa = PBXBuildFile; fileRef = 17B4805F26851C6E00CF0D5E /* radar1.gif */; }; - 17B4806526851C6E00CF0D5E /* radar0.gif in Resources */ = {isa = PBXBuildFile; fileRef = 17B4806026851C6E00CF0D5E /* radar0.gif */; }; - 17B48067268A4E9300CF0D5E /* DistanceExpressionExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17B48066268A4E9300CF0D5E /* DistanceExpressionExample.swift */; }; - 17B4806B268BD91000CF0D5E /* RasterTileSourceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17B4806A268BD91000CF0D5E /* RasterTileSourceExample.swift */; }; - 17E28C5C2672A1160033DF0F /* SymbolClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17E28C5B2672A1160033DF0F /* SymbolClusteringExample.swift */; }; - 304AB3B527439287005B6D09 /* ViewAnnotationMarkerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304AB3B427439287005B6D09 /* ViewAnnotationMarkerExample.swift */; }; - 30517C6A274BD4D300B706E5 /* ViewAnnotationBasicExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30517C69274BD4D300B706E5 /* ViewAnnotationBasicExample.swift */; }; - 3A3AF0032836499F0036F483 /* route.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 3A3AF0022836499F0036F483 /* route.geojson */; }; - 3A7432EF27F3096100E06485 /* DebugMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7432EE27F3096100E06485 /* DebugMapExample.swift */; }; - 3A7CE986282511C900C3A0B8 /* NavigationSimulatorExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7CE985282511C900C3A0B8 /* NavigationSimulatorExample.swift */; }; - 3A7CE98B282AB0DE00C3A0B8 /* NavigationSimulator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7CE98A282AB0DE00C3A0B8 /* NavigationSimulator.swift */; }; - 5812B55127C5A0E500E10063 /* CustomLocationProviderExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5812B55027C5A0E500E10063 /* CustomLocationProviderExample.swift */; }; - 58248C2F2695FF24009AC598 /* StoryboardMapViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58248C2E2695FF24009AC598 /* StoryboardMapViewExample.swift */; }; - 5830FDB7270B5AF7005549EE /* sportcar.glb in Resources */ = {isa = PBXBuildFile; fileRef = 5830FDB6270B5AF7005549EE /* sportcar.glb */; }; - 587429C027BBFDA100104CCA /* VoiceOverAccessibilityExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587429BF27BBFDA100104CCA /* VoiceOverAccessibilityExample.swift */; }; - 58882E5D26962A7200E5C1C6 /* StoryboardMapViewExample.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 58882E5C26962A7200E5C1C6 /* StoryboardMapViewExample.storyboard */; }; - 58A28B5B2869F69C006F7D93 /* HeatmapLayerGlobeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A28B572869F69B006F7D93 /* HeatmapLayerGlobeExample.swift */; }; - 58A28B5C2869F69C006F7D93 /* GlobeFlyToExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A28B582869F69B006F7D93 /* GlobeFlyToExample.swift */; }; - 58A28B5E2869F69C006F7D93 /* GlobeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A28B5A2869F69B006F7D93 /* GlobeExample.swift */; }; - 58A28B602869F728006F7D93 /* SpinningGlobeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A28B5F2869F728006F7D93 /* SpinningGlobeExample.swift */; }; - 58A3C0C925C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3C0C825C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift */; }; - 73694BD325D4B2CE0064F636 /* TrackingModeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73694BD225D4B2CE0064F636 /* TrackingModeExample.swift */; }; - 73E27C2627AF024C0067F277 /* DataJoinExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E27C2527AF024C0067F277 /* DataJoinExample.swift */; }; - 7406E3702806B66F002CC41F /* ResizableImageExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7406E36F2806B66F002CC41F /* ResizableImageExample.swift */; }; - 7412CF6227E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6127E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift */; }; - 7412CF6427E8EA2A00F03B1C /* CircleAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6327E8EA2A00F03B1C /* CircleAnnotationExample.swift */; }; - 7412CF6627E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6527E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift */; }; - 7412CF6827E9A8D600F03B1C /* UIColor+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6727E9A8D600F03B1C /* UIColor+Random.swift */; }; - 7412CF6C27E9D48700F03B1C /* AddMarkersSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6B27E9D48700F03B1C /* AddMarkersSymbolExample.swift */; }; - 748C8BF3283B90280035A979 /* ModelLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 748C8BF2283B90280035A979 /* ModelLayerExample.swift */; }; - 74A2313127EB306F0065FB7D /* Array+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A2313027EB306F0065FB7D /* Array+Split.swift */; }; - 74A2313627EB37EE0065FB7D /* AnimatedMarkerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A2313527EB37EE0065FB7D /* AnimatedMarkerExample.swift */; }; - 74A2313827EC6B360065FB7D /* IconSizeChangeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A2313727EC6B360065FB7D /* IconSizeChangeExample.swift */; }; - 74A2313C27EDCE7F0065FB7D /* AnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A2313B27EDCE7F0065FB7D /* AnnotationView.swift */; }; - 74A2313E27EE1C640065FB7D /* ViewAnnotationWithPointAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74A2313D27EE1C630065FB7D /* ViewAnnotationWithPointAnnotationExample.swift */; }; - A41E58342654593500D8B946 /* SwitchStylesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A41E58332654593500D8B946 /* SwitchStylesExample.swift */; }; - A41E584C26555A3400D8B946 /* PointClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A41E584B26555A3400D8B946 /* PointClusteringExample.swift */; }; - A41E58F4265C005300D8B946 /* blueprint_style.json in Resources */ = {isa = PBXBuildFile; fileRef = A41E58F3265C005300D8B946 /* blueprint_style.json */; }; - A41E590D265C263700D8B946 /* OfflineRegionManagerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A41E590C265C263700D8B946 /* OfflineRegionManagerExample.swift */; }; - A4211CE62592549900D215B4 /* RestrictCoordinateBoundsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4211CE52592549900D215B4 /* RestrictCoordinateBoundsExample.swift */; }; - A495049B2667D64F00130A8F /* SkyLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A495049A2667D64F00130A8F /* SkyLayerExample.swift */; }; - B5327E9D260124C00095B6BD /* MapboxMaps in Frameworks */ = {isa = PBXBuildFile; productRef = B5327E9C260124C00095B6BD /* MapboxMaps */; }; - B5327EBF260277930095B6BD /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5327EBE260277930095B6BD /* Example.swift */; }; - B5327EC3260277AC0095B6BD /* ExampleProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5327EC2260277AC0095B6BD /* ExampleProtocol.swift */; }; - B57E1BAE27B5B6B900E8E3BA /* ViewportExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E1BAD27B5B6B900E8E3BA /* ViewportExample.swift */; }; - B58E470927BABE0E00D87FD6 /* AdvancedViewportGesturesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58E470827BABE0E00D87FD6 /* AdvancedViewportGesturesExample.swift */; }; - B5E6397C28066BDD00F95094 /* SimulatedLocationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E6397B28066BDD00F95094 /* SimulatedLocationProvider.swift */; }; - C608C107267BC5B1003C86C3 /* LocalizationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C608C106267BC5B1003C86C3 /* LocalizationExample.swift */; }; - CA03F10F26268DF700673961 /* OfflineManagerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA03F10E26268DF700673961 /* OfflineManagerExample.swift */; }; - CA4F1F7225E815CF00822D2A /* SwiftUIExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A2E03C25CB64E20082BC31 /* SwiftUIExample.swift */; }; - CA628414262DFD5C00651488 /* OfflineManagerExample.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CA628413262DFD5C00651488 /* OfflineManagerExample.storyboard */; }; - CA86E81825BE7C2300E5A1D9 /* BuildingExtrusionsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA86E81725BE7C2200E5A1D9 /* BuildingExtrusionsExample.swift */; }; - CAC195B725AC098A00F69FEA /* CameraAnimatorsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC195B625AC098A00F69FEA /* CameraAnimatorsExample.swift */; }; - CADCF71D2584990E0065C51B /* CustomPointAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64ED3C42540DD6E00ADADFB /* CustomPointAnnotationExample.swift */; }; - CADCF71E2584990E0065C51B /* ExternalVectorSourceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077E3B2B2581810600564A3E /* ExternalVectorSourceExample.swift */; }; - CADCF71F2584990E0065C51B /* DataDrivenSymbolsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69F01EC2543646A001AB49B /* DataDrivenSymbolsExample.swift */; }; - CADCF7212584990E0065C51B /* PolygonAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A8D86825422C190068D50D /* PolygonAnnotationExample.swift */; }; - CADCF7222584990E0065C51B /* ColorExpressionExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE3D3BE25818626000585A2 /* ColorExpressionExample.swift */; }; - CADCF7242584990E0065C51B /* AnimateLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07DBDC91254C94C500F89304 /* AnimateLayerExample.swift */; }; - CADCF7252584990E0065C51B /* CameraAnimationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64ED3842540A2BE00ADADFB /* CameraAnimationExample.swift */; }; - CADCF7262584990E0065C51B /* SnapshotterExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 072C79EA25685D23006E47A7 /* SnapshotterExample.swift */; }; - CADCF7272584990E0065C51B /* FitCameraToGeometryExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077E3B8F2581966300564A3E /* FitCameraToGeometryExample.swift */; }; - CADCF7282584990E0065C51B /* LineAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64ED3C82541CA3A00ADADFB /* LineAnnotationExample.swift */; }; - CADCF7292584990E0065C51B /* FeaturesAtPointExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07B0715F254789D6007F2865 /* FeaturesAtPointExample.swift */; }; - CADCF72A2584990E0065C51B /* CustomStyleURLExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4AC5DEB2542CB2200995E4C /* CustomStyleURLExample.swift */; }; - CADCF72D2584990E0065C51B /* MultipleGeometriesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07B071CD2547CF2B007F2865 /* MultipleGeometriesExample.swift */; }; - CADCF72F2584990E0065C51B /* SnapshotterCoreGraphicsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07C1A60C257EF7CF00531205 /* SnapshotterCoreGraphicsExample.swift */; }; - CADCF7302584990E0065C51B /* LayerPositionExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 666E0D4925664EE7000B8AF5 /* LayerPositionExample.swift */; }; - CADCF733258499130065C51B /* Examples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0746C14D252FA9D4001638BD /* Examples.swift */; }; - CADCF736258499170065C51B /* ExampleTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0746C15F252FADE4001638BD /* ExampleTableViewController.swift */; }; - CADCF7392584991C0065C51B /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0746C152252FAA03001638BD /* UIViewController+Children.swift */; }; - CADCF73C258499200065C51B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077C4EED252F7E88007636F1 /* AppDelegate.swift */; }; - CADCF743258499570065C51B /* BasicMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADCF742258499570065C51B /* BasicMapExample.swift */; }; - CAF9A9812583E49B007EF9EC /* TestableExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAF9A9802583E49B007EF9EC /* TestableExampleTests.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 077C4F01252F7E89007636F1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 077C4EE2252F7E88007636F1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 077C4EE9252F7E88007636F1; - remoteInfo = Examples; - }; - 077C4F0C252F7E89007636F1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 077C4EE2252F7E88007636F1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 077C4EE9252F7E88007636F1; - remoteInfo = Examples; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 077C4F3E252F7F9C007636F1 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0333B84E25ED942600D667C9 /* SceneKitExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneKitExample.swift; sourceTree = ""; }; - 03BB33F825EDA19200109B28 /* 34M_17.dae */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml.dae; path = 34M_17.dae; sourceTree = ""; }; - 0706C4A525B1181A008733C0 /* TerrainExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerrainExample.swift; sourceTree = ""; }; - 072C79EA25685D23006E47A7 /* SnapshotterExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotterExample.swift; sourceTree = ""; }; - 073475D625AFAE520049B0B8 /* CustomLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayerExample.swift; sourceTree = ""; }; - 0746C14D252FA9D4001638BD /* Examples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Examples.swift; sourceTree = ""; }; - 0746C152252FAA03001638BD /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = ""; }; - 0746C15F252FADE4001638BD /* ExampleTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleTableViewController.swift; sourceTree = ""; }; - 077C4EEA252F7E88007636F1 /* Examples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 077C4EED252F7E88007636F1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 077C4EF9252F7E89007636F1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 077C4EFB252F7E89007636F1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 077C4F00252F7E89007636F1 /* ExamplesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 077C4F04252F7E89007636F1 /* ExamplesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesTests.swift; sourceTree = ""; }; - 077C4F06252F7E89007636F1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 077C4F0B252F7E89007636F1 /* ExamplesUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 077C4F0F252F7E89007636F1 /* ExamplesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesUITests.swift; sourceTree = ""; }; - 077C4F11252F7E89007636F1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 077C4F4B252FA7CD007636F1 /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; - 077E3B2B2581810600564A3E /* ExternalVectorSourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalVectorSourceExample.swift; sourceTree = ""; }; - 077E3B8F2581966300564A3E /* FitCameraToGeometryExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FitCameraToGeometryExample.swift; sourceTree = ""; }; - 07A2E03C25CB64E20082BC31 /* SwiftUIExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIExample.swift; sourceTree = ""; }; - 07A8D86825422C190068D50D /* PolygonAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolygonAnnotationExample.swift; sourceTree = ""; }; - 07B0715F254789D6007F2865 /* FeaturesAtPointExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturesAtPointExample.swift; sourceTree = ""; }; - 07B071CD2547CF2B007F2865 /* MultipleGeometriesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleGeometriesExample.swift; sourceTree = ""; }; - 07B071D22547CFC3007F2865 /* GeoJSONSourceExample.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GeoJSONSourceExample.geojson; sourceTree = ""; }; - 07C1A60C257EF7CF00531205 /* SnapshotterCoreGraphicsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotterCoreGraphicsExample.swift; sourceTree = ""; }; - 07DBDC91254C94C500F89304 /* AnimateLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateLayerExample.swift; sourceTree = ""; }; - 07DC84412538B1F100F4AF14 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 0C52BA9725AF8C880054ECA8 /* Custom3DPuckExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Custom3DPuckExample.swift; sourceTree = ""; }; - 0C784D1026D002DC004AE7D0 /* FeatureStateExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureStateExample.swift; sourceTree = ""; }; - 0C78AC2825BF70E40057F570 /* LineGradientExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineGradientExample.swift; sourceTree = ""; }; - 0C78AC2C25BF71E40057F570 /* GradientLine.geojson */ = {isa = PBXFileReference; lastKnownFileType = text; path = GradientLine.geojson; sourceTree = ""; }; - 0CC4ECE925B8AD3000F998B8 /* Custom2DPuckExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Custom2DPuckExample.swift; sourceTree = ""; }; - 0CE3D3BE25818626000585A2 /* ColorExpressionExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExpressionExample.swift; sourceTree = ""; }; - 177C9D46269CA61100D13A2D /* ShowHideLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowHideLayerExample.swift; sourceTree = ""; }; - 17AF783F26837E91006EA30D /* Fire_Hydrants.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Fire_Hydrants.geojson; sourceTree = ""; }; - 17B40D2326A85500000887EF /* LiveDataExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveDataExample.swift; sourceTree = ""; }; - 17B480592684FB2300CF0D5E /* AnimateImageLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateImageLayerExample.swift; sourceTree = ""; }; - 17B4805C26851C6E00CF0D5E /* radar4.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar4.gif; sourceTree = ""; }; - 17B4805D26851C6E00CF0D5E /* radar2.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar2.gif; sourceTree = ""; }; - 17B4805E26851C6E00CF0D5E /* radar3.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar3.gif; sourceTree = ""; }; - 17B4805F26851C6E00CF0D5E /* radar1.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar1.gif; sourceTree = ""; }; - 17B4806026851C6E00CF0D5E /* radar0.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar0.gif; sourceTree = ""; }; - 17B48066268A4E9300CF0D5E /* DistanceExpressionExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceExpressionExample.swift; sourceTree = ""; }; - 17B4806A268BD91000CF0D5E /* RasterTileSourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RasterTileSourceExample.swift; sourceTree = ""; }; - 17E28C5B2672A1160033DF0F /* SymbolClusteringExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolClusteringExample.swift; sourceTree = ""; }; - 304AB3B427439287005B6D09 /* ViewAnnotationMarkerExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewAnnotationMarkerExample.swift; sourceTree = ""; }; - 30517C69274BD4D300B706E5 /* ViewAnnotationBasicExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationBasicExample.swift; sourceTree = ""; }; - 3A3AF0022836499F0036F483 /* route.geojson */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = route.geojson; sourceTree = ""; }; - 3A7432EE27F3096100E06485 /* DebugMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMapExample.swift; sourceTree = ""; }; - 3A7CE985282511C900C3A0B8 /* NavigationSimulatorExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSimulatorExample.swift; sourceTree = ""; }; - 3A7CE98A282AB0DE00C3A0B8 /* NavigationSimulator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSimulator.swift; sourceTree = ""; }; - 5812B55027C5A0E500E10063 /* CustomLocationProviderExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLocationProviderExample.swift; sourceTree = ""; }; - 58248C2E2695FF24009AC598 /* StoryboardMapViewExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryboardMapViewExample.swift; sourceTree = ""; }; - 5830FDB6270B5AF7005549EE /* sportcar.glb */ = {isa = PBXFileReference; lastKnownFileType = file; path = sportcar.glb; sourceTree = ""; }; - 587429BF27BBFDA100104CCA /* VoiceOverAccessibilityExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceOverAccessibilityExample.swift; sourceTree = ""; }; - 58882E5C26962A7200E5C1C6 /* StoryboardMapViewExample.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = StoryboardMapViewExample.storyboard; sourceTree = ""; }; - 58A28B572869F69B006F7D93 /* HeatmapLayerGlobeExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeatmapLayerGlobeExample.swift; sourceTree = ""; }; - 58A28B582869F69B006F7D93 /* GlobeFlyToExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobeFlyToExample.swift; sourceTree = ""; }; - 58A28B5A2869F69B006F7D93 /* GlobeExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobeExample.swift; sourceTree = ""; }; - 58A28B5F2869F728006F7D93 /* SpinningGlobeExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SpinningGlobeExample.swift; sourceTree = ""; }; - 58A3C0C825C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateGeoJSONLineExample.swift; sourceTree = ""; }; - 666E0D4925664EE7000B8AF5 /* LayerPositionExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayerPositionExample.swift; sourceTree = ""; }; - 73694BD225D4B2CE0064F636 /* TrackingModeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingModeExample.swift; sourceTree = ""; }; - 73E27C2527AF024C0067F277 /* DataJoinExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataJoinExample.swift; sourceTree = ""; }; - 7406E36F2806B66F002CC41F /* ResizableImageExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableImageExample.swift; sourceTree = ""; }; - 7412CF6127E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOneMarkerSymbolExample.swift; sourceTree = ""; }; - 7412CF6327E8EA2A00F03B1C /* CircleAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleAnnotationExample.swift; sourceTree = ""; }; - 7412CF6527E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLLocationCoordinate2D+Random.swift"; sourceTree = ""; }; - 7412CF6727E9A8D600F03B1C /* UIColor+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Random.swift"; sourceTree = ""; }; - 7412CF6B27E9D48700F03B1C /* AddMarkersSymbolExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMarkersSymbolExample.swift; sourceTree = ""; }; - 748C8BF2283B90280035A979 /* ModelLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelLayerExample.swift; sourceTree = ""; }; - 74A2313027EB306F0065FB7D /* Array+Split.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Split.swift"; sourceTree = ""; }; - 74A2313527EB37EE0065FB7D /* AnimatedMarkerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatedMarkerExample.swift; sourceTree = ""; }; - 74A2313727EC6B360065FB7D /* IconSizeChangeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSizeChangeExample.swift; sourceTree = ""; }; - 74A2313B27EDCE7F0065FB7D /* AnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationView.swift; sourceTree = ""; }; - 74A2313D27EE1C630065FB7D /* ViewAnnotationWithPointAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationWithPointAnnotationExample.swift; sourceTree = ""; }; - 74A2313F27EE243B0065FB7D /* annotations.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = annotations.json; sourceTree = ""; }; - A41E58332654593500D8B946 /* SwitchStylesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchStylesExample.swift; sourceTree = ""; }; - A41E584B26555A3400D8B946 /* PointClusteringExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointClusteringExample.swift; sourceTree = ""; }; - A41E58F3265C005300D8B946 /* blueprint_style.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = blueprint_style.json; sourceTree = ""; }; - A41E590C265C263700D8B946 /* OfflineRegionManagerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineRegionManagerExample.swift; sourceTree = ""; }; - A4211CE52592549900D215B4 /* RestrictCoordinateBoundsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestrictCoordinateBoundsExample.swift; sourceTree = ""; }; - A495049A2667D64F00130A8F /* SkyLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkyLayerExample.swift; sourceTree = ""; }; - A4AC5DEB2542CB2200995E4C /* CustomStyleURLExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStyleURLExample.swift; sourceTree = ""; }; - B5327EBE260277930095B6BD /* Example.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Example.swift; sourceTree = ""; }; - B5327EC2260277AC0095B6BD /* ExampleProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleProtocol.swift; sourceTree = ""; }; - B57E1BAD27B5B6B900E8E3BA /* ViewportExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportExample.swift; sourceTree = ""; }; - B58E470827BABE0E00D87FD6 /* AdvancedViewportGesturesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedViewportGesturesExample.swift; sourceTree = ""; }; - B5E6397B28066BDD00F95094 /* SimulatedLocationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatedLocationProvider.swift; sourceTree = ""; }; - C608C106267BC5B1003C86C3 /* LocalizationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationExample.swift; sourceTree = ""; }; - C64ED3842540A2BE00ADADFB /* CameraAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAnimationExample.swift; sourceTree = ""; }; - C64ED3C42540DD6E00ADADFB /* CustomPointAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPointAnnotationExample.swift; sourceTree = ""; }; - C64ED3C82541CA3A00ADADFB /* LineAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineAnnotationExample.swift; sourceTree = ""; }; - C69F01EC2543646A001AB49B /* DataDrivenSymbolsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataDrivenSymbolsExample.swift; sourceTree = ""; }; - CA03F10E26268DF700673961 /* OfflineManagerExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OfflineManagerExample.swift; sourceTree = ""; }; - CA628413262DFD5C00651488 /* OfflineManagerExample.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = OfflineManagerExample.storyboard; sourceTree = ""; }; - CA86E81725BE7C2200E5A1D9 /* BuildingExtrusionsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildingExtrusionsExample.swift; sourceTree = ""; }; - CAC195B625AC098A00F69FEA /* CameraAnimatorsExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraAnimatorsExample.swift; sourceTree = ""; }; - CADCF742258499570065C51B /* BasicMapExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicMapExample.swift; sourceTree = ""; }; - CAF9A9802583E49B007EF9EC /* TestableExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableExampleTests.swift; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 077C4EE7252F7E88007636F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B5327E9D260124C00095B6BD /* MapboxMaps in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 077C4EFD252F7E89007636F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 077C4F08252F7E89007636F1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0746C143252FA97E001638BD /* Models */ = { - isa = PBXGroup; - children = ( - B5327EBE260277930095B6BD /* Example.swift */, - B5327EC2260277AC0095B6BD /* ExampleProtocol.swift */, - 0746C14D252FA9D4001638BD /* Examples.swift */, - B5E6397B28066BDD00F95094 /* SimulatedLocationProvider.swift */, - ); - path = Models; - sourceTree = ""; - }; - 0746C144252FA983001638BD /* Controllers */ = { - isa = PBXGroup; - children = ( - 0746C15F252FADE4001638BD /* ExampleTableViewController.swift */, - ); - path = Controllers; - sourceTree = ""; - }; - 0746C151252FA9E1001638BD /* Extensions */ = { - isa = PBXGroup; - children = ( - 0746C152252FAA03001638BD /* UIViewController+Children.swift */, - 7412CF6527E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift */, - 7412CF6727E9A8D600F03B1C /* UIColor+Random.swift */, - 74A2313027EB306F0065FB7D /* Array+Split.swift */, - ); - path = Extensions; - sourceTree = ""; - }; - 0746C182252FB0F4001638BD /* All Examples */ = { - isa = PBXGroup; - children = ( - B58E470827BABE0E00D87FD6 /* AdvancedViewportGesturesExample.swift */, - 7406E36E2806B659002CC41F /* Lab */, - 58A3C0C825C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift */, - 17B480592684FB2300CF0D5E /* AnimateImageLayerExample.swift */, - 07DBDC91254C94C500F89304 /* AnimateLayerExample.swift */, - 7412CF6027E8DCFB00F03B1C /* Annotations */, - CADCF742258499570065C51B /* BasicMapExample.swift */, - CA86E81725BE7C2200E5A1D9 /* BuildingExtrusionsExample.swift */, - C64ED3842540A2BE00ADADFB /* CameraAnimationExample.swift */, - CAC195B625AC098A00F69FEA /* CameraAnimatorsExample.swift */, - 0CE3D3BE25818626000585A2 /* ColorExpressionExample.swift */, - 0CC4ECE925B8AD3000F998B8 /* Custom2DPuckExample.swift */, - 0C52BA9725AF8C880054ECA8 /* Custom3DPuckExample.swift */, - 073475D625AFAE520049B0B8 /* CustomLayerExample.swift */, - 5812B55027C5A0E500E10063 /* CustomLocationProviderExample.swift */, - A4AC5DEB2542CB2200995E4C /* CustomStyleURLExample.swift */, - C69F01EC2543646A001AB49B /* DataDrivenSymbolsExample.swift */, - 73E27C2527AF024C0067F277 /* DataJoinExample.swift */, - 3A7432EE27F3096100E06485 /* DebugMapExample.swift */, - 17B48066268A4E9300CF0D5E /* DistanceExpressionExample.swift */, - 077E3B2B2581810600564A3E /* ExternalVectorSourceExample.swift */, - 07B0715F254789D6007F2865 /* FeaturesAtPointExample.swift */, - 58A28B5A2869F69B006F7D93 /* GlobeExample.swift */, - 58A28B582869F69B006F7D93 /* GlobeFlyToExample.swift */, - 58A28B572869F69B006F7D93 /* HeatmapLayerGlobeExample.swift */, - 0C784D1026D002DC004AE7D0 /* FeatureStateExample.swift */, - 077E3B8F2581966300564A3E /* FitCameraToGeometryExample.swift */, - 666E0D4925664EE7000B8AF5 /* LayerPositionExample.swift */, - 0C78AC2825BF70E40057F570 /* LineGradientExample.swift */, - 58A28B5F2869F728006F7D93 /* SpinningGlobeExample.swift */, - 17B40D2326A85500000887EF /* LiveDataExample.swift */, - C608C106267BC5B1003C86C3 /* LocalizationExample.swift */, - CA628413262DFD5C00651488 /* OfflineManagerExample.storyboard */, - CA03F10E26268DF700673961 /* OfflineManagerExample.swift */, - A41E590C265C263700D8B946 /* OfflineRegionManagerExample.swift */, - A41E584B26555A3400D8B946 /* PointClusteringExample.swift */, - 17B4806A268BD91000CF0D5E /* RasterTileSourceExample.swift */, - A4211CE52592549900D215B4 /* RestrictCoordinateBoundsExample.swift */, - 07B071D12547CF50007F2865 /* Sample Data */, - 0333B84E25ED942600D667C9 /* SceneKitExample.swift */, - 177C9D46269CA61100D13A2D /* ShowHideLayerExample.swift */, - A495049A2667D64F00130A8F /* SkyLayerExample.swift */, - 07C1A60C257EF7CF00531205 /* SnapshotterCoreGraphicsExample.swift */, - 072C79EA25685D23006E47A7 /* SnapshotterExample.swift */, - 58248C2E2695FF24009AC598 /* StoryboardMapViewExample.swift */, - A41E58332654593500D8B946 /* SwitchStylesExample.swift */, - 0706C4A525B1181A008733C0 /* TerrainExample.swift */, - 73694BD225D4B2CE0064F636 /* TrackingModeExample.swift */, - B57E1BAD27B5B6B900E8E3BA /* ViewportExample.swift */, - 587429BF27BBFDA100104CCA /* VoiceOverAccessibilityExample.swift */, - 748C8BF2283B90280035A979 /* ModelLayerExample.swift */, - 3A7CE989282AB0BC00C3A0B8 /* NavigationSimulator */, - ); - path = "All Examples"; - sourceTree = ""; - }; - 077C4EE1252F7E88007636F1 = { - isa = PBXGroup; - children = ( - 077C4F4A252FA7A5007636F1 /* Configurations */, - 077C4EEC252F7E88007636F1 /* Examples */, - 077C4F03252F7E89007636F1 /* ExamplesTests */, - 077C4F0E252F7E89007636F1 /* ExamplesUITests */, - 077C4EEB252F7E88007636F1 /* Products */, - ); - sourceTree = ""; - }; - 077C4EEB252F7E88007636F1 /* Products */ = { - isa = PBXGroup; - children = ( - 077C4EEA252F7E88007636F1 /* Examples.app */, - 077C4F00252F7E89007636F1 /* ExamplesTests.xctest */, - 077C4F0B252F7E89007636F1 /* ExamplesUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 077C4EEC252F7E88007636F1 /* Examples */ = { - isa = PBXGroup; - children = ( - 0746C182252FB0F4001638BD /* All Examples */, - 0746C143252FA97E001638BD /* Models */, - 0746C144252FA983001638BD /* Controllers */, - 0746C151252FA9E1001638BD /* Extensions */, - 077C4EED252F7E88007636F1 /* AppDelegate.swift */, - 07DC84412538B1F100F4AF14 /* Assets.xcassets */, - 077C4EF8252F7E89007636F1 /* LaunchScreen.storyboard */, - 077C4EFB252F7E89007636F1 /* Info.plist */, - 58882E5C26962A7200E5C1C6 /* StoryboardMapViewExample.storyboard */, - ); - path = Examples; - sourceTree = ""; - }; - 077C4F03252F7E89007636F1 /* ExamplesTests */ = { - isa = PBXGroup; - children = ( - 077C4F06252F7E89007636F1 /* Info.plist */, - 077C4F04252F7E89007636F1 /* ExamplesTests.swift */, - CAF9A9802583E49B007EF9EC /* TestableExampleTests.swift */, - ); - path = ExamplesTests; - sourceTree = ""; - }; - 077C4F0E252F7E89007636F1 /* ExamplesUITests */ = { - isa = PBXGroup; - children = ( - 077C4F0F252F7E89007636F1 /* ExamplesUITests.swift */, - 077C4F11252F7E89007636F1 /* Info.plist */, - ); - path = ExamplesUITests; - sourceTree = ""; - }; - 077C4F4A252FA7A5007636F1 /* Configurations */ = { - isa = PBXGroup; - children = ( - 077C4F4B252FA7CD007636F1 /* base.xcconfig */, - ); - name = Configurations; - path = ../Configurations; - sourceTree = ""; - }; - 07B071D12547CF50007F2865 /* Sample Data */ = { - isa = PBXGroup; - children = ( - 3A3AF0022836499F0036F483 /* route.geojson */, - 17B4805B26851C0600CF0D5E /* Radar */, - 74A2313F27EE243B0065FB7D /* annotations.json */, - 17AF783F26837E91006EA30D /* Fire_Hydrants.geojson */, - A41E58F3265C005300D8B946 /* blueprint_style.json */, - 03BB33F825EDA19200109B28 /* 34M_17.dae */, - 07B071D22547CFC3007F2865 /* GeoJSONSourceExample.geojson */, - 0C78AC2C25BF71E40057F570 /* GradientLine.geojson */, - 5830FDB6270B5AF7005549EE /* sportcar.glb */, - ); - path = "Sample Data"; - sourceTree = ""; - }; - 17B4805B26851C0600CF0D5E /* Radar */ = { - isa = PBXGroup; - children = ( - 17B4806026851C6E00CF0D5E /* radar0.gif */, - 17B4805F26851C6E00CF0D5E /* radar1.gif */, - 17B4805D26851C6E00CF0D5E /* radar2.gif */, - 17B4805E26851C6E00CF0D5E /* radar3.gif */, - 17B4805C26851C6E00CF0D5E /* radar4.gif */, - ); - path = Radar; - sourceTree = ""; - }; - 3A7CE989282AB0BC00C3A0B8 /* NavigationSimulator */ = { - isa = PBXGroup; - children = ( - 3A7CE985282511C900C3A0B8 /* NavigationSimulatorExample.swift */, - 3A7CE98A282AB0DE00C3A0B8 /* NavigationSimulator.swift */, - ); - name = NavigationSimulator; - sourceTree = ""; - }; - 7406E36E2806B659002CC41F /* Lab */ = { - isa = PBXGroup; - children = ( - 7406E36F2806B66F002CC41F /* ResizableImageExample.swift */, - ); - path = Lab; - sourceTree = ""; - }; - 7412CF6027E8DCFB00F03B1C /* Annotations */ = { - isa = PBXGroup; - children = ( - 7412CF6B27E9D48700F03B1C /* AddMarkersSymbolExample.swift */, - 7412CF6127E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift */, - 74A2313527EB37EE0065FB7D /* AnimatedMarkerExample.swift */, - 74A2313B27EDCE7F0065FB7D /* AnnotationView.swift */, - 7412CF6327E8EA2A00F03B1C /* CircleAnnotationExample.swift */, - C64ED3C42540DD6E00ADADFB /* CustomPointAnnotationExample.swift */, - 74A2313727EC6B360065FB7D /* IconSizeChangeExample.swift */, - C64ED3C82541CA3A00ADADFB /* LineAnnotationExample.swift */, - 07B071CD2547CF2B007F2865 /* MultipleGeometriesExample.swift */, - 07A8D86825422C190068D50D /* PolygonAnnotationExample.swift */, - 07A2E03C25CB64E20082BC31 /* SwiftUIExample.swift */, - 17E28C5B2672A1160033DF0F /* SymbolClusteringExample.swift */, - 30517C69274BD4D300B706E5 /* ViewAnnotationBasicExample.swift */, - 304AB3B427439287005B6D09 /* ViewAnnotationMarkerExample.swift */, - 74A2313D27EE1C630065FB7D /* ViewAnnotationWithPointAnnotationExample.swift */, - ); - path = Annotations; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 077C4EE9252F7E88007636F1 /* Examples */ = { - isa = PBXNativeTarget; - buildConfigurationList = 077C4F14252F7E89007636F1 /* Build configuration list for PBXNativeTarget "Examples" */; - buildPhases = ( - 077C4EE6252F7E88007636F1 /* Sources */, - 077C4EE7252F7E88007636F1 /* Frameworks */, - 077C4EE8252F7E88007636F1 /* Resources */, - 077C4F3E252F7F9C007636F1 /* Embed Frameworks */, - FE9223362881673C006A7B2A /* Run swiftlint */, - 0746C187252FB153001638BD /* Insert Mapbox access token */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Examples; - packageProductDependencies = ( - B5327E9C260124C00095B6BD /* MapboxMaps */, - ); - productName = Examples; - productReference = 077C4EEA252F7E88007636F1 /* Examples.app */; - productType = "com.apple.product-type.application"; - }; - 077C4EFF252F7E89007636F1 /* ExamplesTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 077C4F17252F7E89007636F1 /* Build configuration list for PBXNativeTarget "ExamplesTests" */; - buildPhases = ( - 077C4EFC252F7E89007636F1 /* Sources */, - 077C4EFD252F7E89007636F1 /* Frameworks */, - 077C4EFE252F7E89007636F1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 077C4F02252F7E89007636F1 /* PBXTargetDependency */, - ); - name = ExamplesTests; - productName = ExamplesTests; - productReference = 077C4F00252F7E89007636F1 /* ExamplesTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 077C4F0A252F7E89007636F1 /* ExamplesUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 077C4F1A252F7E89007636F1 /* Build configuration list for PBXNativeTarget "ExamplesUITests" */; - buildPhases = ( - 077C4F07252F7E89007636F1 /* Sources */, - 077C4F08252F7E89007636F1 /* Frameworks */, - 077C4F09252F7E89007636F1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 077C4F0D252F7E89007636F1 /* PBXTargetDependency */, - ); - name = ExamplesUITests; - productName = ExamplesUITests; - productReference = 077C4F0B252F7E89007636F1 /* ExamplesUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 077C4EE2252F7E88007636F1 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1200; - LastUpgradeCheck = 1340; - TargetAttributes = { - 077C4EE9252F7E88007636F1 = { - CreatedOnToolsVersion = 12.0; - }; - 077C4EFF252F7E89007636F1 = { - CreatedOnToolsVersion = 12.0; - TestTargetID = 077C4EE9252F7E88007636F1; - }; - 077C4F0A252F7E89007636F1 = { - CreatedOnToolsVersion = 12.0; - TestTargetID = 077C4EE9252F7E88007636F1; - }; - }; - }; - buildConfigurationList = 077C4EE5252F7E88007636F1 /* Build configuration list for PBXProject "Examples" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 077C4EE1252F7E88007636F1; - productRefGroup = 077C4EEB252F7E88007636F1 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 077C4EE9252F7E88007636F1 /* Examples */, - 077C4EFF252F7E89007636F1 /* ExamplesTests */, - 077C4F0A252F7E89007636F1 /* ExamplesUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 077C4EE8252F7E88007636F1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 17B4806526851C6E00CF0D5E /* radar0.gif in Resources */, - 07B071D32547CFC3007F2865 /* GeoJSONSourceExample.geojson in Resources */, - 03BB33F925EDA19200109B28 /* 34M_17.dae in Resources */, - CA628414262DFD5C00651488 /* OfflineManagerExample.storyboard in Resources */, - 0C78AC2F25BF72C70057F570 /* GradientLine.geojson in Resources */, - 07DC84422538B1F100F4AF14 /* Assets.xcassets in Resources */, - A41E58F4265C005300D8B946 /* blueprint_style.json in Resources */, - 17AF784026837E91006EA30D /* Fire_Hydrants.geojson in Resources */, - 17B4806426851C6E00CF0D5E /* radar1.gif in Resources */, - 58882E5D26962A7200E5C1C6 /* StoryboardMapViewExample.storyboard in Resources */, - 17B4806126851C6E00CF0D5E /* radar4.gif in Resources */, - 17B4806326851C6E00CF0D5E /* radar3.gif in Resources */, - 5830FDB7270B5AF7005549EE /* sportcar.glb in Resources */, - 17B4806226851C6E00CF0D5E /* radar2.gif in Resources */, - 077C4EFA252F7E89007636F1 /* LaunchScreen.storyboard in Resources */, - 3A3AF0032836499F0036F483 /* route.geojson in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 077C4EFE252F7E89007636F1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 077C4F09252F7E89007636F1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 0746C187252FB153001638BD /* Insert Mapbox access token */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)", - ); - name = "Insert Mapbox access token"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "$SRCROOT/../../scripts/insert_access_token.sh\n"; - showEnvVarsInLog = 0; - }; - FE9223362881673C006A7B2A /* Run swiftlint */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run swiftlint"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "$SRCROOT/../../scripts/run_swiftlint.sh\n\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 077C4EE6252F7E88007636F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CA03F10F26268DF700673961 /* OfflineManagerExample.swift in Sources */, - CADCF73C258499200065C51B /* AppDelegate.swift in Sources */, - CADCF7392584991C0065C51B /* UIViewController+Children.swift in Sources */, - 58248C2F2695FF24009AC598 /* StoryboardMapViewExample.swift in Sources */, - 74A2313827EC6B360065FB7D /* IconSizeChangeExample.swift in Sources */, - B58E470927BABE0E00D87FD6 /* AdvancedViewportGesturesExample.swift in Sources */, - A495049B2667D64F00130A8F /* SkyLayerExample.swift in Sources */, - CADCF7262584990E0065C51B /* SnapshotterExample.swift in Sources */, - 5812B55127C5A0E500E10063 /* CustomLocationProviderExample.swift in Sources */, - CADCF71D2584990E0065C51B /* CustomPointAnnotationExample.swift in Sources */, - 17B4806B268BD91000CF0D5E /* RasterTileSourceExample.swift in Sources */, - 3A7CE98B282AB0DE00C3A0B8 /* NavigationSimulator.swift in Sources */, - CADCF7212584990E0065C51B /* PolygonAnnotationExample.swift in Sources */, - A41E58342654593500D8B946 /* SwitchStylesExample.swift in Sources */, - CADCF7272584990E0065C51B /* FitCameraToGeometryExample.swift in Sources */, - B5327EC3260277AC0095B6BD /* ExampleProtocol.swift in Sources */, - 7412CF6427E8EA2A00F03B1C /* CircleAnnotationExample.swift in Sources */, - 58A28B602869F728006F7D93 /* SpinningGlobeExample.swift in Sources */, - A41E584C26555A3400D8B946 /* PointClusteringExample.swift in Sources */, - 7412CF6627E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift in Sources */, - 3A7432EF27F3096100E06485 /* DebugMapExample.swift in Sources */, - CADCF7252584990E0065C51B /* CameraAnimationExample.swift in Sources */, - CADCF72A2584990E0065C51B /* CustomStyleURLExample.swift in Sources */, - 3A7CE986282511C900C3A0B8 /* NavigationSimulatorExample.swift in Sources */, - 58A28B5E2869F69C006F7D93 /* GlobeExample.swift in Sources */, - 58A28B5C2869F69C006F7D93 /* GlobeFlyToExample.swift in Sources */, - 177C9D47269CA61100D13A2D /* ShowHideLayerExample.swift in Sources */, - 0C784D1126D002DC004AE7D0 /* FeatureStateExample.swift in Sources */, - CADCF7222584990E0065C51B /* ColorExpressionExample.swift in Sources */, - 58A28B5B2869F69C006F7D93 /* HeatmapLayerGlobeExample.swift in Sources */, - 74A2313C27EDCE7F0065FB7D /* AnnotationView.swift in Sources */, - 7412CF6C27E9D48700F03B1C /* AddMarkersSymbolExample.swift in Sources */, - 17E28C5C2672A1160033DF0F /* SymbolClusteringExample.swift in Sources */, - CADCF733258499130065C51B /* Examples.swift in Sources */, - CADCF743258499570065C51B /* BasicMapExample.swift in Sources */, - 7406E3702806B66F002CC41F /* ResizableImageExample.swift in Sources */, - 17B40D2426A85500000887EF /* LiveDataExample.swift in Sources */, - 748C8BF3283B90280035A979 /* ModelLayerExample.swift in Sources */, - CADCF7242584990E0065C51B /* AnimateLayerExample.swift in Sources */, - 073475D725AFAE520049B0B8 /* CustomLayerExample.swift in Sources */, - 7412CF6827E9A8D600F03B1C /* UIColor+Random.swift in Sources */, - 0CC4ECEA25B8AD3000F998B8 /* Custom2DPuckExample.swift in Sources */, - CADCF71F2584990E0065C51B /* DataDrivenSymbolsExample.swift in Sources */, - CADCF7302584990E0065C51B /* LayerPositionExample.swift in Sources */, - CA86E81825BE7C2300E5A1D9 /* BuildingExtrusionsExample.swift in Sources */, - C608C107267BC5B1003C86C3 /* LocalizationExample.swift in Sources */, - 0706C4A625B1181A008733C0 /* TerrainExample.swift in Sources */, - CADCF7282584990E0065C51B /* LineAnnotationExample.swift in Sources */, - CADCF72F2584990E0065C51B /* SnapshotterCoreGraphicsExample.swift in Sources */, - B5E6397C28066BDD00F95094 /* SimulatedLocationProvider.swift in Sources */, - 304AB3B527439287005B6D09 /* ViewAnnotationMarkerExample.swift in Sources */, - CA4F1F7225E815CF00822D2A /* SwiftUIExample.swift in Sources */, - CAC195B725AC098A00F69FEA /* CameraAnimatorsExample.swift in Sources */, - 587429C027BBFDA100104CCA /* VoiceOverAccessibilityExample.swift in Sources */, - 73E27C2627AF024C0067F277 /* DataJoinExample.swift in Sources */, - 74A2313127EB306F0065FB7D /* Array+Split.swift in Sources */, - 0C78AC2925BF70E40057F570 /* LineGradientExample.swift in Sources */, - 17B48067268A4E9300CF0D5E /* DistanceExpressionExample.swift in Sources */, - 0333B84F25ED942600D667C9 /* SceneKitExample.swift in Sources */, - CADCF72D2584990E0065C51B /* MultipleGeometriesExample.swift in Sources */, - 30517C6A274BD4D300B706E5 /* ViewAnnotationBasicExample.swift in Sources */, - A41E590D265C263700D8B946 /* OfflineRegionManagerExample.swift in Sources */, - 74A2313627EB37EE0065FB7D /* AnimatedMarkerExample.swift in Sources */, - 58A3C0C925C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift in Sources */, - B5327EBF260277930095B6BD /* Example.swift in Sources */, - CADCF736258499170065C51B /* ExampleTableViewController.swift in Sources */, - 17B4805A2684FB2300CF0D5E /* AnimateImageLayerExample.swift in Sources */, - B57E1BAE27B5B6B900E8E3BA /* ViewportExample.swift in Sources */, - A4211CE62592549900D215B4 /* RestrictCoordinateBoundsExample.swift in Sources */, - 0C52BA9825AF8C880054ECA8 /* Custom3DPuckExample.swift in Sources */, - CADCF7292584990E0065C51B /* FeaturesAtPointExample.swift in Sources */, - CADCF71E2584990E0065C51B /* ExternalVectorSourceExample.swift in Sources */, - 7412CF6227E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift in Sources */, - 74A2313E27EE1C640065FB7D /* ViewAnnotationWithPointAnnotationExample.swift in Sources */, - 73694BD325D4B2CE0064F636 /* TrackingModeExample.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 077C4EFC252F7E89007636F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CAF9A9812583E49B007EF9EC /* TestableExampleTests.swift in Sources */, - 077C4F05252F7E89007636F1 /* ExamplesTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 077C4F07252F7E89007636F1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 077C4F10252F7E89007636F1 /* ExamplesUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 077C4F02252F7E89007636F1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 077C4EE9252F7E88007636F1 /* Examples */; - targetProxy = 077C4F01252F7E89007636F1 /* PBXContainerItemProxy */; - }; - 077C4F0D252F7E89007636F1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 077C4EE9252F7E88007636F1 /* Examples */; - targetProxy = 077C4F0C252F7E89007636F1 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 077C4EF8252F7E89007636F1 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 077C4EF9252F7E89007636F1 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 077C4F12252F7E89007636F1 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 077C4F4B252FA7CD007636F1 /* base.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - VALIDATE_WORKSPACE = YES; - }; - name = Debug; - }; - 077C4F13252F7E89007636F1 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 077C4F4B252FA7CD007636F1 /* base.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 077C4F15252F7E89007636F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = Examples/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.examples; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_WORKSPACE = YES; - }; - name = Debug; - }; - 077C4F16252F7E89007636F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = Examples/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.examples; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_WORKSPACE = YES; - }; - name = Release; - }; - 077C4F18252F7E89007636F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = ExamplesTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = mapbox.ExamplesTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Examples.app/Examples"; - }; - name = Debug; - }; - 077C4F19252F7E89007636F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = ExamplesTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = mapbox.ExamplesTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Examples.app/Examples"; - }; - name = Release; - }; - 077C4F1B252F7E89007636F1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = ExamplesUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = mapbox.ExamplesUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Examples; - }; - name = Debug; - }; - 077C4F1C252F7E89007636F1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = ExamplesUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = mapbox.ExamplesUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Examples; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 077C4EE5252F7E88007636F1 /* Build configuration list for PBXProject "Examples" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 077C4F12252F7E89007636F1 /* Debug */, - 077C4F13252F7E89007636F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 077C4F14252F7E89007636F1 /* Build configuration list for PBXNativeTarget "Examples" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 077C4F15252F7E89007636F1 /* Debug */, - 077C4F16252F7E89007636F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 077C4F17252F7E89007636F1 /* Build configuration list for PBXNativeTarget "ExamplesTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 077C4F18252F7E89007636F1 /* Debug */, - 077C4F19252F7E89007636F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 077C4F1A252F7E89007636F1 /* Build configuration list for PBXNativeTarget "ExamplesUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 077C4F1B252F7E89007636F1 /* Debug */, - 077C4F1C252F7E89007636F1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCSwiftPackageProductDependency section */ - B5327E9C260124C00095B6BD /* MapboxMaps */ = { - isa = XCSwiftPackageProductDependency; - productName = MapboxMaps; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = 077C4EE2252F7E88007636F1 /* Project object */; -} diff --git a/Apps/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Apps/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003d68..000000000000 --- a/Apps/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Apps/Examples/Examples.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme b/Apps/Examples/Examples.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme deleted file mode 100644 index 0ce5ca64120c..000000000000 --- a/Apps/Examples/Examples.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/Examples/Examples/All Examples/AnimateImageLayerExample.swift b/Apps/Examples/Examples/All Examples/AnimateImageLayerExample.swift deleted file mode 100644 index 81da86a9744b..000000000000 --- a/Apps/Examples/Examples/All Examples/AnimateImageLayerExample.swift +++ /dev/null @@ -1,109 +0,0 @@ -import MapboxMaps - -@objc(AnimateImageLayerExample) -class AnimateImageLayerExample: UIViewController, ExampleProtocol { - var mapView: MapView! - var sourceId = "radar-source" - var timer: Timer? - var imageNumber = 0 - - override func viewDidLoad() { - super.viewDidLoad() - - let center = CLLocationCoordinate2D(latitude: 41.874, longitude: -75.789) - let cameraOptions = CameraOptions(center: center, zoom: 5) - let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .dark) - mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth] - - // Hide the `scaleBar` at all zoom levels. - mapView.ornaments.options.scaleBar.visibility = .hidden - - // This also updates the color of the info button to match the map's style. - mapView.tintColor = .lightGray - - // Set the map's `CameraBoundsOptions` to limit the map's zoom level. - try? mapView.mapboxMap.setCameraBounds(with: CameraBoundsOptions(maxZoom: 5.99, minZoom: 4)) - - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.addImageLayer() - - // The following line is just for testing purposes. - self.finish() - } - } - - func addImageLayer() { - let style = mapView.mapboxMap.style - - // Create an `ImageSource`. This will manage the image displayed in the `RasterLayer` as well - // as the location of that image on the map. - var imageSource = ImageSource() - - // Set the `coordinates` property to an array of longitude, latitude pairs. - imageSource.coordinates = [ - [-80.425, 46.437], - [-71.516, 46.437], - [-71.516, 37.936], - [-80.425, 37.936] - ] - - // Get the file path for the first radar image, then set the `url` for the `ImageSource` to that path. - let path = Bundle.main.path(forResource: "radar0", ofType: "gif")! - imageSource.url = path - - // Create a `RasterLayer` that will display the images from the `ImageSource` - var imageLayer = RasterLayer(id: "radar-layer") - imageLayer.source = sourceId - - // Set `rasterFadeDuration` to `0`. This prevents visible transitions when the image is updated. - imageLayer.rasterFadeDuration = .constant(0) - - do { - try style.addSource(imageSource, id: sourceId) - try style.addLayer(imageLayer) - - // Add a tap gesture recognizer that will allow the animation to be stopped and started. - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(manageTimer)) - mapView.addGestureRecognizer(tapGestureRecognizer) - } catch { - print("Failed to add the source or layer to style. Error: \(error)") - } - manageTimer() - } - - @objc func manageTimer() { - if timer == nil { - timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in - guard let self = self else { return } - - // There are five radar images, number 0-4. Increment the count. When that would - // result in an `imageNumber` value greater than 4, reset `imageNumber` to `0`. - if self.imageNumber < 4 { - self.imageNumber += 1 - } else { - self.imageNumber = 0 - } - // Create a `UIImage` from the file at the specified path. - let path = Bundle.main.path(forResource: "radar\(self.imageNumber)", ofType: "gif") - let image = UIImage(contentsOfFile: path!) - - do { - // Update the image used by the `ImageSource`. - try self.mapView.mapboxMap.style.updateImageSource(withId: self.sourceId, image: image!) - } catch { - print("Failed to update style image. Error: \(error)") - } - } - } else { - timer?.invalidate() - timer = nil - } - } - - deinit { - timer?.invalidate() - } -} diff --git a/Apps/Examples/Examples/All Examples/AnimateLayerExample.swift b/Apps/Examples/Examples/All Examples/AnimateLayerExample.swift deleted file mode 100644 index 767cd27b1c1f..000000000000 --- a/Apps/Examples/Examples/All Examples/AnimateLayerExample.swift +++ /dev/null @@ -1,128 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(AnimateLayerExample) -public class AnimateLayerExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - // A tuple that associates the source with its identifier. - public var airplaneRoute = (identifier: "airplane-route", source: GeoJSONSource()) - public var airplaneSymbol = (identifier: "airplane-symbol", source: GeoJSONSource()) - - override public func viewDidLoad() { - super.viewDidLoad() - - // Set the map's center coordinate and zoom level - let centerCoordinate = CLLocationCoordinate2D(latitude: 37.8, longitude: -96) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, - zoom: 2)) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allows the view controller to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.setupExample() - } - } - - public func setupExample() { - - // San Francisco, California - let origin = CLLocationCoordinate2DMake(37.776, -122.414) - // Washington, D.C. - let destination = CLLocationCoordinate2DMake(38.913, -77.032) - - let arcLine = arc(start: origin, end: destination) - - // Add the layers to be rendered on the map. - addLayers(for: arcLine) - - // Begin animating the airplane across the route line. - startAnimation(routeLine: arcLine) - } - - public func arc(start: CLLocationCoordinate2D, end: CLLocationCoordinate2D) -> LineString { - let line = LineString([start, end]) - let distance = Int(start.distance(to: end)) - - var coordinates = [CLLocationCoordinate2D]() - let steps = 500 - var index = 0 - - while index < distance { - index += distance / steps - let coord = line.coordinateFromStart(distance: CLLocationDistance(index))! - coordinates.append(coord) - } - - return LineString(coordinates.compactMap({ $0 })) - } - - public func addLayers(for routeLine: LineString) { - - // Define the source data and style layer for the airplane's route line. - airplaneRoute.source.data = .feature(Feature(geometry: routeLine)) - var lineLayer = LineLayer(id: "line-layer") - lineLayer.source = airplaneRoute.identifier - lineLayer.lineColor = .constant(StyleColor(.red)) - lineLayer.lineWidth = .constant(3.0) - lineLayer.lineCap = .constant(.round) - - // Define the source data and style layer for the airplane symbol. - let point = Point(routeLine.coordinates[0]) - airplaneSymbol.source.data = .feature(Feature(geometry: point)) - var airplaneSymbolLayer = SymbolLayer(id: "airplane") - airplaneSymbolLayer.source = airplaneSymbol.identifier - // "airport-15" is the name the image that belongs in the style's sprite by default. - airplaneSymbolLayer.iconImage = .constant(.name("airport-15")) - airplaneSymbolLayer.iconRotationAlignment = .constant(.map) - airplaneSymbolLayer.iconAllowOverlap = .constant(true) - airplaneSymbolLayer.iconIgnorePlacement = .constant(true) - // Get the "bearing" property from the point's feature dictionary, - // and use that value to determine the rotation angle of the airplane icon. - airplaneSymbolLayer.iconRotate = .expression(Exp(.get) { - "bearing" - }) - - // Add the sources and layers to the map style. - try! mapView.mapboxMap.style.addSource(airplaneRoute.source, id: airplaneRoute.identifier) - try! mapView.mapboxMap.style.addLayer(lineLayer) - - try! mapView.mapboxMap.style.addSource(airplaneSymbol.source, id: airplaneSymbol.identifier) - try! mapView.mapboxMap.style.addLayer(airplaneSymbolLayer, layerPosition: nil) - } - - public func startAnimation(routeLine: LineString) { - var runCount = 0 - - _ = Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { [weak self] timer in - - guard let self = self else { return } - - let coordinate = routeLine.coordinates[runCount] - let nextCoordinate = routeLine.coordinates[runCount + 1] - - // Identify the new coordinate to animate to, and calculate - // the bearing between the new coordinate and the following coordinate. - var geoJSON = Feature(geometry: Point(coordinate)) - geoJSON.properties = ["bearing": .number(coordinate.direction(to: nextCoordinate))] - - // Update the airplane source layer with the new coordinate and bearing. - try! self.mapView.mapboxMap.style.updateGeoJSONSource(withId: self.airplaneSymbol.identifier, - geoJSON: .feature(geoJSON)) - - runCount += 1 - - if runCount == 500 { - timer.invalidate() - // The below line is used for internal testing purposes only. - self.finish() - } - } - - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/AddOneMarkerSymbolExample.swift b/Apps/Examples/Examples/All Examples/Annotations/AddOneMarkerSymbolExample.swift deleted file mode 100644 index d658ce5ae0a9..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/AddOneMarkerSymbolExample.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Foundation -import MapboxMaps - -@objc(AddOneMarkerSymbolExample) -final class AddOneMarkerSymbolExample: UIViewController, ExampleProtocol { - private enum Constants { - static let BLUE_ICON_ID = "blue" - static let SOURCE_ID = "source_id" - static let LAYER_ID = "layer_id" - static let coordinate = CLLocationCoordinate2D(latitude: 55.665957, longitude: 12.550343) - } - - private lazy var mapView: MapView = { - let options = MapInitOptions(cameraOptions: CameraOptions(center: Constants.coordinate, zoom: 8)) - - return MapView(frame: view.bounds, mapInitOptions: options) - }() - - override func viewDidLoad() { - super.viewDidLoad() - - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - mapView.mapboxMap.loadStyleURI(.streets) { result in - guard let style = try? result.get() else { return } - - self.addMarkerAnnotation(toStyle: style) - // The following line is just for testing purposes. - self.finish() - } - } - - private func addMarkerAnnotation(toStyle style: Style) { - try? style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.BLUE_ICON_ID) - - var source = GeoJSONSource() - source.data = .geometry(Geometry.point(Point(Constants.coordinate))) - try? style.addSource(source, id: Constants.SOURCE_ID) - - var layer = SymbolLayer(id: Constants.LAYER_ID) - layer.source = Constants.SOURCE_ID - layer.iconImage = .constant(.name(Constants.BLUE_ICON_ID)) - layer.iconAnchor = .constant(.bottom) - try? style.addLayer(layer) - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/AnnotationView.swift b/Apps/Examples/Examples/All Examples/Annotations/AnnotationView.swift deleted file mode 100644 index 0a81b0c56735..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/AnnotationView.swift +++ /dev/null @@ -1,93 +0,0 @@ -import Foundation -import UIKit - -protocol AnnotationViewDelegate: AnyObject { - func annotationViewDidSelect(_ annotationView: AnnotationView) - func annotationViewDidUnselect(_ annotationView: AnnotationView) - func annotationViewDidPressClose(_ annotationView: AnnotationView) -} - -// `AnnotationView` is a custom `UIView` subclass which is used only for annotation demonstration -final class AnnotationView: UIView { - - weak var delegate: AnnotationViewDelegate? - - var selected: Bool = false { - didSet { - selectButton.setTitle(selected ? "Deselect" : "Select", for: .normal) - } - } - - var title: String? { - get { centerLabel.text } - set { centerLabel.text = newValue } - } - - lazy var centerLabel: UILabel = { - let label = UILabel(frame: .zero) - label.font = UIFont.systemFont(ofSize: 10) - label.numberOfLines = 0 - return label - }() - lazy var closeButton: UIButton = { - let button = UIButton(type: .system) - button.setTitleColor(.black, for: .normal) - button.setTitle("X", for: .normal) - return button - }() - lazy var selectButton: UIButton = { - let button = UIButton(type: .system) - button.setTitleColor(.white, for: .normal) - button.backgroundColor = #colorLiteral(red: 0, green: 0.4784313725, blue: 0.9882352941, alpha: 1) - button.layer.cornerRadius = 8 - button.clipsToBounds = true - button.setTitle("Select", for: .normal) - return button - }() - - override init(frame: CGRect) { - super.init(frame: frame) - self.backgroundColor = .green - - closeButton.addTarget(self, action: #selector(closePressed(sender:)), for: .touchUpInside) - selectButton.addTarget(self, action: #selector(selectPressed(sender:)), for: .touchUpInside) - - [centerLabel, closeButton, selectButton].forEach { item in - item.translatesAutoresizingMaskIntoConstraints = false - self.addSubview(item) - } - - NSLayoutConstraint.activate([ - closeButton.topAnchor.constraint(equalTo: topAnchor, constant: 4), - closeButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -4), - - centerLabel.topAnchor.constraint(equalTo: topAnchor, constant: 4), - centerLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -4), - centerLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 4), - - selectButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4), - selectButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -4), - selectButton.leftAnchor.constraint(equalTo: leftAnchor, constant: 4) - ]) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Action handlers - - @objc private func closePressed(sender: UIButton) { - delegate?.annotationViewDidPressClose(self) - } - - @objc private func selectPressed(sender: UIButton) { - if selected { - selected = false - delegate?.annotationViewDidUnselect(self) - } else { - selected = true - delegate?.annotationViewDidSelect(self) - } - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/CircleAnnotationExample.swift b/Apps/Examples/Examples/All Examples/Annotations/CircleAnnotationExample.swift deleted file mode 100644 index fafe36b0f2fa..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/CircleAnnotationExample.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Foundation -import MapboxMaps - -@objc(CircleAnnotationExample) -final class CircleAnnotationExample: UIViewController, ExampleProtocol { - private lazy var mapView: MapView = MapView(frame: view.bounds) - - override func viewDidLoad() { - super.viewDidLoad() - - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Create the CircleAnnotationManager - // Annotation managers are kept alive by `AnnotationOrchestrator` - // (`mapView.annotations`) until you explicitly destroy them - // by calling `mapView.annotations.removeAnnotationManager(withId:)` - let circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager() - circleAnnotationManager.delegate = self - - var annotations = [CircleAnnotation]() - for _ in 0...2000 { - var annotation = CircleAnnotation(centerCoordinate: .random) - annotation.circleColor = StyleColor(.random) - annotation.circleRadius = 12 - annotations.append(annotation) - } - - circleAnnotationManager.annotations = annotations - // The following line is just for testing purposes. - finish() - } -} - -extension CircleAnnotationExample: AnnotationInteractionDelegate { - func annotationManager(_ manager: AnnotationManager, didDetectTappedAnnotations annotations: [Annotation]) { - print("AnnotationManager did detect tapped annotations: \(annotations)") - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift b/Apps/Examples/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift deleted file mode 100644 index e59f1fe72f7f..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift +++ /dev/null @@ -1,52 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(CustomPointAnnotationExample) -final class CustomPointAnnotationExample: UIViewController, ExampleProtocol { - - private var mapView: MapView! - private let customImage = UIImage(named: "star")! - - override func viewDidLoad() { - super.viewDidLoad() - - // Center the map camera over New York City - let centerCoordinate = CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, - zoom: 9.0)) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in - guard let self = self else { return } - self.setupExample() - - // The following line is just for testing purposes. - self.finish() - } - } - - private func setupExample() { - - // We want to display the annotation at the center of the map's current viewport - let centerCoordinate = mapView.cameraState.center - - // Make a `PointAnnotationManager` which will be responsible for managing - // a collection of `PointAnnotion`s. - // Annotation managers are kept alive by `AnnotationOrchestrator` - // (`mapView.annotations`) until you explicitly destroy them - // by calling `mapView.annotations.removeAnnotationManager(withId:)` - let pointAnnotationManager = mapView.annotations.makePointAnnotationManager() - - // Initialize a point annotation with a geometry ("coordinate" in this case) - // and configure it with a custom image (sourced from the asset catalogue) - var customPointAnnotation = PointAnnotation(coordinate: centerCoordinate) - customPointAnnotation.image = .init(image: customImage, name: "my-custom-image-name") - - // Add the annotation to the manager in order to render it on the map. - pointAnnotationManager.annotations = [customPointAnnotation] - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/IconSizeChangeExample.swift b/Apps/Examples/Examples/All Examples/Annotations/IconSizeChangeExample.swift deleted file mode 100644 index 4ea9d3c51296..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/IconSizeChangeExample.swift +++ /dev/null @@ -1,146 +0,0 @@ -import Foundation -import MapboxMaps - -final class IconSizeChangeExample: UIViewController, ExampleProtocol { - enum Constants { - static let blueMarkerImageId = "blue-marker" - static let markerLayerId = "marker-layer" - static let markerSourceId = "marker-source" - static let selectedMarkerLayerId = "selected-marker-layer" - static let selectedMarkerSourceId = "selected-marker" - } - private var mapView: MapView! - private var markerSelected = false - - override func viewDidLoad() { - super.viewDidLoad() - - let initialPosition = CLLocationCoordinate2D(latitude: 42.354950, longitude: -71.065634) - let cameraOptions = CameraOptions(center: initialPosition, zoom: 11) - let initOptions = MapInitOptions(cameraOptions: cameraOptions) - mapView = MapView(frame: view.bounds, mapInitOptions: initOptions) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in - - // Set up the example - self?.setupExample() - - // The below line is used for internal testing purposes only. - self?.finish() - } - - mapView.mapboxMap.loadStyleURI(.dark) - } - - private func setupExample() { - let markerFeatures = [ - CLLocationCoordinate2D(latitude: 42.354950, longitude: -71.065634), // Boston Common Park - CLLocationCoordinate2D(latitude: 42.346645, longitude: -71.097293), // Fenway Park - CLLocationCoordinate2D(latitude: 42.363725, longitude: -71.053694) // The Paul Revere House - ].map({ Feature(geometry: Point($0)) }) - - // Create a GeoJSON data source for markers - var markerSource = GeoJSONSource() - markerSource.data = .featureCollection(FeatureCollection(features: markerFeatures)) - try? mapView.mapboxMap.style.addSource(markerSource, id: Constants.markerSourceId) - - // Add marker image to the map - try? mapView.mapboxMap.style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.blueMarkerImageId) - - // Create a symbol layer for markers - var markerLayer = SymbolLayer(id: Constants.markerLayerId) - markerLayer.source = Constants.markerSourceId - markerLayer.iconImage = .constant(.name(Constants.blueMarkerImageId)) - markerLayer.iconAllowOverlap = .constant(true) - // Adding an offset so that the bottom of the blue icon gets fixed to the coordinate, rather than the - // middle of the icon being fixed to the coordinate point. - markerLayer.iconOffset = .constant([0, -9]) - - try? mapView.mapboxMap.style.addLayer(markerLayer) - - // Create a GeoJSON source for the selected marker - var selectedMarkerSource = GeoJSONSource() - selectedMarkerSource.data = .geometry(.point(Point(CLLocationCoordinate2D()))) - try? mapView.mapboxMap.style.addSource(selectedMarkerSource, id: Constants.selectedMarkerSourceId) - - // Create a symbol layer for the selected marker - var selectedMarkerLayer = SymbolLayer(id: Constants.selectedMarkerLayerId) - selectedMarkerLayer.source = Constants.selectedMarkerSourceId - selectedMarkerLayer.iconImage = .constant(.name(Constants.blueMarkerImageId)) - selectedMarkerLayer.iconAllowOverlap = .constant(true) - // Adding an offset so that the bottom of the blue icon gets fixed to the coordinate, rather than the - // middle of the icon being fixed to the coordinate point. - selectedMarkerLayer.iconOffset = .constant([0, -9]) - - try? mapView.mapboxMap.style.addLayer(selectedMarkerLayer) - - // add a tap gesture recognizer to the map - mapView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(mapTapped(_:)))) - } - - @objc private func mapTapped(_ sender: UITapGestureRecognizer) { - let point = sender.location(in: mapView) - - let options = RenderedQueryOptions(layerIds: [Constants.selectedMarkerLayerId], filter: nil) - // check if the selected marker was tapped - mapView.mapboxMap.queryRenderedFeatures(at: point, options: options) { [weak self] result in - guard let self = self else { return } - - switch result { - case .success(let features): - if !features.isEmpty, self.markerSelected { - return - } - - self.updateSelectedMarker(atPoint: point) - case .failure(let error): - self.showAlert(with: "An error occurred: \(error.localizedDescription)") - } - } - } - - private func updateSelectedMarker(atPoint point: CGPoint) { - let options = RenderedQueryOptions(layerIds: [Constants.markerLayerId], filter: nil) - mapView.mapboxMap.queryRenderedFeatures(at: point, options: options) { [weak self] result in - guard let self = self else { return } - - switch result { - case .success(let features): - if features.isEmpty { - if self.markerSelected { - self.updateMarker(selected: false) - } - return - } - - if let geometry = features.first?.feature.geometry { - try? self.mapView.mapboxMap.style.updateGeoJSONSource(withId: Constants.selectedMarkerSourceId, - geoJSON: .geometry(geometry)) - } - - if self.markerSelected { - self.updateMarker(selected: false) - } - - if !features.isEmpty { - self.updateMarker(selected: true) - } - case .failure(let error): - self.showAlert(with: "An error occurred: \(error.localizedDescription)") - } - } - } - - private func updateMarker(selected: Bool) { - try? mapView.mapboxMap.style.updateLayer( - withId: Constants.selectedMarkerLayerId, - type: SymbolLayer.self, - update: { (layer: inout SymbolLayer) throws in - layer.iconSize = .constant(selected ? 2 : 1) - }) - markerSelected = selected - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/MultipleGeometriesExample.swift b/Apps/Examples/Examples/All Examples/Annotations/MultipleGeometriesExample.swift deleted file mode 100644 index 329df04e9cae..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/MultipleGeometriesExample.swift +++ /dev/null @@ -1,106 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(MultipleGeometriesExample) -public class MultipleGeometriesExample: UIViewController, ExampleProtocol { - enum Constants { - static let geoJSONDataSourceIdentifier = "geoJSON-data-source" - } - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - // Set the center coordinate and zoom level. - let centerCoordinate = CLLocationCoordinate2D(latitude: 38.93490939383946, longitude: -77.03619251024163) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 11)) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allow the view controller to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.addGeoJSONSource() - self.addPolygonLayer() - self.addLineStringLayer() - self.addPointLayer() - - // The below line is used for internal testing purposes only. - self.finish() - } - } - - // Load GeoJSON file from local bundle and decode into a `FeatureCollection`. - internal func decodeGeoJSON(from fileName: String) throws -> FeatureCollection? { - guard let path = Bundle.main.path(forResource: fileName, ofType: "geojson") else { - preconditionFailure("File '\(fileName)' not found.") - } - - let filePath = URL(fileURLWithPath: path) - - var featureCollection: FeatureCollection? - - do { - let data = try Data(contentsOf: filePath) - featureCollection = try JSONDecoder().decode(FeatureCollection.self, from: data) - } catch { - print("Error parsing data: \(error)") - } - - return featureCollection - } - - private func addGeoJSONSource() { - // Attempt to decode GeoJSON from file bundled with application. - guard let featureCollection = try? decodeGeoJSON(from: "GeoJSONSourceExample") else { return } - - // Create a GeoJSON data source. - var geoJSONSource = GeoJSONSource() - geoJSONSource.data = .featureCollection(featureCollection) - try! mapView.mapboxMap.style.addSource(geoJSONSource, id: Constants.geoJSONDataSourceIdentifier) - } - - /// Create and style a FillLayer that uses the Polygon Feature's coordinates in the GeoJSON data - private func addPolygonLayer() { - var polygonLayer = FillLayer(id: "fill-layer") - polygonLayer.filter = Exp(.eq) { - "$type" - "Polygon" - } - polygonLayer.source = Constants.geoJSONDataSourceIdentifier - polygonLayer.fillColor = .constant(StyleColor(red: 68, green: 105, blue: 247, alpha: 1)!) - polygonLayer.fillOpacity = .constant(0.3) - try! mapView.mapboxMap.style.addLayer(polygonLayer) - } - - private func addLineStringLayer() { - // Create and style a LineLayer that uses the Line String Feature's coordinates in the GeoJSON data - var lineLayer = LineLayer(id: "line-layer") - lineLayer.filter = Exp(.eq) { - "$type" - "LineString" - } - lineLayer.source = Constants.geoJSONDataSourceIdentifier - lineLayer.lineColor = .constant(StyleColor(.red)) - lineLayer.lineWidth = .constant(2) - try! mapView.mapboxMap.style.addLayer(lineLayer) - } - - public func addPointLayer() { - // Create a circle layer associated with the GeoJSON data source, - // filter it so that only the point data is shown, - // and apply basic styling to it. - var circleLayer = CircleLayer(id: "circle-layer") - circleLayer.filter = Exp(.eq) { - "$type" - "Point" - } - circleLayer.source = Constants.geoJSONDataSourceIdentifier - circleLayer.circleColor = .constant(StyleColor(.red)) - circleLayer.circleRadius = .constant(6.0) - circleLayer.circleStrokeWidth = .constant(2.0) - circleLayer.circleStrokeColor = .constant(StyleColor(.black)) - try! mapView.mapboxMap.style.addLayer(circleLayer) - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/SwiftUIExample.swift b/Apps/Examples/Examples/All Examples/Annotations/SwiftUIExample.swift deleted file mode 100644 index 00ba95949d39..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/SwiftUIExample.swift +++ /dev/null @@ -1,268 +0,0 @@ -import UIKit -import MapboxMaps -import SwiftUI - -struct Camera { - var center: CLLocationCoordinate2D - var zoom: CGFloat -} - -/// `SwiftUIMapView` is a SwiftUI wrapper around UIKit-based `MapView`. -/// It works by conforming to the `UIViewRepresentable` protocol. When -/// your app uses `SwiftUIMapView`, SwiftUI creates and manages a -/// single instance of `MapView` behind the scenes so that if your map -/// configuration changes, the underlying map view doesn't need to be recreated. -@available(iOS 13.0, *) -struct SwiftUIMapView: UIViewRepresentable { - - /// Bindings should be used for map values that can - /// change as a result of user interaction. They allow - /// other UI elements to stay in sync as the user interacts - /// with the map. Here, we add a `camera` binding - /// that represents a subset of the available camera functionality. - /// Your app could customize this to your use case. In this example - /// the binding is set via `init(resourceOptions:camera:)` - @Binding private var camera: Camera - - /// Map attributes that can only be configured programmatically - /// can simply be exposed as a private var paired with a - /// builder-style method. When you use `SwiftUIMapView`, you - /// have the option to customize it by calling the builder method. - /// For example, with `styleURI`, you might say - private var styleURI = StyleURI.streets - - /// This is the builder-style method for setting `styleURI`. - /// It returns an updated `SwiftUIMapView` value that - /// has the specified `styleURI`. This approach allows you - /// to chain these customizers — a common pattern in SwiftUI. - func styleURI(_ styleURI: StyleURI) -> Self { - var updated = self - updated.styleURI = styleURI - return updated - } - - /// Here's a property and builder method for annotations - private var annotations = [PointAnnotation]() - - func annotations(_ annotations: [PointAnnotation]) -> Self { - var updated = self - updated.annotations = annotations - return updated - } - - /// Unlike `styleURI`, there's no good default value for `mapInitOptions` - /// because it's the value that contains your Mapbox access token. For that reason, - /// it's declared here as a `let` and is a required parameter in the initializer. - private let mapInitOptions: MapInitOptions - - init(mapInitOptions: MapInitOptions, camera: Binding) { - self.mapInitOptions = mapInitOptions - _camera = camera - } - - /// The first time SwiftUI needs to render this view, it starts by invoking `makeCoordinator()`. - /// SwiftUI holds on to the value you return just like it holds on to the `MapView`. This gives you a - /// place to direct callbacks from the MapView (delegates, observer callbacks, etc). You need to - /// use the coordinator for this and not this struct because this struct can be recreated many times - /// as your map configurations change externally. Fortunately, even as this struct is recreated, - /// the coordinator and the map view will only be created once. - func makeCoordinator() -> SwiftUIMapViewCoordinator { - SwiftUIMapViewCoordinator(camera: $camera) - } - - /// After SwiftUI creates the coordinator, it creates the underlying `UIView`, in this case a `MapView`. - /// This method should create the `MapView`, and make sure that it is configured to be in sync - /// with the current settings of `SwiftUIMapView` (in this example, just the `camera` and `styleURI`). - func makeUIView(context: UIViewRepresentableContext) -> MapView { - let mapView = MapView(frame: .zero, mapInitOptions: mapInitOptions) - - /// Additionally, this is your opportunity to connect the coordinator to the map view. In this example - /// the coordinator is given a reference to the map view. It uses the reference to set up the necessary - /// observations so that it can respond to map events. It also creates an annotation manager. - context.coordinator.mapView = mapView - - updateUIView(mapView, context: context) - - return mapView - } - - /// If your `SwiftUIMapView` is reconfigured externally, SwiftUI will invoke `updateUIView(_:context:)` - /// to give you an opportunity to re-sync the state of the underlying map view. - func updateUIView(_ mapView: MapView, context: Context) { - /// When setting the camera, we need to temporarily disable the coordinator's observers. - /// If we didn't do this, the SwiftUI state would be modified during view update, which - /// causes undefined behavior. - context.coordinator.performWithoutObservation { - mapView.mapboxMap.setCamera(to: CameraOptions(center: camera.center, zoom: camera.zoom)) - } - /// Since setting the style causes some reloading to happen, - /// we only call the setter if the value has changed. - if mapView.mapboxMap.style.uri != styleURI { - mapView.mapboxMap.style.uri = styleURI - } - - /// The coordinator exposes the annotation manager so that we can sync the annotations - context.coordinator.pointAnnotationManager.annotations = annotations - } -} - -/// Here's our custom `Coordinator` implementation. -@available(iOS 13.0, *) -final class SwiftUIMapViewCoordinator { - /// It holds a binding to the camera - @Binding private var camera: Camera - - /// It exposes the annotation manager - private(set) var pointAnnotationManager: PointAnnotationManager! - - var mapView: MapView! { - didSet { - cancelable?.cancel() - cancelable = nil - - /// In the following observations, `self` is captured as an unowned reference to avoid a strong - /// reference cycle from mapView --> mapboxMap --> handler block --> self --> mapView. - /// In this situation, weak is unnecessary because the subscription will be canceled via the returned - /// `Cancelable` as soon as `self` is deinitialized. - - /// The coordinator observes the `.cameraChanged` event, and - /// whenever the camera changes, it updates the camera binding. - cancelable = mapView.mapboxMap.onEvery(event: .cameraChanged) { [unowned self] _ in - guard !ignoreNotifications else { - return - } - - /// As the camera changes, we update the binding. SwiftUI - /// will propagate this change to any other UI elements connected - /// to the same binding. - camera.center = mapView.cameraState.center - camera.zoom = mapView.cameraState.zoom - } - - pointAnnotationManager = mapView.annotations.makePointAnnotationManager() - } - } - - private var cancelable: Cancelable? - - init(camera: Binding) { - _camera = camera - } - - deinit { - cancelable?.cancel() - } - - private var ignoreNotifications = false - - func performWithoutObservation(_ block: () -> Void) { - ignoreNotifications = true - block() - ignoreNotifications = false - } -} - -/// Here's an example usage of `SwiftUIMapView` -@available(iOS 13.0, *) -struct ContentView: View { - - /// For demonstration purposes, this view has its own state for the camera and style URL. - /// In your app, these values could be constants defined directly in `body` or could come - /// from a model object. - @State private var camera = Camera(center: CLLocationCoordinate2D(latitude: 40, longitude: -75), zoom: 14) - @State private var styleURI = StyleURI.streets - - private var onAppear: () -> Void - - init(onAppear: @escaping () -> Void) { - self.onAppear = onAppear - } - - /// When you create an annotation, you can assign it an ID or allow it to generate its own UUID. Here - /// we assign IDs explicitly to achieve a consistent result whenever this view is reevaluated. - private let annotations: [PointAnnotation] = [ - CLLocationCoordinate2D(latitude: 40, longitude: -75), - CLLocationCoordinate2D(latitude: 40, longitude: -75.001), - CLLocationCoordinate2D(latitude: 40, longitude: -74.999)] - .enumerated() - .map { (idx, coordinate) in - var annotation = PointAnnotation(id: idx.description, coordinate: coordinate) - annotation.image = .init(image: UIImage(named: "custom_marker")!, name: "custom_marker") - return annotation - } - - var body: some View { - VStack { - SwiftUIMapView( - mapInitOptions: MapInitOptions(), - - /// Here, we pass the camera state variable into `SwiftUIMapView` as a binding - camera: $camera) - - /// Here's an example usage of the builder method to set `styleURI`. - /// Note that in this case, we just need the current value, so we write - /// `styleURI`, not `$styleURI` - .styleURI(styleURI) - - /// Since these methods use the builder pattern, we can chain them together - .annotations(annotations) - - /// We configure the slider to bind to the camera's zoom. Adjusting the slider with - /// change the zoom on the map, and changing the zoom by interacting with the map - /// will change the slider. Here's the data flow: - /// - /// Slider to Map: - /// - User interacts with the slider - /// - Slider updates the camera binding's zoom value - /// - SwiftUI invokes `updateUIView(_:context:)` on `SwiftUIMapView` - /// - `SwiftUIMapView` reads the updated camera zoom value and sets it on the underlying `MapView` - /// - /// Map to Slider: - /// - User interacts with the map, adjusting the zoom - /// - Map sends the `.cameraChanged` event, which is observed by the coordinator - /// - The coordinator updates the value of the zoom on the `camera` binding - /// - SwiftUI updates the Slider accordingly - Slider(value: $camera.zoom, in: 0...20) - - /// The picker is bound to `styleURI`. - Picker(selection: $styleURI, label: Text("Map Style")) { - Text("Streets").tag(StyleURI.streets) - Text("Dark").tag(StyleURI.dark) - }.pickerStyle(SegmentedPickerStyle()) - }.onAppear(perform: onAppear) - } -} - -/// The rest of this example is just some boilerplate to present the ContentView and show the example -@objc(SwiftUIExample) -final class SwiftUIExample: UIViewController, ExampleProtocol { - - override func viewDidLoad() { - super.viewDidLoad() - - if #available(iOS 13.0, *) { - let contentView = ContentView { [weak self] in - // The following line is just for testing purposes. - self?.finish() - } - let hostingViewController = UIHostingController(rootView: contentView) - addChild(hostingViewController) - hostingViewController.view.frame = view.bounds - view.addSubview(hostingViewController.view) - hostingViewController.didMove(toParent: self) - } else { - // Fallback on earlier versions - let label = UILabel() - label.text = "This example runs on iOS 13+" - label.font = .systemFont(ofSize: 20) - label.textColor = .white - label.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(label) - NSLayoutConstraint.activate([ - view.centerXAnchor.constraint(equalTo: label.centerXAnchor), - view.centerYAnchor.constraint(equalTo: label.centerYAnchor) - ]) - finish() - } - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/SymbolClusteringExample.swift b/Apps/Examples/Examples/All Examples/Annotations/SymbolClusteringExample.swift deleted file mode 100644 index 31f911330b47..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/SymbolClusteringExample.swift +++ /dev/null @@ -1,177 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(SymbolClusteringExample) -class SymbolClusteringExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - // Create a `MapView` centered over Washington, DC. - let center = CLLocationCoordinate2D(latitude: 38.889215, longitude: -77.039354) - let cameraOptions = CameraOptions(center: center, zoom: 11) - let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .dark) - mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - - view.addSubview(mapView) - - // Add the source and style layers once the map has loaded. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.addSymbolClusteringLayers() - } - - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(gestureRecognizer:))) - mapView.addGestureRecognizer(tapGestureRecognizer) - } - - func addSymbolClusteringLayers() { - let style = mapView.mapboxMap.style - // The image named `fire-station-11` is included in the app's Assets.xcassets bundle. - // In order to recolor an image, you need to add a template image to the map's style. - // The image's rendering mode can be set programmatically or in the asset catalogue. - let image = UIImage(named: "fire-station-11")!.withRenderingMode(.alwaysTemplate) - - // Add the image tp the map's style. Set `sdf` to `true`. This allows the icon images to be recolored. - // For more information about `SDF`, or Signed Distance Fields, see - // https://docs.mapbox.com/help/troubleshooting/using-recolorable-images-in-mapbox-maps/#what-are-signed-distance-fields-sdf - try! style.addImage(image, id: "fire-station-icon", sdf: true) - - // Fire_Hydrants.geojson contains information about fire hydrants in the District of Columbia. - // It was downloaded on 6/10/21 from https://opendata.dc.gov/datasets/DCGIS::fire-hydrants/about - let url = Bundle.main.url(forResource: "Fire_Hydrants", withExtension: "geojson")! - - // Create a GeoJSONSource using the previously specified URL. - var source = GeoJSONSource() - source.data = .url(url) - - // Enable clustering for this source. - source.cluster = true - source.clusterRadius = 75 - let sourceID = "fire-hydrant-source" - - var clusteredLayer = createClusteredLayer() - clusteredLayer.source = sourceID - - var unclusteredLayer = createUnclusteredLayer() - unclusteredLayer.source = sourceID - - // `clusterCountLayer` is a `SymbolLayer` that represents the point count within individual clusters. - var clusterCountLayer = createNumberLayer() - clusterCountLayer.source = sourceID - - // Add the source and two layers to the map. - try! style.addSource(source, id: sourceID) - try! style.addLayer(clusteredLayer) - try! style.addLayer(unclusteredLayer, layerPosition: .below(clusteredLayer.id)) - try! style.addLayer(clusterCountLayer) - - // This is used for internal testing purposes only and can be excluded - // from your implementation. - finish() - } - - func createClusteredLayer() -> CircleLayer { - // Create a symbol layer to represent the clustered points. - var clusteredLayer = CircleLayer(id: "clustered-circle-layer") - - // Filter out unclustered features by checking for `point_count`. This - // is added to clusters when the cluster is created. If your source - // data includes a `point_count` property, consider checking - // for `cluster_id`. - clusteredLayer.filter = Exp(.has) { "point_count" } - - // Set the color of the icons based on the number of points within - // a given cluster. The first value is a default value. - clusteredLayer.circleColor = .expression(Exp(.step) { - Exp(.get) { "point_count" } - UIColor.systemGreen - 50 - UIColor.systemBlue - 100 - UIColor.systemRed - }) - - clusteredLayer.circleRadius = .constant(25) - - return clusteredLayer - } - - func createUnclusteredLayer() -> SymbolLayer { - // Create a symbol layer to represent the points that aren't clustered. - var unclusteredLayer = SymbolLayer(id: "unclustered-point-layer") - - // Filter out clusters by checking for `point_count`. - unclusteredLayer.filter = Exp(.not) { - Exp(.has) { "point_count" } - } - unclusteredLayer.iconImage = .constant(.name("fire-station-icon")) - unclusteredLayer.iconColor = .constant(StyleColor(.white)) - - // Rotate the icon image based on the recorded water flow. - // The `mod` operator allows you to use the remainder after dividing - // the specified values. - unclusteredLayer.iconRotate = .expression(Exp(.mod) { - Exp(.get) { "FLOW" } - 360 - }) - - return unclusteredLayer - } - - func createNumberLayer() -> SymbolLayer { - var numberLayer = SymbolLayer(id: "cluster-count-layer") - - // check whether the point feature is clustered - numberLayer.filter = Exp(.has) { "point_count" } - - // Display the value for 'point_count' in the text field - numberLayer.textField = .expression(Exp(.get) { "point_count" }) - numberLayer.textSize = .constant(12) - return numberLayer - } - - @objc func handleTap(gestureRecognizer: UITapGestureRecognizer) { - let point = gestureRecognizer.location(in: mapView) - - // Look for features at the tap location within the clustered and - // unclustered layers. - mapView.mapboxMap.queryRenderedFeatures(at: point, - options: RenderedQueryOptions(layerIds: ["unclustered-point-layer", "clustered-circle-layer"], - filter: nil)) { [weak self] result in - switch result { - case .success(let queriedFeatures): - // Return the first feature at that location, then pass attributes to the alert controller. - // Check whether the feature has values for `ASSETNUM` and `LOCATIONDETAIL`. These properties - // come from the fire hydrant dataset and indicate that the selected feature is not clustered. - if let selectedFeatureProperties = queriedFeatures.first?.feature.properties, - case let .string(featureInformation) = selectedFeatureProperties["ASSETNUM"], - case let .string(location) = selectedFeatureProperties["LOCATIONDETAIL"] { - self?.showAlert(withTitle: "Hydrant \(featureInformation)", and: "\(location)") - // If the feature is a cluster, it will have `point_count` and `cluster_id` properties. These are assigned - // when the cluster is created. - } else if let selectedFeatureProperties = queriedFeatures.first?.feature.properties, - case let .number(pointCount) = selectedFeatureProperties["point_count"], - case let .number(clusterId) = selectedFeatureProperties["cluster_id"] { - // If the tap landed on a cluster, pass the cluster ID and point count to the alert. - self?.showAlert(withTitle: "Cluster ID \(Int(clusterId))", and: "There are \(Int(pointCount)) points in this cluster") - } - case .failure(let error): - self?.showAlert(withTitle: "An error occurred: \(error.localizedDescription)", and: "Please try another hydrant") - } - } - } - - // Present an alert with a given title and message. - func showAlert(withTitle title: String, and message: String) { - let alertController = UIAlertController(title: title, - message: message, - preferredStyle: .alert) - - alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) - - present(alertController, animated: true, completion: nil) - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationBasicExample.swift b/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationBasicExample.swift deleted file mode 100644 index 95e77ee98d16..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationBasicExample.swift +++ /dev/null @@ -1,72 +0,0 @@ -import UIKit -import MapboxMaps -import CoreLocation - -@objc(ViewAnnotationBasicExample) -final class ViewAnnotationBasicExample: UIViewController, ExampleProtocol { - - private var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - - let centerCoordinate = CLLocationCoordinate2D(latitude: 39.7128, longitude: -75.0060) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 7)) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onMapClick))) - view.addSubview(mapView) - - addViewAnnotation(at: mapView.mapboxMap.coordinate(for: mapView.center)) - - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in - guard let self = self else { return } - self.finish() - } - } - - // MARK: - Action handlers - - @objc private func onMapClick(_ sender: UITapGestureRecognizer) { - guard sender.state == .ended else { return } - addViewAnnotation(at: mapView.mapboxMap.coordinate(for: sender.location(in: mapView))) - } - - @objc private func onSampleViewClick(_ sender: UITapGestureRecognizer) { - guard let view = sender.view else { return } - mapView.viewAnnotations.remove(view) - } - - // MARK: - Annotation management - - private func addViewAnnotation(at coordinate: CLLocationCoordinate2D) { - let options = ViewAnnotationOptions( - geometry: Point(coordinate), - allowOverlap: true, - anchor: .center - ) - let annotationView = AnnotationView(frame: CGRect(x: 0, y: 0, width: 100, height: 80)) - annotationView.title = String(format: "lat=%.2f\nlon=%.2f", coordinate.latitude, coordinate.longitude) - annotationView.delegate = self - try? mapView.viewAnnotations.add(annotationView, options: options) - } -} - -extension ViewAnnotationBasicExample: AnnotationViewDelegate { - func annotationViewDidSelect(_ annotationView: AnnotationView) { - let options = ViewAnnotationOptions(selected: true) - - try? mapView.viewAnnotations.update(annotationView, options: options) - } - - func annotationViewDidUnselect(_ annotationView: AnnotationView) { - let options = ViewAnnotationOptions(selected: false) - - try? mapView.viewAnnotations.update(annotationView, options: options) - } - - func annotationViewDidPressClose(_ annotationView: AnnotationView) { - mapView.viewAnnotations.remove(annotationView) - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationMarkerExample.swift b/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationMarkerExample.swift deleted file mode 100644 index a91ba3261ec5..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationMarkerExample.swift +++ /dev/null @@ -1,205 +0,0 @@ -import UIKit -import MapboxMaps -import CoreLocation - -@objc(ViewAnnotationMarkerExample) -final class ViewAnnotationMarkerExample: UIViewController, ExampleProtocol { - - private enum Constants { - static let BLUE_ICON_ID = "blue" - static let SOURCE_ID = "source_id" - static let LAYER_ID = "layer_id" - static let TERRAIN_SOURCE = "TERRAIN_SOURCE" - static let TERRAIN_URL_TILE_RESOURCE = "mapbox://mapbox.mapbox-terrain-dem-v1" - static let MARKER_ID_PREFIX = "view_annotation_" - static let SELECTED_ADD_COEF_PX: CGFloat = 50 - } - - private var mapView: MapView! - private var pointList: [Feature] = [] - private var markerId = 0 - - private let image = UIImage(named: "blue_marker_view")! - private lazy var markerHeight: CGFloat = image.size.height - - lazy var styleChangeButton: UIButton = { - let button = UIButton(type: .system) - button.setTitleColor(.white, for: .normal) - button.backgroundColor = .systemTeal - button.layer.cornerRadius = 8 - button.clipsToBounds = true - button.setTitle("Change style", for: .normal) - button.addTarget(self, action: #selector(styleChangePressed(sender:)), for: .touchUpInside) - button.translatesAutoresizingMaskIntoConstraints = false - return button - }() - - override func viewDidLoad() { - super.viewDidLoad() - - let centerCoordinate = CLLocationCoordinate2D(latitude: 39.7128, longitude: -75.0060) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 7)) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onMapClick))) - mapView.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(onMapLongClick))) - view.addSubview(mapView) - - addMarkerAndAnnotation(at: mapView.mapboxMap.coordinate(for: mapView.center)) - - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in - guard let self = self else { return } - self.finish() - } - - mapView.mapboxMap.onEvery(event: .styleLoaded) { [weak self] _ in - self?.prepareStyle() - } - - mapView.mapboxMap.style.uri = .streets - - view.addSubview(styleChangeButton) - - NSLayoutConstraint.activate([ - styleChangeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -32), - styleChangeButton.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -16), - styleChangeButton.widthAnchor.constraint(equalToConstant: 128) - ]) - } - - // MARK: - Action handlers - - @objc private func onMapLongClick(_ sender: UILongPressGestureRecognizer) { - guard sender.state == .ended else { return } - let point = Point(mapView.mapboxMap.coordinate(for: sender.location(in: mapView))) - _ = addMarker(at: point) - } - - @objc private func onMapClick(_ sender: UITapGestureRecognizer) { - let screenPoint = sender.location(in: mapView) - let queryOptions = RenderedQueryOptions(layerIds: [Constants.LAYER_ID], filter: nil) - mapView.mapboxMap.queryRenderedFeatures(at: screenPoint, options: queryOptions) { [weak self] result in - if case let .success(queriedFeatures) = result, - let self = self, - let feature = queriedFeatures.first?.feature, - let id = feature.identifier, - case let .string(idString) = id, - let viewAnnotations = self.mapView.viewAnnotations { - if let annotationView = viewAnnotations.view(forFeatureId: idString) { - let visible = viewAnnotations.options(for: annotationView)?.visible ?? true - try? viewAnnotations.update(annotationView, options: ViewAnnotationOptions(visible: !visible)) - } else { - let markerCoordinates: CLLocationCoordinate2D - if let geometry = feature.geometry, case let Geometry.point(point) = geometry { - markerCoordinates = point.coordinates - } else { - markerCoordinates = self.mapView.mapboxMap.coordinate(for: screenPoint) - } - self.addViewAnnotation(at: markerCoordinates, withMarkerId: idString) - } - } - } - } - - @objc private func styleChangePressed(sender: UIButton) { - mapView.mapboxMap.style.uri = mapView.mapboxMap.style.uri == .streets ? .satelliteStreets : .streets - } - - // MARK: - Style management - - private func prepareStyle() { - let style = mapView.mapboxMap.style - try? style.addImage(image, id: Constants.BLUE_ICON_ID) - - var source = GeoJSONSource() - source.data = .featureCollection(FeatureCollection(features: pointList)) - try? mapView.mapboxMap.style.addSource(source, id: Constants.SOURCE_ID) - - if style.uri == .satelliteStreets { - var demSource = RasterDemSource() - demSource.url = Constants.TERRAIN_URL_TILE_RESOURCE - try? mapView.mapboxMap.style.addSource(demSource, id: Constants.TERRAIN_SOURCE) - let terrain = Terrain(sourceId: Constants.TERRAIN_SOURCE) - try? mapView.mapboxMap.style.setTerrain(terrain) - } - - var layer = SymbolLayer(id: Constants.LAYER_ID) - layer.source = Constants.SOURCE_ID - layer.iconImage = .constant(.name(Constants.BLUE_ICON_ID)) - layer.iconAnchor = .constant(.bottom) - layer.iconAllowOverlap = .constant(true) - try? mapView.mapboxMap.style.addLayer(layer) - } - - // MARK: - Annotation management - - private func addMarkerAndAnnotation(at coordinate: CLLocationCoordinate2D) { - let point = Point(coordinate) - let markerId = addMarker(at: point) - addViewAnnotation(at: coordinate, withMarkerId: markerId) - } - - // Add a marker to a custom GeoJSON source: - // This is an optional step to demonstrate the automatic alignment of view annotations - // with features in a data source - private func addMarker(at point: Point) -> String { - let currentId = "\(Constants.MARKER_ID_PREFIX)\(markerId)" - markerId += 1 - var feature = Feature(geometry: point) - feature.identifier = .string(currentId) - pointList.append(feature) - if (try? mapView.mapboxMap.style.source(withId: Constants.SOURCE_ID)) != nil { - try? mapView.mapboxMap.style.updateGeoJSONSource(withId: Constants.SOURCE_ID, geoJSON: .featureCollection(FeatureCollection(features: pointList))) - } - return currentId - } - - // Add a view annotation at a specified location and optionally bind it to an ID of a marker - private func addViewAnnotation(at coordinate: CLLocationCoordinate2D, withMarkerId markerId: String? = nil) { - let options = ViewAnnotationOptions( - geometry: Point(coordinate), - width: 128, - height: 64, - associatedFeatureId: markerId, - allowOverlap: false, - anchor: .bottom - ) - let annotationView = AnnotationView(frame: CGRect(x: 0, y: 0, width: 128, height: 64)) - annotationView.title = String(format: "lat=%.2f\nlon=%.2f", coordinate.latitude, coordinate.longitude) - annotationView.delegate = self - try? mapView.viewAnnotations.add(annotationView, options: options) - - // Set the vertical offset of the annotation view to be placed above the marker - try? mapView.viewAnnotations.update(annotationView, options: ViewAnnotationOptions(offsetY: markerHeight)) - } -} - -extension ViewAnnotationMarkerExample: AnnotationViewDelegate { - func annotationViewDidSelect(_ annotationView: AnnotationView) { - guard let options = self.mapView.viewAnnotations.options(for: annotationView) else { return } - - let updateOptions = ViewAnnotationOptions( - width: (options.width ?? 0.0) + Constants.SELECTED_ADD_COEF_PX, - height: (options.height ?? 0.0) + Constants.SELECTED_ADD_COEF_PX, - selected: true - ) - try? self.mapView.viewAnnotations.update(annotationView, options: updateOptions) - } - - func annotationViewDidUnselect(_ annotationView: AnnotationView) { - guard let options = self.mapView.viewAnnotations.options(for: annotationView) else { return } - - let updateOptions = ViewAnnotationOptions( - width: (options.width ?? 0.0) - Constants.SELECTED_ADD_COEF_PX, - height: (options.height ?? 0.0) - Constants.SELECTED_ADD_COEF_PX, - selected: false - ) - try? self.mapView.viewAnnotations.update(annotationView, options: updateOptions) - } - - // Handle the actions for the button clicks inside the `SampleView` instance - func annotationViewDidPressClose(_ annotationView: AnnotationView) { - mapView.viewAnnotations.remove(annotationView) - } -} diff --git a/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationWithPointAnnotationExample.swift b/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationWithPointAnnotationExample.swift deleted file mode 100644 index 778c574e8538..000000000000 --- a/Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationWithPointAnnotationExample.swift +++ /dev/null @@ -1,117 +0,0 @@ -import UIKit -import MapboxMaps -import CoreLocation - -@objc(ViewAnnotationWithPointAnnotationExample) -final class ViewAnnotationWithPointAnnotationExample: UIViewController, ExampleProtocol { - private enum Constants { - static let blueIconId = "blue" - static let selectedAddCoefPX: CGFloat = 50 - static let markerId = UUID().uuidString - } - - private var mapView: MapView! - private var pointAnnotationManager: PointAnnotationManager! - - private let image = UIImage(named: "blue_marker_view")! - private lazy var markerHeight: CGFloat = image.size.height - - override func viewDidLoad() { - super.viewDidLoad() - - let centerCoordinate = CLLocationCoordinate2D(latitude: 39.7128, longitude: -75.0060) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 7)) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - pointAnnotationManager = mapView.annotations.makePointAnnotationManager() - - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in - guard let self = self else { return } - - try? self.mapView.mapboxMap.style.addImage(self.image, id: Constants.blueIconId) - self.addPointAndViewAnnotation(at: self.mapView.mapboxMap.coordinate(for: self.mapView.center)) - - // The below line is used for internal testing purposes only. - self.finish() - } - - mapView.mapboxMap.style.uri = .streets - - mapView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onMapTapped(_:)))) - } - - // MARK: - Actions - - @objc private func onMapTapped(_ sender: UITapGestureRecognizer) { - guard mapView.viewAnnotations.view(forFeatureId: Constants.markerId) == nil, - let pointAnnotationCoordinates = pointAnnotationManager.annotations.first?.point.coordinates else { - return - } - - addViewAnnotation(at: pointAnnotationCoordinates) - } - - // MARK: - Annotation management - - private func addPointAndViewAnnotation(at coordinate: CLLocationCoordinate2D) { - addPointAnnotation(at: coordinate) - addViewAnnotation(at: coordinate) - } - - private func addPointAnnotation(at coordinate: CLLocationCoordinate2D) { - var pointAnnotation = PointAnnotation(id: Constants.markerId, coordinate: coordinate) - pointAnnotation.iconImage = Constants.blueIconId - pointAnnotation.iconAnchor = .bottom - - pointAnnotationManager.annotations.append(pointAnnotation) - } - - // Add a view annotation at a specified location and optionally bind it to an ID of a marker - private func addViewAnnotation(at coordinate: CLLocationCoordinate2D) { - let options = ViewAnnotationOptions( - geometry: Point(coordinate), - width: 128, - height: 64, - associatedFeatureId: Constants.markerId, - allowOverlap: false, - anchor: .bottom, - offsetY: markerHeight - ) - let annotationView = AnnotationView(frame: CGRect(x: 0, y: 0, width: 128, height: 64)) - annotationView.title = String(format: "lat=%.2f\nlon=%.2f", coordinate.latitude, coordinate.longitude) - annotationView.delegate = self - try? mapView.viewAnnotations.add(annotationView, options: options) - } -} - -extension ViewAnnotationWithPointAnnotationExample: AnnotationViewDelegate { - func annotationViewDidSelect(_ annotationView: AnnotationView) { - guard let options = self.mapView.viewAnnotations.options(for: annotationView) else { return } - - let updateOptions = ViewAnnotationOptions( - width: (options.width ?? 0.0) + Constants.selectedAddCoefPX, - height: (options.height ?? 0.0) + Constants.selectedAddCoefPX, - selected: true - ) - try? self.mapView.viewAnnotations.update(annotationView, options: updateOptions) - } - - func annotationViewDidUnselect(_ annotationView: AnnotationView) { - guard let options = self.mapView.viewAnnotations.options(for: annotationView) else { return } - - let updateOptions = ViewAnnotationOptions( - width: (options.width ?? 0.0) - Constants.selectedAddCoefPX, - height: (options.height ?? 0.0) - Constants.selectedAddCoefPX, - selected: false - ) - try? self.mapView.viewAnnotations.update(annotationView, options: updateOptions) - } - - // Handle the actions for the button clicks inside the `SampleView` instance - func annotationViewDidPressClose(_ annotationView: AnnotationView) { - mapView.viewAnnotations.remove(annotationView) - } -} diff --git a/Apps/Examples/Examples/All Examples/BasicMapExample.swift b/Apps/Examples/Examples/All Examples/BasicMapExample.swift deleted file mode 100644 index d427f0825f6f..000000000000 --- a/Apps/Examples/Examples/All Examples/BasicMapExample.swift +++ /dev/null @@ -1,24 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(BasicMapExample) -final class BasicMapExample: UIViewController, ExampleProtocol { - - private var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.ornaments.options.scaleBar.visibility = .visible - - view.addSubview(mapView) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } -} diff --git a/Apps/Examples/Examples/All Examples/BuildingExtrusionsExample.swift b/Apps/Examples/Examples/All Examples/BuildingExtrusionsExample.swift deleted file mode 100644 index 1726e2906126..000000000000 --- a/Apps/Examples/Examples/All Examples/BuildingExtrusionsExample.swift +++ /dev/null @@ -1,161 +0,0 @@ -import Foundation -import MapboxMaps - -@objc(BuildingExtrusionsExample) -public class BuildingExtrusionsExample: UIViewController, ExampleProtocol { - - private lazy var lightPositionButton: UIButton = { - let button = UIButton(type: .system) - button.translatesAutoresizingMaskIntoConstraints = false - button.backgroundColor = .systemBlue - button.tintColor = .white - button.layer.cornerRadius = 4 - button.clipsToBounds = true - button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4) - if #available(iOS 13.0, *) { - button.setImage(UIImage(systemName: "flashlight.on.fill"), for: .normal) - } else { - button.setTitle("Position", for: .normal) - } - button.addTarget(self, action: #selector(lightPositionButtonTapped(_:)), for: .touchUpInside) - return button - }() - - private lazy var lightColorButton: UIButton = { - let button = UIButton(type: .system) - button.translatesAutoresizingMaskIntoConstraints = false - button.backgroundColor = .systemBlue - button.tintColor = .white - button.layer.cornerRadius = 4 - button.clipsToBounds = true - button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) - if #available(iOS 13.0, *) { - button.setImage(UIImage(systemName: "paintbrush.fill"), for: .normal) - } else { - button.setTitle("Color", for: .normal) - } - button.addTarget(self, action: #selector(lightColorButtonTapped(_:)), for: .touchUpInside) - return button - }() - - internal var mapView: MapView! - - private var light = Light() - - override public func viewDidLoad() { - super.viewDidLoad() - - let options = MapInitOptions(styleURI: .light) - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - self.setupExample() - } - - view.addSubview(lightPositionButton) - view.addSubview(lightColorButton) - - NSLayoutConstraint.activate([ - view.trailingAnchor.constraint(equalToSystemSpacingAfter: lightPositionButton.trailingAnchor, multiplier: 1), - view.bottomAnchor.constraint(equalTo: lightPositionButton.bottomAnchor, constant: 100), - view.trailingAnchor.constraint(equalToSystemSpacingAfter: lightColorButton.trailingAnchor, multiplier: 1), - lightPositionButton.topAnchor.constraint(equalToSystemSpacingBelow: lightColorButton.bottomAnchor, multiplier: 1), - lightColorButton.widthAnchor.constraint(equalTo: lightPositionButton.widthAnchor), - lightColorButton.heightAnchor.constraint(equalTo: lightPositionButton.heightAnchor) - ]) - } - - internal func setupExample() { - addBuildingExtrusions() - - let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 40.7135, longitude: -74.0066), - zoom: 15.5, - bearing: -17.6, - pitch: 45) - mapView.mapboxMap.setCamera(to: cameraOptions) - - // The below lines are used for internal testing purposes only. - DispatchQueue.main.asyncAfter(deadline: .now()+5.0) { - self.finish() - } - } - - // See https://docs.mapbox.com/mapbox-gl-js/example/3d-buildings/ for equivalent gl-js example - internal func addBuildingExtrusions() { - var layer = FillExtrusionLayer(id: "3d-buildings") - - layer.source = "composite" - layer.minZoom = 15 - layer.sourceLayer = "building" - layer.fillExtrusionColor = .constant(StyleColor(.lightGray)) - layer.fillExtrusionOpacity = .constant(0.6) - - layer.filter = Exp(.eq) { - Exp(.get) { - "extrude" - } - "true" - } - - layer.fillExtrusionHeight = .expression( - Exp(.interpolate) { - Exp(.linear) - Exp(.zoom) - 15 - 0 - 15.05 - Exp(.get) { - "height" - } - } - ) - - layer.fillExtrusionBase = .expression( - Exp(.interpolate) { - Exp(.linear) - Exp(.zoom) - 15 - 0 - 15.05 - Exp(.get) { "min_height"} - } - ) - - layer.fillExtrusionAmbientOcclusionIntensity = .constant(0.3) - - layer.fillExtrusionAmbientOcclusionRadius = .constant(3.0) - - try! mapView.mapboxMap.style.addLayer(layer) - } - - // MARK: - Actions - - @objc private func lightColorButtonTapped(_ sender: UIButton) { - if light.color == StyleColor(.red) { - light.color = StyleColor(.blue) - sender.tintColor = .blue - } else { - light.color = StyleColor(.red) - sender.tintColor = .red - } - - try? mapView.mapboxMap.style.setLight(light) - } - - @objc private func lightPositionButtonTapped(_ sender: UIButton) { - let firstPosition = [1.5, 90, 80] - let secondPosition = [1.15, 210, 30] - - if light.position == firstPosition { - light.position = secondPosition - sender.imageView?.transform = .identity - } else { - light.position = firstPosition - sender.imageView?.transform = CGAffineTransform(rotationAngle: 2.0 * .pi / 3.0) - } - - try? mapView.mapboxMap.style.setLight(light) - } -} diff --git a/Apps/Examples/Examples/All Examples/CameraAnimationExample.swift b/Apps/Examples/Examples/All Examples/CameraAnimationExample.swift deleted file mode 100644 index 70c2c10501a4..000000000000 --- a/Apps/Examples/Examples/All Examples/CameraAnimationExample.swift +++ /dev/null @@ -1,35 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(CameraAnimationExample) - -public class CameraAnimationExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - - // Center the map camera over New York City. - let centerCoordinate = CLLocationCoordinate2D( - latitude: 40.7128, longitude: -74.0060) - - let newCamera = CameraOptions(center: centerCoordinate, - zoom: 7.0, - bearing: 180.0, - pitch: 15.0) - - self.mapView.camera.ease(to: newCamera, duration: 5.0) { [weak self] (_) in - // The below line is used for internal testing purposes only. - self?.finish() - } - } - } -} diff --git a/Apps/Examples/Examples/All Examples/CameraAnimatorsExample.swift b/Apps/Examples/Examples/All Examples/CameraAnimatorsExample.swift deleted file mode 100644 index e88da2122653..000000000000 --- a/Apps/Examples/Examples/All Examples/CameraAnimatorsExample.swift +++ /dev/null @@ -1,68 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(CameraAnimatorsExample) -class CameraAnimatorsExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - // Coordinate in New York City - let newYork = CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060) - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - // Center the map over New York City. - self.mapView.mapboxMap.setCamera(to: CameraOptions(center: self.newYork)) - } - - // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - print("Animating zoom from zoom lvl 3 -> zoom lvl 14") - self.startCameraAnimations() - self.finish() - } - } - - // Start a chain of camera animations - func startCameraAnimations() { - // Declare an animator that changes the map's - let bearingAnimator = mapView.camera.makeAnimator(duration: 4, curve: .easeInOut) { (transition) in - transition.bearing.toValue = -45 - } - - bearingAnimator.addCompletion { (_) in - print("All animations complete!") - } - - // Declare an animator that changes the map's pitch. - let pitchAnimator = mapView.camera.makeAnimator(duration: 2, curve: .easeInOut) { (transition) in - transition.pitch.toValue = 55 - } - - // Begin the bearing animation once the pitch animation has finished. - pitchAnimator.addCompletion { _ in - print("Animating camera bearing from 0 degrees -> 45 degrees") - bearingAnimator.startAnimation() - } - - // Declare an animator that changes the map's zoom level. - let zoomAnimator = self.mapView.camera.makeAnimator(duration: 4, curve: .easeInOut) { (transition) in - transition.zoom.toValue = 14 - } - - // Begin the pitch animation once the zoom animation has finished. - zoomAnimator.addCompletion { _ in - print("Animating camera pitch from 0 degrees -> 55 degrees") - pitchAnimator.startAnimation() - } - - // Begin the zoom animation. - zoomAnimator.startAnimation(afterDelay: 1) - } -} diff --git a/Apps/Examples/Examples/All Examples/Custom2DPuckExample.swift b/Apps/Examples/Examples/All Examples/Custom2DPuckExample.swift deleted file mode 100644 index 6a163ac81c31..000000000000 --- a/Apps/Examples/Examples/All Examples/Custom2DPuckExample.swift +++ /dev/null @@ -1,86 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(Custom2DPuckExample) -public class Custom2DPuckExample: UIViewController, ExampleProtocol { - - internal let toggleAccuracyRadiusButton: UIButton = UIButton(frame: .zero) - internal var mapView: MapView! - internal var showsAccuracyRing: Bool = false { - didSet { - syncPuckAndButton() - } - } - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Setup and create button for toggling accuracy ring - setupToggleShowAccuracyButton() - - // Granularly configure the location puck with a `Puck2DConfiguration` - let configuration = Puck2DConfiguration(topImage: UIImage(named: "star")) - mapView.location.options.puckType = .puck2D(configuration) - mapView.location.options.puckBearingSource = .course - - // Center map over the user's current location - mapView.mapboxMap.onNext(event: .mapLoaded, handler: { [weak self] _ in - guard let self = self else { return } - - if let currentLocation = self.mapView.location.latestLocation { - let cameraOptions = CameraOptions(center: currentLocation.coordinate, zoom: 20.0) - self.mapView.camera.ease(to: cameraOptions, duration: 2.0) - } - }) - - // Accuracy ring is only shown when zoom is greater than or equal to 18 - mapView.mapboxMap.onEvery(event: .cameraChanged, handler: { [weak self] _ in - guard let self = self else { return } - self.toggleAccuracyRadiusButton.isHidden = self.mapView.cameraState.zoom < 18.0 - }) - } - - override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } - - @objc func showHideAccuracyRadius() { - showsAccuracyRing.toggle() - } - - func syncPuckAndButton() { - // Update puck config - var configuration = Puck2DConfiguration(topImage: UIImage(named: "star")) - configuration.showsAccuracyRing = showsAccuracyRing - configuration.accuracyRingColor = UIColor.skyBlue - configuration.accuracyRingBorderColor = UIColor.lightGray - - mapView.location.options.puckType = .puck2D(configuration) - - // Update button title - let title: String = showsAccuracyRing ? "Disable Accuracy Radius" : "Enable Accuracy Radius" - toggleAccuracyRadiusButton.setTitle(title, for: .normal) - } - - private func setupToggleShowAccuracyButton() { - // Styling - toggleAccuracyRadiusButton.backgroundColor = .systemBlue - toggleAccuracyRadiusButton.addTarget(self, action: #selector(showHideAccuracyRadius), for: .touchUpInside) - toggleAccuracyRadiusButton.setTitleColor(.white, for: .normal) - toggleAccuracyRadiusButton.isHidden = true - syncPuckAndButton() - toggleAccuracyRadiusButton.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(toggleAccuracyRadiusButton) - - // Constraints - toggleAccuracyRadiusButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0).isActive = true - toggleAccuracyRadiusButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0).isActive = true - toggleAccuracyRadiusButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -100.0).isActive = true - } -} diff --git a/Apps/Examples/Examples/All Examples/Custom3DPuckExample.swift b/Apps/Examples/Examples/All Examples/Custom3DPuckExample.swift deleted file mode 100644 index a7a3ff045d0f..000000000000 --- a/Apps/Examples/Examples/All Examples/Custom3DPuckExample.swift +++ /dev/null @@ -1,132 +0,0 @@ -import UIKit -@_spi(Experimental) import MapboxMaps - -@objc(Custom3DPuckExample) -final class Custom3DPuckExample: UIViewController, ExampleProtocol, LocationConsumer { - - private var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - self.setupExample() - } - } - - private func setupExample() { - addBuildingExtrusions() - - // set light configuration for shadow visibility - var light = Light() - light.anchor = .map - light.color = StyleColor(.yellow) - light.position = [10.0, 40.0, 50.0] - light.castShadows = .constant(true) - light.shadowIntensity = .constant(0.7) - try! mapView.mapboxMap.style.setLight(light) - - let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 40.7135, longitude: -74.0066), - zoom: 15.5, - bearing: -17.6, - pitch: 45) - mapView.mapboxMap.setCamera(to: cameraOptions) - - // The below lines are used for internal testing purposes only. - DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { - self.finish() - } - - // add the 3D model to the map to represent the user's location - // Fetch the `gltf` asset - let uri = Bundle.main.url(forResource: "sportcar", - withExtension: "glb") - - // Instantiate the model - let myModel = Model(uri: uri, orientation: [0, 0, 180]) - - // Setting an expression to scale the model based on camera zoom - let scalingExpression = Exp(.interpolate) { - Exp(.linear) - Exp(.zoom) - 0 - Exp(.literal) { - [256000.0, 256000.0, 256000.0] - } - 4 - Exp(.literal) { - [40000.0, 40000.0, 40000.0] - } - 8 - Exp(.literal) { - [2000.0, 2000.0, 2000.0] - } - 12 - Exp(.literal) { - [100.0, 100.0, 100.0] - } - 16 - Exp(.literal) { - [7.0, 7.0, 7.0] - } - 20 - Exp(.literal) { - [1.0, 1.0, 1.0] - } - } - - let configuration = Puck3DConfiguration(model: myModel, modelScale: .expression(scalingExpression), modelRotation: .constant([0.0, 0.0, 180.0]), modelCastShadows: .constant(false)) - mapView.location.options.puckType = .puck3D(configuration) - mapView.location.options.puckBearingSource = .course - - mapView.location.addLocationConsumer(newConsumer: self) - } - - // See https://docs.mapbox.com/mapbox-gl-js/example/3d-buildings/ for equivalent gl-js example - private func addBuildingExtrusions() { - var layer = FillExtrusionLayer(id: "3d-buildings") - - layer.source = "composite" - layer.minZoom = 15 - layer.sourceLayer = "building" - layer.fillExtrusionColor = .constant(StyleColor(.lightGray)) - layer.fillExtrusionOpacity = .constant(0.6) - - layer.filter = Exp(.eq) { - Exp(.get) { - "extrude" - } - "true" - } - - layer.fillExtrusionHeight = .expression( - Exp(.get) { "height" } - ) - - layer.fillExtrusionBase = .expression( - Exp(.get) { "min_height"} - ) - - layer.fillExtrusionAmbientOcclusionIntensity = .constant(0.3) - - layer.fillExtrusionAmbientOcclusionRadius = .constant(3.0) - - try! mapView.mapboxMap.style.addLayer(layer) - } - - internal func locationUpdate(newLocation: Location) { - mapView.camera.ease( - to: CameraOptions( - center: newLocation.coordinate, - zoom: 15, - bearing: 0, - pitch: 55), - duration: 1, - curve: .linear, - completion: nil) - } -} diff --git a/Apps/Examples/Examples/All Examples/CustomLayerExample.swift b/Apps/Examples/Examples/All Examples/CustomLayerExample.swift deleted file mode 100644 index eefc35d596f6..000000000000 --- a/Apps/Examples/Examples/All Examples/CustomLayerExample.swift +++ /dev/null @@ -1,158 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(CustomLayerExample) -final class CustomLayerExample: UIViewController, ExampleProtocol { - - var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - self.addCustomLayer() - } - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } - - func addCustomLayer() { - // Position the custom layer above the water layer and below all other layers. - try! mapView.mapboxMap.style.addCustomLayer( - withId: "Custom", - layerHost: CustomLayerExampleCustomLayerHost(), - layerPosition: .above("water")) - } -} - -final class CustomLayerExampleCustomLayerHost: NSObject, CustomLayerHost { - var depthStencilState: MTLDepthStencilState! - var pipelineState: MTLRenderPipelineState! - - func renderingWillStart(_ metalDevice: MTLDevice, colorPixelFormat: UInt, depthStencilPixelFormat: UInt) { - - let compileOptions = MTLCompileOptions() - - var library: MTLLibrary - - do { - library = try metalDevice.makeLibrary(source: metalShaderProgram, - options: compileOptions) - } catch { - fatalError("Failed to create shader") - } - - guard let vertexFunction = library.makeFunction(name: "vertexShader") else { - fatalError("Could not find vertex function") - } - - guard let fragmentFunction = library.makeFunction(name: "fragmentShader") else { - fatalError("Could not find fragment function") - } - - // Set up vertex descriptor - let vertexDescriptor = MTLVertexDescriptor() - vertexDescriptor.attributes[0].offset = 0 - vertexDescriptor.attributes[0].format = .float2 - vertexDescriptor.attributes[0].bufferIndex = 0 - vertexDescriptor.layouts[0].stepRate = 1 - vertexDescriptor.layouts[0].stepFunction = .perVertex - vertexDescriptor.layouts[0].stride = MemoryLayout.size - - // Set up pipeline descriptor - let pipelineStateDescriptor = MTLRenderPipelineDescriptor() - pipelineStateDescriptor.label = "Test Layer" - pipelineStateDescriptor.vertexFunction = vertexFunction - pipelineStateDescriptor.vertexDescriptor = vertexDescriptor - pipelineStateDescriptor.fragmentFunction = fragmentFunction - - // Set up color attachment - let colorAttachment = pipelineStateDescriptor.colorAttachments[0] - colorAttachment?.pixelFormat = MTLPixelFormat(rawValue: colorPixelFormat)! - colorAttachment?.isBlendingEnabled = true - colorAttachment?.rgbBlendOperation = colorAttachment?.alphaBlendOperation ?? .add - colorAttachment?.sourceAlphaBlendFactor = colorAttachment?.sourceAlphaBlendFactor ?? .one - colorAttachment?.destinationRGBBlendFactor = colorAttachment?.destinationRGBBlendFactor ?? .oneMinusSourceAlpha - - // Configure render pipeline descriptor - pipelineStateDescriptor.depthAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat)! - pipelineStateDescriptor.stencilAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat)! - - // Configure the depth stencil - let depthStencilDescriptor = MTLDepthStencilDescriptor() - depthStencilDescriptor.isDepthWriteEnabled = false - depthStencilDescriptor.depthCompareFunction = .always - - depthStencilState = metalDevice.makeDepthStencilState(descriptor: depthStencilDescriptor) - - do { - pipelineState = try metalDevice.makeRenderPipelineState(descriptor: pipelineStateDescriptor) - } catch { - fatalError("Could not make render pipeline state: \(error.localizedDescription)") - } - } - - func render(_ parameters: CustomLayerRenderParameters, mtlCommandBuffer: MTLCommandBuffer, mtlRenderPassDescriptor: MTLRenderPassDescriptor) { - - let vertices = [ - simd_float2(0, 0.5), - simd_float2(0.5, -0.5), - simd_float2(-0.5, -0.5) - ] - - guard let renderCommandEncoder = mtlCommandBuffer.makeRenderCommandEncoder(descriptor: mtlRenderPassDescriptor) else { - fatalError("Could not create render command encoder from render pass descriptor.") - } - - renderCommandEncoder.label = "Custom Layer" - renderCommandEncoder.pushDebugGroup("Custom Layer") - renderCommandEncoder.setDepthStencilState(depthStencilState) - renderCommandEncoder.setRenderPipelineState(pipelineState) - renderCommandEncoder.setVertexBytes(vertices, length: MemoryLayout.size * vertices.count, index: 0) - renderCommandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3) - renderCommandEncoder.popDebugGroup() - renderCommandEncoder.endEncoding() - } - - func renderingWillEnd() { - // Unimplemented - } - - // The Metal shader program, written in the - // [Metal Shader Language](https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf) format. - var metalShaderProgram: String { - return """ - #include - #include - using namespace metal; - struct vertexShader_in - { - float2 a_pos [[attribute(0)]]; - }; - struct vertexShader_out - { - float4 gl_Position [[position]]; - }; - vertex vertexShader_out vertexShader(vertexShader_in in [[stage_in]]) - { - return { float4(in.a_pos, 1.0, 1.0) }; - } - struct fragmentShader_out - { - float4 mbgl_FragColor [[color(0)]]; - }; - fragment fragmentShader_out fragmentShader() - { - return { float4(0, 0.5, 0, 0.5) }; - } - """ - } -} diff --git a/Apps/Examples/Examples/All Examples/CustomLocationProviderExample.swift b/Apps/Examples/Examples/All Examples/CustomLocationProviderExample.swift deleted file mode 100644 index b099117d3bde..000000000000 --- a/Apps/Examples/Examples/All Examples/CustomLocationProviderExample.swift +++ /dev/null @@ -1,26 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(CustomLocationProviderExample) -final class CustomLocationProviderExample: UIViewController, ExampleProtocol { - - var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - let centerCoordinate = CLLocationCoordinate2D(latitude: 40.7131854, longitude: -74.0165265) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 10)) - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - - view.addSubview(mapView) - - // initialize the custom location provoder with the location of your choice - let customLocationProvider = SimulatedLocationProvider( - currentLocation: CLLocation(latitude: 40.7131854, longitude: -74.0165265)) - mapView.location.overrideLocationProvider(with: customLocationProvider) - mapView.location.options.puckType = .puck2D() - // The following line is just for testing purposes. - finish() - } -} diff --git a/Apps/Examples/Examples/All Examples/CustomStyleURLExample.swift b/Apps/Examples/Examples/All Examples/CustomStyleURLExample.swift deleted file mode 100644 index dbd93c045d8e..000000000000 --- a/Apps/Examples/Examples/All Examples/CustomStyleURLExample.swift +++ /dev/null @@ -1,27 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(CustomStyleURLExample) -internal class CustomStyleURLExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - // Create a URL for a custom style created in Mapbox Studio. - guard let customStyleURI = StyleURI(rawValue: "mapbox://styles/examples/cke97f49z5rlg19l310b7uu7j") else { - fatalError("Style URI is invalid") - } - - let options = MapInitOptions(styleURI: customStyleURI) - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .styleLoaded) { [weak self] _ in - // The below line is used for internal testing purposes only. - self?.finish() - } - } -} diff --git a/Apps/Examples/Examples/All Examples/DebugMapExample.swift b/Apps/Examples/Examples/All Examples/DebugMapExample.swift deleted file mode 100644 index 9d65958ea7d5..000000000000 --- a/Apps/Examples/Examples/All Examples/DebugMapExample.swift +++ /dev/null @@ -1,190 +0,0 @@ -import UIKit -import MapboxMaps - -private protocol DebugOptionSettingsDelegate: AnyObject { - func debugOptionSettingsDidChange(_ controller: SettingsViewController) -} - -private struct MapDebugOptionSetting { - let debugOption: MapDebugOptions - let displayTitle: String -} - -final class DebugMapExample: UIViewController, ExampleProtocol, DebugOptionSettingsDelegate { - private var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - view.addSubview(mapView) - view.backgroundColor = .skyBlue - mapView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - ]) - - let debugOptionsBarItem = UIBarButtonItem( - barButtonSystemItem: .edit, - target: self, - action: #selector(openDebugOptionsMenu(_:))) - navigationItem.rightBarButtonItem = debugOptionsBarItem - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } - - @objc private func openDebugOptionsMenu(_ sender: UIBarButtonItem) { - let settingsViewController = SettingsViewController(debugOptions: mapView.mapboxMap.debugOptions) - settingsViewController.delegate = self - - let navigationController = UINavigationController(rootViewController: settingsViewController) - navigationController.modalPresentationStyle = .popover - navigationController.popoverPresentationController?.barButtonItem = sender - - present(navigationController, animated: true, completion: nil) - } - - fileprivate func debugOptionSettingsDidChange(_ controller: SettingsViewController) { - controller.dismiss(animated: true, completion: nil) - mapView.mapboxMap.debugOptions = Array(controller.enabledDebugOptions) - } -} - -private final class SettingsViewController: UIViewController, UITableViewDataSource { - - weak var delegate: DebugOptionSettingsDelegate? - private var listView: UITableView! - - private(set) var enabledDebugOptions: Set - private let allSettings: [MapDebugOptionSetting] = [ - MapDebugOptionSetting(debugOption: .collision, displayTitle: "Debug collision"), - MapDebugOptionSetting(debugOption: .depthBuffer, displayTitle: "Show depth buffer"), - MapDebugOptionSetting(debugOption: .overdraw, displayTitle: "Debug overdraw"), - MapDebugOptionSetting(debugOption: .parseStatus, displayTitle: "Show tile coordinate"), - MapDebugOptionSetting(debugOption: .renderCache, displayTitle: "Render Cache"), - MapDebugOptionSetting(debugOption: .stencilClip, displayTitle: "Show stencil buffer"), - MapDebugOptionSetting(debugOption: .tileBorders, displayTitle: "Debug tile clipping"), - MapDebugOptionSetting(debugOption: .timestamps, displayTitle: "Show tile loaded time"), - ] - - init(debugOptions: [MapDebugOptions]) { - enabledDebugOptions = Set(debugOptions) - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - title = "Debug options" - listView = UITableView() - listView.dataSource = self - listView.register(DebugOptionCell.self, forCellReuseIdentifier: String(describing: DebugOptionCell.self)) - - view.addSubview(listView) - - listView.translatesAutoresizingMaskIntoConstraints = false - NSLayoutConstraint.activate([ - listView.topAnchor.constraint(equalTo: view.topAnchor), - listView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - listView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - listView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - ]) - - navigationItem.largeTitleDisplayMode = .never - navigationItem.rightBarButtonItem = UIBarButtonItem( - barButtonSystemItem: .save, - target: self, - action: #selector(saveSettings(_:))) - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - preferredContentSize = listView.contentSize - } - - @objc private func saveSettings(_ sender: UIBarButtonItem) { - delegate?.debugOptionSettingsDidChange(self) - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - allSettings.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cellID = String(describing: DebugOptionCell.self) - // swiftlint:disable:next force_cast - let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! DebugOptionCell - - let setting = allSettings[indexPath.row] - cell.configure(with: setting, isOptionEnabled: enabledDebugOptions.contains(setting.debugOption)) - cell.onToggled { [unowned self] isEnabled in - if isEnabled { - self.enabledDebugOptions.insert(setting.debugOption) - } else { - self.enabledDebugOptions.remove(setting.debugOption) - } - } - - return cell - } -} - -// MARK: Cell - -private class DebugOptionCell: UITableViewCell { - - private let titleLabel = UILabel() - private let toggle = UISwitch() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - toggle.addTarget(self, action: #selector(didToggle(_:)), for: .valueChanged) - - contentView.addSubview(titleLabel) - contentView.addSubview(toggle) - - titleLabel.translatesAutoresizingMaskIntoConstraints = false - toggle.translatesAutoresizingMaskIntoConstraints = false - - let constraints: [NSLayoutConstraint] = [ - titleLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 16), - titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), - titleLabel.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 8), - toggle.leftAnchor.constraint(greaterThanOrEqualTo: titleLabel.rightAnchor, constant: 16), - toggle.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -16), - toggle.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor), - toggle.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 8), - ] - NSLayoutConstraint.activate(constraints) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func configure(with setting: MapDebugOptionSetting, isOptionEnabled: Bool) { - titleLabel.text = setting.displayTitle - toggle.isOn = isOptionEnabled - } - - private var onToggleHandler: ((Bool) -> Void)? - - func onToggled(_ handler: @escaping (Bool) -> Void) { - onToggleHandler = handler - } - - @objc private func didToggle(_ sender: UISwitch) { - onToggleHandler?(sender.isOn) - } -} diff --git a/Apps/Examples/Examples/All Examples/ExternalVectorSourceExample.swift b/Apps/Examples/Examples/All Examples/ExternalVectorSourceExample.swift deleted file mode 100644 index 2a543eba0791..000000000000 --- a/Apps/Examples/Examples/All Examples/ExternalVectorSourceExample.swift +++ /dev/null @@ -1,62 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(ExternalVectorSourceExample) -public class ExternalVectorSourceExample: UIViewController, ExampleProtocol { - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - let centerCoordinate = CLLocationCoordinate2D(latitude: 41.878781, longitude: -87.622088) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 12.0), - styleURI: .light) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allow the view controller to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.drawLineLayer() - // The following line is just for testing purposes. - self.finish() - } - } - - public func drawLineLayer() { - - let sourceIdentifier = "mapillary" - - var vectorSource = VectorSource() - - // For sources using the {z}/{x}/{y} URL scheme, use the `tiles` - // property on `VectorSource` to set the URL. - vectorSource.tiles = ["https://tiles.mapillary.com/maps/vtp/mly1_public/2/{z}/{x}/{y}?access_token=MLY%7C4142433049200173%7C72206abe5035850d6743b23a49c41333"] - vectorSource.minzoom = 6 - vectorSource.maxzoom = 14 - - var lineLayer = LineLayer(id: "line-layer") - lineLayer.source = sourceIdentifier - lineLayer.sourceLayer = "sequence" - let lineColor = StyleColor(UIColor(red: 0.21, green: 0.69, blue: 0.43, alpha: 1.00)) - lineLayer.lineColor = .constant(lineColor) - lineLayer.lineOpacity = .constant(0.6) - lineLayer.lineWidth = .constant(2.0) - lineLayer.lineCap = .constant(.round) - - do { - try mapView.mapboxMap.style.addSource(vectorSource, id: sourceIdentifier) - } catch { - showAlert(with: error.localizedDescription) - } - - // Define the layer's positioning within the layer stack so - // that it doesn't obscure other important labels. - do { - try mapView.mapboxMap.style.addLayer(lineLayer, layerPosition: .below("waterway-label")) - } catch let layerError { - showAlert(with: layerError.localizedDescription) - } - } -} diff --git a/Apps/Examples/Examples/All Examples/FeatureStateExample.swift b/Apps/Examples/Examples/All Examples/FeatureStateExample.swift deleted file mode 100644 index 8a08fb05e0c5..000000000000 --- a/Apps/Examples/Examples/All Examples/FeatureStateExample.swift +++ /dev/null @@ -1,321 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(FeatureStateExample) -public class FeatureStateExample: UIViewController, ExampleProtocol { - - private var mapView: MapView! - fileprivate var descriptionView: EarthquakeDescriptionView! - static let earthquakeSourceId: String = "earthquakes" - static let earthquakeLayerId: String = "earthquake-viz" - private var previouslyTappedEarthquakeId: String = "" - - private lazy var dateFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.timeStyle = DateFormatter.Style.medium //Set time style - dateFormatter.dateStyle = DateFormatter.Style.medium //Set date style - dateFormatter.timeZone = .current - - return dateFormatter - }() - - override public func viewDidLoad() { - super.viewDidLoad() - - // Center the map over the United States. - let centerCoordinate = CLLocationCoordinate2D(latitude: 39.368279, - longitude: -97.646484) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 2.4)) - - // Set up map view - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.ornaments.options.scaleBar.visibility = .hidden - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Set up description view - descriptionView = EarthquakeDescriptionView(frame: .zero) - view.addSubview(descriptionView) - descriptionView.translatesAutoresizingMaskIntoConstraints = false - descriptionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 2.0).isActive = true - descriptionView.heightAnchor.constraint(equalToConstant: 100).isActive = true - descriptionView.widthAnchor.constraint(equalToConstant: 200).isActive = true - descriptionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 2.0).isActive = true - - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in - guard let self = self else { return } - - self.setupSourceAndLayer() - - // Set up tap gesture - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.findFeatures)) - self.mapView.addGestureRecognizer(tapGesture) - - // The below lines are used for internal testing purposes only. - DispatchQueue.main.asyncAfter(deadline: .now()+3.0) { [weak self] in - self?.finish() - } - } - } - - public func setupSourceAndLayer() { - - // Create a new GeoJSON data source which gets its data from an external URL. - guard let sevenDaysAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date()) else { - preconditionFailure("Could not calculate date for seven days ago.") - } - - // Format the date to ISO8601 as required by the earthquakes API - let iso8601DateFormatter = ISO8601DateFormatter() - iso8601DateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] - let startTime = iso8601DateFormatter.string(from: sevenDaysAgo) - - // Create the url required for the GeoJSONSource - guard let earthquakeURL = URL(string: "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&eventtype=earthquake&minmagnitude=1&starttime=" + startTime) else { - preconditionFailure("URL is not valid") - } - - var earthquakeSource = GeoJSONSource() - earthquakeSource.data = .url(earthquakeURL) - earthquakeSource.generateId = true - - do { - try mapView.mapboxMap.style.addSource(earthquakeSource, id: Self.earthquakeSourceId) - } catch { - print("Ran into an error adding a source: \(error)") - } - - // Add earthquake-viz layer - var earthquakeVizLayer = CircleLayer(id: Self.earthquakeLayerId) - earthquakeVizLayer.source = Self.earthquakeSourceId - - // The feature-state dependent circle-radius expression will render - // the radius size according to its magnitude when - // a feature's selected state is set to true - earthquakeVizLayer.circleRadius = .expression( - Exp(.switchCase) { - Exp(.boolean) { - Exp(.featureState) { "selected" } - false - } - Exp(.interpolate) { - Exp(.linear) - Exp(.get) { "mag" } - 1 - 8 - 1.5 - 10 - 2 - 12 - 2.5 - 14 - 3 - 16 - 3.5 - 18 - 4.5 - 20 - 6.5 - 22 - 8.5 - 24 - 10.5 - 26 - } - 5 - } - ) - earthquakeVizLayer.circleRadiusTransition = StyleTransition(duration: 0.5, delay: 0) - earthquakeVizLayer.circleStrokeColor = .constant(StyleColor(.black)) - earthquakeVizLayer.circleStrokeWidth = .constant(1) - - // The feature-state dependent circle-color expression will render - // the color according to its magnitude when - // a feature's hover state is set to true - earthquakeVizLayer.circleColor = .expression( - Exp(.switchCase) { - Exp(.boolean) { - Exp(.featureState) { "selected" } - false - } - Exp(.interpolate) { - Exp(.linear) - Exp(.get) { "mag" } - 1 - "#fff7ec" - 1.5 - "#fee8c8" - 2 - "#fdd49e" - 2.5 - "#fdbb84" - 3 - "#fc8d59" - 3.5 - "#ef6548" - 4.5 - "#d7301f" - 6.5 - "#b30000" - 8.5 - "#7f0000" - 10.5 - "#000" - } - "#000" - } - ) - earthquakeVizLayer.circleColorTransition = StyleTransition(duration: 0.5, delay: 0) - - do { - try mapView.mapboxMap.style.addLayer(earthquakeVizLayer) - } catch { - print("Ran into an error adding a layer: \(error)") - } - } - - /** - Use the tap point received from the gesture recognizer to query - the map for rendered features at the given point within the layer specified. - */ - @objc public func findFeatures(_ sender: UITapGestureRecognizer) { - let tapPoint = sender.location(in: mapView) - - mapView.mapboxMap.queryRenderedFeatures( - at: tapPoint, - options: RenderedQueryOptions(layerIds: ["earthquake-viz"], filter: nil)) { [weak self] result in - - guard let self = self else { return } - - switch result { - case .success(let queriedfeatures): - - // Extract the earthquake feature from the queried features - if let earthquakeFeature = queriedfeatures.first?.feature, - case .number(let earthquakeIdDouble) = earthquakeFeature.identifier, - case .point(let point) = earthquakeFeature.geometry, - case let .number(magnitude) = earthquakeFeature.properties?["mag"], - case let .string(place) = earthquakeFeature.properties?["place"], - case let .number(timestamp) = earthquakeFeature.properties?["time"] { - - let earthquakeId = Int(earthquakeIdDouble).description - - // Set the description of the earthquake from the `properties` object - self.setDescription(magnitude: magnitude, timeStamp: timestamp, location: place) - - // Set the earthquake to be "selected" - self.setSelectedState(earthquakeId: earthquakeId) - - // Reset a previously tapped earthquake to be "unselected". - self.resetPreviouslySelectedStateIfNeeded(currentTappedEarthquakeId: earthquakeId) - - // Store the currently tapped earthquake so it can be reset when another earthquake is tapped. - self.previouslyTappedEarthquakeId = earthquakeId - - // Center the selected earthquake on the screen - self.mapView.camera.fly(to: CameraOptions(center: point.coordinates, zoom: 10)) - } - case .failure(let error): - self.showAlert(with: "An error occurred: \(error.localizedDescription)") - } - } - } - - func setDescription(magnitude: Double, timeStamp: Double, location: String) { - self.descriptionView.magnitudeLabel.text = "Magnitude: \(magnitude)" - self.descriptionView.locationLabel.text = "Location: \(location)" - self.descriptionView.dateLabel.text = "Date: " + self.dateFormatter.string( - from: Date(timeIntervalSince1970: timeStamp / 1000.0)) - } - - // Sets a particular earthquake to be selected - func setSelectedState(earthquakeId: String) { - self.mapView.mapboxMap.setFeatureState(sourceId: Self.earthquakeSourceId, - sourceLayerId: nil, - featureId: earthquakeId, - state: ["selected": true]) - } - - // Resets the previously selected earthquake to be "unselected" if needed. - func resetPreviouslySelectedStateIfNeeded(currentTappedEarthquakeId: String) { - - if self.previouslyTappedEarthquakeId != "" - && currentTappedEarthquakeId != self.previouslyTappedEarthquakeId { - // Reset a previously tapped earthquake to be "unselected". - self.mapView.mapboxMap.setFeatureState(sourceId: Self.earthquakeSourceId, - sourceLayerId: nil, - featureId: self.previouslyTappedEarthquakeId, - state: ["selected": false]) - } - } - - // Present an alert with a given title. - public func showAlert(with title: String) { - let alertController = UIAlertController(title: title, - message: nil, - preferredStyle: .alert) - - alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil)) - - present(alertController, animated: true, completion: nil) - } -} - -private class EarthquakeDescriptionView: UIView { - - var magnitudeLabel: UILabel! - var locationLabel: UILabel! - var dateLabel: UILabel! - var stackView: UIStackView! - - override init(frame: CGRect) { - super.init(frame: frame) - backgroundColor = .white - layer.opacity = 0.7 - layer.borderWidth = 0.5 - layer.borderColor = UIColor.black.cgColor - createSubviews() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func createSubviews() { - - func createLabel(placeholder: String) -> UILabel { - let label = UILabel(frame: .zero) - label.font = UIFont.systemFont(ofSize: 10) - label.numberOfLines = 0 - label.text = placeholder - label.textColor = .black - return label - } - - magnitudeLabel = createLabel(placeholder: "Magnitude: ---") - locationLabel = createLabel(placeholder: "Location: ---") - dateLabel = createLabel(placeholder: "Date: ---") - - let stackview = UIStackView() - stackview.axis = .vertical - stackview.spacing = 1 - stackview.alignment = .leading - stackview.distribution = .fillEqually - stackview.translatesAutoresizingMaskIntoConstraints = false - stackview.addArrangedSubview(magnitudeLabel) - magnitudeLabel.widthAnchor.constraint(equalTo: stackview.widthAnchor).isActive = true - - stackview.addArrangedSubview(locationLabel) - locationLabel.widthAnchor.constraint(equalTo: stackview.widthAnchor).isActive = true - - stackview.addArrangedSubview(dateLabel) - dateLabel.widthAnchor.constraint(equalTo: stackview.widthAnchor).isActive = true - - self.addSubview(stackview) - stackview.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10).isActive = true - stackview.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true - stackview.topAnchor.constraint(equalTo: self.topAnchor).isActive = true - stackview.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true - - } -} diff --git a/Apps/Examples/Examples/All Examples/FeaturesAtPointExample.swift b/Apps/Examples/Examples/All Examples/FeaturesAtPointExample.swift deleted file mode 100644 index c42be1140da9..000000000000 --- a/Apps/Examples/Examples/All Examples/FeaturesAtPointExample.swift +++ /dev/null @@ -1,88 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(FeaturesAtPointExample) - -public class FeaturesAtPointExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - // Center the map over the United States. - let centerCoordinate = CLLocationCoordinate2D(latitude: 39.368279, - longitude: -97.646484) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 2.4)) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allows the view controller to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.setupExample() - - // The following line is just for testing purposes. - self.finish() - } - } - - public func setupExample() { - - // Create a new GeoJSON data source which gets its data from an external URL. - guard let dataURL = URL(string: "https://docs.mapbox.com/mapbox-gl-js/assets/us_states.geojson") else { - preconditionFailure("URL is not valid") - } - - let sourceIdentifier = "US-states-vector-source" - - var geoJSONSource = GeoJSONSource() - geoJSONSource.data = .url(dataURL) - - // Create a new fill layer associated with the data source. - var fillLayer = FillLayer(id: "US-states") - fillLayer.sourceLayer = "state_county_population_2014_cen" - fillLayer.source = sourceIdentifier - - // Apply basic styling to the fill layer. - fillLayer.fillColor = .constant(StyleColor(.blue)) - fillLayer.fillOpacity = .constant(0.3) - fillLayer.fillOutlineColor = .constant(StyleColor(.black)) - - // Add the data source and style layer to the map. - try! mapView.mapboxMap.style.addSource(geoJSONSource, id: sourceIdentifier) - try! mapView.mapboxMap.style.addLayer(fillLayer, layerPosition: nil) - - // Set up the tap gesture - addTapGesture(to: mapView) - } - - // Add a tap gesture to the map view. - public func addTapGesture(to mapView: MapView) { - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(findFeatures)) - mapView.addGestureRecognizer(tapGesture) - } - - /** - Use the tap point received from the gesture recognizer to query - the map for rendered features at the given point within the layer specified. - */ - @objc public func findFeatures(_ sender: UITapGestureRecognizer) { - let tapPoint = sender.location(in: mapView) - - mapView.mapboxMap.queryRenderedFeatures( - at: tapPoint, - options: RenderedQueryOptions(layerIds: ["US-states"], filter: nil)) { [weak self] result in - switch result { - case .success(let queriedfeatures): - if let firstFeature = queriedfeatures.first?.feature.properties, - case let .string(stateName) = firstFeature["STATE_NAME"] { - self?.showAlert(with: "You selected \(stateName)") - } - case .failure(let error): - self?.showAlert(with: "An error occurred: \(error.localizedDescription)") - } - } - } -} diff --git a/Apps/Examples/Examples/All Examples/FitCameraToGeometryExample.swift b/Apps/Examples/Examples/All Examples/FitCameraToGeometryExample.swift deleted file mode 100644 index 19f7da027667..000000000000 --- a/Apps/Examples/Examples/All Examples/FitCameraToGeometryExample.swift +++ /dev/null @@ -1,68 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(FitCameraToGeometryExample) -public class FitCameraToGeometryExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Allows the view controller to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.fitToCameraToGeometry() - } - - } - - public func fitToCameraToGeometry() { - let triangleCoordinates = [ - CLLocationCoordinate2DMake(43.274580742195845, -2.938070297241211), - CLLocationCoordinate2DMake(43.258768377941465, -2.9680252075195312), - CLLocationCoordinate2DMake(43.24063848114794, -2.912750244140625), - CLLocationCoordinate2DMake(43.274580742195845, -2.938070297241211) - ] - - let polygon = Geometry.polygon(Polygon([triangleCoordinates])) - let polygonFeature = Feature(geometry: polygon) - - let sourceIdentifier = "triangle-source" - var source = GeoJSONSource() - source.data = .feature(polygonFeature) - - var polygonLayer = FillLayer(id: "triangle-style") - polygonLayer.fillOpacity = .constant(0.5) - polygonLayer.fillColor = .constant(StyleColor(.gray)) - polygonLayer.source = sourceIdentifier - - do { - try mapView.mapboxMap.style.addSource(source, id: sourceIdentifier) - } catch { - displayAlert(message: error.localizedDescription) - } - - do { - try mapView.mapboxMap.style.addLayer(polygonLayer, layerPosition: nil) - } catch { - displayAlert(message: error.localizedDescription) - } - - let newCamera = mapView.mapboxMap.camera(for: polygon, padding: .zero, bearing: 0, pitch: 0) - mapView.mapboxMap.setCamera(to: newCamera) - // The below line is used for internal testing purposes only. - self.finish() - } - - fileprivate func displayAlert(message: String) { - let alertController = UIAlertController(title: "Error:", - message: message, - preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil)) - present(alertController, animated: true, completion: nil) - } -} diff --git a/Apps/Examples/Examples/All Examples/GlobeExample.swift b/Apps/Examples/Examples/All Examples/GlobeExample.swift deleted file mode 100644 index de260781f28d..000000000000 --- a/Apps/Examples/Examples/All Examples/GlobeExample.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation -import UIKit -import MapboxMaps - -@objc(GlobeExample) -class GlobeExample: UIViewController, ExampleProtocol { - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds, mapInitOptions: .init(styleURI: .satelliteStreets)) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.mapboxMap.setCamera(to: .init(center: CLLocationCoordinate2D(latitude: 50, longitude: 30), zoom: 0.45)) - try! self.mapView.mapboxMap.style.setProjection(StyleProjection(name: .globe)) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - try! self.mapView.mapboxMap.style.setAtmosphere(Atmosphere()) - self.finish() - } - - view.addSubview(mapView) - } -} diff --git a/Apps/Examples/Examples/All Examples/GlobeFlyToExample.swift b/Apps/Examples/Examples/All Examples/GlobeFlyToExample.swift deleted file mode 100644 index e30626a0a6e4..000000000000 --- a/Apps/Examples/Examples/All Examples/GlobeFlyToExample.swift +++ /dev/null @@ -1,91 +0,0 @@ -import Foundation -import UIKit -import MapboxMaps -import CoreLocation - -class GlobeFlyToExample: UIViewController, ExampleProtocol { - internal var mapView: MapView! - internal var isAtStart = true - var instuctionLabel = UILabel(frame: CGRect.zero) - - private var cameraStart = CameraOptions( - center: CLLocationCoordinate2D(latitude: 36, longitude: 80), - zoom: 1.0, - bearing: 0, - pitch: 0) - - private var cameraEnd = CameraOptions( - center: CLLocationCoordinate2D(latitude: 46.58842, longitude: 8.11862), - zoom: 12.5, - bearing: 130.0, - pitch: 75.0) - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds, mapInitOptions: .init(styleURI: .satelliteStreets)) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.mapboxMap.setCamera(to: .init(center: CLLocationCoordinate2D(latitude: 40, longitude: -78), zoom: 1.0)) - try! self.mapView.mapboxMap.style.setProjection(StyleProjection(name: .globe)) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - try! self.mapView.mapboxMap.style.setAtmosphere(Atmosphere()) - self.addTerrain() - self.finish() - } - - let tap = UITapGestureRecognizer(target: self, action: #selector(animateCameraOnClick)) - mapView.addGestureRecognizer(tap) - - instuctionLabel.text = "Tap anywhere on the map" - instuctionLabel.textColor = UIColor.black - instuctionLabel.textAlignment = .center - instuctionLabel.layer.backgroundColor = UIColor.gray.cgColor - instuctionLabel.layer.cornerRadius = 20.0 - instuctionLabel.translatesAutoresizingMaskIntoConstraints = false - - view.addSubview(mapView) - view.addSubview(instuctionLabel) - installConstraints() - } - - func installConstraints() { - let safeView = view.safeAreaLayoutGuide - - NSLayoutConstraint.activate([ - instuctionLabel.topAnchor.constraint(equalTo: safeView.bottomAnchor, constant: -70), - instuctionLabel.bottomAnchor.constraint(equalTo: safeView.bottomAnchor, constant: -30), - instuctionLabel.leadingAnchor.constraint(equalTo: safeView.leadingAnchor, constant: 70), - instuctionLabel.trailingAnchor.constraint(equalTo: safeView.trailingAnchor, constant: -70) - ]) - - } - - func addTerrain() { - var demSource = RasterDemSource() - demSource.url = "mapbox://mapbox.mapbox-terrain-dem-v1" - // Setting the `tileSize` to 514 provides better performance and adds padding around the outside - // of the tiles. - demSource.tileSize = 514 - demSource.maxzoom = 14.0 - try! mapView.mapboxMap.style.addSource(demSource, id: "mapbox-dem") - - var terrain = Terrain(sourceId: "mapbox-dem") - terrain.exaggeration = .constant(1.5) - - try! mapView.mapboxMap.style.setTerrain(terrain) - } - - @objc func animateCameraOnClick() { - instuctionLabel.isHidden = true - var target = CameraOptions() - if isAtStart { - target = self.cameraEnd - } else { - target = self.cameraStart - } - isAtStart = !isAtStart - mapView.camera.fly(to: target, duration: 12) - - } -} diff --git a/Apps/Examples/Examples/All Examples/Lab/ResizableImageExample.swift b/Apps/Examples/Examples/All Examples/Lab/ResizableImageExample.swift deleted file mode 100644 index e7576f66e86b..000000000000 --- a/Apps/Examples/Examples/All Examples/Lab/ResizableImageExample.swift +++ /dev/null @@ -1,110 +0,0 @@ -import Foundation -import UIKit -import MapboxMaps - -/// An example showcasing of adding a resizable image to the style -/// and demonstrating how the image is stretched -@objc(ResizableImageExample) -final class ResizableImageExample: UIViewController, ExampleProtocol { - private static let center = CLLocationCoordinate2DMake(55.70651, 12.554729) - private static let layerId = "layer_id" - private static let textBase = "Hi! " - - private lazy var mapView: MapView = { - let mapInitOptions = MapInitOptions(cameraOptions: CameraOptions(center: Self.center, zoom: 9)) - let mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - return mapView - }() - private var style: Style { mapView.mapboxMap.style } - - private var appendTextCounter = 1 - private var symbolLayer: SymbolLayer! - private weak var timer: Timer? { - didSet { oldValue?.invalidate() } - } - - // MARK: - Lifecycle - - override func viewDidLoad() { - super.viewDidLoad() - - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.setupExample() - self.startUpdatingIconText() - } - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } - - override func didMove(toParent parent: UIViewController?) { - super.didMove(toParent: parent) - - if parent == nil { - timer = nil - } - } - - // MARK: - Private - - private func setupExample() { - let geoJSONSourceId = "source_id" - - // create an image of a circle and specify the corners that should remain unchanged - let image = UIImage(named: "circle")! - .resizableImage(withCapInsets: UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)) - try? style.addImage(image, id: "circle") - - // add a GeoJSON source with a single point to the style - var source = GeoJSONSource() - source.data = .feature(Feature(geometry: Point(Self.center))) - - try? style.addSource(source, id: geoJSONSourceId) - - // add a symbol layer with the resizable icon image - symbolLayer = SymbolLayer(id: Self.layerId) - symbolLayer.source = geoJSONSourceId - symbolLayer.iconImage = .constant(.name("circle")) - // make sure the icon image is stretched both vertically and horizontally - symbolLayer.iconTextFit = .constant(.both) - symbolLayer.iconTextFitPadding = .constant([10, 10, 10, 10]) - symbolLayer.textField = .constant(Self.textBase) - - try? style.addLayer(symbolLayer, layerPosition: .default) - } - - private func startUpdatingIconText() { - Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(updateIconText), userInfo: nil, repeats: true) - } - - // Append some text to the layer's textField, stretching the icon image in both X and Y axes - @objc private func updateIconText() { - guard style.isLoaded else { - return - } - - let layer = try? style.layer(withId: Self.layerId, type: SymbolLayer.self) - - guard case .expression(let expression) = layer?.textField else { - return - } - - appendTextCounter += 1 - - guard let textLabel = expression.arguments.first?.description - .appending(Self.textBase) - .appending(appendTextCounter % 3 == 0 ? "\n" : "") else { - return - } - - try? style.updateLayer(withId: Self.layerId, type: SymbolLayer.self) { layer in - layer.textField = .constant(textLabel) - } - } -} diff --git a/Apps/Examples/Examples/All Examples/LineGradientExample.swift b/Apps/Examples/Examples/All Examples/LineGradientExample.swift deleted file mode 100644 index 552fecd8ed64..000000000000 --- a/Apps/Examples/Examples/All Examples/LineGradientExample.swift +++ /dev/null @@ -1,125 +0,0 @@ -import Foundation -import MapboxMaps - -@objc(LineGradientExample) -public class LineGradientExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - internal var lastTrimOffset = 0.0 - let button = UIButton(type: .system) - - override public func viewDidLoad() { - super.viewDidLoad() - - let options = MapInitOptions(styleURI: .light) - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - - self.setupExample() - - // Set the center coordinate and zoom level. - let centerCoordinate = CLLocationCoordinate2D(latitude: 38.875, longitude: -77.035) - let camera = CameraOptions(center: centerCoordinate, zoom: 12.0) - self.mapView.mapboxMap.setCamera(to: camera) - } - button.setTitle("Increase trim offset", for: .normal) - button.backgroundColor = .white - button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) - button.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(button) - NSLayoutConstraint.activate([button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)]) - button.addTarget(self, action: #selector(increaseTrimOffset), for: .touchUpInside) - } - - @objc - func increaseTrimOffset() { - lastTrimOffset += 0.05 - let trimOffset = Double.minimum(lastTrimOffset, 1.0) - try? mapView.mapboxMap.style.setLayerProperty(for: "line-layer", property: "line-trim-offset", value: [0.0, trimOffset]) - } - - // Load GeoJSON file from local bundle and decode into a `FeatureCollection`. - internal func decodeGeoJSON(from fileName: String) throws -> FeatureCollection? { - guard let path = Bundle.main.path(forResource: fileName, ofType: "geojson") else { - preconditionFailure("File '\(fileName)' not found.") - } - let filePath = URL(fileURLWithPath: path) - var featureCollection: FeatureCollection? - do { - let data = try Data(contentsOf: filePath) - featureCollection = try JSONDecoder().decode(FeatureCollection.self, from: data) - } catch { - print("Error parsing data: \(error)") - } - return featureCollection - } - - internal func setupExample() { - // The below lines are used for internal testing purposes only. - DispatchQueue.main.asyncAfter(deadline: .now()+5.0) { - self.finish() - } - - // Attempt to decode GeoJSON from file bundled with application. - guard let featureCollection = try? decodeGeoJSON(from: "GradientLine") else { return } - let geoJSONDataSourceIdentifier = "geoJSON-data-source" - - // Create a GeoJSON data source. - var geoJSONSource = GeoJSONSource() - geoJSONSource.data = .featureCollection(featureCollection) - geoJSONSource.lineMetrics = true // MUST be `true` in order to use `lineGradient` expression - - // Create a line layer - var lineLayer = LineLayer(id: "line-layer") - lineLayer.filter = Exp(.eq) { - "$type" - "LineString" - } - - // Setting the source - lineLayer.source = geoJSONDataSourceIdentifier - - // Styling the line - lineLayer.lineColor = .constant(StyleColor(.red)) - lineLayer.lineGradient = .expression( - Exp(.interpolate) { - Exp(.linear) - Exp(.lineProgress) - 0 - UIColor.blue - 0.1 - UIColor.purple - 0.3 - UIColor.cyan - 0.5 - UIColor.green - 0.7 - UIColor.yellow - 1 - UIColor.red - } - ) - - let lowZoomWidth = 10 - let highZoomWidth = 20 - lineLayer.lineWidth = .expression( - Exp(.interpolate) { - Exp(.linear) - Exp(.zoom) - 14 - lowZoomWidth - 18 - highZoomWidth - } - ) - lineLayer.lineCap = .constant(.round) - lineLayer.lineJoin = .constant(.round) - - // Add the source and style layer to the map style. - try! mapView.mapboxMap.style.addSource(geoJSONSource, id: geoJSONDataSourceIdentifier) - try! mapView.mapboxMap.style.addLayer(lineLayer, layerPosition: nil) - } -} diff --git a/Apps/Examples/Examples/All Examples/LocalizationExample.swift b/Apps/Examples/Examples/All Examples/LocalizationExample.swift deleted file mode 100644 index d0019645c732..000000000000 --- a/Apps/Examples/Examples/All Examples/LocalizationExample.swift +++ /dev/null @@ -1,78 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(LocalizationExample) - -public class LocalizationExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - configureLanguageButton() - - // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.finish() // Needed for internal testing purposes. - } - } - - private func configureLanguageButton() { - // Set up layer postion change button - let button = UIButton(type: .system) - button.setTitle("Change Language", for: .normal) - button.setTitleColor(.white, for: .normal) - button.backgroundColor = #colorLiteral(red: 0, green: 0.4784313725, blue: 0.9882352941, alpha: 1) - button.translatesAutoresizingMaskIntoConstraints = false - button.layer.cornerRadius = 20 - button.clipsToBounds = true - button.addTarget(self, action: #selector(changeLanguage(sender:)), for: .touchUpInside) - view.addSubview(button) - - // Set button location - let horizontalConstraint = button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24) - let verticalConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor) - let widthConstraint = button.widthAnchor.constraint(equalToConstant: 200) - let heightConstraint = button.heightAnchor.constraint(equalToConstant: 40) - NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint]) - } - - @objc public func changeLanguage(sender: UIButton) { - let alert = UIAlertController(title: "Languages", - message: "Please select a language to localize to.", - preferredStyle: .actionSheet) - - alert.addAction(UIAlertAction(title: "Spanish", style: .default, handler: { [weak self] _ in - try! self?.mapView.mapboxMap.style.localizeLabels(into: Locale(identifier: "es")) - })) - - alert.addAction(UIAlertAction(title: "French", style: .default, handler: { [weak self] _ in - try! self?.mapView.mapboxMap.style.localizeLabels(into: Locale(identifier: "fr")) - })) - - alert.addAction(UIAlertAction(title: "Traditional Chinese", style: .default, handler: { [weak self] _ in - try! self?.mapView.mapboxMap.style.localizeLabels(into: Locale(identifier: "zh-Hant")) - })) - - alert.addAction(UIAlertAction(title: "Arabic", style: .default, handler: { [weak self] _ in - try! self?.mapView.mapboxMap.style.localizeLabels(into: Locale(identifier: "ar")) - })) - - alert.addAction(UIAlertAction(title: "English", style: .default, handler: { [weak self] _ in - try! self?.mapView.mapboxMap.style.localizeLabels(into: Locale(identifier: "en")) - })) - - alert.addAction(UIAlertAction(title: "Japanese - Countries Only", style: .default, handler: { [weak self] _ in - try! self?.mapView.mapboxMap.style.localizeLabels(into: Locale(identifier: "ja"), forLayerIds: ["country-label"]) - })) - - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) - - present(alert, animated: true) - } -} diff --git a/Apps/Examples/Examples/All Examples/ModelLayerExample.swift b/Apps/Examples/Examples/All Examples/ModelLayerExample.swift deleted file mode 100644 index e29483535266..000000000000 --- a/Apps/Examples/Examples/All Examples/ModelLayerExample.swift +++ /dev/null @@ -1,71 +0,0 @@ -import UIKit -@_spi(Experimental) import MapboxMaps - -@objc(ModelLayerExample) -final class ModelLayerExample: UIViewController, ExampleProtocol { - private enum Constants { - static let helsinki = Point(CLLocationCoordinate2D(latitude: 60.1699, longitude: 24.9384)) - static let mapboxHelsinki = Point(CLLocationCoordinate2D(latitude: 60.17195694011002, longitude: 24.945389069265598)) - static let modelIdKey = "model-id-key" - static let sourceId = "source-id" - static let duckModelId = "model-id-duck" - static let carModelId = "model-id-car" - static let duck = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf" - static let car = Bundle.main.url(forResource: "sportcar", withExtension: "glb")!.absoluteString - } - private var mapView: MapView! - - override func viewDidLoad() { - super.viewDidLoad() - - let cameraOptions = CameraOptions( - center: mid(Constants.helsinki.coordinates, Constants.mapboxHelsinki.coordinates), - zoom: 14, - pitch: 45 - ) - let options = MapInitOptions(cameraOptions: cameraOptions) - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.ornaments.options.scaleBar.visibility = .visible - - view.addSubview(mapView) - - mapView.mapboxMap.loadStyleURI(.light) { [weak self] _ in - self?.setupExample() - } - } - - private func setupExample() { - let style = mapView.mapboxMap.style - - try! style.addModel(withId: Constants.duckModelId, modelURI: Constants.duck) - try! style.addModel(withId: Constants.carModelId, modelURI: Constants.car) - - var source = GeoJSONSource() - var duckFeature = Feature(geometry: Constants.helsinki) - duckFeature.properties = [Constants.modelIdKey: .string(Constants.duckModelId)] - var carFeature = Feature(geometry: Constants.mapboxHelsinki) - carFeature.properties = [Constants.modelIdKey: .string(Constants.carModelId)] - - source.data = .featureCollection(FeatureCollection(features: [duckFeature, carFeature])) - - try! style.addSource(source, id: Constants.sourceId) - - var layer = ModelLayer(id: "model-layer-id") - layer.source = Constants.sourceId - layer.modelId = .expression(Exp(.get) { Constants.modelIdKey }) - layer.modelType = .constant(.common3d) - layer.modelScale = .constant([100, 100, 100]) - layer.modelTranslation = .constant([0, 0, 0]) - layer.modelRotation = .constant([0, 0, 90]) - layer.modelOpacity = .constant(0.7) - - try! style.addLayer(layer) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } -} diff --git a/Apps/Examples/Examples/All Examples/NavigationSimulator.swift b/Apps/Examples/Examples/All Examples/NavigationSimulator.swift deleted file mode 100644 index 16502ba4af96..000000000000 --- a/Apps/Examples/Examples/All Examples/NavigationSimulator.swift +++ /dev/null @@ -1,98 +0,0 @@ -import Foundation -import CoreLocation -@_spi(Experimental) import MapboxMaps - -final class NavigationSimulator: LocationProvider { - - private let viewport: Viewport - private let route: LineString - - private lazy var followPuckViewPortState = viewport.makeFollowPuckViewportState( - options: FollowPuckViewportStateOptions(bearing: .course) - ) - - var locationProviderOptions = LocationOptions() - var authorizationStatus: CLAuthorizationStatus = .authorizedAlways - var accuracyAuthorization: CLAccuracyAuthorization = .fullAccuracy - - var heading: CLHeading? - var headingOrientation: CLDeviceOrientation = .portrait - - private weak var delegate: LocationProviderDelegate? - - private var isStarted = false - private let routeLength: LocationDistance - private var routePointsToTravel: [LocationCoordinate2D] - - private var direction: LocationDirection - private var currentLocation: LocationCoordinate2D { - didSet { - direction = oldValue.direction(to: currentLocation) - startUpdatingLocation() - } - } - - init(viewport: Viewport, route: LineString) { - self.viewport = viewport - self.route = route - routeLength = route.distance()! - routePointsToTravel = route.coordinates - - currentLocation = routePointsToTravel.removeFirst() - direction = currentLocation.direction(to: routePointsToTravel[0]) - } - - func start() { - guard !isStarted else { return } - isStarted = true - - viewport.transition(to: followPuckViewPortState) { _ in - Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in - if !self.routePointsToTravel.isEmpty { - let nextPoint = self.routePointsToTravel.removeFirst() - self.currentLocation = nextPoint - } else { - // Journey completed. - timer.invalidate() - } - } - } - } - - func progressFromStart(to location: Location) -> Double { - route.distance(to: location.coordinate)! / routeLength - } - - // MARK: LocationProvider - - func setDelegate(_ delegate: LocationProviderDelegate) { - self.delegate = delegate - } - - func requestAlwaysAuthorization() {} - func requestWhenInUseAuthorization() {} - func requestTemporaryFullAccuracyAuthorization(withPurposeKey purposeKey: String) {} - - func startUpdatingLocation() { - let location = CLLocation( - coordinate: currentLocation, - altitude: 0, - horizontalAccuracy: kCLLocationAccuracyBestForNavigation, - verticalAccuracy: kCLLocationAccuracyBestForNavigation, - // Turf calculates bearing in decimal degrees within -180 to 180, - // while Apple's course requires value in decimal degrees from 0 - 359.9 - course: direction < 0 ? 360 + direction : direction, - speed: 0, - timestamp: Date() - ) - delegate?.locationProvider( - self, - didUpdateLocations: [location] - ) - } - func stopUpdatingLocation() {} - - func startUpdatingHeading() {} - func stopUpdatingHeading() {} - func dismissHeadingCalibrationDisplay() {} -} diff --git a/Apps/Examples/Examples/All Examples/PointClusteringExample.swift b/Apps/Examples/Examples/All Examples/PointClusteringExample.swift deleted file mode 100644 index 5b4d6c07a18f..000000000000 --- a/Apps/Examples/Examples/All Examples/PointClusteringExample.swift +++ /dev/null @@ -1,125 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(PointClusteringExample) - -public class PointClusteringExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - // Initialize a map view centered over the United States and using the Mapbox Dark style. - let center = CLLocationCoordinate2D(latitude: 40.669957, longitude: -103.5917968) - let cameraOptions = CameraOptions(center: center, zoom: 2) - let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .dark) - - mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.tintColor = .lightGray - - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - self.addPointClusters() - } - } - - func addPointClusters() { - let style = self.mapView.mapboxMap.style - // Parse GeoJSON data. This example uses all M1.0+ earthquakes from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program. - guard let url = URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson") else { return } - - // Create a GeoJSONSource from the earthquake data URL. - var source = GeoJSONSource() - source.data = .url(url) - - // Set the clustering properties directly on the source. - source.cluster = true - source.clusterRadius = 50 - - // The maximum zoom level where points will be clustered. - source.clusterMaxZoom = 14 - let sourceID = "earthquake-source" - - // Create three separate layers from the same source. - // `clusteredLayer` contains clustered point features. - var clusteredLayer = createClusteredLayer() - clusteredLayer.source = sourceID - - // `unclusteredLayer` contains individual point features that do not represent clusters. - var unclusteredLayer = createUnclusteredLayer() - unclusteredLayer.source = sourceID - - // `clusterCountLayer` is a `SymbolLayer` that represents the point count within individual clusters. - var clusterCountLayer = createNumberLayer() - clusterCountLayer.source = sourceID - - // Add source and layers to the map view's style. - try! style.addSource(source, id: sourceID) - try! style.addLayer(clusteredLayer) - try! style.addLayer(unclusteredLayer, layerPosition: .below(clusteredLayer.id)) - try! style.addLayer(clusterCountLayer) - } - - func createClusteredLayer() -> CircleLayer { - // Create a `CircleLayer` that only contains clustered points. - var clusteredLayer = CircleLayer(id: "clustered-earthquake-layer") - clusteredLayer.filter = Exp(.has) { "point_count" } - - // Set the circle's color and radius based on the number of points within each cluster. - clusteredLayer.circleColor = .expression(Exp(.step) { - Exp(.get) { "point_count" } - UIColor(red: 0.32, green: 0.73, blue: 0.84, alpha: 1.00) - 100 - UIColor(red: 0.95, green: 0.94, blue: 0.46, alpha: 1.00) - 750 - UIColor(red: 0.95, green: 0.55, blue: 0.69, alpha: 1.00) - }) - - clusteredLayer.circleRadius = .expression(Exp(.step) { - Exp(.get) { "point_count" } - 20 - 100 - 30 - 750 - 40 - }) - - return clusteredLayer - } - - func createUnclusteredLayer() -> CircleLayer { - var unclusteredLayer = CircleLayer(id: "unclusteredPointLayer") - - // Filter out clusters by checking for point_count. - unclusteredLayer.filter = Exp(.not) { - Exp(.has) { "point_count" } - } - - unclusteredLayer.circleColor = .constant(StyleColor(UIColor(red: 0.07, green: 0.71, blue: 0.85, alpha: 1.00))) - unclusteredLayer.circleRadius = .constant(4) - unclusteredLayer.circleStrokeWidth = .constant(1) - unclusteredLayer.circleStrokeColor = .constant(StyleColor(.black)) - return unclusteredLayer - } - - func createNumberLayer() -> SymbolLayer { - var numberLayer = SymbolLayer(id: "cluster-count-layer") - - // Check whether the point feature is clustered. - numberLayer.filter = Exp(.has) { "point_count" } - - // Display the value for 'point_count' in the text field. - numberLayer.textField = .expression(Exp(.get) { "point_count" }) - numberLayer.textSize = .constant(12) - return numberLayer - } - - override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } -} diff --git a/Apps/Examples/Examples/All Examples/RasterTileSourceExample.swift b/Apps/Examples/Examples/All Examples/RasterTileSourceExample.swift deleted file mode 100644 index f3db8e6aec53..000000000000 --- a/Apps/Examples/Examples/All Examples/RasterTileSourceExample.swift +++ /dev/null @@ -1,75 +0,0 @@ -import MapboxMaps -import UIKit - -@objc(RasterTileSourceExample) -class RasterTileSourceExample: UIViewController, ExampleProtocol { - var mapView: MapView! - var isTileRequestDelayEnabled = false - - let button = UIButton(type: .system) - let sourceId = "raster-source" - - override func viewDidLoad() { - super.viewDidLoad() - - // Initialize a `MapView` that is centered over the southeastern United States. - let centerCoordinate = CLLocationCoordinate2D(latitude: 40, longitude: -74.5) - let cameraOptions = CameraOptions(center: centerCoordinate, zoom: 2) - let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions) - - mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - // Once the map has finished loading, add the `RasterSource` and `RasterLayer` to the map's style. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.addRasterSource() - - // The following line is just for testing purposes. - self.finish() - } - - button.setTitle("Enable tile request delay", for: .normal) - button.backgroundColor = .white - button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) - button.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(button) - NSLayoutConstraint.activate([button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)]) - button.addTarget(self, action: #selector(toggleTileRequestDelay), for: .touchUpInside) - } - - @objc - func toggleTileRequestDelay() { - isTileRequestDelayEnabled.toggle() - try? mapView.mapboxMap.style.setSourceProperty(for: sourceId, property: "tile-requests-delay", value: isTileRequestDelayEnabled ? 5000 : 0) - button.setTitle(isTileRequestDelayEnabled ? "Disable tile request delay" : "Enable tile request delay", for: .normal) - } - - func addRasterSource() { - let style = mapView.mapboxMap.style - - // This URL points to raster tiles designed by Stamen Design. - let sourceUrl = "https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg" - - // Create a `RasterSource` and set the source's `tiles` to the Stamen watercolor raster tiles. - var rasterSource = RasterSource() - rasterSource.tiles = [sourceUrl] - - // Specify the tile size for the `RasterSource`. - rasterSource.tileSize = 256 - - var rasterLayer = RasterLayer(id: "raster-layer") - - // Specify that the layer should use the source with the ID `raster-source`. This ID will be - // assigned to the `RasterSource` when it is added to the style. - - rasterLayer.source = sourceId - - do { - try style.addSource(rasterSource, id: sourceId) - try style.addLayer(rasterLayer) - } catch { - print("Failed to update the style. Error: \(error)") - } - } -} diff --git a/Apps/Examples/Examples/All Examples/RestrictCoordinateBoundsExample.swift b/Apps/Examples/Examples/All Examples/RestrictCoordinateBoundsExample.swift deleted file mode 100644 index 931ac114371e..000000000000 --- a/Apps/Examples/Examples/All Examples/RestrictCoordinateBoundsExample.swift +++ /dev/null @@ -1,33 +0,0 @@ -import UIKit -import MapboxMaps -import MapboxCoreMaps - -@objc(RestrictCoordinateBoundsExample) -final class RestrictCoordinateBoundsExample: UIViewController, ExampleProtocol { - - override func viewDidLoad() { - super.viewDidLoad() - - let mapView = MapView(frame: view.bounds) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - let bounds = CoordinateBounds(southwest: CLLocationCoordinate2D(latitude: 63.33, longitude: -25.52), - northeast: CLLocationCoordinate2D(latitude: 66.61, longitude: -13.47)) - - // Restrict the camera to `bounds`. - try? mapView.mapboxMap.setCameraBounds(with: CameraBoundsOptions(bounds: bounds)) - - // Center the camera on the bounds - let camera = mapView.mapboxMap.camera(for: bounds, padding: .zero, bearing: 0, pitch: 0) - - // Set the camera's center coordinate. - mapView.mapboxMap.setCamera(to: camera) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } -} diff --git a/Apps/Examples/Examples/All Examples/ShowHideLayerExample.swift b/Apps/Examples/Examples/All Examples/ShowHideLayerExample.swift deleted file mode 100644 index 90fef5572c5b..000000000000 --- a/Apps/Examples/Examples/All Examples/ShowHideLayerExample.swift +++ /dev/null @@ -1,163 +0,0 @@ -import MapboxMaps - -@objc(ShowHideLayerExample) - -class ShowHideLayerExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - let museumLayerId = "museum-circle-layer" - let contourLayerId = "contour-line-layer" - override func viewDidLoad() { - super.viewDidLoad() - - // Create an initial camera that is centered over Cusco, Peru and use it - // when initializing the `MapView`. - let center = CLLocationCoordinate2D(latitude: -13.517379, longitude: -71.977221) - let cameraOptions = CameraOptions(center: center, zoom: 15) - let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions) - - mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.ornaments.options.scaleBar.visibility = .hidden - view.addSubview(mapView) - - // Once the map has finished loading, add the museum and contour layers to the map's style, - // then add switches that toggle the visibility for those two layers. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.addStyleLayers() - self.addVisibilitySwitches() - - // The following line is just for testing purposes. - self.finish() - } - } - - func addStyleLayers() { - // Specify the source IDs. They will be assigned to their respective sources when we - // add the source to the map's style. - let museumSourceId = "museum-source" - let contourSourceId = "contour-source" - - // Create a custom vector tileset source. This source contains point features - // that represent museums. - var museumsSource = VectorSource() - museumsSource.url = "mapbox://mapbox.2opop9hr" - - var museumLayer = CircleLayer(id: museumLayerId) - - // Assign this layer's source. - museumLayer.source = museumSourceId - // Specify the layer within the vector source to render on the map. - museumLayer.sourceLayer = "museum-cusco" - - // Use a constant circle radius and color to style the layer. - museumLayer.circleRadius = .constant(8) - - // `visibility` is `nil` by default. Set to `visible`. - museumLayer.visibility = .constant(.visible) - - let museumColor = UIColor(red: 0.22, green: 0.58, blue: 0.70, alpha: 1.00) - museumLayer.circleColor = .constant(StyleColor(museumColor)) - - var contourSource = VectorSource() - // Add the Mapbox Terrain v2 vector tileset. Documentation for this vector tileset - // can be found at https://docs.mapbox.com/vector-tiles/reference/mapbox-terrain-v2/ - contourSource.url = "mapbox://mapbox.mapbox-terrain-v2" - - var contourLayer = LineLayer(id: contourLayerId) - - // Assign this layer's source and source layer ID. - contourLayer.source = contourSourceId - contourLayer.sourceLayer = "contour" - - // Style the contents of the source's contour layer. - contourLayer.lineCap = .constant(.round) - contourLayer.lineJoin = .constant(.round) - - // `visibility` is `nil` by default. Set to `visible`. - contourLayer.visibility = .constant(.visible) - let contourLineColor = UIColor(red: 0.53, green: 0.48, blue: 0.35, alpha: 1.00) - contourLayer.lineColor = .constant(StyleColor(contourLineColor)) - - let style = mapView.mapboxMap.style - - // Add the sources and layers to the map's style. - do { - try style.addSource(museumsSource, id: museumSourceId) - try style.addSource(contourSource, id: contourSourceId) - try style.addLayer(museumLayer) - try style.addLayer(contourLayer) - } catch { - print("Error when adding sources and layers: \(error.localizedDescription)") - } - } - - @objc func toggleMuseumLayerVisibility(sender: UISwitch) { - let style = mapView.mapboxMap.style - // Update the museum layer's visibility based on whether the switch - // is on. `visibility` is `nil` by default. - do { - try style.updateLayer(withId: museumLayerId, type: CircleLayer.self) { layer in - layer.visibility = .constant(sender.isOn ? .visible : .none) - } - } catch { - print("Failed to update the visibility for layer with id \(museumLayerId). Error: \(error.localizedDescription)") - } - } - - @objc func toggleContourLayerVisibility(sender: UISwitch) { - let style = mapView.mapboxMap.style - // Update the contour layer's visibility based on whether the switch - // is on. - do { - try style.updateLayer(withId: contourLayerId, type: CircleLayer.self) { layer in - layer.visibility = .constant(sender.isOn ? .visible : .none) - } - } catch { - print("Failed to update the visibility for layer with id \(contourLayerId). Error: \(error.localizedDescription)") - } - } - - func addVisibilitySwitches() { - // Create switches to toggle the layers' visibility. - let museumSwitch = UISwitch() - museumSwitch.addTarget(self, action: #selector(toggleMuseumLayerVisibility(sender:)), for: .valueChanged) - museumSwitch.isOn = true - museumSwitch.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(museumSwitch) - - let contourSwitch = UISwitch() - contourSwitch.addTarget(self, action: #selector(toggleContourLayerVisibility(sender:)), for: .valueChanged) - contourSwitch.isOn = true - contourSwitch.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(contourSwitch) - - // Add labels for the toggles. - let museumLabel = UILabel() - museumLabel.text = "Show museums" - museumLabel.textColor = .darkText - museumLabel.backgroundColor = .white - museumLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(museumLabel) - - let contourLabel = UILabel() - contourLabel.text = "Show contours" - contourLabel.textColor = .darkText - contourLabel.backgroundColor = .white - contourLabel.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(contourLabel) - - // Layout the switches and labels. - NSLayoutConstraint.activate([ - museumSwitch.topAnchor.constraint(equalTo: mapView.safeAreaLayoutGuide.topAnchor, constant: 20), - museumSwitch.leadingAnchor.constraint(equalTo: mapView.safeAreaLayoutGuide.leadingAnchor, constant: 20), - museumLabel.leadingAnchor.constraint(equalTo: museumSwitch.trailingAnchor, constant: 10), - museumLabel.centerYAnchor.constraint(equalTo: museumSwitch.centerYAnchor), - contourSwitch.topAnchor.constraint(equalTo: museumSwitch.bottomAnchor, constant: 20), - contourSwitch.leadingAnchor.constraint(equalTo: mapView.safeAreaLayoutGuide.leadingAnchor, constant: 20), - contourLabel.leadingAnchor.constraint(equalTo: contourSwitch.trailingAnchor, constant: 10), - contourLabel.centerYAnchor.constraint(equalTo: contourSwitch.centerYAnchor) - ]) - } -} diff --git a/Apps/Examples/Examples/All Examples/SnapshotterExample.swift b/Apps/Examples/Examples/All Examples/SnapshotterExample.swift deleted file mode 100644 index f6513286b831..000000000000 --- a/Apps/Examples/Examples/All Examples/SnapshotterExample.swift +++ /dev/null @@ -1,89 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(SnapshotterExample) - -public class SnapshotterExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - public var snapshotter: Snapshotter! - public var snapshotView: UIImageView! - private var snapshotting = false - - override public func viewDidLoad() { - super.viewDidLoad() - - // Create a vertical stack view to hold both the map view and the snapshot. - let stackView = UIStackView(frame: view.safeAreaLayoutGuide.layoutFrame) - stackView.axis = .vertical - stackView.distribution = .fill - stackView.alignment = .center - stackView.spacing = 12.0 - - let mapViewRect = CGRect(x: 0, y: 0, width: view.bounds.width/2, height: view.bounds.height / 2) - - let mapInitOptions = MapInitOptions(cameraOptions: CameraOptions(center: CLLocationCoordinate2D(latitude: 50, longitude: 138.482), - zoom: 3.5), - styleURI: .dark) - - mapView = MapView(frame: mapViewRect, mapInitOptions: mapInitOptions) - // Add the `MapViewController`'s view to the stack view as a - // child view controller. - stackView.addArrangedSubview(mapView) - - // Add the image view to the stack view, which will eventually contain the snapshot. - let stackViewBounds = CGRect(x: 0, - y: 0, - width: view.bounds.size.width, - height: view.bounds.height / 2) - snapshotView = UIImageView(frame: stackViewBounds) - stackView.addArrangedSubview(snapshotView) - - NSLayoutConstraint.activate([ - mapView.widthAnchor.constraint(equalToConstant: view.bounds.size.width), - snapshotView.widthAnchor.constraint(equalToConstant: stackViewBounds.width), - snapshotView.heightAnchor.constraint(equalToConstant: stackViewBounds.height) - ]) - - // Add the stack view to the root view. - view.addSubview(stackView) - - // Configure the snapshotter object with its default access - // token, size, map style, and camera. - let options = MapSnapshotOptions( - size: stackViewBounds.size, - pixelRatio: UIScreen.main.scale, - resourceOptions: mapInitOptions.resourceOptions) - - snapshotter = Snapshotter(options: options) - snapshotter.style.uri = .light - - // Set the camera of the snapshotter - - mapView.mapboxMap.onEvery(event: .mapIdle) { [weak self] _ in - // Allow the previous snapshot to complete before starting a new one. - guard let self = self, !self.snapshotting else { - return - } - - let snapshotterCameraOptions = CameraOptions(cameraState: self.mapView.cameraState) - self.snapshotter.setCamera(to: snapshotterCameraOptions) - self.startSnapshot() - } - } - - public func startSnapshot() { - snapshotting = true - snapshotter.start(overlayHandler: nil) { ( result ) in - switch result { - case .success(let image): - self.snapshotView.image = image - case .failure(let error): - print("Error generating snapshot: \(error)") - } - self.snapshotting = false - // The below line is used for internal testing purposes only. - self.finish() - } - } -} diff --git a/Apps/Examples/Examples/All Examples/SpinningGlobeExample.swift b/Apps/Examples/Examples/All Examples/SpinningGlobeExample.swift deleted file mode 100644 index 5e6ebb410706..000000000000 --- a/Apps/Examples/Examples/All Examples/SpinningGlobeExample.swift +++ /dev/null @@ -1,81 +0,0 @@ -import Foundation -import UIKit -import MapboxMaps -import CoreLocation - -class SpinningGlobeExample: UIViewController, GestureManagerDelegate, ExampleProtocol { - - var userInteracting = false - var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - mapView = MapView(frame: view.bounds, mapInitOptions: .init(styleURI: .satellite)) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - mapView.mapboxMap.setCamera(to: .init(center: CLLocationCoordinate2D(latitude: 40, longitude: -90), zoom: 1.0)) - try! self.mapView.mapboxMap.style.setProjection(StyleProjection(name: .globe)) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - try! self.mapView.mapboxMap.style.setAtmosphere(Atmosphere()) - self.spinGlobe() - self.finish() - } - - mapView.gestures.delegate = self - - view.addSubview(mapView) - } - - func spinGlobe() { - // At low zooms, complete a revolution every two minutes. - let secPerRevolution = 120.0 - // Above zoom level 5, do not rotate. - let maxSpinZoom = 5.0 - // Rotate at intermediate speeds between zoom levels 3 and 5. - let slowSpinZoom = 3.0 - - let zoom = mapView.cameraState.zoom - if !userInteracting && zoom < maxSpinZoom { - var distancePerSecond = 360.0 / secPerRevolution - if zoom > slowSpinZoom { - // Slow spinning at higher zooms - let zoomDif = (maxSpinZoom - zoom) / (maxSpinZoom - slowSpinZoom) - distancePerSecond *= zoomDif - } - let center = mapView.cameraState.center - let targetCenter = CLLocationCoordinate2D(latitude: center.latitude, longitude: center.longitude - distancePerSecond) - - // Smoothly animate the map over one second. - // When this animation is complete, call it again - mapView.camera.ease(to: .init(center: targetCenter), duration: 1.0, curve: .linear) { rotating in - - guard rotating == .end else { - return - } - self.spinGlobe() - } - } - } - - func gestureManager(_ gestureManager: GestureManager, didBegin gestureType: GestureType) { - userInteracting = true - } - - func gestureManager(_ gestureManager: GestureManager, didEnd gestureType: GestureType, willAnimate: Bool) { - - if !willAnimate { - userInteracting = false - DispatchQueue.main.async { - self.spinGlobe() - } - } - } - - func gestureManager(_ gestureManager: GestureManager, didEndAnimatingFor gestureType: GestureType) { - userInteracting = false - DispatchQueue.main.async { - self.spinGlobe() - } - } -} diff --git a/Apps/Examples/Examples/All Examples/StoryboardMapViewExample.swift b/Apps/Examples/Examples/All Examples/StoryboardMapViewExample.swift deleted file mode 100644 index 1e4ec97b588d..000000000000 --- a/Apps/Examples/Examples/All Examples/StoryboardMapViewExample.swift +++ /dev/null @@ -1,25 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(StoryboardMapViewExample) - -public class StoryboardMapViewExample: UIViewController, MapInitOptionsProvider, ExampleProtocol { - - public func mapInitOptions() -> MapInitOptions { - let resourceOptions = ResourceOptions(accessToken: ResourceOptionsManager.default.resourceOptions.accessToken) - let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 40.728, longitude: -74.0060), zoom: 10) - - //return a custom MapInitOptions - return MapInitOptions( - resourceOptions: resourceOptions, - cameraOptions: cameraOptions, - styleURI: .light) - } - - override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } - -} diff --git a/Apps/Examples/Examples/All Examples/SwitchStylesExample.swift b/Apps/Examples/Examples/All Examples/SwitchStylesExample.swift deleted file mode 100644 index 865bb752bd70..000000000000 --- a/Apps/Examples/Examples/All Examples/SwitchStylesExample.swift +++ /dev/null @@ -1,92 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(SwitchStylesExample) - -public class SwitchStylesExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - internal var styleToggle: UISegmentedControl! - internal var style: Style = .satelliteStreets { - didSet { - mapView.mapboxMap.style.uri = style.uri - } - } - - enum Style: Int, CaseIterable { - - var name: String { - switch self { - case .light: - return "light".capitalized - case .satelliteStreets: - return "s. streets".capitalized - case .customUri: - return "custom".capitalized - } - } - - var uri: StyleURI { - switch self { - case .light: - return .light - case .satelliteStreets: - return .satelliteStreets - case .customUri: - let localStyleURL = Bundle.main.url(forResource: "blueprint_style", withExtension: "json")! - return .init(url: localStyleURL)! - } - } - - case light - case satelliteStreets - case customUri - } - - override public func viewDidLoad() { - super.viewDidLoad() - - // Initialize a map centered near El Ejido, Spain and with a zoom level of 13. - let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 36.77271, longitude: -2.81361), zoom: 13) - let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: style.uri) - - mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - - view.addSubview(mapView) - - // Add a `UISegmentedControl` to toggle between Mapbox Streets, Mapbox Satellite Streets, and a custom style. - addStyleToggle() - installConstraints() - } - - @objc func switchStyle(sender: UISegmentedControl) { - style = Style(rawValue: sender.selectedSegmentIndex) ?? . satelliteStreets - } - - func addStyleToggle() { - // Create a UISegmentedControl to toggle between map styles - styleToggle = UISegmentedControl(items: Style.allCases.map(\.name)) - styleToggle.tintColor = UIColor(red: 0.976, green: 0.843, blue: 0.831, alpha: 1) - styleToggle.backgroundColor = .white - styleToggle.selectedSegmentIndex = style.rawValue - view.insertSubview(styleToggle, aboveSubview: mapView) - styleToggle.addTarget(self, action: #selector(switchStyle(sender:)), for: .valueChanged) - styleToggle.translatesAutoresizingMaskIntoConstraints = false - } - - func installConstraints() { - // Configure autolayout constraints for the UISegmentedControl to align - // at the bottom of the map view. - NSLayoutConstraint.activate([ - styleToggle.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -60), - styleToggle.centerXAnchor.constraint(equalTo: mapView.centerXAnchor), - ]) - } - - override public func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } -} diff --git a/Apps/Examples/Examples/All Examples/TerrainExample.swift b/Apps/Examples/Examples/All Examples/TerrainExample.swift deleted file mode 100644 index f0fec2db0cf3..000000000000 --- a/Apps/Examples/Examples/All Examples/TerrainExample.swift +++ /dev/null @@ -1,54 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(TerrainExample) -public class TerrainExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { - super.viewDidLoad() - - let centerCoordinate = CLLocationCoordinate2D(latitude: 32.6141, longitude: -114.34411) - let camera = CameraOptions(center: centerCoordinate, - zoom: 13.1, - bearing: 80, - pitch: 85) - let options = MapInitOptions( - cameraOptions: camera, - styleURI: StyleURI(rawValue: "mapbox://styles/mapbox-map-design/ckhqrf2tz0dt119ny6azh975y")!) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - - view.addSubview(mapView) - - mapView.mapboxMap.onNext(event: .styleLoaded) { _ in - self.addTerrain() - // The following line is just for testing purposes. - self.finish() - } - } - - func addTerrain() { - var demSource = RasterDemSource() - demSource.url = "mapbox://mapbox.mapbox-terrain-dem-v1" - // Setting the `tileSize` to 514 provides better performance and adds padding around the outside - // of the tiles. - demSource.tileSize = 514 - demSource.maxzoom = 14.0 - try! mapView.mapboxMap.style.addSource(demSource, id: "mapbox-dem") - - var terrain = Terrain(sourceId: "mapbox-dem") - terrain.exaggeration = .constant(1.5) - - try! mapView.mapboxMap.style.setTerrain(terrain) - - var skyLayer = SkyLayer(id: "sky-layer") - skyLayer.skyType = .constant(.atmosphere) - skyLayer.skyAtmosphereSun = .constant([0.0, 0.0]) - skyLayer.skyAtmosphereSunIntensity = .constant(15.0) - - try! mapView.mapboxMap.style.addLayer(skyLayer) - } -} diff --git a/Apps/Examples/Examples/All Examples/TrackingModeExample.swift b/Apps/Examples/Examples/All Examples/TrackingModeExample.swift deleted file mode 100644 index 862295834c23..000000000000 --- a/Apps/Examples/Examples/All Examples/TrackingModeExample.swift +++ /dev/null @@ -1,149 +0,0 @@ -import UIKit -import MapboxMaps - -@objc(TrackingModeExample) -public class TrackingModeExample: UIViewController, ExampleProtocol { - - private var mapView: MapView! - private var cameraLocationConsumer: CameraLocationConsumer! - private lazy var toggleBearingImageButton = UIButton(frame: .zero) - private lazy var styleToggle = UISegmentedControl(items: Style.allCases.map(\.name)) - private var style: Style = .satelliteStreets { - didSet { - mapView.mapboxMap.style.uri = style.uri - } - } - private var showsBearingImage: Bool = false { - didSet { - syncPuckAndButton() - } - } - - private enum Style: Int, CaseIterable { - var name: String { - switch self { - case .light: - return "Light" - case .satelliteStreets: - return "Satelite" - case .customUri: - return "Custom" - } - } - - var uri: StyleURI { - switch self { - case .light: - return .light - case .satelliteStreets: - return .satelliteStreets - case .customUri: - let localStyleURL = Bundle.main.url(forResource: "blueprint_style", withExtension: "json")! - return .init(url: localStyleURL)! - } - } - - case light - case satelliteStreets - case customUri - } - - override public func viewDidLoad() { - super.viewDidLoad() - - // Set initial camera settings - let options = MapInitOptions(styleURI: style.uri) - - mapView = MapView(frame: view.bounds, mapInitOptions: options) - mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - view.addSubview(mapView) - - addStyleToggle() - - // Setup and create button for toggling show bearing image - setupToggleShowBearingImageButton() - - cameraLocationConsumer = CameraLocationConsumer(mapView: mapView) - - // Add user position icon to the map with location indicator layer - mapView.location.options.puckType = .puck2D() - - // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - // Register the location consumer with the map - // Note that the location manager holds weak references to consumers, which should be retained - self.mapView.location.addLocationConsumer(newConsumer: self.cameraLocationConsumer) - - } - } - - public override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - // The below line is used for internal testing purposes only. - finish() - } - - @objc func showHideBearingImage() { - showsBearingImage.toggle() - } - - func syncPuckAndButton() { - // Update puck config - let configuration = Puck2DConfiguration.makeDefault(showBearing: showsBearingImage) - - mapView.location.options.puckType = .puck2D(configuration) - - // Update button title - let title: String = showsBearingImage ? "Hide bearing image" : "Show bearing image" - toggleBearingImageButton.setTitle(title, for: .normal) - } - - private func setupToggleShowBearingImageButton() { - // Styling - toggleBearingImageButton.backgroundColor = .systemBlue - toggleBearingImageButton.addTarget(self, action: #selector(showHideBearingImage), for: .touchUpInside) - toggleBearingImageButton.setTitleColor(.white, for: .normal) - toggleBearingImageButton.layer.cornerRadius = 4 - toggleBearingImageButton.clipsToBounds = true - toggleBearingImageButton.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) - - toggleBearingImageButton.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(toggleBearingImageButton) - - // Constraints - toggleBearingImageButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0).isActive = true - toggleBearingImageButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0).isActive = true - toggleBearingImageButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -70.0).isActive = true - - syncPuckAndButton() - } - - @objc func switchStyle(sender: UISegmentedControl) { - style = Style(rawValue: sender.selectedSegmentIndex) ?? . satelliteStreets - } - - func addStyleToggle() { - // Create a UISegmentedControl to toggle between map styles - styleToggle.selectedSegmentIndex = style.rawValue - styleToggle.addTarget(self, action: #selector(switchStyle(sender:)), for: .valueChanged) - styleToggle.translatesAutoresizingMaskIntoConstraints = false - - // set the segmented control as the title view - navigationItem.titleView = styleToggle - } -} - -// Create class which conforms to LocationConsumer, update the camera's centerCoordinate when a locationUpdate is received -public class CameraLocationConsumer: LocationConsumer { - weak var mapView: MapView? - - init(mapView: MapView) { - self.mapView = mapView - } - - public func locationUpdate(newLocation: Location) { - mapView?.camera.ease( - to: CameraOptions(center: newLocation.coordinate, zoom: 15), - duration: 1.3) - } -} diff --git a/Apps/Examples/Examples/AppDelegate.swift b/Apps/Examples/Examples/AppDelegate.swift deleted file mode 100644 index c982b9e03532..000000000000 --- a/Apps/Examples/Examples/AppDelegate.swift +++ /dev/null @@ -1,29 +0,0 @@ -import UIKit - -@main -//swiftlint:disable explicit_top_level_acl explicit_acl -class AppDelegate: UIResponder, UIApplicationDelegate { - - lazy var window: UIWindow? = { - return UIWindow(frame: UIScreen.main.bounds) - }() - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. - - let examplesTableViewController = ExampleTableViewController() - let navigationController = UINavigationController(rootViewController: examplesTableViewController) - - let appearance = UINavigationBar.appearance() - appearance.prefersLargeTitles = true - - if #available(iOS 13.0, *) { - appearance.scrollEdgeAppearance = UINavigationBarAppearance() - } - - window?.rootViewController = navigationController - window?.makeKeyAndVisible() - - return true - } -} diff --git a/Apps/Examples/Examples/Assets.xcassets/blue_marker_view.imageset/Contents.json b/Apps/Examples/Examples/Assets.xcassets/blue_marker_view.imageset/Contents.json deleted file mode 100644 index 9bde967b6692..000000000000 --- a/Apps/Examples/Examples/Assets.xcassets/blue_marker_view.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "blue_marker_view-1.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/Examples/Examples/Assets.xcassets/blue_marker_view.imageset/blue_marker_view-1.png b/Apps/Examples/Examples/Assets.xcassets/blue_marker_view.imageset/blue_marker_view-1.png deleted file mode 100644 index 68783a8b9309..000000000000 Binary files a/Apps/Examples/Examples/Assets.xcassets/blue_marker_view.imageset/blue_marker_view-1.png and /dev/null differ diff --git a/Apps/Examples/Examples/Assets.xcassets/custom_marker.imageset/Contents.json b/Apps/Examples/Examples/Assets.xcassets/custom_marker.imageset/Contents.json deleted file mode 100644 index c6443d46b037..000000000000 --- a/Apps/Examples/Examples/Assets.xcassets/custom_marker.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "custom-marker.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/Examples/Examples/Assets.xcassets/custom_marker.imageset/custom-marker.png b/Apps/Examples/Examples/Assets.xcassets/custom_marker.imageset/custom-marker.png deleted file mode 100644 index 500231e3c7d1..000000000000 Binary files a/Apps/Examples/Examples/Assets.xcassets/custom_marker.imageset/custom-marker.png and /dev/null differ diff --git a/Apps/Examples/Examples/Assets.xcassets/mapbox-logo.imageset/Contents.json b/Apps/Examples/Examples/Assets.xcassets/mapbox-logo.imageset/Contents.json deleted file mode 100644 index 640ce9655bcc..000000000000 --- a/Apps/Examples/Examples/Assets.xcassets/mapbox-logo.imageset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "mapbox-logo.pdf" - }, - { - "idiom" : "universal", - "filename" : "mapbox-logo-white.pdf", - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ] - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Apps/Examples/Examples/Assets.xcassets/red_marker.imageset/Contents.json b/Apps/Examples/Examples/Assets.xcassets/red_marker.imageset/Contents.json deleted file mode 100644 index 029fb59ace2d..000000000000 --- a/Apps/Examples/Examples/Assets.xcassets/red_marker.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "red_marker.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/Examples/Examples/Assets.xcassets/red_marker.imageset/red_marker.png b/Apps/Examples/Examples/Assets.xcassets/red_marker.imageset/red_marker.png deleted file mode 100644 index be782e1d4b3d..000000000000 Binary files a/Apps/Examples/Examples/Assets.xcassets/red_marker.imageset/red_marker.png and /dev/null differ diff --git a/Apps/Examples/Examples/Assets.xcassets/star.imageset/Contents.json b/Apps/Examples/Examples/Assets.xcassets/star.imageset/Contents.json deleted file mode 100644 index 8eebcd1242cc..000000000000 --- a/Apps/Examples/Examples/Assets.xcassets/star.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "star.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/Examples/Examples/Assets.xcassets/star.imageset/star.pdf b/Apps/Examples/Examples/Assets.xcassets/star.imageset/star.pdf deleted file mode 100644 index e3b51002e365..000000000000 Binary files a/Apps/Examples/Examples/Assets.xcassets/star.imageset/star.pdf and /dev/null differ diff --git a/Apps/Examples/Examples/Assets.xcassets/yellow_star.imageset/Contents.json b/Apps/Examples/Examples/Assets.xcassets/yellow_star.imageset/Contents.json deleted file mode 100644 index 75ae2ba4b2ee..000000000000 --- a/Apps/Examples/Examples/Assets.xcassets/yellow_star.imageset/Contents.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "images" : [ - { - "filename" : "yellow_star.png", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/Examples/Examples/Assets.xcassets/yellow_star.imageset/yellow_star.png b/Apps/Examples/Examples/Assets.xcassets/yellow_star.imageset/yellow_star.png deleted file mode 100644 index 525f96d9d8bb..000000000000 Binary files a/Apps/Examples/Examples/Assets.xcassets/yellow_star.imageset/yellow_star.png and /dev/null differ diff --git a/Apps/Examples/Examples/Controllers/ExampleTableViewController.swift b/Apps/Examples/Examples/Controllers/ExampleTableViewController.swift deleted file mode 100644 index 425a90e0e436..000000000000 --- a/Apps/Examples/Examples/Controllers/ExampleTableViewController.swift +++ /dev/null @@ -1,110 +0,0 @@ -import UIKit -import ObjectiveC - -//swiftlint:disable force_cast -final class ExampleTableViewController: UITableViewController { - - let allExamples = Examples.all - var filteredExamples = [Example]() - - var isFiltering: Bool { navigationItem.searchController?.isActive ?? false } - - override func viewDidLoad() { - super.viewDidLoad() - title = "Examples" - - let searchController = UISearchController(searchResultsController: nil) - searchController.searchResultsUpdater = self - searchController.searchBar.placeholder = "Search examples" - searchController.obscuresBackgroundDuringPresentation = false - navigationItem.searchController = searchController - navigationItem.hidesSearchBarWhenScrolling = false - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "reuseIdentifier") - } -} - -extension ExampleTableViewController: UISearchResultsUpdating { - func updateSearchResults(for searchController: UISearchController) { - if let searchText = searchController.searchBar.text { - filterContentForSearchText(searchText) - } - } -} - -extension ExampleTableViewController { - - override func numberOfSections(in tableView: UITableView) -> Int { - if isFiltering { - return 1 - } - - return allExamples.count - } - - override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - if isFiltering { - return nil - } - - return allExamples[section]["title"] as? String - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if isFiltering { - return filteredExamples.count - } - - let examples = allExamples[section]["examples"] as! [Example] - return examples.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - - var example: Example - - if isFiltering { - example = filteredExamples[indexPath.row] - } else { - let examples = allExamples[indexPath.section]["examples"] as! [Example] - example = examples[indexPath.row] - } - - let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "reuseIdentifier") - cell.textLabel?.text = example.title - cell.textLabel?.numberOfLines = 2 - cell.detailTextLabel?.text = example.description.trimmingCharacters(in: .whitespacesAndNewlines) - cell.detailTextLabel?.numberOfLines = 2 - if #available(iOS 13.0, *) { - cell.detailTextLabel?.textColor = .secondaryLabel - } else { - cell.detailTextLabel?.textColor = .lightGray - } - return cell - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - - var example: Example - - if isFiltering { - example = filteredExamples[indexPath.row] - } else { - let examples = allExamples[indexPath.section]["examples"] as! [Example] - example = examples[indexPath.row] - } - - let exampleViewController = example.makeViewController() - navigationController?.pushViewController(exampleViewController, animated: true) - } - - func filterContentForSearchText(_ searchText: String) { - let flatExamples = allExamples.flatMap { $0["examples"] as! [Example] } - if searchText.isEmpty { - filteredExamples = flatExamples - } else { - filteredExamples = flatExamples.filter { $0.title.lowercased().contains(searchText.lowercased()) } - } - - tableView.reloadData() - } -} diff --git a/Apps/Examples/Examples/Extensions/UIColor+Random.swift b/Apps/Examples/Examples/Extensions/UIColor+Random.swift deleted file mode 100644 index 58f092d559a2..000000000000 --- a/Apps/Examples/Examples/Extensions/UIColor+Random.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation -import UIKit - -extension UIColor { - static var random: UIColor { - UIColor(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1), alpha: 1) - } -} diff --git a/Apps/Examples/Examples/Extensions/UIViewController+Children.swift b/Apps/Examples/Examples/Extensions/UIViewController+Children.swift deleted file mode 100644 index 696d7891becb..000000000000 --- a/Apps/Examples/Examples/Extensions/UIViewController+Children.swift +++ /dev/null @@ -1,19 +0,0 @@ -import UIKit - -extension UIViewController { - internal func addChildViewController(_ child: UIViewController) { - addChild(child) - view.addSubview(child.view) - child.didMove(toParent: self) - } - - internal func remove() { - guard parent != nil else { - return - } - - willMove(toParent: nil) - view.removeFromSuperview() - removeFromParent() - } -} diff --git a/Apps/Examples/Examples/Info.plist b/Apps/Examples/Examples/Info.plist deleted file mode 100644 index 9642df9a3280..000000000000 --- a/Apps/Examples/Examples/Info.plist +++ /dev/null @@ -1,49 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - MBXAccessToken - - NSLocationWhenInUseUsageDescription - Get user location - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/Apps/Examples/Examples/Models/Examples.swift b/Apps/Examples/Examples/Models/Examples.swift deleted file mode 100644 index 946643dd5fbf..000000000000 --- a/Apps/Examples/Examples/Models/Examples.swift +++ /dev/null @@ -1,327 +0,0 @@ -import Foundation -import MapboxMaps - -// To add a new example, create a new `Example` struct -// and place it within the array for the category it belongs to below. Make sure -// the `fileName` is the same name of the new `UIViewController` -// you added in Examples/All Examples. See the README.md for more details. - -// swiftlint:disable:next type_body_length -struct Examples { - static let all = [ - [ - "title": "Getting started", - "examples": gettingStartedExamples - ], - [ - "title": "3D and Fill Extrusions", - "examples": threeDExamples - ], - [ - "title": "Annotations", - "examples": annotationExamples - ], - [ - "title": "Camera", - "examples": cameraExamples - ], - [ - "title": "Lab", - "examples": labExamples - ], - [ - "title": "Location", - "examples": locationExamples - ], - [ - "title": "Offline", - "examples": offlineExamples - ], - [ - "title": "Snapshot", - "examples": snapshotExamples - ], - [ - "title": "Style", - "examples": styleExamples - ], - [ - "title": "User Interaction", - "examples": userInteractionExamples - ], - [ - "title": "Accessibility", - "examples": accessibilityExamples - ], - [ "title": "Globe and Atmosphere", - "examples": globeAndAtmosphere - ], - [ - "title": "Experimental", - "examples": experimentalExamples - ] - ] - - // Examples that show how to get started with Mapbox, such as creating a basic map view or setting a style once. - static let gettingStartedExamples = [ - Example(title: "Display a map view", - description: """ - Create and display a map that uses the default Mapbox streets style. This example also shows how to update the starting camera for a map. - """, - type: BasicMapExample.self), - Example(title: "Use a custom map style", - description: "Set and use a custom map style URL.", - type: CustomStyleURLExample.self), - Example(title: "Display a map view using storyboard", - description: "Create and display a map using a storyboard.", - type: StoryboardMapViewExample.self), - Example(title: "Debug Map", - description: "This example shows how the map looks with different debug options", - type: DebugMapExample.self), - ] - - // Examples that show how to use 3D terrain or fill extrusions. - static let threeDExamples = [ - Example(title: "Show 3D terrain", - description: "Show realistic elevation by enabling terrain.", - type: TerrainExample.self), - Example(title: "SceneKit rendering on map", - description: "Use custom layer to render SceneKit model over terrain.", - type: SceneKitExample.self), - Example(title: "Display 3D buildings", - description: "Extrude the building layer in the Mapbox Light style using FillExtrusionLayer and set up the light position.", - type: BuildingExtrusionsExample.self), - Example(title: "Add a sky layer", - description: "Add a customizable sky layer to simulate natural lighting with a Terrain layer.", - type: SkyLayerExample.self), - Example(title: "Display a 3D model in a model layer", - description: "Showcase the usage of a 3D model layer.", - type: ModelLayerExample.self) - ] - - // Examples that focus on annotations. - static let annotationExamples = [ - Example(title: "Add a polygon annotation", - description: "Add a polygon annotation to the map.", - type: PolygonAnnotationExample.self), - Example(title: "Add a marker symbol", - description: "Add a blue teardrop-shaped marker image to a style and display it on the map using a SymbolLayer.", - type: AddOneMarkerSymbolExample.self), - Example(title: "Add Circle Annotations", - description: "Show circle annotations on a map", - type: CircleAnnotationExample.self), - Example(title: "Add Cluster Symbol Annotations", - description: "Show fire hydrants in Washington DC area in a cluster.", - type: SymbolClusteringExample.self), - Example(title: "Add markers to a map", - description: "Add markers that use different icons.", - type: AddMarkersSymbolExample.self), - Example(title: "Add Point Annotations", - description: "Show point annotations on a map", - type: CustomPointAnnotationExample.self), - Example(title: "Add Polylines Annotations", - description: "Show polyline annotations on a map.", - type: LineAnnotationExample.self), - Example(title: "Animate Marker Position", - description: "Animate updates to a marker/annotation's position.", - type: AnimatedMarkerExample.self), - Example(title: "Change icon size", - description: "Change icon size with Symbol layer.", - type: IconSizeChangeExample.self), - Example(title: "Draw multiple geometries", - description: "Draw multiple shapes on a map.", - type: MultipleGeometriesExample.self), - Example(title: "Use a map & annotations with SwiftUI", - description: "Use the UIViewRepresentable protocol to wrap a MapView in a SwiftUI view.", - type: SwiftUIExample.self), - Example(title: "View annotation with point annotation", - description: "Add view annotation to a point annotation", - type: ViewAnnotationWithPointAnnotationExample.self), - Example(title: "View annotations: basic example", - description: "Add view annotation on a map with a click.", - type: ViewAnnotationBasicExample.self), - Example(title: "View annotations: advanced example", - description: "Add view annotations anchored to a symbol layer feature.", - type: ViewAnnotationMarkerExample.self) - ] - - // Examples that focus on setting, animating, or otherwise changing the map's camera. - static let cameraExamples = [ - Example(title: "Use custom camera animations", - description: """ - Animate the map camera to a new position using camera animators. Individual camera properties such as zoom, bearing, and center coordinate can be animated independently. - """, - type: CameraAnimatorsExample.self), - Example(title: "Use camera animations", - description: "Use ease(to:) to animate updates to the camera's position.", - type: CameraAnimationExample.self), - - ] - - // Miscellaneous examples - public static let labExamples = [ - Example(title: "Resizable image", - description: "Add a resizable image with cap insets to a style.", - type: ResizableImageExample.self) - ] - - // Examples focused on displaying the user's location. - public static let locationExamples = [ - Example(title: "Display the user's location", - description: "Display the user's location on a map with the default user location puck.", - type: TrackingModeExample.self), - Example(title: "Customize the location puck", - description: "Use a different asset to represent the puck.", - type: Custom2DPuckExample.self), - Example(title: "Use a 3D model to show the user's location", - description: "A 3D model is used to represent the user's location.", - type: Custom3DPuckExample.self), - Example(title: "Add a custom location provider", - description: "Display the location puck at a custom location.", - type: CustomLocationProviderExample.self), - Example(title: "Simulate navigation", - description: "Simulate a driving trip from LA to San Francisco along a pre-defined route", - type: NavigationSimulatorExample.self), - ] - - // Examples that highlight using the Offline APIs. - static let offlineExamples = [ - Example(title: "Use OfflineManager and TileStore to download a region", - description: """ - Shows how to use OfflineManager and TileStore to download regions - for offline use. - - By default, users may download up to 750 tile packs for offline - use across all regions. If the limit is hit, any loadRegion call - will fail until excess regions are deleted. This limit is subject - to change. Please contact Mapbox if you require a higher limit. - Additional charges may apply. - """, - type: OfflineManagerExample.self), - Example(title: "Use OfflineRegionManager to download a region", - description: "Use the deprecated OfflineRegionManager to download regions for offline use.", - testTimeout: 120, - type: OfflineRegionManagerExample.self), - ] - - // Examples that show how to use the map's snapshotter. - static let snapshotExamples = [ - Example(title: "Create a static map snapshot", - description: """ - Create a static, non-interactive image of a map style with specified camera position. The resulting snapshot is provided as a `UIImage`. - The map on top is interactive. The bottom one is a static snapshot. - """, - type: SnapshotterExample.self), - Example(title: "Draw on a static snapshot with Core Graphics", - description: """ - Use the overlayHandler parameter to draw on top of a snapshot - using Core Graphhics APIs. - """, - type: SnapshotterCoreGraphicsExample.self), - ] - - // Examples that highlight how to set or modify the map's style and its contents. - static let styleExamples = [ - Example(title: "Display multiple icon images in a symbol layer", - description: """ - Add point data and several images to a style and use the switchCase and get expressions to choose which image to display at each point in a SymbolLayer based on a data property. - """, - type: DataDrivenSymbolsExample.self), - Example(title: "Change the position of a layer", - description: "Insert a specific layer above or below other layers.", - type: LayerPositionExample.self), - Example(title: "Cluster points within a layer", - description: "Create a circle layer from a geoJSON source and cluster the points from that source. The clusters will update as the map's camera changes.", - type: PointClusteringExample.self), - Example(title: "Animate a line layer", - description: "Animate updates to a line layer from a geoJSON source.", - type: AnimateGeoJSONLineExample.self), - Example(title: "Animate a style layer", - description: "Animate the position of a style layer by updating its source data.", - type: AnimateLayerExample.self), - Example(title: "Add external vector tiles", - description: "Add vector map tiles from an external source, using the {z}/{x}/{y} URL scheme.", - type: ExternalVectorSourceExample.self), - Example(title: "Use interpolate colors between zoom level", - description: """ - Use an interpolate expression to style the background layer color depending on zoom level. - """, - type: ColorExpressionExample.self), - Example(title: "Add a custom rendered layer", - description: "Add a custom rendered Metal layer.", - type: CustomLayerExample.self), - Example(title: "Add a line with a color gradient", - description: "Load a polyline to a style using GeoJSONSource, display it on a map using LineLayer, and style it with a rainbow color gradient.", - type: LineGradientExample.self), - Example(title: "Change the map's style", - description: "Switch between local and default Mapbox styles for the same map view.", - type: SwitchStylesExample.self), - Example(title: "Change the map's language", - description: "Switch between supported languages for Symbol Layers", - type: LocalizationExample.self), - Example(title: "Add animated weather data", - description: "Load a raster image to a style using ImageSource and display it on a map as animated weather data using RasterLayer.", - type: AnimateImageLayerExample.self), - Example(title: "Add a raster tile source", - description: "Add third-party raster tiles to a map.", - type: RasterTileSourceExample.self), - Example(title: "Show and hide layers", - description: "Allow the user to toggle the visibility of a CircleLayer and LineLayer on a map.", - type: ShowHideLayerExample.self), - Example(title: "Add live data", - description: "Update feature coordinates from a geoJSON source in real time.", - type: LiveDataExample.self), - Example(title: "Join data to vector geometry", - description: "Join local JSON data with vector tile geometries.", - type: DataJoinExample.self), - Example(title: "Use a distance expression", description: "Use a distance style expression to show features within a specific radius.", type: DistanceExpressionExample.self) - ] - - // Examples that show use cases related to user interaction with the map. - static let userInteractionExamples = [ - Example(title: "Find features at a point", - description: "Query the map for rendered features belonging to a specific layer.", - type: FeaturesAtPointExample.self), - Example(title: "Use Feature State", - description: "Manipulate map styling with feature states and expressions.", - type: FeatureStateExample.self), - Example(title: "Restrict the map's coordinate bounds", - description: "Prevent the map from panning outside the specified coordinate bounds.", - type: RestrictCoordinateBoundsExample.self), - Example(title: "Add an interactive clustered layer", - description: "Display an alert controller after selecting a feature.", - type: SymbolClusteringExample.self), - ] - - static let accessibilityExamples = [ - Example(title: "Access map features using VoiceOver", - description: "Use VoiceOver to highlight annotations and hear their associated features.", - type: VoiceOverAccessibilityExample.self), - ] - - // Examples that display maps using the globe projection - static let globeAndAtmosphere = [ - Example(title: "Display a globe", - description: "Create a map using the globe projection.", - type: GlobeExample.self), - Example(title: "Fly-to camera animation", - description: "Smoothly interpolate between locations with the fly-to animation.", - type: GlobeFlyToExample.self), - Example(title: "Create a rotating globe", - description: "Display your map as an interactive, rotating globe.", - type: SpinningGlobeExample.self), - Example(title: "Visualize data as a heatmap", - description: "Display your heatmap using the globe projection.", - type: HeatmapLayerGlobeExample.self) - ] - - // Examples that uses experimental APIs - static let experimentalExamples = [ - Example(title: "Viewport", - description: "Viewport camera showcase", - type: ViewportExample.self), - Example(title: "Advanced Viewport Gestures", - description: "Viewport configured to allow gestures", - type: AdvancedViewportGesturesExample.self), - ] -} diff --git a/Apps/Examples/Examples/Models/SimulatedLocationProvider.swift b/Apps/Examples/Examples/Models/SimulatedLocationProvider.swift deleted file mode 100644 index ddef678590d9..000000000000 --- a/Apps/Examples/Examples/Models/SimulatedLocationProvider.swift +++ /dev/null @@ -1,50 +0,0 @@ -import MapboxMaps - -final class SimulatedLocationProvider: LocationProvider { - var locationProviderOptions = LocationOptions() - - let authorizationStatus = CLAuthorizationStatus.authorizedAlways - - let accuracyAuthorization = CLAccuracyAuthorization.fullAccuracy - - let heading: CLHeading? = nil - - var headingOrientation = CLDeviceOrientation.portrait - - private let currentLocation: CLLocation - - private weak var delegate: LocationProviderDelegate? - - init(currentLocation: CLLocation) { - self.currentLocation = currentLocation - } - - func setDelegate(_ delegate: LocationProviderDelegate) { - self.delegate = delegate - } - - func startUpdatingLocation() { - delegate?.locationProvider(self, didUpdateLocations: [currentLocation]) - } - - func requestAlwaysAuthorization() { - } - - func requestWhenInUseAuthorization() { - } - - func requestTemporaryFullAccuracyAuthorization(withPurposeKey purposeKey: String) { - } - - func stopUpdatingLocation() { - } - - func startUpdatingHeading() { - } - - func stopUpdatingHeading() { - } - - func dismissHeadingCalibrationDisplay() { - } -} diff --git a/Apps/Examples/Examples/StoryboardMapViewExample.storyboard b/Apps/Examples/Examples/StoryboardMapViewExample.storyboard deleted file mode 100644 index d1ba97f824d8..000000000000 --- a/Apps/Examples/Examples/StoryboardMapViewExample.storyboard +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/Examples/ExamplesTests/ExamplesTests.swift b/Apps/Examples/ExamplesTests/ExamplesTests.swift deleted file mode 100644 index ab9b4b8500ff..000000000000 --- a/Apps/Examples/ExamplesTests/ExamplesTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -import XCTest -@testable import Examples - -//swiftlint:disable force_cast -class ExamplesTests: XCTestCase { - - func testExampleClassExists() throws { - - for category in Examples.all { - for example in category["examples"] as! [Example] { - // Check view controller can be extrapolated from the example file name. - XCTAssert(example.type is UIViewController.Type) - - // Check if the example file name has the word "Example" appended to the end. - let string = "\(example.type)" - let indexEnd = string.index(string.endIndex, offsetBy: -("Example".count)) - XCTAssertTrue(string[indexEnd...] == "Example") - - // Check that examples have descriptions. - XCTAssertFalse(example.description.isEmpty, "Example '\(example.type)' should have a description.") - - // Check that examples have titles. - XCTAssertFalse(example.title.isEmpty, "Example '\(example.type)' should have a title.") - - // Check that example titles do not end in punctuation - if let last = example.title.last { - XCTAssertTrue(CharacterSet(charactersIn: String(last)).isDisjoint(with: .punctuationCharacters), - "Title for example '\(example.type)' should not end with punctuation.") - } - } - } - } - -} diff --git a/Apps/Examples/ExamplesTests/Info.plist b/Apps/Examples/ExamplesTests/Info.plist deleted file mode 100644 index 64d65ca49577..000000000000 --- a/Apps/Examples/ExamplesTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Apps/Examples/ExamplesTests/TestableExampleTests.swift b/Apps/Examples/ExamplesTests/TestableExampleTests.swift deleted file mode 100644 index 36bacd19a8f4..000000000000 --- a/Apps/Examples/ExamplesTests/TestableExampleTests.swift +++ /dev/null @@ -1,72 +0,0 @@ -import XCTest -import ObjectiveC.runtime -@testable import Examples - -//swiftlint:disable force_cast -extension UINavigationController { - func popToRootViewController(animated: Bool, completion: @escaping () -> Void) { - popToRootViewController(animated: animated) - - if animated, let coordinator = transitionCoordinator { - coordinator.animate(alongsideTransition: nil) { _ in - completion() - } - } else { - completion() - } - } -} - -class TestableExampleTests: XCTestCase { - private var example: Example! - - override class var defaultTestSuite: XCTestSuite { - let newTestSuite = XCTestSuite(forTestCaseClass: TestableExampleTests.self) - - guard let method = class_getInstanceMethod(Self.self, #selector(runExample)) else { - fatalError() - } - - let existingImpl = method_getImplementation(method) - - for category in Examples.all { - for example in category["examples"] as! [Example] { - // Add a method for this test, but using the same implementation - let selectorName = "test\(example.type)" - let testSelector = Selector((selectorName)) - class_addMethod(Self.self, testSelector, existingImpl, "v@:f") - - let test = TestableExampleTests(selector: testSelector) - test.example = example - newTestSuite.addTest(test) - } - } - return newTestSuite - } - - @objc private func runExample() { - guard let navigationController = UIApplication.shared.windows.first?.rootViewController as? UINavigationController else { - XCTFail("Root controller is not a UINavigationController") - return - } - - let exampleViewController = example.makeViewController() - - // Wait for the "finish" notification - let expectation = XCTNSNotificationExpectation(name: Example.finishNotificationName, object: exampleViewController) - - navigationController.pushViewController(exampleViewController, animated: false) - - let result = XCTWaiter().wait(for: [expectation], timeout: example.testTimeout) - switch result { - case .completed: - break - case .timedOut: - XCTFail("Example: \(example.title) timed out. Don't forget to call finish().") - default: - XCTFail("Expectation failed with \(result)") - } - - navigationController.popToRootViewController(animated: false) - } -} diff --git a/Apps/Examples/ExamplesUITests/ExamplesUITests.swift b/Apps/Examples/ExamplesUITests/ExamplesUITests.swift deleted file mode 100644 index 15d5dd7fd513..000000000000 --- a/Apps/Examples/ExamplesUITests/ExamplesUITests.swift +++ /dev/null @@ -1,29 +0,0 @@ -import XCTest - -class ExamplesUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testEveryExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Tap every example cell in the table view, and then dismiss the example. - for cell in app.tables.element(boundBy: 0).cells.allElementsBoundByIndex { - // Open the example - cell.tap() - // Navigate back to the table view - app.navigationBars.buttons.element(boundBy: 0).tap() - } - } -} diff --git a/Apps/Examples/ExamplesUITests/Info.plist b/Apps/Examples/ExamplesUITests/Info.plist deleted file mode 100644 index 64d65ca49577..000000000000 --- a/Apps/Examples/ExamplesUITests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Apps/Examples/README.md b/Apps/Examples/README.md deleted file mode 100644 index 9d8e7708242e..000000000000 --- a/Apps/Examples/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# Maps SDK Examples - -Welcome! This sample application is intended to showcase different ways you can -use Mapbox Maps SDK v10 for iOS, which now runs on -[Metal](https://developer.apple.com/metal/). - -![screenshots](readme-screenshots.png) - -## Running this project - -### Requirements - -* Xcode 12 -* A Mapbox account - -### Project Setup - -The Examples project needs to be able to access Mapbox services to install -dependencies and load maps. Follow the instructions in the docs to -[Configure credentials](https://docs.mapbox.com/ios/maps/guides/install/). - -When you get to the "Configure your public token" step, you can optionally place -your public token in the file `~/.mapbox` instead of modifying the Example app's -`Info.plist`. A build phase will read the public token from that file and -populate it in the `Info.plist` of the built product. This helps to ensure that -your public token does not end up in any of your commits. - -Next, open `Apps/Apps.xcworkspace` and select the Examples scheme to build and -run the Examples app. - -## Contributing to Examples - -### Guidelines - -Before you begin creating a new example, first make sure there isn't an already -similar one. We value quality over quantity, we welcome alterations to existing -examples if that makes more sense than adding a new one. - -If you don't see a desired use case well explained by an example, you can -contribute to a new one. New examples should strive to be: - -* **Concise.** An example that is hundreds of lines long can be hard to quickly - understand. -* **Self-contained.** Examples in this project shouldn't rely on any third party - dependencies. -* **Useful to others.** An example's purpose should be broad enough to help out - many other developers. - -### Adding New Examples - -To add a new example, go through the following steps: - -1. Create a new `UIViewController` in the `Example/All Examples`. The file name - should be the name of your example appended by the word `Example`, typed in - the [camel case](https://en.wikipedia.org/wiki/Camel_case) style. For - instance, if we're adding a new example to show how to download an offline - map, the example file would be called `OfflineMapExample`. - - Within this new `UIViewController` file, paste the following boilerplate code - to import the dependencies needed to make the example work: - - ```swift - import UIKit - import MapboxMaps - import MapboxCoreMaps - import MapboxCommon - - @objc(OfflineMapExample) // This must refer to the file name - - public class OfflineMapExample: UIViewController, ExampleProtocol { - - override public func viewDidLoad() { - super.viewDidLoad() - } - - // Allows the delegate to receive information about map events. - mapView.on(.mapLoaded) { [weak self] _ in - guard let self = self else { return } - self.finish() // Needed for internal testing purposes. - } - } - ``` - -2. In `Models/Examples.swift`, add a new `Example` entry into the `Examples.all` - array, as illustrated below. - - ```swift - Example(title: "Download an offline map", - description: "Download a map for use without a network connection.", - type: "OfflineMapExample.self") - ``` - - Note: The title you supply here will be displayed in the navigation bar of the - app, so ensure to keep the title short. You can always highlight more about the - example in the description, which appears as an alert when the user taps the - `Info` button on the rop right corner of the navigation bar. - -3. At this point, the example file has been fully configured to work with the - project. You can now customize the code in your example file! Once you've - finished, **[submit a pull request here](https://github.com/Mapbox/mapbox-maps-ios/compare/main...main?expand=1)**. - We look forward to seeing your contributions! diff --git a/Apps/Examples/readme-screenshots.png b/Apps/Examples/readme-screenshots.png deleted file mode 100644 index 2c36db02a5e7..000000000000 Binary files a/Apps/Examples/readme-screenshots.png and /dev/null differ diff --git a/Apps/StressTest/StressTest.xcodeproj/project.pbxproj b/Apps/StressTest/StressTest.xcodeproj/project.pbxproj deleted file mode 100644 index b9dfd295baf4..000000000000 --- a/Apps/StressTest/StressTest.xcodeproj/project.pbxproj +++ /dev/null @@ -1,641 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 52; - objects = { - -/* Begin PBXBuildFile section */ - B54B7F232601021F00B45174 /* MapboxMaps in Frameworks */ = {isa = PBXBuildFile; productRef = B54B7F222601021F00B45174 /* MapboxMaps */; }; - CADCF8B1258551390065C51B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADCF8B0258551390065C51B /* AppDelegate.swift */; }; - CADCF8B3258551390065C51B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADCF8B2258551390065C51B /* SceneDelegate.swift */; }; - CADCF8B5258551390065C51B /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADCF8B4258551390065C51B /* ViewController.swift */; }; - CADCF8B8258551390065C51B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CADCF8B6258551390065C51B /* Main.storyboard */; }; - CADCF8BA2585513B0065C51B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CADCF8B92585513B0065C51B /* Assets.xcassets */; }; - CADCF8BD2585513B0065C51B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CADCF8BB2585513B0065C51B /* LaunchScreen.storyboard */; }; - CADCF8C82585513C0065C51B /* StressTestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADCF8C72585513C0065C51B /* StressTestTests.swift */; }; - CADCF8D32585513C0065C51B /* StressTestUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CADCF8D22585513C0065C51B /* StressTestUITests.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - CADCF8C42585513C0065C51B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = CADCF8A5258551390065C51B /* Project object */; - proxyType = 1; - remoteGlobalIDString = CADCF8AC258551390065C51B; - remoteInfo = StressTest; - }; - CADCF8CF2585513C0065C51B /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = CADCF8A5258551390065C51B /* Project object */; - proxyType = 1; - remoteGlobalIDString = CADCF8AC258551390065C51B; - remoteInfo = StressTest; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - CADCF8F0258551630065C51B /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - CADCF8AD258551390065C51B /* StressTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StressTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; - CADCF8B0258551390065C51B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - CADCF8B2258551390065C51B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - CADCF8B4258551390065C51B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - CADCF8B7258551390065C51B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - CADCF8B92585513B0065C51B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - CADCF8BC2585513B0065C51B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - CADCF8BE2585513B0065C51B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CADCF8C32585513C0065C51B /* StressTestTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StressTestTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - CADCF8C72585513C0065C51B /* StressTestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StressTestTests.swift; sourceTree = ""; }; - CADCF8C92585513C0065C51B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CADCF8CE2585513C0065C51B /* StressTestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StressTestUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - CADCF8D22585513C0065C51B /* StressTestUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StressTestUITests.swift; sourceTree = ""; }; - CADCF8D42585513C0065C51B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CADCF8FD258551D40065C51B /* unitTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = unitTests.xcconfig; sourceTree = ""; }; - CADCF8FF258551D40065C51B /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - CADCF8AA258551390065C51B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B54B7F232601021F00B45174 /* MapboxMaps in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - CADCF8C02585513C0065C51B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - CADCF8CB2585513C0065C51B /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - CADCF8A4258551390065C51B = { - isa = PBXGroup; - children = ( - CADCF8FB258551D40065C51B /* Configurations */, - CADCF8AF258551390065C51B /* StressTest */, - CADCF8C62585513C0065C51B /* StressTestTests */, - CADCF8D12585513C0065C51B /* StressTestUITests */, - CADCF8AE258551390065C51B /* Products */, - ); - sourceTree = ""; - }; - CADCF8AE258551390065C51B /* Products */ = { - isa = PBXGroup; - children = ( - CADCF8AD258551390065C51B /* StressTest.app */, - CADCF8C32585513C0065C51B /* StressTestTests.xctest */, - CADCF8CE2585513C0065C51B /* StressTestUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - CADCF8AF258551390065C51B /* StressTest */ = { - isa = PBXGroup; - children = ( - CADCF8B0258551390065C51B /* AppDelegate.swift */, - CADCF8B2258551390065C51B /* SceneDelegate.swift */, - CADCF8B4258551390065C51B /* ViewController.swift */, - CADCF8B6258551390065C51B /* Main.storyboard */, - CADCF8B92585513B0065C51B /* Assets.xcassets */, - CADCF8BB2585513B0065C51B /* LaunchScreen.storyboard */, - CADCF8BE2585513B0065C51B /* Info.plist */, - ); - path = StressTest; - sourceTree = ""; - }; - CADCF8C62585513C0065C51B /* StressTestTests */ = { - isa = PBXGroup; - children = ( - CADCF8C72585513C0065C51B /* StressTestTests.swift */, - CADCF8C92585513C0065C51B /* Info.plist */, - ); - path = StressTestTests; - sourceTree = ""; - }; - CADCF8D12585513C0065C51B /* StressTestUITests */ = { - isa = PBXGroup; - children = ( - CADCF8D22585513C0065C51B /* StressTestUITests.swift */, - CADCF8D42585513C0065C51B /* Info.plist */, - ); - path = StressTestUITests; - sourceTree = ""; - }; - CADCF8FB258551D40065C51B /* Configurations */ = { - isa = PBXGroup; - children = ( - CADCF8FD258551D40065C51B /* unitTests.xcconfig */, - CADCF8FF258551D40065C51B /* base.xcconfig */, - ); - name = Configurations; - path = ../Configurations; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - CADCF8AC258551390065C51B /* StressTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = CADCF8D72585513C0065C51B /* Build configuration list for PBXNativeTarget "StressTest" */; - buildPhases = ( - CADCF8A9258551390065C51B /* Sources */, - CADCF8AA258551390065C51B /* Frameworks */, - CADCF8AB258551390065C51B /* Resources */, - CADCF8F0258551630065C51B /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = StressTest; - packageProductDependencies = ( - B54B7F222601021F00B45174 /* MapboxMaps */, - ); - productName = StressTest; - productReference = CADCF8AD258551390065C51B /* StressTest.app */; - productType = "com.apple.product-type.application"; - }; - CADCF8C22585513C0065C51B /* StressTestTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = CADCF8DA2585513C0065C51B /* Build configuration list for PBXNativeTarget "StressTestTests" */; - buildPhases = ( - CADCF8BF2585513C0065C51B /* Sources */, - CADCF8C02585513C0065C51B /* Frameworks */, - CADCF8C12585513C0065C51B /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - CADCF8C52585513C0065C51B /* PBXTargetDependency */, - ); - name = StressTestTests; - productName = StressTestTests; - productReference = CADCF8C32585513C0065C51B /* StressTestTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - CADCF8CD2585513C0065C51B /* StressTestUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = CADCF8DD2585513C0065C51B /* Build configuration list for PBXNativeTarget "StressTestUITests" */; - buildPhases = ( - CADCF8CA2585513C0065C51B /* Sources */, - CADCF8CB2585513C0065C51B /* Frameworks */, - CADCF8CC2585513C0065C51B /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - CADCF8D02585513C0065C51B /* PBXTargetDependency */, - ); - name = StressTestUITests; - productName = StressTestUITests; - productReference = CADCF8CE2585513C0065C51B /* StressTestUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - CADCF8A5258551390065C51B /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1210; - LastUpgradeCheck = 1210; - TargetAttributes = { - CADCF8AC258551390065C51B = { - CreatedOnToolsVersion = 12.1.1; - }; - CADCF8C22585513C0065C51B = { - CreatedOnToolsVersion = 12.1.1; - TestTargetID = CADCF8AC258551390065C51B; - }; - CADCF8CD2585513C0065C51B = { - CreatedOnToolsVersion = 12.1.1; - TestTargetID = CADCF8AC258551390065C51B; - }; - }; - }; - buildConfigurationList = CADCF8A8258551390065C51B /* Build configuration list for PBXProject "StressTest" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = CADCF8A4258551390065C51B; - productRefGroup = CADCF8AE258551390065C51B /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - CADCF8AC258551390065C51B /* StressTest */, - CADCF8C22585513C0065C51B /* StressTestTests */, - CADCF8CD2585513C0065C51B /* StressTestUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - CADCF8AB258551390065C51B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CADCF8BD2585513B0065C51B /* LaunchScreen.storyboard in Resources */, - CADCF8BA2585513B0065C51B /* Assets.xcassets in Resources */, - CADCF8B8258551390065C51B /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - CADCF8C12585513C0065C51B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - CADCF8CC2585513C0065C51B /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - CADCF8A9258551390065C51B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CADCF8B5258551390065C51B /* ViewController.swift in Sources */, - CADCF8B1258551390065C51B /* AppDelegate.swift in Sources */, - CADCF8B3258551390065C51B /* SceneDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - CADCF8BF2585513C0065C51B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CADCF8C82585513C0065C51B /* StressTestTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - CADCF8CA2585513C0065C51B /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CADCF8D32585513C0065C51B /* StressTestUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - CADCF8C52585513C0065C51B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = CADCF8AC258551390065C51B /* StressTest */; - targetProxy = CADCF8C42585513C0065C51B /* PBXContainerItemProxy */; - }; - CADCF8D02585513C0065C51B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = CADCF8AC258551390065C51B /* StressTest */; - targetProxy = CADCF8CF2585513C0065C51B /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - CADCF8B6258551390065C51B /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - CADCF8B7258551390065C51B /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - CADCF8BB2585513B0065C51B /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - CADCF8BC2585513B0065C51B /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - CADCF8D52585513C0065C51B /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CADCF8FF258551D40065C51B /* base.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.2; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - CADCF8D62585513C0065C51B /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = CADCF8FF258551D40065C51B /* base.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.2; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - CADCF8D82585513C0065C51B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = StressTest/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.StressTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - CADCF8D92585513C0065C51B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = StressTest/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.StressTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - CADCF8DB2585513C0065C51B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = StressTestTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.StressTestTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StressTest.app/StressTest"; - }; - name = Debug; - }; - CADCF8DC2585513C0065C51B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = StressTestTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.2; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.StressTestTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/StressTest.app/StressTest"; - }; - name = Release; - }; - CADCF8DE2585513C0065C51B /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = StressTestUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.StressTestUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = StressTest; - }; - name = Debug; - }; - CADCF8DF2585513C0065C51B /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = GJZR2MEM28; - INFOPLIST_FILE = StressTestUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.StressTestUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = StressTest; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - CADCF8A8258551390065C51B /* Build configuration list for PBXProject "StressTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CADCF8D52585513C0065C51B /* Debug */, - CADCF8D62585513C0065C51B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - CADCF8D72585513C0065C51B /* Build configuration list for PBXNativeTarget "StressTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CADCF8D82585513C0065C51B /* Debug */, - CADCF8D92585513C0065C51B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - CADCF8DA2585513C0065C51B /* Build configuration list for PBXNativeTarget "StressTestTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CADCF8DB2585513C0065C51B /* Debug */, - CADCF8DC2585513C0065C51B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - CADCF8DD2585513C0065C51B /* Build configuration list for PBXNativeTarget "StressTestUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - CADCF8DE2585513C0065C51B /* Debug */, - CADCF8DF2585513C0065C51B /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCSwiftPackageProductDependency section */ - B54B7F222601021F00B45174 /* MapboxMaps */ = { - isa = XCSwiftPackageProductDependency; - productName = MapboxMaps; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = CADCF8A5258551390065C51B /* Project object */; -} diff --git a/Apps/StressTest/StressTest.xcodeproj/xcshareddata/xcschemes/StressTest.xcscheme b/Apps/StressTest/StressTest.xcodeproj/xcshareddata/xcschemes/StressTest.xcscheme deleted file mode 100644 index 67992e5bc543..000000000000 --- a/Apps/StressTest/StressTest.xcodeproj/xcshareddata/xcschemes/StressTest.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/StressTest/StressTest/AppDelegate.swift b/Apps/StressTest/StressTest/AppDelegate.swift deleted file mode 100644 index a7114e276d1c..000000000000 --- a/Apps/StressTest/StressTest/AppDelegate.swift +++ /dev/null @@ -1,46 +0,0 @@ -import UIKit -import MetricKit - -@main -class AppDelegate: UIResponder, UIApplicationDelegate { - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - MXMetricManager.shared.add(self) - return true - } - - func applicationWillTerminate(_ application: UIApplication) { - MXMetricManager.shared.remove(self) - } -} - -extension AppDelegate: MXMetricManagerSubscriber { - func didReceive(_ payloads: [MXMetricPayload]) { - - do { - let data = try NSKeyedArchiver.archivedData(withRootObject: payloads, requiringSecureCoding: true) - - // Save to disk - var cacheDirectoryURL = try FileManager.default.url(for: .applicationSupportDirectory, - in: .userDomainMask, - appropriateFor: nil, - create: true) - - cacheDirectoryURL = cacheDirectoryURL.appendingPathComponent("MXMetricPayloads") - - try FileManager.default.createDirectory(at: cacheDirectoryURL, - withIntermediateDirectories: true, - attributes: nil) - - // Append file name - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yMMdd-HHmm" - cacheDirectoryURL.appendPathComponent(dateFormatter.string(from: Date())) - try data.write(to: cacheDirectoryURL) - - print("Wrote metric data to \(cacheDirectoryURL)") - - } catch { - print("Payload error: \(error)") - } - } -} diff --git a/Apps/StressTest/StressTest/Assets.xcassets/AccentColor.colorset/Contents.json b/Apps/StressTest/StressTest/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb8789700816..000000000000 --- a/Apps/StressTest/StressTest/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/StressTest/StressTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/Apps/StressTest/StressTest/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 9221b9bb1a35..000000000000 --- a/Apps/StressTest/StressTest/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/StressTest/StressTest/Assets.xcassets/custom_marker.imageset/Contents.json b/Apps/StressTest/StressTest/Assets.xcassets/custom_marker.imageset/Contents.json deleted file mode 100644 index c6443d46b037..000000000000 --- a/Apps/StressTest/StressTest/Assets.xcassets/custom_marker.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "custom-marker.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Apps/StressTest/StressTest/Assets.xcassets/custom_marker.imageset/custom-marker.png b/Apps/StressTest/StressTest/Assets.xcassets/custom_marker.imageset/custom-marker.png deleted file mode 100644 index 500231e3c7d1..000000000000 Binary files a/Apps/StressTest/StressTest/Assets.xcassets/custom_marker.imageset/custom-marker.png and /dev/null differ diff --git a/Apps/StressTest/StressTest/Base.lproj/LaunchScreen.storyboard b/Apps/StressTest/StressTest/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 865e9329f376..000000000000 --- a/Apps/StressTest/StressTest/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/StressTest/StressTest/Base.lproj/Main.storyboard b/Apps/StressTest/StressTest/Base.lproj/Main.storyboard deleted file mode 100644 index 25a763858ecd..000000000000 --- a/Apps/StressTest/StressTest/Base.lproj/Main.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Apps/StressTest/StressTest/Info.plist b/Apps/StressTest/StressTest/Info.plist deleted file mode 100644 index 4552bb712ab5..000000000000 --- a/Apps/StressTest/StressTest/Info.plist +++ /dev/null @@ -1,68 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - LSRequiresIPhoneOS - - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - UISceneStoryboardFile - Main - - - - - UIApplicationSupportsIndirectInputEvents - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - MBXAccessToken - $(MAPBOX_ACCESS_TOKEN) - - diff --git a/Apps/StressTest/StressTest/SceneDelegate.swift b/Apps/StressTest/StressTest/SceneDelegate.swift deleted file mode 100644 index e0be6e9cfe19..000000000000 --- a/Apps/StressTest/StressTest/SceneDelegate.swift +++ /dev/null @@ -1,5 +0,0 @@ -import UIKit - -class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? -} diff --git a/Apps/StressTest/StressTest/ViewController.swift b/Apps/StressTest/StressTest/ViewController.swift deleted file mode 100644 index d7f06d15d3f7..000000000000 --- a/Apps/StressTest/StressTest/ViewController.swift +++ /dev/null @@ -1,319 +0,0 @@ -import UIKit -import MapboxMaps -import MetricKit - -extension CLLocationCoordinate2D { - init(_ lat: Double, _ lng: Double) { - self.init(latitude: lat, longitude: lng) - } - - static let Boston = CLLocationCoordinate2D(42.3601, -71.0589) - static let SanFrancisco = CLLocationCoordinate2D(37.7749, -122.4194) - static let MexicoCity = CLLocationCoordinate2D(19.4326, -99.1332) - static let London = CLLocationCoordinate2D(51.5074, -0.1278) - static let Madrid = CLLocationCoordinate2D(40.4168, -3.7038) - static let Minsk = CLLocationCoordinate2D(53.9006, 27.5590) - static let Moscow = CLLocationCoordinate2D(55.7558, 37.6173) - static let HongKong = CLLocationCoordinate2D(22.3193, 114.1694) - static let Tokyo = CLLocationCoordinate2D(35.6762, 139.6503) - static let Melbourne = CLLocationCoordinate2D(-37.8136, 144.9631) - static let BuenosAires = CLLocationCoordinate2D(-34.6037, -58.3816) - static let Reykjavik = CLLocationCoordinate2D(64.1466, -21.9426) -} - -class ViewController: UIViewController { - var mapView: MapView! - - lazy var pointAnnotationManager: PointAnnotationManager? = { - mapView?.annotations.makePointAnnotationManager() - }() - - lazy var lineAnnotationManager: PolylineAnnotationManager? = { - mapView?.annotations.makePolylineAnnotationManager() - }() - - var startTime: TimeInterval = 0 - var endTime: TimeInterval = 0 - - // Fly through these coordinates - var coordStep = 0 - var coords: [CLLocationCoordinate2D] = [ - .London, - .Madrid, - .Minsk, - .Moscow, - .HongKong, - .Tokyo, - .Melbourne, - .BuenosAires, - .MexicoCity, - .SanFrancisco, - .Boston, - .Reykjavik - ] - - // Cycle each fly-to set through these styles - var styleStep = 0 - var styles: [(StyleURI, String?)] = { - return [ - (.streets, "land"), - (.outdoors, "land"), - (.dark, "land"), - (.light, "land"), - (.satellite, nil), - (.satelliteStreets, nil), - (StyleURI(rawValue: "mapbox://styles/mapbox-map-design/ck40ed2go56yr1cp7bbsalr1c")!, "land"), - (StyleURI(rawValue: "mapbox://styles/examples/cke97f49z5rlg19l310b7uu7j")!, nil), - ] - }() - - var step = 0 - - // How many steps to take before finishing - var maxSteps: Int { - 2 * styles.count * coords.count - } - - var annotations: [PointAnnotation] = [] - var color: Any? - var mapInitOptions: MapInitOptions! - var snapshotter: Snapshotter? - var logHandle: OSLog! - - override func viewDidLoad() { - super.viewDidLoad() - - annotations.reserveCapacity(100) - - // Do any additional setup after loading the view. - mapInitOptions = MapInitOptions() - mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) - view.addSubview(mapView) - NSLayoutConstraint.activate([ - mapView.leftAnchor.constraint(equalTo: view.leftAnchor), - mapView.topAnchor.constraint(equalTo: view.topAnchor), - mapView.rightAnchor.constraint(equalTo: view.rightAnchor), - mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor) - ]) - - logHandle = MXMetricManager.makeLogHandle(category: "StressTest") - start() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - // Set initial conditions - mapView.mapboxMap.onNext(.styleLoaded) { _ in - self.flyToNextCoordinate() - } - - mapView.mapboxMap.style.uri = styles[styleStep].0 - } - - func flyToNextCoordinate() { - - // Check to see if we should cycle through the coordinates - // and styles - if coordStep >= coords.count { - coordStep = 0 - styleStep += 1 - - if styleStep >= styles.count { - styleStep = 0 - } - - removeAnnotations() - - // Change the style - mapView.mapboxMap.style.uri = styles[styleStep].0 - print("Changing style to \(styles[styleStep].0)") - - return - } - - let dest = coords[coordStep] - - // Every 2 steps, add or remove annotations - if step % 2 == 0 { - if annotations.isEmpty { - addAnnotations(around: dest) - } else { - removeAnnotations() - } - } - - // Every 5 steps, toggle color expressions - if step % 5 == 0 { - if color == nil { - pushColorExpression() - } else { - popColorExpression() - } - } - - snapshotter = nil - - flyTo(end: dest) { - // At the end of the fly-to, use the snapshotter before moving on - self.takeSnapshot { - DispatchQueue.main.async(execute: self.nextStep) - } - } - } - - func nextStep() { - coordStep += 1 - step += 1 - - if step < maxSteps { - flyToNextCoordinate() - } else { - finish() - } - } - - func start() { - mxSignpost(.begin, log: logHandle, name: "StressTest") - startTime = CACurrentMediaTime() - } - - func finish() { - endTime = CACurrentMediaTime() - mxSignpost(.end, log: logHandle, name: "StressTest") - - let totalSeconds = endTime - startTime - let message = "Time taken: \(totalSeconds)" - print("Stress-test completed: \(message)") - - let label = UILabel() - label.text = message - label.textColor = .white - label.backgroundColor = .red - label.sizeToFit() - view.addSubview(label) - - DispatchQueue.main.asyncAfter(deadline: .now()+10) { - self.mapView.removeFromSuperview() - self.mapView = nil - } - } - - func flyTo(end: CLLocationCoordinate2D, completion: @escaping () -> Void) { - let startOptions = mapView.cameraState - let start = startOptions.center - - var lineAnnotation = PolylineAnnotation(lineCoordinates: [start, end]) - lineAnnotation.lineColor = StyleColor(.red) - - // Add the annotation to the map. - print("Adding line annotation") - lineAnnotationManager?.annotations = [lineAnnotation] - - let endOptions = CameraOptions(center: end, zoom: 17) - - var cancelableAnimator: Cancelable? - cancelableAnimator = mapView.camera.fly(to: endOptions) { _ in - print("Removing line annotation for animator \(String(describing: cancelableAnimator))") - self.lineAnnotationManager?.annotations = [] - cancelableAnimator = nil - completion() - } - } - - func removeAnnotations() { - print("Removing \(annotations.count) annotations") - annotations = [] - pointAnnotationManager?.annotations = [] - } - - func addAnnotations(around coord: CLLocationCoordinate2D) { - for lat in stride(from: coord.latitude-0.25, to: coord.latitude+0.25, by: 0.05) { - for lng in stride(from: coord.longitude-0.25, to: coord.longitude+0.25, by: 0.05) { - var pointAnnotation = PointAnnotation(coordinate: CLLocationCoordinate2D(lat, lng)) - pointAnnotation.image = .init(image: UIImage(named: "custom_marker")!, name: "custom_marker") - annotations.append(pointAnnotation) - } - } - - print("Adding \(annotations.count) annotations") - pointAnnotationManager?.annotations = annotations - } - - func pushColorExpression() { - guard let land = styles[styleStep].1 else { - return - } - - let exp = Exp(.interpolate) { - Exp(.linear) - Exp(.zoom) - 0 - UIColor.red - 14 - UIColor.blue - } - - do { - let data = try JSONEncoder().encode(exp.self) - let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) - color = mapView.mapboxMap.style.layerProperty( - for: land, - property: "background-color") - - print("Setting background color expression") - try! mapView.mapboxMap.style.setLayerProperty( - for: land, - property: "background-color", - value: jsonObject) - } catch { - print("Error setting background color: \(error)") - } - } - - func popColorExpression() { - guard let land = styles[styleStep].1 else { - return - } - - if let color = color { - print("Re-setting background color expression") - try! mapView.mapboxMap.style.setLayerProperty( - for: land, - property: "background-color", - value: color) - } - color = nil - } - - func takeSnapshot(_ completion: @escaping () -> Void) { - guard snapshotter == nil else { - fatalError() - } - - // Configure the snapshotter object with its default access - // token, size, map style, and camera. - let options = MapSnapshotOptions(size: CGSize(width: 300, height: 300), - pixelRatio: 1, - resourceOptions: mapInitOptions.resourceOptions) - - print("Creating snapshotter") - let snapshotter = Snapshotter(options: options) - snapshotter.style.uri = .light - snapshotter.setCamera(to: CameraOptions(cameraState: mapView.cameraState)) - - snapshotter.onNext(.styleLoaded) { [weak self] _ in - guard let snapshotter = self?.snapshotter else { - assertionFailure("Snapshotter does not exist") - completion() - return - } - - snapshotter.start(overlayHandler: nil) { _ in - completion() - } - } - - self.snapshotter = snapshotter - } -} diff --git a/Apps/StressTest/StressTestTests/Info.plist b/Apps/StressTest/StressTestTests/Info.plist deleted file mode 100644 index 64d65ca49577..000000000000 --- a/Apps/StressTest/StressTestTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Apps/StressTest/StressTestTests/StressTestTests.swift b/Apps/StressTest/StressTestTests/StressTestTests.swift deleted file mode 100644 index 90694c0ab115..000000000000 --- a/Apps/StressTest/StressTestTests/StressTestTests.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// StressTestTests.swift -// StressTestTests -// -// Created by Julian Rex on 12/12/20. -// - -import XCTest -@testable import StressTest - -class StressTestTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/Apps/StressTest/StressTestUITests/Info.plist b/Apps/StressTest/StressTestUITests/Info.plist deleted file mode 100644 index 64d65ca49577..000000000000 --- a/Apps/StressTest/StressTestUITests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/Apps/StressTest/StressTestUITests/StressTestUITests.swift b/Apps/StressTest/StressTestUITests/StressTestUITests.swift deleted file mode 100644 index 48725d919292..000000000000 --- a/Apps/StressTest/StressTestUITests/StressTestUITests.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// StressTestUITests.swift -// StressTestUITests -// -// Created by Julian Rex on 12/12/20. -// - -import XCTest - -class StressTestUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 945478165a72..5d29709db9f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,987 @@ -# Changelog for Mapbox Maps SDK v10 for iOS +# Changelog for Mapbox Maps SDK v11 for iOS Mapbox welcomes participation and contributions from everyone. -## main +## 11.10.0-rc.1 - 31 January, 2025 + +* Expose experimental ColorTheme API to set style wide color theme. A color theme modifies the global colors of a style using a LUT (lookup table) for color grading. +Pass the image either as a base64-encoded string or as UIImage: +```swift +let mapView = MapView() +mapView.mapboxMap.setMapStyleContent { + ColorTheme(base64: "base64EncodedImage") // or use an uiimage shortcut ColorTheme(uiimage: lutImage) +} +``` +Note: Each style can have only one `ColorTheme`. Setting a new theme overwrites the previous one. Further details can be fouund in documentation for `ColorTheme` +* Promote `ClipLayer.clipLayerTypes` and `ClipLayer.clipLayerScope` to stable. +* Remove experimental `DirectionalLight.shadowQuality`. +* Add experimental `ViewAnnotationManager.viewAnnotationAvoidLayers` for specifying layers that view annotations should avoid. The API currently only supports line layers. +* Add support for the `maxOverscaleFactorForParentTiles` property in `CustomRasterSource` and `CustomGeometrySource`, allowing greater control over tile overscaling behavior when rendering custom raster tiles. +* Add support for experimental *-use-theme propert that allow to override the color theme set on the Map. This is experimental and have several limitations - currently expressions are not supported. Color properties in Lights, Rain, Snow are not supported. *-use-theme for layer applied only after zoom level change. +* Update CoreMaps to 11.10.0-rc.1 and Common to 24.10.0-rc.1. + +## 11.10.0-beta.1 - 20 January, 2025 + +* Mark `SymbolElevationReference`, `FillExtrusionBaseAlignment`, `FillExtrusionHeightAlignment`, `ModelScaleMode`, `ModelType`, `ClipLayerTypes`, `BackgroundPitchAlignment` types as Experimental. Initially they were exposed as stable by mistake. If you use them, please import `MapboxMaps` with `Experimental` SPI: +``` +@_spi(Experimental) import MapboxMaps +``` + +* Localize geofencing attribution dialog. +* Support dictionary expression literals. +* Bump minimal deployment target from 12.0 to 14.0. +* [SwiftUI] Expose new `slot()` method on annotation groups that takes `Slot` instead of `String`. Use the type with annotationGroups: + +swift +``` +CircleAnnotationGroup {} + // old + .slot("middle") + // new + .slot(.middle) +``` + +* Introduce `ViewAnnotation.priority`, deprecate `ViewAnnotation.selected`. +Use this property to define view annotation sort order. +* Introduce `ViewAnnotation.minZoom` and `ViewAnnotation.maxZoom`. Use these properties to configure zoom-level specific view annotations. +* Update CoreMaps to 11.10.0-beta.2 and Common to 24.10.0-beta.2. + +## 11.9.1 - 20 January, 2025 + +* Update CoreMaps to 11.9.2. + +## 11.9.0 - 18 December, 2024 + +* Remove experimental SPI from `StyleImage`. +* Promote ClipLayer to stable. +* Fix the encoding/decoding key for `Rain/centerThinning` and `Snow/centerThinning`. +* Update CoreMaps to 11.9.0 and Common to 24.9.0. + +## 11.9.0-beta.1 - 9 December, 2024 + +* Add a new API to disable custom resizing implementation of the MapView. To disable the custom resizing implementation, set `MapView.resizingAnimation` to `.none`. +* Add `to-hsla` expression support. + +## 11.9.0-beta.1 - 28 November, 2024 + +⚠️⚠️⚠️ Potentially breaking changes ⚠️⚠️⚠️ +* Mark `symbolElevationReference`, `symbolZOffset`, `lineTrimColor `, `lineTrimFadeRange`, `lineZOffset` as Experimental in AnnotationManagers. This is potentially breaking change, however those properties are not marked as experimental only in AnnotationManagers by mistake. +In order to continue use them use the following import `@_spi(Experimental) import MapboxMaps`. + +* Add two separete Geofence examples in SwiftUI - `GeofencingPlayground` and `GeofencingUserLocation` +* Add support for Base and Height alignment in FillExtrusionLayer. +* Add support for `pitchAlignment` in BackgroundLayer. +* Add support for `zOffset` in FillLayer, PolygonAnnotation[Manager] and PolygonAnnotationGroup. +* Add a property emphasisCircleGlowRange to LocationIndicatorLayer to control the glow effect of the emphasis circle – from the solid start to the fully transparent end. +* Fix a crash on calling `LocationIndicatorLayer/location(coordinate:) function` due to missing 0 altitude value. +* Add a new Expression initializer `init(_ operator: Operator, _ arguments: ExpressionArgumentConvertible...)` to simplify the creation of expressions with multiple arguments. +That initializer doesn't require to wrap arguments in `Argument` cases. For example, `Exp(.eq, Exp(.get, "extrude"), "true")`. +* Expose a `TileStore/clearAmbientCache()` method to clear ambient cache. +* Add new experimental `radius` parameter to `TapInteraction`, `LongPressInteraction` and interaction managers to control the radius of a tappable area. +* Add a way to specify image expression options. +* Bump core maps version to 11.9.0-beta.1 and common sdk to 24.9.0-beta.1 +* Add new experimental APIs to control precipitation rendering. Snow and Rain are available now with an `@_spi(Experimental)` import prefix. +* Add a way to filter attribution menu items. + +## 11.8.0 - 11 November, 2024 + +* Add two separated Geofence examples in SwiftUI - `GeofencingPlayground` and `GeofencingUserLocation` +* Expose `lineElevationReference`, `lineCrossSlope`, `iconSizeScaleRange`, `textSizeScaleRange` as experimental +* Mark `ClipLayer` as stable + +## 11.8.0-rc.1 - 23 October, 2024 + +* Fix the bug when MapView would ignore the new bounds size if there are more than a single resizing event in the animation. + +## 11.8.0-beta.1 - 14 October, 2024 + +* [SwiftUI] Fixed crash when ForEvery was used with duplicated IDs. +* Introduce experimental Geofencing API. Implementation example: [GeofencingExample.swift](Sources/Examples/All%20Examples/GeofencingExample.swift) +* Refactor of the experimental Interactions and Featuresets API: + - `InteractiveFeature` is renamed to `FeaturesetFeature`. + - Introduce new `StandardPoiFeature`, `StandardBuildingsFeature`, `StandardPlaceLabelsFeature`. + - Introduce new `FeaturesetDescriptor`. + +* Generate `MapStyle.standard` and `MapStyle.standardSatellite` from the style specification. Added the new `StandardFont` type to represent the font family in these configurations. If you used a string variable, update your code: +```swift +// Old: +Map().mapStyle(.standard(font: fontValue)) +/// New: +Map().mapStyle(.standard(font: StandardFont(rawValue: fontValue))) +Map().mapStyle(.standard(font: .lato)) +``` +* Introduce experimental property `MapboxMap.styleGlyphURL`. Use this property to apply custom fonts to the map at runtime, without modifying the base style. +* Fix a console warning (`Source x missing for layer x`) when using annotation managers. + +## 11.7.0 - 26 September, 2024 + +* Fix the bug where displaying ViewAnnotation and setting a feature state simultaneously could result in an unapplied feature state. +* Remove `MapboxMaps-Swift.h` from MapboxMaps framework, this will disable ObjC interop for MapboMaps. +* Update CoreMaps to 11.7.0 and Common to 24.7.0 + +## 11.7.0-rc.1 - 13 September, 2024 + +* Add experimental `FillExtrusionLayer.fillExtrusionLineWidth` that can switches fill extrusion rendering into wall rendering mode. Use this property to render the feature with the given width over the outlines of the geometry. + +## 11.7.0-beta.1 - 30 August, 2024 + +* Expose data-driven properties on annotation managers. Now it's possible to set data-driven properties globally on annotation manager and specify per-annotation overrides. +Previously user had to specify those properties on each annotation and couldn't specify them globally +* Added new experimental interactive features API. Interactive features allow you to add interactions to both layers, the map itself, or the features defined in the imported styles, such as Standard Style. The new API supersedes the Map Content Gesture API and makes it cross-platform. +* Rename the `MapContentGestuereContext` to the `InteractionContext` +* Introduce a new `RenderedQueryGeometry` type to replace multiple `MapboxMaps.queryRenderedFeatures` overloads. +* [SwiftUI] Introduce new experimental `FeatureState` primitive. + +* Expose data-driven properties on annotation managers. Now it's possible to set data-dirven properties globally on annotation manager and specify per-annotation overrides. +Previosuly user had to specify those properties on each annotation and couldn't specify them globally + +```swift +CircleAnnotationGroup(circles, id: \.id) { circle in + CircleAnnotation(centerCoordinate: circle.coordinate) + .circleColor(circle.color) + .circleRadius(10) + .circleStrokeWidth(1) + .circleStrokeColor(.black) +} +``` + +The problem with the above approach is that most of the properties are just duplicated for each annotation, which can lead to **large memory overhead** in case of big datasets. In order to solve this issue and provide more versatile API the following approach is now possible, which is visually identical to previous snippet, but more performant. + +```swift +CircleAnnotationGroup(circles, id: \.id) { circle in + CircleAnnotation(centerCoordinate: circle.coordinate) + .circleColor(circle.color) +} +.circleRadius(10) +.circleStrokeWidth(1) +.circleStrokeColor(.black) +``` + +Same applies for imperative API. In this case each even annotation will have random color, but others will use the global default specified in the annotation manager. + +```swift +let circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager() +var annotations = [CircleAnnotation]() +for i in 0...2000 { + var annotation = CircleAnnotation(centerCoordinate: .random) + if i % 2 == 0 { annotation.circleColor = StyleColor(.random) } + annotations.append(annotation) +} +circleAnnotationManager.circleColor = .blue +``` + +* Improve memory reclamation behavior when using partial GeoJSON update API. +* Update Turf to 3.0.0 version. That version introduce breaking change – there is no more `RawRepresentable` conformances for `Array` and `Dictionary` system types. If you were relying on the `init(rawValue:)` function or `rawValue` property, you can use the substitution instead: + + * `init(rawValue:)` -> `init(turfRawValue:)` + * `rawValue` -> `turfRawValue` +* Remove experimental `model-front-cutoff` property from `ModelLayer` +* Bump core maps version to 11.7.0-beta.2 and common sdk to 24.7.0-beta.2 +* Expose experimental `ClipLayer.clipLayerScope`, `SymbolLayer.symbolElevationReference` and `SymbolLayer.symbolZOffset`. +* Most of public value types was marked as Sendable now, to facilitate adoption of Swift 6 concurrency model for SDK clients. +* `autoMaxZoom` property exposed for GeoJSONSource to fix rendering issues with `FillExtrusionLayer` in some cases + +## 11.6.0 - 14 August, 2024 +* Expose getters for `MapOptions.orientation`, `MapOptions.constrainMode` and `MapOptions.viewportMode`. +* Expose `lineTrimColor` and `lineTrimFadeRange` on `LineLayer` which allow to set custom color for trimmed line and fade effect for trim. Update navigation example to use those properties. + +## 11.6.0-rc.1 - 31 July, 2024 + +⚠️⚠️⚠️ Known Issues ⚠️⚠️⚠️ + +* `ClipLayer` property `clipLayerTypes` is not updated in runtime. The fix is expected to land in stable 11.6.0. + +### Features ✨ and improvements 🏁 + +* Expose new Standard Satellite style. Add new parameters to the Standard Style. With new Standard Style API it's possible to apply color themes on the map, hide/show road labels and show/hide 3D models. With new Standard Satellite style it's possible to show satellite imagery and also apply some configurations similar to Standard Style. + +### Bug fixes 🐞 + +* Fix bug where updating MapStyle didn't update the configuration properties. +* Fix symbols with occlusion crashing on iOS simulators + +## 11.6.0-beta.1 - 19 July, 2024 + +⚠️⚠️⚠️ Known Issues ⚠️⚠️⚠️ + +* `ClipLayer` property `clipLayerTypes` is not updated in runtime. The fix is expected to land in 11.6.0-rc.1. + +### Features ✨ and improvements 🏁 + +* SwiftUI API marked as stable +* Expose experimental `ClipLayer` to remove 3D data (fill extrusions, landmarks, trees) and symbols. +* `CustomRasterSource` API updated, now `CustomRasterSourceOptions` accepts protocol `CustomRasterSourceClient`, enabling direct rendering into `CustomRasterSource` tiles. To achieve behavior similar to previous releases one may construct instance of `CustomRasterSourceClient` as shown below: + +```swift +CustomRasterSourceOptions(tileStatusChangedFunction: { tileID, status in }) // Before +CustomRasterSourceOptions(clientCallback: CustomRasterSourceClient.fromCustomRasterSourceTileStatusChangedCallback { tileID, status in }) // Now +``` + +* Introduce new `ViewAnnotation.allowZElevate` and `MapViewAnnotation.allowZElevate` properties. When set to true, the annotation will be positioned on the rooftops of buildings, including both fill extrusions and models. +* Deprecate `MapView.presentsWithTransaction` and `Map.presentsWithTransaction` in favor of `MapView.presentationTransactionMode` and `Map.presentationTransactionMode`. The new default `PresentationTransactionMode.automatic` updates the `presentsWithTransaction` automatically when need to optimize performance. If you used the `MapView.presentsWithTransaction` with View Annotations, now you can safely remove this option: + +```swift +Map { + MapViewAnnotation(...) +} +.presentsWithTransaction(true) // Remove this +``` + +In case you need to preserve the old default behavior use `presentationTransactionMode = .async`: + +```swift +mapView.presentationTransactionMode = .async // UIKit +Map().presentationTransactionMode(.async) // SwiftUI +``` + +* MapboxMaps XCFramework structure now properly constructed for `maccatalyst` platform and code signing issues was eliminated. + +### Bug fixes 🐞 + +* Improved `line-pattern` precision +* Fixed `CustomRasterSource` rendering when camera shows anti-meridian or multiple world copies. + +## 11.5.1 - 5 July, 2024 + +* Update CoreMaps to the 11.5.1 version. + +## 11.5.0 - 3 July, 2024 + +* Use new `LineJoin.none` in conjunction with an image as a `linePattern` value to display repeated series of images along a line(e.g. dotted route line). +* Deprecate `Expression` in favor of `Exp` to avoid name clash with `Foundation.Expression`. + +## 11.5.0-rc.1 - 19 June, 2024 + +* The CustomRasterSource API has been updated. It no longer includes a cache and now provides notifications about alternative tiles that can be used when the ideal ones are unavailable. +* Expose text-occlusion-opacity, icon-occlusion-opacity, line-occlusion-opacity, model-front-cutoff, lineZOffset as experimental. +* Add min/max/default values for most of the style properties. +* Fix compilation of Examples and MapboxMaps in Xcode 16 + +## 11.5.0-beta.1 - 11 June, 2024 + +* Improve stability of symbol placement when using `FollowPuckViewportState`. +* Expose `clusterMinPoints` property for `GeoJSONSource` and for `ClusterOptions` +* Root properties (`Atmosphere`, `Lights`, `Projection`, `Terrain`, `Transition`) are now revertible for all styles. +* Introduce raster particles rendering example +* Bump core maps version to 11.5.0-beta.1 and common sdk to 24.5.0-beta.4 +* Root properties (`Atmosphere`, `Lights`, `Projection`, `Terrain`, `Transition`) are now revertible for all styles. + +## 11.4.0 - 22 May, 2024 + +* Live performance metrics collection. Mapbox Maps SDK v11.4.0 collects certain performance and feature usage counters so we can better benchmark the MapboxMaps library and invest in its performance. The performance counters have been carefully designed so that user-level metrics and identifiers are not collected. +* Bump core maps version to 11.4.0 and common sdk to 24.4.0 + +## 11.4.0-rc.2 - 15 May, 2024 + +* Bump core maps version to 11.4.0-rc.2 and common sdk to 24.4.0-rc.2 + +## 11.4.0-rc.1 - 8 May, 2024 + +* Added camera(for:) deprecation for several methods. Added `CameraForExample` showcasing camera(for:) usageq +* Expose experimental `RasterParticleLayer` which is suitable for displaying precipitation or wind on the map +* Expose the list of added `ViewAnnotation` +* Bump core maps version to 11.4.0-rc.1 and common sdk to 24.4.0-rc.1. + +## 11.4.0-beta.3 - 6 May, 2024 + +* Bump common sdk to 24.4.0-beta.3. + +## 11.4.0-beta.2 - 2 May, 2024 + +* Bump core maps version to 11.4.0-beta.2 and common sdk to 24.4.0-beta.2. +* `MapboxMap.loadStyle()` and `Snapshotter.loadStyle()` behaviour is rolled back to pre 11.4.0-beta.1 state. + +## 11.4.0-beta.1 - 24 April, 2024 + +⚠️⚠️⚠️ Known Issues ⚠️⚠️⚠️ + +* In v11.4.0-beta.1, setting a `RasterLayer`’s `rasterColor` property with an expression will block the layer from rendering. This issue will be resolved in v11.4.0-rc.1. + +### Experimental API breaking changes ⚠️ + +In this release, we introduce the new [Declarative Styling API](https://docs.tilestream.net/ios/maps/api/latest/documentation/mapboxmaps/declarative-map-styling) for UIKit and SwiftUI. This change is based on `MapContent` introduced for SwiftUI; therefore, it has been restructured. The changes are compatible; however, in some rare cases, you may need to adjust your code. + +* [SwiftUI] `MapContent` now supports custom implementations, similar to SwiftUI views. The `MapContent` protocol now requires the `var body: some MapContent` implementation. +* [SwiftUI] PointAnnotation and Puck3D property-setters that consumed fixed-length arrays reworked to use named properties or platform types for better readability: + +```swift +// Before +PointAnnotation() + .iconOffset([10, 20]) // x, y + .iconTextFitPadding([1, 2, 3, 4]) // top, right, bottom, left +Puck3D() + .modelScale([1, 2, 3]) // x, y, z + +// After +PointAnnotation() + .iconOffset(x: 10, y: 20) + .iconTextFitPadding(UIEdgeInsets(top: 1, left: 4, bottom: 3, right: 2)) +Puck3D() + .modelScale(x: 1, y: 2, z: 3) +``` + +* `StyleImportConfiguration` was removed from public API, the `MapStyle` now contains the configuration directly. +* `TransitionOptions` is now a Swift `struct` rather than an Objective-C `class`. + +### Features ✨ and improvements 🏁 + +* All the style primitives can now be used as `MapContent` in SwiftUI. + +```swift +@_spi(Experimental) MapboxMaps +Map { + LineLayer(id: "traffic") + .lineColor(.red) + .lineWidth(2) +} +``` + +* UIKit applications can now use the `setMapStyleContent` to use style primitives: + +```swift +@_spi(Experimental) MapboxMaps +mapView.mapboxMap.setMapStyleContent { + LineLayer(id: "traffic") + .lineColor(.red) + .lineWidth(2) +} +``` + +* Allow to assign slot to 2D and 3D location indicators. +* Allow observing start/stop event of `CameraAnimator` + You can observe start/stop event of `CameraAnimator` by using new `CameraAnimationsManager` APIs as shown below + + ```swift + // Observe start event of any CameraAnimator owned by AnimationOwner.cameraAnimationsManager + mapView.camera + .onCameraAnimatorStarted + .owned(by: .cameraAnimationsManager) + .observe { cameraAnimator in + // Handle camera animation started here. + } + .store(in: &cancelables) + // Observe finished events of any CameraAnimator + mapView.camera + .onCameraAnimatorFinished + .observe { animator in + // Handle camera animation stopped here. + } + .store(in: &cancelables) + ``` + + You can also observe directly on an instance of `CameraAnimator` when using low-level camera APIs to create a custom animator + + ```swift + // Declare an animator that changes the map's bearing + let bearingAnimator = mapView.camera.makeAnimator(duration: 4, curve: .easeInOut) { (transition) in + transition.bearing.toValue = -45 + } + bearingAnimator.onStarted.observe { + // Bearing animator has started. + }.store(in: &cancelables) + ``` + +* Allow adding slots at runtime. +* Expose API to interact with style imports using Declarative Styling and regular imperative API. +* Expose `StyleImport` for declarative styling as `MapStyleContent`. +* Expose `removeStyleImport`, `moveStyleImport`, `updateStyleImport`, `addStyleImport` methods on `StyleManager` +* Allow assigning layerPosition to 2D and 3D location indicators in imperative API. +* Make Puck2D and Puck3D to be positioned according to relative layer position in declarative API instead of always top-most position. +* Add codesign for XCFrameworks. +* `MapboxMap.loadStyle()` and `Snapshotter.loadStyle()` now correctly call the `completion` closure. + +## 11.3.0 - 10 April, 2024 + +### Features ✨ and improvements 🏁 + +* Introduce an experimental Style DSL, enabling developers to add map style content like Sources, Layers, Style Images, Terrain, Light and Atmosphere to their map style at runtime in a declarative pattern. See the documentation [here](https://docs.mapbox.com/ios/maps/api/11.2.0-beta.1/documentation/mapboxmaps/style-dsl) for more information. For SwiftUI users, this Style DSL provides a more natural approach to manipulating content. +[tile store] Expose API for estimating Tile Region downloads and storage size. + +## 11.3.0-rc.1 - 27 March, 2024 + +* [tile store] Expose API for estimating Tile Region downloads and storage size. +* Remove metal view's contentScaleFactor assertion. +* Bump core maps version to 11.3.0-rc.1 and common sdk to 24.3.0-rc.1. + +## 11.3.0-beta.1 - 14 March, 2024 + +* Update the minimum Xcode version to 15.2 (Swift 5.9). +* Add `onClusterTap` and `onClusterLongPress` to AnnotationManagers(UIKit) and AnnotationGroups(SwiftUI) which support clustering +* Add annotations drag handlers callbacks `dragBeginHandler`, `dragChangeHandler`, `dragEndHandler` to all Annotation types. +* [SwiftUI] Expose `captureSnapshot` on `MapProxy` which allows to capture SwiftUI Map snapshot using `MapReader` +* [SwiftUI] Expose `opaque` and `frameRate` on SwiftUI Map +* [SwiftUI] Add `allowHistTesting` modifier on `MapViewAnnotation`. +* [SwiftUI] Fix view annotations positioning on `.ignoresSafeArea(.all)` +* Add `includeOverlays` parameter to `MapView.snapshot()` +* Fix taps propagation on `ViewAnnotation` and `MapViewAnnotation`. +* Added Attribution and Telemetry pop-up dialogs and compass view content description translations for Arabic, Belarusian, Bulgarian, Catalan, Chinese Simplified, Chinese Traditional, Czech, Danish, Dutch, French, Galician, German, Hebrew, Italian, Japanese, Korean, Lithuanian, Norwegian, Polish, Belarusian, Russian, Spanish, Swedish, Ukranian and Vietnamese. +* Bump core maps version to 11.3.0-beta.1 and common sdk to 24.3.0-beta.1. + +## 11.2.0 - 28 February, 2024 + +* Bump core maps version to 11.2.0 and common sdk to 24.2.0. + +## 11.2.0-rc.1 - 15 February, 2024 + +### Bug fixes 🐞 + +* Fix Map and encompassing List scroll at the same time +* visionOS small enhancements + +## 11.2.0-beta.1 - 1 February, 2024 + +### Features ✨ and improvements 🏁 + +* vision OS support. 🚀 +* Add easing curve parameter to `CameraAnimationsManager.fly(to:duration:curve:completion)`, make `TimingCurve` public with few more options. +* Expose `MapboxMap.centerAltitudeMode` and ensure correct `centerAltitudeMode` on gesture ending. +* Expose extra configuration methods for `MapboxMap`: `setNorthOrientation(_:)`, `setConstrainMode(_:)` and `setViewportMode(_:)`. +Use them to configure respective map options after creating a map view. +* Expose `MapboxMap.reduceMemoryUse()` which can be used in situations when it is important to keep the memory footprint minimal. +* Expose `MapboxMap.isAnimationInProgress` and `MapboxMap.isGestureInProgress` to query current status of both built-in and custom camera animations and gestures. +* Expose experimental `CustomRasterSource` and non-experimental `CustomGeometrySource` as regular `Source`'s providing a better way to work with them and also allow for using them in Style DSL. +* Introduce `tileCacheBudget` property on `GeoJsonSource`, `RasterSource`, `RasterDemSource`, `RasterArraySource`, `VectorSource`, `CustomGeometrySource`, and `CustomRasterSource`. +* `MapboxMaps/setTileCacheBudget(size:)` will now use the `TileCacheBudgetSize` property, the older method with `TileCacheBudget` has been deprecated and will be removed in a future major release. +* Introduce `SymbolLayer.iconColorSaturation` API. +* Introduce experimental `RasterLayer.rasterElevation` API. +* Introduce experimental `MapboxMap.collectPerformanceStatistics` allowing to collect map rendering performance statistics, both for UIKit and SwiftUI. + +### Bug fixes 🐞 + +* Fix MapView flickering during resizing. +* Fix glitch in chained camera animations. +* Build XCFramework with `SWIFT_SERIALIZE_DEBUGGING_OPTIONS=NO` flag to avoid serialized search paths in Swift modules. +* Fixed a crash that occurs when annotations have duplicate identifiers. + +### Dependency Updates + +* Bump Turf version to `2.8.0`. +* Bump minimum Xcode version to `14.3.1`. + +## 11.1.0 - 17 January, 2024 + +* Add `customData` field in Annotaion and deprecate `userInfo`. `userInfo` behaviour rolled back to v10 behaviour. +* Fixed a bug where the attribution dialog does not appear when there is a presented view controller. +* Make padding optional in `MapboxMap.camera(for:padding:bearing:pitch:maxZoom:offset:)` and `MapboxMap.camera(for:padding:bearing:pitch:)`. +* Update CoreMaps to 11.1.0 and Common to 24.1.0 + +## 11.1.0-rc.1 - 04 January, 2024 + +### Bug fixes 🐞 + +* Fix the bug where the annotation could disappear when it is dragged. + +## 11.1.0-beta.1 - 19 December, 2023 + +⚠️⚠️⚠️ Known Issues ⚠️⚠️⚠️ + +* `RasterArraySource.rasterLayers` is always `nil` for any source. +Workaround: use `MapboxMap.sourceProperty(for:property:).value` to fetch a value of `RasterArraySource.rasterLayers`. + +* Expose method to get coordinate info for point(s): `MapboxMap.coordinateInfo(for:)` and `MapboxMap.coordinatesInfo(for:)`. +* [SwiftUI] Expose `Map.gestureHandlers()` for handling Map gesture events. +* Introduce experimental `RasterArraySource`, along with `RasterLayer.rasterArrayBand`. +* Introduce `-emissiveStrength` attribute for `FillExtrusionLayer`, `HillShadeLayer` and `RasterLayer`. +* Update MapboxCoreMaps to v11.1.0-beta.1 and MapboxCommon to v24.1.0-beta.2 + +## 11.0.0 - 29 November, 2023 + +* Introduce [`Slot`](https://docs.mapbox.com/ios/maps/api/11.0.0-rc.2/documentation/mapboxmaps/slot/) for assigning a layer to a slot. +* Update MapboxCoreMaps to v11.0.0 and MapboxCommon to v24.0.0 + +## 11.0.0-rc.2 - 17 November, 2023 + +### Breaking changes ⚠️ + +* Note: SwiftUI support is an experimental feature, its API may be changed until it stabilizes. + +* [SwiftUI] Fixed point annotations clustering. +* [SwiftUI] Viewport inset system was refactored: + * The `Viewport.inset(...)` function was removed in favor of the `Viewport.padding(...)` + * The `Viewport.inset(...)` previously had an `ignoringSafeArea` parameter which allowed developers to specify if an edge safe area inset should be accounted for in padding calculation. Starting with this version, instead of this parameter there is a `Map.usesSafeAreaInsetsAsPadding(_:)` modifier that enables or disables this for all edges. + +### Features ✨ and improvements 🏁 + +* [SwiftUI] New `Map.additionalSafeAreaInsets(...)` modifier that adds additional global safe area insets for the map. Use them to display any UI elements on top of the map. The additional safe area will automatically be accounted for in camera padding calculation in every Viewport. +* Added `allowOverlapWithPuck` and `ignoreCameraPadding` options to `ViewAnnotation` and `MapViewAnnotation`. + +### Bug fixes 🐞 + +* [SwiftUI] Fix bug when `Viewport.inset(...)` didn't use safe area insets on the first load. +* [SwiftUI] Fix map basic coordinator clinging to the first subscriptions. + +## 11.0.0-rc.1 - 3 November, 2023 + +### Breaking changes ⚠️ + +* `MapboxMap.loadStyle` methods changed error type from `MapLoadingError` to `Error`. +* `OverviewViewportStateOptions.coordinatesPadding` is renamed to `OverviewViewportStateOptions.geometryPadding`. +* [SwiftUI] ``Viewport.overview(geometry:bearing:pitch:coordinatesPadding:maxZoom:offset:)` is renamed to `Viewport.overview(geometry:bearing:pitch:geometryPadding:maxZoom:offset:)` +* Bearing indication on user location puck is disabled by default to reduce amount map redraws. + To re-enable bearing update rendering, set `mapView.location.options.puckBearingEnabled` to `true`. +* The default behavior of resetting the viewport to idle is changed. Previously viewport was reset to idle when the user touched the map for longer than 150 ms. Now it will happen when the user pans the map. If the desired behavior is different, you can disable the default by setting `mapView.viewport.options.transitionsToIdleUponUserInteraction` to `false` and implementing any gesture that calls `mapView.viewport.idle()`. + +### Features ✨ and improvements 🏁 + +* Refactor `MapboxMap.loadStyle` to cancel previous style loads when called multiple times. +* New experimental `StyleManager.load(mapStyle:transition:completion)` method to load `MapStyle` in `MapboxMap`, or `Snapshotter`: + + ```swift + mapboxMap.load(mapStyle: .standard(lightPreset: .dawn, showRoadLabels: false)) { _ in + print("Style is loaded") + } + ``` + +* Support `slot` for annotation managers and annotation groups. +* [SwiftUI] Annotation groups can be created with static list of annotations. In the example below polyline annotation group displays two annotations on the same layer. + + ```swift + Map { + PolylineAnnotationGroup { + PolylineAnnotation(lineCoordinates: route.coordinates) + .lineColor("blue") + if let alternativeRoute { + PolylineAnnotation(lineCoordinates: alternativeRoute.coordinates) + .lineColor("green") + } + } + .lineCap(.round) + .slot("middle") + } + ``` + +* [SwiftUI] Expose `transitionsToIdleUponUserInteraction` modifier. +* Introduce typed API for assining a layer to a slot. +* Introduce `Slot` for assining a layer to a slot. +* Introduce [`Slot`](https://docs.mapbox.com/ios/maps/api/11.0.0-rc.1-docc/documentation/mapboxmaps/slot) for assigning a layer to a slot. + +### Bug fixes 🐞 + +* Fix issue where 2D puck images are not getting updates. +* [SwiftUI] Fixed issue when viewport inset with safe area is calculated incorrectly. +* Fixed issue when quick interaction didn't lead to resetting viewport to `idle`. + +## 11.0.0-beta.6 - 23 October, 2023 + +### Breaking changes ⚠️ + +* Style projection can be undefined for styles that do not explicitly specify it, so `MapboxMap.projection` has become optional. +* View Annotation API is changed: + * `ViewAnnotationOptions.geometry` was removed in favor of `ViewAnnotationOptions.annotatedFeature`. + * `ViewAnnotationOptions.associatedFeatureId` was removed. Use `AnnotatedFeature.layerFeature(layerId:featureId:)` with `ViewAnnotationOptions.annotatedFeature` to bind View Annotation to features rendered by any layer. + * [SwiftUI] Use `MapViewAnnotation` instead of `ViewAnnotation` to display view annotations in SwiftUI. +* `OverviewViewportStateOptions.padding` is renamed to `OverviewViewportStateOptions.coordinatePadding`, the `OverviewViewportStateOptions.padding` now represents the camera padding. + +### Features ✨ and improvements 🏁 + +* New `ViewAnnotation` class is added for simplifying View Annotation management. It is a simple to use replacement for the old `ViewAnnotationOptions`. It automatically updates size and other properties of annotations, and provides new features: + * Automatic anchor position from specified `ViewAnnotation.variableAnchor` configurations. + * Supports displaying not only at point features, but also at lines and polygons. +* Support Dynamic View Annotations in SwiftUI. +* Add `MapboxMaps.camera(for:camera:coordinatesPadding:maxZoom:offset)`. +* Add `MapViewDebugOptions.padding` debug option. +* Add `maxZoom` and `offset` parameters to `OverviewViewportStateOptions`. + +### Bug fixes 🐞 + +* Fix issue when transition to Overview Viewport resulted in double padding. +* [SwiftUI] Fix issue when Overview Viewport is incorrect if set as initial viewport. + +## 11.0.0-beta.5 - 9 October, 2023 + +* Add a new CustomLayer API to simplify manipulation of layers with custom rendering (aka "CustomLayerHost"). +* The following APIs have been promoted to stable: + * `LineLayer/lineDepthOcclusionFactor`, `LineLayer/lineDepthOcclusionFactorTransition`, `LineLayer/lineEmissiveStrength` and `LineLayer/lineEmissiveStrengthTransition` + * `SymbolLayer/iconImageCrossFade`, `SymbolLayer/iconImageCrossFadeTransition`, `SymbolLayer/iconEmissiveStrength`, `SymbolLayer/iconEmissiveStrengthTransition`, `SymbolLayer/textEmissiveStrength` and `SymbolLayer/textEmissiveStrengthTransition` + * `BackgroundLayer/backgroundEmissiveStrength` and `BackgroundLayer/backgroundEmissiveStrengthTransition` + * `CircleLayer/circleEmissiveStrength` and `CircleLayer/circleEmissiveStrengthTransition` + * `FillLayer/fillEmissiveStrength` and `FillLayer/fillEmissiveStrengthTransition` + * `AmbientLight`, `DirectionalLight` and related APIs. +* Fix memory leak in SwiftUI. +* Expose `MapViewDebugOptions` in SwiftUI. + +## 11.0.0-beta.4 - 20 September, 2023 + +### Breaking changes ⚠️ + +* `StyleColor.red`, `StyleColor.green`, `StyleColor.blue`, `StyleColor.alpha` are not in use anymore and got removed. +* The `syncSourceAndLayerIfNeeded` method in every annotation manager (e.g`PointAnnotationManager` and others) was removed from the public API. + +### Features ✨ and improvements 🏁 + +* Add MSAA support with the `MapInitOptions/antialiasingSampleCount` property. +* `StyleColor` - add support for all color formats as defined by [Mapbox Style Spec](https://docs.mapbox.com/style-spec/reference/types/#color). +* Introduce experimental Custom Raster Source APIs: `StyleManager/addCustomRasterSource`, `StyleManager/setCustomRasterSourceTileData`, `StyleManager/invalidateCustomRasterSourceTile`, `StyleManager/invalidateCustomRasterSourceRegion`. +* Introduce new Map Content Gesture System. +* Add an experimental `MapView/cameraDebugOverlay` which returns a UIView displaying the current state of the camera. +* Add `MapView/debugOptions` which wraps the debugOptions on the underlying map in `MapViewDebugOptions`. An additional `.camera` debug option has been added, which adds a `CameraDebugView` to the map to see the current camera state. `MapboxMap/debugOptions` has been deprecated; access the underlying map debug options through `mapView.debugOptions.nativeDebugOptions` instead. + +## 11.0.0-beta.3 - 8 September, 2023 + +### Breaking changes ⚠️ + +* `MapboxMap.dragStart()` and `MapboxMap.dragEnd()` are not in use anymore and got removed. +* Remove `MapOptions/optimizeForTerrain` option. Whenever terrain is present layer order is automatically adjusted for better performance. + +### Features ✨ and improvements 🏁 + +* Improve map camera and gestures when terrain is used to fix camera bumpiness and map flickering. +* Expose a method to remove tile region with a completion: `TileStore.removeTileRegion(forId:completion:)`. +* Bump core maps version to 11.0.0-beta.4 and common sdk to 24.0.0-beta.4. + +### Bug fixes 🐞 + +* Fix `modelCastShadows` and `modelReceiveShadows` options of `Puck3DConfiguration` being ignored. +* Fix `StyleColor` failing to initialize with non-sRGB color spaces by converting supplied `UIColor`s to `sRGB` color space by default. + +## 11.0.0-beta.2 - 23 August, 2023 + +* Introduce experimental `MapboxRecorder`, which allows recording of the map and replaying custom scenarios. +* Expose `slot` property on `Layer` protocol. +* Bump core maps version to 11.0.0-beta.3 and common sdk to 24.0.0-beta.3. +* Add privacy policy attribution dialog action. + +## 11.0.0-beta.1 - 2 August, 2023 + +* Introduce `hsl`, `hsla` color expression. +* Introduce `random` expression. +* Introduce `measureLight` expression lights configuration property. +* Introduce `LineLayer/lineBorderColor`, `LineLayer/lineBorderWidth` APIs. +* Introduce `SymbolLayer/iconImageCrossFade` API. +* Introduce experimental `BackgroundLayer/backgroundEmissiveStrength`, `CircleLayer/circleEmissiveStrength`, `FillLayer/fillEmissiveStrength`, `LineLayer/lineEmissiveStrength`, `SymbolLayer/iconEmissiveStrength`, `SymbolLayer/textEmissiveStrength`, `ModelLayer/modelEmissiveStrength`, `ModelLayer/modelRoughness`, `ModelLayer/modelHeightBasedEmissiveStrengthMultiplier` APIs. +* Introduce experimental `FillExtrusionLayer/fillExtrusionAmbientOcclusionWallRadius`, `FillExtrusionLayer/fillExtrusionAmbientOcclusionGroundRadius`, `FillExtrusionLayer/fillExtrusionAmbientOcclusionGroundAttenuation`, `FillExtrusionLayer/fillExtrusionFloodLightColor`, `FillExtrusionLayer/fillExtrusionFloodLightIntensity`, `FillExtrusionLayer/fillExtrusionFloodLightWallRadius`, `FillExtrusionLayer/fillExtrusionFloodLightGroundRadius`, `FillExtrusionLayer/fillExtrusionFloodLightGroundAttenuation`, `FillExtrusionLayer/fillExtrusionVerticalScale` APIs. +* Rename `Viewport` to `ViewportManager`. +* Apply `ModelScaleMode.viewport` to Puck3D configuration and remove the custom expression for the `modelScale` of the puck. This means if you are using a constant for `Puck3DConfiguration/modelScale` in v10, you need to adjust this model-scale constant so the puck would be rendered correctly in v11, while this value depends on other configurations of your puck, we have found the new adjusted model-scale to fall between 10x-100x of the old value. +* Add experimental `tileCover` method to the `Snapshotter` that returns tile ids covering the map. +* Add optional `maxZoom` and `offset` parameters to `MapboxMap.camera(for coordinateBounds:)`. `MapboxMap.camera(for coordinateBounds:)`, `MapboxMap.camera(for coordinates:)`, and `MapboxMap.camera(for geometry:)` no longer return a padding value. +* `Location` is splitted into `Location` and `Heading` structs, the location and heading data are now animated individually. +* Replace `loadStyleJSON(_:completion:)`/`loadStyleJSON(_:completion:)` with overloaded `loadStyle(_:completion:)`. +* Mark `Expression.Operator.activeAnchor` as experimental. +* Add transition options as a parameter to `loadStyle(...)` methods. +* `Expression.Operator` is now a struct with static variables instead of enum. +* Add `MapboxMap.coordinate(s)Info(for:)` for converting offscreen points into geographical coordinates. +* Fixed an issue when `MapboxMap.point(for:)` could return false negative result. +* Remove `source`, `sourceLayer`, `filter` properties from the `Layer` protocol requirement. +* Bump core maps version to 11.0.0-beta.1. +* Refactor style Light API: introduce `AmbientLight`, `DirectionalLight`, `FlatLight` and methods to set them. +* Add expression support to `Layer.visibility`. +* Expose new APIs for working with style importing and configuration: getStyleImports(), removeStyleImport(forImportId:), getStyleImportSchema(forImportId:), getStyleImportConfigProperties(forImportId:), setStyleImportConfigPropertiesForImportId(_:configs:), getStyleImportConfigProperty(forImportId:config:), setStyleImportConfigPropertyForImportId(_:config:value:) +* Expose `slot` property for all `Layer`s to link layers from imported styles. +* Convert Style properties enums into structs. +* Bump core maps version to 11.0.0-beta.2 and common sdk to 24.0.0-beta.2. +* Remove MetaKit reexport. + +## 11.0.0-alpha.2 - 21 June, 2023 + +* Remove unnecessary check before updating a geo json source. +* Remove deprecated `LocationManager.updateHeadingForCurrentDeviceOrientation()` method. +* Remove deprecated `MapEvents.EventKind`. +* Make NSNumber extension internal. +* Remove experimental `MapboxMap.setRenderCache(_:)` method. +* Remove deprecated `GestureOptions.pinchRotateEnabled`. +* Remove deprecated `Location` initializer. +* Remove deprecated transition properties from layers. +* Make `easeTo/flyTo` return non-optional cancelable token. +* Add `rotation` case to `GestureType` to be able to detect rotation separately from other gestures. +* Enable zoom during a drag gesture. +* Fix bearing value is fluctuating between initial value and correct value during a rotation gesture. +* Allows animation during any ongoing gestures. +* Sync map size to the size of the metal view. +* Fix missing feature properties for `nil`/`null` values. +* Added experimental `tileCover` method to `MapboxMap` that returns tile ids covering the map. +* Expose `owner` property for `CameraAnimator` protocol +* Updated core styles to the latest versions. +* Merge `TilesetDescriptorOptions` and `TilesetDescriptorOptionsForTilesets`. To enable tileset descriptor creation for a list of tilesets that are not part of the original style use `TilesetDescriptorOptions`. +* Use `DataRef` to pass snapshot and style image data by reference, improving performance +* Bumped min iOS version to 12.0 +* Expose a subset of ModelLayer APIs. +* Protocol `LocationProvider` now requires class semantic for implementation. +* The Map events have been reworked: + * Now all Map events payloads are serialize-free, which brings more type safety and eliminates possible deserialization errors; + * The `MapboxMap` and `Snapshotter` now expose `on`-prefixed properties that allows you to subscribe to map events via `observe` and `observeNext` methods: + + ```swift + mapboxMap.onCameraChanged.observe { [weak self] event in + self?.camera = event.cameraState + }.store(in: &cancelables) + + mapboxMap.onStyleLoaded.observeNext { [weak self] _ in + self?.configureStyle() + }.store(in: &cancelables) + ``` + + * The `AnyCancelable` object returned from `observe` and `observeNext` should be stored, otherwise the subscription will be immediately canceled; + * The same `on`-prefixed properties can now be used as `Combine.Publisher`: + + ```swift + import Combine + mapboxMap.onCameraChanged + .debounce(for: .milliseconds(500), scheduler: DispatchQueue.main) + .map(\.cameraState) + .sink { [weak self] cameraState in + self?.camera = cameraState + }.store(in: &cancellables) + ``` + + * Methods `MapboxMap.onEvery`, `MapboxMap.onNext`, `Snapshotter.onEvery`, `Snapshotter.onNext` have been deprecated; + * Methods `MapboxMap.observe` and `Snapshotter.observe` have been removed. +* Deprecate `PointAnnotationManager.iconTextFit` and `PointAnnotationManager.iconTextFitPadding` in favor of `PointAnnotation.iconTextFit` and `PointAnnotation.iconTextFitPadding`. +* Remove deprecated `PuckBearingSource`and related APIs. +* Experimental API `MapboxMap/setMemoryBudget` was renamed to `MapboxMaps/setTileCacheBudget` and promoted to stable. +* Location consumer methods have been renamed to align with Swift API Design Guidelines. Use `addLocationConsumer(_:)` and `removeLocationConsumer(_:)` rather than `addLocationConsumer(newConsumer:)` and `removeLocationConsumer(consumer:)`. +* `SourceType` and `LayerType` are now structs with static variables instead of enums +* Remove `ResourceOptions` and `ResourceOptionsManager`. Introduce `MapboxOptions` and `MapboxMapsOptions` to handle application-level access token and other generic options. + * Mapbox's access token can now be set with `MapboxCommon.MapboxOptions`. By default, MapboxMaps SDK will try to read the access token from app bundle's property list or `MapboxAccessToken` file when Maps service are initialized; if you wish to set access token programmatically, it is highly recommended to set it before initializing a `MapView`. + + ```swift + import MapboxMaps + + MapboxOptions.accessToken = accessToken + ``` + + * `TileStore`no longer requires `TileStoreOptions.mapboxAccessToken` to be explicitly set. + * Configurations for the external resources used by Maps API can now be set with `MapboxMapsOptions`: + + ```swift + import MapboxMaps + + MapboxMapsOptions.dataPath = customDataPathURL + MapboxMapsOptions.assetPath = customAssetPathURL + MapboxMapsOptions.tileStoreUsageMode = .readOnly + MapboxMapsOptions.tileStore = tileStore + ``` + + * To clear the temporary map data, you can use `MapboxMap.clearData(completion:)` +* Expose new 3D Lights API: `AmbientLight` and `DirectionalLight`. +* `TypeConversionError`, `SnapshotError`, and `ViewAnnotationManagerError` are now structs with static variables instead of enums +* Extend `Layer` protocol with `visibility` property. +* Add required `id` property to `Source`. After that change `id` should be specified for source upon creation: + + ```swift + let terrainSource = RasterDemSource(id: "terrain-source") + mapView.mapboxMap.addSource(terrainSource) + ``` + +* Support string option in `GeoJSONSourceData`. +* Allows passing `extraOptions` (which must be a valid JSON object) when creating `StylePackLoadOptions`and `TilesetDescriptorOptions`. +* Deprecate `MapboxMap/style` and `Snapshotter/style`, from now on you can access Style APIs directly from `MapboxMap` and `Snapshotter` instance. +* Add a new API to enable Tracing with `Tracing.status = .enabled`. Checkout `Tracing` reference to see more. +* Introduce `FillExtrusionLayer.fillExtrusionRoundedRoof` , `FillExtrusionLayer.fillExtrusionEdgeRadius` API. +* Introduce `line-depth-occlusion` API. +* Introduce `FillExtrusionLayer/fillExtrusionRoundedRoof`, `FillExtrusionLayer/fillExtrusionEdgeRadius` API. +* Introduce `lineDepthOcclusionFactor` API for `LineLayer`s and `PolylineAnnotiationManager`. +* Add `Codable` support to `CameraOptions`, `CameraState`, `FollowPuckViewportStateBearing`, `FollowPuckViewportStateOptions`. +* Expose new Style APIs for partial GeoJSON update: + +```swift +MapboxMap.addGeoJSONSourceFeatures(forSourceId:features:dataId:) +MapboxMap.updateGeoJSONSourceFeatures(forSourceId:features:dataId:) +MapboxMap.removeGeoJSONSourceFeatures(forSourceId:featureIds:dataId:) +``` + +## 10.15.0 - July 27, 2023 + +* Update MapboxCoreMaps to 10.15.0 and MapboxCommon to 23.7.0. +* Fixed an issue when `MapboxMap.point(for:)` could return false negative result. + +## 10.15.0-rc.1 - July 13, 2023 + +* Update MapboxCoreMaps to 10.15.0-rc.1 and MapboxCommon to 23.7.0-rc.1. +* Fixed an issue when `MapboxMap.point(for:)` could return false negative result. + +## 10.15.0-beta.1 - June 29, 2023 + +* Remove unneeded synthesized initializers +* Update MapboxCoreMaps to 10.15.0-beta.1 and MapboxCommon to 23.7.0-beta.1. + +## 10.14.0 - June 14, 2023 + +* Added experimental `tileCover` method to the `Snapshotter` that returns tile ids covering the map. +* Update MapboxCoreMaps to 10.14.0 and MapboxCommon to 23.6.0. + +## 10.14.0-rc.1 - May 31, 2023 + +* Fix the issue with simultaneous zooming/panning during the pitch gesture. +* Fix the issue with black MapView when transparent style is used. +* Update MapboxCoreMaps to 10.14.0-rc.1 and MapboxCommon to 23.6.0-rc.1. + +## 10.14.0-beta.1 - May 17, 2023 + +* Add a renamed flag to `PuckBearingSource` and related APIs. +* Update MapboxCoreMaps to 10.14.0-beta.1 and MapboxCommon to 23.6.0-beta.1. + +## 10.13.1 - May 5, 2023 + +* Remove XCFramework binary dependency on MapboxMobileEvents. +* Update MapboxCoreMaps to 10.13.1 and MapboxCommon to 23.5.0 +* [CarPlay] Fix display link is not correctly paused/resumed when map is added to a CarPlay dashboard scene. + +## 10.13.0-rc.1 - April 19, 2023 + +* Update MapboxCoreMaps to 10.13.0-rc.1 and MapboxCommon to 23.5.0-rc.1. + +## 10.13.0-beta.1 - April 5, 2023 + +* Remove unnecessary check before updating a geo json source. +* Enable zoom during a drag gesture. +* Fix bearing value is fluctuating between initial value and correct value during a rotation gesture. +* Allows animation during any ongoing gestures. +* Sync map size to the size of the metal view. +* Fix visual jitter when an annotation dragging ends. +* Fix missing feature properties for `nil`/`null` values. +* Added experimental `tileCover` method to `MapboxMap` that returns tile ids covering the map. +* Update MapboxCoreMaps to 10.13.0-beta.1 and MapboxCommon to 23.5.0-beta.1. + +## 10.12.1 - March 29, 2023 + +* Bump MapboxCoreMaps to 10.12.1 + +## 10.12.0 - March 22, 2023 + +* Deprecate `Snapshotter.tileMode`. +* Bump MapboxCoreMaps to 10.12.0 and MapboxCommon to 23.4.0 + +## 10.12.0-rc.1 - March 8, 2023 + +* Correct user-agent fragment sent to events/telemetry service. +* Bump MapboxCoreMaps to 10.12.0-rc.1 and MapboxCommon to 23.4.0-rc.1. +* Change annotation end-of-drag delay to 0.125 to minimize lagging. +* Different data types are now used for `querySourceFeatures` and `queryRenderedFeatures`: `QueriedSourceFeature` and `QueriedRenderedFeature`. `QueriedRenderedFeature` has a new field `layer` which contains the queried feature's layer id. +* Remove deprecated `queryRenderedFeatures()` methods. Use `queryRenderedFeatures(with:options:completion:)` instead. +* Remove deprecated `queryFeatureExtension()` method. Use `getGeoJsonClusterLeaves()`/`getGeoJsonClusterChildren()`/`getGeoJsonClusterExpansionZoom()` instead. +* Add the `MapboxMap.resetFeatureState` method. +* Add `callback` argument to the `MapboxMap` methods `getFeatureState`, `setFeatureState`, `removeFeatureState`. +* Return `cancelable` from the `MapboxMap` methods : `getFeatureState`, `setFeatureState`, `removeFeatureState`, `querySourceFeatures`, `getGeoJsonClusterLeaves`, `getGeoJsonClusterChildren`, `getGeoJsonClusterExpansionZoom`. +* The `CameraOptions/padding` field is now optional. + +## 10.12.0-beta.1 - February 22, 2023 + +* Added basic signposts for performance profiling. To enable them, use `MAPBOX_MAPS_SIGNPOSTS_ENABLED` environment variable. ([#1818](https://github.com/mapbox/mapbox-maps-ios/pull/1818)) +* Fix build erros appearing when SDK distributed as a static library through Cocoapods. ([#1888](https://github.com/mapbox/mapbox-maps-ios/pull/1888)) +* Update MapboxCoreMaps to `v10.12.0-beta.1` and MapboxCommon to `v23.4.0-beta.1` +* Fix app extension support. ([#1916](https://github.com/mapbox/mapbox-maps-ios/pull/1916)) +* Allow pass `dataId` to `sourceDataLoaded` event. +* Add a dedicated GestureRecognizer (and Handler) to interrupt deceleration animation on tap on the map. + +## 10.11.0 - February 8, 2023 + +* Update to MapboxCoreMaps 10.11.1 and MapboxCommon 23.3.1. ([#1899](https://github.com/mapbox/mapbox-maps-ios/pull/1899)) + +## 10.11.0-rc.1 - January 26, 2023 + +* Improve stability of attribution parsing. ([#1849](https://github.com/mapbox/mapbox-maps-ios/pull/1849)) +* Enable `Expression` to be created without an operator so `clusterProperties` can support advanced use cases. ([#1855](https://github.com/mapbox/mapbox-maps-ios/pull/1855)) +* Update CoreMaps `10.11.0-rc.1` and CommonSDK `23.3.0-rc.1`. ([#1856](https://github.com/mapbox/mapbox-maps-ios/pull/1875)) +* Angle normalization function was improved to prevent map spinning on close angles. ([#1828](https://github.com/mapbox/mapbox-maps-ios/pull/1828)) + +## 10.11.0-beta.1 - January 12, 2023 + +* Reduce CPU usage/energy consumption whem map idling while showing user location. ([#1789](https://github.com/mapbox/mapbox-maps-ios/pull/1789)) +* Fix loading errors appearing when providing custom endpoint for `ResourceOptions.baseURL`. ([#1749](https://github.com/mapbox/mapbox-maps-ios/pull/1749)) +* Remove delegate requirement for annotation interaction. ([#1750](https://github.com/mapbox/mapbox-maps-ios/pull/1750)) +* Reset compass image to default one if `nil` was passed to the `MapboxCompassOrnamentView.updateImage(image:)`. ([#1766](https://github.com/mapbox/mapbox-maps-ios/pull/1766), [#1772](https://github.com/mapbox/mapbox-maps-ios/pull/1772)) +* Prevent `PointAnnotationManager` to remove images that are not owned by it from Style. ([#1775](https://github.com/mapbox/mapbox-maps-ios/pull/1775)) +* Use `sdf` parameter when adding a style image. ([#1803](https://github.com/mapbox/mapbox-maps-ios/pull/1803)) +* Fix scale bar grows beyond its maximum width at large zoom near north/south poles. ([#1802](https://github.com/mapbox/mapbox-maps-ios/pull/1802)) +* Improve GeoJSONSource add/update performance by passing `GeoJSONSourceData` directly. ([#1815](https://github.com/mapbox/mapbox-maps-ios/pull/1815)) +* Support `Expression` in `FormatOptions`. ([#1826](https://github.com/mapbox/mapbox-maps-ios/pull/1826)) +* Update to MapboxCoreMaps 10.11.0-beta.1 and MapboxCommon 23.3.0-beta.1. ([#1842](https://github.com/mapbox/mapbox-maps-ios/pull/1842)) + +## 10.10.1 - December 20, 2022 + +* [CarPlay] Fix map view permanently pausing when moving to window on foreground ([#1808](https://github.com/mapbox/mapbox-maps-ios/pull/1808)) + +## 10.10.0 - December 8, 2022 + +* Update CoreMaps and CommonSDK. ([#1777](https://github.com/mapbox/mapbox-maps-ios/pull/1777)) + +## 10.10.0-rc.1 - November 18, 2022 + +* Fix memory leak when viewport is being deallocated while transition is running. ([#1691](https://github.com/mapbox/mapbox-maps-ios/pull/1691)) +* Fix issue with simultaneous recognition of tap gesture. ([#1712](https://github.com/mapbox/mapbox-maps-ios/pull/1712)) +* Fix label localization to properly handle Simplified and Traditional Chinese. ([#1687](https://github.com/mapbox/mapbox-maps-ios/pull/1687)) +* Allow simultaneous recognition of map- and annotation- handling gesture recognizers. ([#1737](https://github.com/mapbox/mapbox-maps-ios/pull/1737)) +* Update MapboxCommon to `v23.2.0-rc.3`. ([#1738](https://github.com/mapbox/mapbox-maps-ios/pull/1738)) + +## 10.10.0-beta.1 - November 4, 2022 + +* Animates to camera that fit a list of view annotations. ([#1634](https://github.com/mapbox/mapbox-maps-ios/pull/1634)) +* Prevent view annotation being shown erroneously after options update.([#1627](https://github.com/mapbox/mapbox-maps-ios/pull/1627)) +* Add an example animating a view annotation along a route line. ([#1639](https://github.com/mapbox/mapbox-maps-ios/pull/1639)) +* Enable clustering of point annotations, add example of feature. ([#1475](https://github.com/mapbox/mapbox-maps-ios/issues/1475)) +* Reduce location provider heading orientation update frequency. ([#1618](https://github.com/mapbox/mapbox-maps-ios/pull/1618)) +* Expose the list of added view annotations. ([#1621](https://github.com/mapbox/mapbox-maps-ios/pull/1621)) +* Fix `loadStyleURI/loadStyleJSON` completion being invoked more than once. ([#1665](https://github.com/mapbox/mapbox-maps-ios/pull/1665)) +* Remove ornament position deprecation. ([#1676](https://github.com/mapbox/mapbox-maps-ios/pull/1676)) +* Prevent map from being rendered on background. By aligning better with Scene lifecycle API, as well as, respecting scene/application activation status, rendering artifacts should no longer be an issue after app is coming from background. ([#1675](https://github.com/mapbox/mapbox-maps-ios/pull/1675)) +* Support `isDraggable` and `isSelected` properties for annotations. ([#1659](https://github.com/mapbox/mapbox-maps-ios/pull/1659)) +* New API to load custom style JSON on the initilization of MapView. ([#1686](https://github.com/mapbox/mapbox-maps-ios/pull/1686)) +* Update MapboxCoreMaps to `v10.10.0-beta.1` and MapboxCommon to `v23.2.0-beta.1`. ([#1680](https://github.com/mapbox/mapbox-maps-ios/pull/1680)) +* Add API to enable/disable render of world copies. ([#1684](https://github.com/mapbox/mapbox-maps-ios/pull/1684)) +* Avoid triggering assertion for the 3D puck layer when returning `allLayerIdentifiers`. ([#1650](https://github.com/mapbox/mapbox-maps-ios/pull/1650)) + +## 10.9.0 - October 19, 2022 + +* Update to MapboxCoreMaps 10.9.0 and MapboxCommon 23.1.0. ([#1652](https://github.com/mapbox/mapbox-maps-ios/pull/1652)) + +## 10.9.0-rc.1 - October 7, 2022 + +* Fix accuracy ring radius jumping when zooming the map in/out with `.reducedAccuracy` location authorization.([#1625](https://github.com/mapbox/mapbox-maps-ios/pull/1625)) +* Fix behavior with initial view annotation placement.([#1604](https://github.com/mapbox/mapbox-maps-ios/pull/1604)) +* Fix behavior where selected view annotation is not moved to correct z-order.([#1607](https://github.com/mapbox/mapbox-maps-ios/pull/1607)) +* Update MapboxCoreMaps to `v10.9.0-rc.1`. ([#1630](https://github.com/mapbox/mapbox-maps-ios/pull/1630)) +* Update MapboxCommon to `v21.1.0-rc.2`. ([#1630](https://github.com/mapbox/mapbox-maps-ios/pull/1630)) + +## 10.9.0-beta.2 - September 29, 2022 + +* Replace MapboxMobileEvents dependency with CoreTelemetry (part of MapboxCommon). ([#1379](https://github.com/mapbox/mapbox-maps-ios/pull/1379)) + +## 10.9.0-beta.1 - September 21, 2022 + +* Expose `ResourceRequest` properties publicly. ([#1548](https://github.com/mapbox/mapbox-maps-ios/pull/1548)) +* Parse GeoJSON data on a background queue. ([#1576](https://github.com/mapbox/mapbox-maps-ios/pull/1576)) +* Fix block retain cycle in `MapboxMap/observeStyleLoad(_:)`, from now on `loadStyleURI` and `loadStyleJSON` completion block will not be invoked when MapboxMap is deallocated. ([#1575](https://github.com/mapbox/mapbox-maps-ios/pull/1575)) +* Remove `DictionaryEncoder` enforce nil encoding for nested level of the dictionary. ([#1565](https://github.com/mapbox/mapbox-maps-ios/pull/1565)) +* Expose `distance-from-center` and `pitch` expressions. ([#1559](https://github.com/mapbox/mapbox-maps-ios/pull/1559)) +* Expose location puck opacity. ([#1585](https://github.com/mapbox/mapbox-maps-ios/pull/1585)) +* Update MapboxCoreMaps to v10.9.0-beta.1 and MapboxCommon to v23.1.0-beta.1. ([#1589](https://github.com/mapbox/mapbox-maps-ios/pull/1589)) + +## 10.8.1 - September 8, 2022 + +* Downgrade MME to 1.0.8. ([#1572](https://github.com/mapbox/mapbox-maps-ios/pull/1572)) +* Update to MapboxCoreMaps 10.8.0 and MapboxCommon 23.0.0. ([#1564](https://github.com/mapbox/mapbox-maps-ios/pull/1564)) + +## 10.8.0 - September 8, 2022 + + ⚠️ MapboxMaps SDK iOS v10.8.0 contained a defect. Although this bug had no known end-user impact, 10.8.0 should not be used in production and has been removed from availability. v10.8.1 resolves the issue and is a drop-in replacement. + +## 10.8.0-rc.1 - August 24, 2022 + +* Apply mercator scale to 3D puck also when its `modelScale` is not specified. ([#1523](https://github.com/mapbox/mapbox-maps-ios/pull/1523)) + +## 10.8.0-beta.1 - August 11, 2022 + +* Expose image property for compass ornament. ([#1468](https://github.com/mapbox/mapbox-maps-ios/pull/1468)) * Expand scale bar range up to 15000 km/10000 miles. ([#1455](https://github.com/mapbox/mapbox-maps-ios/pull/1455)) * Add the ability to override scale bar units. ([#1473](https://github.com/mapbox/mapbox-maps-ios/pull/1473)) * Animate padding changes between 2 camera when used with `FlyToCameraAnimator`. ([#1479](https://github.com/mapbox/mapbox-maps-ios/pull/1479)) +* Fix NaN latitude crash rarely happening in `CameraAnimationsManager.fly(to:duration:completion)`. ([#1485](https://github.com/mapbox/mapbox-maps-ios/pull/1485)) +* Fix `Style.updateLayer(withId:type:update)` so resetting a layer's properties should work. ([#1476](https://github.com/mapbox/mapbox-maps-ios/pull/1476)) +* Add the ability to display heading calibration alert. ([#1509](https://github.com/mapbox/mapbox-maps-ios/pull/1509)) +* Add support for sonar-like pulsing animation around 2D puck. ([#1513](https://github.com/mapbox/mapbox-maps-ios/pull/1513)) +* Support view annotation lookup by an identifier. ([#1512](https://github.com/mapbox/mapbox-maps-ios/pull/1512)) + +## 10.7.0 - July 28, 2022 + +* Update to MapboxCoreMaps 10.7.0 and MapboxCommon 22.1.0. ([#1492](https://github.com/mapbox/mapbox-maps-ios/pull/1492)) +* Limit `MapboxMap.points(for:)` to the bounds of the map view, if the coordinate's point is beyond then return (-1, -1) for its corresponding point.([#1490](https://github.com/mapbox/mapbox-maps-ios/pull/1490)) +* Remove experimental ModelLayer API. ([#1486](https://github.com/mapbox/mapbox-maps-ios/pull/1486)) ## 10.7.0-rc.1 - July 14, 2022 * Add rotation threshold to prevent map from being rotated accidentally. ([#1429](https://github.com/mapbox/mapbox-maps-ios/pull/1429)) * Introduce `GestureOptions.simultaneousRotateAndPinchZoomEnabled` and deprecate `GestureOptions.pinchRotateEnabled` in favor of `GestureOptions.rotateEnabled`. ([1429](https://github.com/mapbox/mapbox-maps-ios/pull/1429)) -* Add experimental `Puck3DConfiguration.modelCastShadows` option to control shadow casting for the 3D puck. ([#1435](https://github.com/mapbox/mapbox-maps-ios/pull/1435)) * Expose public initializer for `TilesetDescriptorOptionsForTilesets`. ([#1431](https://github.com/mapbox/mapbox-maps-ios/pull/1431)) * Fix view annotation losing its feature association after update. ([#1446](https://github.com/mapbox/mapbox-maps-ios/pull/1446)) * Update CoreMaps to `10.7.0-rc.1`. ([#1456](https://github.com/mapbox/mapbox-maps-ios/pull/1456)) @@ -21,7 +990,6 @@ Mapbox welcomes participation and contributions from everyone. * Introduce `FillExtrusionLayer.fillExtrusionAmbientOcclusionIntensity` and `FillExtrusionLayer.fillExtrusionAmbientOcclusionRadius` properties for FillExtrusionLayer. ([1410](https://github.com/mapbox/mapbox-maps-ios/pull/1410)) * Introduce `PointAnnotation.textLineHeight` and deprecated `PointAnnotationManager.textLineHeight`, as `text-line-height` is data-driven property now. ([1410](https://github.com/mapbox/mapbox-maps-ios/pull/1410)) -* Expose experimental shadow APIs for `Light`. ([#1409](https://github.com/mapbox/mapbox-maps-ios/pull/1409)) * Remove experimental annotation from Viewport API. ([#1392](https://github.com/mapbox/mapbox-maps-ios/pull/1392)) * Remove deprecated `animationDuration` parameter in `FollowPuckViewportStateOptions` initializer.([#1390](https://github.com/mapbox/mapbox-maps-ios/pull/1390)) * Deprecate existing QueryRenderedFeatures methods and add cancellable counterparts. ([#1378](https://github.com/mapbox/mapbox-maps-ios/pull/1378)) @@ -131,7 +1099,7 @@ Mapbox welcomes participation and contributions from everyone. * Prevent rendering in background by pausing/resuming display link in response to application or scene lifecycle events. ([#1086](https://github.com/mapbox/mapbox-maps-ios/pull/1086)) * Sync viewport and puck animations. ([#1090](https://github.com/mapbox/mapbox-maps-ios/pull/1090)) * Add puckBearingEnabled property for location. ([#1107](https://github.com/mapbox/mapbox-maps-ios/pull/1107)) -* Fix camera change events being fired after map has stopped moving. ([#1118])(https://github.com/mapbox/mapbox-maps-ios/pull/1118)) +* Fix camera change events being fired after map has stopped moving. ([#1118](https://github.com/mapbox/mapbox-maps-ios/pull/1118)) * Fix issue where single tap and double tap to zoom in gestures could recognize simultaneously. ([#1113](https://github.com/mapbox/mapbox-maps-ios/pull/1113)) * Remove experimental GestureOptions.pinchBehavior property. ([#1125](https://github.com/mapbox/mapbox-maps-ios/pull/1125)) * Update to MapboxCoreMaps 10.4.0-beta.1 and MapboxCommon 21.2.0-beta.1. ([#1126](https://github.com/mapbox/mapbox-maps-ios/pull/1126)) @@ -169,6 +1137,7 @@ Mapbox welcomes participation and contributions from everyone. * Fixed a bug where setting LocationManager.options would cause the LocationProvider to be reconfigured. ([#992](https://github.com/mapbox/mapbox-maps-ios/pull/992)) ## 10.2.0 - December 15, 2021 + * Update to MapboxCoreMaps 10.2.0 and MapboxCommon 21.0.1. ([#952](https://github.com/mapbox/mapbox-maps-ios/pull/952)) * Fix the crash when MapView had zero width or height. ([#903](https://github.com/mapbox/mapbox-maps-ios/pull/903)) @@ -294,6 +1263,7 @@ Mapbox welcomes participation and contributions from everyone. * Adds `TileStoreObserver` protocol. ([#737](https://github.com/mapbox/mapbox-maps-ios/pull/737)) ### Bug fixes 🐞 + * Fix rendering artifacts for a model layer when `model-opacity` property is used. ([#732](https://github.com/mapbox/mapbox-maps-ios/pull/732)) * Improve rendering performance by avoiding unnecessary re-layout for cached tiles. ([#732](https://github.com/mapbox/mapbox-maps-ios/pull/732)) * Fix telemetry opt-out through attribution dialog. ([#743](https://github.com/mapbox/mapbox-maps-ios/pull/743)) @@ -393,7 +1363,6 @@ Mapbox welcomes participation and contributions from everyone. * Location puck can now hide the accuracy ring. The default value is to hide the accuracy ring. In order to enable the ring, set the `showAccuracyRing` property in `Puck2DConfiguration` to `true`. [#629](https://github.com/mapbox/mapbox-maps-ios/pull/629) * Annotation interaction delegates are only called when at least one annotation is detected to have been tapped ([638](https://github.com/mapbox/mapbox-maps-ios/issues/638)) - ### Bug fixes 🐞 * Fix volatile tiles disappearing on "not modified" response ([#628](https://github.com/mapbox/mapbox-maps-ios/pull/628)) @@ -409,6 +1378,7 @@ Mapbox welcomes participation and contributions from everyone. * Fix issue where annotations were not being returned to annotation interaction delegates ([638](https://github.com/mapbox/mapbox-maps-ios/issues/638)) ### Breaking changes ⚠️ + * `TileStore.tileRegionGeometry(forId: String, completion: @escaping (Result) -> Void)` has been updated to `TileStore.tileRegionGeometry(forId: String, completion: @escaping (Result) -> Void)`. ([#661](https://github.com/mapbox/mapbox-maps-ios/pull/661)) ## 10.0.0-rc.7 - August 25, 2021 @@ -416,9 +1386,9 @@ Mapbox welcomes participation and contributions from everyone. ### Features ✨ and improvements 🏁 * Add support for `FeatureState` in GeoJSON sources. ([#611](https://github.com/mapbox/mapbox-maps-ios/pull/611)) - * `setFeatureState(sourceId:sourceLayerId:featureId:state:)` is used to associate a `stateMap` for a particular feature - * `getFeatureState(sourceId:sourceLayerId:featureId:callback:)` is used to retrieve a previously stored `stateMap` for a feature - * `removeFeatureState(sourceId:sourceLayerId:featureId:stateKey:)` is used to remove a previously stored `stateMap` for a feature + * `setFeatureState(sourceId:sourceLayerId:featureId:state:)` is used to associate a `stateMap` for a particular feature + * `getFeatureState(sourceId:sourceLayerId:featureId:callback:)` is used to retrieve a previously stored `stateMap` for a feature + * `removeFeatureState(sourceId:sourceLayerId:featureId:stateKey:)` is used to remove a previously stored `stateMap` for a feature * Added `GeoJSONSource.generateId` ([#593](https://github.com/mapbox/mapbox-maps-ios/pull/593)) * Enable the combined usage of line-dasharray with line-gradient ([#588](https://github.com/mapbox/mapbox-maps-ios/pull/588)) * Fixed rendering issue for round line-join in line gradients ([#594](https://github.com/mapbox/mapbox-maps-ios/pull/594)) @@ -454,9 +1424,10 @@ Mapbox welcomes participation and contributions from everyone. * Fixed an issue where `MapView` positioning wasn't correct when used in containers such as UIStackView. ([#533](https://github.com/mapbox/mapbox-maps-ios/pull/533)) ### Features ✨ and improvements 🏁 + * Added new options to `MapSnapshotOptions` - * `showsLogo` is a flag that will decide whether the logo will be shown on a snapshot - * `showsAttribution` is a flag that will decide whether the attribution will be shown on a snapshot + * `showsLogo` is a flag that will decide whether the logo will be shown on a snapshot + * `showsAttribution` is a flag that will decide whether the attribution will be shown on a snapshot ## 10.0.0-rc.4 - July 14, 2021 @@ -504,14 +1475,16 @@ Mapbox welcomes participation and contributions from everyone. ### Features ✨ and improvements 🏁 * Introduced experimental `Style._addPersistentLayer(with:layerPosition:)`, `Style._isPersistentLayer(id:)`, `Style._addPersistentCustomLayer(withId:layerHost:layerPosition:)` APIs, so that the tagged layer and its associated resources remain when a style is reloaded. This improves performance of annotations during a style change. Experimental APIs should be considered liable to change in any SEMVER version. ([#471](https://github.com/mapbox/mapbox-maps-ios/pull/471), [#473](https://github.com/mapbox/mapbox-maps-ios/pull/473)) -- Annotations now will persist across style changes by default. ([#475](https://github.com/mapbox/mapbox-maps-ios/pull/475)) -- Adds localization support for v10 Maps SDK. This can be used by setting the `mapView.locale`. Use the `SupportedLanguages` enum, which lists currently supported `Locale`. ([#480](https://github.com/mapbox/mapbox-maps-ios/pull/480)) -- Fixed Tileset descriptor bug: Completion handler is called even if the `OfflineManager` instance goes out of scope. -- Fixed text rendering when both 'text-rotate' and 'text-offset' are set. + +* Annotations now will persist across style changes by default. ([#475](https://github.com/mapbox/mapbox-maps-ios/pull/475)) + +* Adds localization support for v10 Maps SDK. This can be used by setting the `mapView.locale`. Use the `SupportedLanguages` enum, which lists currently supported `Locale`. ([#480](https://github.com/mapbox/mapbox-maps-ios/pull/480)) +* Fixed Tileset descriptor bug: Completion handler is called even if the `OfflineManager` instance goes out of scope. +* Fixed text rendering when both 'text-rotate' and 'text-offset' are set. ### Breaking changes ⚠️ -- MapboxMaps now pins exactly to `MapboxCommon`. ([#485](https://github.com/mapbox/mapbox-maps-ios/pull/485), [#481](https://github.com/mapbox/mapbox-maps-ios/pull/481)) +* MapboxMaps now pins exactly to `MapboxCommon`. ([#485](https://github.com/mapbox/mapbox-maps-ios/pull/485), [#481](https://github.com/mapbox/mapbox-maps-ios/pull/481)) ## 10.0.0-rc.1 - June 9, 2021 @@ -519,146 +1492,151 @@ Mapbox welcomes participation and contributions from everyone. ### Breaking changes ⚠️ -- Converted `MapSnapshotOptions` to a struct. ([#430](https://github.com/mapbox/mapbox-maps-ios/pull/430)) -- Removed `CacheManager`. In the following releases, an API to control temporary map data may be provided. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) -- Changed `ResourceOptions.cachePathURL` to `dataPathURL` and removed `cacheSize`. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) -- Annotations don't have a `type` property since they can be directly compared to a type. ([451](https://github.com/mapbox/mapbox-maps-ios/pull/451)) -- Internalize extensions of Core and Common types. ([#449](https://github.com/mapbox/mapbox-maps-ios/pull/449)) +* Converted `MapSnapshotOptions` to a struct. ([#430](https://github.com/mapbox/mapbox-maps-ios/pull/430)) +* Removed `CacheManager`. In the following releases, an API to control temporary map data may be provided. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) +* Changed `ResourceOptions.cachePathURL` to `dataPathURL` and removed `cacheSize`. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) +* Annotations don't have a `type` property since they can be directly compared to a type. ([451](https://github.com/mapbox/mapbox-maps-ios/pull/451)) +* Internalize extensions of Core and Common types. ([#449](https://github.com/mapbox/mapbox-maps-ios/pull/449)) ### Features ✨ and improvements 🏁 -- Allows a developer to choose whether the puck is oriented based on `heading` or `course` via a new `puckBearingSource` option in `mapView.location.options`. By default, the puck will be oriented using `heading`. ([#428](https://github.com/mapbox/mapbox-maps-ios/pull/428)) -- All stock gesture recognizers are now public on the `GestureManager`. ([450](https://github.com/mapbox/mapbox-maps-ios/pull/450)) -- The tap gesture recognizer controlled by any given annotation manager is now public. ([451](https://github.com/mapbox/mapbox-maps-ios/pull/451)) + +* Allows a developer to choose whether the puck is oriented based on `heading` or `course` via a new `puckBearingSource` option in `mapView.location.options`. By default, the puck will be oriented using `heading`. ([#428](https://github.com/mapbox/mapbox-maps-ios/pull/428)) + +* All stock gesture recognizers are now public on the `GestureManager`. ([450](https://github.com/mapbox/mapbox-maps-ios/pull/450)) +* The tap gesture recognizer controlled by any given annotation manager is now public. ([451](https://github.com/mapbox/mapbox-maps-ios/pull/451)) ### Bug fixes 🐞 -- Fixed a bug where animations were not always honored. ([#443](https://github.com/mapbox/mapbox-maps-ios/pull/443)) -- Fixed an issue that vertical text was not positioned correctly if the `text-offset` property was used. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) -- Emit `.mapLoadingError` when an empty token is provided for accessing Mapbox data sources. Before the fix, the application may crash if an empty token was provided and map tries to load data from Mapbox data source. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) -- Do not emit `.mapLoadingError` when an empty URL is set to GeoJSON source. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) +* Fixed a bug where animations were not always honored. ([#443](https://github.com/mapbox/mapbox-maps-ios/pull/443)) +* Fixed an issue that vertical text was not positioned correctly if the `text-offset` property was used. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) +* Emit `.mapLoadingError` when an empty token is provided for accessing Mapbox data sources. Before the fix, the application may crash if an empty token was provided and map tries to load data from Mapbox data source. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) +* Do not emit `.mapLoadingError` when an empty URL is set to GeoJSON source. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) ### Dependencies -- Updated MapboxCoreMaps, MapboxCommon and Turf dependencies. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) +* Updated MapboxCoreMaps, MapboxCommon and Turf dependencies. ([#440](https://github.com/mapbox/mapbox-maps-ios/pull/440)) ## 10.0.0-beta.21 - June 3, 2021 ### Breaking changes ⚠️ -- Updated MapboxCoreMaps and MapboxCommon dependencies. ([#388](https://github.com/mapbox/mapbox-maps-ios/pull/388)) - - Removed the `MBX` prefix from `MBXGeometry`, `MBXGeometryType` and `MBXFeature`. Existing uses of the similar Turf types need to be fully namespaced, i.e. `Turf.Feature` - - Introduced separate minZoom/maxZoom fields into CustomGeometrySourceOptions API instead of the formerly used `zoomRange` - - Improved zooming performance. - - Fixed terrain transparency issue when a sky layer is not used. -- `MapboxMap.__map` is now private. ([#374](https://github.com/mapbox/mapbox-maps-ios/pull/374)) -- Added `CameraManagerProtocol.setCameraBounds`, `MapboxMap.prefetchZoomDelta`, `MapboxMap.options`, `MapboxMap.reduceMemoryUse()`, `MapboxMap.resourceOptions` and `MapboxMap.elevation(at:)`. ([#374](https://github.com/mapbox/mapbox-maps-ios/pull/374)) -- Removed `OfflineError.invalidResult` and `OfflineError.typeMismatch`. ([#374](https://github.com/mapbox/mapbox-maps-ios/pull/374)) -- Updated `Projection` APIs to be more Swift-like. ([#390](https://github.com/mapbox/mapbox-maps-ios/pull/390)) -- Added `ResourceOptionsManager` and removed `CredentialsManager` which it replaces. `ResourceOptions` is now a struct. ([#396](https://github.com/mapbox/mapbox-maps-ios/pull/396)) -- Updated the ambient cache path. ([#396](https://github.com/mapbox/mapbox-maps-ios/pull/396)) -- Removed `CameraAnimationsManager.setCamera()` and renamed `CameraManagerProtocol._setCamera` to `CameraManagerProtocol.setCamera()`. Use `MapView.mapboxMap.setCamera()` to set the camera. ([#426](https://github.com/mapbox/mapbox-maps-ios/pull/426)) -- Removed `MapCameraOptions` and `RenderOptions`; this behavior has moved to both `MapboxMap` and `MapView`. ([#427](https://github.com/mapbox/mapbox-maps-ios/pull/427/files)) -- The Annotations library has been rebuilt to expose many more customization options for each annotation. ([#398](https://github.com/mapbox/mapbox-maps-ios/pull/398)) -- High level animations return `Cancelable` instead of `CameraAnimator`. ([#400](https://github.com/mapbox/mapbox-maps-ios/pull/400)) +* Updated MapboxCoreMaps and MapboxCommon dependencies. ([#388](https://github.com/mapbox/mapbox-maps-ios/pull/388)) + * Removed the `MBX` prefix from `MBXGeometry`, `MBXGeometryType` and `MBXFeature`. Existing uses of the similar Turf types need to be fully namespaced, i.e. `Turf.Feature` + * Introduced separate minZoom/maxZoom fields into CustomGeometrySourceOptions API instead of the formerly used `zoomRange` + * Improved zooming performance. + * Fixed terrain transparency issue when a sky layer is not used. +* `MapboxMap.__map` is now private. ([#374](https://github.com/mapbox/mapbox-maps-ios/pull/374)) +* Added `CameraManagerProtocol.setCameraBounds`, `MapboxMap.prefetchZoomDelta`, `MapboxMap.options`, `MapboxMap.reduceMemoryUse()`, `MapboxMap.resourceOptions` and `MapboxMap.elevation(at:)`. ([#374](https://github.com/mapbox/mapbox-maps-ios/pull/374)) +* Removed `OfflineError.invalidResult` and `OfflineError.typeMismatch`. ([#374](https://github.com/mapbox/mapbox-maps-ios/pull/374)) +* Updated `Projection` APIs to be more Swift-like. ([#390](https://github.com/mapbox/mapbox-maps-ios/pull/390)) +* Added `ResourceOptionsManager` and removed `CredentialsManager` which it replaces. `ResourceOptions` is now a struct. ([#396](https://github.com/mapbox/mapbox-maps-ios/pull/396)) +* Updated the ambient cache path. ([#396](https://github.com/mapbox/mapbox-maps-ios/pull/396)) +* Removed `CameraAnimationsManager.setCamera()` and renamed `CameraManagerProtocol._setCamera` to `CameraManagerProtocol.setCamera()`. Use `MapView.mapboxMap.setCamera()` to set the camera. ([#426](https://github.com/mapbox/mapbox-maps-ios/pull/426)) +* Removed `MapCameraOptions` and `RenderOptions`; this behavior has moved to both `MapboxMap` and `MapView`. ([#427](https://github.com/mapbox/mapbox-maps-ios/pull/427/files)) +* The Annotations library has been rebuilt to expose many more customization options for each annotation. ([#398](https://github.com/mapbox/mapbox-maps-ios/pull/398)) +* High level animations return `Cancelable` instead of `CameraAnimator`. ([#400](https://github.com/mapbox/mapbox-maps-ios/pull/400)) ### Bug fixes 🐞 -- Fixed a bug with `TileStore.tileRegionGeometry` returning invalid value. ([#390](https://github.com/mapbox/mapbox-maps-ios/pull/390)) -- Fixed a bug where the underlying renderer was not being destroyed. ([#395](https://github.com/mapbox/mapbox-maps-ios/pull/395)) -- Fixed a bug where the snapshotter completion handler was being called twice on cancellation. +* Fixed a bug with `TileStore.tileRegionGeometry` returning invalid value. ([#390](https://github.com/mapbox/mapbox-maps-ios/pull/390)) +* Fixed a bug where the underlying renderer was not being destroyed. ([#395](https://github.com/mapbox/mapbox-maps-ios/pull/395)) +* Fixed a bug where the snapshotter completion handler was being called twice on cancellation. ([#382](https://github.com/mapbox/mapbox-maps-ios/pull/382)) -- Fixed a bug where `GestureManager.delegate` was inaccessible. ([#401](https://github.com/mapbox/mapbox-maps-ios/pull/401)) +* Fixed a bug where `GestureManager.delegate` was inaccessible. ([#401](https://github.com/mapbox/mapbox-maps-ios/pull/401)) ### Features ✨ and improvements 🏁 -- Added `Snapshotter.coordinateBounds(for:)` and `Snapshotter.camera(for:padding:bearing:pitch:)`. ([#386](https://github.com/mapbox/mapbox-maps-ios/pull/386)) +* Added `Snapshotter.coordinateBounds(for:)` and `Snapshotter.camera(for:padding:bearing:pitch:)`. ([#386](https://github.com/mapbox/mapbox-maps-ios/pull/386)) ### Development 🛠 -- Dependency management for development of the SDK has moved to Swift Package Manager and the existing Cartfile has been removed. +* Dependency management for development of the SDK has moved to Swift Package Manager and the existing Cartfile has been removed. ## 10.0.0-beta.20 - May 20, 2021 ### Breaking changes ⚠️ - - `BaseMapView.on()` has now been replaced by `mapView.mapboxMap.onNext(...) -> Cancelable` and `mapView.mapboxMap.onEvery(...) -> Cancelable`. ([#339](https://github.com/mapbox/mapbox-maps-ios/pull/339)) - - `StyleURI`, `PreferredFPS`, and `AnimationOwner` are now structs. ([#285](https://github.com/mapbox/mapbox-maps-ios/pull/285)) - - The `layout` and `paint` substructs for each layer are now merged into the root layer struct. ([#362](https://github.com/mapbox/mapbox-maps-ios/pull/362)) - - `GestureOptions` are owned by `GestureManager` directly. ([#343](https://github.com/mapbox/mapbox-maps-ios/pull/343)) - - `LocationOptions` are owned by `LocationManager` directly. ([#344](https://github.com/mapbox/mapbox-maps-ios/pull/344)) - - `MapCameraOptions` are owned by `mapView.camera` directly. ([#345](https://github.com/mapbox/mapbox-maps-ios/pull/345)) - - `RenderOptions` are owned by `BaseMapView` directly. ([#350](https://github.com/mapbox/mapbox-maps-ios/pull/350)) - - `AnnotationOptions` are owned by `AnnotationManager` directly. ([#351](https://github.com/mapbox/mapbox-maps-ios/pull/351)) - - `MapView` has been coalesced into `BaseMapView` and the resulting object is called `MapView`. ([#353](https://github.com/mapbox/mapbox-maps-ios/pull/353)) - - `Style.uri` is now an optional property. ([#347](https://github.com/mapbox/mapbox-maps-ios/pull/347)) - - `Style` is no longer a dependency on `LocationSupportableMapView`. ([#352](https://github.com/mapbox/mapbox-maps-ios/pull/352)) - - `Style` now has a more flat structure. `Layout` and `Paint` structs are now obsolete and `Layer` properties are at the root layer. ([#362](https://github.com/mapbox/mapbox-maps-ios/pull/362)) - - Changed `LayerPosition` to an enum. ([#](https://github.com/mapbox/mapbox-maps-ios/pull/221)) - - Removed `style` from MapView; updated tests and examples to use `mapboxMap.style`. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) - - The `visibleFeatures` APIs have been renamed to `queryRenderedFeatures`. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) - - `LoggingConfiguration` is no longer public. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) - - The following Swift wrappers have been added for existing types; these primarily change callbacks from using an internal `MBXExpected` type to using Swift's `Result` type. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) - - `CacheManager` - - `HttpResponse` - - `OfflineSwitch` (which replaces NetworkConnectivity) - - `OfflineRegionManager` (though this API is deprecated) - - Adds `loadStyleURI` and `loadStyleJSON` to `MapboxMap`. ([#354](https://github.com/mapbox/mapbox-maps-ios/pull/354)) +* `BaseMapView.on()` has now been replaced by `mapView.mapboxMap.onNext(...) -> Cancelable` and `mapView.mapboxMap.onEvery(...) -> Cancelable`. ([#339](https://github.com/mapbox/mapbox-maps-ios/pull/339)) +* `StyleURI`, `PreferredFPS`, and `AnimationOwner` are now structs. ([#285](https://github.com/mapbox/mapbox-maps-ios/pull/285)) +* The `layout` and `paint` substructs for each layer are now merged into the root layer struct. ([#362](https://github.com/mapbox/mapbox-maps-ios/pull/362)) +* `GestureOptions` are owned by `GestureManager` directly. ([#343](https://github.com/mapbox/mapbox-maps-ios/pull/343)) +* `LocationOptions` are owned by `LocationManager` directly. ([#344](https://github.com/mapbox/mapbox-maps-ios/pull/344)) +* `MapCameraOptions` are owned by `mapView.camera` directly. ([#345](https://github.com/mapbox/mapbox-maps-ios/pull/345)) +* `RenderOptions` are owned by `BaseMapView` directly. ([#350](https://github.com/mapbox/mapbox-maps-ios/pull/350)) +* `AnnotationOptions` are owned by `AnnotationManager` directly. ([#351](https://github.com/mapbox/mapbox-maps-ios/pull/351)) +* `MapView` has been coalesced into `BaseMapView` and the resulting object is called `MapView`. ([#353](https://github.com/mapbox/mapbox-maps-ios/pull/353)) +* `Style.uri` is now an optional property. ([#347](https://github.com/mapbox/mapbox-maps-ios/pull/347)) +* `Style` is no longer a dependency on `LocationSupportableMapView`. ([#352](https://github.com/mapbox/mapbox-maps-ios/pull/352)) +* `Style` now has a more flat structure. `Layout` and `Paint` structs are now obsolete and `Layer` properties are at the root layer. ([#362](https://github.com/mapbox/mapbox-maps-ios/pull/362)) +* Changed `LayerPosition` to an enum. ([#](https://github.com/mapbox/mapbox-maps-ios/pull/221)) +* Removed `style` from MapView; updated tests and examples to use `mapboxMap.style`. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) +* The `visibleFeatures` APIs have been renamed to `queryRenderedFeatures`. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) +* `LoggingConfiguration` is no longer public. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) +* The following Swift wrappers have been added for existing types; these primarily change callbacks from using an internal `MBXExpected` type to using Swift's `Result` type. ([#361](https://github.com/mapbox/mapbox-maps-ios/pull/361)) + * `CacheManager` + * `HttpResponse` + * `OfflineSwitch` (which replaces NetworkConnectivity) + * `OfflineRegionManager` (though this API is deprecated) +* Adds `loadStyleURI` and `loadStyleJSON` to `MapboxMap`. ([#354](https://github.com/mapbox/mapbox-maps-ios/pull/354)) ### Bug fixes 🐞 -- Fixed an issue where the map's scale bar and compass view could trigger `layoutSubviews()` for the map view. ([#338](https://github.com/mapbox/mapbox-maps-ios/pull/338)) +* Fixed an issue where the map's scale bar and compass view could trigger `layoutSubviews()` for the map view. ([#338](https://github.com/mapbox/mapbox-maps-ios/pull/338)) ## 10.0.0-beta.19.1 - May 7, 2021 ### Breaking changes ⚠️ - - `OrnamentOptions.logo._isVisible` and `OrnamentOptions.attributionButton._isVisible` have been replaced with `OrnamentOptions.logo.visibility` and `OrnamentOptions.attributionButton.visibility`. ([#326](https://github.com/mapbox/mapbox-maps-ios/pull/326)) +* `OrnamentOptions.logo._isVisible` and `OrnamentOptions.attributionButton._isVisible` have been replaced with `OrnamentOptions.logo.visibility` and `OrnamentOptions.attributionButton.visibility`. ([#326](https://github.com/mapbox/mapbox-maps-ios/pull/326)) ### Bug fixes 🐞 - - Fixed an issue where location pucks would not be rendered. ([#331](https://github.com/mapbox/mapbox-maps-ios/pull/331)) +* Fixed an issue where location pucks would not be rendered. ([#331](https://github.com/mapbox/mapbox-maps-ios/pull/331)) ## 10.0.0-beta.19 - May 6, 2021 ### Breaking changes ⚠️ -- `camera(for:)` methods have moved from `BaseMapView` to `MapboxMap` ([#286](https://github.com/mapbox/mapbox-maps-ios/pull/286)) +* `camera(for:)` methods have moved from `BaseMapView` to `MapboxMap` ([#286](https://github.com/mapbox/mapbox-maps-ios/pull/286)) * The API has also been aligned with Android by: - * Removing default values for parameters - * Making `bearing` and `pitch` parameters optional - * Adding the `camera(for:camera:rect:)` variant -- `OrnamentOptions` should now be accessed via `MapView.ornaments.options`. `MapConfig.ornaments` has been removed. Updates can be applied directly to `OrnamentsManager.options`. Previously the map's ornament options were updated on `MapConfig.ornaments` with `MapView.update`. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) -- `OrnamentOptions` now uses structs to manage options for individual ornaments. For example, `OrnamentOptions.scaleBarPosition` is now `OrnamentOptions.scaleBar.position`. ([#318](https://github.com/mapbox/mapbox-maps-ios/pull/318)) -- The `LogoView` class is now private. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) -- `Style` has been significantly refactored, for example: - - Synchronous APIs returning `Result` types now throw. - - A number of APIs previously accessed via `__map` are now available via the `Style` object. - - APIs with a `get` prefix have been renamed; for example `getLayer(with:type:)` to `layer(withId:type:) throws` and `getSource(id:type:)` to `source(withId:type:) throws` + * Removing default values for parameters + * Making `bearing` and `pitch` parameters optional + * Adding the `camera(for:camera:rect:)` variant +* `OrnamentOptions` should now be accessed via `MapView.ornaments.options`. `MapConfig.ornaments` has been removed. Updates can be applied directly to `OrnamentsManager.options`. Previously the map's ornament options were updated on `MapConfig.ornaments` with `MapView.update`. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) +* `OrnamentOptions` now uses structs to manage options for individual ornaments. For example, `OrnamentOptions.scaleBarPosition` is now `OrnamentOptions.scaleBar.position`. ([#318](https://github.com/mapbox/mapbox-maps-ios/pull/318)) +* The `LogoView` class is now private. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) +* `Style` has been significantly refactored, for example: + * Synchronous APIs returning `Result` types now throw. + * A number of APIs previously accessed via `__map` are now available via the `Style` object. + * APIs with a `get` prefix have been renamed; for example `getLayer(with:type:)` to `layer(withId:type:) throws` and `getSource(id:type:)` to `source(withId:type:) throws` ### Features ✨ and improvements 🏁 -- `OrnamentsManager` is now a public class and can be accessed via the `MapView`'s `ornaments` property. -- `CompassDirectionFormatter` is now public. It provides a string representation of a `CLLocationDirection` and supports the same languages as in pre-v10 versions of the Maps SDK. ([#300](https://github.com/mapbox/mapbox-maps-ios/pull/300))- `OrnamentOptions` should now be accessed via `MapView.ornaments.options`. Updates can be applied directly to the `options` property. Previously the map's ornament options were updated via `MapConfig.ornaments`. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) -- The `LogoView` class is now private. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) +* `OrnamentsManager` is now a public class and can be accessed via the `MapView`'s `ornaments` property. +* `CompassDirectionFormatter` is now public. It provides a string representation of a `CLLocationDirection` and supports the same languages as in pre-v10 versions of the Maps SDK. ([#300](https://github.com/mapbox/mapbox-maps-ios/pull/300))- `OrnamentOptions` should now be accessed via `MapView.ornaments.options`. Updates can be applied directly to the `options` property. Previously the map's ornament options were updated via `MapConfig.ornaments`. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) +* The `LogoView` class is now private. ([#310](https://github.com/mapbox/mapbox-maps-ios/pull/310)) ## 10.0.0-beta.18.1 - April 28, 2021 ### Breaking changes ⚠️ -- #### Camera Animations +* #### Camera Animations + * A new `CameraTransition` struct has been introduced to allow better control on the "from" and "to" values of a camera animation ([#282](https://github.com/mapbox/mapbox-maps-ios/pull/282)) - * A mutable version of the `CameraTransition` struct is passed into every animation block. + * A mutable version of the `CameraTransition` struct is passed into every animation block. * Animations can only be constructor injected into `CameraAnimator` as part of the `makeAnimator*` methods on `mapView.camera`. * The `makeCameraAnimator*` methods have been renamed to `makeAnimator*` methods -- #### Gestures - - Gestures now directly call `__map.setCamera()` instead of using CoreAnimation +* #### Gestures + + * Gestures now directly call `__map.setCamera()` instead of using CoreAnimation ## 10.0.0-beta.18 - April 23, 2021 ### Breaking changes ⚠️ -- #### `MapView` +* #### `MapView` + * The initializer has changed to `public init(frame: CGRect, mapInitOptions: MapInitOptions = MapInitOptions(), styleURI: StyleURI? = .streets)`. * `MapOptions` has been renamed `MapConfig`. A new `MapOptions` has been introduced; its properties are required to initialize the underlying map object. * A `MapInitOptions` configuration struct has been introduced. It currently wraps both `ResourceOptions` and `MapOptions` and is used when initializing a `MapView`. @@ -666,74 +1644,79 @@ Mapbox welcomes participation and contributions from everyone. * The `Manager` suffix has been removed from `MapView.gesturesManager`, `MapView.ornamentsManager`, `MapView.cameraManager`, `MapView.locationManager`, and `MapView.annotationsManager`. * `BaseMapView.camera` has been renamed to `BaseMapView.cameraOptions`. -- #### Foundation +* #### Foundation + * `AccountManager` has been removed. A new `CredentialsManager` replaces it. You can use `CredentialsManager.default` to set a global access token. * MapboxCoreMaps protocol conformances have been encapsulated. ([#265](https://github.com/mapbox/mapbox-maps-ios/pull/265)) - * `ObserverConcrete` has been removed. - * `BaseMapView` no longer conforms to `MapClient` or `MBMMetalViewProvider`, and the methods they required are now internal. - * The setter for `BaseMapView.__map` is now private - * `Snapshotter` no longer conforms to `Observer`, and the method it required is now internal. + * `ObserverConcrete` has been removed. + * `BaseMapView` no longer conforms to `MapClient` or `MBMMetalViewProvider`, and the methods they required are now internal. + * The setter for `BaseMapView.__map` is now private + * `Snapshotter` no longer conforms to `Observer`, and the method it required is now internal. * The `BaseMapView.__map` property has been moved to `BaseMapView.mapboxMap.__map`. ([#280](https://github.com/mapbox/mapbox-maps-ios/pull/280)) * A `CameraOptions` struct has been introduced. This shadows the class of the same name from MapboxCoreMaps and. This avoids unintended sharing and better reflects the intended value semantics of the `CameraOptions` concept. ([#284](https://github.com/mapbox/mapbox-maps-ios/pull/284)) -- #### Dependencies +* #### Dependencies + * Updated dependencies to MapboxCoreMaps 10.0.0-beta.20 and MapboxCommon 11.0.1 * ResourceOptions now contains a `TileStore` instance. Tile store usage is enabled by default, the resource option `tileStoreEnabled` flag is introduced to disable it. * `TileStore` no longer returns cached responses for 401, 403 and unauthorized requests. * Fixed a bug where `TileStore` would not invoke completion closures (when client code did not keep a strong reference to the tile store instance). - ### Features ✨ and improvements 🏁 -- Introduced the `OfflineManager` API that manages style packs and produces tileset descriptors for use with the tile store. The `OfflineManager` and `TileStore` APIs are used in conjunction to download offline regions and associated "style packs". These new APIs replace the deprecated `OfflineRegionManager`. Please see the new `OfflineManager` guide for more details. +* Introduced the `OfflineManager` API that manages style packs and produces tileset descriptors for use with the tile store. The `OfflineManager` and `TileStore` APIs are used in conjunction to download offline regions and associated "style packs". These new APIs replace the deprecated `OfflineRegionManager`. Please see the new `OfflineManager` guide for more details. ### Bug fixes 🐞 -- Fixed a crash in line layer rendering, where the uniform buffer size had an incorrect value. +* Fixed a crash in line layer rendering, where the uniform buffer size had an incorrect value. ## 10.0.0-beta.17 - April 13, 2021 ### Breaking changes ⚠️ -- `AnnotationManager` no longer conforms to `Observer` and no longer has a `peer` ([#246](https://github.com/mapbox/mapbox-maps-ios/pull/246)) -- `AnnotationSupportableMap` is now internal ([#246](https://github.com/mapbox/mapbox-maps-ios/pull/246)) - -- #### MapView - * Initializer has been changed to `public init(frame: CGRect, resourceOptions: ResourceOptions, glyphsRasterizationOptions: GlyphsRasterizationOptions = GlyphsRasterizationOptions.default, styleURI: StyleURI? = .streets)`. - * `StyleURL` has been renamed to `StyleURI` - * `OrnamentSupportableMapView` is not internal. - -- #### Ornaments - * `LayoutPosition` has been deprecated in favor of `OrnamentPosition`. - * `LayoutVisibility` has been deprecated in favor of `OrnamentVisibility`. - * `showsLogoView` has been renamed to `_showsLogoView`. - * `showsCompass` and `showsScale` have been deprecated. Visibility properties can be used to set how the Compass and Scale Bar should be shown. - -- #### Foundation - * `cancelTransitions` has been renamed to `cancelAnimations`. - * [`setCamera()`](https://github.com/mapbox/mapbox-maps-ios/pull/250/files#diff-8fa667141ac423a208a6e7036ed759e7e52fc6940bd58834c1935c2c6ead9c65L177) with individual parameters has been deprecated in favor of [`setCamera(to targetCamera: CameraOptions...)`](https://github.com/mapbox/mapbox-maps-ios/blob/edbf08e37975c81c7ee1cbc4bb046c48d522d306/Sources/MapboxMaps/Foundation/Camera/CameraManager.swift#L140) which requires `CameraOptions`. - * The following camera convenience functions have been removed: - * `public func transitionCoordinateBounds(newCoordinateBounds: CoordinateBounds, animated: Bool = false)` - * `public func transitionCoordinateBounds(to newCoordinateBounds: CoordinateBounds, edgePadding: UIEdgeInsets, animated: Bool = false, completion: ((UIViewAnimatingPosition) -> Void)? = nil)` - * `public func transitionVisibleCoordinates(newCoordinates: [CLLocationCoordinate2D], edgePadding: UIEdgeInsets, animated: Bool = false)` - * `public func transitionVisibleCoordinates(to newCoordinates: [CLLocationCoordinate2D], edgePadding: UIEdgeInsets, bearing: CLLocationDirection, duration: TimeInterval, animated: Bool = false, completion: ((UIViewAnimatingPosition) -> Void)? = nil)` - * `public func resetPosition()` - * `public func resetNorth(_ animated: Bool = false)` - * In `CameraAnimator`, `fractionComplete` is now of type `Double` and `delayFactor` now returns a `Double`. - * `MapboxLogoView` has been renamed to `LogoView`. - * `MapboxLogoSize` has been renamed to `LogoSize`. - -- #### Style - * Initializer is now marked as internal. - * `styleUri` property has been renamed to `uri`. - * The `url` property from `StyleURL` has been removed. - -- #### Expressions - * `init(from: jsonObject)` and `public func jsonObject()` have been removed. - * `Element.op` has been renamed to `Element.operator`. - * `Argument.array` has been renamed to `Argument.numberArray`. - * `ValidExpressionArgument` has been renamed to `ExpressionArgumentConvertible` +* `AnnotationManager` no longer conforms to `Observer` and no longer has a `peer` ([#246](https://github.com/mapbox/mapbox-maps-ios/pull/246)) +* `AnnotationSupportableMap` is now internal ([#246](https://github.com/mapbox/mapbox-maps-ios/pull/246)) + +* #### MapView + + * Initializer has been changed to `public init(frame: CGRect, resourceOptions: ResourceOptions, glyphsRasterizationOptions: GlyphsRasterizationOptions = GlyphsRasterizationOptions.default, styleURI: StyleURI? = .streets)`. + * `StyleURL` has been renamed to `StyleURI` + * `OrnamentSupportableMapView` is not internal. + +* #### Ornaments + + * `LayoutPosition` has been deprecated in favor of `OrnamentPosition`. + * `LayoutVisibility` has been deprecated in favor of `OrnamentVisibility`. + * `showsLogoView` has been renamed to `_showsLogoView`. + * `showsCompass` and `showsScale` have been deprecated. Visibility properties can be used to set how the Compass and Scale Bar should be shown. + +* #### Foundation + + * `cancelTransitions` has been renamed to `cancelAnimations`. + * [`setCamera()`](https://github.com/mapbox/mapbox-maps-ios/pull/250/files#diff-8fa667141ac423a208a6e7036ed759e7e52fc6940bd58834c1935c2c6ead9c65L177) with individual parameters has been deprecated in favor of [`setCamera(to targetCamera: CameraOptions...)`](https://github.com/mapbox/mapbox-maps-ios/blob/edbf08e37975c81c7ee1cbc4bb046c48d522d306/Sources/MapboxMaps/Foundation/Camera/CameraManager.swift#L140) which requires `CameraOptions`. + * The following camera convenience functions have been removed: + * `public func transitionCoordinateBounds(newCoordinateBounds: CoordinateBounds, animated: Bool = false)` + * `public func transitionCoordinateBounds(to newCoordinateBounds: CoordinateBounds, edgePadding: UIEdgeInsets, animated: Bool = false, completion: ((UIViewAnimatingPosition) -> Void)? = nil)` + * `public func transitionVisibleCoordinates(newCoordinates: [CLLocationCoordinate2D], edgePadding: UIEdgeInsets, animated: Bool = false)` + * `public func transitionVisibleCoordinates(to newCoordinates: [CLLocationCoordinate2D], edgePadding: UIEdgeInsets, bearing: CLLocationDirection, duration: TimeInterval, animated: Bool = false, completion: ((UIViewAnimatingPosition) -> Void)? = nil)` + * `public func resetPosition()` + * `public func resetNorth(_ animated: Bool = false)` + * In `CameraAnimator`, `fractionComplete` is now of type `Double` and `delayFactor` now returns a `Double`. + * `MapboxLogoView` has been renamed to `LogoView`. + * `MapboxLogoSize` has been renamed to `LogoSize`. + +* #### Style + + * Initializer is now marked as internal. + * `styleUri` property has been renamed to `uri`. + * The `url` property from `StyleURL` has been removed. + +* #### Expressions + * `init(from: jsonObject)` and `public func jsonObject()` have been removed. + * `Element.op` has been renamed to `Element.operator`. + * `Argument.array` has been renamed to `Argument.numberArray`. + * `ValidExpressionArgument` has been renamed to `ExpressionArgumentConvertible` ### Bug fixes 🐞 diff --git a/Configurations/base.xcconfig b/Configurations/base.xcconfig deleted file mode 100644 index b56d805d6dce..000000000000 --- a/Configurations/base.xcconfig +++ /dev/null @@ -1,132 +0,0 @@ -// Common configurations used across projects and targets -// Sorted alphabetically within sections - -// ----------------------------------------------------------------------------- -// Build System - -ALWAYS_SEARCH_USER_PATHS = NO - -DEVELOPMENT_TEAM = GJZR2MEM28 - -ENABLE_TESTABILITY[config=Release] = NO -ENABLE_TESTABILITY[config=Debug] = YES - -IPHONEOS_DEPLOYMENT_TARGET = 11.0 -MACOSX_DEPLOYMENT_TARGET = - -ONLY_ACTIVE_ARCH[config=Release] = NO -ONLY_ACTIVE_ARCH[config=Debug] = YES - -SDKROOT = iphoneos -SKIP_INSTALL = YES -SUPPORTS_MACCATALYST = NO - -// ----------------------------------------------------------------------------- -// Swift - -SWIFT_VERSION = 5.3 - -SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Debug] = DEBUG -SWIFT_ACTIVE_COMPILATION_CONDITIONS[config=Release] = RELEASE - -SWIFT_OPTIMIZATION_LEVEL[config=Debug] = -Onone -SWIFT_OPTIMIZATION_LEVEL[config=Release] = -O - -// ----------------------------------------------------------------------------- -// Clang - -CLANG_ANALYZER_GCD_PERFORMANCE = YES -CLANG_ANALYZER_LOCALIZABILITY_EMPTY_CONTEXT = YES -CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES -CLANG_ANALYZER_NONNULL = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES -CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES -CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE - -CLANG_CXX_LANGUAGE_STANDARD = compiler-default -CLANG_CXX_LIBRARY = compiler-default - -CLANG_ENABLE_MODULES = YES -CLANG_ENABLE_OBJC_ARC = YES -CLANG_ENABLE_OBJC_WEAK = YES - -// Static analysis doesn't include Swift, so no harm enabling for those cases -// where there may be some Obj-C -CLANG_STATIC_ANALYZER_MODE[config=Debug] = shallow -CLANG_STATIC_ANALYZER_MODE[config=Release] = deep - -CLANG_USE_OPTIMIZATION_PROFILE = NO - -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_ASSIGN_ENUM = YES -CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES -CLANG_WARN_BOOL_CONVERSION = YES -CLANG_WARN_COMMA = YES -CLANG_WARN_CONSTANT_CONVERSION = YES -CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES -CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR -CLANG_WARN_DOCUMENTATION_COMMENTS = YES -CLANG_WARN_EMPTY_BODY = YES -CLANG_WARN_ENUM_CONVERSION = YES -CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES -CLANG_WARN_INFINITE_RECURSION = YES -CLANG_WARN_INT_CONVERSION = YES -CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -CLANG_WARN_OBJC_INTERFACE_IVARS = YES -CLANG_WARN_OBJC_LITERAL_CONVERSION = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR -CLANG_WARN_RANGE_LOOP_ANALYSIS = YES -CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES -CLANG_WARN_SUSPICIOUS_MOVE = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN_UNREACHABLE_CODE = YES - -ENABLE_STRICT_OBJC_MSGSEND = YES - -GCC_C_LANGUAGE_STANDARD = compiler-default - -GCC_DYNAMIC_NO_PIC = NO -GCC_ENABLE_CPP_RTTI = NO - -GCC_NO_COMMON_BLOCKS = YES - -GCC_OPTIMIZATION_LEVEL[config=Debug] = 0 -GCC_OPTIMIZATION_LEVEL[config=Release] = s - -GCC_PREPROCESSOR_DEFINITIONS[config=Debug] = DEBUG=1 $(inherited) -GCC_PREPROCESSOR_DEFINITIONS[config=Release] = RELEASE=1 $(inherited) - -GCC_TREAT_WARNINGS_AS_ERRORS = YES - -GCC_WARN_64_TO_32_BIT_CONVERSION = YES -GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES -GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR -GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES -GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES -GCC_WARN_SHADOW = YES -GCC_WARN_SIGN_COMPARE = YES -GCC_WARN_UNDECLARED_SELECTOR = YES -GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE -GCC_WARN_UNKNOWN_PRAGMAS = YES -GCC_WARN_UNUSED_FUNCTION = YES -GCC_WARN_UNUSED_LABEL = YES -GCC_WARN_UNUSED_VARIABLE = YES - -LD_GENERATE_MAP_FILE = YES - -RUN_CLANG_STATIC_ANALYZER = YES - -SWIFT_TREAT_WARNINGS_AS_ERRORS[config=Debug] = NO -SWIFT_TREAT_WARNINGS_AS_ERRORS[config=Release] = NO - -// ----------------------------------------------------------------------------- -// Custom - -CURRENT_COMMIT_HASH = deadbeef -#include? "../developer.xcconfig" - diff --git a/Configurations/unitTests.xcconfig b/Configurations/unitTests.xcconfig deleted file mode 100644 index 71ac8d1795d5..000000000000 --- a/Configurations/unitTests.xcconfig +++ /dev/null @@ -1,6 +0,0 @@ - -#include "base.xcconfig" - -LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/../lib - -IPHONEOS_DEPLOYMENT_TARGET = 12.2 diff --git a/DEVELOPING.md b/DEVELOPING.md index d0a4195a7433..015ad8eefc42 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -30,16 +30,16 @@ This project: `swiftlint` in the root of the repository. 4. uses [Swift Package Manager](https://github.com/apple/swift-package-manager) - to manage dependencies for development. Cocapods is supported for *consuming* + to manage dependencies for development. CocoaPods is supported for *consuming* the SDK. -5. uses [secret-shield](https://github.com/mapbox/secret-shield) to help block - secrets such as access tokens from being exposed. Setup `secret-shield` by running: +5. Install pre-commit hooks + ```bash + brew install xcodegen swiftlint + pip install pre-commit - ```sh - npm install -g @mapbox/secret-shield - scripts/install-pre-commit/install-pre-commit.sh - ``` + pre-commit install # Installs the pre-commit hooks + ``` 6. uses CircleCI and Firebase Test Lab for continuous integration. @@ -148,5 +148,13 @@ own `UIWindow` and root view controller, before adding the MapView to it. ## Making an Example -* Check out this [doc](https://github.com/mapbox/mapbox-maps-ios/blob/main/Apps/Examples/README.md) +* Check out this [project](https://github.com/mapbox/mapbox-maps-ios/blob/main/Examples.xcodeproj) to get more information about adding examples to our project. + +## Tracing map performance + +Internal events of MapboxMaps can captured in Xcode Instruments using [signposts](https://developer.apple.com/documentation/os/logging/recording_performance_data). Most useful examples of them: +- Rendering calls +- Gestures points of interests + +In order to enable them, set `MAPBOX_MAPS_SIGNPOSTS_ENABLED` environment variable to your Profile Scheme. diff --git a/Examples.xcodeproj/project.pbxproj b/Examples.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..72c21b35bd82 --- /dev/null +++ b/Examples.xcodeproj/project.pbxproj @@ -0,0 +1,1380 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 01310DFD10B3804EE3548509 /* sf_airport_route.geojson in Resources */ = {isa = PBXBuildFile; fileRef = FE2A263DD2E9DC52CEE356FA /* sf_airport_route.geojson */; }; + 03EDEA6452582E7E6805C824 /* CalloutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD8F852552CA316EFFCDD64 /* CalloutView.swift */; }; + 03EEF25ABD58ADD9631AB509 /* MapEventsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370DFCA52EB6C7F119BF81DA /* MapEventsExample.swift */; platformFilters = (ios, ); }; + 0414AD72988F405F5BA1D843 /* GlobeFlyToExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384FD8FC97B9F5011AF4BD61 /* GlobeFlyToExample.swift */; platformFilters = (ios, ); }; + 05DF15DADC248A2CAA5EEDC4 /* AddMarkersSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B33F64CDBA98B91EE819B2C4 /* AddMarkersSymbolExample.swift */; platformFilters = (ios, ); }; + 08DD7D352E50C412B667D6F6 /* CustomLayerExampleShaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 5C0C8783B2A74AE9DE3F6C32 /* CustomLayerExampleShaders.metal */; }; + 0E191B29AE31584DCFDC3821 /* RasterColorExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D66333697502A83B85BCD9 /* RasterColorExample.swift */; platformFilters = (ios, ); }; + 10C2E5ADC16B91D43288E820 /* AdvancedViewportGesturesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB717239BBFE5103C1EB1A4 /* AdvancedViewportGesturesExample.swift */; platformFilters = (ios, ); }; + 10ECE7FE19CEC239DDA96961 /* ExampleProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10C7CEE3F343DF482D428211 /* ExampleProtocol.swift */; platformFilters = (ios, ); }; + 1372F3B8047B6B4EE70933D9 /* StandardStyleExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 996964F634A4536F664C2611 /* StandardStyleExample.swift */; platformFilters = (ios, ); }; + 14799547EFD5C4757FBAD6E4 /* ViewportExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0C19E67A2E87A3D18B7B511 /* ViewportExample.swift */; platformFilters = (ios, ); }; + 1687412AC1637D7EA697C7A4 /* View+OnShake.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83E9078B1B13E54C2FFC5FFC /* View+OnShake.swift */; }; + 1820AE40702C7875656BA2D7 /* radar0.gif in Resources */ = {isa = PBXBuildFile; fileRef = 1BCE444AC33D2F1F88F4CCF0 /* radar0.gif */; }; + 18919387995A155D6F64DADF /* StandardInteractiveBuildingsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E97E057D9FFB1DE3E16F1F0B /* StandardInteractiveBuildingsExample.swift */; }; + 18F76FE745B049D1F0CAF6CA /* GeoJSONSourceExample.geojson in Resources */ = {isa = PBXBuildFile; fileRef = F033C8EFB89A90D6705B047D /* GeoJSONSourceExample.geojson */; }; + 191391C51FC69A6D36EB67F0 /* ResizableImageExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12A0818B5BC601707E3235A9 /* ResizableImageExample.swift */; platformFilters = (ios, ); }; + 1B5230204B5659B1F05C303D /* DataDrivenSymbolsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99A72B5DBF68C4729A5DC65D /* DataDrivenSymbolsExample.swift */; platformFilters = (ios, ); }; + 1B97702805C5EC4703A6CAA9 /* LineAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDE7738CA55957F3FAC3ECE /* LineAnnotationExample.swift */; platformFilters = (ios, ); }; + 1C70390E725564D6E60865EF /* DistanceExpressionExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CCB8EE8E1C9C6CC85DA5D36 /* DistanceExpressionExample.swift */; platformFilters = (ios, ); }; + 1DAE02D73D16E543777C2025 /* ClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46CE3D9C2873C0767DD76D85 /* ClusteringExample.swift */; }; + 1F860D5B445E75772C4C3B6C /* SkyLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93B8372871DB4BC991737A06 /* SkyLayerExample.swift */; platformFilters = (ios, ); }; + 215230836B6AD1040D3DA547 /* CombineLocationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4EE8F38428A64B5B9D4DBBE /* CombineLocationExample.swift */; platformFilters = (ios, ); }; + 2997D21A7DB20098C6D03D3B /* StandardStyleImportExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640198169EEDFC7CBEFCFCCF /* StandardStyleImportExample.swift */; }; + 2B44F3E8EF3A50D9AE6B825F /* route.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 450C8D5E4B84428FE51BCA97 /* route.geojson */; }; + 2C03342240D5487880316518 /* AddOneMarkerSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */; platformFilters = (ios, ); }; + 30589E5AB307FC934E466332 /* radar2.gif in Resources */ = {isa = PBXBuildFile; fileRef = D8730F8FB259A4F889609108 /* radar2.gif */; }; + 312CE7CED726F0A572301622 /* PinView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC3D7884D057238010CB6E4 /* PinView.swift */; }; + 32FA2A4133B0464494212B34 /* Array+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BB15B17EDE597D37CFF3FCA /* Array+Split.swift */; }; + 33B816803AF5330796686AA1 /* CameraForExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF35650C6319088CAAF95F84 /* CameraForExample.swift */; platformFilters = (ios, ); }; + 373BD1EE35B76E43534E23F6 /* Fingertips in Frameworks */ = {isa = PBXBuildFile; platformFilters = (ios, ); productRef = FD9311FF1C736B80A26F4258 /* Fingertips */; }; + 38AD95B6DD9BE858F4E59C31 /* WeatherAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91D21963A6FF5DA26A210DA5 /* WeatherAnnotationExample.swift */; }; + 38DF3926AD9DDDE883454F64 /* TestableExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CB74046A2FC03770B62B9E6 /* TestableExampleTests.swift */; platformFilters = (ios, ); }; + 392857DBD1231B0438144335 /* AnimatedMarkerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44C0F343A1B5F4DA0C19B9C /* AnimatedMarkerExample.swift */; platformFilters = (ios, ); }; + 3B4862E6832F23CB115D444A /* ClipLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A3027A7DA59E090DAD25F1 /* ClipLayerExample.swift */; }; + 3E515D1DD1D9CA02F3E95AA2 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75D03F5A3A0E879717BFE421 /* Constants.swift */; }; + 3FD83483E0AE57790504CB0C /* MapRecorderExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E612275E3042D0D0AF8B583E /* MapRecorderExample.swift */; platformFilters = (ios, ); }; + 4105BDB79F22905F065071F3 /* CustomRasterSourceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F6B4718F05FB1E6736EA1FF /* CustomRasterSourceExample.swift */; platformFilters = (ios, ); }; + 423A42B555DD0B3AD4856FCF /* InsetMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BCB1CC4577300FEF4DE017B /* InsetMapExample.swift */; platformFilters = (ios, ); }; + 4417BB8A356335BC8421A19B /* PointClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 289434058C4AB25A17655FEF /* PointClusteringExample.swift */; platformFilters = (ios, ); }; + 442DB919BE75CE7B0A537757 /* SpinningGlobeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A182413E535260335459F26 /* SpinningGlobeExample.swift */; platformFilters = (ios, ); }; + 4791CACAC0846107E4B0955B /* SceneKitExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 267AA1719061B281479BBBCB /* SceneKitExample.swift */; platformFilters = (ios, ); }; + 48040990713D3220E7055434 /* LiveDataExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DA62349C7E9846A885BCD3 /* LiveDataExample.swift */; platformFilters = (ios, ); }; + 49F6209402BF34C06C90107A /* HeatmapLayerGlobeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */; platformFilters = (ios, ); }; + 4ACB99FAFBF38A425EBD0285 /* ModelLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D98F58F756D035C98B1F39 /* ModelLayerExample.swift */; platformFilters = (ios, ); }; + 4E64A70408A69F2BC9F70610 /* CustomGeometrySourceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DED80ACD1618F8EE8F95A4A /* CustomGeometrySourceExample.swift */; }; + 4EF3E4C342C3F8ED5BF6C332 /* ViewAnnotationMarkerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1317C28ACDAC187017096A99 /* ViewAnnotationMarkerExample.swift */; platformFilters = (ios, ); }; + 50641F1F3A58B85873E2E5B8 /* AttributionDialogueExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A77AEDBF679F223D4412FEE /* AttributionDialogueExamples.swift */; }; + 556C8423BA408C7FF54BB5DA /* AnimateLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61A7965C46F2F371AA940A99 /* AnimateLayerExample.swift */; platformFilters = (ios, ); }; + 560D4A0D2C704ECC346D8B5F /* fragment-realestate-NY.json in Resources */ = {isa = PBXBuildFile; fileRef = 50618B3CF42CCF735CCAE9B4 /* fragment-realestate-NY.json */; }; + 56446E388868862E2BB80F4D /* ViewAnnotationWithPointAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB726A94B069761528B57EC8 /* ViewAnnotationWithPointAnnotationExample.swift */; platformFilters = (ios, ); }; + 5A28C124249725578389175A /* ColorExpressionExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9B2D532D2F54C6C40DA466 /* ColorExpressionExample.swift */; platformFilters = (ios, ); }; + 5A68CBA756780F7DE8F7BDCC /* radar3.gif in Resources */ = {isa = PBXBuildFile; fileRef = 24FD45C982E4403DA3BD241A /* radar3.gif */; }; + 5A6D7B2A302A6555FE23FF80 /* blueprint_style.json in Resources */ = {isa = PBXBuildFile; fileRef = 989F5AB9D5D8AD39D21327A1 /* blueprint_style.json */; }; + 5B2CE02503AF44EBC86FE884 /* MapboxMaps in Frameworks */ = {isa = PBXBuildFile; productRef = 0AF5F744C6369BF1FB233FB6 /* MapboxMaps */; }; + 5E508E47388A646B4F74DD0B /* AnnotationsOrderTestExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3595A6E8FB1FD9F41DEB5C6F /* AnnotationsOrderTestExample.swift */; }; + 5F2AD73C8104089C9291574E /* GeofencingUserLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3A816C6E4D7A0A532EEE84 /* GeofencingUserLocation.swift */; }; + 5F537B052041931CB507E12B /* ViewportPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE863B179BF4F740C36D185E /* ViewportPlayground.swift */; }; + 5F556CB71C442EC2A8C2E229 /* VisionOSMain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96B0E9086FE939F5D723136D /* VisionOSMain.swift */; platformFilters = (xros, ); }; + 5FF3E34B523C39A404154BF7 /* OfflineRegionManagerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1229327C13654C370B5641FC /* OfflineRegionManagerExample.swift */; platformFilters = (ios, ); }; + 60A1572CCF5763FA3C946B89 /* Custom2DPuckExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 262AD875661BAB5564084A9E /* Custom2DPuckExample.swift */; platformFilters = (ios, ); }; + 61B79A9069DCE6865E43E261 /* radar4.gif in Resources */ = {isa = PBXBuildFile; fileRef = 876CE24F4E565ED342DDDCD6 /* radar4.gif */; }; + 634BA74F4E553C53EE906F5A /* OfflineManagerExample.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 000D0E4CEFB6D5AF02518807 /* OfflineManagerExample.storyboard */; platformFilters = (ios, ); }; + 64F4FA139388DB34564AD42D /* CLLocationCoordinate2D+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 455C0B9F01316D0FF38ED62B /* CLLocationCoordinate2D+Random.swift */; }; + 655105BD0FAFF4C4BA65DC32 /* ExamplesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE19F00E87B31FDE5481D56 /* ExamplesTests.swift */; platformFilters = (ios, ); }; + 65E9F2B993AEB394FC2D0080 /* ColorThemeMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F519EC4AF0B4BEE0FDCABC /* ColorThemeMapExample.swift */; platformFilters = (ios, ); }; + 6661DB69D4980E24BCA18AB2 /* PolygonAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DCBE4524A8793B4DE950533 /* PolygonAnnotationExample.swift */; platformFilters = (ios, ); }; + 68FD9E1F4606B2729BA1E6DC /* SnapshotterExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588FD640D91E9DD366703F7B /* SnapshotterExample.swift */; platformFilters = (ios, ); }; + 6B040F65241ABF600D70D14D /* Custom3DPuckExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C957F9CA07061B793C2DD4A /* Custom3DPuckExample.swift */; platformFilters = (ios, ); }; + 7036A19FCD2CCE85BDDF4E00 /* TrackingModeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F5E598A16FA446F583344CB /* TrackingModeExample.swift */; platformFilters = (ios, ); }; + 7365170E39A459EB4DFA198B /* ExamplesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE18E37A8652B4807D2459F1 /* ExamplesUITests.swift */; platformFilters = (ios, ); }; + 759D42AA5FE93B6FA9DFADF5 /* LocationOverrideExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45B39AE24486FED5ED30392D /* LocationOverrideExample.swift */; }; + 7686448F8648BECC75A912B6 /* DashboardCarPlaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 913B4773A82AD6357D6AAEA1 /* DashboardCarPlaySceneDelegate.swift */; platformFilters = (ios, ); }; + 79843B780E7C5DC68433B745 /* SnapshotMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC5A8729C9AEA4711B56B5F0 /* SnapshotMapExample.swift */; }; + 79B889CF23A3C0A5EA7F6ADD /* ApplicationCarPlaySceneDelegage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F3F37CAE7A0B66BE429525C /* ApplicationCarPlaySceneDelegage.swift */; platformFilters = (ios, ); }; + 7B9835E597E0B2655E181A48 /* ExampleTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0A6063E57EC170F558A3F74 /* ExampleTableViewController.swift */; platformFilters = (ios, ); }; + 7E84D4D6459049E452808C91 /* AnnotationsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = F890746B56E20150A053B41B /* AnnotationsExample.swift */; }; + 7EDF38D2E9CDE489F320977E /* SwiftUIRoot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90CEF3209781923E53974F20 /* SwiftUIRoot.swift */; }; + 803CCCEA28B209111BE0786F /* sportcar.glb in Resources */ = {isa = PBXBuildFile; fileRef = C47942F80A50166AC823012B /* sportcar.glb */; }; + 821807D61D52F0E60925BCD4 /* CameraAnimationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E3A0CCD53B02FF2BAD830A2 /* CameraAnimationExample.swift */; platformFilters = (ios, ); }; + 83DCCC7FEFF6D94D3DF0B587 /* 34M_17.dae in Resources */ = {isa = PBXBuildFile; fileRef = 28CE7DA39D29A8311E4A58A4 /* 34M_17.dae */; }; + 8418F1775D49F5A66489B988 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F393DF039D7AD2F35C8DE4CE /* ViewExtensions.swift */; }; + 854CE1A84AADF6FBB232CB5F /* FeaturesQueryExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B06A1D70F479D8DC5C375A /* FeaturesQueryExample.swift */; }; + 85AA0D942D4C0E218D87F7D8 /* GradientLine.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 2DD8B1D25297B7433F4AAF35 /* GradientLine.geojson */; }; + 85E0F727CBB3374D3EF499C3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55FDF5B3329BFD6E6C346D80 /* LaunchScreen.storyboard */; platformFilters = (ios, ); }; + 86AED5DD9F8C8BB2C9736483 /* ResizeMapViewExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E54D3F5943238258AA0A9BE /* ResizeMapViewExample.swift */; platformFilters = (ios, ); }; + 8913C410B72BE0C40EE02BB9 /* MapSettingsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6E875420B5C674C8CCAB9B1 /* MapSettingsExample.swift */; }; + 8B4085733CCABE3BE3D16F7E /* LayerPositionExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99610E884967673F7F28D2B2 /* LayerPositionExample.swift */; platformFilters = (ios, ); }; + 8F0BEA796867B64E48A1B328 /* StandardStyleLocationsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6F1212BB2453DBFECE12F2 /* StandardStyleLocationsExample.swift */; }; + 902FD51EC410A1E8BD88941D /* NavigationSimulatorExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A090EE21C9A65BE2697868F /* NavigationSimulatorExample.swift */; platformFilters = (ios, ); }; + 918F4BDCC25819DD68BC9518 /* LayerSlotExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D058C94675BB58AE74392829 /* LayerSlotExample.swift */; platformFilters = (ios, ); }; + 94DB7E8C829041DC5F5B2300 /* InstrumentClusterCarPlaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD7661708AEADD25AB726B4 /* InstrumentClusterCarPlaySceneDelegate.swift */; platformFilters = (ios, ); }; + 9717811318AAE88DE9214307 /* GeofencingExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F9127909E8758EAC82E19F7 /* GeofencingExample.swift */; platformFilters = (ios, ); }; + 9A403D6AB6D6336E212726C5 /* CameraAnimatorsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDABBED8502001D17EF30F35 /* CameraAnimatorsExample.swift */; platformFilters = (ios, ); }; + 9DFE9DDE63B78393031C843E /* Examples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B30F4697545D9F02DD4425D /* Examples.swift */; platformFilters = (ios, ); }; + A3D7C0836BFE6FEB40C3C15A /* BasicLocationPulsingExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC98E9169E8E7DFE8DC1CB27 /* BasicLocationPulsingExample.swift */; platformFilters = (ios, ); }; + A6389C28B8AAC39878591AD0 /* PitchAndDistanceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A5C0A3C44715B96D646ACB7 /* PitchAndDistanceExample.swift */; platformFilters = (ios, ); }; + A6A68B4ED674A924ACBD8FA2 /* UIColor+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = F000C4D3B6FC70FA9607E3A3 /* UIColor+Random.swift */; }; + A972D3306BC53DEC9798C60D /* ExternalVectorSourceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133E4EABC7540ED460F08B8F /* ExternalVectorSourceExample.swift */; platformFilters = (ios, ); }; + AD0922FA7F69AEE4C23F2351 /* InteractionsPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388932B3A65BB7E9B59FDBE0 /* InteractionsPlayground.swift */; }; + AE51E276DCD8CF89AB339224 /* LongTapAnimationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DB76F486D80FED88678B04D /* LongTapAnimationExample.swift */; platformFilters = (ios, ); }; + AE6E90DB7B6DA4580C2DAB59 /* FrameViewAnnotationsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC0F09FF1EF1A88BC1C6545 /* FrameViewAnnotationsExample.swift */; platformFilters = (ios, ); }; + B304BACFCD08802A740E8919 /* PuckPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */; }; + B53EA441C54E2B680A7E99F0 /* BuildingExtrusionsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFF80A86D7DEF54E0A7256DF /* BuildingExtrusionsExample.swift */; platformFilters = (ios, ); }; + B9B1EE72E6203358F2785916 /* IconSizeChangeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */; platformFilters = (ios, ); }; + B9D4B9C3042383738AB5B667 /* DebugMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59EF8798357CF55109A4E56 /* DebugMapExample.swift */; platformFilters = (ios, ); }; + BD99E89F050E7D93846147FF /* ViewAnnotationBasicExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C6CF222C8EE9AE69563E39 /* ViewAnnotationBasicExample.swift */; platformFilters = (ios, ); }; + BDABAAC8727AF67A0DEE2020 /* AnimateGeoJSONLineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */; platformFilters = (ios, ); }; + C04160BF66055F7DE9315395 /* Lights3DExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 60A8CD8A69D3429FCF8ACBDD /* Lights3DExample.swift */; platformFilters = (ios, ); }; + C315E1C61D222296FE0244FC /* VoiceOverAccessibilityExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D91A8B64951711546335530 /* VoiceOverAccessibilityExample.swift */; platformFilters = (ios, ); }; + C327DBA17D79D5DFBBE84BE0 /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 858990E6795D3162A941E82C /* ButtonStyle.swift */; }; + C664365A373267B564EC84EE /* CombineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DBF737C9C7FBF46BB5F7B78 /* CombineExample.swift */; platformFilters = (ios, ); }; + C6E1E615C75960D1BD1755A9 /* annotations.json in Resources */ = {isa = PBXBuildFile; fileRef = B05B410135D0B466B73C0765 /* annotations.json */; }; + C940835B030A20F0C5BC31AD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B31A932A62B6142FE20C39DF /* Assets.xcassets */; }; + C953F022C91FCA59CFF06BE9 /* SymbolClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD38B8D20C0695A03AE4E68 /* SymbolClusteringExample.swift */; platformFilters = (ios, ); }; + CA2209956E93ECB18C4C9DEC /* CircleAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1658938A5F0908D151A9B9 /* CircleAnnotationExample.swift */; platformFilters = (ios, ); }; + CBCC60FF68BE9754DE0C6AF3 /* DynamicStylingExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = C61CC711054A032EE0446036 /* DynamicStylingExample.swift */; }; + CBD01BBA4E78796827A6E52D /* RuntimeSlotsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AB4D202692A8951BAF2D57 /* RuntimeSlotsExample.swift */; platformFilters = (ios, ); }; + CF5C5513D659D4981706DDEC /* ViewAnnotationAnimationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E5647315A2357320BCF575 /* ViewAnnotationAnimationExample.swift */; platformFilters = (ios, ); }; + D27F0573360A7234BCF7AB6C /* AnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC8E7B565C817D872CFBCAD /* AnnotationView.swift */; platformFilters = (ios, ); }; + D4FFFAE49D4B805BDA014AAD /* BasicMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 257F10278F5E580C7ABA5570 /* BasicMapExample.swift */; platformFilters = (ios, ); }; + D62F69A9BD802A1926B92968 /* Example.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB52F9D3A810B1A9CEC832C /* Example.swift */; platformFilters = (ios, ); }; + D63431CA78A557A0FB92177A /* FeatureStateExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3FA795972BB141B9C582ED0 /* FeatureStateExample.swift */; platformFilters = (ios, ); }; + D77EEB488CFD90F602077E8F /* CustomPointAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D5DB9BD5E97D3C0080EC5D3 /* CustomPointAnnotationExample.swift */; platformFilters = (ios, ); }; + D9297596469F9B31C2350B43 /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A615EFC3D6CF2A25C9864086 /* UIViewController+Extensions.swift */; }; + D94672F30272E31087AB5DDD /* NavigationSimulator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FC5980DD30479F30127BA71 /* NavigationSimulator.swift */; platformFilters = (ios, ); }; + D98624793DA36578289F02FF /* MapScrollExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65535FB9F190778001AB847A /* MapScrollExample.swift */; }; + DA109856E64BBD8071DF0619 /* ColorThemeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29DD4C2F0049E575A6B5BF66 /* ColorThemeExample.swift */; }; + DA69CB0BD9F0DDA0FD1387B0 /* DataJoinExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87D0CD9C2D04EA5B12E7F84C /* DataJoinExample.swift */; platformFilters = (ios, ); }; + DCA54F7383085A8FD822F0BF /* GeofencingPlayground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7613C4E19DCD679A2620223C /* GeofencingPlayground.swift */; }; + DFC64A62538E787D57B6514D /* DynamicViewAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3333EF3E0F1C789809F385AF /* DynamicViewAnnotationExample.swift */; platformFilters = (ios, ); }; + E121F023995CCF2F3A65BC2A /* LocateMeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62DA0608D44DEF6C4A82777C /* LocateMeExample.swift */; }; + E2617ACF1E2367C012A87CD1 /* ViewAnnotationsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59AACE9E33102AE90526569F /* ViewAnnotationsExample.swift */; }; + E2CD9B66367265509567A4E3 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 524D79FF01638A0E8A3C1248 /* .swiftlint.yml */; platformFilters = (ios, ); }; + E5A3B926DD7E451F1E660547 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 274D496EC7E47F63FD0D1337 /* AppDelegate.swift */; platformFilters = (ios, ); }; + E6B722A64C15CE701287B464 /* OfflineManagerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C452C7342E73F6F16A83C /* OfflineManagerExample.swift */; platformFilters = (ios, ); }; + E8CEBC697D805204F129C4FB /* RasterTileSourceExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67FA937DC17F7EA27931763A /* RasterTileSourceExample.swift */; platformFilters = (ios, ); }; + EB39F159A9F5DFAB935F629D /* SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26964B05A794CC808C594AB5 /* SwiftExtensions.swift */; }; + EE4064D753E360A6A6AC5BAC /* CustomLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06955521CCC52B47AAB475F4 /* CustomLayerExample.swift */; platformFilters = (ios, ); }; + F0502A1ACF0AED218F8184AB /* CarPlayMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6CEA9899CC0EC4F4381E19 /* CarPlayMapViewController.swift */; platformFilters = (ios, ); }; + F2B385831A78B3EE16BFEA69 /* SnapshotterCoreGraphicsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77368780E4DBCCFB2CBD400 /* SnapshotterCoreGraphicsExample.swift */; platformFilters = (ios, ); }; + F476D12AC7B4347AA55BEC4C /* AnimateImageLayerExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A471857797898860C3A8F685 /* AnimateImageLayerExample.swift */; platformFilters = (ios, ); }; + F48BF087BB56B0A44D8B16F3 /* PointAnnotationClusteringExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8359ECB5024BEF3722C3CA1 /* PointAnnotationClusteringExample.swift */; platformFilters = (ios, ); }; + F492E2C8D35572B4CF48FE68 /* RasterParticleExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD7ADCCB774239AA0090C46 /* RasterParticleExample.swift */; }; + F5311222553DA118AC571D82 /* MultipleGeometriesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7A7586D05B960928AB17A0D /* MultipleGeometriesExample.swift */; platformFilters = (ios, ); }; + F5E96E5798947CA56FD77CF9 /* Fire_Hydrants.geojson in Resources */ = {isa = PBXBuildFile; fileRef = 02DA2CC04980F807255D646B /* Fire_Hydrants.geojson */; }; + F613749DCDDDDC6F041032A0 /* SimpleMapExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78811E5A3185D2D32495870A /* SimpleMapExample.swift */; }; + F6E3EF9BE4F1D2F58DE1BED2 /* radar1.gif in Resources */ = {isa = PBXBuildFile; fileRef = 8BD8BADE1108B0D380D9BEF8 /* radar1.gif */; }; + FA077DC5A6CF295906536DF1 /* PrecipitationsExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDBCA79855B58FE84333124 /* PrecipitationsExample.swift */; }; + FA53EEA88DB29D4D5AC58514 /* StandardInteractiveFeaturesExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FBAFDF929532EDB964786F4 /* StandardInteractiveFeaturesExample.swift */; }; + FDA4B57BE32D92BB57A5B7E6 /* FeaturesAtPointExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6239A5CDA61892902765B843 /* FeaturesAtPointExample.swift */; platformFilters = (ios, ); }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 39418E4FA1B2F4F66836739C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 604281A06A1DE9F942BCBA73 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DF268D7B1F3C2B89CA87979A; + remoteInfo = Examples; + }; + C86D6DE9936D944850C4ED52 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 604281A06A1DE9F942BCBA73 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DF268D7B1F3C2B89CA87979A; + remoteInfo = Examples; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 000D0E4CEFB6D5AF02518807 /* OfflineManagerExample.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = OfflineManagerExample.storyboard; sourceTree = ""; }; + 02DA2CC04980F807255D646B /* Fire_Hydrants.geojson */ = {isa = PBXFileReference; path = Fire_Hydrants.geojson; sourceTree = ""; }; + 06955521CCC52B47AAB475F4 /* CustomLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayerExample.swift; sourceTree = ""; }; + 083C452C7342E73F6F16A83C /* OfflineManagerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineManagerExample.swift; sourceTree = ""; }; + 0A5C0A3C44715B96D646ACB7 /* PitchAndDistanceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PitchAndDistanceExample.swift; sourceTree = ""; }; + 0AD7661708AEADD25AB726B4 /* InstrumentClusterCarPlaySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstrumentClusterCarPlaySceneDelegate.swift; sourceTree = ""; }; + 0F9127909E8758EAC82E19F7 /* GeofencingExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeofencingExample.swift; sourceTree = ""; }; + 0FBAFDF929532EDB964786F4 /* StandardInteractiveFeaturesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardInteractiveFeaturesExample.swift; sourceTree = ""; }; + 10C7CEE3F343DF482D428211 /* ExampleProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleProtocol.swift; sourceTree = ""; }; + 1229327C13654C370B5641FC /* OfflineRegionManagerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineRegionManagerExample.swift; sourceTree = ""; }; + 12A0818B5BC601707E3235A9 /* ResizableImageExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableImageExample.swift; sourceTree = ""; }; + 1317C28ACDAC187017096A99 /* ViewAnnotationMarkerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationMarkerExample.swift; sourceTree = ""; }; + 133E4EABC7540ED460F08B8F /* ExternalVectorSourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalVectorSourceExample.swift; sourceTree = ""; }; + 1A182413E535260335459F26 /* SpinningGlobeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinningGlobeExample.swift; sourceTree = ""; }; + 1BCE444AC33D2F1F88F4CCF0 /* radar0.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar0.gif; sourceTree = ""; }; + 1D9B2D532D2F54C6C40DA466 /* ColorExpressionExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExpressionExample.swift; sourceTree = ""; }; + 24FD45C982E4403DA3BD241A /* radar3.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar3.gif; sourceTree = ""; }; + 257F10278F5E580C7ABA5570 /* BasicMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicMapExample.swift; sourceTree = ""; }; + 262AD875661BAB5564084A9E /* Custom2DPuckExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Custom2DPuckExample.swift; sourceTree = ""; }; + 267AA1719061B281479BBBCB /* SceneKitExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneKitExample.swift; sourceTree = ""; }; + 26964B05A794CC808C594AB5 /* SwiftExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftExtensions.swift; sourceTree = ""; }; + 274D496EC7E47F63FD0D1337 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 289434058C4AB25A17655FEF /* PointClusteringExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointClusteringExample.swift; sourceTree = ""; }; + 28CE7DA39D29A8311E4A58A4 /* 34M_17.dae */ = {isa = PBXFileReference; lastKnownFileType = text.xml.dae; path = 34M_17.dae; sourceTree = ""; }; + 29DD4C2F0049E575A6B5BF66 /* ColorThemeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorThemeExample.swift; sourceTree = ""; }; + 2C957F9CA07061B793C2DD4A /* Custom3DPuckExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Custom3DPuckExample.swift; sourceTree = ""; }; + 2D91A8B64951711546335530 /* VoiceOverAccessibilityExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceOverAccessibilityExample.swift; sourceTree = ""; }; + 2DD8B1D25297B7433F4AAF35 /* GradientLine.geojson */ = {isa = PBXFileReference; path = GradientLine.geojson; sourceTree = ""; }; + 2F6B4718F05FB1E6736EA1FF /* CustomRasterSourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRasterSourceExample.swift; sourceTree = ""; }; + 3333EF3E0F1C789809F385AF /* DynamicViewAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicViewAnnotationExample.swift; sourceTree = ""; }; + 33E5647315A2357320BCF575 /* ViewAnnotationAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationAnimationExample.swift; sourceTree = ""; }; + 3595A6E8FB1FD9F41DEB5C6F /* AnnotationsOrderTestExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationsOrderTestExample.swift; sourceTree = ""; }; + 370DFCA52EB6C7F119BF81DA /* MapEventsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapEventsExample.swift; sourceTree = ""; }; + 384FD8FC97B9F5011AF4BD61 /* GlobeFlyToExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobeFlyToExample.swift; sourceTree = ""; }; + 388932B3A65BB7E9B59FDBE0 /* InteractionsPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionsPlayground.swift; sourceTree = ""; }; + 3BCB1CC4577300FEF4DE017B /* InsetMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsetMapExample.swift; sourceTree = ""; }; + 3BDE7738CA55957F3FAC3ECE /* LineAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineAnnotationExample.swift; sourceTree = ""; }; + 3E2F68B22AFF73A71F86CABC /* ExamplesUITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = ExamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3E3A0CCD53B02FF2BAD830A2 /* CameraAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAnimationExample.swift; sourceTree = ""; }; + 3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSizeChangeExample.swift; sourceTree = ""; }; + 3FB717239BBFE5103C1EB1A4 /* AdvancedViewportGesturesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedViewportGesturesExample.swift; sourceTree = ""; }; + 3FC5980DD30479F30127BA71 /* NavigationSimulator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSimulator.swift; sourceTree = ""; }; + 450C8D5E4B84428FE51BCA97 /* route.geojson */ = {isa = PBXFileReference; path = route.geojson; sourceTree = ""; }; + 455C0B9F01316D0FF38ED62B /* CLLocationCoordinate2D+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLLocationCoordinate2D+Random.swift"; sourceTree = ""; }; + 45B39AE24486FED5ED30392D /* LocationOverrideExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationOverrideExample.swift; sourceTree = ""; }; + 46CE3D9C2873C0767DD76D85 /* ClusteringExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClusteringExample.swift; sourceTree = ""; }; + 4D5DB9BD5E97D3C0080EC5D3 /* CustomPointAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPointAnnotationExample.swift; sourceTree = ""; }; + 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOneMarkerSymbolExample.swift; sourceTree = ""; }; + 4EE19F00E87B31FDE5481D56 /* ExamplesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesTests.swift; sourceTree = ""; }; + 50618B3CF42CCF735CCAE9B4 /* fragment-realestate-NY.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "fragment-realestate-NY.json"; sourceTree = ""; }; + 50F519EC4AF0B4BEE0FDCABC /* ColorThemeMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorThemeMapExample.swift; sourceTree = ""; }; + 524D79FF01638A0E8A3C1248 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + 588FD640D91E9DD366703F7B /* SnapshotterExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotterExample.swift; sourceTree = ""; }; + 590D83805AA598D2B0A7638A /* ExamplesTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = ExamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 59AACE9E33102AE90526569F /* ViewAnnotationsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationsExample.swift; sourceTree = ""; }; + 5C0C8783B2A74AE9DE3F6C32 /* CustomLayerExampleShaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = CustomLayerExampleShaders.metal; sourceTree = ""; }; + 5C1658938A5F0908D151A9B9 /* CircleAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleAnnotationExample.swift; sourceTree = ""; }; + 5FC0F09FF1EF1A88BC1C6545 /* FrameViewAnnotationsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameViewAnnotationsExample.swift; sourceTree = ""; }; + 60A8CD8A69D3429FCF8ACBDD /* Lights3DExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Lights3DExample.swift; sourceTree = ""; }; + 61A7965C46F2F371AA940A99 /* AnimateLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateLayerExample.swift; sourceTree = ""; }; + 6239A5CDA61892902765B843 /* FeaturesAtPointExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturesAtPointExample.swift; sourceTree = ""; }; + 62DA0608D44DEF6C4A82777C /* LocateMeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocateMeExample.swift; sourceTree = ""; }; + 63A3027A7DA59E090DAD25F1 /* ClipLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClipLayerExample.swift; sourceTree = ""; }; + 640198169EEDFC7CBEFCFCCF /* StandardStyleImportExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardStyleImportExample.swift; sourceTree = ""; }; + 65535FB9F190778001AB847A /* MapScrollExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapScrollExample.swift; sourceTree = ""; }; + 65AB4D202692A8951BAF2D57 /* RuntimeSlotsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeSlotsExample.swift; sourceTree = ""; }; + 67FA937DC17F7EA27931763A /* RasterTileSourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RasterTileSourceExample.swift; sourceTree = ""; }; + 6A090EE21C9A65BE2697868F /* NavigationSimulatorExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSimulatorExample.swift; sourceTree = ""; }; + 6BB52F9D3A810B1A9CEC832C /* Example.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Example.swift; sourceTree = ""; }; + 6CD7ADCCB774239AA0090C46 /* RasterParticleExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RasterParticleExample.swift; sourceTree = ""; }; + 6D5B23007CF347976470A069 /* Examples-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Examples-Bridging-Header.h"; sourceTree = ""; }; + 6DC3D7884D057238010CB6E4 /* PinView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinView.swift; sourceTree = ""; }; + 6E54D3F5943238258AA0A9BE /* ResizeMapViewExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeMapViewExample.swift; sourceTree = ""; }; + 70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeatmapLayerGlobeExample.swift; sourceTree = ""; }; + 7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateGeoJSONLineExample.swift; sourceTree = ""; }; + 75D03F5A3A0E879717BFE421 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 7613C4E19DCD679A2620223C /* GeofencingPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeofencingPlayground.swift; sourceTree = ""; }; + 78811E5A3185D2D32495870A /* SimpleMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleMapExample.swift; sourceTree = ""; }; + 7A77AEDBF679F223D4412FEE /* AttributionDialogueExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributionDialogueExamples.swift; sourceTree = ""; }; + 7DB76F486D80FED88678B04D /* LongTapAnimationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongTapAnimationExample.swift; sourceTree = ""; }; + 7DCBE4524A8793B4DE950533 /* PolygonAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PolygonAnnotationExample.swift; sourceTree = ""; }; + 7DED80ACD1618F8EE8F95A4A /* CustomGeometrySourceExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomGeometrySourceExample.swift; sourceTree = ""; }; + 7F5E598A16FA446F583344CB /* TrackingModeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingModeExample.swift; sourceTree = ""; }; + 83E9078B1B13E54C2FFC5FFC /* View+OnShake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+OnShake.swift"; sourceTree = ""; }; + 858990E6795D3162A941E82C /* ButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyle.swift; sourceTree = ""; }; + 876CE24F4E565ED342DDDCD6 /* radar4.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar4.gif; sourceTree = ""; }; + 87D0CD9C2D04EA5B12E7F84C /* DataJoinExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataJoinExample.swift; sourceTree = ""; }; + 8B30F4697545D9F02DD4425D /* Examples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Examples.swift; sourceTree = ""; }; + 8BB15B17EDE597D37CFF3FCA /* Array+Split.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Split.swift"; sourceTree = ""; }; + 8BD8BADE1108B0D380D9BEF8 /* radar1.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar1.gif; sourceTree = ""; }; + 8DBF737C9C7FBF46BB5F7B78 /* CombineExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineExample.swift; sourceTree = ""; }; + 8FD38B8D20C0695A03AE4E68 /* SymbolClusteringExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolClusteringExample.swift; sourceTree = ""; }; + 90CEF3209781923E53974F20 /* SwiftUIRoot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIRoot.swift; sourceTree = ""; }; + 913B4773A82AD6357D6AAEA1 /* DashboardCarPlaySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardCarPlaySceneDelegate.swift; sourceTree = ""; }; + 91D21963A6FF5DA26A210DA5 /* WeatherAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherAnnotationExample.swift; sourceTree = ""; }; + 93B8372871DB4BC991737A06 /* SkyLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkyLayerExample.swift; sourceTree = ""; }; + 96B0E9086FE939F5D723136D /* VisionOSMain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisionOSMain.swift; sourceTree = ""; }; + 989F5AB9D5D8AD39D21327A1 /* blueprint_style.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = blueprint_style.json; sourceTree = ""; }; + 98D66333697502A83B85BCD9 /* RasterColorExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RasterColorExample.swift; sourceTree = ""; }; + 99610E884967673F7F28D2B2 /* LayerPositionExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayerPositionExample.swift; sourceTree = ""; }; + 996964F634A4536F664C2611 /* StandardStyleExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardStyleExample.swift; sourceTree = ""; }; + 99A72B5DBF68C4729A5DC65D /* DataDrivenSymbolsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataDrivenSymbolsExample.swift; sourceTree = ""; }; + 9CB74046A2FC03770B62B9E6 /* TestableExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableExampleTests.swift; sourceTree = ""; }; + 9CCB8EE8E1C9C6CC85DA5D36 /* DistanceExpressionExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DistanceExpressionExample.swift; sourceTree = ""; }; + 9F3F37CAE7A0B66BE429525C /* ApplicationCarPlaySceneDelegage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCarPlaySceneDelegage.swift; sourceTree = ""; }; + A471857797898860C3A8F685 /* AnimateImageLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimateImageLayerExample.swift; sourceTree = ""; }; + A4EE8F38428A64B5B9D4DBBE /* CombineLocationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineLocationExample.swift; sourceTree = ""; }; + A59EF8798357CF55109A4E56 /* DebugMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMapExample.swift; sourceTree = ""; }; + A615EFC3D6CF2A25C9864086 /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extensions.swift"; sourceTree = ""; }; + A6B06A1D70F479D8DC5C375A /* FeaturesQueryExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturesQueryExample.swift; sourceTree = ""; }; + A8359ECB5024BEF3722C3CA1 /* PointAnnotationClusteringExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointAnnotationClusteringExample.swift; sourceTree = ""; }; + A8700CFE38DA4F1333F9E0F9 /* mapbox-maps-ios */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "mapbox-maps-ios"; path = .; sourceTree = SOURCE_ROOT; }; + A9A26CBC58F3271DBFD2EE7D /* CustomLayerShaderTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CustomLayerShaderTypes.h; sourceTree = ""; }; + AC5A8729C9AEA4711B56B5F0 /* SnapshotMapExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotMapExample.swift; sourceTree = ""; }; + B05B410135D0B466B73C0765 /* annotations.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = annotations.json; sourceTree = ""; }; + B31A932A62B6142FE20C39DF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B33F64CDBA98B91EE819B2C4 /* AddMarkersSymbolExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMarkersSymbolExample.swift; sourceTree = ""; }; + B7A7586D05B960928AB17A0D /* MultipleGeometriesExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleGeometriesExample.swift; sourceTree = ""; }; + BE18E37A8652B4807D2459F1 /* ExamplesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesUITests.swift; sourceTree = ""; }; + BFDBCA79855B58FE84333124 /* PrecipitationsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrecipitationsExample.swift; sourceTree = ""; }; + C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PuckPlayground.swift; sourceTree = ""; }; + C47942F80A50166AC823012B /* sportcar.glb */ = {isa = PBXFileReference; path = sportcar.glb; sourceTree = ""; }; + C61CC711054A032EE0446036 /* DynamicStylingExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicStylingExample.swift; sourceTree = ""; }; + CB726A94B069761528B57EC8 /* ViewAnnotationWithPointAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationWithPointAnnotationExample.swift; sourceTree = ""; }; + CBD8F852552CA316EFFCDD64 /* CalloutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalloutView.swift; sourceTree = ""; }; + CDABBED8502001D17EF30F35 /* CameraAnimatorsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraAnimatorsExample.swift; sourceTree = ""; }; + CF35650C6319088CAAF95F84 /* CameraForExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraForExample.swift; sourceTree = ""; }; + CFF80A86D7DEF54E0A7256DF /* BuildingExtrusionsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildingExtrusionsExample.swift; sourceTree = ""; }; + D058C94675BB58AE74392829 /* LayerSlotExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayerSlotExample.swift; sourceTree = ""; }; + D2D98F58F756D035C98B1F39 /* ModelLayerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelLayerExample.swift; sourceTree = ""; }; + D3C6CF222C8EE9AE69563E39 /* ViewAnnotationBasicExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewAnnotationBasicExample.swift; sourceTree = ""; }; + D3FA795972BB141B9C582ED0 /* FeatureStateExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureStateExample.swift; sourceTree = ""; }; + D44C0F343A1B5F4DA0C19B9C /* AnimatedMarkerExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimatedMarkerExample.swift; sourceTree = ""; }; + D4DA62349C7E9846A885BCD3 /* LiveDataExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveDataExample.swift; sourceTree = ""; }; + D77368780E4DBCCFB2CBD400 /* SnapshotterCoreGraphicsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SnapshotterCoreGraphicsExample.swift; sourceTree = ""; }; + D8730F8FB259A4F889609108 /* radar2.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = radar2.gif; sourceTree = ""; }; + DAC8E7B565C817D872CFBCAD /* AnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationView.swift; sourceTree = ""; }; + DC98E9169E8E7DFE8DC1CB27 /* BasicLocationPulsingExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicLocationPulsingExample.swift; sourceTree = ""; }; + DD3A816C6E4D7A0A532EEE84 /* GeofencingUserLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeofencingUserLocation.swift; sourceTree = ""; }; + DD6F1212BB2453DBFECE12F2 /* StandardStyleLocationsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardStyleLocationsExample.swift; sourceTree = ""; }; + DE6CEA9899CC0EC4F4381E19 /* CarPlayMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarPlayMapViewController.swift; sourceTree = ""; }; + DE863B179BF4F740C36D185E /* ViewportPlayground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportPlayground.swift; sourceTree = ""; }; + E09DABCB8139643F1BD3B972 /* Examples_CarPlay.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Examples_CarPlay.entitlements; sourceTree = ""; }; + E0C19E67A2E87A3D18B7B511 /* ViewportExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportExample.swift; sourceTree = ""; }; + E612275E3042D0D0AF8B583E /* MapRecorderExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRecorderExample.swift; sourceTree = ""; }; + E6E875420B5C674C8CCAB9B1 /* MapSettingsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapSettingsExample.swift; sourceTree = ""; }; + E97E057D9FFB1DE3E16F1F0B /* StandardInteractiveBuildingsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardInteractiveBuildingsExample.swift; sourceTree = ""; }; + EC1155178B21E2E8075454A8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + F000C4D3B6FC70FA9607E3A3 /* UIColor+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Random.swift"; sourceTree = ""; }; + F033C8EFB89A90D6705B047D /* GeoJSONSourceExample.geojson */ = {isa = PBXFileReference; path = GeoJSONSourceExample.geojson; sourceTree = ""; }; + F0A6063E57EC170F558A3F74 /* ExampleTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleTableViewController.swift; sourceTree = ""; }; + F0CE51977FA6E83B6F11BE5C /* Examples.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F393DF039D7AD2F35C8DE4CE /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = ""; }; + F890746B56E20150A053B41B /* AnnotationsExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnotationsExample.swift; sourceTree = ""; }; + FC1CEEE6277DEAD9FDFF4AAC /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + FE2A263DD2E9DC52CEE356FA /* sf_airport_route.geojson */ = {isa = PBXFileReference; path = sf_airport_route.geojson; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + AF4891A70CEBFB45EE553EAA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B2CE02503AF44EBC86FE884 /* MapboxMaps in Frameworks */, + 373BD1EE35B76E43534E23F6 /* Fingertips in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2CDB0066C8683D83F2532B62 /* Testing Examples */ = { + isa = PBXGroup; + children = ( + 3595A6E8FB1FD9F41DEB5C6F /* AnnotationsOrderTestExample.swift */, + 7A77AEDBF679F223D4412FEE /* AttributionDialogueExamples.swift */, + 388932B3A65BB7E9B59FDBE0 /* InteractionsPlayground.swift */, + 65535FB9F190778001AB847A /* MapScrollExample.swift */, + E6E875420B5C674C8CCAB9B1 /* MapSettingsExample.swift */, + C0CC67084BA1191D0B179A94 /* PuckPlayground.swift */, + 78811E5A3185D2D32495870A /* SimpleMapExample.swift */, + DE863B179BF4F740C36D185E /* ViewportPlayground.swift */, + ); + path = "Testing Examples"; + sourceTree = ""; + }; + 3FDBCEEB7C4C53F0545FC427 /* Products */ = { + isa = PBXGroup; + children = ( + F0CE51977FA6E83B6F11BE5C /* Examples.app */, + 590D83805AA598D2B0A7638A /* ExamplesTests.xctest */, + 3E2F68B22AFF73A71F86CABC /* ExamplesUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 43EC579EBDA110D1E1B3E494 /* Controllers */ = { + isa = PBXGroup; + children = ( + F0A6063E57EC170F558A3F74 /* ExampleTableViewController.swift */, + ); + path = Controllers; + sourceTree = ""; + }; + 52909911727F239150D4FE30 /* Examples */ = { + isa = PBXGroup; + children = ( + 524D79FF01638A0E8A3C1248 /* .swiftlint.yml */, + 274D496EC7E47F63FD0D1337 /* AppDelegate.swift */, + B31A932A62B6142FE20C39DF /* Assets.xcassets */, + E09DABCB8139643F1BD3B972 /* Examples_CarPlay.entitlements */, + 6D5B23007CF347976470A069 /* Examples-Bridging-Header.h */, + EC1155178B21E2E8075454A8 /* Info.plist */, + 55FDF5B3329BFD6E6C346D80 /* LaunchScreen.storyboard */, + 96B0E9086FE939F5D723136D /* VisionOSMain.swift */, + ACB753539ABAD3312B849CB1 /* All Examples */, + 43EC579EBDA110D1E1B3E494 /* Controllers */, + 9AC6F30FB33614908581AE8E /* Extensions */, + 846FF5BF97AEEAC876994DF3 /* Models */, + 55E0EEEC0D91AF2DFDCD9A33 /* SwiftUI Examples */, + ); + name = Examples; + path = Sources/Examples; + sourceTree = ""; + }; + 55E0EEEC0D91AF2DFDCD9A33 /* SwiftUI Examples */ = { + isa = PBXGroup; + children = ( + F890746B56E20150A053B41B /* AnnotationsExample.swift */, + 63A3027A7DA59E090DAD25F1 /* ClipLayerExample.swift */, + 46CE3D9C2873C0767DD76D85 /* ClusteringExample.swift */, + 29DD4C2F0049E575A6B5BF66 /* ColorThemeExample.swift */, + 7DED80ACD1618F8EE8F95A4A /* CustomGeometrySourceExample.swift */, + C61CC711054A032EE0446036 /* DynamicStylingExample.swift */, + A6B06A1D70F479D8DC5C375A /* FeaturesQueryExample.swift */, + 7613C4E19DCD679A2620223C /* GeofencingPlayground.swift */, + DD3A816C6E4D7A0A532EEE84 /* GeofencingUserLocation.swift */, + 62DA0608D44DEF6C4A82777C /* LocateMeExample.swift */, + 45B39AE24486FED5ED30392D /* LocationOverrideExample.swift */, + BFDBCA79855B58FE84333124 /* PrecipitationsExample.swift */, + 6CD7ADCCB774239AA0090C46 /* RasterParticleExample.swift */, + AC5A8729C9AEA4711B56B5F0 /* SnapshotMapExample.swift */, + E97E057D9FFB1DE3E16F1F0B /* StandardInteractiveBuildingsExample.swift */, + 0FBAFDF929532EDB964786F4 /* StandardInteractiveFeaturesExample.swift */, + 640198169EEDFC7CBEFCFCCF /* StandardStyleImportExample.swift */, + DD6F1212BB2453DBFECE12F2 /* StandardStyleLocationsExample.swift */, + 90CEF3209781923E53974F20 /* SwiftUIRoot.swift */, + 59AACE9E33102AE90526569F /* ViewAnnotationsExample.swift */, + 91D21963A6FF5DA26A210DA5 /* WeatherAnnotationExample.swift */, + 2CDB0066C8683D83F2532B62 /* Testing Examples */, + 708CF4C3DE36627B66561595 /* Util */, + ); + path = "SwiftUI Examples"; + sourceTree = ""; + }; + 708CF4C3DE36627B66561595 /* Util */ = { + isa = PBXGroup; + children = ( + 858990E6795D3162A941E82C /* ButtonStyle.swift */, + CBD8F852552CA316EFFCDD64 /* CalloutView.swift */, + 75D03F5A3A0E879717BFE421 /* Constants.swift */, + 6DC3D7884D057238010CB6E4 /* PinView.swift */, + 26964B05A794CC808C594AB5 /* SwiftExtensions.swift */, + 83E9078B1B13E54C2FFC5FFC /* View+OnShake.swift */, + F393DF039D7AD2F35C8DE4CE /* ViewExtensions.swift */, + ); + path = Util; + sourceTree = ""; + }; + 7E899D0C531F0E3FE49D97BB /* Packages */ = { + isa = PBXGroup; + children = ( + A8700CFE38DA4F1333F9E0F9 /* mapbox-maps-ios */, + ); + name = Packages; + sourceTree = ""; + }; + 800E2B17DCE6F4BCF502700A /* Annotations */ = { + isa = PBXGroup; + children = ( + B33F64CDBA98B91EE819B2C4 /* AddMarkersSymbolExample.swift */, + 4DCBE814694CF08A9C2E4A42 /* AddOneMarkerSymbolExample.swift */, + D44C0F343A1B5F4DA0C19B9C /* AnimatedMarkerExample.swift */, + DAC8E7B565C817D872CFBCAD /* AnnotationView.swift */, + 5C1658938A5F0908D151A9B9 /* CircleAnnotationExample.swift */, + 4D5DB9BD5E97D3C0080EC5D3 /* CustomPointAnnotationExample.swift */, + 3333EF3E0F1C789809F385AF /* DynamicViewAnnotationExample.swift */, + 5FC0F09FF1EF1A88BC1C6545 /* FrameViewAnnotationsExample.swift */, + 3F6F479FB7FFD90FA95F400E /* IconSizeChangeExample.swift */, + 3BDE7738CA55957F3FAC3ECE /* LineAnnotationExample.swift */, + B7A7586D05B960928AB17A0D /* MultipleGeometriesExample.swift */, + A8359ECB5024BEF3722C3CA1 /* PointAnnotationClusteringExample.swift */, + 7DCBE4524A8793B4DE950533 /* PolygonAnnotationExample.swift */, + 8FD38B8D20C0695A03AE4E68 /* SymbolClusteringExample.swift */, + 33E5647315A2357320BCF575 /* ViewAnnotationAnimationExample.swift */, + D3C6CF222C8EE9AE69563E39 /* ViewAnnotationBasicExample.swift */, + 1317C28ACDAC187017096A99 /* ViewAnnotationMarkerExample.swift */, + CB726A94B069761528B57EC8 /* ViewAnnotationWithPointAnnotationExample.swift */, + ); + path = Annotations; + sourceTree = ""; + }; + 846FF5BF97AEEAC876994DF3 /* Models */ = { + isa = PBXGroup; + children = ( + 6BB52F9D3A810B1A9CEC832C /* Example.swift */, + 10C7CEE3F343DF482D428211 /* ExampleProtocol.swift */, + 8B30F4697545D9F02DD4425D /* Examples.swift */, + ); + path = Models; + sourceTree = ""; + }; + 86B4D8C80C5BE1C8AA868516 /* Radar */ = { + isa = PBXGroup; + children = ( + 1BCE444AC33D2F1F88F4CCF0 /* radar0.gif */, + 8BD8BADE1108B0D380D9BEF8 /* radar1.gif */, + D8730F8FB259A4F889609108 /* radar2.gif */, + 24FD45C982E4403DA3BD241A /* radar3.gif */, + 876CE24F4E565ED342DDDCD6 /* radar4.gif */, + ); + path = Radar; + sourceTree = ""; + }; + 9AC6F30FB33614908581AE8E /* Extensions */ = { + isa = PBXGroup; + children = ( + 8BB15B17EDE597D37CFF3FCA /* Array+Split.swift */, + 455C0B9F01316D0FF38ED62B /* CLLocationCoordinate2D+Random.swift */, + F000C4D3B6FC70FA9607E3A3 /* UIColor+Random.swift */, + A615EFC3D6CF2A25C9864086 /* UIViewController+Extensions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 9BE0C6485A67784A07236499 /* CarPlay */ = { + isa = PBXGroup; + children = ( + 9F3F37CAE7A0B66BE429525C /* ApplicationCarPlaySceneDelegage.swift */, + DE6CEA9899CC0EC4F4381E19 /* CarPlayMapViewController.swift */, + 913B4773A82AD6357D6AAEA1 /* DashboardCarPlaySceneDelegate.swift */, + 0AD7661708AEADD25AB726B4 /* InstrumentClusterCarPlaySceneDelegate.swift */, + ); + path = CarPlay; + sourceTree = ""; + }; + ACB753539ABAD3312B849CB1 /* All Examples */ = { + isa = PBXGroup; + children = ( + 3FB717239BBFE5103C1EB1A4 /* AdvancedViewportGesturesExample.swift */, + 7274E152F7FBB7894447F822 /* AnimateGeoJSONLineExample.swift */, + A471857797898860C3A8F685 /* AnimateImageLayerExample.swift */, + 61A7965C46F2F371AA940A99 /* AnimateLayerExample.swift */, + DC98E9169E8E7DFE8DC1CB27 /* BasicLocationPulsingExample.swift */, + 257F10278F5E580C7ABA5570 /* BasicMapExample.swift */, + CFF80A86D7DEF54E0A7256DF /* BuildingExtrusionsExample.swift */, + 3E3A0CCD53B02FF2BAD830A2 /* CameraAnimationExample.swift */, + CDABBED8502001D17EF30F35 /* CameraAnimatorsExample.swift */, + CF35650C6319088CAAF95F84 /* CameraForExample.swift */, + 1D9B2D532D2F54C6C40DA466 /* ColorExpressionExample.swift */, + 262AD875661BAB5564084A9E /* Custom2DPuckExample.swift */, + 2C957F9CA07061B793C2DD4A /* Custom3DPuckExample.swift */, + 06955521CCC52B47AAB475F4 /* CustomLayerExample.swift */, + 2F6B4718F05FB1E6736EA1FF /* CustomRasterSourceExample.swift */, + 99A72B5DBF68C4729A5DC65D /* DataDrivenSymbolsExample.swift */, + 87D0CD9C2D04EA5B12E7F84C /* DataJoinExample.swift */, + A59EF8798357CF55109A4E56 /* DebugMapExample.swift */, + 9CCB8EE8E1C9C6CC85DA5D36 /* DistanceExpressionExample.swift */, + 133E4EABC7540ED460F08B8F /* ExternalVectorSourceExample.swift */, + 6239A5CDA61892902765B843 /* FeaturesAtPointExample.swift */, + D3FA795972BB141B9C582ED0 /* FeatureStateExample.swift */, + 0F9127909E8758EAC82E19F7 /* GeofencingExample.swift */, + 384FD8FC97B9F5011AF4BD61 /* GlobeFlyToExample.swift */, + 70922E748D003176C4A3C60A /* HeatmapLayerGlobeExample.swift */, + 3BCB1CC4577300FEF4DE017B /* InsetMapExample.swift */, + 99610E884967673F7F28D2B2 /* LayerPositionExample.swift */, + D058C94675BB58AE74392829 /* LayerSlotExample.swift */, + D4DA62349C7E9846A885BCD3 /* LiveDataExample.swift */, + 7DB76F486D80FED88678B04D /* LongTapAnimationExample.swift */, + D2D98F58F756D035C98B1F39 /* ModelLayerExample.swift */, + 3FC5980DD30479F30127BA71 /* NavigationSimulator.swift */, + 6A090EE21C9A65BE2697868F /* NavigationSimulatorExample.swift */, + 000D0E4CEFB6D5AF02518807 /* OfflineManagerExample.storyboard */, + 083C452C7342E73F6F16A83C /* OfflineManagerExample.swift */, + 1229327C13654C370B5641FC /* OfflineRegionManagerExample.swift */, + 0A5C0A3C44715B96D646ACB7 /* PitchAndDistanceExample.swift */, + 289434058C4AB25A17655FEF /* PointClusteringExample.swift */, + 98D66333697502A83B85BCD9 /* RasterColorExample.swift */, + 67FA937DC17F7EA27931763A /* RasterTileSourceExample.swift */, + 65AB4D202692A8951BAF2D57 /* RuntimeSlotsExample.swift */, + 267AA1719061B281479BBBCB /* SceneKitExample.swift */, + 93B8372871DB4BC991737A06 /* SkyLayerExample.swift */, + D77368780E4DBCCFB2CBD400 /* SnapshotterCoreGraphicsExample.swift */, + 588FD640D91E9DD366703F7B /* SnapshotterExample.swift */, + 1A182413E535260335459F26 /* SpinningGlobeExample.swift */, + 996964F634A4536F664C2611 /* StandardStyleExample.swift */, + 7F5E598A16FA446F583344CB /* TrackingModeExample.swift */, + E0C19E67A2E87A3D18B7B511 /* ViewportExample.swift */, + 2D91A8B64951711546335530 /* VoiceOverAccessibilityExample.swift */, + 800E2B17DCE6F4BCF502700A /* Annotations */, + 9BE0C6485A67784A07236499 /* CarPlay */, + F4E57BB1463BD6558C9EE37A /* Lab */, + D8F447EF49D96FF849215036 /* Sample Data */, + ); + path = "All Examples"; + sourceTree = ""; + }; + ADB2E9042412519F1EA183A8 /* ExamplesUITests */ = { + isa = PBXGroup; + children = ( + BE18E37A8652B4807D2459F1 /* ExamplesUITests.swift */, + ); + name = ExamplesUITests; + path = Tests/ExamplesUITests; + sourceTree = ""; + }; + AFDB1EA82615CFDF02CE1D4D = { + isa = PBXGroup; + children = ( + 52909911727F239150D4FE30 /* Examples */, + DA93D15473B052576C7D2965 /* ExamplesTests */, + ADB2E9042412519F1EA183A8 /* ExamplesUITests */, + 7E899D0C531F0E3FE49D97BB /* Packages */, + 3FDBCEEB7C4C53F0545FC427 /* Products */, + ); + sourceTree = ""; + }; + D8F447EF49D96FF849215036 /* Sample Data */ = { + isa = PBXGroup; + children = ( + 28CE7DA39D29A8311E4A58A4 /* 34M_17.dae */, + B05B410135D0B466B73C0765 /* annotations.json */, + 989F5AB9D5D8AD39D21327A1 /* blueprint_style.json */, + 5C0C8783B2A74AE9DE3F6C32 /* CustomLayerExampleShaders.metal */, + A9A26CBC58F3271DBFD2EE7D /* CustomLayerShaderTypes.h */, + 02DA2CC04980F807255D646B /* Fire_Hydrants.geojson */, + 50618B3CF42CCF735CCAE9B4 /* fragment-realestate-NY.json */, + F033C8EFB89A90D6705B047D /* GeoJSONSourceExample.geojson */, + 2DD8B1D25297B7433F4AAF35 /* GradientLine.geojson */, + 450C8D5E4B84428FE51BCA97 /* route.geojson */, + FE2A263DD2E9DC52CEE356FA /* sf_airport_route.geojson */, + C47942F80A50166AC823012B /* sportcar.glb */, + 86B4D8C80C5BE1C8AA868516 /* Radar */, + ); + path = "Sample Data"; + sourceTree = ""; + }; + DA93D15473B052576C7D2965 /* ExamplesTests */ = { + isa = PBXGroup; + children = ( + 4EE19F00E87B31FDE5481D56 /* ExamplesTests.swift */, + 9CB74046A2FC03770B62B9E6 /* TestableExampleTests.swift */, + ); + name = ExamplesTests; + path = Tests/ExamplesTests; + sourceTree = ""; + }; + F4E57BB1463BD6558C9EE37A /* Lab */ = { + isa = PBXGroup; + children = ( + 50F519EC4AF0B4BEE0FDCABC /* ColorThemeMapExample.swift */, + 8DBF737C9C7FBF46BB5F7B78 /* CombineExample.swift */, + A4EE8F38428A64B5B9D4DBBE /* CombineLocationExample.swift */, + 60A8CD8A69D3429FCF8ACBDD /* Lights3DExample.swift */, + 370DFCA52EB6C7F119BF81DA /* MapEventsExample.swift */, + E612275E3042D0D0AF8B583E /* MapRecorderExample.swift */, + 12A0818B5BC601707E3235A9 /* ResizableImageExample.swift */, + 6E54D3F5943238258AA0A9BE /* ResizeMapViewExample.swift */, + ); + path = Lab; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 542BE47134765F0824559C2F /* ExamplesTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0D8722284ECC1867E6E635FE /* Build configuration list for PBXNativeTarget "ExamplesTests" */; + buildPhases = ( + 4BAC53897FC5F4EA73042115 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + A1988D683D0768E02BB6E3C8 /* PBXTargetDependency */, + ); + name = ExamplesTests; + productName = ExamplesTests; + productReference = 590D83805AA598D2B0A7638A /* ExamplesTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + DF268D7B1F3C2B89CA87979A /* Examples */ = { + isa = PBXNativeTarget; + buildConfigurationList = CFED1FDA584AD607AF2DD604 /* Build configuration list for PBXNativeTarget "Examples" */; + buildPhases = ( + 85A476271D6539E943802635 /* Insert Mapbox Access Token */, + 9A544F8C16BDF68F6F137C4C /* Sources */, + E6F37CB2EFFA996B3BEF9714 /* Resources */, + AF4891A70CEBFB45EE553EAA /* Frameworks */, + A0C4B2DCB12F20C319BE0B90 /* Run swiftlint */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Examples; + packageProductDependencies = ( + 0AF5F744C6369BF1FB233FB6 /* MapboxMaps */, + FD9311FF1C736B80A26F4258 /* Fingertips */, + ); + productName = Examples; + productReference = F0CE51977FA6E83B6F11BE5C /* Examples.app */; + productType = "com.apple.product-type.application"; + }; + F3570A57FF15EEC3B4529EF0 /* ExamplesUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F1694CBC313E7E2A2DCF4B56 /* Build configuration list for PBXNativeTarget "ExamplesUITests" */; + buildPhases = ( + DDC20FA491768DC603B80E86 /* Sources */, + ); + buildRules = ( + ); + dependencies = ( + 50B94E0A74D725D5BEDF1F9D /* PBXTargetDependency */, + ); + name = ExamplesUITests; + productName = ExamplesUITests; + productReference = 3E2F68B22AFF73A71F86CABC /* ExamplesUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 604281A06A1DE9F942BCBA73 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; + TargetAttributes = { + 542BE47134765F0824559C2F = { + DevelopmentTeam = GJZR2MEM28; + }; + DF268D7B1F3C2B89CA87979A = { + DevelopmentTeam = GJZR2MEM28; + }; + F3570A57FF15EEC3B4529EF0 = { + DevelopmentTeam = GJZR2MEM28; + TestTargetID = DF268D7B1F3C2B89CA87979A; + }; + }; + }; + buildConfigurationList = 2A75F03E88AFDF6CA6BA2141 /* Build configuration list for PBXProject "Examples" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + Base, + en, + ); + mainGroup = AFDB1EA82615CFDF02CE1D4D; + packageReferences = ( + B50D5CC28BF0DFBA55456D89 /* XCRemoteSwiftPackageReference "Fingertips" */, + ); + projectDirPath = ""; + projectRoot = ""; + targets = ( + DF268D7B1F3C2B89CA87979A /* Examples */, + 542BE47134765F0824559C2F /* ExamplesTests */, + F3570A57FF15EEC3B4529EF0 /* ExamplesUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E6F37CB2EFFA996B3BEF9714 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E2CD9B66367265509567A4E3 /* .swiftlint.yml in Resources */, + 83DCCC7FEFF6D94D3DF0B587 /* 34M_17.dae in Resources */, + C940835B030A20F0C5BC31AD /* Assets.xcassets in Resources */, + F5E96E5798947CA56FD77CF9 /* Fire_Hydrants.geojson in Resources */, + 18F76FE745B049D1F0CAF6CA /* GeoJSONSourceExample.geojson in Resources */, + 85AA0D942D4C0E218D87F7D8 /* GradientLine.geojson in Resources */, + 85E0F727CBB3374D3EF499C3 /* LaunchScreen.storyboard in Resources */, + 634BA74F4E553C53EE906F5A /* OfflineManagerExample.storyboard in Resources */, + C6E1E615C75960D1BD1755A9 /* annotations.json in Resources */, + 5A6D7B2A302A6555FE23FF80 /* blueprint_style.json in Resources */, + 560D4A0D2C704ECC346D8B5F /* fragment-realestate-NY.json in Resources */, + 1820AE40702C7875656BA2D7 /* radar0.gif in Resources */, + F6E3EF9BE4F1D2F58DE1BED2 /* radar1.gif in Resources */, + 30589E5AB307FC934E466332 /* radar2.gif in Resources */, + 5A68CBA756780F7DE8F7BDCC /* radar3.gif in Resources */, + 61B79A9069DCE6865E43E261 /* radar4.gif in Resources */, + 2B44F3E8EF3A50D9AE6B825F /* route.geojson in Resources */, + 01310DFD10B3804EE3548509 /* sf_airport_route.geojson in Resources */, + 803CCCEA28B209111BE0786F /* sportcar.glb in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 85A476271D6539E943802635 /* Insert Mapbox Access Token */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Insert Mapbox Access Token"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(INFOPLIST_PREFIX_HEADER)", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/bash\n\n# Usage: Used as a build phase script to inject the Mapbox access token into the Info.plist.\n# Dependencies: INFOPLIST_PREFIX_HEADER) build setting must be set in target that use this script, in order to locate Info.plis file.\n\necho \"Inserting Mapbox access token...\"\ntoken_file=~/.mapbox\ntoken_file2=~/mapbox\ntoken=\"$(cat $token_file 2>/dev/null || cat $token_file2 2>/dev/null)\"\n\nif [ \"$token\" ]; then\n echo \"#define MAPBOX_ACCESS_TOKEN $token\" > \"$INFOPLIST_PREFIX_HEADER\"\n echo \"Generated $INFOPLIST_PREFIX_HEADER\"\nelse\n echo \\'error: Missing Mapbox access token\\'\n echo \"error: Get an access token from , then create a new file at ~/.mapbox that contains the access token.\"\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + A0C4B2DCB12F20C319BE0B90 /* Run swiftlint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run swiftlint"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/usr/bin/env bash\n\n# Usage: Used as a build phase script in Xcode to run SwiftLint\n# Dependencies: MAPBOXMAPS_PATH (relative path to mapbox-maps-ios) build setting must be set in target that use this script, in order to find the .swiftlint.yml file\n\n# Support Howebrew path on Apple Silicon macOS\nexport PATH=\"$PATH:/opt/homebrew/bin\"\n\necho \"MAPBOXMAPS_PATH=$MAPBOXMAPS_PATH\"\n\npushd \"$MAPBOXMAPS_PATH\" || exit 1\npwd\n\necho \"Running SwiftLint in $PWD\"\n\nif which swiftlint > /dev/null; then\n swiftlint lint \"$@\"\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4BAC53897FC5F4EA73042115 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 655105BD0FAFF4C4BA65DC32 /* ExamplesTests.swift in Sources */, + 38DF3926AD9DDDE883454F64 /* TestableExampleTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9A544F8C16BDF68F6F137C4C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 05DF15DADC248A2CAA5EEDC4 /* AddMarkersSymbolExample.swift in Sources */, + 2C03342240D5487880316518 /* AddOneMarkerSymbolExample.swift in Sources */, + 10C2E5ADC16B91D43288E820 /* AdvancedViewportGesturesExample.swift in Sources */, + BDABAAC8727AF67A0DEE2020 /* AnimateGeoJSONLineExample.swift in Sources */, + F476D12AC7B4347AA55BEC4C /* AnimateImageLayerExample.swift in Sources */, + 556C8423BA408C7FF54BB5DA /* AnimateLayerExample.swift in Sources */, + 392857DBD1231B0438144335 /* AnimatedMarkerExample.swift in Sources */, + D27F0573360A7234BCF7AB6C /* AnnotationView.swift in Sources */, + 7E84D4D6459049E452808C91 /* AnnotationsExample.swift in Sources */, + 5E508E47388A646B4F74DD0B /* AnnotationsOrderTestExample.swift in Sources */, + E5A3B926DD7E451F1E660547 /* AppDelegate.swift in Sources */, + 79B889CF23A3C0A5EA7F6ADD /* ApplicationCarPlaySceneDelegage.swift in Sources */, + 32FA2A4133B0464494212B34 /* Array+Split.swift in Sources */, + 50641F1F3A58B85873E2E5B8 /* AttributionDialogueExamples.swift in Sources */, + A3D7C0836BFE6FEB40C3C15A /* BasicLocationPulsingExample.swift in Sources */, + D4FFFAE49D4B805BDA014AAD /* BasicMapExample.swift in Sources */, + B53EA441C54E2B680A7E99F0 /* BuildingExtrusionsExample.swift in Sources */, + C327DBA17D79D5DFBBE84BE0 /* ButtonStyle.swift in Sources */, + 64F4FA139388DB34564AD42D /* CLLocationCoordinate2D+Random.swift in Sources */, + 03EDEA6452582E7E6805C824 /* CalloutView.swift in Sources */, + 821807D61D52F0E60925BCD4 /* CameraAnimationExample.swift in Sources */, + 9A403D6AB6D6336E212726C5 /* CameraAnimatorsExample.swift in Sources */, + 33B816803AF5330796686AA1 /* CameraForExample.swift in Sources */, + F0502A1ACF0AED218F8184AB /* CarPlayMapViewController.swift in Sources */, + CA2209956E93ECB18C4C9DEC /* CircleAnnotationExample.swift in Sources */, + 3B4862E6832F23CB115D444A /* ClipLayerExample.swift in Sources */, + 1DAE02D73D16E543777C2025 /* ClusteringExample.swift in Sources */, + 5A28C124249725578389175A /* ColorExpressionExample.swift in Sources */, + DA109856E64BBD8071DF0619 /* ColorThemeExample.swift in Sources */, + 65E9F2B993AEB394FC2D0080 /* ColorThemeMapExample.swift in Sources */, + C664365A373267B564EC84EE /* CombineExample.swift in Sources */, + 215230836B6AD1040D3DA547 /* CombineLocationExample.swift in Sources */, + 3E515D1DD1D9CA02F3E95AA2 /* Constants.swift in Sources */, + 60A1572CCF5763FA3C946B89 /* Custom2DPuckExample.swift in Sources */, + 6B040F65241ABF600D70D14D /* Custom3DPuckExample.swift in Sources */, + 4E64A70408A69F2BC9F70610 /* CustomGeometrySourceExample.swift in Sources */, + EE4064D753E360A6A6AC5BAC /* CustomLayerExample.swift in Sources */, + 08DD7D352E50C412B667D6F6 /* CustomLayerExampleShaders.metal in Sources */, + D77EEB488CFD90F602077E8F /* CustomPointAnnotationExample.swift in Sources */, + 4105BDB79F22905F065071F3 /* CustomRasterSourceExample.swift in Sources */, + 7686448F8648BECC75A912B6 /* DashboardCarPlaySceneDelegate.swift in Sources */, + 1B5230204B5659B1F05C303D /* DataDrivenSymbolsExample.swift in Sources */, + DA69CB0BD9F0DDA0FD1387B0 /* DataJoinExample.swift in Sources */, + B9D4B9C3042383738AB5B667 /* DebugMapExample.swift in Sources */, + 1C70390E725564D6E60865EF /* DistanceExpressionExample.swift in Sources */, + CBCC60FF68BE9754DE0C6AF3 /* DynamicStylingExample.swift in Sources */, + DFC64A62538E787D57B6514D /* DynamicViewAnnotationExample.swift in Sources */, + D62F69A9BD802A1926B92968 /* Example.swift in Sources */, + 10ECE7FE19CEC239DDA96961 /* ExampleProtocol.swift in Sources */, + 7B9835E597E0B2655E181A48 /* ExampleTableViewController.swift in Sources */, + 9DFE9DDE63B78393031C843E /* Examples.swift in Sources */, + A972D3306BC53DEC9798C60D /* ExternalVectorSourceExample.swift in Sources */, + D63431CA78A557A0FB92177A /* FeatureStateExample.swift in Sources */, + FDA4B57BE32D92BB57A5B7E6 /* FeaturesAtPointExample.swift in Sources */, + 854CE1A84AADF6FBB232CB5F /* FeaturesQueryExample.swift in Sources */, + AE6E90DB7B6DA4580C2DAB59 /* FrameViewAnnotationsExample.swift in Sources */, + 9717811318AAE88DE9214307 /* GeofencingExample.swift in Sources */, + DCA54F7383085A8FD822F0BF /* GeofencingPlayground.swift in Sources */, + 5F2AD73C8104089C9291574E /* GeofencingUserLocation.swift in Sources */, + 0414AD72988F405F5BA1D843 /* GlobeFlyToExample.swift in Sources */, + 49F6209402BF34C06C90107A /* HeatmapLayerGlobeExample.swift in Sources */, + B9B1EE72E6203358F2785916 /* IconSizeChangeExample.swift in Sources */, + 423A42B555DD0B3AD4856FCF /* InsetMapExample.swift in Sources */, + 94DB7E8C829041DC5F5B2300 /* InstrumentClusterCarPlaySceneDelegate.swift in Sources */, + AD0922FA7F69AEE4C23F2351 /* InteractionsPlayground.swift in Sources */, + 8B4085733CCABE3BE3D16F7E /* LayerPositionExample.swift in Sources */, + 918F4BDCC25819DD68BC9518 /* LayerSlotExample.swift in Sources */, + C04160BF66055F7DE9315395 /* Lights3DExample.swift in Sources */, + 1B97702805C5EC4703A6CAA9 /* LineAnnotationExample.swift in Sources */, + 48040990713D3220E7055434 /* LiveDataExample.swift in Sources */, + E121F023995CCF2F3A65BC2A /* LocateMeExample.swift in Sources */, + 759D42AA5FE93B6FA9DFADF5 /* LocationOverrideExample.swift in Sources */, + AE51E276DCD8CF89AB339224 /* LongTapAnimationExample.swift in Sources */, + 03EEF25ABD58ADD9631AB509 /* MapEventsExample.swift in Sources */, + 3FD83483E0AE57790504CB0C /* MapRecorderExample.swift in Sources */, + D98624793DA36578289F02FF /* MapScrollExample.swift in Sources */, + 8913C410B72BE0C40EE02BB9 /* MapSettingsExample.swift in Sources */, + 4ACB99FAFBF38A425EBD0285 /* ModelLayerExample.swift in Sources */, + F5311222553DA118AC571D82 /* MultipleGeometriesExample.swift in Sources */, + D94672F30272E31087AB5DDD /* NavigationSimulator.swift in Sources */, + 902FD51EC410A1E8BD88941D /* NavigationSimulatorExample.swift in Sources */, + E6B722A64C15CE701287B464 /* OfflineManagerExample.swift in Sources */, + 5FF3E34B523C39A404154BF7 /* OfflineRegionManagerExample.swift in Sources */, + 312CE7CED726F0A572301622 /* PinView.swift in Sources */, + A6389C28B8AAC39878591AD0 /* PitchAndDistanceExample.swift in Sources */, + F48BF087BB56B0A44D8B16F3 /* PointAnnotationClusteringExample.swift in Sources */, + 4417BB8A356335BC8421A19B /* PointClusteringExample.swift in Sources */, + 6661DB69D4980E24BCA18AB2 /* PolygonAnnotationExample.swift in Sources */, + FA077DC5A6CF295906536DF1 /* PrecipitationsExample.swift in Sources */, + B304BACFCD08802A740E8919 /* PuckPlayground.swift in Sources */, + 0E191B29AE31584DCFDC3821 /* RasterColorExample.swift in Sources */, + F492E2C8D35572B4CF48FE68 /* RasterParticleExample.swift in Sources */, + E8CEBC697D805204F129C4FB /* RasterTileSourceExample.swift in Sources */, + 191391C51FC69A6D36EB67F0 /* ResizableImageExample.swift in Sources */, + 86AED5DD9F8C8BB2C9736483 /* ResizeMapViewExample.swift in Sources */, + CBD01BBA4E78796827A6E52D /* RuntimeSlotsExample.swift in Sources */, + 4791CACAC0846107E4B0955B /* SceneKitExample.swift in Sources */, + F613749DCDDDDC6F041032A0 /* SimpleMapExample.swift in Sources */, + 1F860D5B445E75772C4C3B6C /* SkyLayerExample.swift in Sources */, + 79843B780E7C5DC68433B745 /* SnapshotMapExample.swift in Sources */, + F2B385831A78B3EE16BFEA69 /* SnapshotterCoreGraphicsExample.swift in Sources */, + 68FD9E1F4606B2729BA1E6DC /* SnapshotterExample.swift in Sources */, + 442DB919BE75CE7B0A537757 /* SpinningGlobeExample.swift in Sources */, + 18919387995A155D6F64DADF /* StandardInteractiveBuildingsExample.swift in Sources */, + FA53EEA88DB29D4D5AC58514 /* StandardInteractiveFeaturesExample.swift in Sources */, + 1372F3B8047B6B4EE70933D9 /* StandardStyleExample.swift in Sources */, + 2997D21A7DB20098C6D03D3B /* StandardStyleImportExample.swift in Sources */, + 8F0BEA796867B64E48A1B328 /* StandardStyleLocationsExample.swift in Sources */, + EB39F159A9F5DFAB935F629D /* SwiftExtensions.swift in Sources */, + 7EDF38D2E9CDE489F320977E /* SwiftUIRoot.swift in Sources */, + C953F022C91FCA59CFF06BE9 /* SymbolClusteringExample.swift in Sources */, + 7036A19FCD2CCE85BDDF4E00 /* TrackingModeExample.swift in Sources */, + A6A68B4ED674A924ACBD8FA2 /* UIColor+Random.swift in Sources */, + D9297596469F9B31C2350B43 /* UIViewController+Extensions.swift in Sources */, + 1687412AC1637D7EA697C7A4 /* View+OnShake.swift in Sources */, + CF5C5513D659D4981706DDEC /* ViewAnnotationAnimationExample.swift in Sources */, + BD99E89F050E7D93846147FF /* ViewAnnotationBasicExample.swift in Sources */, + 4EF3E4C342C3F8ED5BF6C332 /* ViewAnnotationMarkerExample.swift in Sources */, + 56446E388868862E2BB80F4D /* ViewAnnotationWithPointAnnotationExample.swift in Sources */, + E2617ACF1E2367C012A87CD1 /* ViewAnnotationsExample.swift in Sources */, + 8418F1775D49F5A66489B988 /* ViewExtensions.swift in Sources */, + 14799547EFD5C4757FBAD6E4 /* ViewportExample.swift in Sources */, + 5F537B052041931CB507E12B /* ViewportPlayground.swift in Sources */, + 5F556CB71C442EC2A8C2E229 /* VisionOSMain.swift in Sources */, + C315E1C61D222296FE0244FC /* VoiceOverAccessibilityExample.swift in Sources */, + 38AD95B6DD9BE858F4E59C31 /* WeatherAnnotationExample.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DDC20FA491768DC603B80E86 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7365170E39A459EB4DFA198B /* ExamplesUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 50B94E0A74D725D5BEDF1F9D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DF268D7B1F3C2B89CA87979A /* Examples */; + targetProxy = 39418E4FA1B2F4F66836739C /* PBXContainerItemProxy */; + }; + A1988D683D0768E02BB6E3C8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DF268D7B1F3C2B89CA87979A /* Examples */; + targetProxy = C86D6DE9936D944850C4ED52 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 55FDF5B3329BFD6E6C346D80 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + FC1CEEE6277DEAD9FDFF4AAC /* en */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 09D1E7576035B421AAE335B6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/../lib"; + PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxTests; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2,7"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Examples.app/Examples"; + }; + name = Debug; + }; + 411D97DEB7A3E977012CCD82 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_GCD_PERFORMANCE = YES; + CLANG_ANALYZER_LOCALIZABILITY_EMPTY_CONTEXT = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_COMMIT_HASH = deadbeef; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = GJZR2MEM28; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "DEBUG=1", + ); + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_GENERATE_MAP_FILE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.9; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Debug; + }; + AD36CEE41FE9B3978B1F79A1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/../lib"; + PRODUCT_BUNDLE_IDENTIFIER = mapbox.ExamplesUITests; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2,7"; + TEST_TARGET_NAME = Examples; + }; + name = Debug; + }; + B95EDA70D0A9CFB3CA61A6D7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = Sources/Examples/Info.plist; + INFOPLIST_PREFIX_HEADER = "$(DERIVED_FILE_DIR)/InfoPlist.Prefix.h"; + INFOPLIST_PREPROCESS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MAPBOXMAPS_PATH = ./; + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.examples; + SDKROOT = auto; + SKIP_INSTALL = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "$(MAPBOXMAPS_PATH)/Sources/Examples/Examples-Bridging-Header.h"; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Debug; + }; + D2FCF69D38F0749388CA45CE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_GCD_PERFORMANCE = YES; + CLANG_ANALYZER_LOCALIZABILITY_EMPTY_CONTEXT = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_STATIC_ANALYZER_MODE = deep; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_COMMIT_HASH = deadbeef; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = GJZR2MEM28; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_ENABLE_CPP_RTTI = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_GENERATE_MAP_FILE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + RUN_CLANG_STATIC_ANALYZER = YES; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.9; + TARGETED_DEVICE_FAMILY = "1,2,7"; + XROS_DEPLOYMENT_TARGET = 1.0; + }; + name = Release; + }; + EA4E5215ED4A7AFAB117E236 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = Sources/Examples/Info.plist; + INFOPLIST_PREFIX_HEADER = "$(DERIVED_FILE_DIR)/InfoPlist.Prefix.h"; + INFOPLIST_PREPROCESS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MAPBOXMAPS_PATH = ./; + MARKETING_VERSION = 1.0.0; + PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.examples; + SDKROOT = auto; + SKIP_INSTALL = NO; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_OBJC_BRIDGING_HEADER = "$(MAPBOXMAPS_PATH)/Sources/Examples/Examples-Bridging-Header.h"; + TARGETED_DEVICE_FAMILY = "1,2,7"; + }; + name = Release; + }; + F739366A761B1838E499973E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/../lib"; + PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxTests; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2,7"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Examples.app/Examples"; + }; + name = Release; + }; + FB78F9850E592CB7090068A8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; + BUNDLE_LOADER = "$(TEST_HOST)"; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks $(PROJECT_DIR)/../lib"; + PRODUCT_BUNDLE_IDENTIFIER = mapbox.ExamplesUITests; + SDKROOT = auto; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + TARGETED_DEVICE_FAMILY = "1,2,7"; + TEST_TARGET_NAME = Examples; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0D8722284ECC1867E6E635FE /* Build configuration list for PBXNativeTarget "ExamplesTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 09D1E7576035B421AAE335B6 /* Debug */, + F739366A761B1838E499973E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + 2A75F03E88AFDF6CA6BA2141 /* Build configuration list for PBXProject "Examples" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 411D97DEB7A3E977012CCD82 /* Debug */, + D2FCF69D38F0749388CA45CE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + CFED1FDA584AD607AF2DD604 /* Build configuration list for PBXNativeTarget "Examples" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B95EDA70D0A9CFB3CA61A6D7 /* Debug */, + EA4E5215ED4A7AFAB117E236 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; + F1694CBC313E7E2A2DCF4B56 /* Build configuration list for PBXNativeTarget "ExamplesUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AD36CEE41FE9B3978B1F79A1 /* Debug */, + FB78F9850E592CB7090068A8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Debug; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + B50D5CC28BF0DFBA55456D89 /* XCRemoteSwiftPackageReference "Fingertips" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "git@github.com:mapbox/Fingertips.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 0AF5F744C6369BF1FB233FB6 /* MapboxMaps */ = { + isa = XCSwiftPackageProductDependency; + productName = MapboxMaps; + }; + FD9311FF1C736B80A26F4258 /* Fingertips */ = { + isa = XCSwiftPackageProductDependency; + package = B50D5CC28BF0DFBA55456D89 /* XCRemoteSwiftPackageReference "Fingertips" */; + productName = Fingertips; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 604281A06A1DE9F942BCBA73 /* Project object */; +} diff --git a/Apps/Examples/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Apps/Examples/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 000000000000..629806d1b51e --- /dev/null +++ b/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,41 @@ +{ + "pins" : [ + { + "identity" : "fingertips", + "kind" : "remoteSourceControl", + "location" : "git@github.com:mapbox/Fingertips.git", + "state" : { + "revision" : "828349081181608bf67abe53f5881728a14a0f78", + "version" : "0.6.0" + } + }, + { + "identity" : "mapbox-common-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mapbox/mapbox-common-ios.git", + "state" : { + "revision" : "eea3a287b1f6212a624b05286799cd22c554849f", + "version" : "24.10.0-rc.1" + } + }, + { + "identity" : "mapbox-core-maps-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mapbox/mapbox-core-maps-ios.git", + "state" : { + "revision" : "1938905a7bc2721069df573d5010d8461fbe5ec7", + "version" : "11.10.0-rc.1" + } + }, + { + "identity" : "turf-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mapbox/turf-swift.git", + "state" : { + "revision" : "bf840e6b9529d105687840fe2c9dcd74197d46d1", + "version" : "4.0.0" + } + } + ], + "version" : 2 +} diff --git a/Examples.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme b/Examples.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme new file mode 100644 index 000000000000..1b29575a57f2 --- /dev/null +++ b/Examples.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples.yml b/Examples.yml new file mode 100644 index 000000000000..246a647be30e --- /dev/null +++ b/Examples.yml @@ -0,0 +1,25 @@ +name: Examples +include: + - xcodegen/Examples.yml +packages: + MapboxMaps: + path: . +targets: + Examples: + settings: + base: + MAPBOXMAPS_PATH: ./ + scheme: + testPlans: + - path: Tests/TestPlans/Examples.xctestplan + defaultPlan: true + - path: Tests/TestPlans/Examples no unit tests.xctestplan + defaultPlan: true + +targetTemplates: + map-library:REPLACE: + dependencies: + - package: MapboxMaps + + maps-dependencies: + dependencies: diff --git a/Gemfile b/Gemfile index e11fb319155b..f407285aacad 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source "https://rubygems.org" -gem "fastlane" +# Fastlane 2.220.0 introduced a new crypto algo for Match, which is not compatible with the pre-existed versions +gem "fastlane", '= 2.219.0' plugins_path = File.join(File.dirname(__FILE__), '.fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 1a700de3a956..3a8006f6b0a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,43 +14,45 @@ GIT GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.5) + CFPropertyList (3.0.7) + base64 + nkf rexml - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - artifactory (3.0.15) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + artifactory (3.0.17) atomos (0.1.3) - aws-eventstream (1.2.0) - aws-partitions (1.598.0) - aws-sdk-core (3.131.1) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.525.0) - aws-sigv4 (~> 1.1) + aws-eventstream (1.3.0) + aws-partitions (1.1023.0) + aws-sdk-core (3.214.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.57.0) - aws-sdk-core (~> 3, >= 3.127.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.114.0) - aws-sdk-core (~> 3, >= 3.127.0) + aws-sdk-kms (1.96.0) + aws-sdk-core (~> 3, >= 3.210.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.176.1) + aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) - aws-sigv4 (1.5.0) + aws-sigv4 (~> 1.5) + aws-sigv4 (1.10.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) + base64 (0.2.0) claide (1.1.0) colored (1.2) colored2 (3.1.2) commander (4.6.0) highline (~> 2.0.0) declarative (0.0.20) - digest-crc (0.6.4) + digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.6) + domain_name (0.6.20240107) + dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.92.3) - faraday (1.10.0) + excon (0.112.0) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -71,15 +73,15 @@ GEM faraday-httpclient (1.0.1) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (1.0.1) + faraday-net_http (1.0.2) faraday-net_http_persistent (1.2.0) faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) - fastimage (2.2.6) - fastlane (2.206.2) + fastimage (2.3.1) + fastlane (2.219.0) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -98,20 +100,22 @@ GEM gh_inspector (>= 1.1.2, < 2.0.0) google-apis-androidpublisher_v3 (~> 0.3) google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-env (>= 1.6.0, < 2.0.0) google-cloud-storage (~> 1.31) highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (~> 2.0.0) + multipart-post (>= 2.0.0, < 3.0.0) naturally (~> 2.2) - optparse (~> 0.1.1) + optparse (>= 0.1.1) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) simctl (~> 1.6.3) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) @@ -119,9 +123,9 @@ GEM xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.22.0) - google-apis-core (>= 0.5, < 2.a) - google-apis-core (0.5.0) + google-apis-androidpublisher_v3 (0.54.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -129,93 +133,88 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick - google-apis-iamcredentials_v1 (0.11.0) - google-apis-core (>= 0.5, < 2.a) - google-apis-playcustomapp_v1 (0.8.0) - google-apis-core (>= 0.5, < 2.a) - google-apis-storage_v1 (0.15.0) - google-apis-core (>= 0.5, < 2.a) - google-cloud-core (1.6.0) - google-cloud-env (~> 1.0) + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.31.0) + google-apis-core (>= 0.11.0, < 2.a) + google-cloud-core (1.7.1) + google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.2.0) - google-cloud-storage (1.36.2) + google-cloud-errors (1.4.0) + google-cloud-storage (1.47.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.31.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.1.3) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.5) + http-cookie (1.0.8) domain_name (~> 0.5) httpclient (2.8.3) - jmespath (1.6.1) - json (2.6.2) - jwt (2.4.1) - memoist (0.16.2) - mini_magick (4.11.0) - mini_mime (1.1.2) + jmespath (1.6.2) + json (2.9.0) + jwt (2.9.3) + base64 + mini_magick (4.13.2) + mini_mime (1.1.5) multi_json (1.15.0) - multipart-post (2.0.0) - nanaimo (0.3.0) + multipart-post (2.4.1) + nanaimo (0.4.0) naturally (2.2.1) - optparse (0.1.1) + nkf (0.2.0) + optparse (0.6.0) os (1.1.4) - plist (3.6.0) - public_suffix (4.0.7) - rake (13.0.6) + plist (3.7.1) + public_suffix (6.0.1) + rake (13.2.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.5) + rexml (3.4.0) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.16.1) + signet (0.19.0) addressable (~> 2.8) - faraday (>= 0.17.5, < 3.0) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.8) + simctl (1.6.10) CFPropertyList naturally terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) tty-cursor (0.7.1) - tty-screen (0.8.1) + tty-screen (0.8.2) tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (1.8.0) - webrick (1.7.0) + unicode-display_width (2.6.0) word_wrap (1.0.0) - xcodeproj (1.21.0) + xcodeproj (1.27.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (~> 3.2.4) + nanaimo (~> 0.4.0) + rexml (>= 3.3.6, < 4.0) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) @@ -223,10 +222,12 @@ GEM PLATFORMS arm64-darwin-21 + arm64-darwin-22 + arm64-darwin-23 DEPENDENCIES - fastlane + fastlane (= 2.219.0) fastlane-plugin-firebase_test_lab! BUNDLED WITH - 2.3.15 + 2.5.4 diff --git a/LICENSE.md b/LICENSE.md index 629152d1c3db..338fb11e0619 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,104 +1,118 @@ ## License -Mapbox Maps for iOS version 10.0 +Mapbox Maps for iOS version 11.10.0-rc.1 Mapbox Maps iOS SDK -Copyright © 2021 Mapbox +Copyright © 2021 - 2025 Mapbox, Inc. All rights reserved. -All rights reserved. - -Mapbox Maps for iOS version 10.0 (“Mapbox Maps iOS SDK”) or higher must be used according to the Mapbox Terms of Service. This license allows developers with a current active Mapbox account to use and modify the Mapbox Maps iOS SDK. Developers may modify the Mapbox Maps iOS SDK code so long as the modifications do not change or interfere with marked portions of the code related to billing, accounting, and anonymized data collection. The Mapbox Maps iOS SDK sends anonymized location and usage data, which Mapbox uses for fixing bugs and errors, accounting, and generating aggregated anonymized statistics. This license terminates automatically if a user no longer has an active Mapbox account. - -For the full license terms, please see the Mapbox Terms of Service at https://www.mapbox.com/legal/tos/ +The software and files in this repository (collectively, “Software”) are licensed under the Mapbox TOS for use only with the relevant Mapbox product(s) listed at www.mapbox.com/pricing. This license allows developers with a current active Mapbox account to use and modify the authorized portions of the Software as needed for use only with the relevant Mapbox product(s) through their Mapbox account in accordance with the Mapbox TOS. This license terminates automatically if a developer no longer has a Mapbox account in good standing or breaches the Mapbox TOS. For the license terms, please see the Mapbox TOS at https://www.mapbox.com/legal/tos/ which incorporates the Mapbox Product Terms at www.mapbox.com/legal/service-terms. If this Software is a SDK, modifications that change or interfere with marked portions of the code related to billing, accounting, or data collection are not authorized and the SDK sends limited de-identified location and usage data which is used in accordance with the Mapbox TOS. [Updated 2023-01] ## Acknowledgements This application makes use of the following third party libraries: -### MapboxCommon-ios +### turf-swift, https://github.com/mapbox/turf-swift ``` -Copyright (c) 2021 - Mapbox, Inc. - -You may use this code with your Mapbox account and under the -Mapbox Terms of Service (https://www.mapbox.com/tos/). - -All other rights reserved. - -=========================================================================== +Copyright © 2014–2024, Mapbox -Copyright (c) 2021 - Mapbox, Inc. +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. -You may use this code with your Mapbox account and under the -Mapbox Terms of Service (https://www.mapbox.com/tos/). +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` -All other rights reserved. +--- -=========================================================================== +### MapboxCoreMaps,11.10.0-rc.1,Mapbox ToS,Mapbox,https://www.mapbox.com/ -Mapbox Common SDK uses portions of linenoise +``` +Mapbox Core Maps version 11.0 +Mapbox Core Maps SDK -Copyright (c) 2010-2014, Salvatore Sanfilippo -Copyright (c) 2010-2013, Pieter Noordhuis +Copyright (c) 2025 Mapbox All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -=========================================================================== - -Mapbox Common SDK uses portions of asio +Mapbox Core Maps version 10.0 ("Mapbox Core Maps SDK") or higher must +be used according to the Mapbox Terms of Service. This license allows +developers with a current active Mapbox account to use and modify the +Mapbox Core Maps. Developers may modify the Mapbox Core Maps SDK code +so long as the modifications do not change or interfere with marked +portions of the code related to billing, accounting, and anonymized +data collection. The Mapbox Core Maps SDK sends anonymized location +and usage data, which Mapbox uses for fixing bugs and errors, +accounting, and generating aggregated anonymized statistics. This +license terminates automatically if a user no longer has an active +Mapbox account. + +For the full license terms, please see the Mapbox Terms of Service at +https://www.mapbox.com/legal/tos/ -Boost Software License - Version 1.0 - August 17th, 2003 +``` -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: +--- -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. +### icu,75.1,Unicode-3.0,Unicode Inc,https://github.com/unicode-org/icu -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +``` +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2016-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 -=========================================================================== +``` -Mapbox Common SDK uses portions of cpp-httplib +--- -The MIT License (MIT) +### filesystem,1.5.10,MIT,Steffen Schümann,https://github.com/gulrak/filesystem/ -Copyright (c) 2017 yhirose +``` +Copyright (c) 2018, Steffen Schümann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -118,11 +132,101 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +--- -=========================================================================== +### kdbush.hpp,0.1.3,ISC,Vladimir Agafonkin,https://github.com/mourner/kdbush.hpp + +``` +Copyright (c) 2016, Vladimir Agafonkin + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +--- + +### cheap-ruler-cpp-internal,2.5.4,ISC,Mapbox,https://github.com/mapbox/cheap-ruler-cpp-internal + +``` +ISC License + +Copyright (c) 2017, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. -Mapbox Common SDK uses portions of fmt +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +``` + +--- + +### shelf-pack-cpp,2.1.1,ISC,Mapbox,https://github.com/mapbox/shelf-pack-cpp + +``` +ISC License + +Copyright (c) 2017, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +``` + +--- + +### supercluster-hpp-internal,0.5.0,ISC,Mapbox,https://github.com/mapbox/supercluster-hpp-internal + +``` +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +--- + +### fmt,7.1.3,MIT,Victor Zverovich,https://fmt.dev/latest/index.html + +``` Copyright (c) 2012 - present, Victor Zverovich Permission is hereby granted, free of charge, to any person obtaining @@ -151,200 +255,466 @@ of this Software are embedded into a machine-executable object form of such source code, you may redistribute such embedded portions in such object form without including the above copyright and permission notices. -=========================================================================== +``` -Mapbox Common SDK uses portions of googletest +--- -Copyright 2008, Google Inc. -All rights reserved. +### basis_universal,dev,Apache-2.0,Binomial LLC,https://github.com/BinomialLLC/basis_universal -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works within the Source form or + documentation, if provided along with the Derivative Works or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License") + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` -=========================================================================== +--- -Mapbox Common SDK uses portions of gzip-hpp +### Boost C++ Libraries,1.84.0,BSL-1.0,Boost authors,https://www.boost.org -Copyright (c) 2017, Mapbox Inc. -All rights reserved. +``` +Boost Software License - Version 1.0 - August 17th, 2003 -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: -- Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=========================================================================== +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. -Mapbox Common SDK uses portions of mapbox-bindgen +``` -Copyright (c) 2019 - Mapbox, Inc. +--- -You may use this code with your Mapbox account and under the -Mapbox Terms of Service (https://www.mapbox.com/tos/). +### csscolorparser,dev,MIT,Dean McNamee and Konstantin Käfer,https://github.com/mapbox/css-color-parser-cpp -All other rights reserved. +``` +(c) Dean McNamee , 2012. +(c) Konstantin Käfer , 2014. -=========================================================================== +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -Mapbox Common SDK uses portions of mapbox-license-checker +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -Copyright (c) 2020 - Mapbox, Inc. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. -You may use this code with your Mapbox account and under the -Mapbox Terms of Service (https://www.mapbox.com/tos/). +``` -All other rights reserved. +--- + +### earcut.hpp,2.2.4,ISC,Mapbox,https://github.com/mapbox/earcut.hpp ``` +ISC License + +Copyright (c) 2015, Mapbox -### mapbox-events-ios +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. ``` -mapbox-events-ios copyright (c) 2014-2019 Mapbox. +--- -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +### eternal,1.0.1,ISC,Mapbox,https://github.com/mapbox/eternal -- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +``` +ISC License -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2018, Mapbox +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. ----------- +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. -Mapbox Events uses portions of zlib. +``` -Acknowledgments: -The deflate format used by zlib was defined by Phil Katz. The deflate and zlib specifications were written by L. Peter Deutsch. Thanks to all the people who reported problems and suggested various improvements in zlib; they are too numerous to cite here. -Copyright notice: -(C) 1995-2013 Jean-loup Gailly and Mark Adler -This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +--- -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. +### geojson-vt-cpp,6.6.5,ISC,Mapbox,https://github.com/mapbox/geojson-vt-cpp -Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu +``` +ISC License +Copyright (c) 2015, Mapbox ----------- +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. -Mapbox Events uses portions of GZIP +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. -GZIP -Copyright (C) 2012 Charcoal Design -This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: +``` -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. +--- +### parsedate,dev,curl,Daniel Stenberg and others,https://curl.haxx.se ----------- +``` +COPYRIGHT AND PERMISSION NOTICE -Mapbox Events uses portions of Reachability. +Copyright (c) 1996 - 2020, Daniel Stenberg, , and many +contributors, see the THANKS file. -Reachability (BSD 2) -Copyright (c) 2011, Tony Million. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +``` -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. +--- +### polylabel,1.0.3,ISC,Mapbox,https://github.com/mapbox/polylabel ----------- +``` +ISC License +Copyright (c) 2016 Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA +OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. -Mapbox Events uses portions of TrustKit. +``` -Copyright 2015 The TrustKit Project Authors. Licensed under the MIT license. +--- -The MIT License (MIT) +### protozero,1.7.1,BSD-2-Clause,Mapbox,https://github.com/mapbox/protozero -Copyright (c) 2015 Data Theorem, Inc. +``` +protozero copyright (c) Mapbox. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR +PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +--- + +### unique_resource,dev,BSL-1.0,Shintarou Okada,https://github.com/okdshin/unique_resource + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. ``` -### turf-swift +--- + +### vector-tile-internal,1.0.4,ISC,Mapbox,https://github.com/mapbox/vector-tile-internal ``` -Copyright © 2014–2020, Mapbox +Copyright (c) 2016, Mapbox Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -358,35 +728,888 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ``` -### UnitBezier +--- + +### wagyu,0.5.0,BSL-1.0,Angus Johnson and Mapbox,https://github.com/mapbox/wagyu.git + +``` +Parts of the code in the Wagyu Library are derived from the version of the +Clipper Library by Angus Johnson listed below. + +Author : Angus Johnson +Version : 6.4.0 +Date : 2 July 2015 +Website : http://www.angusj.com + +Copyright for portions of the derived code in the Wagyu library are held +by Angus Johnson, 2010-2015. Copyright for the "include/mapbox/geometry/wagyu/almost_equal.hpp" +file is held by Google Inc and its license is listed at the top of that file. +All other copyright for the Wagyu Library are held by Mapbox, 2016. This code +is published in accordance with, and retains the same license as the Clipper +Library by Angus Johnson. + +Copyright (c) 2010-2015, Angus Johnson +Copyright (c) 2016-2020, Mapbox + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. ``` -MapboxMaps uses a port of UnitBezier.h -Copyright (C) 2008 Apple Inc. All Rights Reserved. +--- + +### vtzero,1.1.0,BSD-2-Clause,Mapbox,https://github.com/mapbox/vtzero + +``` +BSD 2-Clause License + +Copyright (c) 2017, Mapbox +All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +--- + +### microprofile,deda3d4,Unlicense,zeux,https://github.com/zeux/microprofile + +``` +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +``` + +--- + +### draco,1.5.3,Apache-2.0,Google,https://github.com/google/draco + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works within the Source form or + documentation, if provided along with the Derivative Works or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License") + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +-------------------------------------------------------------------------------- +Files: docs/assets/js/ASCIIMathML.js + +Copyright (c) 2014 Peter Jipsen and other ASCIIMathML.js contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +-------------------------------------------------------------------------------- +Files: docs/assets/css/pygments/* + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +``` + +--- + +### cgltf,1.13,MIT,Johannes Kuhlmann,https://github.com/jkuhlmann/cgltf + +``` +Copyright (c) 2018-2021 Johannes Kuhlmann + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +--- + +### meshoptimizer,0.20,MIT,zeux,https://github.com/zeux/meshoptimizer + +``` +MIT License + +Copyright (c) 2016-2023 Arseny Kapoulkine + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +--- + +### spatial-algorithms,0.1.0,Mapbox ToS,Mapbox,https://github.com/mapbox/spatial-algorithms + +``` +Mapbox spatial-algorithms version 0.1.0 + +Copyright © 2017 - 2024 Mapbox, Inc. All rights reserved. + +The software and files in this repository (collectively, "Software") are +licensed under the Mapbox TOS for use only with the relevant Mapbox product(s) +listed at www.mapbox.com/pricing. This license allows developers with a +current active Mapbox account to use and modify the authorized portions of the +Software as needed for use only with the relevant Mapbox product(s) through +their Mapbox account in accordance with the Mapbox TOS. This license +terminates automatically if a developer no longer has a Mapbox account in good +standing or breaches the Mapbox TOS. For the license terms, please see the +Mapbox TOS at https://www.mapbox.com/legal/tos/ which incorporates the Mapbox +Product Terms at www.mapbox.com/legal/service-terms. If this Software is a +SDK, modifications that change or interfere with marked portions of the code +related to billing, accounting, or data collection are not authorized and the +SDK sends limited de-identified location and usage data which is used in +accordance with the Mapbox TOS. [Updated 2023-01] +``` + +--- + +### vtcomposite,2.1.0,CC0,Mapbox,https://github.com/mapbox/vtcomposite + +``` +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work + + ii. moral rights retained by the original author(s) and/or performer(s) + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive) and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + +``` + +--- + +### mapbox-base-internal,dev,BSD-3-Clause,Mapbox,https://www.mapbox.com/ + +``` +Copyright (c) MapBox +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +- Neither the name "MapBox" nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES +LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` + +--- + +### expected-lite,0.4.0,BSL-1.0,Martin Moene,https://github.com/martinmoene/expected-lite + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +--- + +### RapidJSON,1.1.0,MIT,THL A29 Limited and Milo Yip,https://rapidjson.org + +``` +Tencent is pleased to support the open source community by making RapidJSON available. + +Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. + +If you have downloaded a copy of the RapidJSON binary from Tencent, please note that the RapidJSON binary is licensed under the MIT License. +If you have downloaded a copy of the RapidJSON source code from Tencent, please note that RapidJSON source code is licensed under the MIT License, except for the third-party components listed below which are subject to different license terms. Your integration of RapidJSON into your own projects may require compliance with the MIT License, as well as the other licenses applicable to the third-party components included within RapidJSON. To avoid the problematic JSON license in your own projects, it's sufficient to exclude the bin/jsonchecker/ directory, as it's the only code under the JSON license. +A copy of the MIT License is included in this file. + +Other dependencies and licenses: + +Open Source Software Licensed Under the BSD License: +-------------------------------------------------------------------- + +The msinttypes r29 +Copyright (c) 2006-2013 Alexander Chemeris +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Open Source Software Licensed Under the JSON License: +-------------------------------------------------------------------- + +json.org +Copyright (c) 2002 JSON.org +All Rights Reserved. + +JSON_checker +Copyright (c) 2002 JSON.org +All Rights Reserved. + + +Terms of the JSON License: +--------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Terms of the MIT License: +-------------------------------------------------------------------- + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +``` + +--- + +### geojson-cpp-internal,0.5.1,ISC,Mapbox,https://github.com/mapbox/geojson-cpp-internal + +``` +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +``` + +--- + +### geometry-hpp-internal,1.1.0,ISC,Mapbox,https://github.com/mapbox/geometry-hpp-internal + +``` +Copyright (c) 2016, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` + +--- + +### glm,1.0.0,MIT,g-truc,https://github.com/g-truc/glm + +``` +================================================================================ +The MIT License +-------------------------------------------------------------------------------- +Copyright (c) 2005 - G-Truc Creation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +--- + +### glad,2.0,MIT,glad,https://github.com/Dav1dde/glad + +``` +The glad source code: + The MIT License (MIT) + + Copyright (c) 2013-2022 David Herberth + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +The Khronos Specifications: + + Copyright (c) 2013-2020 The Khronos Group Inc. + + Licensed under the Apache License, Version 2.0 (the "License") + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +The EGL Specification and various headers: + + Copyright (c) 2007-2016 The Khronos Group Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and/or associated documentation files (the + "Materials"), to deal in the Materials without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Materials, and to + permit persons to whom the Materials are furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Materials. + + THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ``` + +--- + +### msgpack,6.1.0,boost,FURUHASHI Sadayuki,https://github.com/msgpack/msgpack-c.git + +``` +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +``` + +--- + +### mapbox-common,dev,Mapbox ToS,Mapbox,https://www.mapbox.com/ + +``` +Mapbox Common SDK + +Copyright (c) 2021 - Mapbox, Inc. + +You may use this code with your Mapbox account and under the +Mapbox Terms of Service (https://www.mapbox.com/tos/). + +All other rights reserved. + +=========================================================================== + + +``` + +--- diff --git a/Makefile b/Makefile deleted file mode 100644 index 2cbf783fb9e3..000000000000 --- a/Makefile +++ /dev/null @@ -1,295 +0,0 @@ -# ---------------------------------------------------------------------------------------------------------------------- -# Configurable varibles -ifneq ($(XCODE_WORKSPACE),) - XCODE_PROJECT := -workspace $(XCODE_WORKSPACE) -else - XCODE_PROJECT ?= -project MapboxMaps.xcodeproj -endif - -XCODE_PROJECT_FILE := MapboxMaps.xcodeproj/project.pbxproj - -# Default to Debug since Release will require testability. (See #157) -CONFIGURATION ?= Debug -BUILD_DIR ?= $(CURDIR)/build -JOBS ?= $(shell sysctl -n hw.ncpu) -APP_NAME ?= $(SCHEME) - -# Circle -CIRCLE_CI_CLI ?= /tmp/circleci - -# Derived variables -BUILT_DEVICE_PRODUCTS_DIR := $(BUILD_DIR)/Build/Products/$(CONFIGURATION)-iphoneos -TEST_ROOT := $(BUILD_DIR)/test-root -PAYLOAD_DIR := $(BUILD_DIR)/Payload - -# Netrc -NETRC_FILE=~/.netrc - -# See https://stackoverflow.com/a/7377522 -define NETRC -machine api.mapbox.com -login mapbox -password $(SDK_REGISTRY_TOKEN) -endef -export NETRC - - -# Disabled optional code signing for the time-being. Currently with the setup below, -# when build-for-testing unit tests on device **locally** (i.e. you need code-signing), -# the build can fail with -# -# error: Cycle inside MapboxMapsTests; building could produce unreliable results. -# -# This can be fixed by adding MapboxMapTestHost as a dependency to the Test targets, -# but this isn't the appropriate a solution here (these originate as simulator unit -# tests). -# -# The probable solution is to keep code signing disabled (during build), but then -# re-sign prior to running with the xctestrun file. The app, xctest, dylibs and -# frameworks need to be recursively signed. Something along the lines of: -# -# find build -name \*.framework -print0 | xargs -0 -I{} codesign --force --sign --preserve-metadata=identifier,entitlements,flags --timestamp=none {} - -ENABLE_CODE_SIGNING ?= 0 - -# Disable code signing by default -ifeq (1,$(ENABLE_CODE_SIGNING)) -CODE_SIGNING := -else -CODE_SIGNING := CODE_SIGN_IDENTITY="" \ - CODE_SIGNING_REQUIRED=NO \ - CODE_SIGNING_ALLOWED=NO -endif - -# ---------------------------------------------------------------------------------------------------------------------- -# House keeping - -.PHONY: clean -clean: - -rm -rf $(BUILD_DIR) - - -.PHONY: distclean -distclean: clean - -rm Package.resolved - -rm Apps/Apps.xcworkspace/xcshareddata/swiftpm/Package.resolved - -rm MapboxMaps.xcodeproj - -$(PAYLOAD_DIR) $(TEST_ROOT): - -mkdir -p $@ - -# ---------------------------------------------------------------------------------------------------------------------- -# Simulators - SDK - -XCODE_BUILD_SIM_SDK = set -o pipefail && xcodebuild \ - -scheme MapboxMaps \ - -sdk iphonesimulator \ - -configuration $(CONFIGURATION) \ - -jobs $(JOBS) - -.PHONY: build-sdk-for-simulator -build-sdk-for-simulator: - $(XCODE_BUILD_SIM_SDK) \ - -destination 'platform=iOS Simulator,OS=latest,name=iPhone 11' \ - build \ - ONLY_ACTIVE_ARCH=NO - -.PHONY: build-sdk-for-testing-simulator -build-sdk-for-testing-simulator: - $(XCODE_BUILD_SIM_SDK) \ - -destination 'platform=iOS Simulator,OS=latest,name=iPhone 11' \ - -enableCodeCoverage YES \ - build-for-testing \ - ENABLE_TESTABILITY=YES \ - ONLY_ACTIVE_ARCH=YES - -.PHONY: test-sdk-without-building-simulator -test-sdk-without-building-simulator: - $(XCODE_BUILD_SIM_SDK) \ - -destination 'platform=iOS Simulator,OS=latest,name=iPhone 11' \ - -enableCodeCoverage YES \ - test-without-building \ - -resultBundlePath MapboxMapsTests.xcresult \ - ONLY_ACTIVE_ARCH=YES - -# ---------------------------------------------------------------------------------------------------------------------- -# Simulators - Apps - -.PHONY: build-app-for-simulator -build-app-for-simulator: - set -o pipefail && xcodebuild \ - -workspace Apps/Apps.xcworkspace \ - -scheme '$(SCHEME)' \ - -sdk iphonesimulator \ - -destination 'platform=iOS Simulator,OS=latest,name=iPhone 11' \ - -configuration $(CONFIGURATION) \ - -jobs $(JOBS) \ - build \ - ONLY_ACTIVE_ARCH=NO - -# ---------------------------------------------------------------------------------------------------------------------- -# Devices - SDK - -# Xcode build command for building for device. The CODE_SIGNING_* variables are so that no code signing occurs on CI - -# this is because AWS Device Farm re-signs the applications. This may need to change if a different provider is used. -XCODE_BUILD_DEVICE = xcodebuild \ - $(XCODE_PROJECT) \ - -sdk iphoneos \ - -configuration $(CONFIGURATION) \ - -derivedDataPath $(BUILD_DIR) \ - -jobs $(JOBS) \ - $(CODE_SIGNING) - -.PHONY: build-sdk-for-device -build-sdk-for-device: - set -o pipefail && xcodebuild \ - -scheme MapboxMaps \ - -sdk iphoneos \ - -destination 'generic/platform=iOS' \ - -configuration $(CONFIGURATION) \ - -jobs $(JOBS) \ - build \ - ONLY_ACTIVE_ARCH=NO \ - $(CODE_SIGNING) - -$(XCODE_PROJECT_FILE): project.yml - xcodegen - -# ---------------------------------------------------------------------------------------------------------------------- -# Devices - Apps - -XCODE_BUILD_DEVICE_APPS = xcodebuild \ - ONLY_ACTIVE_ARCH=NO \ - -workspace Apps/Apps.xcworkspace \ - -sdk iphoneos \ - -configuration $(CONFIGURATION) \ - -jobs $(JOBS) \ - $(CODE_SIGNING) - -.PHONY: build-app-for-device -build-app-for-device: - set -o pipefail && $(XCODE_BUILD_DEVICE_APPS) -scheme '$(SCHEME)' build - -# ---------------------------------------------------------------------------------------------------------------------- -# Symbolication - -.PHONY: symbolicate -symbolicate: - @echo Symbolicating crash reports - - @export DEVELOPER_DIR=$$(xcode-select -p); \ - CRASHES=`find $(BUILD_DIR) -name *.ips` ; \ - echo "crashes: $${CRASHES}"; \ - for CRASH in $${CRASHES[@]} ; \ - do \ - if [ ! -f $${CRASH}.symbolicated.txt ]; then \ - echo "Symbolicating $${CRASH}" ; \ - $${DEVELOPER_DIR}/Platforms/MacOSX.platform/Developer/iOSSupport/Library/PrivateFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash \ - $${CRASH} \ - $(BUILT_DEVICE_PRODUCTS_DIR)/$(APP_NAME).app/ \ - $(BUILT_DEVICE_PRODUCTS_DIR)/$(APP_NAME).app/Frameworks/ \ - $(BUILT_DEVICE_PRODUCTS_DIR)/$(APP_NAME).app/Plugins/ \ - -o $${CRASH}.symbolicated.txt ; \ - cat $${CRASH}.symbolicated.txt ; \ - fi ; \ - done - - -# Codecov.io appears to struggle with the raw coverage data from Xcode (in this Device Farm testing scenario). -# Explicitly converting it to an lcov format helps. -# -# However, the following conversion of the profdata has failed once. If this continues to be an -# issue, it will be worth trying the following first: -# -# xcrun llvm-profdata merge -o dest.profdata source.profraw -# - -# Root directory in which to search for "profdata" coverage files, from which we generate -# the lcov data (both lcov and json formats) -COVERAGE_ROOT_DIR ?= $(BUILD_DIR)/Build/ProfileData -COVERAGE_MAPBOX_MAPS ?= $(BUILD_DIR)/Build/Products/$(CONFIGURATION)-iphonesimulator/MapboxMaps.o -COVERAGE_ARCH ?= x86_64 - -# .PHONY: update-codecov-with-profdata -# update-codecov-with-profdata: -# curl -sSfL --retry 5 --connect-timeout 5 https://codecov.io/bash > /tmp/codecov.sh -# @PROF_DATA=`find $(COVERAGE_ROOT_DIR) -regex '.*\.profraw'` ; \ -# for RESULT in $${PROF_DATA[@]} ; \ -# do \ -# echo "Generating $${RESULT}.lcov" ; \ -# xcrun llvm-profdata merge -o $${RESULT}.profdata $${RESULT} ; \ -# xcrun llvm-cov export \ -# $(COVERAGE_MAPBOX_MAPS) \ -# -instr-profile=$${RESULT}.profdata \ -# -arch=$(COVERAGE_ARCH) \ -# -format=lcov > $${RESULT}.lcov ; \ -# echo "Uploading $${RESULT}.lcov to CodeCov.io" ; \ -# bash /tmp/codecov.sh \ -# -f $${RESULT}.lcov \ -# -t $(CODECOV_TOKEN) \ -# -J '^MapboxMaps$$' \ -# -n $${RESULT}.lcov \ -# -F "$$(echo '$(SCHEME)' | sed 's/[[:upper:]]/_&/g;s/^_//' | tr '[:upper:]' '[:lower:]')" ; \ -# echo "Generating lcov JSON" ; \ -# xcrun llvm-cov export \ -# $(COVERAGE_MAPBOX_MAPS) \ -# -instr-profile=$${RESULT}.profdata \ -# -arch=$(COVERAGE_ARCH) \ -# -format=text | python3 -m json.tool > $${RESULT}.json ; \ -# echo "Uploading to S3" ; \ -# python3 ./scripts/code-coverage/parse-code-coverage.py \ -# -g . \ -# -c MapboxMaps \ -# --scheme $(SCHEME) \ -# --report $${RESULT}.json ; \ -# done -# @echo "Done" - -.PHONY: update-codecov-with-profdata -update-codecov-with-profdata: - @PROF_DATA=`find $(COVERAGE_ROOT_DIR) -regex '.*\.profdata'` ; \ - for RESULT in $${PROF_DATA[@]} ; \ - do \ - echo "Generating lcov JSON" ; \ - xcrun llvm-cov export \ - $(COVERAGE_MAPBOX_MAPS) \ - -instr-profile=$${RESULT} \ - -arch=$(COVERAGE_ARCH) \ - -format=text | python3 -m json.tool > $${RESULT}.json ; \ - echo "Uploading to S3" ; \ - python3 ./scripts/code-coverage/parse-code-coverage.py \ - -g . \ - -c MapboxMaps \ - --scheme $(SCHEME) \ - --report $${RESULT}.json ; \ - done - @echo "Done" - -.PHONY: device-update-codecov-with-profdata -device-update-codecov-with-profdata: - make update-codecov-with-profdata \ - COVERAGE_ARCH=arm64 \ - COVERAGE_ROOT_DIR=$(BUILD_DIR)/ \ - COVERAGE_MAPBOX_MAPS='$(BUILT_DEVICE_PRODUCTS_DIR)/MapboxMaps.o' - -# ---------------------------------------------------------------------------------------------------------------------- -# Dependencies - -$(NETRC_FILE): -ifndef SDK_REGISTRY_TOKEN - @echo SDK_REGISTRY_TOKEN not set. - exit 1 -endif - @echo "$$NETRC" > $(NETRC_FILE) - -# ---------------------------------------------------------------------------------------------------------------------- -# Validation - -# See https://circleci.com/blog/circleci-hacks-validate-circleci-config-on-every-commit-with-a-git-hook/ for installing -# a pre commit hook to validate the CircleCI config. Call `make validate` from the pre-commit script. -.PHONY: validate -validate: $(CIRCLE_CI_CLI) - $(CIRCLE_CI_CLI) config validate -c .circleci/config.yml - -$(CIRCLE_CI_CLI): - curl -fLSs https://circle.ci/cli | DESTDIR=/tmp bash diff --git a/MapboxMaps.podspec b/MapboxMaps.podspec index 20894d9fed0d..8185424daa04 100644 --- a/MapboxMaps.podspec +++ b/MapboxMaps.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |m| - maps_version = '10.7.0-rc.1' + maps_version = '11.10.0-rc.1' m.name = 'MapboxMaps' m.version = maps_version @@ -15,15 +15,14 @@ Pod::Spec.new do |m| m.source = { :git => 'https://github.com/mapbox/mapbox-maps-ios.git', :tag => "v#{maps_version}" } m.platform = :ios - m.ios.deployment_target = '11.0' - m.swift_version = '5.3' + m.ios.deployment_target = '14.0' + m.swift_version = '5.9' m.source_files = 'Sources/MapboxMaps/**/*.{swift,h}' - m.resources = ['Sources/**/*.{xcassets,strings}', 'Sources/MapboxMaps/MapboxMaps.json'] + m.resource_bundles = { 'MapboxMapsResources' => ['Sources/**/*.{xcassets,strings}', 'Sources/MapboxMaps/MapboxMaps.json', 'Sources/MapboxMaps/PrivacyInfo.xcprivacy'] } - m.dependency 'MapboxCoreMaps', '10.7.0-rc.1' - m.dependency 'MapboxCommon', '22.1.0-rc.1' - m.dependency 'MapboxMobileEvents', '1.0.8' - m.dependency 'Turf', '~> 2.0' + m.dependency 'MapboxCoreMaps', '11.10.0-rc.1' + m.dependency 'MapboxCommon', '24.10.0-rc.1' + m.dependency 'Turf', '4.0.0' end diff --git a/MapboxTestHost/AppDelegate.swift b/MapboxTestHost/AppDelegate.swift deleted file mode 100644 index fbcea21f4872..000000000000 --- a/MapboxTestHost/AppDelegate.swift +++ /dev/null @@ -1,6 +0,0 @@ -import UIKit - -@UIApplicationMain -public class AppDelegate: UIResponder, UIApplicationDelegate { - public var window: UIWindow? -} diff --git a/MapboxTestHost/Assets.xcassets/AppIcon.appiconset/Contents.json b/MapboxTestHost/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 982f07596f51..000000000000 --- a/MapboxTestHost/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "images" : [ - { - "scale" : "1x", - "filename" : "icon-40.png", - "idiom" : "ipad", - "size" : "40x40" - }, - { - "size" : "40x40", - "scale" : "2x", - "filename" : "icon-40@2x.png", - "idiom" : "ipad" - }, - { - "scale" : "2x", - "idiom" : "iphone", - "size" : "60x60", - "filename" : "icon-60@2x.png" - }, - { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "1x", - "filename" : "icon-72.png" - }, - { - "filename" : "icon-72@2x.png", - "idiom" : "ipad", - "size" : "72x72", - "scale" : "2x" - }, - { - "scale" : "1x", - "size" : "76x76", - "idiom" : "ipad", - "filename" : "icon-76.png" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "filename" : "icon-76@2x.png", - "size" : "76x76" - }, - { - "scale" : "1x", - "filename" : "icon-small-50.png", - "idiom" : "ipad", - "size" : "50x50" - }, - { - "size" : "50x50", - "filename" : "icon-small-50@2x.png", - "idiom" : "ipad", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "1x", - "filename" : "icon-small.png" - }, - { - "size" : "29x29", - "scale" : "2x", - "idiom" : "iphone", - "filename" : "icon-small@2x.png" - }, - { - "scale" : "1x", - "filename" : "icon.png", - "size" : "57x57", - "idiom" : "iphone" - }, - { - "idiom" : "iphone", - "filename" : "icon@2x.png", - "size" : "57x57", - "scale" : "2x" - }, - { - "filename" : "icon-small@3x.png", - "scale" : "3x", - "idiom" : "iphone", - "size" : "29x29" - }, - { - "filename" : "icon-40@3x.png", - "size" : "40x40", - "idiom" : "iphone", - "scale" : "3x" - }, - { - "filename" : "icon-60@3x.png", - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - }, - { - "filename" : "icon-40@2x.png", - "scale" : "2x", - "size" : "40x40", - "idiom" : "iphone" - }, - { - "scale" : "1x", - "idiom" : "ipad", - "size" : "29x29", - "filename" : "icon-small.png" - }, - { - "filename" : "icon-small@2x.png", - "scale" : "2x", - "size" : "29x29", - "idiom" : "ipad" - }, - { - "scale" : "2x", - "idiom" : "ipad", - "size" : "83.5x83.5", - "filename" : "icon-83.5@2x.png" - }, - { - "filename" : "notification-icon@2x.png", - "scale" : "2x", - "size" : "20x20", - "idiom" : "iphone" - }, - { - "idiom" : "iphone", - "size" : "20x20", - "scale" : "3x", - "filename" : "notification-icon@3x.png" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "scale" : "1x", - "filename" : "notification-icon~ipad.png" - }, - { - "scale" : "2x", - "filename" : "notification-icon~ipad@2x.png", - "idiom" : "ipad", - "size" : "20x20" - }, - { - "filename" : "ios-marketing.png", - "scale" : "1x", - "idiom" : "ios-marketing", - "size" : "1024x1024" - } - ] -} \ No newline at end of file diff --git a/MapboxTestHost/Assets.xcassets/Contents.json b/MapboxTestHost/Assets.xcassets/Contents.json deleted file mode 100644 index 4aa7c5350bfc..000000000000 --- a/MapboxTestHost/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} \ No newline at end of file diff --git a/MapboxTestHost/Info.plist b/MapboxTestHost/Info.plist deleted file mode 100644 index 96abb26d58ef..000000000000 --- a/MapboxTestHost/Info.plist +++ /dev/null @@ -1,47 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 10.0.0 - CFBundleVersion - 17 - CFBundleDisplayName - Test Host - LSRequiresIPhoneOS - - UILaunchStoryboardName - Main - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/PCVD/DefaultAnnotationIcon.pcvd b/PCVD/DefaultAnnotationIcon.pcvd deleted file mode 100644 index 329e6ce61905..000000000000 Binary files a/PCVD/DefaultAnnotationIcon.pcvd and /dev/null differ diff --git a/PCVD/compass.pcvd b/PCVD/compass.pcvd deleted file mode 100644 index f4286591de62..000000000000 Binary files a/PCVD/compass.pcvd and /dev/null differ diff --git a/Package.resolved b/Package.resolved index b03b4ccfe3c6..f370fd850220 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,52 +1,32 @@ { - "object": { - "pins": [ - { - "package": "CocoaImageHashing", - "repositoryURL": "https://github.com/ameingast/cocoaimagehashing", - "state": { - "branch": null, - "revision": "ad01eee3c3f91bd181f7f4eba7f48f7cb211b51c", - "version": "1.9.0" - } - }, - { - "package": "MapboxCommon", - "repositoryURL": "https://github.com/mapbox/mapbox-common-ios.git", - "state": { - "branch": null, - "revision": "25794d3d3a0dab1763b7973ca217feb5e073806c", - "version": "22.1.0-rc.1" - } - }, - { - "package": "MapboxCoreMaps", - "repositoryURL": "https://github.com/mapbox/mapbox-core-maps-ios.git", - "state": { - "branch": null, - "revision": "6640d6d46a6ceee6b50c451f38975d66ff5ba291", - "version": "10.7.0-rc.1" - } - }, - { - "package": "MapboxMobileEvents", - "repositoryURL": "https://github.com/mapbox/mapbox-events-ios.git", - "state": { - "branch": null, - "revision": "5e130fba637f5bbb0bb8bca0dec8d38a06ce07bf", - "version": "1.0.8" - } - }, - { - "package": "Turf", - "repositoryURL": "https://github.com/mapbox/turf-swift.git", - "state": { - "branch": null, - "revision": "569e0f0e96fda86e1a1dcefaefa68cfa9491ffc1", - "version": "2.4.0" - } + "pins" : [ + { + "identity" : "mapbox-common-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mapbox/mapbox-common-ios.git", + "state" : { + "revision" : "eea3a287b1f6212a624b05286799cd22c554849f", + "version" : "24.10.0-rc.1" } - ] - }, - "version": 1 + }, + { + "identity" : "mapbox-core-maps-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mapbox/mapbox-core-maps-ios.git", + "state" : { + "revision" : "1938905a7bc2721069df573d5010d8461fbe5ec7", + "version" : "11.10.0-rc.1" + } + }, + { + "identity" : "turf-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mapbox/turf-swift.git", + "state" : { + "revision" : "bf840e6b9529d105687840fe2c9dcd74197d46d1", + "version" : "4.0.0" + } + } + ], + "version" : 2 } diff --git a/Package.swift b/Package.swift index 7df42c748598..366769b860f7 100644 --- a/Package.swift +++ b/Package.swift @@ -1,41 +1,49 @@ -// swift-tools-version:5.4 +// swift-tools-version:5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription +import Foundation + +let coreMaps = MapsDependency.coreMaps(version: "11.10.0-rc.1") + +let common = MapsDependency.common(version: "24.10.0-rc.1") + +let mapboxMapsPath: String? = nil let package = Package( name: "MapboxMaps", defaultLocalization: "en", - platforms: [.iOS(.v11)], + // Maps SDK doesn't support macOS but declared the minimum macOS requirement with downstream deps to enable `swift run` cli tools + platforms: [.iOS(.v14), .macOS(.v10_15), .custom("visionos", versionString: "1.0")], products: [ .library( name: "MapboxMaps", targets: ["MapboxMaps"]), ], dependencies: [ - .package(name: "MapboxCoreMaps", url: "https://github.com/mapbox/mapbox-core-maps-ios.git", .exact("10.7.0-rc.1")), - .package(name: "MapboxCommon", url: "https://github.com/mapbox/mapbox-common-ios.git", .exact("22.1.0-rc.1")), - .package(name: "MapboxMobileEvents", url: "https://github.com/mapbox/mapbox-events-ios.git", .exact("1.0.8")), - .package(name: "Turf", url: "https://github.com/mapbox/turf-swift.git", from: "2.0.0"), - .package(name: "CocoaImageHashing", url: "https://github.com/ameingast/cocoaimagehashing", .exact("1.9.0")) - ], + .package(url: "https://github.com/mapbox/turf-swift.git", exact: "4.0.0"), + ] + coreMaps.packageDependencies + common.packageDependencies, targets: [ .target( name: "MapboxMaps", - dependencies: ["MapboxCoreMaps", "Turf", "MapboxMobileEvents", "MapboxCommon"], + dependencies: [ + coreMaps.mapsTargetDependencies, + common.mapsTargetDependencies, + .product(name: "Turf", package: "turf-swift") + ], + path: mapboxMapsPath, exclude: [ - "Info.plist" + "Info.plist", ], resources: [ - .copy("MapboxMaps.json") + .copy("MapboxMaps.json"), + .copy("PrivacyInfo.xcprivacy"), ] ), .testTarget( name: "MapboxMapsTests", - dependencies: ["MapboxMaps", "CocoaImageHashing"], - exclude: [ - "Info.plist", - "Integration Tests/HTTP/HTTPIntegrationTests.swift", + dependencies: [ + "MapboxMaps", ], resources: [ .copy("MigrationGuide/Fixtures/polygon.geojson"), @@ -56,5 +64,73 @@ let package = Package( .process("Resources/MapInitOptionsTests.xib"), ] ) - ] + ] + coreMaps.packageTargets + common.packageTargets ) + +struct MapsDependency { + init(name: String, version: String, checksum: String? = nil, isSnapshot: Bool?, repositoryName: String, registryProjectName: String, registryFileName: String) { + self.name = name + self.version = version + self.checksum = checksum + self.isSnapshot = isSnapshot ?? version.contains("SNAPSHOT") + + self.repositoryName = repositoryName + self.registryProjectName = registryProjectName + self.registryFileName = registryFileName + } + + let name: String + let version: String + let checksum: String? + let isSnapshot: Bool + + let repositoryName: String + let registryProjectName: String + let registryFileName: String + + static func coreMaps(version: String, checksum: String? = nil, isSnapshot: Bool? = nil) -> MapsDependency { + return MapsDependency(name: "MapboxCoreMaps", version: version, checksum: checksum, isSnapshot: isSnapshot, + repositoryName: "mapbox-core-maps-ios", + registryProjectName: "mobile-maps-core", + registryFileName: "MapboxCoreMaps.xcframework-dynamic.zip") + } + + static func common(version: String, checksum: String? = nil, isSnapshot: Bool? = nil) -> MapsDependency { + return MapsDependency(name: "MapboxCommon", version: version, checksum: checksum, isSnapshot: isSnapshot, + repositoryName: "mapbox-common-ios", + registryProjectName: "mapbox-common", + registryFileName: "MapboxCommon.zip") + } + + var packageDependencies: [Package.Dependency] { + guard !isSnapshot else { return [] } + + return [ + .package(url: repositoryURL, exact: Version(stringLiteral: version)) + ] + } + + var packageTargets: [Target] { + guard isSnapshot else { return [] } + + return [ + .binaryTarget(name: name, url: registryURL, checksum: checksum ?? "") + ] + } + + var mapsTargetDependencies: Target.Dependency { + if isSnapshot { + return .byName(name: name) + } else { + return .product(name: name, package: repositoryName) + } + } + + var repositoryURL: String { return "https://github.com/mapbox/\(repositoryName).git" } + + var registryReleaseFolder: String { isSnapshot ? "snapshots" : "releases" } + + var registryURL: String { + return "https://api.mapbox.com/downloads/v2/\(registryProjectName)/\(registryReleaseFolder)/ios/packages/\(version)/\(registryFileName)" + } +} diff --git a/README.md b/README.md index f5791bca558c..6d31873591fd 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ The Mapbox Maps SDK for iOS is a public library for displaying interactive, thor This README is intended for developers who are interested in contributing to the Mapbox Maps SDK for iOS. Please visit https://docs.mapbox.com/ios/maps/guides/ for general information and instructions on using the Maps SDK in your iOS application. ## Examples App -The Examples app shows how to use many of the features in the Maps SDK. See the [Examples README](https://github.com/mapbox/mapbox-maps-ios/tree/main/Apps/Examples/README.md) for more information. +The Examples app shows how to use many of the features in the Maps SDK. See the [Examples project](https://github.com/mapbox/mapbox-maps-ios/tree/main/Examples.xcodeproj) for more information. ## Developing -If you are interested in contributing to the Maps SDK, please see the [DEVELOPING.md](https://github.com/mapbox/mapbox-maps-ios/tree/main/DEVELOPING.md) file for information on setting up, running and building the SDK. +If you are interested in contributing to the Maps SDK, please see the [DEVELOPING.md](https://github.com/mapbox/mapbox-maps-ios/tree/main/DEVELOPING.md) file for information on setting up, running and building the SDK. ## Reporting issues / Need help? @@ -22,7 +22,7 @@ Please use our [bug template](https://github.com/mapbox/mapbox-maps-ios/issues/n Please use our [feature template](https://github.com/mapbox/mapbox-maps-ios/issues/new?labels=feature%20%3Agreen_apple%3A&template=feature.md) to request new features or enhancements. #### Other questions -Can't find the answer you're looking for? [Contact support](https://www.mapbox.com/contact) +If you have any questions about how to use the SDK or if you need help with specific features, you can reach out to our support team [via our website](https://docs.mapbox.com/help/) or connect with our developer community by joining our [Discord channel](https://discord.gg/UshjQYyDFw). We kindly ask you to avoid using the issue tracker in this repository for questions and instead use our support channels. ## Historical Note -This repository is a continuation of the Mapbox Maps SDK for iOS available at [mapbox/mapbox-gl-native-ios](https://github.com/mapbox/mapbox-gl-native-ios). +This repository is a continuation of the Mapbox Maps SDK for iOS available at [mapbox/mapbox-gl-native-ios](https://github.com/mapbox/mapbox-gl-native-ios). diff --git a/STYLE_README.md b/STYLE_README.md index d2962b891854..8bc632b6ba35 100644 --- a/STYLE_README.md +++ b/STYLE_README.md @@ -1,6 +1,6 @@ # Style -In v10 SDK, the style API is directly aligned with the Mapbox Style Specification. `Sources`, `Layers`, `Light` all work in the exact same manner as in the Style Specification. +In v10 SDK, the style API is directly aligned with the Mapbox Style Specification. `Sources`, `Layers`, `Light` all work in the exact same manner as in the Style Specification. The `Style` object in the `MapViewController` is responsible for all run time styling related functionality. @@ -8,14 +8,14 @@ The `Style` object in the `MapViewController` is responsible for all run time st Every `Source` and `Layer` declared in the Style Specification exists in v10 as a simple Swift struct. Furthermore, every property within those sources and layers is modeled using familiar swift types. This means that creating new sources and layers are easy! ##### GeoJSON Source Example -For example, a simple GeoJSON source can be created by the following code. We can set properties on this source (with the help of the full power of Xcode's autocomplete) as we would expect too! +For example, a simple GeoJSON source can be created by the following code. We can set properties on this source (with the help of the full power of Xcode's autocomplete) as we would expect too! ```swift var myGeoJSONSource = GeoJSONSource() myGeoJSONSource.maxZoom = 14.0 ``` -GeoJSON sources have a `data` property that can be set to either a `url` or inline geojson. The swift struct representing a GeoJSON source is modeled 1:1 with this expectation. +GeoJSON sources have a `data` property that can be set to either a `url` or inline geojson. The swift struct representing a GeoJSON source is modeled 1:1 with this expectation. The SDK uses [Turf-swift](https://github.com/mapbox/turf-swift) to model geojson. So crafting geojson at runtime becomes straightforward and is backed by the type-safety that Turf provides. @@ -50,9 +50,9 @@ self.mapViewController.addLayer(myBackgroundLayer) ### What about Expressions? -In the Background Layer example above, we set a constant value to the `backgroundColor` porperty of the layer. However, the `backgroundColor` property (and nearly all layout and paint properties) support expressions too! +In the Background Layer example above, we set a constant value to the `backgroundColor` porperty of the layer. However, the `backgroundColor` property (and nearly all layout and paint properties) support expressions too! -Fortunately, the SDK is flexible enough to handle both expressions and constants. Moreover, expressions have been redesigned from the ground up in v10. Instead of using `NSExpression` to represent expressions, the new Expression DSL directly models expressions based on the Mapbox Style Specification. +Fortunately, the SDK is flexible enough to handle both expressions and constants. Moreover, expressions have been redesigned from the ground up in v10. Instead of using `NSExpression` to represent expressions, the new Expression DSL directly models expressions based on the Mapbox Style Specification. In v10, expressions now exist as simple, familiar and type-safe Swift structs. They are also backed by Swift function-builders to make the experience of writing an expression similar to the way SwiftUI works. @@ -119,4 +119,4 @@ Exp(.interpolate) { ``` #### Other Expression Examples -- This (example)[Apps/Examples/Examples/All%20Examples/DataDrivenSymbolsExample.swift#L74] highlights the use of a match, and a switchcase expression +- This (example)[Sources/Examples/All%20Examples/DataDrivenSymbolsExample.swift#L74] highlights the use of a match, and a switchcase expression diff --git a/Sources/.swiftlint.yml b/Sources/.swiftlint.yml index 9921238891d4..ea45afe752a7 100644 --- a/Sources/.swiftlint.yml +++ b/Sources/.swiftlint.yml @@ -1,3 +1,5 @@ --- opt_in_rules: - - explicit_top_level_acl + - missing_docs +disabled_rules: + - file_length diff --git a/Sources/Examples/.swiftlint.yml b/Sources/Examples/.swiftlint.yml new file mode 100644 index 000000000000..f9e489f64e9c --- /dev/null +++ b/Sources/Examples/.swiftlint.yml @@ -0,0 +1,6 @@ +--- +disabled_rules: + - force_cast + - file_length + - type_body_length + - function_body_length diff --git a/Apps/Examples/Examples/All Examples/AdvancedViewportGesturesExample.swift b/Sources/Examples/All Examples/AdvancedViewportGesturesExample.swift similarity index 97% rename from Apps/Examples/Examples/All Examples/AdvancedViewportGesturesExample.swift rename to Sources/Examples/All Examples/AdvancedViewportGesturesExample.swift index 579b08bdaa88..34b7ea4e1983 100644 --- a/Apps/Examples/Examples/All Examples/AdvancedViewportGesturesExample.swift +++ b/Sources/Examples/All Examples/AdvancedViewportGesturesExample.swift @@ -7,7 +7,6 @@ import MapboxMaps // // When trying this example in the simulator, choose Features > Location > Freeway Drive // to get a good sense of the resulting user experience. -@objc(AdvancedViewportGesturesExample) final class AdvancedViewportGesturesExample: UIViewController, ExampleProtocol { private enum State { @@ -36,7 +35,8 @@ final class AdvancedViewportGesturesExample: UIViewController, ExampleProtocol { mapView.mapboxMap.setCamera(to: CameraOptions(center: cupertino, zoom: 14)) mapView.location.options.puckType = .puck2D(.makeDefault(showBearing: true)) - mapView.location.options.puckBearingSource = .course + mapView.location.options.puckBearing = .course + mapView.location.options.puckBearingEnabled = true followPuckViewportState = mapView.viewport.makeFollowPuckViewportState( options: FollowPuckViewportStateOptions( diff --git a/Apps/Examples/Examples/All Examples/AnimateGeoJSONLineExample.swift b/Sources/Examples/All Examples/AnimateGeoJSONLineExample.swift similarity index 89% rename from Apps/Examples/Examples/All Examples/AnimateGeoJSONLineExample.swift rename to Sources/Examples/All Examples/AnimateGeoJSONLineExample.swift index 8f27647a8be3..da1c4fc6c5c5 100644 --- a/Apps/Examples/Examples/All Examples/AnimateGeoJSONLineExample.swift +++ b/Sources/Examples/All Examples/AnimateGeoJSONLineExample.swift @@ -1,17 +1,14 @@ import UIKit import MapboxMaps -@objc(AnimateGeoJSONLine) -public class AnimateGeoJSONLineExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - internal let sourceIdentifier = "route-source-identifier" - internal var routeLineSource: GeoJSONSource! - var currentIndex = 0 - - public var geoJSONLine = (identifier: "routeLine", source: GeoJSONSource()) - - override public func viewDidLoad() { +final class AnimateGeoJSONLineExample: UIViewController, ExampleProtocol { + private let sourceIdentifier = "route-source-identifier" + private var mapView: MapView! + private var routeLineSource: GeoJSONSource! + private var currentIndex = 0 + private var cancelables = Set() + + override func viewDidLoad() { super.viewDidLoad() let centerCoordinate = CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736) @@ -23,25 +20,24 @@ public class AnimateGeoJSONLineExample: UIViewController, ExampleProtocol { view.addSubview(mapView) // Wait for the map to load its style before adding data. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in + mapView.mapboxMap.onMapLoaded.observeNext { _ in self.addLine() self.animatePolyline() // The below line is used for internal testing purposes only. self.finish() - } + }.store(in: &cancelables) } func addLine() { // Create a GeoJSON data source. - routeLineSource = GeoJSONSource() + routeLineSource = GeoJSONSource(id: sourceIdentifier) routeLineSource.data = .feature(Feature(geometry: LineString([allCoordinates[currentIndex]]))) // Create a line layer - var lineLayer = LineLayer(id: "line-layer") - lineLayer.source = sourceIdentifier + var lineLayer = LineLayer(id: "line-layer", source: sourceIdentifier) lineLayer.lineColor = .constant(StyleColor(.red)) let lowZoomWidth = 5 @@ -62,15 +58,19 @@ public class AnimateGeoJSONLineExample: UIViewController, ExampleProtocol { lineLayer.lineJoin = .constant(.round) // Add the lineLayer to the map. - try! mapView.mapboxMap.style.addSource(routeLineSource, id: sourceIdentifier) - try! mapView.mapboxMap.style.addLayer(lineLayer) + try! mapView.mapboxMap.addSource(routeLineSource) + try! mapView.mapboxMap.addLayer(lineLayer) } func animatePolyline() { var currentCoordinates = [CLLocationCoordinate2D]() // Start a timer that will add a new coordinate to the line and redraw it every time it repeats. - Timer.scheduledTimer(withTimeInterval: 0.10, repeats: true) { ( timer ) in + Timer.scheduledTimer(withTimeInterval: 0.10, repeats: true) { [weak self] timer in + guard let self = self else { + timer.invalidate() + return + } if self.currentIndex > self.allCoordinates.count { timer.invalidate() @@ -84,7 +84,7 @@ public class AnimateGeoJSONLineExample: UIViewController, ExampleProtocol { let updatedLine = Feature(geometry: LineString(currentCoordinates)) self.routeLineSource.data = .feature(updatedLine) - try! self.mapView.mapboxMap.style.updateGeoJSONSource(withId: self.sourceIdentifier, + self.mapView.mapboxMap.updateGeoJSONSource(withId: self.sourceIdentifier, geoJSON: .feature(updatedLine)) } } diff --git a/Sources/Examples/All Examples/AnimateImageLayerExample.swift b/Sources/Examples/All Examples/AnimateImageLayerExample.swift new file mode 100644 index 000000000000..b2a8c53fdf1a --- /dev/null +++ b/Sources/Examples/All Examples/AnimateImageLayerExample.swift @@ -0,0 +1,110 @@ +import MapboxMaps +import UIKit + +final class AnimateImageLayerExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private let sourceId = "radar-source" + private var timer: Timer? + private var imageNumber = 0 + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + let center = CLLocationCoordinate2D(latitude: 41.874, longitude: -75.789) + let cameraOptions = CameraOptions(center: center, zoom: 5) + let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .dark) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth] + + // Hide the `scaleBar` at all zoom levels. + mapView.ornaments.options.scaleBar.visibility = .hidden + + // This also updates the color of the info button to match the map's style. + mapView.tintColor = .lightGray + + // Set the map's `CameraBoundsOptions` to limit the map's zoom level. + try? mapView.mapboxMap.setCameraBounds(with: CameraBoundsOptions(maxZoom: 5.99, minZoom: 4)) + + view.addSubview(mapView) + + mapView.mapboxMap.onMapLoaded.observeNext { _ in + self.addImageLayer() + + // The following line is just for testing purposes. + self.finish() + }.store(in: &cancelables) + } + + func addImageLayer() { + // Create an `ImageSource`. This will manage the image displayed in the `RasterLayer` as well + // as the location of that image on the map. + var imageSource = ImageSource(id: sourceId) + + // Set the `coordinates` property to an array of longitude, latitude pairs. + imageSource.coordinates = [ + [-80.425, 46.437], + [-71.516, 46.437], + [-71.516, 37.936], + [-80.425, 37.936] + ] + + // Get the file path for the first radar image, then set the `url` for the `ImageSource` to that path. + let path = Bundle.main.url(forResource: "radar0", withExtension: "gif") + imageSource.url = path?.absoluteString + + // Create a `RasterLayer` that will display the images from the `ImageSource` + var imageLayer = RasterLayer(id: "radar-layer", source: sourceId) + + // Set `rasterFadeDuration` to `0`. This prevents visible transitions when the image is updated. + imageLayer.rasterFadeDuration = .constant(0) + + do { + try mapView.mapboxMap.addSource(imageSource) + try mapView.mapboxMap.addLayer(imageLayer) + + } catch { + print("Failed to add the source or layer to style. Error: \(error)") + } + + // Add a tap gesture handler that will allow the animation to be stopped and started. + mapView.gestures.onMapTap.observe {[weak self] _ in + self?.manageTimer() + }.store(in: &cancelables) + + manageTimer() + } + + func manageTimer() { + if timer == nil { + timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in + guard let self = self else { return } + + // There are five radar images, number 0-4. Increment the count. When that would + // result in an `imageNumber` value greater than 4, reset `imageNumber` to `0`. + if self.imageNumber < 4 { + self.imageNumber += 1 + } else { + self.imageNumber = 0 + } + // Create a `UIImage` from the file at the specified path. + let path = Bundle.main.url(forResource: "radar\(self.imageNumber)", withExtension: "gif") + let image = UIImage(contentsOfFile: path!.relativePath) + + do { + // Update the image used by the `ImageSource`. + try self.mapView.mapboxMap.updateImageSource(withId: self.sourceId, image: image!) + } catch { + print("Failed to update style image. Error: \(error)") + } + } + } else { + timer?.invalidate() + timer = nil + } + } + + deinit { + timer?.invalidate() + } +} diff --git a/Sources/Examples/All Examples/AnimateLayerExample.swift b/Sources/Examples/All Examples/AnimateLayerExample.swift new file mode 100644 index 000000000000..61f833176eec --- /dev/null +++ b/Sources/Examples/All Examples/AnimateLayerExample.swift @@ -0,0 +1,125 @@ +import UIKit +import MapboxMaps + +final class AnimateLayerExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // Set the map's center coordinate and zoom level + let centerCoordinate = CLLocationCoordinate2D(latitude: 37.8, longitude: -96) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, + zoom: 2), + styleURI: .streets) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allows the view controller to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.setupExample() + }.store(in: &cancelables) + } + + func setupExample() { + + // San Francisco, California + let origin = CLLocationCoordinate2DMake(37.776, -122.414) + // Washington, D.C. + let destination = CLLocationCoordinate2DMake(38.913, -77.032) + + let arcLine = arc(start: origin, end: destination) + + // Add the layers to be rendered on the map. + addLayers(for: arcLine) + + // Begin animating the airplane across the route line. + startAnimation(routeLine: arcLine) + } + + func arc(start: CLLocationCoordinate2D, end: CLLocationCoordinate2D) -> LineString { + let line = LineString([start, end]) + let distance = Int(start.distance(to: end)) + + var coordinates = [CLLocationCoordinate2D]() + let steps = 500 + var index = 0 + + while index < distance { + index += distance / steps + let coord = line.coordinateFromStart(distance: CLLocationDistance(index))! + coordinates.append(coord) + } + + return LineString(coordinates.compactMap({ $0 })) + } + + func addLayers(for routeLine: LineString) { + // Define the source data and style layer for the airplane's route line. + var airplaneRoute = GeoJSONSource(id: "airplane-route") + airplaneRoute.data = .feature(Feature(geometry: routeLine)) + + var lineLayer = LineLayer(id: "line-layer", source: airplaneRoute.id) + lineLayer.lineColor = .constant(StyleColor(.red)) + lineLayer.lineWidth = .constant(3.0) + lineLayer.lineCap = .constant(.round) + + // Define the source data and style layer for the airplane symbol. + var airplaneSymbol = GeoJSONSource(id: "airplane-symbol") + let point = Point(routeLine.coordinates[0]) + airplaneSymbol.data = .feature(Feature(geometry: point)) + + var airplaneSymbolLayer = SymbolLayer(id: "airplane", source: airplaneSymbol.id) + // "airport" is the name the image that belongs in the style's sprite by default. + airplaneSymbolLayer.iconImage = .constant(.name("airport")) + airplaneSymbolLayer.iconRotationAlignment = .constant(.map) + airplaneSymbolLayer.iconAllowOverlap = .constant(true) + airplaneSymbolLayer.iconIgnorePlacement = .constant(true) + // Get the "bearing" property from the point's feature dictionary, + // and use that value to determine the rotation angle of the airplane icon. + airplaneSymbolLayer.iconRotate = .expression(Exp(.get) { + "bearing" + }) + + // Add the sources and layers to the map style. + try! mapView.mapboxMap.addSource(airplaneRoute) + try! mapView.mapboxMap.addLayer(lineLayer) + + try! mapView.mapboxMap.addSource(airplaneSymbol) + try! mapView.mapboxMap.addLayer(airplaneSymbolLayer, layerPosition: nil) + } + + func startAnimation(routeLine: LineString) { + var runCount = 0 + + _ = Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { [weak self] timer in + + guard let self = self else { return } + + let coordinate = routeLine.coordinates[runCount] + let nextCoordinate = routeLine.coordinates[runCount + 1] + + // Identify the new coordinate to animate to, and calculate + // the bearing between the new coordinate and the following coordinate. + var geoJSON = Feature(geometry: Point(coordinate)) + geoJSON.properties = ["bearing": .number(coordinate.direction(to: nextCoordinate))] + + // Update the airplane source layer with the new coordinate and bearing. + self.mapView.mapboxMap.updateGeoJSONSource(withId: "airplane-symbol", + geoJSON: .feature(geoJSON)) + + runCount += 1 + + if runCount == 500 { + timer.invalidate() + // The below line is used for internal testing purposes only. + self.finish() + } + } + + } +} diff --git a/Apps/Examples/Examples/All Examples/Annotations/AddMarkersSymbolExample.swift b/Sources/Examples/All Examples/Annotations/AddMarkersSymbolExample.swift similarity index 75% rename from Apps/Examples/Examples/All Examples/Annotations/AddMarkersSymbolExample.swift rename to Sources/Examples/All Examples/Annotations/AddMarkersSymbolExample.swift index 6c2d473a1a1f..31dc71c30959 100644 --- a/Apps/Examples/Examples/All Examples/Annotations/AddMarkersSymbolExample.swift +++ b/Sources/Examples/All Examples/Annotations/AddMarkersSymbolExample.swift @@ -1,19 +1,9 @@ -import Foundation +import UIKit import MapboxMaps -@objc(AddMarkersSymbolExample) final class AddMarkersSymbolExample: UIViewController, ExampleProtocol { - private enum Constants { - static let ICON_KEY = "icon_key" - static let BLUE_MARKER_PROPERTY = "icon_blue_property" - static let RED_MARKER_PROPERTY = "icon_red_property" - static let BLUE_ICON_ID = "blue" - static let RED_ICON_ID = "red" - static let SOURCE_ID = "source_id" - static let LAYER_ID = "layer_id" - } - private var mapView: MapView! + private var cancelables = Set() override func viewDidLoad() { super.viewDidLoad() @@ -25,23 +15,22 @@ final class AddMarkersSymbolExample: UIViewController, ExampleProtocol { mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView) - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in guard let self = self else { return } self.prepareStyle() // The following line is just for testing purposes. self.finish() - } + }.store(in: &cancelables) - mapView.mapboxMap.style.uri = .streets + mapView.mapboxMap.styleURI = .streets } // MARK: - Style management private func prepareStyle() { - let style = mapView.mapboxMap.style - try? style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.BLUE_ICON_ID) - try? style.addImage(UIImage(named: "red_marker")!, id: Constants.RED_ICON_ID) + try? mapView.mapboxMap.addImage(UIImage(named: "intermediate-pin")!, id: Constants.BLUE_ICON_ID) + try? mapView.mapboxMap.addImage(UIImage(named: "dest-pin")!, id: Constants.RED_ICON_ID) var features = [Feature]() var feature = Feature(geometry: Point(LocationCoordinate2D(latitude: 55.608166, longitude: 12.65147))) @@ -52,9 +41,9 @@ final class AddMarkersSymbolExample: UIViewController, ExampleProtocol { feature1.properties = [Constants.ICON_KEY: .string(Constants.RED_MARKER_PROPERTY)] features.append(feature1) - var source = GeoJSONSource() + var source = GeoJSONSource(id: Constants.SOURCE_ID) source.data = .featureCollection(FeatureCollection(features: features)) - try? style.addSource(source, id: Constants.SOURCE_ID) + try? mapView.mapboxMap.addSource(source) let rotateExpression = Exp(.match) { Exp(.get) { Constants.ICON_KEY } @@ -70,12 +59,25 @@ final class AddMarkersSymbolExample: UIViewController, ExampleProtocol { Constants.RED_ICON_ID Constants.RED_ICON_ID } - var layer = SymbolLayer(id: Constants.LAYER_ID) - layer.source = Constants.SOURCE_ID + var layer = SymbolLayer(id: Constants.LAYER_ID, source: Constants.SOURCE_ID) layer.iconImage = .expression(imageExpression) layer.iconAnchor = .constant(.bottom) layer.iconAllowOverlap = .constant(false) layer.iconRotate = .expression(rotateExpression) - try? style.addLayer(layer) + // Add Y-offset so icon will point to exact location. + layer.iconOffset = .constant([0, 12]) + try? mapView.mapboxMap.addLayer(layer) + } +} + +extension AddMarkersSymbolExample { + private enum Constants { + static let ICON_KEY = "icon_key" + static let BLUE_MARKER_PROPERTY = "icon_blue_property" + static let RED_MARKER_PROPERTY = "icon_red_property" + static let BLUE_ICON_ID = "blue" + static let RED_ICON_ID = "red" + static let SOURCE_ID = "source_id" + static let LAYER_ID = "layer_id" } } diff --git a/Sources/Examples/All Examples/Annotations/AddOneMarkerSymbolExample.swift b/Sources/Examples/All Examples/Annotations/AddOneMarkerSymbolExample.swift new file mode 100644 index 000000000000..c34042d23e7d --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/AddOneMarkerSymbolExample.swift @@ -0,0 +1,48 @@ +import UIKit +import MapboxMaps + +final class AddOneMarkerSymbolExample: UIViewController, ExampleProtocol { + private lazy var mapView: MapView = { + let options = MapInitOptions(cameraOptions: CameraOptions(center: Constants.coordinate, zoom: 8)) + + return MapView(frame: view.bounds, mapInitOptions: options) + }() + + override func viewDidLoad() { + super.viewDidLoad() + + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.loadStyle(.standard) { [weak self] error in + guard error == nil else { return } + + self?.addMarkerAnnotation() + // The following line is just for testing purposes. + self?.finish() + } + } + + private func addMarkerAnnotation() { + try? mapView.mapboxMap.addImage(UIImage(named: "dest-pin")!, id: Constants.RED_ICON_ID) + + var source = GeoJSONSource(id: Constants.SOURCE_ID) + source.data = .geometry(Geometry.point(Point(Constants.coordinate))) + try? mapView.mapboxMap.addSource(source) + + var layer = SymbolLayer(id: Constants.LAYER_ID, source: Constants.SOURCE_ID) + layer.iconImage = .constant(.name(Constants.RED_ICON_ID)) + layer.iconAnchor = .constant(.bottom) + layer.iconOffset = .constant([0, 12]) + try? mapView.mapboxMap.addLayer(layer) + } +} + +extension AddOneMarkerSymbolExample { + private enum Constants { + static let RED_ICON_ID = "red" + static let SOURCE_ID = "source_id" + static let LAYER_ID = "layer_id" + static let coordinate = CLLocationCoordinate2D(latitude: 55.665957, longitude: 12.550343) + } +} diff --git a/Apps/Examples/Examples/All Examples/Annotations/AnimatedMarkerExample.swift b/Sources/Examples/All Examples/Annotations/AnimatedMarkerExample.swift similarity index 78% rename from Apps/Examples/Examples/All Examples/Annotations/AnimatedMarkerExample.swift rename to Sources/Examples/All Examples/Annotations/AnimatedMarkerExample.swift index 27dda2954603..1c96dc1b57eb 100644 --- a/Apps/Examples/Examples/All Examples/Annotations/AnimatedMarkerExample.swift +++ b/Sources/Examples/All Examples/Annotations/AnimatedMarkerExample.swift @@ -2,23 +2,16 @@ import Foundation import MapboxMaps import UIKit -@objc(AnimatedMarkerExample) final class AnimatedMarkerExample: UIViewController, ExampleProtocol { - enum Constants { - static let markerIconId = "marker_icon" - static let sourceId = "source-id" - static let animationDuration: CFTimeInterval = 2 - } private var mapView: MapView! - private var currentPosition = CLLocationCoordinate2D(latitude: 64.900932, longitude: -18.167040) - private var animationStartTimestamp: CFTimeInterval = 0 private var origin: CLLocationCoordinate2D! private var destination: CLLocationCoordinate2D! private var displayLink: CADisplayLink? { didSet { oldValue?.invalidate() } } + private var cancelables = Set() deinit { displayLink?.invalidate() @@ -34,14 +27,14 @@ final class AnimatedMarkerExample: UIViewController, ExampleProtocol { view.addSubview(mapView) // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in // Set up the example self?.setupExample() // The below line is used for internal testing purposes only. self?.finish() - } + }.store(in: &cancelables) let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false @@ -55,29 +48,31 @@ final class AnimatedMarkerExample: UIViewController, ExampleProtocol { label.centerXAnchor.constraint(equalTo: view.centerXAnchor) ]) - mapView.mapboxMap.loadStyleURI(.satelliteStreets) + mapView.mapboxMap.loadStyle(.satelliteStreets) - // add a tap gesture recognizer that will allow the marker to be animated - mapView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(updatePosition(_:)))) + // add a tap gesture handler that will allow the marker to be animated + mapView.gestures.onMapTap.observe {[weak self] context in + self?.updatePosition(newCoordinate: context.coordinate) + }.store(in: &cancelables) } private func setupExample() { - try? mapView.mapboxMap.style.addImage(UIImage(named: "red_marker")!, id: Constants.markerIconId) + try? mapView.mapboxMap.addImage(UIImage(named: "dest-pin")!, id: Constants.markerIconId) // Create a GeoJSON data source. - var source = GeoJSONSource() + var source = GeoJSONSource(id: Constants.sourceId) source.data = .feature(Feature(geometry: Point(currentPosition))) - try? mapView.mapboxMap.style.addSource(source, id: Constants.sourceId) + try? mapView.mapboxMap.addSource(source) // Create a symbol layer - var symbolLayer = SymbolLayer(id: "layer-id") - symbolLayer.source = Constants.sourceId + var symbolLayer = SymbolLayer(id: "layer-id", source: Constants.sourceId) symbolLayer.iconImage = .constant(.name(Constants.markerIconId)) symbolLayer.iconIgnorePlacement = .constant(true) symbolLayer.iconAllowOverlap = .constant(true) + symbolLayer.iconOffset = .constant([0, 12]) - try? mapView.mapboxMap.style.addLayer(symbolLayer) + try? mapView.mapboxMap.addLayer(symbolLayer) } override func didMove(toParent parent: UIViewController?) { @@ -108,14 +103,12 @@ final class AnimatedMarkerExample: UIViewController, ExampleProtocol { self.currentPosition = coordinate // update source with the new marker location - try? self.mapView.mapboxMap.style.updateGeoJSONSource(withId: Constants.sourceId, + self.mapView.mapboxMap.updateGeoJSONSource(withId: Constants.sourceId, geoJSON: .feature(Feature(geometry: Point(coordinate)))) } - @objc private func updatePosition(_ sender: UITapGestureRecognizer) { - let newCoordinate = mapView.mapboxMap.coordinate(for: sender.location(in: mapView)) - + private func updatePosition(newCoordinate: CLLocationCoordinate2D) { // save marker's origin and destination to interpolate between them during the animation destination = newCoordinate origin = currentPosition @@ -126,3 +119,11 @@ final class AnimatedMarkerExample: UIViewController, ExampleProtocol { displayLink?.add(to: .current, forMode: .common) } } + +extension AnimatedMarkerExample { + enum Constants { + static let markerIconId = "marker_icon" + static let sourceId = "source-id" + static let animationDuration: CFTimeInterval = 2 + } +} diff --git a/Sources/Examples/All Examples/Annotations/AnnotationView.swift b/Sources/Examples/All Examples/Annotations/AnnotationView.swift new file mode 100644 index 000000000000..ffb679e267f3 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/AnnotationView.swift @@ -0,0 +1,88 @@ +import Foundation +import UIKit + +// `AnnotationView` is a custom `UIView` subclass which is used only for annotation demonstration +final class AnnotationView: UIView { + + var onSelect: ((Bool) -> Void)? + var onClose: (() -> Void)? + + var selected: Bool = false { + didSet { + selectButton.setTitle(selected ? "Deselect" : "Select", for: .normal) + vStack.spacing = selected ? 20 : 4 + onSelect?(selected) + } + } + + var title: String? { + get { centerLabel.text } + set { centerLabel.text = newValue } + } + + lazy var centerLabel: UILabel = { + let label = UILabel(frame: .zero) + label.font = UIFont.systemFont(ofSize: 10) + label.numberOfLines = 0 + return label + }() + lazy var closeButton: UIButton = { + let button = UIButton(type: .system) + button.setTitleColor(.black, for: .normal) + button.setTitle("X", for: .normal) + return button + }() + lazy var selectButton: UIButton = { + let button = UIButton(type: .system) + button.setTitleColor(.white, for: .normal) + button.backgroundColor = #colorLiteral(red: 0, green: 0.4784313725, blue: 0.9882352941, alpha: 1) + button.layer.cornerRadius = 8 + button.clipsToBounds = true + button.setTitle("Select", for: .normal) + return button + }() + private let vStack: UIStackView + + override init(frame: CGRect) { + vStack = UIStackView() + super.init(frame: frame) + backgroundColor = .white + layer.shadowOpacity = 0.25 + layer.shadowRadius = 8 + layer.shadowOffset = CGSize(width: 0, height: 2) + layer.cornerRadius = 8 + + let hStack = UIStackView(arrangedSubviews: [centerLabel, closeButton]) + hStack.spacing = 4 + + vStack.addArrangedSubview(hStack) + vStack.addArrangedSubview(selectButton) + vStack.axis = .vertical + vStack.translatesAutoresizingMaskIntoConstraints = false + vStack.spacing = 4 + addSubview(vStack) + NSLayoutConstraint.activate([ + vStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4), + vStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4), + vStack.topAnchor.constraint(equalTo: topAnchor, constant: 4), + vStack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4), + ]) + + closeButton.addTarget(self, action: #selector(closePressed(sender:)), for: .touchUpInside) + selectButton.addTarget(self, action: #selector(selectPressed(sender:)), for: .touchUpInside) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Action handlers + + @objc private func closePressed(sender: UIButton) { + onClose?() + } + + @objc private func selectPressed(sender: UIButton) { + selected.toggle() + } +} diff --git a/Sources/Examples/All Examples/Annotations/CircleAnnotationExample.swift b/Sources/Examples/All Examples/Annotations/CircleAnnotationExample.swift new file mode 100644 index 000000000000..b3e715af1000 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/CircleAnnotationExample.swift @@ -0,0 +1,82 @@ +import UIKit +import MapboxMaps + +final class CircleAnnotationExample: UIViewController, ExampleProtocol { + private lazy var mapView: MapView = MapView(frame: view.bounds) + + override func viewDidLoad() { + super.viewDidLoad() + + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 0, longitude: 0), zoom: 2) + let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + /// Create the CircleAnnotationManager + /// Annotation managers are kept alive by `AnnotationOrchestrator` + /// (`mapView.annotations`) until you explicitly destroy them + /// by calling `mapView.annotations.removeAnnotationManager(withId:)` + let circleAnnotationManager = mapView.annotations.makeCircleAnnotationManager() + + var annotations = [CircleAnnotation]() + for i in 0...2000 { + var annotation = CircleAnnotation(centerCoordinate: .random) + if i % 2 == 0 { + annotation.circleColor = StyleColor(.random) + annotation.circleStrokeColor = StyleColor(UIColor.black) + annotation.circleOpacity = 0.7 + } + annotation.isDraggable = true + + /// The following handlers add tap and longpress gesture handlers. The `context` parameter + /// contains the `point` of the gesture in view coordinate system and a geographical `coordinate`. + annotation.tapHandler = { [id = annotation.id] context in + let latlon = String(format: "lat: %.3f, lon: %.3f", context.coordinate.latitude, context.coordinate.longitude) + print("annotation tap: \(id), \(latlon)") + return true // don't propagate tap to annotations below + } + annotation.longPressHandler = { [id = annotation.id] context in + let latlon = String(format: "lat: %.3f, lon: %.3f", context.coordinate.latitude, context.coordinate.longitude) + print("annotation longpress: \(id), \(latlon)") + return true // don't propagate tap to annotations below + } + + /// The following gesture handlers create the dragging effect. + /// The dragged annotation becomes larger and receives a stroke. + /// + /// - Important: In order to modify the annotation while it is being dragged, + /// use the inout `annotation` that comes as the first argument to the handler. + /// Don't use the source annotation that you used to configure it initially. + /// The annotations are value types. + /// + /// The second `context` argument is similar to tap and longpress gestures. + annotation.dragBeginHandler = { annotation, _ in + annotation.circleRadius = 22 + annotation.circleStrokeWidth = 2 + print("annotation drag begin: \(annotation.id)") + return true // allow drag gesture begin + } + annotation.dragChangeHandler = { annotation, context in + let latlon = String(format: "lat: %.3f, lon: %.3f", context.coordinate.latitude, context.coordinate.longitude) + print("annotation drag: \(annotation.id), \(latlon)") + } + annotation.dragEndHandler = { annotation, _ in + annotation.circleRadius = 12 + annotation.circleStrokeWidth = 4 + print("annotation drag ended: \(annotation.id)") + } + annotations.append(annotation) + } + + circleAnnotationManager.annotations = annotations + circleAnnotationManager.circleColor = StyleColor(UIColor.blue) + circleAnnotationManager.circleStrokeColor = StyleColor(UIColor.white) + circleAnnotationManager.circleStrokeWidth = 4 + circleAnnotationManager.circleRadius = 12 + circleAnnotationManager.circleOpacity = 0.3 + circleAnnotationManager.circleStrokeOpacity = 0.8 + // The following line is just for testing purposes. + finish() + } +} diff --git a/Sources/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift b/Sources/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift new file mode 100644 index 000000000000..84cf6aaf8698 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/CustomPointAnnotationExample.swift @@ -0,0 +1,65 @@ +import UIKit +import MapboxMaps + +final class CustomPointAnnotationExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private let customImage = UIImage(named: "dest-pin")! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // Center the map camera over New York City + let centerCoordinate = CLLocationCoordinate2D(latitude: 40.7128, longitude: -74.0060) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, + zoom: 9.0)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allows the delegate to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + self.setupExample() + + // The following line is just for testing purposes. + self.finish() + }.store(in: &cancelables) + } + + private func setupExample() { + + // We want to display the annotation at the center of the map's current viewport + let centerCoordinate = mapView.mapboxMap.cameraState.center + + // Make a `PointAnnotationManager` which will be responsible for managing + // a collection of `PointAnnotion`s. + // Annotation managers are kept alive by `AnnotationOrchestrator` + // (`mapView.annotations`) until you explicitly destroy them + // by calling `mapView.annotations.removeAnnotationManager(withId:)` + let pointAnnotationManager = mapView.annotations.makePointAnnotationManager() + + // Initialize a point annotation with a geometry ("coordinate" in this case) + // and configure it with a custom image (sourced from the asset catalogue) + var customPointAnnotation = PointAnnotation(coordinate: centerCoordinate) + customPointAnnotation.image = .init(image: customImage, name: "my-custom-image-name") + customPointAnnotation.isDraggable = true + customPointAnnotation.iconOffset = [0, 12] + customPointAnnotation.tapHandler = { [id = customPointAnnotation.id] _ in + print("tapped annotation: \(id)") + return true + } + + customPointAnnotation.dragBeginHandler = { annotation, _ in + annotation.iconSize = 1.2 + return true // allow drag gesture begin + } + customPointAnnotation.dragEndHandler = { annotation, _ in + annotation.iconSize = 1 + } + + // Add the annotation to the manager in order to render it on the map. + pointAnnotationManager.annotations = [customPointAnnotation] + } +} diff --git a/Sources/Examples/All Examples/Annotations/DynamicViewAnnotationExample.swift b/Sources/Examples/All Examples/Annotations/DynamicViewAnnotationExample.swift new file mode 100644 index 000000000000..56783897d39d --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/DynamicViewAnnotationExample.swift @@ -0,0 +1,561 @@ +import UIKit +@_spi(Experimental) import MapboxMaps +import CoreLocation +import MapboxCoreMaps + +private let simulatedCoordinate = CLLocationCoordinate2D(latitude: 37.6421, longitude: -122.4062) + +final class DynamicViewAnnotationExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + private var routes = [Route]() { + didSet { + oldValue.forEach { $0.remove() } + // prevent eta labels from showing up on overlapped parts of the route + mapView.viewAnnotations.viewAnnotationAvoidLayers = Set(routes.map(\.layerId)) + routes.forEach { route in + route.mapView = mapView + route.display() + route.onTap = { [unowned route, weak self] in + self?.select(route: route) + } + } + if let last = routes.last { + select(route: last, animated: false) + } + } + } + + private lazy var modeButton = { + let button = UIButton(type: .system) + button.backgroundColor = .white + button.tintColor = .black + button.translatesAutoresizingMaskIntoConstraints = false + button.layer.cornerRadius = 20 + button.layer.shadowColor = UIColor(red: 0.084, green: 0.176, blue: 0.283, alpha: 0.25).cgColor + button.layer.shadowOpacity = 1 + button.layer.shadowRadius = 8 + button.layer.shadowOffset = CGSize(width: 0, height: 2) + button.addTarget(self, action: #selector(changeMode), for: .touchUpInside) + NSLayoutConstraint.activate([ + button.widthAnchor.constraint(equalToConstant: 200), + button.heightAnchor.constraint(equalToConstant: 40) + ]) + return button + }() + + private var driveMode = false + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + updateModeButton() + + mapView.location.override( + locationProvider: Signal(just: [ + Location( + coordinate: simulatedCoordinate, + bearing: 168.8) + ]), + headingProvider: Signal(just: Heading(direction: 180, accuracy: 0))) + mapView.location.options = LocationOptions(puckType: .puck2D(.init(topImage: UIImage(named: "dash-puck"))), puckBearing: .heading, puckBearingEnabled: true) + + mapView.viewport.options.usesSafeAreaInsetsAsPadding = true + + mapView.mapboxMap.onStyleLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + loadRoutes() + self.finish() + }.store(in: &cancelables) + + self.toolbarItems = [ + UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil), + UIBarButtonItem(customView: modeButton), + UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + ] + + addParkingAnnotation( + coordinate: CLLocationCoordinate2D(latitude: 37.445, longitude: -122.1704), + text: "$6.99/hr", + minZoom: 12) + addParkingAnnotation( + coordinate: CLLocationCoordinate2D(latitude: 37.4441, longitude: -122.1691), + text: "$5.99/hr", + minZoom: 10) + } + + private func loadRoutes() { + DispatchQueue.global(qos: .userInitiated).async { + let route1 = Route.load(name: "route-sf-1", time: "52 min") + let route2 = Route.load(name: "route-sf-2", time: "55 min") + route1.hint = ETAHint(text: "Avoid traffic", icon: "maneuver-straight") + route2.hint = ETAHint(text: "On highway", icon: "maneuver-turn-right") + DispatchQueue.main.async { + self.routes = [route1, route2] + } + } + } + + private func select(route: Route, animated: Bool = true) { + // Move selected route layer on top of unselected route layers + let routeLayersIds = Set(routes.map(\.layerId)) + let lastUnselected = mapView.mapboxMap.allLayerIdentifiers.last { info in + routeLayersIds.contains(info.id) + } + if let lastUnselected, lastUnselected.id != route.layerId { + try? mapView.mapboxMap.moveLayer(withId: route.layerId, to: .above(lastUnselected.id)) + } + + for r in routes { + // Update layer color + r.selected = r === route + } + if !driveMode { + updateViewport(animated: animated) + } + } + + @objc private func changeMode() { + self.driveMode.toggle() + updateModeButton() + + hideAnnotations(true) + updateViewport(animated: true) { [weak self] in + self?.hideAnnotations(false) + } + routes.forEach { + $0.updateProgress(with: driveMode ? simulatedCoordinate : nil) + } + } + + private func updateModeButton() { + modeButton.setTitle("Mode: \(driveMode ? "Drive" : "Overview")", for: .normal) + } + + private func hideAnnotations(_ hidden: Bool) { + routes.forEach { + $0.etaAnnotation?.visible = !hidden + } + } + + private func updateViewport(animated: Bool, completion: (() -> Void)? = nil) { + var viewportState: ViewportState? + if driveMode { + viewportState = mapView.viewport.makeFollowPuckViewportState(options: .init(zoom: 17, bearing: .course, pitch: 49)) + } else { + if let route = routes.first(where: \.selected), let geometry = route.feature.geometry { + let coordPadding = UIEdgeInsets(allEdges: 20) + let options = OverviewViewportStateOptions(geometry: geometry, geometryPadding: coordPadding) + viewportState = mapView.viewport.makeOverviewViewportState(options: options) + } + } + + if let viewportState { + mapView.viewport.transition( + to: viewportState, + transition: animated ? mapView.viewport.makeDefaultViewportTransition() : mapView.viewport.makeImmediateViewportTransition() + ) { _ in completion?() } + } else { + completion?() + } + } + + private func addParkingAnnotation(coordinate: CLLocationCoordinate2D, text: String, minZoom: Double) { + let view = ParkingAnnotationView(text: text) + + let annotation = ViewAnnotation(coordinate: coordinate, view: view) + annotation.allowOverlap = true + annotation.minZoom = minZoom + mapView.viewAnnotations.add(annotation) + + view.onTap = { [unowned view, unowned annotation] in + view.selected.toggle() + annotation.priority = view.selected ? 1 : 0 + annotation.setNeedsUpdateSize() + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setToolbarHidden(false, animated: false) + } +} + +struct ETAHint { + var text: String + var icon: String +} + +private final class Route { + let name: String + let time: String + let feature: Feature + var hint: ETAHint? + var selected: Bool = false { + didSet { updateSelected() } + } + var layerId: String { "route-\(name)" } + private(set) var etaAnnotation: ViewAnnotation? + private var etaView: ETAView? + private var displayed = false + private var tokens = Set() + + var onTap: (() -> Void)? + weak var mapView: MapView? + + init(name: String, time: String, feature: Feature) { + self.name = name + self.time = time + self.feature = feature + } + + func updateProgress(with coordinate: CLLocationCoordinate2D?) { + var progress = 0.0 + if let coordinate, + case let .lineString(s) = feature.geometry, + let doneDistance = s.distance(to: coordinate), + let length = s.distance() { + progress = doneDistance / length + 0.0005 + } + + try? mapView?.mapboxMap.setLayerProperty(for: layerId, property: "line-trim-offset", value: [0, progress]) + } + + func display() { + guard !displayed, let mapView else { return } + displayed = true + + func colorExpression(normal: String, selected: String) -> Exp { + Exp(.switchCase) { + Exp(.boolean) { + Exp(.featureState) { "selected" } + false + } + selected + normal + } + } + + // Routes data source and layer + var source = GeoJSONSource(id: layerId) + source.data = .feature(feature) + source.lineMetrics = true + try! mapView.mapboxMap.addSource(source) + + var routeLayer = LineLayer(id: layerId, source: layerId) + routeLayer.lineCap = .constant(.round) + routeLayer.lineJoin = .constant(.round) + routeLayer.lineWidth = .constant(10.0) + routeLayer.lineColor = .expression(colorExpression(normal: "#999999", selected: "#57A9FB")) + routeLayer.lineBorderWidth = .constant(2) + routeLayer.lineBorderColor = .expression(colorExpression(normal: "#666666", selected: "#327AC2")) + routeLayer.slot = .middle + try! mapView.mapboxMap.addLayer(routeLayer) + + // Annotation + let etaView = ETAView(text: time) + self.etaView = etaView + + let etaAnnotation = ViewAnnotation(layerId: layerId, view: etaView) + etaAnnotation.onAnchorChanged = { config in + etaView.anchor = config.anchor + } + etaAnnotation.variableAnchors = .all + etaAnnotation.minZoom = 8 + etaView.onTap = { [weak self] in self?.onTap?() } + + self.etaAnnotation = etaAnnotation + updateSelected() + + mapView.viewAnnotations.add(etaAnnotation) + mapView.gestures.onLayerTap(layerId) { [weak self] feature, _ in + guard let self, + let onTap = onTap, + let identifier = feature.feature.identifier, + case let .string(id) = identifier, + id == self.name else { return false } + onTap() + return true + }.store(in: &tokens) + } + + func remove() { + try? mapView?.mapboxMap.removeLayer(withId: self.layerId) + try? mapView?.mapboxMap.removeSource(withId: self.layerId) + self.etaAnnotation?.remove() + self.etaAnnotation = nil + self.etaView = nil + mapView = nil + onTap = nil + tokens.removeAll() + } + + private func updateSelected() { + etaAnnotation?.priority = selected ? 1 : 0 + etaView?.selected = selected + mapView?.mapboxMap.setFeatureState(sourceId: layerId, featureId: name, state: ["selected": selected]) {_ in} + + etaView?.hint = selected ? nil : hint + etaAnnotation?.setNeedsUpdateSize() + } + + static func load(name: String, time: String) -> Route { + let data = NSDataAsset(name: name)!.data + let feature = try! JSONDecoder().decode(Feature.self, from: data) + return .init(name: name, time: time, feature: feature) + } +} + +private final class ParkingAnnotationView: UIView { + private let label = UILabel() + private let icon = UIImageView() + private let stack = UIStackView() + var text: String { + didSet { label.text = text } + } + var selected: Bool = false { + didSet { updateSelection() } + } + + var onTap: (() -> Void)? + + init(text: String) { + self.text = text + + super.init(frame: .zero) + icon.image = UIImage(named: "parking-icon") + stack.axis = .horizontal + stack.spacing = 3 + stack.addArrangedSubview(icon) + stack.addArrangedSubview(label) + stack.translatesAutoresizingMaskIntoConstraints = false + addSubview(stack) + + NSLayoutConstraint.activate([ + stack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 3), + stack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12), + stack.topAnchor.constraint(equalTo: topAnchor, constant: 3), + stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -3), + icon.widthAnchor.constraint(equalToConstant: 24), + icon.heightAnchor.constraint(equalToConstant: 24) + ]) + layer.shadowColor = UIColor(red: 0.084, green: 0.176, blue: 0.283, alpha: 0.25).cgColor + layer.shadowOpacity = 1 + layer.shadowRadius = 8 + layer.shadowOffset = CGSize(width: 0, height: 2) + addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap))) + updateSelection() + updateText() + } + + func updateText() { + label.attributedText = .labelText(text, size: 16, color: .black) + } + + func updateSelection() { + backgroundColor = selected ? .systemBlue : .white + label.textColor = selected ? .white : .black + } + + @objc private func handleTap() { + onTap?() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func layoutSubviews() { + super.layoutSubviews() + layer.cornerRadius = bounds.size.height / 2 + } +} + +final class ETAView: UIView { + private let label = UILabel() + private let iconView = UIImageView() + private var tail = UIView() + private let backgroundShape = CAShapeLayer() + + var hint: ETAHint? { + didSet { update() } + } + + var padding = UIEdgeInsets(allEdges: 10) + var tailSize = 8.0 + var cornerRadius = 8.0 + var selected: Bool = false { + didSet { update() } + } + var onTap: (() -> Void)? + + var text: String { + didSet { update() } + } + var anchor: ViewAnnotationAnchor? { + didSet { setNeedsLayout() } + } + + init(text: String) { + self.text = text + super.init(frame: .zero) + self.layer.addSublayer(backgroundShape) + backgroundShape.shadowRadius = 1.4 + backgroundShape.shadowOffset = CGSize(width: 0, height: 0.7) + backgroundShape.shadowColor = UIColor.black.cgColor + backgroundShape.shadowOpacity = 0.3 + + iconView.contentMode = .scaleAspectFit + iconView.tintColor = UIColor(red: 0.04, green: 0.66, blue: 0.45, alpha: 1) + label.numberOfLines = 0 + label.textAlignment = .left + addSubview(label) + addSubview(iconView) + + update() + + addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap))) + } + + @objc private func handleTap() { + onTap?() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private var attributedText: NSAttributedString { + let text = NSMutableAttributedString(attributedString: + .labelText(text, size: 16, color: selected ? .white : .black, bold: true)) + if let hint { + text.append(NSAttributedString(string: "\n")) + text.append(.labelText(hint.text, size: 12, color: .gray)) + } + return text + } + + private func update() { + self.backgroundShape.fillColor = selected ? UIColor.systemBlue.cgColor : UIColor.white.cgColor + self.label.attributedText = attributedText + self.iconView.image = hint.flatMap { + UIImage(named: $0.icon)?.withRenderingMode(.alwaysTemplate) + } + } + + struct Layout { + var label: CGRect + var bubble: CGRect + var icon: CGRect + var size: CGSize + + init(availableSize: CGSize, text: NSAttributedString, showIcon: Bool, tailSize: CGFloat, padding: UIEdgeInsets) { + let tailPadding = UIEdgeInsets(allEdges: tailSize) + + var iconToText = 0.0 + var iconFrame = CGRect.zero + if showIcon { + iconFrame = CGRect(padding: padding + tailPadding, size: CGSize(width: 24, height: 24)) + iconToText = 5.0 + } + + let textPadding = padding + tailPadding + UIEdgeInsets(top: 0, left: iconFrame.width + iconToText, bottom: 0, right: 0) + let textAvailableSize = availableSize - textPadding + var textSize = text.boundingRect( + with: textAvailableSize, + options: .usesLineFragmentOrigin, context: nil + ).size.roundedUp() + textSize.height = max(textSize.height, iconFrame.height) + iconFrame.size.height = textSize.height + icon = iconFrame + label = CGRect(padding: textPadding, size: textSize) + bubble = CGRect(padding: tailPadding, size: textSize + textPadding - tailPadding) + size = bubble.size + tailPadding + } + } + + override func sizeThatFits(_ size: CGSize) -> CGSize { + Layout(availableSize: size, text: attributedText, showIcon: hint != nil, tailSize: tailSize, padding: padding).size + } + + override func layoutSubviews() { + super.layoutSubviews() + + let layout = Layout(availableSize: bounds.size, text: attributedText, showIcon: hint != nil, tailSize: tailSize, padding: padding) + label.frame = layout.label + iconView.frame = layout.icon + + let calloutPath = UIBezierPath.calloutPath(size: bounds.size, tailSize: tailSize, cornerRadius: cornerRadius, anchor: anchor ?? .center) + backgroundShape.path = calloutPath.cgPath + backgroundShape.frame = bounds + } +} + +func + (lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> UIEdgeInsets { + return UIEdgeInsets(top: lhs.top + rhs.top, left: lhs.left + rhs.left, bottom: lhs.bottom + rhs.bottom, right: lhs.right + rhs.right) +} + +func + (lhs: CGSize, rhs: UIEdgeInsets) -> CGSize { + return CGSize(width: lhs.width + rhs.left + rhs.right, height: lhs.height + rhs.top + rhs.bottom) +} + +func - (lhs: CGSize, rhs: UIEdgeInsets) -> CGSize { + return lhs + -rhs +} + +func + (lhs: CGSize, rhs: CGSize) -> CGSize { + return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height) +} + +func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { + CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) +} + +func * (lhs: CGPoint, rhs: CGPoint) -> CGPoint { + CGPoint(x: lhs.x * rhs.x, y: lhs.y * rhs.y) +} + +prefix func - (p: CGPoint) -> CGPoint { + p * CGPoint(x: -1, y: -1) +} + +prefix func - (ins: UIEdgeInsets) -> UIEdgeInsets { + return UIEdgeInsets(top: -ins.top, left: -ins.left, bottom: -ins.bottom, right: -ins.right) +} + +extension CGSize { + func roundedUp() -> CGSize { + CGSize(width: width.rounded(.up), height: height.rounded(.up)) + } +} + +extension CGRect { + init(padding: UIEdgeInsets, size: CGSize) { + self.init(origin: CGPoint(x: padding.left, y: padding.top), size: size) + } +} + +extension UIEdgeInsets { + init(allEdges value: CGFloat) { + self.init(top: value, left: value, bottom: value, right: value) + } +} + +extension NSAttributedString { + static func labelText(_ string: String, size: CGFloat, color: UIColor, bold: Bool = false) -> NSAttributedString { + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineHeightMultiple = 1.11 + paragraphStyle.lineSpacing = 4 + paragraphStyle.alignment = .left + let attributes = [ + NSAttributedString.Key.paragraphStyle: paragraphStyle, + .font: bold ? UIFont.boldSystemFont(ofSize: size) : .systemFont(ofSize: size), + .foregroundColor: color, + ] + return NSAttributedString(string: string, attributes: attributes) + } +} diff --git a/Sources/Examples/All Examples/Annotations/FrameViewAnnotationsExample.swift b/Sources/Examples/All Examples/Annotations/FrameViewAnnotationsExample.swift new file mode 100644 index 000000000000..247ae111796c --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/FrameViewAnnotationsExample.swift @@ -0,0 +1,193 @@ +import UIKit +import MapboxMaps + +final class FrameViewAnnotationsExample: UIViewController, ExampleProtocol { + private enum Animator { + case flyTo, easeTo, viewport + } + + private var cancelables = Set() + private var flyToButton: UIButton! + private var easeToButton: UIButton! + private var viewportButton: UIButton! + private var resetButton: UIButton! + private var annotations = [ViewAnnotation]() + + private var mapView: MapView! + private let initialCamera = CameraOptions( + center: .random, + padding: UIEdgeInsets(top: .random(in: 0...20), left: .random(in: 0...20), bottom: .random(in: 0...20), right: .random(in: 0...20)), + zoom: 0, + bearing: 0, + pitch: 0 + ) + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + + mapView = MapView(frame: view.bounds, mapInitOptions: MapInitOptions(cameraOptions: initialCamera)) + // Camera + try! mapView.mapboxMap.setProjection(StyleProjection(name: .mercator)) + let buttonsView = makeButtonsView() + + view.addSubview(mapView) + view.addSubview(buttonsView) + + mapView.translatesAutoresizingMaskIntoConstraints = false + buttonsView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + buttonsView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + buttonsView.leadingAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.leadingAnchor), + buttonsView.bottomAnchor.constraint(equalTo: mapView.ornaments.logoView.topAnchor, constant: -10), + ]) + + addAnnotations() + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + // The below line is used for internal testing purposes only. + self?.finish() + }.store(in: &cancelables) + } + + private func makeButtonsView() -> UIView { + func makeButton(title: String, selector: Selector) -> UIButton { + let button = UIButton() + button.setTitle(title, for: .normal) + button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20) + button.backgroundColor = .black + button.addTarget(self, action: selector, for: .touchUpInside) + return button + } + + flyToButton = makeButton(title: "FlyTo", selector: #selector(flyToButtonTapped(_:))) + easeToButton = makeButton(title: "EaseTo", selector: #selector(easeToButtonTapped(_:))) + viewportButton = makeButton(title: "Viewport", selector: #selector(viewportButtonTapped(_:))) + resetButton = makeButton(title: "Reset camera", selector: #selector(resetButtonTapped(_:))) + + let buttonsView = UIStackView(arrangedSubviews: [flyToButton, easeToButton, viewportButton, resetButton]) + buttonsView.axis = .horizontal + buttonsView.spacing = 10 + buttonsView.distribution = .fillEqually + + resetButton.isHidden = true + + return buttonsView + } + + @objc private func flyToButtonTapped(_ sender: UIButton) { + frameViewAnnotation(with: .flyTo, sender: sender) + } + + @objc private func easeToButtonTapped(_ sender: UIButton) { + frameViewAnnotation(with: .easeTo, sender: sender) + } + + @objc private func viewportButtonTapped(_ sender: UIButton) { + frameViewAnnotation(with: .viewport, sender: sender) + } + + @objc private func resetButtonTapped(_ sender: UIButton) { + mapView.viewport.idle() + mapView.mapboxMap.setCamera(to: initialCamera) + resetButton.isHidden = true + flyToButton.isHidden = false + easeToButton.isHidden = false + viewportButton.isHidden = false + } + + private func frameViewAnnotation(with animator: Animator, sender: UIButton) { + flyToButton.isHidden = true + easeToButton.isHidden = true + viewportButton.isHidden = true + resetButton.isHidden = false + + let camera = self.mapView.viewAnnotations.camera( + forAnnotations: annotations, + padding: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10), + bearing: nil, + pitch: nil + )! + + switch animator { + case .flyTo: + mapView.camera.fly(to: camera, duration: 1) + case .easeTo: + mapView.camera.ease(to: camera, duration: 1) + case .viewport: + let bounds = mapView.mapboxMap.coordinateBounds(for: camera) + let overviewViewportStateOptions = OverviewViewportStateOptions( + geometry: MultiPoint([bounds.northeast, bounds.southeast, bounds.southwest, bounds.northwest]), + bearing: camera.bearing, + pitch: camera.pitch, + animationDuration: 1 + ) + let overviewViewportState = mapView.viewport.makeOverviewViewportState(options: overviewViewportStateOptions) + mapView.viewport.transition(to: overviewViewportState) + } + } + + private func addAnnotations() { + func makeView(text: String) -> UIView { + let view = UIView() + view.backgroundColor = .white + view.layer.shadowOpacity = 0.25 + view.layer.shadowRadius = 8 + view.layer.shadowOffset = CGSize(width: 0, height: 2) + view.layer.cornerRadius = 8 + let label = UILabel() + label.text = text + label.textColor = .black + label.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(label) + NSLayoutConstraint.activate([ + label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 4), + label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -4), + label.topAnchor.constraint(equalTo: view.topAnchor, constant: 4), + label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -4), + ]) + return view + } + + self.annotations = annotationData.map { + let view = makeView(text: $0.name) + let annotation = ViewAnnotation(coordinate: $0.coordinate, view: view) + annotation.variableAnchors = [.init(anchor: $0.anchor)] + self.mapView.viewAnnotations.add(annotation) + return annotation + } + } + + private struct AnnotationInfo { + var name: String + var coordinate: CLLocationCoordinate2D + var anchor: ViewAnnotationAnchor + } + + private let annotationData: [AnnotationInfo] = [ + AnnotationInfo(name: "Saigon", coordinate: .init(latitude: 10.823099, longitude: 106.629662), + anchor: .top), + AnnotationInfo(name: "Hanoi", coordinate: .init(latitude: 21.027763, longitude: 105.834160), + anchor: .bottomLeft), + AnnotationInfo(name: "Tokyo", coordinate: .init(latitude: 35.689487, longitude: 139.691711), + anchor: .right), + AnnotationInfo(name: "Bangkok", coordinate: .init(latitude: 13.756331, longitude: 100.501762), + anchor: .topRight), + AnnotationInfo(name: "Jakarta", coordinate: .init(latitude: -6.175110, longitude: 106.865036), + anchor: .topLeft) + ] +} + +private func annotation(geometry: GeometryConvertible, width: CGFloat, height: CGFloat, anchor: ViewAnnotationAnchor) -> ViewAnnotationOptions { + .init( + annotatedFeature: .geometry(geometry), + width: width, + height: height, + allowOverlap: true, + variableAnchors: [ViewAnnotationAnchorConfig(anchor: anchor)]) +} diff --git a/Sources/Examples/All Examples/Annotations/IconSizeChangeExample.swift b/Sources/Examples/All Examples/Annotations/IconSizeChangeExample.swift new file mode 100644 index 000000000000..451e37e8200e --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/IconSizeChangeExample.swift @@ -0,0 +1,139 @@ +import UIKit +import MapboxMaps + +final class IconSizeChangeExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var markerSelected = false + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + let initialPosition = CLLocationCoordinate2D(latitude: 42.354950, longitude: -71.065634) + let cameraOptions = CameraOptions(center: initialPosition, zoom: 11) + let initOptions = MapInitOptions(cameraOptions: cameraOptions) + mapView = MapView(frame: view.bounds, mapInitOptions: initOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allows the delegate to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + + // Set up the example + self?.setupExample() + + // The below line is used for internal testing purposes only. + self?.finish() + }.store(in: &cancelables) + + mapView.mapboxMap.loadStyle(.dark) + } + + private func setupExample() { + let markerFeatures = [ + CLLocationCoordinate2D(latitude: 42.354950, longitude: -71.065634), // Boston Common Park + CLLocationCoordinate2D(latitude: 42.346645, longitude: -71.097293), // Fenway Park + CLLocationCoordinate2D(latitude: 42.363725, longitude: -71.053694) // The Paul Revere House + ].map({ Feature(geometry: Point($0)) }) + + // Create a GeoJSON data source for markers + var markerSource = GeoJSONSource(id: Constants.markerSourceId) + markerSource.data = .featureCollection(FeatureCollection(features: markerFeatures)) + try? mapView.mapboxMap.addSource(markerSource) + + // Add marker image to the map + try? mapView.mapboxMap.addImage(UIImage(named: "intermediate-pin")!, id: Constants.blueMarkerImageId) + + // Create a symbol layer for markers + var markerLayer = SymbolLayer(id: Constants.markerLayerId, source: Constants.markerSourceId) + markerLayer.iconImage = .constant(.name(Constants.blueMarkerImageId)) + markerLayer.iconAllowOverlap = .constant(true) + markerLayer.iconOffset = .constant([0, 12]) + // Adding an offset so that the bottom of the blue icon gets fixed to the coordinate, rather than the + // middle of the icon being fixed to the coordinate point. + markerLayer.iconOffset = .constant([0, -9]) + + try? mapView.mapboxMap.addLayer(markerLayer) + + // Create a GeoJSON source for the selected marker + var selectedMarkerSource = GeoJSONSource(id: Constants.selectedMarkerSourceId) + selectedMarkerSource.data = .geometry(.point(Point(CLLocationCoordinate2D()))) + try? mapView.mapboxMap.addSource(selectedMarkerSource) + + // Create a symbol layer for the selected marker + var selectedMarkerLayer = SymbolLayer(id: Constants.selectedMarkerLayerId, source: Constants.selectedMarkerSourceId) + selectedMarkerLayer.iconImage = .constant(.name(Constants.blueMarkerImageId)) + selectedMarkerLayer.iconAllowOverlap = .constant(true) + // Adding an offset so that the bottom of the blue icon gets fixed to the coordinate, rather than the + // middle of the icon being fixed to the coordinate point. + selectedMarkerLayer.iconOffset = .constant([0, -9]) + + try? mapView.mapboxMap.addLayer(selectedMarkerLayer) + + // Add a handler for tap on the selected marker layer. + mapView.gestures.onLayerTap(Constants.selectedMarkerLayerId) { [weak self] _, context in + guard let self else { return false } + if !self.markerSelected { + self.updateSelectedMarker(atPoint: context.point) + } + return true + }.store(in: &cancelables) + + // Add a handler for on map, except taps on selected marker. + mapView.gestures.onMapTap.observe { [weak self] context in + self?.updateSelectedMarker(atPoint: context.point) + }.store(in: &cancelables) + } + + private func updateSelectedMarker(atPoint point: CGPoint) { + let options = RenderedQueryOptions(layerIds: [Constants.markerLayerId], filter: nil) + mapView.mapboxMap.queryRenderedFeatures(with: point, options: options) { [weak self] result in + guard let self = self else { return } + + switch result { + case .success(let features): + if features.isEmpty { + if self.markerSelected { + self.updateMarker(selected: false) + } + return + } + + if let geometry = features.first?.queriedFeature.feature.geometry { + self.mapView.mapboxMap.updateGeoJSONSource(withId: Constants.selectedMarkerSourceId, + geoJSON: .geometry(geometry)) + } + + if self.markerSelected { + self.updateMarker(selected: false) + } + + if !features.isEmpty { + self.updateMarker(selected: true) + } + case .failure(let error): + self.showAlert(with: "An error occurred: \(error.localizedDescription)") + } + } + } + + private func updateMarker(selected: Bool) { + try? mapView.mapboxMap.updateLayer( + withId: Constants.selectedMarkerLayerId, + type: SymbolLayer.self, + update: { (layer: inout SymbolLayer) throws in + layer.iconSize = .constant(selected ? 2 : 1) + }) + markerSelected = selected + } +} + +extension IconSizeChangeExample { + private enum Constants { + static let blueMarkerImageId = "blue-marker" + static let markerLayerId = "marker-layer" + static let markerSourceId = "marker-source" + static let selectedMarkerLayerId = "selected-marker-layer" + static let selectedMarkerSourceId = "selected-marker" + } +} diff --git a/Apps/Examples/Examples/All Examples/Annotations/LineAnnotationExample.swift b/Sources/Examples/All Examples/Annotations/LineAnnotationExample.swift similarity index 84% rename from Apps/Examples/Examples/All Examples/Annotations/LineAnnotationExample.swift rename to Sources/Examples/All Examples/Annotations/LineAnnotationExample.swift index c5ebd43b33c9..c960bc2ca47d 100644 --- a/Apps/Examples/Examples/All Examples/Annotations/LineAnnotationExample.swift +++ b/Sources/Examples/All Examples/Annotations/LineAnnotationExample.swift @@ -1,27 +1,28 @@ import UIKit import MapboxMaps -@objc(LineAnnotationExample) final class LineAnnotationExample: UIViewController, ExampleProtocol { - private var mapView: MapView! + private var cancelables = Set() override func viewDidLoad() { super.viewDidLoad() - mapView = MapView(frame: view.bounds) + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 0, longitude: 0), zoom: 2) + let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .streets) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView) // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in // Set up the example self?.setupExample() // The below line is used for internal testing purposes only. self?.finish() - } + }.store(in: &cancelables) } private func setupExample() { @@ -39,7 +40,7 @@ final class LineAnnotationExample: UIViewController, ExampleProtocol { // random add lines across the globe var randomCoordinates = [CLLocationCoordinate2D]() - for _ in 0...400 { + for _ in 0..<400 { randomCoordinates.append(.random) } diff --git a/Sources/Examples/All Examples/Annotations/MultipleGeometriesExample.swift b/Sources/Examples/All Examples/Annotations/MultipleGeometriesExample.swift new file mode 100644 index 000000000000..7487ddd043d0 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/MultipleGeometriesExample.swift @@ -0,0 +1,107 @@ +import UIKit +import MapboxMaps + +final class MultipleGeometriesExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // Set the center coordinate and zoom level. + let centerCoordinate = CLLocationCoordinate2D(latitude: 38.93490939383946, longitude: -77.03619251024163) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 11)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allow the view controller to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + self.addGeoJSONSource() + self.addPolygonLayer() + self.addLineStringLayer() + self.addPointLayer() + + // The below line is used for internal testing purposes only. + self.finish() + }.store(in: &cancelables) + } + + // Load GeoJSON file from local bundle and decode into a `FeatureCollection`. + private func decodeGeoJSON(from fileName: String) throws -> FeatureCollection? { + guard let path = Bundle.main.path(forResource: fileName, ofType: "geojson") else { + preconditionFailure("File '\(fileName)' not found.") + } + + let filePath = URL(fileURLWithPath: path) + + var featureCollection: FeatureCollection? + + do { + let data = try Data(contentsOf: filePath) + featureCollection = try JSONDecoder().decode(FeatureCollection.self, from: data) + } catch { + print("Error parsing data: \(error)") + } + + return featureCollection + } + + private func addGeoJSONSource() { + // Attempt to decode GeoJSON from file bundled with application. + guard let featureCollection = try? decodeGeoJSON(from: "GeoJSONSourceExample") else { return } + + // Create a GeoJSON data source. + var geoJSONSource = GeoJSONSource(id: Constants.geoJSONDataSourceIdentifier) + geoJSONSource.data = .featureCollection(featureCollection) + try! mapView.mapboxMap.addSource(geoJSONSource) + } + + /// Create and style a FillLayer that uses the Polygon Feature's coordinates in the GeoJSON data + private func addPolygonLayer() { + var polygonLayer = FillLayer(id: "fill-layer", source: Constants.geoJSONDataSourceIdentifier) + polygonLayer.filter = Exp(.eq) { + "$type" + "Polygon" + } + polygonLayer.fillColor = .constant(StyleColor(red: 68, green: 105, blue: 247, alpha: 1)!) + polygonLayer.fillOpacity = .constant(0.3) + try! mapView.mapboxMap.addLayer(polygonLayer) + } + + private func addLineStringLayer() { + // Create and style a LineLayer that uses the Line String Feature's coordinates in the GeoJSON data + var lineLayer = LineLayer(id: "line-layer", source: Constants.geoJSONDataSourceIdentifier) + lineLayer.filter = Exp(.eq) { + "$type" + "LineString" + } + lineLayer.lineColor = .constant(StyleColor(.red)) + lineLayer.lineWidth = .constant(2) + try! mapView.mapboxMap.addLayer(lineLayer) + } + + private func addPointLayer() { + // Create a circle layer associated with the GeoJSON data source, + // filter it so that only the point data is shown, + // and apply basic styling to it. + var circleLayer = CircleLayer(id: "circle-layer", source: Constants.geoJSONDataSourceIdentifier) + circleLayer.filter = Exp(.eq) { + "$type" + "Point" + } + circleLayer.circleColor = .constant(StyleColor(.red)) + circleLayer.circleRadius = .constant(6.0) + circleLayer.circleStrokeWidth = .constant(2.0) + circleLayer.circleStrokeColor = .constant(StyleColor(.black)) + try! mapView.mapboxMap.addLayer(circleLayer) + } +} + +extension MultipleGeometriesExample { + private enum Constants { + static let geoJSONDataSourceIdentifier = "geoJSON-data-source" + } +} diff --git a/Sources/Examples/All Examples/Annotations/PointAnnotationClusteringExample.swift b/Sources/Examples/All Examples/Annotations/PointAnnotationClusteringExample.swift new file mode 100644 index 000000000000..12dbf50903f2 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/PointAnnotationClusteringExample.swift @@ -0,0 +1,163 @@ +import UIKit +import MapboxMaps + +final class PointAnnotationClusteringExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private let clusterLayerID = "fireHydrantClusters" + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // Center the map over Washington, D.C. + let center = CLLocationCoordinate2D(latitude: 38.889215, longitude: -77.039354) + let cameraOptions = CameraOptions(center: center, zoom: 11) + let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .light) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + view.addSubview(mapView) + + // Add the source and style layers once the map has loaded. + mapView.mapboxMap.onMapLoaded.observeNext { _ in + self.addPointAnnotations() + }.store(in: &cancelables) + } + + func addPointAnnotations() { + // The image named `fire-station-11` is included in the app's Assets.xcassets bundle. + let image = UIImage(named: "fire-station-11")! + DispatchQueue.global(qos: .userInitiated).async { + // Fire_Hydrants.geojson contains information about fire hydrants in Washington, D.C. + // It was downloaded on 6/10/21 from https://opendata.dc.gov/datasets/DCGIS::fire-hydrants/about + // Decode the GeoJSON into a feature collection on a background thread + guard let featureCollection = try? self.decodeGeoJSON(from: "Fire_Hydrants") else { + return + } + + // Create an array of annotations for each fire hydrant + var annotations = [PointAnnotation]() + for feature in featureCollection.features { + guard let geometry = feature.geometry, case let Geometry.point(point) = geometry else { + return + } + var pointAnnotation = PointAnnotation(coordinate: point.coordinates) + pointAnnotation.image = .init(image: image, name: "fire-station-11") + pointAnnotation.tapHandler = { [id = pointAnnotation.id] _ in + print("tapped annotation: \(id)") + return true + } + annotations.append(pointAnnotation) + } + DispatchQueue.main.async { + self.createClusters(annotations: annotations) + } + } + } + + func createClusters(annotations: [PointAnnotation]) { + // Use a step expressions (https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-step) + // with three steps to implement three sizes of circles: + // * 25 when point count is less than 50 + // * 30 when point count is between 50 and 100 + // * 35 when point count is greater than or equal to 100 + let circleRadiusExpression = Exp(.step) { + Exp(.get) {"point_count"} + 25 + 50 + 30 + 100 + 35 + } + + // Use a similar expression to get different colors of circles: + // * yellow when point count is less than 10 + // * green when point count is between 10 and 50 + // * cyan when point count is between 50 and 100 + // * red when point count is between 100 and 150 + // * orange when point count is between 150 and 250 + // * light pink when point count is greater than or equal to 250 + let circleColorExpression = Exp(.step) { + Exp(.get) {"point_count"} + UIColor.yellow + 10 + UIColor.green + 50 + UIColor.cyan + 100 + UIColor.red + 150 + UIColor.orange + 250 + UIColor.lightPink + } + + // Create expression to get the total count of hydrants in a cluster + let sumExpression = Exp { + Exp(.sum) { + Exp(.accumulated) + Exp(.get) { "sum" } + } + 1 + } + + // Create a cluster property to add to each cluster + let clusterProperties: [String: Exp] = [ + "sum": sumExpression + ] + + // If a feature has the point_count property then prepend "Count:" and display the sum of hydrants in the cluster + // The sum property is added here for demonstration, you can use the built-in "point_count" + // property instead: Exp(.get) {"point_count"} + let textFieldExpression = Exp(.switchCase) { + Exp(.has) { "point_count" } + Exp(.concat) { + Exp(.string) { "Count:\n" } + Exp(.get) {"sum"} + } + Exp(.string) { "" } + } + + // Select the options for clustering and pass them to the PointAnnotationManager to display + let clusterOptions = ClusterOptions(circleRadius: .expression(circleRadiusExpression), + circleColor: .expression(circleColorExpression), + textColor: .constant(StyleColor(.black)), + textField: .expression(textFieldExpression), + clusterRadius: 75, + clusterProperties: clusterProperties) + let pointAnnotationManager = mapView.annotations.makePointAnnotationManager(id: clusterLayerID, clusterOptions: clusterOptions) + pointAnnotationManager.annotations = annotations + pointAnnotationManager.onClusterTap = { [weak self] context in + self?.mapView.camera.ease(to: CameraOptions(center: context.coordinate, zoom: context.expansionZoom), duration: 1) + } + + // Additional properties on the text and circle layers can be modified like this below + // To modify the text layer use: "mapbox-iOS-cluster-text-layer-manager-" and SymbolLayer.self + do { + try mapView.mapboxMap.updateLayer(withId: "mapbox-iOS-cluster-circle-layer-manager-" + clusterLayerID, type: CircleLayer.self) { layer in + layer.circleStrokeColor = .constant(StyleColor(.black)) + layer.circleStrokeWidth = .constant(3) + } + } catch { + print("Updating the layer failed: \(error.localizedDescription)") + } + + finish() + } + + // Load GeoJSON file from local bundle and decode into a `FeatureCollection`. + func decodeGeoJSON(from fileName: String) throws -> FeatureCollection? { + guard let path = Bundle.main.path(forResource: fileName, ofType: "geojson") else { + preconditionFailure("File '\(fileName)' not found.") + } + let filePath = URL(fileURLWithPath: path) + var featureCollection: FeatureCollection? + do { + let data = try Data(contentsOf: filePath) + featureCollection = try JSONDecoder().decode(FeatureCollection.self, from: data) + } catch { + print("Error parsing data: \(error)") + } + return featureCollection + } +} diff --git a/Apps/Examples/Examples/All Examples/Annotations/PolygonAnnotationExample.swift b/Sources/Examples/All Examples/Annotations/PolygonAnnotationExample.swift similarity index 87% rename from Apps/Examples/Examples/All Examples/Annotations/PolygonAnnotationExample.swift rename to Sources/Examples/All Examples/Annotations/PolygonAnnotationExample.swift index 2539a3b337bc..715965c63fdf 100644 --- a/Apps/Examples/Examples/All Examples/Annotations/PolygonAnnotationExample.swift +++ b/Sources/Examples/All Examples/Annotations/PolygonAnnotationExample.swift @@ -1,8 +1,9 @@ import MapboxMaps +import UIKit -@objc(PolygonAnnotationExample) final class PolygonAnnotationExample: UIViewController, ExampleProtocol { private var mapView: MapView! + private var cancelables = Set() override func viewDidLoad() { super.viewDidLoad() @@ -15,12 +16,12 @@ final class PolygonAnnotationExample: UIViewController, ExampleProtocol { view.addSubview(mapView) // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in + mapView.mapboxMap.onMapLoaded.observeNext { _ in self.setupExample() // The below line is used for internal testing purposes only. self.finish() - } + }.store(in: &cancelables) } // Wait for the map to load before adding an annotation. @@ -31,7 +32,6 @@ final class PolygonAnnotationExample: UIViewController, ExampleProtocol { // (`mapView.annotations`) until you explicitly destroy them // by calling `mapView.annotations.removeAnnotationManager(withId:)` let polygonAnnotationManager = mapView.annotations.makePolygonAnnotationManager() - polygonAnnotationManager.delegate = self // Create the polygon annotation var polygonAnnotation = PolygonAnnotation(polygon: makePolygon()) @@ -40,6 +40,14 @@ final class PolygonAnnotationExample: UIViewController, ExampleProtocol { polygonAnnotation.fillColor = StyleColor(.red) polygonAnnotation.fillOpacity = 0.8 + // Enable the polygon annotation to be dragged + polygonAnnotation.isDraggable = true + + polygonAnnotation.tapHandler = { _ in + print("polygon is tapped") + return true + } + // Add the polygon annotation to the manager polygonAnnotationManager.annotations = [polygonAnnotation] } @@ -71,9 +79,3 @@ final class PolygonAnnotationExample: UIViewController, ExampleProtocol { return Polygon(outerRing: outerRing, innerRings: [innerRing]) } } - -extension PolygonAnnotationExample: AnnotationInteractionDelegate { - func annotationManager(_ manager: AnnotationManager, didDetectTappedAnnotations annotations: [Annotation]) { - print("AnnotationManager did detect tapped annotations: \(annotations)") - } -} diff --git a/Sources/Examples/All Examples/Annotations/SymbolClusteringExample.swift b/Sources/Examples/All Examples/Annotations/SymbolClusteringExample.swift new file mode 100644 index 000000000000..5fe109008279 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/SymbolClusteringExample.swift @@ -0,0 +1,188 @@ +import UIKit +import MapboxMaps + +final class SymbolClusteringExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // Create a `MapView` centered over Washington, DC. + let center = CLLocationCoordinate2D(latitude: 38.889215, longitude: -77.039354) + let cameraOptions = CameraOptions(center: center, zoom: 11) + let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .dark) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + view.addSubview(mapView) + + // Add the source and style layers once the map has loaded. + mapView.mapboxMap.onMapLoaded.observeNext { _ in + self.addSymbolClusteringLayers() + }.store(in: &cancelables) + + // Add tap handers to and clustered and unclustered layers. + for layer in ["unclustered-point-layer", "clustered-circle-layer"] { + mapView.gestures.onLayerTap(layer) { [weak self] queriedFeature, _ in + return self?.handleTap(queriedFeature: queriedFeature) ?? false + }.store(in: &cancelables) + } + } + + func addSymbolClusteringLayers() { + // The image named `fire-station-11` is included in the app's Assets.xcassets bundle. + // In order to recolor an image, you need to add a template image to the map's style. + // The image's rendering mode can be set programmatically or in the asset catalogue. + let image = UIImage(named: "fire-station-11")!.withRenderingMode(.alwaysTemplate) + + // Add the image tp the map's style. Set `sdf` to `true`. This allows the icon images to be recolored. + // For more information about `SDF`, or Signed Distance Fields, see + // https://docs.mapbox.com/help/troubleshooting/using-recolorable-images-in-mapbox-maps/#what-are-signed-distance-fields-sdf + try! mapView.mapboxMap.addImage(image, id: "fire-station-icon", sdf: true) + + // Fire_Hydrants.geojson contains information about fire hydrants in the District of Columbia. + // It was downloaded on 6/10/21 from https://opendata.dc.gov/datasets/DCGIS::fire-hydrants/about + let url = Bundle.main.url(forResource: "Fire_Hydrants", withExtension: "geojson")! + + // Create a GeoJSONSource using the previously specified URL. + var source = GeoJSONSource(id: "fire-hydrant-source") + source.data = .url(url) + + // Enable clustering for this source. + source.cluster = true + source.clusterRadius = 75 + + // Create expression to identify the max flow rate of one hydrant in the cluster + // ["max", ["get", "FLOW"]] + let maxExpression = Exp(.max) {Exp(.get) { "FLOW" }} + + // Create expression to determine if a hydrant with EngineID E-9 is in the cluster + // ["any", ["==", ["get", "ENGINEID"], "E-9"]] + let ine9Expression = Exp(.any) { + Exp(.eq) { + Exp(.get) { "ENGINEID" } + "E-9" + } + } + + // Create expression to get the sum of all of the flow rates in the cluster + // [["+", ["accumulated"], ["get", "sum"]], ["get", "FLOW"]] + let sumExpression = Exp { + Exp(.sum) { + Exp(.accumulated) + Exp(.get) { "sum" } + } + Exp(.get) { "FLOW" } + } + + // Add the expressions to the cluster as ClusterProperties so they can be accessed below + let clusterProperties: [String: Exp] = [ + "max": maxExpression, + "in_e9": ine9Expression, + "sum": sumExpression + ] + source.clusterProperties = clusterProperties + + let clusteredLayer = createClusteredLayer(source: source.id) + let unclusteredLayer = createUnclusteredLayer(source: source.id) + + // `clusterCountLayer` is a `SymbolLayer` that represents the point count within individual clusters. + let clusterCountLayer = createNumberLayer(source: source.id) + + // Add the source and two layers to the map. + try! mapView.mapboxMap.addSource(source) + try! mapView.mapboxMap.addLayer(clusteredLayer) + try! mapView.mapboxMap.addLayer(unclusteredLayer, layerPosition: .below(clusteredLayer.id)) + try! mapView.mapboxMap.addLayer(clusterCountLayer) + + // This is used for internal testing purposes only and can be excluded + // from your implementation. + finish() + } + + func createClusteredLayer(source: String) -> CircleLayer { + // Create a symbol layer to represent the clustered points. + var clusteredLayer = CircleLayer(id: "clustered-circle-layer", source: source) + + // Filter out unclustered features by checking for `point_count`. This + // is added to clusters when the cluster is created. If your source + // data includes a `point_count` property, consider checking + // for `cluster_id`. + clusteredLayer.filter = Exp(.has) { "point_count" } + + // Set the color of the icons based on the number of points within + // a given cluster. The first value is a default value. + clusteredLayer.circleColor = .expression(Exp(.step) { + Exp(.get) { "point_count" } + UIColor.systemGreen + 50 + UIColor.systemBlue + 100 + UIColor.systemRed + }) + + clusteredLayer.circleRadius = .constant(25) + + return clusteredLayer + } + + func createUnclusteredLayer(source: String) -> SymbolLayer { + // Create a symbol layer to represent the points that aren't clustered. + var unclusteredLayer = SymbolLayer(id: "unclustered-point-layer", source: source) + + // Filter out clusters by checking for `point_count`. + unclusteredLayer.filter = Exp(.not) { + Exp(.has) { "point_count" } + } + unclusteredLayer.iconImage = .constant(.name("fire-station-icon")) + unclusteredLayer.iconColor = .constant(StyleColor(.white)) + + // Rotate the icon image based on the recorded water flow. + // The `mod` operator allows you to use the remainder after dividing + // the specified values. + unclusteredLayer.iconRotate = .expression(Exp(.mod) { + Exp(.get) { "FLOW" } + 360 + }) + + return unclusteredLayer + } + + func createNumberLayer(source: String) -> SymbolLayer { + var numberLayer = SymbolLayer(id: "cluster-count-layer", source: source) + + // check whether the point feature is clustered + numberLayer.filter = Exp(.has) { "point_count" } + + // Display the value for 'point_count' in the text field + numberLayer.textField = .expression(Exp(.get) { "point_count" }) + numberLayer.textSize = .constant(12) + return numberLayer + } + + // Shows cluster or hydrant info. Returns false if couldn't parse data. + private func handleTap(queriedFeature: QueriedFeature) -> Bool { + if let selectedFeatureProperties = queriedFeature.feature.properties, + case let .string(featureInformation) = selectedFeatureProperties["ASSETNUM"], + case let .string(location) = selectedFeatureProperties["LOCATIONDETAIL"] { + showAlert(withTitle: "Hydrant \(featureInformation)", and: "\(location)") + // If the feature is a cluster, it will have `point_count` and `cluster_id` properties. + // These are assigned when the cluster is created. + return true + } + + if let selectedFeatureProperties = queriedFeature.feature.properties, + case let .number(pointCount) = selectedFeatureProperties["point_count"], + case let .number(clusterId) = selectedFeatureProperties["cluster_id"], + case let .number(maxFlow) = selectedFeatureProperties["max"], + case let .number(sum) = selectedFeatureProperties["sum"], + case let .boolean(in_e9) = selectedFeatureProperties["in_e9"] { + // If the tap landed on a cluster, pass the cluster ID and point count to the alert. + let inEngineNine = in_e9 ? "Some hydrants belong to Engine 9." : "No hydrants belong to Engine 9." + showAlert(withTitle: "Cluster ID \(Int(clusterId))", and: "There are \(Int(pointCount)) hydrants in this cluster. The highest water flow is \(Int(maxFlow)) and the collective flow is \(Int(sum)). \(inEngineNine)") + return true + } + return false + } +} diff --git a/Sources/Examples/All Examples/Annotations/ViewAnnotationAnimationExample.swift b/Sources/Examples/All Examples/Annotations/ViewAnnotationAnimationExample.swift new file mode 100644 index 000000000000..3350778feeb7 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/ViewAnnotationAnimationExample.swift @@ -0,0 +1,100 @@ +import UIKit +import MapboxMaps +import CoreLocation + +final class ViewAnnotationAnimationExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + private lazy var route: LineString = { + let routeURL = Bundle.main.url(forResource: "sf_airport_route", withExtension: "geojson")! + let routeData = try! Data(contentsOf: routeURL) + + return try! JSONDecoder().decode(LineString.self, from: routeData) + }() + private lazy var totalDistance: CLLocationDistance = route.distance() ?? 0 + private var annotation: ViewAnnotation? + private var animationStartTime: TimeInterval = 0 + + override func viewDidLoad() { + super.viewDidLoad() + + // center camera around SF airport + let centerCoordinate = CLLocationCoordinate2D(latitude: 37.7080221537549, longitude: -122.39470445734368) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 11)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.onStyleLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + + self.setupExample() + }.store(in: &cancelables) + } + + private func setupExample() { + var source = GeoJSONSource(id: "route-source") + source.data = .geometry(route.geometry) + + try! mapView.mapboxMap.addSource(source) + + var layer = LineLayer(id: "route-layer", source: source.id) + layer.lineColor = .constant(StyleColor(UIColor.systemPink)) + layer.lineWidth = .constant(4) + + try! mapView.mapboxMap.addLayer(layer) + + let view = UIImageView(image: UIImage(named: "intermediate-pin")) + view.contentMode = .scaleAspectFit + let annotation = ViewAnnotation(coordinate: route.coordinates.first!, view: view) + annotation.variableAnchors = [.init(anchor: .bottom, offsetY: -12)] + mapView.viewAnnotations.add(annotation) + self.annotation = annotation + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + if mapView.mapboxMap.isStyleLoaded { + startAnimation() + } else { + mapView.mapboxMap.onMapLoaded.observeNext { _ in + self.startAnimation() + }.store(in: &cancelables) + } + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + animationStartTime = 0 // stops the animation + } + + private func startAnimation() { + let link = CADisplayLink(target: self, selector: #selector(animateNextStep)) + link.add(to: .main, forMode: .default) + + animationStartTime = CACurrentMediaTime() + } + + @objc private func animateNextStep(_ displayLink: CADisplayLink) { + let animationDuration: TimeInterval = 30 + let progress = (CACurrentMediaTime() - animationStartTime) / animationDuration + let currentDistanceOffset = totalDistance * min(progress, 1) + + defer { + if progress >= 1 { + displayLink.invalidate() + + // The below line is used for internal testing purposes only. + self.finish() + } + } + let currentCoordinate = route.coordinateFromStart(distance: currentDistanceOffset)! + + // set new coordinate to the annotation + annotation?.annotatedFeature = .geometry(Point(currentCoordinate)) + } +} diff --git a/Sources/Examples/All Examples/Annotations/ViewAnnotationBasicExample.swift b/Sources/Examples/All Examples/Annotations/ViewAnnotationBasicExample.swift new file mode 100644 index 000000000000..0cda10a3b6e1 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/ViewAnnotationBasicExample.swift @@ -0,0 +1,43 @@ +import UIKit +import MapboxMaps +import CoreLocation + +final class ViewAnnotationBasicExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + let centerCoordinate = CLLocationCoordinate2D(latitude: 39.7128, longitude: -75.0060) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 7)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + addViewAnnotation(at: mapView.mapboxMap.coordinate(for: mapView.center)) + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + self.finish() + }.store(in: &cancelables) + + mapView.gestures.onMapTap.observe { [weak self] context in + self?.addViewAnnotation(at: context.coordinate) + }.store(in: &cancelables) + } + + private func addViewAnnotation(at coordinate: CLLocationCoordinate2D) { + let annotationView = AnnotationView(frame: CGRect(x: 0, y: 0, width: 100, height: 80)) + annotationView.title = String(format: "lat=%.2f\nlon=%.2f", coordinate.latitude, coordinate.longitude) + let annotation = ViewAnnotation(coordinate: coordinate, view: annotationView) + annotation.allowOverlap = true + annotationView.onClose = { [weak annotation] in annotation?.remove() } + annotationView.onSelect = { [weak annotation] selected in + annotation?.priority = selected ? 1 : 0 + annotation?.setNeedsUpdateSize() + } + mapView.viewAnnotations.add(annotation) + } +} diff --git a/Sources/Examples/All Examples/Annotations/ViewAnnotationMarkerExample.swift b/Sources/Examples/All Examples/Annotations/ViewAnnotationMarkerExample.swift new file mode 100644 index 000000000000..1997e0f37171 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/ViewAnnotationMarkerExample.swift @@ -0,0 +1,169 @@ +import UIKit +import MapboxMaps +import CoreLocation + +final class ViewAnnotationMarkerExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var pointList: [Feature] = [] + private var markerId = 0 + private var annotations = [String: ViewAnnotation]() + private var _topPriority = 0 + private var topPriority: Int { + _topPriority += 1 + return _topPriority + } + + private let image = UIImage(named: "intermediate-pin")! + private lazy var markerHeight: CGFloat = image.size.height + private var cancelables = Set() + + lazy var styleChangeButton: UIButton = { + let button = UIButton(type: .system) + button.setTitleColor(.white, for: .normal) + button.backgroundColor = .systemTeal + button.layer.cornerRadius = 8 + button.clipsToBounds = true + button.setTitle("Change style", for: .normal) + button.addTarget(self, action: #selector(styleChangePressed(sender:)), for: .touchUpInside) + button.translatesAutoresizingMaskIntoConstraints = false + return button + }() + + override func viewDidLoad() { + super.viewDidLoad() + + let centerCoordinate = CLLocationCoordinate2D(latitude: 39.7128, longitude: -75.0060) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 7)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + self.finish() + }.store(in: &cancelables) + + mapView.mapboxMap.onStyleLoaded.observe { [weak self, weak mapView] _ in + guard let self, let mapView else { return } + self.prepareStyle() + self.addMarker(at: mapView.mapboxMap.coordinate(for: mapView.center), viewAnnotation: true) + }.store(in: &cancelables) + + mapView.gestures.onMapLongPress.observe { [weak self] context in + self?.addMarker(at: context.coordinate) + }.store(in: &cancelables) + + mapView.gestures.onLayerTap(Constants.LAYER_ID) { [weak self] feature, _ in + self?.handleMarkerTap(feature.feature) ?? false + }.store(in: &cancelables) + + mapView.mapboxMap.styleURI = .streets + + view.addSubview(styleChangeButton) + + NSLayoutConstraint.activate([ + styleChangeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -32), + styleChangeButton.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -16), + styleChangeButton.widthAnchor.constraint(equalToConstant: 128) + ]) + } + + private func handleMarkerTap(_ feature: Feature) -> Bool { + guard case let .string(id) = feature.identifier else { return false } + + if let annotation = annotations[id] { + annotation.priority = topPriority + return true + } + return addViewAnnotation(to: feature) + } + + @objc private func styleChangePressed(sender: UIButton) { + mapView.mapboxMap.styleURI = mapView.mapboxMap.styleURI == .streets ? .satelliteStreets : .streets + } + + // MARK: - Style management + + private func prepareStyle() { + try? mapView.mapboxMap.addImage(image, id: Constants.BLUE_ICON_ID) + + var source = GeoJSONSource(id: Constants.SOURCE_ID) + source.data = .featureCollection(FeatureCollection(features: pointList)) + try? mapView.mapboxMap.addSource(source) + + if mapView.mapboxMap.styleURI == .satelliteStreets { + var demSource = RasterDemSource(id: "terrain-source") + demSource.url = Constants.TERRAIN_URL_TILE_RESOURCE + try? mapView.mapboxMap.addSource(demSource) + let terrain = Terrain(sourceId: demSource.id) + try? mapView.mapboxMap.setTerrain(terrain) + } + + var layer = SymbolLayer(id: Constants.LAYER_ID, source: Constants.SOURCE_ID) + layer.iconImage = .constant(.name(Constants.BLUE_ICON_ID)) + layer.iconAnchor = .constant(.bottom) + layer.iconOffset = .constant([0, 12]) + layer.iconAllowOverlap = .constant(true) + try? mapView.mapboxMap.addLayer(layer) + } + + // MARK: - Annotation management + + // Add a marker to a custom GeoJSON source: + // This is an optional step to demonstrate the automatic alignment of view annotations + // with features in a data source + private func addMarker(at coordinate: CLLocationCoordinate2D, viewAnnotation: Bool = false) { + let currentId = "\(Constants.MARKER_ID_PREFIX)\(markerId)" + markerId += 1 + var feature = Feature(geometry: Point(coordinate)) + feature.identifier = .string(currentId) + pointList.append(feature) + if (try? mapView.mapboxMap.source(withId: Constants.SOURCE_ID)) != nil { + mapView.mapboxMap.updateGeoJSONSource(withId: Constants.SOURCE_ID, geoJSON: .featureCollection(FeatureCollection(features: pointList))) + } + + if viewAnnotation { + addViewAnnotation(to: feature) + } + } + + // Add a view annotation at a specified location and optionally bind it to an ID of a marker + @discardableResult + private func addViewAnnotation(to feature: Feature) -> Bool { + guard case let .string(id) = feature.identifier, + case let .point(point) = feature.geometry else { return false } + let annotationView = AnnotationView(frame: .zero) + annotationView.title = String(format: "lat=%.2f\nlon=%.2f", point.coordinates.latitude, point.coordinates.longitude) + + let annotation = ViewAnnotation( + annotatedFeature: .layerFeature(layerId: Constants.LAYER_ID, featureId: id), + view: annotationView) + annotation.variableAnchors = [ViewAnnotationAnchorConfig(anchor: .bottom, offsetY: markerHeight - 12)] + mapView.viewAnnotations.add(annotation) + + annotationView.onClose = { [weak annotation, weak self] in + annotation?.remove() + self?.annotations.removeValue(forKey: id) + } + annotationView.onSelect = { [weak annotation, weak self] _ in + guard let self else { return } + annotation?.priority = self.topPriority + annotation?.setNeedsUpdateSize() + } + + annotations[id] = annotation + return true + } +} + +extension ViewAnnotationMarkerExample { + private enum Constants { + static let BLUE_ICON_ID = "blue" + static let SOURCE_ID = "source_id" + static let LAYER_ID = "layer_id" + static let TERRAIN_URL_TILE_RESOURCE = "mapbox://mapbox.mapbox-terrain-dem-v1" + static let MARKER_ID_PREFIX = "view_annotation_" + static let SELECTED_ADD_COEF_PX: CGFloat = 50 + } +} diff --git a/Sources/Examples/All Examples/Annotations/ViewAnnotationWithPointAnnotationExample.swift b/Sources/Examples/All Examples/Annotations/ViewAnnotationWithPointAnnotationExample.swift new file mode 100644 index 000000000000..e1f29cc5a230 --- /dev/null +++ b/Sources/Examples/All Examples/Annotations/ViewAnnotationWithPointAnnotationExample.swift @@ -0,0 +1,87 @@ +import UIKit +import MapboxMaps +import CoreLocation + +final class ViewAnnotationWithPointAnnotationExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var pointAnnotationManager: PointAnnotationManager! + private var cancelables = Set() + private var annotation: ViewAnnotation? + + private let image = UIImage(named: "intermediate-pin")! + private lazy var markerHeight: CGFloat = image.size.height + + override func viewDidLoad() { + super.viewDidLoad() + + let centerCoordinate = CLLocationCoordinate2D(latitude: 39.7128, longitude: -75.0060) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 7)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + pointAnnotationManager = mapView.annotations.makePointAnnotationManager() + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + + try? self.mapView.mapboxMap.addImage(self.image, id: Constants.blueIconId) + self.addPointAndViewAnnotation(at: self.mapView.mapboxMap.coordinate(for: self.mapView.center)) + + // The below line is used for internal testing purposes only. + self.finish() + }.store(in: &cancelables) + + mapView.gestures.onMapTap.observe { [weak self] context in + if let self, self.annotation == nil { + self.addViewAnnotation(at: context.coordinate) + } + }.store(in: &cancelables) + + mapView.mapboxMap.styleURI = .streets + } + + // MARK: - Annotation management + + private func addPointAndViewAnnotation(at coordinate: CLLocationCoordinate2D) { + addPointAnnotation(at: coordinate) + addViewAnnotation(at: coordinate) + } + + private func addPointAnnotation(at coordinate: CLLocationCoordinate2D) { + var pointAnnotation = PointAnnotation(id: Constants.markerId, coordinate: coordinate) + pointAnnotation.iconImage = Constants.blueIconId + pointAnnotation.iconAnchor = .bottom + pointAnnotation.iconOffset = [0, 12] + + pointAnnotationManager.annotations.append(pointAnnotation) + } + + // Add a view annotation at a specified location and optionally bind it to an ID of a marker + private func addViewAnnotation(at coordinate: CLLocationCoordinate2D) { + let annotationView = AnnotationView(frame: CGRect(x: 0, y: 0, width: 128, height: 64)) + annotationView.title = String(format: "lat=%.2f\nlon=%.2f", coordinate.latitude, coordinate.longitude) + let annotation = ViewAnnotation( + annotatedFeature: .layerFeature(layerId: pointAnnotationManager.layerId, featureId: Constants.markerId), + view: annotationView) + annotation.variableAnchors = [ViewAnnotationAnchorConfig(anchor: .bottom, offsetY: markerHeight - 12)] + annotationView.onClose = { [weak self, weak annotation] in + annotation?.remove() + self?.annotation = nil + } + annotationView.onSelect = { [weak annotation] _ in + annotation?.setNeedsUpdateSize() + } + self.annotation = annotation + + mapView.viewAnnotations.add(annotation) + } +} + +extension ViewAnnotationWithPointAnnotationExample { + private enum Constants { + static let blueIconId = "blue" + static let markerId = UUID().uuidString + } +} diff --git a/Sources/Examples/All Examples/BasicLocationPulsingExample.swift b/Sources/Examples/All Examples/BasicLocationPulsingExample.swift new file mode 100644 index 000000000000..29628711503d --- /dev/null +++ b/Sources/Examples/All Examples/BasicLocationPulsingExample.swift @@ -0,0 +1,125 @@ +import Foundation +import UIKit +import MapboxMaps + +/// This example shows a basic usage of sonar-like pulsing circle animation around the 2D puck. +final class BasicLocationPulsingExample: UIViewController, ExampleProtocol { + private var cancelables = Set() + + private lazy var mapView: MapView = { + let view = MapView(frame: view.bounds, mapInitOptions: .init(styleURI: .streets)) + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(mapView) + NSLayoutConstraint.activate([ + mapView.topAnchor.constraint(equalTo: view.topAnchor), + mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) + + var puckConfiguration = Puck2DConfiguration.makeDefault() + puckConfiguration.pulsing = .default + mapView.location.options.puckType = .puck2D(puckConfiguration) + + mapView.location.onLocationChange.observeNext { [weak mapView] newLocation in + guard let mapView, let location = newLocation.last else { return } + mapView.mapboxMap.setCamera(to: .init(center: location.coordinate, zoom: 18)) + }.store(in: &cancelables) + + navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .action) + updateMenu() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + // The below line is used for internal testing purposes only. + finish() + } + + private func enablePulsingWithConstantRadius() { + var puckConfiguration = Puck2DConfiguration.makeDefault() + puckConfiguration.pulsing = .default + mapView.location.options.puckType = .puck2D(puckConfiguration) + } + + private func enablePulsingWithAccuracyRadius() { + var puckConfiguration = Puck2DConfiguration.makeDefault() + puckConfiguration.pulsing = .default + puckConfiguration.pulsing?.radius = .accuracy + mapView.location.options.puckType = .puck2D(puckConfiguration) + } + + private func enableStaticAccuracyCircle() { + var puckConfiguration = Puck2DConfiguration.makeDefault() + puckConfiguration.showsAccuracyRing = true + puckConfiguration.accuracyRingColor = Puck2DConfiguration.Pulsing.default.color.withAlphaComponent(0.3) + mapView.location.options.puckType = .puck2D(puckConfiguration) + } + + private func disablePulsing() { + mapView.location.options.puckType = .puck2D(.makeDefault()) + } + + private func updateMenu() { + let state = mapView.location.options.puckType.map { type -> PuckCircle? in + if case PuckType.puck2D(let config) = type { + return PuckCircle(config: config) + } + return nil + } + + let constantPulseAction = UIAction(title: "Pulse with constant radius", + state: state == .pulseConstant ? .on : .off) { [weak self] _ in + self?.enablePulsingWithConstantRadius() + self?.updateMenu() + + } + let accuracyPulseAction = UIAction(title: "Pulse with accuracy radius", + state: state == .pulseAccuracy ? .on : .off) { [weak self] _ in + self?.enablePulsingWithAccuracyRadius() + self?.updateMenu() + } + let disablePulseAction = UIAction(title: "None", state: state == .disabled ? .on : .off) { [weak self] _ in + self?.disablePulsing() + self?.updateMenu() + } + let staticAccuracyRingAction = UIAction(title: "Static with accuracy radius", + state: state == .static ? .on : .off) { [weak self] _ in + self?.enableStaticAccuracyCircle() + self?.updateMenu() + } + + let menu = UIMenu( + title: "Puck circle", + children: [constantPulseAction, accuracyPulseAction, staticAccuracyRingAction, disablePulseAction] + ) + + navigationItem.rightBarButtonItem?.menu = menu + } +} + +private enum PuckCircle { + case pulseConstant + case pulseAccuracy + case `static` + case disabled + + init(config: Puck2DConfiguration) { + if case .constant = config.pulsing?.radius { + self = .pulseConstant + } else if config.pulsing?.radius == .accuracy { + self = .pulseAccuracy + } else if config.showsAccuracyRing { + self = .static + } else { + self = .disabled + } + } +} diff --git a/Sources/Examples/All Examples/BasicMapExample.swift b/Sources/Examples/All Examples/BasicMapExample.swift new file mode 100644 index 000000000000..a0cc6cf995a8 --- /dev/null +++ b/Sources/Examples/All Examples/BasicMapExample.swift @@ -0,0 +1,31 @@ +import UIKit +@_spi(Experimental) import MapboxMaps + +final class BasicMapExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + + override func viewDidLoad() { + super.viewDidLoad() + + let cameraOptions = CameraOptions( + center: CLLocationCoordinate2D(latitude: 41.879, longitude: -87.635), + zoom: 16, + bearing: 12, + pitch: 60) + let options = MapInitOptions(cameraOptions: cameraOptions) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + mapView.ornaments.options.scaleBar.visibility = .visible + + view.addSubview(mapView) + + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} diff --git a/Sources/Examples/All Examples/BuildingExtrusionsExample.swift b/Sources/Examples/All Examples/BuildingExtrusionsExample.swift new file mode 100644 index 000000000000..6240dd175c01 --- /dev/null +++ b/Sources/Examples/All Examples/BuildingExtrusionsExample.swift @@ -0,0 +1,263 @@ +import UIKit +@_spi(Experimental) import MapboxMaps + +extension UIButton { + static func exampleActionButton() -> UIButton { + let button = UIButton(type: .custom) + button.translatesAutoresizingMaskIntoConstraints = false + button.backgroundColor = .systemBlue + button.tintColor = .white + button.layer.cornerRadius = 4 + button.clipsToBounds = true + button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) + return button + } +} + +final class BuildingExtrusionsExample: UIViewController, ExampleProtocol { + private var cancelables = Set() + + private lazy var lightPositionButton: UIButton = { + let button = UIButton.exampleActionButton() + button.setImage(UIImage(systemName: "flashlight.on.fill"), for: .normal) + button.addTarget(self, action: #selector(lightPositionButtonTapped(_:)), for: .primaryActionTriggered) + return button + }() + + private lazy var lightColorButton: UIButton = { + let button = UIButton.exampleActionButton() + button.setImage(UIImage(systemName: "paintbrush.fill"), for: .normal) + button.addTarget(self, action: #selector(lightColorButtonTapped(_:)), for: .primaryActionTriggered) + return button + }() + + private lazy var heightAlignmentButton: UIButton = { + let button = UIButton.exampleActionButton() + + button.setImage(UIImage(systemName: "align.vertical.top"), for: .normal) + button.setImage(UIImage(systemName: "align.vertical.top.fill"), for: .selected) + button.addTarget(self, action: #selector(heightAlignmentButtonTapped(_:)), for: .primaryActionTriggered) + return button + }() + + private lazy var baseAlignmentButton: UIButton = { + let button = UIButton.exampleActionButton() + + button.setImage(UIImage(systemName: "align.vertical.bottom"), for: .normal) + button.setImage(UIImage(systemName: "align.vertical.bottom.fill"), for: .selected) + button.addTarget(self, action: #selector(baseAlignmentButtonTapped(_:)), for: .primaryActionTriggered) + return button + }() + + private lazy var terrainSwitchButton: UIButton = { + let button = UIButton.exampleActionButton() + button.setImage(UIImage(systemName: "mountain.2"), for: .normal) + button.setImage(UIImage(systemName: "mountain.2.fill"), for: .selected) + + button.addTarget(self, action: #selector(terrainButtonTapped(_:)), for: .primaryActionTriggered) + return button + }() + + lazy var buttons = [ + heightAlignmentButton, + baseAlignmentButton, + lightPositionButton, + lightColorButton, + terrainSwitchButton + ] + + private var ambientLight: AmbientLight = { + var light = AmbientLight() + light.color = .constant(StyleColor(.blue)) + light.intensity = .constant(0.9) + return light + }() + + private var directionalLight: DirectionalLight = { + var light = DirectionalLight() + light.color = .constant(StyleColor(.white)) + light.intensity = .constant(0.9) + light.castShadows = .constant(true) + light.direction = .constant([0.0, 15.0]) + return light + }() + + private var mapView: MapView! + + override func viewDidLoad() { + super.viewDidLoad() + + let options = MapInitOptions(styleURI: .light) + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.onStyleLoaded.observeNext { _ in + self.setupExample() + }.store(in: &cancelables) + + buttons.forEach(view.addSubview(_:)) + terrainSwitchButton.isSelected = isTerrainEnabled + + let accessoryButtonsStackView = UIStackView(arrangedSubviews: buttons) + accessoryButtonsStackView.axis = .vertical + accessoryButtonsStackView.spacing = 20 + accessoryButtonsStackView.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(accessoryButtonsStackView) + + NSLayoutConstraint.activate([ + mapView.ornaments.attributionButton.topAnchor.constraint(equalToSystemSpacingBelow: accessoryButtonsStackView.bottomAnchor, multiplier: 1), + view.trailingAnchor + .constraint(equalToSystemSpacingAfter: accessoryButtonsStackView.trailingAnchor, multiplier: 1) + ]) + } + + internal func setupExample() { + try! addTerrain() + try! addBuildingExtrusions() + + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 40.7135, longitude: -74.0066), + zoom: 15.5, + bearing: -17.6, + pitch: 45) + mapView.mapboxMap.setCamera(to: cameraOptions) + + try! mapView.mapboxMap.setLights(ambient: ambientLight, directional: directionalLight) + + // The below lines are used for internal testing purposes only. + finish() + } + + // See https://docs.mapbox.com/mapbox-gl-js/example/3d-buildings/ for equivalent gl-js example + internal func addBuildingExtrusions() throws { + let wallOnlyThreshold = 20 + let extrudeFilter = Exp(.eq, Exp(.get, "extrude"), "true") + var layer = FillExtrusionLayer(id: "3d-buildings", source: "composite") + .minZoom(15) + .sourceLayer("building") + .fillExtrusionColor(.lightGray) + .fillExtrusionOpacity(0.8) + .fillExtrusionAmbientOcclusionIntensity(0.3) + .fillExtrusionAmbientOcclusionRadius(3.0) + .fillExtrusionHeight(Exp(.get, "height")) + .fillExtrusionBase(Exp(.get, "min_height")) + .fillExtrusionVerticalScale(Exp(.interpolate, Exp(.linear), Exp(.zoom), 15, 0, 15.05, 1)) + + layer.filter = Exp(.all) { + extrudeFilter + Exp(.gt) { + Exp(.get) { "height" } + wallOnlyThreshold + } + } + + try mapView.mapboxMap.addLayer(layer) + + var wallsOnlyExtrusionLayer = layer + .fillExtrusionLineWidth(2) + wallsOnlyExtrusionLayer.id = "3d-buildings-wall" + wallsOnlyExtrusionLayer.filter = Exp(.all) { + extrudeFilter + Exp(.lte) { + Exp(.get) { "height" } + wallOnlyThreshold + } + } + + try mapView.mapboxMap.addLayer(wallsOnlyExtrusionLayer) + } + + func addTerrain() throws { + let terrainSourceID = "mapbox-dem" + + if !mapView.mapboxMap.sourceExists(withId: terrainSourceID) { + try addTerrainSource(id: terrainSourceID) + } + + try mapView.mapboxMap.setTerrain(Terrain(sourceId: terrainSourceID) + .exaggeration(1.5)) + } + + func addTerrainSource(id: String) throws { + var demSource = RasterDemSource(id: id) + demSource.url = "mapbox://mapbox.mapbox-terrain-dem-v1" + // Setting the `tileSize` to 514 provides better performance and adds padding around the outside + // of the tiles. + demSource.tileSize = 514 + demSource.maxzoom = 14.0 + try mapView.mapboxMap.addSource(demSource) + } + + // MARK: - Actions + + var isTerrainEnabled = true + + @objc private func terrainButtonTapped(_ sender: UIButton) { + if isTerrainEnabled { + mapView.mapboxMap.removeTerrain() + } else { + try! addTerrain() + } + + isTerrainEnabled.toggle() + sender.isSelected = isTerrainEnabled + } + + var baseAlignment: FillExtrusionBaseAlignment = .flat + var heightAlignment: FillExtrusionHeightAlignment = .flat + + @objc private func baseAlignmentButtonTapped(_ sender: UIButton) { + if baseAlignment == .flat { + baseAlignment = .terrain + } else { + baseAlignment = .flat + } + sender.backgroundColor = .systemBlue + sender.isSelected = baseAlignment == .terrain + + try! mapView.mapboxMap.updateLayer(withId: "3d-buildings", type: FillExtrusionLayer.self) { layer in + layer.fillExtrusionBaseAlignment = .constant(baseAlignment) + } + } + + @objc private func heightAlignmentButtonTapped(_ sender: UIButton) { + if heightAlignment == .flat { + heightAlignment = .terrain + } else { + heightAlignment = .flat + } + sender.isSelected = heightAlignment == .terrain + + try! mapView.mapboxMap.updateLayer(withId: "3d-buildings", type: FillExtrusionLayer.self) { layer in + layer.fillExtrusionHeightAlignment = .constant(heightAlignment) + } + } + + @objc private func lightColorButtonTapped(_ sender: UIButton) { + if case .constant(let color) = ambientLight.color, color == StyleColor(.red) { + ambientLight.color = .constant(StyleColor(.blue)) + sender.tintColor = .blue + } else { + ambientLight.color = .constant(StyleColor(.red)) + sender.tintColor = .red + } + + try! mapView.mapboxMap.setLights(ambient: ambientLight, directional: directionalLight) + } + + @objc private func lightPositionButtonTapped(_ sender: UIButton) { + let firstPosition: [Double] = [0, 15] + let secondPosition: [Double] = [90, 60] + + if case .constant(let position) = directionalLight.direction, position == firstPosition { + directionalLight.direction = .constant(secondPosition) + sender.imageView?.transform = .identity + } else { + directionalLight.direction = .constant(firstPosition) + sender.imageView?.transform = CGAffineTransform(rotationAngle: 2.0 * .pi / 3.0) + } + + try! mapView.mapboxMap.setLights(ambient: ambientLight, directional: directionalLight) + } +} diff --git a/Sources/Examples/All Examples/CameraAnimationExample.swift b/Sources/Examples/All Examples/CameraAnimationExample.swift new file mode 100644 index 000000000000..160ebba90462 --- /dev/null +++ b/Sources/Examples/All Examples/CameraAnimationExample.swift @@ -0,0 +1,50 @@ +import UIKit +import MapboxMaps + +final class CameraAnimationExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 42.88, longitude: -78.870000), zoom: 6) + let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allows the delegate to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { _ in + + // Center the map camera over New York City. + let centerCoordinate = CLLocationCoordinate2D( + latitude: 40.7128, longitude: -74.0060) + + let newCamera = CameraOptions(center: centerCoordinate, + zoom: 7.0, + bearing: 180.0, + pitch: 15.0) + + self.mapView.camera.ease(to: newCamera, duration: 5.0) { [weak self] (_) in + // The below line is used for internal testing purposes only. + self?.finish() + } + }.store(in: &cancelables) + + mapView.camera + .onCameraAnimatorStarted + .observe { animator in + print("Animator started: \(animator.owner)") + } + .store(in: &cancelables) + + mapView.camera + .onCameraAnimatorFinished + .owned(by: .compass) + .observe { animator in + print("Animator finished: \(animator.owner)") + } + .store(in: &cancelables) + } +} diff --git a/Sources/Examples/All Examples/CameraAnimatorsExample.swift b/Sources/Examples/All Examples/CameraAnimatorsExample.swift new file mode 100644 index 000000000000..d7e4103b6295 --- /dev/null +++ b/Sources/Examples/All Examples/CameraAnimatorsExample.swift @@ -0,0 +1,119 @@ +import UIKit +import MapboxMaps +import os + +final class CameraAnimatorsExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + lazy var barButtonItem = UIBarButtonItem(title: nil, style: .plain, target: self, action: #selector(barButtonTap(_:))) + + // Coordinate in New York City + let newYorkCamera = CameraOptions(center: CLLocationCoordinate2D(latitude: 40.7128, + longitude: -74.0060), + zoom: 16, + bearing: 12, + pitch: 60.340) + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + animationState = .reset + + // Allows the delegate to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { _ in + self.navigationItem.rightBarButtonItem = self.barButtonItem + self.finish() + }.store(in: &cancelables) + } + + enum AnimationState { + case reset + case run + case stop + + func next() -> AnimationState { + switch self { + case .reset: .run + case .run: .stop + case .stop: .reset + } + } + + var title: String { + switch self { + case .reset: "Reset" + case .run: "Run" + case .stop: "Stop" + } + } + } + + var animationState: AnimationState = .reset { + didSet { + barButtonItem.title = animationState.next().title + switch animationState { + case .reset: + // Center the map over New York City. + mapView.mapboxMap.setCamera(to: newYorkCamera) + case .run: + startCameraAnimations() + case .stop: + mapView.camera.cancelAnimations() + } + } + } + + @objc func barButtonTap(_ barButtonItem: UIBarButtonItem) { + animationState = animationState.next() + } + + // Start a chain of camera animations + func startCameraAnimations() { + os_log(.default, "Animating zoom from zoom to lvl 14") + + // Declare an animator that changes the map's bearing + let bearingAnimator = mapView.camera.makeAnimator(duration: 4, curve: .easeInOut) { (transition) in + transition.bearing.toValue = -45 + } + + bearingAnimator.addCompletion { position in + os_log(.default, "All animations complete!") + if position == .end { + self.animationState = .stop + } + } + bearingAnimator.onStarted.observe { + os_log(.default, "Bearing animator has started") + }.store(in: &cancelables) + + // Declare an animator that changes the map's pitch. + let pitchAnimator = mapView.camera.makeAnimator(duration: 2, curve: .easeInOut) { (transition) in + transition.pitch.toValue = 55 + } + + // Begin the bearing animation once the pitch animation has finished. + pitchAnimator.addCompletion { _ in + os_log(.default, "Animating camera bearing to 45 degrees") + bearingAnimator.startAnimation() + } + + // Declare an animator that changes the map's zoom level. + let zoomAnimator = self.mapView.camera.makeAnimator(duration: 4, curve: .easeInOut) { (transition) in + transition.zoom.toValue = 14 + } + + // Begin the pitch animation once the zoom animation has finished. + zoomAnimator.addCompletion { _ in + os_log(.default, "Animating camera pitch to 55 degrees") + pitchAnimator.startAnimation() + } + + // Begin the zoom animation. + zoomAnimator.startAnimation() + } +} diff --git a/Sources/Examples/All Examples/CameraForExample.swift b/Sources/Examples/All Examples/CameraForExample.swift new file mode 100644 index 000000000000..fbb546c4f926 --- /dev/null +++ b/Sources/Examples/All Examples/CameraForExample.swift @@ -0,0 +1,219 @@ +import UIKit +import MapboxMaps + +final class CameraForExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + private var selectedPlace: [CLLocationCoordinate2D] = .baltic + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + mapView.debugOptions = [.camera, .padding] + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + addBottomSheet() + addPolygons() + + setCamera(immediately: true, onMapLoaded: true) + } + + private func setCamera(immediately: Bool, onMapLoaded: Bool) { + if immediately { + setCamera(coordinates: selectedPlace, mapPadding: .zero, coordinatesPadding: .zero) + } + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self else { return } + if onMapLoaded { + setCamera(coordinates: selectedPlace, mapPadding: .zero, coordinatesPadding: .zero) + } + finish() // for testing purposes + }.store(in: &cancelables) + } + + private func setCamera( + coordinates: [CLLocationCoordinate2D], + mapPadding: UIEdgeInsets, + coordinatesPadding: UIEdgeInsets + ) { + do { + let initialCameraOptions = CameraOptions( + padding: mapPadding, + bearing: 0, + pitch: 0 + ) + + let boundingPolygonCameraOptions = try mapView.mapboxMap.camera( + for: coordinates, + camera: initialCameraOptions, + coordinatesPadding: coordinatesPadding, + maxZoom: nil, + offset: nil + ) + + if mapView.mapboxMap.isStyleLoaded { + mapView.camera.ease(to: boundingPolygonCameraOptions, duration: 0.5) + } else { + mapView.mapboxMap.setCamera(to: boundingPolygonCameraOptions) + } + + } catch { + showAlert(with: String(describing: error)) + } + } + + private func addPolygons() { + mapView.mapboxMap.setMapStyleContent { + GeoJSONSource(id: "poly-south-pole") + .data(.geometry(.lineString(LineString(.antarctic)))) + + LineLayer(id: "poly-south-pole-line", source: "poly-south-pole") + .lineWidth(2.0) + .lineColor(.red) + + GeoJSONSource(id: "poly-baltic") + .data(.geometry(.lineString(LineString(.baltic)))) + + LineLayer(id: "poly-baltic-line", source: "poly-baltic") + .lineWidth(2.0) + .lineColor(.red) + } + } + + private func addBottomSheet() { + let bottomSheet = BottomSheet(frame: CGRect( + x: 0, + y: view.bounds.height - 280, + width: view.bounds.width, + height: 280 + )) + bottomSheet.layer.cornerRadius = 16 + bottomSheet.layer.opacity = 0.8 + bottomSheet.backgroundColor = .white + bottomSheet.selectedPlace = selectedPlace + bottomSheet.onSelectionChanged = { [weak self] cordinates, mapPadding, coordinatesPadding in + self?.setCamera(coordinates: cordinates, mapPadding: mapPadding, coordinatesPadding: coordinatesPadding) + } + + view.addSubview(bottomSheet) + } +} + +private final class BottomSheet: UIView { + let placesControl = UISegmentedControl(items: places.map(\.name)) + let mapAllEdgesInset = UISegmentedControl(items: insetValues) + let leftCoordinatesInset = UISegmentedControl(items: insetValues) + let rightCoordinatesInsets = UISegmentedControl(items: insetValues) + + let titles = ["right coordinates padding", "left coordinates padding", "map padding", "place"] + var titleLabels: [UILabel] = [] + + var onSelectionChanged: (([CLLocationCoordinate2D], UIEdgeInsets, UIEdgeInsets) -> Void)? + var selectedPlace: [CLLocationCoordinate2D]? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + + setupSegmentedControl(placesControl, selected: places.firstIndex(where: { $0.coordinates == selectedPlace }) ?? 0) + setupSegmentedControl(mapAllEdgesInset) + setupSegmentedControl(leftCoordinatesInset) + setupSegmentedControl(rightCoordinatesInsets) + + addSubview(placesControl) + addSubview(mapAllEdgesInset) + addSubview(leftCoordinatesInset) + addSubview(rightCoordinatesInsets) + + createLabels() + layoutViews() + } + + func setupSegmentedControl(_ segmentedControl: UISegmentedControl, selected: Int = 0) { + segmentedControl.translatesAutoresizingMaskIntoConstraints = false + segmentedControl.selectedSegmentIndex = selected + segmentedControl.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .valueChanged) + } + + @objc func segmentedControlValueChanged(_ segmentedControl: UISegmentedControl) { + let mapPadding = UIEdgeInsets( + allEdges: CGFloat((insetValues[mapAllEdgesInset.selectedSegmentIndex] as NSString).floatValue) + ) + + let coordinatesPadding = UIEdgeInsets( + top: 0, + left: CGFloat((insetValues[leftCoordinatesInset.selectedSegmentIndex] as NSString).floatValue), + bottom: 0, + right: CGFloat((insetValues[rightCoordinatesInsets.selectedSegmentIndex] as NSString).floatValue) + ) + + onSelectionChanged?(places[placesControl.selectedSegmentIndex].coordinates, mapPadding, coordinatesPadding) + } + + func createLabels() { + for title in titles { + let label = UILabel() + label.text = title + label.translatesAutoresizingMaskIntoConstraints = false + addSubview(label) + titleLabels.append(label) + } + } + + func layoutViews() { + let spacing: CGFloat = 20 + var lastBottomAnchor = bottomAnchor + + let controls = [rightCoordinatesInsets, leftCoordinatesInset, mapAllEdgesInset, placesControl] + + for (index, segmentedControl) in controls.enumerated() { + NSLayoutConstraint.activate([ + segmentedControl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: spacing), + segmentedControl.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -spacing), + segmentedControl.bottomAnchor.constraint(equalTo: lastBottomAnchor, constant: -spacing - 10), + + titleLabels[index].leftAnchor.constraint(equalTo: segmentedControl.leftAnchor), + titleLabels[index].bottomAnchor.constraint(equalTo: segmentedControl.topAnchor, constant: 0) + ]) + + lastBottomAnchor = segmentedControl.topAnchor + } + } +} + +private extension Array where Element == CLLocationCoordinate2D { + static let baltic: [CLLocationCoordinate2D] = [.helsinki, .vyborg, .saintPetersburg, .talinn, .helsinki] + static let antarctic: [CLLocationCoordinate2D] = [ + CLLocationCoordinate2D(latitude: -74.41429582091831, longitude: -105.02738295071447), + CLLocationCoordinate2D(latitude: -82.41571395310365, longitude: -108.67784207799926), + CLLocationCoordinate2D(latitude: -71.45151781686236, longitude: -117.5641615804278), + CLLocationCoordinate2D(latitude: -74.41429582091831, longitude: -105.02738295071447) + ] +} + +private extension CLLocationCoordinate2D { + static let saintPetersburg = CLLocationCoordinate2D(latitude: 59.9375, longitude: 30.308611) + static let talinn = CLLocationCoordinate2D(latitude: 59.437039, longitude: 24.745739) + static let vyborg = CLLocationCoordinate2D(latitude: 60.7, longitude: 28.766667) +} + +private let insetValues = ["0", "20", "50", "100"] + +private struct Place { + let name: String + let coordinates: [CLLocationCoordinate2D] +} + +private let places = [ + Place(name: "baltic", coordinates: .baltic), + Place(name: "antarctic", coordinates: .antarctic) +] diff --git a/Sources/Examples/All Examples/CarPlay/ApplicationCarPlaySceneDelegage.swift b/Sources/Examples/All Examples/CarPlay/ApplicationCarPlaySceneDelegage.swift new file mode 100644 index 000000000000..24e16c8d655f --- /dev/null +++ b/Sources/Examples/All Examples/CarPlay/ApplicationCarPlaySceneDelegage.swift @@ -0,0 +1,25 @@ +import CarPlay + +class ApplicationCarPlaySceneDelegage: NSObject, CPTemplateApplicationSceneDelegate { + let applicationVC = CarPlayRootVC() + + func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene, didConnect interfaceController: CPInterfaceController, to window: CPWindow) { + window.rootViewController = applicationVC + let mapTemplate = CPMapTemplate() + mapTemplate.leadingNavigationBarButtons = [ + CPBarButton(title: "Start") { _ in + CarPlayViewController.shared.play() + } + ] + mapTemplate.trailingNavigationBarButtons = [ + CPBarButton(title: "Stop") { _ in + CarPlayViewController.shared.stop() + } + ] + interfaceController.setRootTemplate(mapTemplate, animated: false, completion: nil) + } + + func sceneWillEnterForeground(_ scene: UIScene) { + applicationVC.updateCarPlayViewController(CarPlayViewController.shared) + } +} diff --git a/Sources/Examples/All Examples/CarPlay/CarPlayMapViewController.swift b/Sources/Examples/All Examples/CarPlay/CarPlayMapViewController.swift new file mode 100644 index 000000000000..ac03fe10e55b --- /dev/null +++ b/Sources/Examples/All Examples/CarPlay/CarPlayMapViewController.swift @@ -0,0 +1,100 @@ +import UIKit +import MapboxMaps + +final class CarPlayRootVC: UIViewController { + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .skyBlue + } + + func updateCarPlayViewController(_ controller: CarPlayViewController) { + controller.willMove(toParent: self) + addChild(controller) + view.addSubview(controller.view) + controller.view.frame = view.bounds + } +} + +final class CarPlayViewController: UIViewController { + lazy var mapView: MapView = { + let mapOptions = MapOptions(pixelRatio: UIScreen.screens[1].nativeScale) + + let mapInitOptions = MapInitOptions(mapOptions: mapOptions, cameraOptions: CameraOptions( + center: CLLocationCoordinate2D( + latitude: 59.31, + longitude: 18.06 + ), + zoom: 15.0 + )) + + return MapView(frame: UIScreen.screens[1].bounds, mapInitOptions: mapInitOptions) + }() + + static let shared = CarPlayViewController() + + enum AnimationState { + case stopped + case running + } + + private var animationState: AnimationState = .stopped { + didSet { + guard oldValue != animationState else { return } + + switch animationState { + case .stopped: + animationCancellable?.cancel() + case .running: + startAnimation() + } + } + } + var animationCancellable: Cancelable? + + func play() { + animationState = .running + } + + func stop() { + animationState = .stopped + } + + private func startAnimation() { + let seattleСamera = CameraOptions(center: CLLocationCoordinate2D(latitude: 47.602730, + longitude: -122.338158), + zoom: 15.0) + let berlinCamera = CameraOptions(center: CLLocationCoordinate2D(latitude: 52.517794, + longitude: 13.408331), + zoom: 15.0) + let animationDuration = 20.0 + + func animateToBerlin(position: UIViewAnimatingPosition) { + guard animationState == .running else { return } + animationCancellable = mapView.camera.fly(to: berlinCamera, duration: animationDuration, completion: animateToSeattle(position:)) + } + + func animateToSeattle(position: UIViewAnimatingPosition) { + guard animationState == .running else { return } + animationCancellable = mapView.camera.fly(to: seattleСamera, duration: animationDuration, completion: animateToBerlin(position:)) + } + + animateToBerlin(position: .start) + } + + override func viewDidLoad() { + super.viewDidLoad() + + mapView.ornaments.options.scaleBar.visibility = .hidden + mapView.ornaments.options.attributionButton.position = .bottomRight + + mapView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(mapView) + NSLayoutConstraint.activate([ + mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + mapView.topAnchor.constraint(equalTo: view.topAnchor), + mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + } +} diff --git a/Sources/Examples/All Examples/CarPlay/DashboardCarPlaySceneDelegate.swift b/Sources/Examples/All Examples/CarPlay/DashboardCarPlaySceneDelegate.swift new file mode 100644 index 000000000000..22fdb89851ca --- /dev/null +++ b/Sources/Examples/All Examples/CarPlay/DashboardCarPlaySceneDelegate.swift @@ -0,0 +1,22 @@ +import CarPlay + +class DashboardCarPlaySceneDelegate: NSObject, CPTemplateApplicationSceneDelegate, CPTemplateApplicationDashboardSceneDelegate { + let dashboardVC = CarPlayRootVC() + + func templateApplicationDashboardScene(_ templateApplicationDashboardScene: CPTemplateApplicationDashboardScene, didConnect dashboardController: CPDashboardController, to window: UIWindow) { + window.rootViewController = dashboardVC + + dashboardController.shortcutButtons = [ + CPDashboardButton(titleVariants: ["Start"], subtitleVariants: [], image: UIImage(systemName: "play.square.fill")!, handler: { _ in + CarPlayViewController.shared.play() + }), + CPDashboardButton(titleVariants: ["Stop"], subtitleVariants: [], image: UIImage(systemName: "square.circle.fill")!, handler: { _ in + CarPlayViewController.shared.stop() + }) + ] + } + + func sceneWillEnterForeground(_ scene: UIScene) { + dashboardVC.updateCarPlayViewController(CarPlayViewController.shared) + } +} diff --git a/Sources/Examples/All Examples/CarPlay/InstrumentClusterCarPlaySceneDelegate.swift b/Sources/Examples/All Examples/CarPlay/InstrumentClusterCarPlaySceneDelegate.swift new file mode 100644 index 000000000000..36fc683c45ce --- /dev/null +++ b/Sources/Examples/All Examples/CarPlay/InstrumentClusterCarPlaySceneDelegate.swift @@ -0,0 +1,72 @@ +import CarPlay +import MapboxMaps + +class InstrumentClusterCarPlaySceneDelegate: NSObject, + CPTemplateApplicationInstrumentClusterSceneDelegate, + CPInstrumentClusterControllerDelegate { + + func instrumentClusterControllerDidConnect(_ instrumentClusterWindow: UIWindow) { + instrumentClusterWindow.rootViewController = carPlayController + } + + func instrumentClusterControllerDidDisconnectWindow(_ instrumentClusterWindow: UIWindow) { + instrumentClusterWindow.rootViewController = nil + } + + func instrumentClusterControllerDidZoom(in instrumentClusterController: CPInstrumentClusterController) { + guard let carPlayController = carPlayController else { return } + + let cameraState = carPlayController.mapView.mapboxMap.cameraState + carPlayController.mapView.camera.ease(to: .init(zoom: cameraState.zoom + 1), + duration: 0.3) + } + + func instrumentClusterControllerDidZoomOut(_ instrumentClusterController: CPInstrumentClusterController) { + guard let carPlayController = carPlayController else { return } + + let cameraState = carPlayController.mapView.mapboxMap.cameraState + carPlayController.mapView.camera.ease(to: .init(zoom: cameraState.zoom - 1), + duration: 0.3) + } + + func contentStyleDidChange(_ contentStyle: UIUserInterfaceStyle) { + let style: StyleURI + switch contentStyle { + case .dark: + style = .dark + default: + style = .light + } + carPlayController?.mapView.mapboxMap.styleURI = style + } + + func instrumentClusterController(_ instrumentClusterController: CPInstrumentClusterController, + didChangeCompassSetting compassSetting: CPInstrumentClusterSetting) { + let compassVisibility: OrnamentVisibility + switch compassSetting { + case .enabled: + compassVisibility = .visible + case .disabled: + compassVisibility = .hidden + default: + compassVisibility = .adaptive + } + carPlayController?.mapView.ornaments.options.compass.visibility = compassVisibility + } + + func instrumentClusterController(_ instrumentClusterController: CPInstrumentClusterController, + didChangeSpeedLimitSetting speedLimitSetting: CPInstrumentClusterSetting) { + // Do nothing, prevent runtime crash + } + + var carPlayController: CarPlayViewController? + + func templateApplicationInstrumentClusterScene(_ templateApplicationInstrumentClusterScene: CPTemplateApplicationInstrumentClusterScene, didConnect instrumentClusterController: CPInstrumentClusterController) { + carPlayController = CarPlayViewController() + instrumentClusterController.delegate = self + } + + func templateApplicationInstrumentClusterScene(_ templateApplicationInstrumentClusterScene: CPTemplateApplicationInstrumentClusterScene, didDisconnectInstrumentClusterController instrumentClusterController: CPInstrumentClusterController) { + carPlayController = nil + } +} diff --git a/Apps/Examples/Examples/All Examples/ColorExpressionExample.swift b/Sources/Examples/All Examples/ColorExpressionExample.swift similarity index 76% rename from Apps/Examples/Examples/All Examples/ColorExpressionExample.swift rename to Sources/Examples/All Examples/ColorExpressionExample.swift index 051e79ce698d..2bdf804da7a7 100644 --- a/Apps/Examples/Examples/All Examples/ColorExpressionExample.swift +++ b/Sources/Examples/All Examples/ColorExpressionExample.swift @@ -1,34 +1,31 @@ import UIKit import MapboxMaps -@objc(ColorExpressionExample) +final class ColorExpressionExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() -public class ColorExpressionExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() // Center the map over the United States. let centerCoordinate = CLLocationCoordinate2D(latitude: 40.58058466412761, longitude: -97.734375) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, - zoom: 3)) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 3), styleURI: .streets) mapView = MapView(frame: view.bounds, mapInitOptions: options) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView) // Allows the view controller to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.setupExample() - } + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.setupExample() + }.store(in: &cancelables) } // Wait for the style to load before adding data to it. - public func setupExample() { + func setupExample() { /** This JSON expression is transformed to swift below: [ @@ -55,7 +52,7 @@ public class ColorExpressionExample: UIViewController, ExampleProtocol { if let data = try? JSONEncoder().encode(exp.self), let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []) { - try! mapView.mapboxMap.style.setLayerProperty(for: "land", + try! mapView.mapboxMap.setLayerProperty(for: "land", property: "background-color", value: jsonObject) } diff --git a/Sources/Examples/All Examples/Custom2DPuckExample.swift b/Sources/Examples/All Examples/Custom2DPuckExample.swift new file mode 100644 index 000000000000..b41fdabbb082 --- /dev/null +++ b/Sources/Examples/All Examples/Custom2DPuckExample.swift @@ -0,0 +1,300 @@ +import UIKit +import MapboxMaps + +final class Custom2DPuckExample: UIViewController, ExampleProtocol { + private var cancelables = Set() + private var mapView: MapView! + private var puckConfiguration = Puck2DConfiguration.makeDefault(showBearing: true) + + private var showsPuck: PuckVisibility = .isVisible { + didSet { + updatePuckUI() + } + } + + private var puckImage: PuckImage = .blueDot { + didSet { + updatePuckUI() + } + } + + private var showsBearing: PuckBearingVisibility = .isVisible { + didSet { + updatePuckUI() + } + } + + private var showsAccuracyRing: PuckAccuracyRingVisibility = .isHidden { + didSet { + updatePuckUI() + } + } + + private var puckBearing: PuckBearing = .heading { + didSet { + mapView.location.options.puckBearing = puckBearing + } + } + + private var style: Style = .dark { + didSet { + mapView.mapboxMap.styleURI = style.styleURL + } + } + + private var projection: StyleProjectionName = .mercator { + didSet { + updateProjection() + } + } + + private var puckOpacity: PuckOpaticy = .opaque { + didSet { + updatePuckUI() + } + } + + private enum PuckOpaticy: Double { + case opaque = 1 + case semiTransparent = 0.5 + + mutating func toggle() { + self = self == .opaque ? .semiTransparent : .opaque + } + } + + private enum PuckVisibility { + case isVisible + case isHidden + + var isVisible: Bool { + switch self { + case .isVisible: + return true + case .isHidden: + return false + } + } + + mutating func toggle() { + self = self == .isVisible ? .isHidden : .isVisible + } + } + + private enum PuckImage: CaseIterable { + case dash + case jpegSquare + case blueDot + + var image: UIImage? { + switch self { + case .dash: + return UIImage(named: "dash-puck") + case .jpegSquare: + return UIImage(named: "jpeg-image") + case .blueDot: + return .none + } + } + + var usesDefaultShadowImage: Bool { + self == .blueDot + } + + mutating func toggle() { + var idx = Self.allCases.firstIndex(of: self)! + 1 + if idx == Self.allCases.count { + idx = 0 + } + self = Self.allCases[idx] + } + } + + private enum PuckBearingVisibility { + case isVisible + case isHidden + + var isVisible: Bool { + switch self { + case .isVisible: + return true + case .isHidden: + return false + } + } + + mutating func toggle() { + self = self == .isVisible ? .isHidden : .isVisible + } + } + + private enum PuckAccuracyRingVisibility { + case isVisible + case isHidden + + var isVisible: Bool { + switch self { + case .isVisible: + return true + case .isHidden: + return false + } + } + + mutating func toggle() { + self = self == .isVisible ? .isHidden : .isVisible + } + } + + private enum Style { + case light + case dark + + var styleURL: StyleURI { + switch self { + case .light: + return StyleURI.light + case .dark: + return StyleURI.dark + } + } + + mutating func toggle() { + self = self == .light ? .dark : .light + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 37.26301831966747, longitude: -121.97647612483807), zoom: 6) + let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions, styleURI: .dark) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + addCustomizePuckButton() + + // Granularly configure the location puck with a `Puck2DConfiguration` + puckConfiguration.layerPosition = .default + mapView.location.options.puckType = .puck2D(puckConfiguration) + mapView.location.options.puckBearing = .heading + mapView.location.options.puckBearingEnabled = true + + // Center map over the user's current location + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + + if let currentLocation = self.mapView.location.latestLocation { + let cameraOptions = CameraOptions(center: currentLocation.coordinate, zoom: 16.0) + self.mapView.camera.ease(to: cameraOptions, duration: 2.0) + } + }.store(in: &cancelables) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } + + private func addCustomizePuckButton() { + // Set up button to change the puck options + let button = UIButton(type: .system) + button.setTitle("Customize puck", for: .normal) + button.setTitleColor(.white, for: .normal) + button.backgroundColor = #colorLiteral(red: 0, green: 0.4784313725, blue: 0.9882352941, alpha: 1) + button.translatesAutoresizingMaskIntoConstraints = false + button.layer.cornerRadius = 20 + button.addTarget(self, action: #selector(changePuckOptions(sender:)), for: .touchUpInside) + view.addSubview(button) + + // Set button location + NSLayoutConstraint.activate([ + button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24), + button.centerXAnchor.constraint(equalTo: view.centerXAnchor), + button.widthAnchor.constraint(equalToConstant: 200), + button.heightAnchor.constraint(equalToConstant: 40) + ]) + } + + @objc func changePuckOptions(sender: UIButton) { + let alert = UIAlertController(title: "Toggle Puck Options", + message: "Select an options to toggle.", + preferredStyle: .actionSheet) + alert.popoverPresentationController?.sourceView = sender + + alert.addAction(UIAlertAction(title: "Toggle Puck visibility", style: .default) { _ in + self.showsPuck.toggle() + }) + + alert.addAction(UIAlertAction(title: "Toggle Puck opacity", style: .default) { _ in + self.puckOpacity.toggle() + }) + + alert.addAction(UIAlertAction(title: "Toggle Puck image", style: .default) { _ in + self.puckImage.toggle() + }) + + alert.addAction(UIAlertAction(title: "Toggle bearing visibility", style: .default) { _ in + self.showsBearing.toggle() + }) + + alert.addAction(UIAlertAction(title: "Toggle accuracy ring", style: .default) { _ in + self.showsAccuracyRing.toggle() + }) + + alert.addAction(UIAlertAction(title: "Toggle bearing source", style: .default) { _ in + self.puckBearing.toggle() + }) + + alert.addAction(UIAlertAction(title: "Toggle Map Style", style: .default) { _ in + self.style.toggle() + }) + + alert.addAction(UIAlertAction(title: "Toggle Projection", style: .default) { _ in + self.projection.toggle() + }) + + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + + present(alert, animated: true) + } + + func updatePuckUI() { + puckConfiguration = Puck2DConfiguration.makeDefault(showBearing: showsBearing.isVisible) + puckConfiguration.showsAccuracyRing = showsAccuracyRing.isVisible + puckConfiguration.topImage = puckImage.image + puckConfiguration.layerPosition = .default + if !puckImage.usesDefaultShadowImage { + puckConfiguration.shadowImage = nil + } + puckConfiguration.opacity = puckOpacity.rawValue + switch showsPuck { + case .isVisible: + mapView.location.options.puckType = .puck2D(puckConfiguration) + default: + mapView.location.options.puckType = .none + } + } + + func updateProjection() { + do { + try mapView.mapboxMap.setProjection(StyleProjection(name: projection)) + } catch { + print(error) + } + } +} + +extension PuckBearing { + mutating func toggle() { + self = self == .heading ? .course : .heading + } +} + +extension StyleProjectionName { + mutating func toggle() { + self = self == .mercator ? .globe : .mercator + } +} diff --git a/Sources/Examples/All Examples/Custom3DPuckExample.swift b/Sources/Examples/All Examples/Custom3DPuckExample.swift new file mode 100644 index 000000000000..31358095999d --- /dev/null +++ b/Sources/Examples/All Examples/Custom3DPuckExample.swift @@ -0,0 +1,58 @@ +import UIKit +import MapboxMaps + +final class Custom3DPuckExample: UIViewController, ExampleProtocol { + private var cancelables = Set() + private var mapView: MapView! + + override func viewDidLoad() { + super.viewDidLoad() + + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 37.26301831966747, longitude: -121.97647612483807), zoom: 15, pitch: 55) + mapView = MapView(frame: view.bounds, mapInitOptions: MapInitOptions(cameraOptions: cameraOptions)) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.onStyleLoaded.observeNext { _ in + self.setupExample() + }.store(in: &cancelables) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } + + private func setupExample() { + + // Fetch the `gltf` asset + let uri = Bundle.main.url(forResource: "sportcar", withExtension: "glb") + + // Instantiate the model + let myModel = Model(uri: uri, orientation: [0, 0, 180]) + + let configuration = Puck3DConfiguration( + model: myModel, + modelScale: .constant([10, 10, 10]), + modelOpacity: .constant(0.5), + layerPosition: .default + ) + mapView.location.options.puckType = .puck3D(configuration) + mapView.location.options.puckBearing = .course + mapView.location.options.puckBearingEnabled = true + + mapView.location.onLocationChange.observeNext { [weak mapView] newLocation in + guard let location = newLocation.last, let mapView else { return } + mapView.camera.ease( + to: CameraOptions( + center: location.coordinate, + zoom: 15, + bearing: 0, + pitch: 55), + duration: 1, + curve: .linear, + completion: nil) + }.store(in: &cancelables) + } +} diff --git a/Sources/Examples/All Examples/CustomLayerExample.swift b/Sources/Examples/All Examples/CustomLayerExample.swift new file mode 100644 index 000000000000..6d4b4f69ea2f --- /dev/null +++ b/Sources/Examples/All Examples/CustomLayerExample.swift @@ -0,0 +1,162 @@ +import UIKit +@_spi(Experimental) import MapboxMaps +import MetalKit + +final class CustomLayerExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + + var colorArray = [ + simd_float4(1, 0, 0, 0.5), + simd_float4(0.5, 0, 0, 0.5), + simd_float4(0, 1, 0, 0.5), + simd_float4(0, 0.5, 0, 0.5), + simd_float4(0, 0, 1, 0.5), + simd_float4(0, 0, 0.5, 0.5), + ] + + // The CustomLayerExampleCustomLayerHost() should be created and stored outside of MapStyleContent so that it is not recreated with every style update. + let renderer = CustomLayerExampleCustomLayerHost() + + override func viewDidLoad() { + super.viewDidLoad() + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Update", style: .plain, target: self, action: #selector(barButtonTap(_:))) + + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 58, longitude: 20), zoom: 3) + + mapView = MapView(frame: view.bounds, mapInitOptions: MapInitOptions(cameraOptions: cameraOptions)) + mapView.mapboxMap.mapStyle = .streets + mapView.mapboxMap.setMapStyleContent { + StyleProjection(name: .mercator) + CustomLayer(id: "custom-layer-example", renderer: renderer) + .position(.below("waterway")) + } + + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + } + + @objc func barButtonTap(_ barButtonItem: UIBarButtonItem) { + renderer.colors = colorArray.shuffled() + + // You must trigger a repaint manually to re-draw the updated Custom Layer + mapView.mapboxMap.triggerRepaint() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} + +final class CustomLayerExampleCustomLayerHost: NSObject, CustomLayerHost { + var colors = [ + simd_float4(1, 0, 0, 0.5), + simd_float4(0, 1, 0, 0.5), + simd_float4(0, 0, 1, 0.5), + ] + + var depthStencilState: MTLDepthStencilState! + var pipelineState: MTLRenderPipelineState! + + func renderingWillStart(_ metalDevice: MTLDevice, colorPixelFormat: UInt, depthStencilPixelFormat: UInt) { + guard let library = metalDevice.makeDefaultLibrary() else { + fatalError("Failed to create shader") + } + + guard let vertexFunction = library.makeFunction(name: "vertexShader") else { + fatalError("Could not find vertex function") + } + + guard let fragmentFunction = library.makeFunction(name: "fragmentShader") else { + fatalError("Could not find fragment function") + } + + // Set up vertex descriptor + let vertexDescriptor = MTLVertexDescriptor() + + // Set up pipeline descriptor + let pipelineStateDescriptor = MTLRenderPipelineDescriptor() + pipelineStateDescriptor.label = "Test Layer" + pipelineStateDescriptor.vertexFunction = vertexFunction + pipelineStateDescriptor.vertexDescriptor = vertexDescriptor + pipelineStateDescriptor.fragmentFunction = fragmentFunction + + // Set up color attachment + let colorAttachment = pipelineStateDescriptor.colorAttachments[0] + colorAttachment?.pixelFormat = MTLPixelFormat(rawValue: colorPixelFormat)! + colorAttachment?.isBlendingEnabled = true + colorAttachment?.rgbBlendOperation = colorAttachment?.alphaBlendOperation ?? .add + colorAttachment?.sourceAlphaBlendFactor = colorAttachment?.sourceAlphaBlendFactor ?? .one + colorAttachment?.destinationRGBBlendFactor = .oneMinusSourceAlpha + + // Configure render pipeline descriptor + pipelineStateDescriptor.depthAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat)! + pipelineStateDescriptor.stencilAttachmentPixelFormat = MTLPixelFormat(rawValue: depthStencilPixelFormat)! + + // Configure the depth stencil + let depthStencilDescriptor = MTLDepthStencilDescriptor() + depthStencilDescriptor.isDepthWriteEnabled = false + depthStencilDescriptor.depthCompareFunction = .always + + depthStencilState = metalDevice.makeDepthStencilState(descriptor: depthStencilDescriptor) + + do { + pipelineState = try metalDevice.makeRenderPipelineState(descriptor: pipelineStateDescriptor) + } catch { + fatalError("Could not make render pipeline state: \(error.localizedDescription)") + } + } + + func render(_ parameters: CustomLayerRenderParameters, mtlCommandBuffer: MTLCommandBuffer, mtlRenderPassDescriptor: MTLRenderPassDescriptor) { + + let zoomScale = pow(2, parameters.zoom) + let projectedHelsinki = Projection.project(.helsinki, zoomScale: zoomScale) + let projectedBerlin = Projection.project(.berlin, zoomScale: zoomScale) + let projectedKyiv = Projection.project(.kyiv, zoomScale: zoomScale) + let positions = [ + simd_float2(Float(projectedHelsinki.x), Float(projectedHelsinki.y)), + simd_float2(Float(projectedBerlin.x), Float(projectedBerlin.y)), + simd_float2(Float(projectedKyiv.x), Float(projectedKyiv.y)) + ] + + guard let renderCommandEncoder = mtlCommandBuffer.makeRenderCommandEncoder(descriptor: mtlRenderPassDescriptor) else { + fatalError("Could not create render command encoder from render pass descriptor.") + } + + let projectionMatrix = parameters.projectionMatrix.map(\.floatValue) + let vertices = zip(positions, colors).map(VertexData.init) + let viewport = MTLViewport( + originX: 0, + originY: 0, + width: parameters.width, + height: parameters.height, + znear: 0, + zfar: 1 + ) + + renderCommandEncoder.label = "Custom Layer" + renderCommandEncoder.pushDebugGroup("Custom Layer") + renderCommandEncoder.setDepthStencilState(depthStencilState) + renderCommandEncoder.setRenderPipelineState(pipelineState) + renderCommandEncoder.setVertexBytes( + vertices, + length: MemoryLayout.size * vertices.count, + index: Int(VertexInputIndexVertices.rawValue) + ) + renderCommandEncoder.setVertexBytes( + projectionMatrix, + length: MemoryLayout.size, + index: Int(VertexInputIndexTransformation.rawValue) + ) + renderCommandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3) + + renderCommandEncoder.setViewport(viewport) + renderCommandEncoder.popDebugGroup() + renderCommandEncoder.endEncoding() + } + + func renderingWillEnd() { + // Unimplemented + } +} diff --git a/Sources/Examples/All Examples/CustomRasterSourceExample.swift b/Sources/Examples/All Examples/CustomRasterSourceExample.swift new file mode 100644 index 000000000000..3a64c578fe00 --- /dev/null +++ b/Sources/Examples/All Examples/CustomRasterSourceExample.swift @@ -0,0 +1,106 @@ +import UIKit +import os +@_spi(Experimental) import MapboxMaps + +final class CustomRasterSourceExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables: Set = [] + private var requiredTiles: [CanonicalTileID] = [] + + private enum ID { + static let customRasterSource = "custom-raster-source" + static let rasterLayer = "customRaster" + } + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds, mapInitOptions: .init(cameraOptions: CameraOptions(center: .helsinki, zoom: 2))) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.onStyleLoaded.observeNext { [weak self] _ in + self?.setupExample() + // The below line is used for internal testing purposes only. + self?.finish() + } + .store(in: &cancelables) + } + + private func setupExample() { + let rasterSourceClient = CustomRasterSourceClient.fromCustomRasterSourceTileStatusChangedCallback { [weak self] (tileID, status) in + os_log(.info, "Tile status changed: tileId={%@}, status=%@", tileID.log, status.log) + guard let self else { return } + + switch status { + case .required: + if !self.requiredTiles.contains(where: { $0 == tileID }) { + self.requiredTiles.append(tileID) + } + self.refreshTiles() + case .notNeeded, .optional: + if let index = self.requiredTiles.firstIndex(of: tileID) { + self.requiredTiles.remove(at: index) + } + try! self.mapView.mapboxMap.setCustomRasterSourceTileData( + forSourceId: ID.customRasterSource, + tiles: [CustomRasterSourceTileData(tileId: tileID, image: nil)]) + default: break + } + } + let rasterSourceOptions = CustomRasterSourceOptions(clientCallback: rasterSourceClient, minZoom: 0, maxZoom: 0, tileSize: 256) + let customRasterSource = CustomRasterSource(id: ID.customRasterSource, options: rasterSourceOptions) + + do { + try mapView.mapboxMap.addSource(customRasterSource) + + var rasterLayer = RasterLayer(id: ID.rasterLayer, source: ID.customRasterSource) + rasterLayer.rasterColorMix = .constant([1, 0, 0, 0]) + rasterLayer.rasterColor = .expression( + Exp(.interpolate) { + Exp(.linear) + Exp(.lineProgress) + 0 + "rgba(0.0, 0.0, 0.0, 0.0)" + 0.3 + "rgba(7, 238, 251, 0.4)" + 0.5 + "rgba(0, 255, 42, 0.5)" + 0.7 + "rgba(255, 255, 0, 0.7)" + 1 + "rgba(255, 30, 0, 0.9)" + } + ) + try mapView.mapboxMap.addLayer(rasterLayer) + refreshTiles() + } catch { + print("[Example/CustomRasterSourceExample] Error:\(error)") + } + } + + private func refreshTiles() { + let rasterImage = nextImage() + let tiles = requiredTiles + .map { CustomRasterSourceTileData(tileId: $0, image: rasterImage) } + try! mapView.mapboxMap.setCustomRasterSourceTileData(forSourceId: ID.customRasterSource, tiles: tiles) + } + + // MARK: Raster Images + + private var currentImageIndex = 0 + private let rasterImages: [UIImage] = [ + UIImage(named: "RasterSource/wind_0")!, + UIImage(named: "RasterSource/wind_1")!, + UIImage(named: "RasterSource/wind_2")!, + UIImage(named: "RasterSource/wind_3")!, + ] + + private func nextImage() -> UIImage { + var currentImageIndex = self.currentImageIndex + 1 + if currentImageIndex >= self.rasterImages.endIndex { + currentImageIndex = 0 + } + return rasterImages[currentImageIndex] + } +} diff --git a/Apps/Examples/Examples/All Examples/DataDrivenSymbolsExample.swift b/Sources/Examples/All Examples/DataDrivenSymbolsExample.swift similarity index 81% rename from Apps/Examples/Examples/All Examples/DataDrivenSymbolsExample.swift rename to Sources/Examples/All Examples/DataDrivenSymbolsExample.swift index 02e0efea50ea..eb1dabf66b29 100644 --- a/Apps/Examples/Examples/All Examples/DataDrivenSymbolsExample.swift +++ b/Sources/Examples/All Examples/DataDrivenSymbolsExample.swift @@ -1,13 +1,11 @@ import UIKit import MapboxMaps -@objc(DataDrivenSymbolsExample) +final class DataDrivenSymbolsExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() -public class DataDrivenSymbolsExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() let centerCoordinate = CLLocationCoordinate2D(latitude: 37.761, longitude: -119.624) @@ -19,32 +17,30 @@ public class DataDrivenSymbolsExample: UIViewController, ExampleProtocol { view.addSubview(mapView) // Allows the delegate to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.setupExample() - } + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.setupExample() + }.store(in: &cancelables) } - public func setupExample() { + func setupExample() { // Constant used to identify the source layer let sourceLayerIdentifier = "yosemite-pois" // Add icons from the U.S. National Parks Service to the map's style. // Icons are located in the asset catalog - try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-restrooms")!, id: "restrooms") - try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-trailhead")!, id: "trailhead") - try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-picnic-area")!, id: "picnic-area") + try! mapView.mapboxMap.addImage(UIImage(named: "nps-restrooms")!, id: "restrooms") + try! mapView.mapboxMap.addImage(UIImage(named: "nps-trailhead")!, id: "trailhead") + try! mapView.mapboxMap.addImage(UIImage(named: "nps-picnic-area")!, id: "picnic-area") // Access a vector tileset that contains places of interest at Yosemite National Park. // This tileset was created by uploading NPS shapefiles to Mapbox Studio. - var source = VectorSource() + var source = VectorSource(id: sourceLayerIdentifier) source.url = "mapbox://examples.ciuz0vpc" - try! mapView.mapboxMap.style.addSource(source, id: sourceLayerIdentifier) + try! mapView.mapboxMap.addSource(source) // Create a symbol layer and access the layer contained. - var layer = SymbolLayer(id: sourceLayerIdentifier) - // The source property refers to the identifier provided when the source was added. - layer.source = sourceLayerIdentifier + var layer = SymbolLayer(id: sourceLayerIdentifier, source: sourceLayerIdentifier) // Access the layer that contains the Point of Interest (POI) data. // The source layer property is a unique identifier for a layer within a vector tile source. @@ -112,7 +108,7 @@ public class DataDrivenSymbolsExample: UIViewController, ExampleProtocol { layer.iconImage = .expression(expression) - try! mapView.mapboxMap.style.addLayer(layer, layerPosition: nil) + try! mapView.mapboxMap.addLayer(layer, layerPosition: nil) // The below line is used for internal testing purposes only. finish() diff --git a/Apps/Examples/Examples/All Examples/DataJoinExample.swift b/Sources/Examples/All Examples/DataJoinExample.swift similarity index 91% rename from Apps/Examples/Examples/All Examples/DataJoinExample.swift rename to Sources/Examples/All Examples/DataJoinExample.swift index 5fe499a8ef53..51e38741e636 100644 --- a/Apps/Examples/Examples/All Examples/DataJoinExample.swift +++ b/Sources/Examples/All Examples/DataJoinExample.swift @@ -1,9 +1,9 @@ import MapboxMaps -import Foundation +import UIKit -@objc(DataJoinExample) final class DataJoinExample: UIViewController, ExampleProtocol { - var mapView: MapView! + private var mapView: MapView! + private var cancelables = Set() override func viewDidLoad() { super.viewDidLoad() @@ -19,12 +19,12 @@ final class DataJoinExample: UIViewController, ExampleProtocol { view.addSubview(mapView) // Add the data layer once the map has finished loading. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in + mapView.mapboxMap.onMapLoaded.observeNext { _ in self.addJSONDataLayer() // The following line is just for testing purposes. self.finish() - } + }.store(in: &cancelables) } func addJSONDataLayer() { @@ -81,13 +81,11 @@ final class DataJoinExample: UIViewController, ExampleProtocol { // Create the source for country polygons using the Mapbox Countries tileset // The polygons contain an ISO 3166 alpha-3 code which can be used to for joining the data // https://docs.mapbox.com/vector-tiles/reference/mapbox-countries-v1 - let sourceID = "countries" - var source = VectorSource() + var source = VectorSource(id: "countries") source.url = "mapbox://mapbox.country-boundaries-v1" // Add layer from the vector tile source to create the choropleth - var layer = FillLayer(id: "countries") - layer.source = sourceID + var layer = FillLayer(id: "countries", source: source.id) layer.sourceLayer = "country_boundaries" // Build a GL match expression that defines the color for every vector tile feature @@ -129,11 +127,11 @@ final class DataJoinExample: UIViewController, ExampleProtocol { // Insert the vector layer below the 'admin-1-boundary-bg' layer in the style // Join data to the vector layer do { - try mapView.mapboxMap.style.addSource(source, id: sourceID) - try mapView.mapboxMap.style.addLayer(layer, layerPosition: .below("admin-1-boundary-bg")) + try mapView.mapboxMap.addSource(source) + try mapView.mapboxMap.addLayer(layer, layerPosition: .below("admin-1-boundary-bg")) if let expressionData = jsonExpression.data(using: .utf8) { let expJSONObject = try JSONSerialization.jsonObject(with: expressionData, options: []) - try mapView.mapboxMap.style.setLayerProperty(for: "countries", + try mapView.mapboxMap.setLayerProperty(for: "countries", property: "fill-color", value: expJSONObject) } diff --git a/Sources/Examples/All Examples/DebugMapExample.swift b/Sources/Examples/All Examples/DebugMapExample.swift new file mode 100644 index 000000000000..22e6db57715d --- /dev/null +++ b/Sources/Examples/All Examples/DebugMapExample.swift @@ -0,0 +1,261 @@ +import UIKit +@_spi(Experimental) import MapboxMaps + +final class DebugMapExample: UIViewController, ExampleProtocol { + private var collectStatisticsButton = UIButton(type: .system) + private var mapView: MapView! + private var performanceStatisticsCancelable: AnyCancelable? + private let settings: [Setting] = [ + Setting(option: .debug(.collision), title: "Debug collision"), + Setting(option: .debug(.depthBuffer), title: "Show depth buffer"), + Setting(option: .debug(.overdraw), title: "Debug overdraw"), + Setting(option: .debug(.parseStatus), title: "Show tile coordinate"), + Setting(option: .debug(.stencilClip), title: "Show stencil buffer"), + Setting(option: .debug(.tileBorders), title: "Debug tile clipping"), + Setting(option: .debug(.timestamps), title: "Show tile loaded time"), + Setting(option: .debug(.modelBounds), title: "Show 3D model bounding boxes"), + Setting(option: .debug(.light), title: "Show light conditions"), + Setting(option: .debug(.camera), title: "Show camera debug view"), + Setting(option: .debug(.padding), title: "Camera padding"), + Setting(option: .performance(.init([.perFrame, .cumulative], samplingDurationMillis: 5000)), title: "Performance statistics"), + ] + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + let maxFPS = Float(UIScreen.main.maximumFramesPerSecond) + mapView.preferredFrameRateRange = CAFrameRateRange(minimum: 1, maximum: maxFPS, preferred: maxFPS) + + view.addSubview(mapView) + view.backgroundColor = .skyBlue + mapView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) + + let debugOptionsBarItem = UIBarButtonItem( + title: "Debug", + style: .plain, + target: self, + action: #selector(openDebugOptionsMenu(_:))) + let tileCover = UIBarButtonItem( + title: "Tiles", + style: .plain, + target: self, + action: #selector(tileCover)) + navigationItem.rightBarButtonItems = [debugOptionsBarItem, tileCover] + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } + + @objc private func openDebugOptionsMenu(_ sender: UIBarButtonItem) { + let settingsViewController = SettingsViewController(settings: settings) + settingsViewController.delegate = self + + let navigationController = UINavigationController(rootViewController: settingsViewController) + navigationController.modalPresentationStyle = .popover + navigationController.popoverPresentationController?.barButtonItem = sender + + present(navigationController, animated: true, completion: nil) + } + + @objc private func tileCover() { + let tileIds = mapView.mapboxMap.tileCover(for: TileCoverOptions(tileSize: 512, minZoom: 0, maxZoom: 22, roundZoom: false)) + let message = tileIds.map { "\($0.z)/\($0.x)/\($0.y)" }.joined(separator: "\n") + showAlert(withTitle: "Displayed tiles", and: message) + } + + private func handle(statistics: PerformanceStatistics) { + showAlert(with: "\(statistics.topRenderedGroupDescription)\n\(statistics.renderingDurationStatisticsDescription)") + } +} + +extension DebugMapExample: DebugOptionSettingsDelegate { + func settingsDidChange(debugOptions: MapViewDebugOptions, performanceOptions: PerformanceStatisticsOptions?) { + mapView.debugOptions = debugOptions + + guard let performanceOptions else { return performanceStatisticsCancelable = nil } + performanceStatisticsCancelable?.cancel() + performanceStatisticsCancelable = mapView.mapboxMap.collectPerformanceStatistics(performanceOptions, callback: handle(statistics:)) + } +} + +final class SettingsViewController: UIViewController, UITableViewDataSource { + weak var delegate: DebugOptionSettingsDelegate? + private var listView: UITableView! + private let settings: [Setting] + + fileprivate init(settings: [Setting]) { + self.settings = settings + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Debug options" + listView = UITableView() + listView.dataSource = self + listView.register(DebugOptionCell.self, forCellReuseIdentifier: String(describing: DebugOptionCell.self)) + + view.addSubview(listView) + + listView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + listView.topAnchor.constraint(equalTo: view.topAnchor), + listView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + listView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + listView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) + + navigationItem.largeTitleDisplayMode = .never + navigationItem.rightBarButtonItem = UIBarButtonItem( + barButtonSystemItem: .save, + target: self, + action: #selector(saveSettings(_:))) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + preferredContentSize = listView.contentSize + } + + @objc private func saveSettings(_ sender: UIBarButtonItem) { + let debugOptions = settings + .filter(\.isEnabled) + .compactMap(\.option.debugOption) + .reduce(MapViewDebugOptions()) { result, next in result.union(next) } + + let performanceOptions = settings + .filter(\.isEnabled) + .compactMap(\.option.performanceOption) + + delegate?.settingsDidChange(debugOptions: debugOptions, performanceOptions: performanceOptions.first) + dismiss(animated: true, completion: nil) + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + settings.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cellID = String(describing: DebugOptionCell.self) + // swiftlint:disable:next force_cast + let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! DebugOptionCell + + let setting = settings[indexPath.row] + cell.configure(with: setting.title, isOptionEnabled: setting.isEnabled) + cell.onToggled(setting.toggle) + + return cell + } +} + +// MARK: Cell + +private class DebugOptionCell: UITableViewCell { + private let titleLabel = UILabel() + private let toggle = UISwitch() + private var onToggleHandler: (() -> Void)? + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + toggle.addTarget(self, action: #selector(didToggle(_:)), for: .valueChanged) + + contentView.addSubview(titleLabel) + contentView.addSubview(toggle) + + titleLabel.translatesAutoresizingMaskIntoConstraints = false + toggle.translatesAutoresizingMaskIntoConstraints = false + + let constraints: [NSLayoutConstraint] = [ + titleLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 16), + titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), + titleLabel.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 8), + toggle.leftAnchor.constraint(greaterThanOrEqualTo: titleLabel.rightAnchor, constant: 16), + toggle.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -16), + toggle.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor), + toggle.topAnchor.constraint(greaterThanOrEqualTo: contentView.topAnchor, constant: 8), + ] + NSLayoutConstraint.activate(constraints) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(with title: String, isOptionEnabled: Bool) { + titleLabel.text = title + toggle.isOn = isOptionEnabled + } + + func onToggled(_ handler: @escaping () -> Void) { + onToggleHandler = handler + } + + @objc private func didToggle(_ sender: UISwitch) { + onToggleHandler?() + } +} + +protocol DebugOptionSettingsDelegate: AnyObject { + func settingsDidChange(debugOptions: MapViewDebugOptions, performanceOptions: PerformanceStatisticsOptions?) +} + +private final class Setting { + enum Option { + case debug(MapViewDebugOptions) + case performance(PerformanceStatisticsOptions) + } + + let option: Option + let title: String + private(set) var isEnabled: Bool + + init(option: Option, title: String, isEnabled: Bool = false) { + self.option = option + self.title = title + self.isEnabled = isEnabled + } + + func toggle() { isEnabled.toggle() } +} + +extension Setting.Option { + var debugOption: MapViewDebugOptions? { + if case let .debug(option) = self { return option } else { return nil } + } + + var performanceOption: PerformanceStatisticsOptions? { + if case let .performance(option) = self { return option } else { return nil } + } +} + +extension PerformanceStatistics { + fileprivate var topRenderedGroupDescription: String { + if let topRenderedGroup = perFrameStatistics?.topRenderGroups.first { + return "Top rendered group: `\(topRenderedGroup.name)` took \(topRenderedGroup.durationMillis)ms." + } else { + return "No information about topRenderedLayer." + } + } + + fileprivate var renderingDurationStatisticsDescription: String { + guard let drawCalls = cumulativeStatistics?.drawCalls else { return "Cumulative statistics haven't been collected." } + return """ + Number of draw calls: \(drawCalls). + """ + } +} diff --git a/Apps/Examples/Examples/All Examples/DistanceExpressionExample.swift b/Sources/Examples/All Examples/DistanceExpressionExample.swift similarity index 85% rename from Apps/Examples/Examples/All Examples/DistanceExpressionExample.swift rename to Sources/Examples/All Examples/DistanceExpressionExample.swift index d09b40ddbe53..4d6ae714079d 100644 --- a/Apps/Examples/Examples/All Examples/DistanceExpressionExample.swift +++ b/Sources/Examples/All Examples/DistanceExpressionExample.swift @@ -1,9 +1,10 @@ import MapboxMaps +import UIKit -@objc(DistanceExpressionExample) -class DistanceExpressionExample: UIViewController, ExampleProtocol { - var mapView: MapView! - var point: Turf.Feature! +final class DistanceExpressionExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var point: Turf.Feature! + private var cancelables = Set() override func viewDidLoad() { super.viewDidLoad() @@ -15,20 +16,19 @@ class DistanceExpressionExample: UIViewController, ExampleProtocol { mapView.autoresizingMask = [.flexibleHeight, .flexibleWidth] view.addSubview(mapView) - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in + mapView.mapboxMap.onMapLoaded.observeNext { _ in self.addCircleLayer() // The following line is just for testing purposes. self.finish() - } + }.store(in: &cancelables) } func addCircleLayer() { - let style = mapView.mapboxMap.style let center = mapView.mapboxMap.cameraState.center // Create a `GeoJSONSource` from a Turf geometry. - var source = GeoJSONSource() + var source = GeoJSONSource(id: "source-id") point = Feature(geometry: Point(center)) // Filter out POI labels that are more than 150 meters from the point. @@ -39,8 +39,7 @@ class DistanceExpressionExample: UIViewController, ExampleProtocol { // Create a `CircleLayer` from the previously defined source. The source ID // will be set for the source once it is added to the map's style. - var circleLayer = CircleLayer(id: "circle-layer") - circleLayer.source = "source-id" + var circleLayer = CircleLayer(id: "circle-layer", source: source.id) // This expression simulates a `CircleLayer` with a radius of 150 meters. For features that will be // visible at lower zoom levels, add more stops at the zoom levels where the feature will be more @@ -87,19 +86,17 @@ class DistanceExpressionExample: UIViewController, ExampleProtocol { circleLayer.circleOpacity = .constant(0.3) // Add the source and layer to the map's style. - try! style.addSource(source, id: "source-id") - try! style.addLayer(circleLayer) + try! mapView.mapboxMap.addSource(source) + try! mapView.mapboxMap.addLayer(circleLayer) } func filterPoiLabels() { - let style = mapView.mapboxMap.style - do { // Update the `SymbolLayer` with id "poi-label". This layer is included in the Mapbox // Streets v11 style. In order to see all layers included with your style, either inspect // the style in Mapbox Studio or inspect the `style.allLayerIdentifiers` property once // the style has finished loading. - try style.updateLayer(withId: "poi-label", type: SymbolLayer.self) { (layer: inout SymbolLayer) throws in + try mapView.mapboxMap.updateLayer(withId: "poi-label", type: SymbolLayer.self) { (layer: inout SymbolLayer) throws in // Filter the "poi-label" layer to only show points less than 150 meters away from the // the specified feature. layer.filter = Exp(.lt) { @@ -120,7 +117,7 @@ class DistanceExpressionExample: UIViewController, ExampleProtocol { } func circleRadius(forZoom zoom: CGFloat) -> Double { - let centerLatitude = mapView.cameraState.center.latitude + let centerLatitude = mapView.mapboxMap.cameraState.center.latitude // Get the meters per pixel at a given latitude and zoom level. let metersPerPoint = Projection.metersPerPoint(for: centerLatitude, zoom: zoom) diff --git a/Sources/Examples/All Examples/ExternalVectorSourceExample.swift b/Sources/Examples/All Examples/ExternalVectorSourceExample.swift new file mode 100644 index 000000000000..aa2049c5fc8a --- /dev/null +++ b/Sources/Examples/All Examples/ExternalVectorSourceExample.swift @@ -0,0 +1,58 @@ +import UIKit +import MapboxMaps + +final class ExternalVectorSourceExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + let centerCoordinate = CLLocationCoordinate2D(latitude: 41.878781, longitude: -87.622088) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 12.0), + styleURI: .light) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allow the view controller to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.drawLineLayer() + // The following line is just for testing purposes. + self?.finish() + }.store(in: &cancelables) + } + + func drawLineLayer() { + var vectorSource = VectorSource(id: "mapillary") + + // For sources using the {z}/{x}/{y} URL scheme, use the `tiles` + // property on `VectorSource` to set the URL. + vectorSource.tiles = ["https://tiles.mapillary.com/maps/vtp/mly1_public/2/{z}/{x}/{y}?access_token=MLY%7C4142433049200173%7C72206abe5035850d6743b23a49c41333"] + vectorSource.minzoom = 6 + vectorSource.maxzoom = 14 + + var lineLayer = LineLayer(id: "line-layer", source: vectorSource.id) + lineLayer.sourceLayer = "sequence" + let lineColor = StyleColor(UIColor(red: 0.21, green: 0.69, blue: 0.43, alpha: 1.00)) + lineLayer.lineColor = .constant(lineColor) + lineLayer.lineOpacity = .constant(0.6) + lineLayer.lineWidth = .constant(2.0) + lineLayer.lineCap = .constant(.round) + + do { + try mapView.mapboxMap.addSource(vectorSource) + } catch { + showAlert(with: error.localizedDescription) + } + + // Define the layer's positioning within the layer stack so + // that it doesn't obscure other important labels. + do { + try mapView.mapboxMap.addLayer(lineLayer, layerPosition: .below("waterway-label")) + } catch let layerError { + showAlert(with: layerError.localizedDescription) + } + } +} diff --git a/Sources/Examples/All Examples/FeatureStateExample.swift b/Sources/Examples/All Examples/FeatureStateExample.swift new file mode 100644 index 000000000000..8737c791efeb --- /dev/null +++ b/Sources/Examples/All Examples/FeatureStateExample.swift @@ -0,0 +1,315 @@ +import UIKit +import MapboxMaps + +final class FeatureStateExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var descriptionView: EarthquakeDescriptionView! + private var previouslyTappedEarthquakeId: String = "" + private var cancelables = Set() + + private lazy var dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.timeStyle = DateFormatter.Style.medium //Set time style + dateFormatter.dateStyle = DateFormatter.Style.medium //Set date style + dateFormatter.timeZone = .current + + return dateFormatter + }() + + override func viewDidLoad() { + super.viewDidLoad() + + // Center the map over the United States. + let centerCoordinate = CLLocationCoordinate2D(latitude: 39.368279, + longitude: -97.646484) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 2.4)) + + // Set up map view + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.ornaments.options.scaleBar.visibility = .hidden + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Set up description view + descriptionView = EarthquakeDescriptionView(frame: .zero) + view.addSubview(descriptionView) + descriptionView.translatesAutoresizingMaskIntoConstraints = false + descriptionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 2.0).isActive = true + descriptionView.heightAnchor.constraint(equalToConstant: 100).isActive = true + descriptionView.widthAnchor.constraint(equalToConstant: 200).isActive = true + descriptionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 2.0).isActive = true + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.setupSourceAndLayer() + + // The below lines are used for internal testing purposes only. + DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { + self?.finish() + } + }.store(in: &cancelables) + + mapView.gestures.onLayerTap("earthquake-viz") { [weak self] queriedFeature, _ in + self?.handleTappedFeature(queriedFeature) + return true + }.store(in: &cancelables) + } + + func setupSourceAndLayer() { + + // Create a new GeoJSON data source which gets its data from an external URL. + guard let sevenDaysAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date()) else { + preconditionFailure("Could not calculate date for seven days ago.") + } + + // Format the date to ISO8601 as required by the earthquakes API + let iso8601DateFormatter = ISO8601DateFormatter() + iso8601DateFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + let startTime = iso8601DateFormatter.string(from: sevenDaysAgo) + + // Create the url required for the GeoJSONSource + guard let earthquakeURL = URL(string: "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&eventtype=earthquake&minmagnitude=1&starttime=" + startTime) else { + preconditionFailure("URL is not valid") + } + + var earthquakeSource = GeoJSONSource(id: Self.earthquakeSourceId) + earthquakeSource.data = .url(earthquakeURL) + earthquakeSource.generateId = true + + do { + try mapView.mapboxMap.addSource(earthquakeSource) + } catch { + print("Ran into an error adding a source: \(error)") + } + + // Add earthquake-viz layer + var earthquakeVizLayer = CircleLayer(id: Self.earthquakeLayerId, source: Self.earthquakeSourceId) + + // The feature-state dependent circle-radius expression will render + // the radius size according to its magnitude when + // a feature's selected state is set to true + earthquakeVizLayer.circleRadius = .expression( + Exp(.switchCase) { + Exp(.boolean) { + Exp(.featureState) { "selected" } + false + } + Exp(.interpolate) { + Exp(.linear) + Exp(.get) { "mag" } + 1 + 8 + 1.5 + 10 + 2 + 12 + 2.5 + 14 + 3 + 16 + 3.5 + 18 + 4.5 + 20 + 6.5 + 22 + 8.5 + 24 + 10.5 + 26 + } + 5 + } + ) + earthquakeVizLayer.circleRadiusTransition = StyleTransition(duration: 0.5, delay: 0) + earthquakeVizLayer.circleStrokeColor = .constant(StyleColor(.black)) + earthquakeVizLayer.circleStrokeWidth = .constant(1) + + // The feature-state dependent circle-color expression will render + // the color according to its magnitude when + // a feature's hover state is set to true + earthquakeVizLayer.circleColor = .expression( + Exp(.switchCase) { + Exp(.boolean) { + Exp(.featureState) { "selected" } + false + } + Exp(.interpolate) { + Exp(.linear) + Exp(.get) { "mag" } + 1 + "#fff7ec" + 1.5 + "#fee8c8" + 2 + "#fdd49e" + 2.5 + "#fdbb84" + 3 + "#fc8d59" + 3.5 + "#ef6548" + 4.5 + "#d7301f" + 6.5 + "#b30000" + 8.5 + "#7f0000" + 10.5 + "#000" + } + "#000" + } + ) + earthquakeVizLayer.circleColorTransition = StyleTransition(duration: 0.5, delay: 0) + + do { + try mapView.mapboxMap.addLayer(earthquakeVizLayer) + } catch { + print("Ran into an error adding a layer: \(error)") + } + } + + private func handleTappedFeature(_ queriedFeature: QueriedFeature) { + let earthquakeFeature = queriedFeature.feature + if case .number(let earthquakeIdDouble) = earthquakeFeature.identifier, + case .point(let point) = earthquakeFeature.geometry, + case let .number(magnitude) = earthquakeFeature.properties?["mag"], + case let .string(place) = earthquakeFeature.properties?["place"], + case let .number(timestamp) = earthquakeFeature.properties?["time"] { + + let earthquakeId = Int(earthquakeIdDouble).description + + // Set the description of the earthquake from the `properties` object + self.setDescription(magnitude: magnitude, timeStamp: timestamp, location: place) + + // Set the earthquake to be "selected" + self.setSelectedState(earthquakeId: earthquakeId) + + // Reset a previously tapped earthquake to be "unselected". + self.resetPreviouslySelectedStateIfNeeded(currentTappedEarthquakeId: earthquakeId) + + // Store the currently tapped earthquake so it can be reset when another earthquake is tapped. + self.previouslyTappedEarthquakeId = earthquakeId + + // Center the selected earthquake on the screen + self.mapView.camera.fly(to: CameraOptions(center: point.coordinates, zoom: 10)) + } + } + + func setDescription(magnitude: Double, timeStamp: Double, location: String) { + self.descriptionView.magnitudeLabel.text = "Magnitude: \(magnitude)" + self.descriptionView.locationLabel.text = "Location: \(location)" + self.descriptionView.dateLabel.text = "Date: " + self.dateFormatter.string( + from: Date(timeIntervalSince1970: timeStamp / 1000.0)) + } + + // Sets a particular earthquake to be selected + func setSelectedState(earthquakeId: String) { + self.mapView.mapboxMap.setFeatureState(sourceId: Self.earthquakeSourceId, + sourceLayerId: nil, + featureId: earthquakeId, + state: ["selected": true]) { result in + switch result { + case .failure(let error): + print("Could not retrieve feature state: \(error).") + case .success: + print("Succesfully set feature state.") + } + } + } + + // Resets the previously selected earthquake to be "unselected" if needed. + func resetPreviouslySelectedStateIfNeeded(currentTappedEarthquakeId: String) { + + if self.previouslyTappedEarthquakeId != "" + && currentTappedEarthquakeId != self.previouslyTappedEarthquakeId { + // Reset a previously tapped earthquake to be "unselected". + self.mapView.mapboxMap.setFeatureState(sourceId: Self.earthquakeSourceId, + sourceLayerId: nil, + featureId: self.previouslyTappedEarthquakeId, + state: ["selected": false]) { result in + switch result { + case .failure(let error): + print("Could not retrieve feature state: \(error).") + case .success: + print("Succesfully set feature state.") + } + } + } + } + + // Present an alert with a given title. + func showAlert(with title: String) { + let alertController = UIAlertController(title: title, + message: nil, + preferredStyle: .alert) + + alertController.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil)) + + present(alertController, animated: true, completion: nil) + } +} + +private class EarthquakeDescriptionView: UIView { + + var magnitudeLabel: UILabel! + var locationLabel: UILabel! + var dateLabel: UILabel! + var stackView: UIStackView! + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .white + layer.opacity = 0.7 + layer.borderWidth = 0.5 + layer.borderColor = UIColor.black.cgColor + createSubviews() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func createSubviews() { + + func createLabel(placeholder: String) -> UILabel { + let label = UILabel(frame: .zero) + label.font = UIFont.systemFont(ofSize: 10) + label.numberOfLines = 0 + label.text = placeholder + label.textColor = .black + return label + } + + magnitudeLabel = createLabel(placeholder: "Magnitude: ---") + locationLabel = createLabel(placeholder: "Location: ---") + dateLabel = createLabel(placeholder: "Date: ---") + + let stackview = UIStackView() + stackview.axis = .vertical + stackview.spacing = 1 + stackview.alignment = .leading + stackview.distribution = .fillEqually + stackview.translatesAutoresizingMaskIntoConstraints = false + stackview.addArrangedSubview(magnitudeLabel) + magnitudeLabel.widthAnchor.constraint(equalTo: stackview.widthAnchor).isActive = true + + stackview.addArrangedSubview(locationLabel) + locationLabel.widthAnchor.constraint(equalTo: stackview.widthAnchor).isActive = true + + stackview.addArrangedSubview(dateLabel) + dateLabel.widthAnchor.constraint(equalTo: stackview.widthAnchor).isActive = true + + self.addSubview(stackview) + stackview.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10).isActive = true + stackview.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true + stackview.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + stackview.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true + + } +} + +private extension FeatureStateExample { + static let earthquakeSourceId: String = "earthquakes" + static let earthquakeLayerId: String = "earthquake-viz" +} diff --git a/Sources/Examples/All Examples/FeaturesAtPointExample.swift b/Sources/Examples/All Examples/FeaturesAtPointExample.swift new file mode 100644 index 000000000000..6456657e41ff --- /dev/null +++ b/Sources/Examples/All Examples/FeaturesAtPointExample.swift @@ -0,0 +1,56 @@ +import UIKit +import MapboxMaps + +final class FeaturesAtPointExample: UIViewController, ExampleProtocol { + private var cancelables = Set() + private var mapView: MapView! + + override func viewDidLoad() { + super.viewDidLoad() + + // Center the map over the United States. + let centerCoordinate = CLLocationCoordinate2D(latitude: 39.368279, + longitude: -97.646484) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 2.4)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allows the view controller to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { _ in + self.setupExample() + + // The following line is just for testing purposes. + self.finish() + }.store(in: &cancelables) + + // Set up the tap gesture + mapView.gestures.onLayerTap("US-states") { [weak self] queriedFeature, _ in + if let firstFeature = queriedFeature.feature.properties, + case let .string(stateName) = firstFeature["STATE_NAME"] { + self?.showAlert(with: "You selected \(stateName)") + } + return true + }.store(in: &cancelables) + } + + func setupExample() { + // Create a new GeoJSON data source which gets its data from an external URL. + var geoJSONSource = GeoJSONSource(id: "US-states-vector-source") + geoJSONSource.data = .string("https://docs.mapbox.com/mapbox-gl-js/assets/us_states.geojson") + + // Create a new fill layer associated with the data source. + var fillLayer = FillLayer(id: "US-states", source: geoJSONSource.id) + fillLayer.sourceLayer = "state_county_population_2014_cen" + + // Apply basic styling to the fill layer. + fillLayer.fillColor = .constant(StyleColor(.blue)) + fillLayer.fillOpacity = .constant(0.3) + fillLayer.fillOutlineColor = .constant(StyleColor(.black)) + + // Add the data source and style layer to the map. + try! mapView.mapboxMap.addSource(geoJSONSource) + try! mapView.mapboxMap.addLayer(fillLayer, layerPosition: nil) + } +} diff --git a/Sources/Examples/All Examples/GeofencingExample.swift b/Sources/Examples/All Examples/GeofencingExample.swift new file mode 100644 index 000000000000..9569fe26d38e --- /dev/null +++ b/Sources/Examples/All Examples/GeofencingExample.swift @@ -0,0 +1,391 @@ +import UIKit +import OSLog +import Turf +@_spi(Experimental) import MapboxMaps +@_spi(Experimental) import MapboxCommon + +/// NOTE: - This example show the usage of experimental Geofencing API which is subject to changes. +final class GeofencingExample: UIViewController, ExampleProtocol, GeofencingObserver { + private var mapView: MapView! + private var geofencingBt: UIButton? + private var geofenceDisabledText: UITextView? + private var cancelables = Set() + private var geofencing = GeofencingFactory.getOrCreate() + private var enterGeofenceId: String? + private var dwellGeofenceId: String? + private var exitGeofenceId: String? + private var geofences: [Turf.Geometry?] = [nil, nil, nil] + private var geofencingSpinner: UIActivityIndicatorView? + + override func viewDidLoad() { + super.viewDidLoad() + let options = MapInitOptions(cameraOptions: CameraOptions(center: .helsinki, zoom: 11)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self = self else { return } + setupGeofence() + + // The below line is used for internal testing purposes only. + self.finish() + }.store(in: &cancelables) + + var puckConfiguration = Puck2DConfiguration.makeDefault() + puckConfiguration.pulsing = .default + mapView.location.options.puckType = .puck2D(puckConfiguration) + + mapView.gestures.onMapTap.observe {[weak self] context in + self?.addCustomGeofence(coordinate: context.coordinate) + }.store(in: &cancelables) + + mapView.location.onLocationChange.observeNext { [weak mapView] newLocation in + guard let mapView, let location = newLocation.last else { return } + mapView.mapboxMap.setCamera(to: CameraOptions(center: location.coordinate, zoom: 18)) + }.store(in: &cancelables) + + addGeofencingButtonAndText() + } + + private func setupGeofence() { + CLLocationManager().requestAlwaysAuthorization() + requestNotificationPermission() + + geofencing.configure(options: GeofencingOptions(maximumMonitoredFeatures: 150_000)) { [weak self] result in + guard let self else { return } + geofencing.addObserver(observer: self) { result in print("Add observer: \(result)") } + updateGeofences() + } + } + + override func viewDidDisappear(_ animated: Bool) { + geofencing.removeObserver(observer: self) { result in print("Remove observer: \(result)") } + } + + func onExit(event: MapboxCommon.GeofencingEvent) { + guard case let .string(id) = event.feature.identifier else { return } + os_log(.debug, "onExit: %s", id) + exitGeofenceId = id + updateGeofences() + } + + func onEntry(event: MapboxCommon.GeofencingEvent) { + guard case let .string(id) = event.feature.identifier else { return } + os_log(.debug, "onEntry: %s", id) + enterGeofenceId = id + updateGeofences() + } + + func onDwell(event: MapboxCommon.GeofencingEvent) { + guard case let .string(id) = event.feature.identifier else { return } + os_log(.debug, "onDwell: ", id) + dwellGeofenceId = id + updateGeofences() + } + + func onError(error: MapboxCommon.GeofencingError) { + os_log(.error, "onError: %s", error.message) + } + + func onUserConsentChanged(isConsentGiven: Bool) { + userRevokedConsentUI(isConsentGiven: isConsentGiven) + } + + private func userRevokedConsentUI(isConsentGiven: Bool) { + DispatchQueue.main.async { + self.geofencingBt?.isHidden = !isConsentGiven + self.geofenceDisabledText?.isHidden = isConsentGiven + } + } + + private func requestNotificationPermission() { + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in + print("Notification permission request finished. Success: \(success), error: \(String(describing: error))") + } + } + + private func sendNotification(title: String, subtitle: String) { + let content = UNMutableNotificationContent() + content.title = title + content.subtitle = subtitle + content.sound = UNNotificationSound.default + let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false) + let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger) + UNUserNotificationCenter.current().add(request) + } + + private func clearGeofences() { + geofencing.clearFeatures { result in + print("Geofences cleared. Result: \(result)") + } + } + + private func addCustomGeofence(coordinate: CLLocationCoordinate2D) { + var feature = Feature(geometry: Point(coordinate)) + feature.identifier = .string(UUID().uuidString) + feature.properties = [GeofencingPropertiesKeys.dwellTimeKey: 1] + + self.geofencing.addFeature(feature: feature) { result in + print("Feature added with result: \(result)") + } + + showToast(message: "Circle Geofence with center at (\(String(format: "%.4f", coordinate.latitude)), \(String(format: "%.4f", coordinate.longitude))) and default radius: \(Constants.defaultRadius) m is added", duration: 2) + } + + private func getAndUpdateFeature(geofenceId: String, collectionIdx: Int, sourceId: String) { + geofencing.getFeature(identifier: geofenceId, callback: {[weak self] result in + switch result { + case .success(let value): + var feature = value.feature + guard let self else { return } + + if let center = feature.coordinate { + geofences[collectionIdx] = Turf.Polygon(center: center, radius: 300.0, vertices: 64).geometry + feature.geometry = geofences[collectionIdx] + } else { + geofences[collectionIdx] = feature.geometry! + } + + let geometries = Turf.Geometry.geometryCollection(GeometryCollection(geometries: geofences.compactMap { $0 })) + + DispatchQueue.main.async { + self.mapView.camera.fly(to: self.mapView.mapboxMap.camera(for: geometries, padding: UIEdgeInsets(allEdges: 10), bearing: nil, pitch: nil)) + self.mapView.mapboxMap.updateGeoJSONSource(withId: sourceId, geoJSON: .feature(feature)) + } + case .failure(let error): + os_log(.error, "Error while retrieving feature %s: %s", geofenceId, error.message) + } + }) + } + + private func updateGeofences() { + if let enterGeofenceId { + getAndUpdateFeature(geofenceId: enterGeofenceId, collectionIdx: 0, sourceId: Constants.enterGeofenceDataSourceId) + } + if let dwellGeofenceId { + getAndUpdateFeature(geofenceId: dwellGeofenceId, collectionIdx: 1, sourceId: Constants.dwellGeofenceDataSourceId) + } + if let exitGeofenceId { + getAndUpdateFeature(geofenceId: exitGeofenceId, collectionIdx: 2, sourceId: Constants.exitGeofenceDataSourceId) + } + } +} + +/// Download data from internet +extension GeofencingExample { + /// Enqueues a dataset fetch to the global async queue + func datasetFetch(baseUrl: String, completion: @escaping (Result) -> Void) { + let url = URL(string: baseUrl)! + DispatchQueue.global().async { + let result: Result + do { + let data = try Data(contentsOf: url) + let featureCollection = try JSONDecoder().decode(Turf.FeatureCollection.self, from: data) + result = .success(featureCollection) + } catch { + result = .failure(error) + } + DispatchQueue.main.async { + completion(result) + } + } + } + + /// Fetches the geofences from Mapbox datasets API (starting at startId) and adds them to the geofencing engine + private func loadGeofences(baseUrl: String = Constants.datastoreBaseUrl) { + showSpinnerIfNeeded() + datasetFetch(baseUrl: baseUrl) {[weak self] result in + guard let self = self else { return } + switch result { + case .success(let featureCollection): + featureCollection.features.forEach { feature in + var feature = feature + let properties: JSONObject = feature.properties! + guard let fid = properties["FID"]!!.rawValue as? Double, + let fid1 = properties["Fid_1"]!!.rawValue as? Double, + let hslId = properties["Id"]!!.rawValue as? String else { + os_log(.error, "Missing iso or unitCode") + return + } + let id = String(fid) + "-" + String(fid1) + "-" + hslId + feature.identifier = .string(id) + feature.properties![GeofencingPropertiesKeys.dwellTimeKey] = 1 + self.geofencing.addFeature(feature: feature) { result in print("Feature added with result: \(result)") } + } + + hideSpinner() + + case .failure(let failure): + os_log(.error, "Failed to read features dataset: %s", failure.localizedDescription) + } + } + } +} + +/// UI Layout +extension GeofencingExample { + private func addGeofencingButtonAndText() { + let button = UIButton(type: .system) + button.setTitle("Geofencing", for: .normal) + button.setTitleColor(.white, for: .normal) + button.backgroundColor = #colorLiteral(red: 0, green: 0.4784313725, blue: 0.9882352941, alpha: 1) + button.translatesAutoresizingMaskIntoConstraints = false + button.layer.cornerRadius = 20 + button.addTarget(self, action: #selector(geofencingOptions(sender:)), for: .touchUpInside) + view.addSubview(button) + + let buttonLayoutConstraints = [ + button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24), + button.centerXAnchor.constraint(equalTo: view.centerXAnchor), + button.widthAnchor.constraint(equalToConstant: 200), + button.heightAnchor.constraint(equalToConstant: 40) + ] + NSLayoutConstraint.activate(buttonLayoutConstraints) + geofencingBt = button + + let textView = UITextView() + textView.text = "User consent revoked" + textView.textColor = .white + textView.backgroundColor = #colorLiteral(red: 0.9529411793, green: 0.6862745285, blue: 0.1333333403, alpha: 1) + textView.font = UIFont.systemFont(ofSize: 18) + textView.textAlignment = .center + textView.isEditable = false + textView.isSelectable = false + textView.isScrollEnabled = false + textView.layer.cornerRadius = 10 + textView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(textView) + + let textLayoutConstraints = [ + textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24), + textView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + textView.widthAnchor.constraint(equalToConstant: 200), + textView.heightAnchor.constraint(equalToConstant: 40) + ] + NSLayoutConstraint.activate(textLayoutConstraints) + geofenceDisabledText = textView + + let isConsentGiven = GeofencingUtils.getUserConsent() + + self.geofencingBt?.isHidden = !isConsentGiven + self.geofenceDisabledText?.isHidden = isConsentGiven + } + + @objc func geofencingOptions(sender: UIButton) { + let alert = UIAlertController(title: "Geofencing Options", message: "", preferredStyle: .actionSheet) + alert.popoverPresentationController?.sourceView = sender + + alert.addAction(UIAlertAction(title: "Load HSL geofences", style: .default) { _ in + self.loadGeofences() + }) + + alert.addAction(UIAlertAction(title: "Remove all geofences", style: .destructive) { _ in + self.clearGeofences() + }) + + alert.addAction(UIAlertAction(title: "Geofence boundaries courtesy Helsingin seuden liiken HSL(CC-BY)", style: .default) { _ in + UIApplication.shared.open(URL(string: "https://hri.fi/data/en_GB/dataset/hsl-n-taksavyohykkeet")!, options: [:], completionHandler: nil) + }) + + alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel) { _ in }) + + present(alert, animated: true) + } + + private func showSpinnerIfNeeded() { + if geofencingSpinner == nil { + let spinner = UIActivityIndicatorView(style: .large) + + spinner.translatesAutoresizingMaskIntoConstraints = false + spinner.startAnimating() + spinner.hidesWhenStopped = true + view.addSubview(spinner) + + NSLayoutConstraint.activate([ + spinner.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24), + spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor), + spinner.widthAnchor.constraint(equalToConstant: 200), + spinner.heightAnchor.constraint(equalToConstant: 40) + ]) + + geofencingBt?.isEnabled = false + geofencingBt?.backgroundColor = #colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1) + + geofencingSpinner = spinner + } + } + + private func hideSpinner() { + guard let spinner = geofencingSpinner else { return } + spinner.stopAnimating() + spinner.removeFromSuperview() + geofencingSpinner = nil + geofencingBt?.isEnabled = true + geofencingBt?.backgroundColor = #colorLiteral(red: 0, green: 0.4784313725, blue: 0.9882352941, alpha: 1) + } + + func showToast(message: String, duration: Double = 2.0) { + let toastLabel = UILabel() + toastLabel.text = message + toastLabel.textColor = .white + toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6) + toastLabel.textAlignment = .center + toastLabel.font = UIFont.systemFont(ofSize: 14) + toastLabel.numberOfLines = 0 + toastLabel.layer.cornerRadius = 10 + toastLabel.clipsToBounds = true + + let maxSizeTitle = CGSize(width: self.view.bounds.size.width - 40, height: self.view.bounds.size.height) + var expectedSizeTitle = toastLabel.sizeThatFits(maxSizeTitle) + expectedSizeTitle.width = min(maxSizeTitle.width, expectedSizeTitle.width) + expectedSizeTitle.height = min(50, expectedSizeTitle.height) + + toastLabel.frame = CGRect( + x: (self.view.frame.size.width - expectedSizeTitle.width) / 2, + y: self.view.frame.size.height - 150, + width: expectedSizeTitle.width + 20, + height: expectedSizeTitle.height + 10 + ) + + self.view.addSubview(toastLabel) + + UIView.animate(withDuration: 0.5, delay: duration, options: .curveEaseInOut) { + toastLabel.alpha = 0 + } completion: { _ in + toastLabel.removeFromSuperview() + } + } +} + +/// Support Uset Notifications use case +extension GeofencingExample { + func updateEnterGeofenceId(id: String) { + enterGeofenceId = id + updateGeofences() + } + + func updateExitGeofenceId(id: String) { + exitGeofenceId = id + updateGeofences() + } + + func updateDwellGeofenceId(id: String) { + dwellGeofenceId = id + updateGeofences() + } +} + +private enum Constants { + static let defaultRadius: UInt32 = 300 + static let enterGeofenceDataSourceId = "enter-geofence-source" + static let exitGeofenceDataSourceId = "exit-geofence-source" + static let dwellGeofenceDataSourceId = "dwell-geofence-source" + // HSL public dataset of Helsinki tariff zones + static let datastoreBaseUrl = "https://opendata.arcgis.com/datasets/89b6b5142a9b4bb9a5c5f4404ff28963_0.geojson" +} + +extension Turf.Feature { + var coordinate: CLLocationCoordinate2D? { geometry?.point?.coordinates } +} diff --git a/Sources/Examples/All Examples/GlobeFlyToExample.swift b/Sources/Examples/All Examples/GlobeFlyToExample.swift new file mode 100644 index 000000000000..1852d61080a0 --- /dev/null +++ b/Sources/Examples/All Examples/GlobeFlyToExample.swift @@ -0,0 +1,93 @@ +import Foundation +import UIKit +import MapboxMaps +import CoreLocation + +final class GlobeFlyToExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var isAtStart = true + private var instuctionLabel = UILabel(frame: CGRect.zero) + private var cancelables = Set() + + private var cameraStart = CameraOptions( + center: CLLocationCoordinate2D(latitude: 36, longitude: 80), + zoom: 1.0, + bearing: 0, + pitch: 0) + + private var cameraEnd = CameraOptions( + center: CLLocationCoordinate2D(latitude: 46.58842, longitude: 8.11862), + zoom: 12.5, + bearing: 130.0, + pitch: 75.0) + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds, mapInitOptions: .init(styleURI: .satelliteStreets)) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + mapView.mapboxMap.setCamera(to: .init(center: CLLocationCoordinate2D(latitude: 40, longitude: -78), zoom: 1.0)) + try! self.mapView.mapboxMap.setProjection(StyleProjection(name: .globe)) + + mapView.mapboxMap.onStyleLoaded.observeNext { _ in + try! self.mapView.mapboxMap.setAtmosphere(Atmosphere()) + self.addTerrain() + self.finish() + }.store(in: &cancelables) + + mapView.gestures.onMapTap.observe { [weak self] _ in + self?.animateCameraOnClick() + }.store(in: &cancelables) + + instuctionLabel.text = "Tap anywhere on the map" + instuctionLabel.textColor = UIColor.black + instuctionLabel.textAlignment = .center + instuctionLabel.layer.backgroundColor = UIColor.gray.cgColor + instuctionLabel.layer.cornerRadius = 20.0 + instuctionLabel.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(mapView) + view.addSubview(instuctionLabel) + installConstraints() + } + + func installConstraints() { + let safeView = view.safeAreaLayoutGuide + + NSLayoutConstraint.activate([ + instuctionLabel.topAnchor.constraint(equalTo: safeView.bottomAnchor, constant: -70), + instuctionLabel.bottomAnchor.constraint(equalTo: safeView.bottomAnchor, constant: -30), + instuctionLabel.leadingAnchor.constraint(equalTo: safeView.leadingAnchor, constant: 70), + instuctionLabel.trailingAnchor.constraint(equalTo: safeView.trailingAnchor, constant: -70) + ]) + + } + + func addTerrain() { + var demSource = RasterDemSource(id: "mapbox-dem") + demSource.url = "mapbox://mapbox.mapbox-terrain-dem-v1" + // Setting the `tileSize` to 514 provides better performance and adds padding around the outside + // of the tiles. + demSource.tileSize = 514 + demSource.maxzoom = 14.0 + try! mapView.mapboxMap.addSource(demSource) + + var terrain = Terrain(sourceId: "mapbox-dem") + terrain.exaggeration = .constant(1.5) + + try! mapView.mapboxMap.setTerrain(terrain) + } + + private func animateCameraOnClick() { + instuctionLabel.isHidden = true + var target = CameraOptions() + if isAtStart { + target = self.cameraEnd + } else { + target = self.cameraStart + } + isAtStart = !isAtStart + mapView.camera.fly(to: target, duration: 12) + + } +} diff --git a/Apps/Examples/Examples/All Examples/HeatmapLayerGlobeExample.swift b/Sources/Examples/All Examples/HeatmapLayerGlobeExample.swift similarity index 76% rename from Apps/Examples/Examples/All Examples/HeatmapLayerGlobeExample.swift rename to Sources/Examples/All Examples/HeatmapLayerGlobeExample.swift index e9421c4bf81b..f3d1c22c4b84 100644 --- a/Apps/Examples/Examples/All Examples/HeatmapLayerGlobeExample.swift +++ b/Sources/Examples/All Examples/HeatmapLayerGlobeExample.swift @@ -1,37 +1,30 @@ import UIKit import MapboxMaps -@objc(HeatmapLayerGlobeExample) -public class HeatmapLayerGlobeExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! - internal let earthquakeSourceId: String = "earthquakes" - internal let earthquakeLayerId: String = "earthquake-viz" - internal let heatmapLayerId = "earthquakes-heat" - internal let heatmapLayerSource = "earthquakes" - internal let circleLayerId = "earthquakes-circle" - internal let earthquakeURL = URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson")! - - override public func viewDidLoad() { +final class HeatmapLayerGlobeExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { super.viewDidLoad() // Center the map over the United States. - let centerCoordinate = CLLocationCoordinate2D(latitude: 50, - longitude: -120) + let centerCoordinate = CLLocationCoordinate2D(latitude: 50, longitude: -120) let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 1.0), styleURI: .dark) + // Set up map view mapView = MapView(frame: view.bounds, mapInitOptions: options) mapView.ornaments.options.scaleBar.visibility = .hidden mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - try! self.mapView.mapboxMap.style.setProjection(StyleProjection(name: .globe)) + try! self.mapView.mapboxMap.setProjection(StyleProjection(name: .globe)) view.addSubview(mapView) - mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in guard let self = self else { return } - try! self.mapView.mapboxMap.style.setAtmosphere(Atmosphere()) + try! self.mapView.mapboxMap.setAtmosphere(Atmosphere()) self.addRuntimeLayers() self.finish() - } + }.store(in: &cancelables) } func addRuntimeLayers() { @@ -41,12 +34,12 @@ public class HeatmapLayerGlobeExample: UIViewController, ExampleProtocol { } func createEarthquakeSource() { - var earthquakeSource = GeoJSONSource() + var earthquakeSource = GeoJSONSource(id: self.earthquakeSourceId) earthquakeSource.data = .url(self.earthquakeURL) earthquakeSource.generateId = true do { - try mapView.mapboxMap.style.addSource(earthquakeSource, id: self.earthquakeSourceId) + try mapView.mapboxMap.addSource(earthquakeSource) } catch { print("Ran into an error adding a source: \(error)") } @@ -55,8 +48,7 @@ public class HeatmapLayerGlobeExample: UIViewController, ExampleProtocol { func createHeatmapLayer() { // Add earthquake-viz layer - var heatmapLayer = HeatmapLayer(id: self.heatmapLayerId) - heatmapLayer.source = self.earthquakeSourceId + var heatmapLayer = HeatmapLayer(id: self.heatmapLayerId, source: self.earthquakeSourceId) heatmapLayer.maxZoom = 9.0 heatmapLayer.sourceLayer = self.heatmapLayerSource @@ -126,27 +118,26 @@ public class HeatmapLayerGlobeExample: UIViewController, ExampleProtocol { ) do { - try mapView.mapboxMap.style.addLayer(heatmapLayer, layerPosition: .above("waterway-label")) + try mapView.mapboxMap.addLayer(heatmapLayer, layerPosition: .above("waterway-label")) } catch { print("Ran into an error adding a layer: \(error)") } } - public func createCircleLayer() { + func createCircleLayer() { - var circleLayerSource = GeoJSONSource() + var circleLayerSource = GeoJSONSource(id: self.earthquakeSourceId) circleLayerSource.data = .url(self.earthquakeURL) circleLayerSource.generateId = true do { - try mapView.mapboxMap.style.addSource(circleLayerSource, id: self.earthquakeSourceId) + try mapView.mapboxMap.addSource(circleLayerSource) } catch { print("Ran into an error adding a source: \(error)") } // Add circle layer - var circleLayer = CircleLayer(id: self.circleLayerId) - circleLayer.source = self.earthquakeSourceId + var circleLayer = CircleLayer(id: self.circleLayerId, source: self.earthquakeSourceId) // Adjust the circle layer radius by zoom level circleLayer.circleRadius = .expression( @@ -217,10 +208,19 @@ public class HeatmapLayerGlobeExample: UIViewController, ExampleProtocol { circleLayer.circleStrokeWidth = .constant(0.1) do { - try mapView.mapboxMap.style.addLayer(circleLayer, layerPosition: .below(self.heatmapLayerId)) + try mapView.mapboxMap.addLayer(circleLayer, layerPosition: .below(self.heatmapLayerId)) } catch { print("Ran into an error adding a layer: \(error)") } } } + +private extension HeatmapLayerGlobeExample { + var earthquakeSourceId: String { "earthquakes" } + var earthquakeLayerId: String { "earthquake-viz" } + var heatmapLayerId: String { "earthquakes-heat" } + var heatmapLayerSource: String { "earthquakes" } + var circleLayerId: String { "earthquakes-circle" } + var earthquakeURL: URL { URL(string: "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson")! } +} diff --git a/Sources/Examples/All Examples/InsetMapExample.swift b/Sources/Examples/All Examples/InsetMapExample.swift new file mode 100644 index 000000000000..25864598179b --- /dev/null +++ b/Sources/Examples/All Examples/InsetMapExample.swift @@ -0,0 +1,118 @@ +// Add a main map and a small inset map that does not respond to gestures +// the inset map updates its center when the main map is moved +// the inset map displays a polygon based on the current viewport of the main map +import UIKit +import MapboxMaps + +@objc(InsetMapExample) +final class InsetMapExample: UIViewController, ExampleProtocol { + static let styleUri = StyleURI(rawValue: "mapbox://styles/mapbox/cj5l80zrp29942rmtg0zctjto")! + + // create MapViews for the main map and the inset map + private var mapView: MapView! + private var insetMapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // set up the main map + let cameraCenter = CLLocationCoordinate2D(latitude: 39.13954, longitude: -77.25637) + let initOptions = MapInitOptions( + cameraOptions: CameraOptions(center: cameraCenter, zoom: 6.52), + styleURI: Self.styleUri + ) + mapView = MapView(frame: view.bounds, mapInitOptions: initOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + mapView.gestures.options.rotateEnabled = false + + view.addSubview(mapView) + + mapView.mapboxMap.onCameraChanged.observe { [weak self] event in + self?.updateInsetMap(center: event.cameraState.center) + }.store(in: &cancelables) + + setupInsetMapView() + } + + private func setupInsetMapView() { + let myInsetMapInitOptions = MapInitOptions(cameraOptions: CameraOptions(zoom: 0), styleURI: Self.styleUri ) + + // position the inset map in the bottom left corner + insetMapView = MapView( + frame: CGRect(x: 8, y: (view.frame.size.height - 250), width: 120, height: 120), + mapInitOptions: myInsetMapInitOptions + ) + + // hide the scaleBar, logo, and attribution on the inset map + insetMapView.ornaments.options.scaleBar.visibility = .hidden + insetMapView.ornaments.logoView.isHidden = true + insetMapView.ornaments.attributionButton.isHidden = true + + // disable panning and zooming on the inset map + insetMapView.gestures.options.panEnabled = false + insetMapView.gestures.options.doubleTapToZoomInEnabled = false + insetMapView.gestures.options.doubleTouchToZoomOutEnabled = false + + // add a border, radius, and shadow around the inset map + insetMapView.layer.borderWidth = 2 + insetMapView.layer.cornerRadius = 10 + insetMapView.layer.borderColor = UIColor.gray.cgColor + insetMapView.layer.shadowColor = UIColor.black.cgColor + insetMapView.layer.shadowOffset = CGSize(width: 3, height: 3) + insetMapView.layer.shadowOpacity = 0.7 + insetMapView.layer.shadowRadius = 4.0 + insetMapView.layer.masksToBounds = true + + view.addSubview(insetMapView) + + // when the inset map loads, add a source and layer to show the bounds rectangle for the main map + insetMapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + guard let self else { return } + + let geoJSONSource = GeoJSONSource(id: "bounds") + + // Create a line layer + var lineBoundsLayer = LineLayer(id: "line-bounds", source: geoJSONSource.id) + lineBoundsLayer.lineWidth = .constant(2.5) + lineBoundsLayer.lineColor = .constant(StyleColor(#colorLiteral(red: 1, green: 0.99608, blue: 0.11373, alpha: 1))) + + // Add the source and layers to the map style + try! self.insetMapView.mapboxMap.addSource(geoJSONSource) + try! self.insetMapView.mapboxMap.addLayer(lineBoundsLayer) + + self.updateInsetMap(center: self.mapView.mapboxMap.cameraState.center) + }.store(in: &cancelables) + + } + + private func updateInsetMap(center: CLLocationCoordinate2D) { + // set the inset map's center to the main map's center + insetMapView.mapboxMap.setCamera(to: CameraOptions(center: center)) + + // get the main map's bounds + let bounds = mapView.mapboxMap.coordinateBounds(for: mapView.bounds) + + // create a geojson polygon based on the main map's bounds + // use it to update the "bounds" source in the inset map + let polygon = Polygon( + outerRing: Ring( + coordinates: [ + CLLocationCoordinate2D(latitude: bounds.south, longitude: bounds.west), + CLLocationCoordinate2D(latitude: bounds.north, longitude: bounds.west), + CLLocationCoordinate2D(latitude: bounds.north, longitude: bounds.east), + CLLocationCoordinate2D(latitude: bounds.south, longitude: bounds.east), + CLLocationCoordinate2D(latitude: bounds.south, longitude: bounds.west) + ] + ) + ) + + insetMapView.mapboxMap.updateGeoJSONSource(withId: "bounds", geoJSON: polygon.geometry.geoJSONObject) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} diff --git a/Sources/Examples/All Examples/Lab/ColorThemeMapExample.swift b/Sources/Examples/All Examples/Lab/ColorThemeMapExample.swift new file mode 100644 index 000000000000..d39d877f3b92 --- /dev/null +++ b/Sources/Examples/All Examples/Lab/ColorThemeMapExample.swift @@ -0,0 +1,83 @@ +import UIKit +@_spi(Experimental) import MapboxMaps + +final class ColorThemeMapExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var mapUseTheme = true + private var circleUseTheme = true + private var cancellables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + let mapInitOptions = MapInitOptions(styleURI: .streets) + mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + + view.addSubview(mapView) + view.backgroundColor = .skyBlue + mapView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) + + try! mapView.mapboxMap.setColorTheme(ColorTheme(uiimage: UIImage(named: "monochrome_lut")!)) + addTestLayer() + + mapView.gestures.onMapTap.observe { [weak self] _ in + guard let self else { return } + + self.mapUseTheme.toggle() + if self.mapUseTheme { + try! self.mapView.mapboxMap.setColorTheme(ColorTheme(uiimage: UIImage(named: "monochrome_lut")!)) + } else { + try! self.mapView.mapboxMap.removeColorTheme() + } + } + .store(in: &cancellables) + + mapView.gestures.onLayerTap("blue-layer") { [weak self] _, _ in + guard let self else { return true } + + self.circleUseTheme.toggle() + self.addTestLayer(useTheme: self.circleUseTheme) + return true + } + .store(in: &cancellables) + } + + private func addTestLayer( + id: String = "blue-layer", + radius: LocationDistance = 2, + color: UIColor = .blue, + coordinate: CLLocationCoordinate2D = .init(latitude: 40, longitude: -104), + useTheme: Bool = true + ) { + let sourceId = "\(id)-source" + try? mapView.mapboxMap.removeLayer(withId: id) + try? mapView.mapboxMap.removeLayer(withId: "\(id)-border") + try? mapView.mapboxMap.removeSource(withId: sourceId) + + mapView.mapboxMap.setMapStyleContent { + FillLayer(id: id, source: sourceId) + .fillColorUseTheme(useTheme ? .default : .none) + .fillColor(color) + .fillOpacity(0.4) + LineLayer(id: "\(id)-border", source: sourceId) + .lineColor(color.darker) + .lineColorUseTheme(useTheme ? .default : .none) + .lineOpacity(0.4) + .lineWidth(2) + GeoJSONSource(id: sourceId) + .data(.geometry(.polygon(Polygon(center: coordinate, radius: radius * 1000000, vertices: 60)))) + } + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} diff --git a/Sources/Examples/All Examples/Lab/CombineExample.swift b/Sources/Examples/All Examples/Lab/CombineExample.swift new file mode 100644 index 000000000000..6b3d08640a8d --- /dev/null +++ b/Sources/Examples/All Examples/Lab/CombineExample.swift @@ -0,0 +1,78 @@ +import UIKit +import MapboxMaps +import Combine + +/// This examples shows how to use Map events with Combine framework. +final class CombineExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var tokens = Set() + private let cameraLabel = UILabel.makeCameraLabel() + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + cameraLabel.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(cameraLabel) + cameraLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24).isActive = true + cameraLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + + // The on-prefixed event signals can be used as publishers: + mapView.mapboxMap.onCameraChanged + .debounce(for: .milliseconds(500), scheduler: DispatchQueue.main) + .map(\.cameraState) + .print("Camera State") + .sink { [weak self] state in + self?.cameraLabel.attributedText = .formatted(cameraSate: state) + self?.cameraLabel.setNeedsLayout() + } + .store(in: &tokens) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} + +private extension UILabel { + static func makeCameraLabel() -> UILabel { + let label = UILabel() + label.numberOfLines = 0 + label.backgroundColor = UIColor.systemBackground + label.layer.cornerRadius = 5 + label.layer.masksToBounds = true + return label + } +} + +private extension NSAttributedString { + static func logString(_ text: String, bold: Bool = false) -> NSAttributedString { + var attributes = [NSAttributedString.Key: Any]() + attributes[.font] = UIFont.monospacedSystemFont(ofSize: 13, weight: bold ? .bold : .regular) + return NSAttributedString(string: text, attributes: attributes) + } + + static func formatted(cameraSate: CameraState) -> NSAttributedString { + let str = NSMutableAttributedString() + str.append(.logString("lat:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.center.latitude))\n")) + str.append(.logString("lon:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.center.longitude))\n")) + str.append(.logString("zoom:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.zoom))")) + if cameraSate.bearing != 0 { + str.append(.logString("\nbearing:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.bearing))")) + } + if cameraSate.pitch != 0 { + str.append(.logString("\npitch:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.pitch))")) + } + return str + } +} diff --git a/Sources/Examples/All Examples/Lab/CombineLocationExample.swift b/Sources/Examples/All Examples/Lab/CombineLocationExample.swift new file mode 100644 index 000000000000..b9a8d0503e20 --- /dev/null +++ b/Sources/Examples/All Examples/Lab/CombineLocationExample.swift @@ -0,0 +1,70 @@ +import UIKit +import MapboxMaps +import Combine +import Turf + +/// This examples shows how to use Combine framework to drive the Puck's location and heading. +final class CombineLocationExample: UIViewController, ExampleProtocol { + @Published + private var location = Location(coordinate: CLLocationCoordinate2D(latitude: 60.17195694011002, longitude: 24.945389069265598)) + @Published + private var heading = Heading(direction: 0, accuracy: 0) + + private var mapView: MapView! + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Set the custom location & heading providers + mapView.location.override( + locationProvider: $location.map { [$0] }.eraseToSignal(), + headingProvider: $heading.eraseToSignal()) + + // Enable the location puck + mapView.location.options = LocationOptions( + puckType: .puck2D(.makeDefault(showBearing: true)), + puckBearing: .heading, + puckBearingEnabled: true + ) + + // Set up the follow-puck viewport, so camera will always be focused on the puck + mapView.viewport.transition( + to: mapView.viewport.makeFollowPuckViewportState(options: .init(zoom: 16, bearing: .constant(0), pitch: 0)), + transition: mapView.viewport.makeImmediateViewportTransition()) + + mapView.gestures.singleTapGestureRecognizer.addTarget(self, action: #selector(onTap)) + + let guideLabel = UILabel() + guideLabel.text = "Tap anywhere on the map to place the puck" + guideLabel.backgroundColor = .systemBackground + guideLabel.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(guideLabel) + NSLayoutConstraint.activate([ + guideLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -30), + guideLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor) + ]) + } + + @objc func onTap(_ gesture: UIGestureRecognizer) { + // Place the puck to the tap location + let point = gesture.location(in: mapView) + let coordinate = mapView.mapboxMap.coordinate(for: point) + + // Calculating the angle between the current coordinate, and the target coordinate + let headingDirection = location.coordinate.direction(to: coordinate) + + location = Location(coordinate: coordinate) + heading = Heading(direction: headingDirection, accuracy: 0) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} diff --git a/Sources/Examples/All Examples/Lab/Lights3DExample.swift b/Sources/Examples/All Examples/Lab/Lights3DExample.swift new file mode 100644 index 000000000000..d4b0f9097ea7 --- /dev/null +++ b/Sources/Examples/All Examples/Lab/Lights3DExample.swift @@ -0,0 +1,177 @@ +import UIKit +import MapboxMaps + +final class Lights3DExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + let directionalLightId = "directional-light" + let ambientLightId = "ambient-light" + + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 40.713589095634475, + longitude: -74.00370030835559), + zoom: 16.868, + bearing: 60.54, + pitch: 60) + mapView.mapboxMap.setCamera(to: cameraOptions) + view.addSubview(mapView) + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.finish() + }.store(in: &cancelables) + + navigationItem.rightBarButtonItem = sunAnimationButton() + + updateMapState() + } + + func sunAnimationButton() -> UIBarButtonItem { + return UIBarButtonItem(image: UIImage(systemName: "sunrise"), style: .plain, target: self, action: #selector(startSunAnimation)) + } + + func updateMapState(azimuth: Double = 210, polarAngle: Double = 30, ambientColor: UIColor = .lightGray) { + /// We use the new experimental style content feature to set the lights. + mapView.mapboxMap.setMapStyleContent { + Atmosphere() + .range(start: 0, end: 12) + .horizonBlend(0.1) + .starIntensity(0.2) + .color(StyleColor(red: 240, green: 196, blue: 152, alpha: 1)!) + .highColor(StyleColor(red: 221, green: 209, blue: 197, alpha: 1)!) + .spaceColor(StyleColor(red: 153, green: 180, blue: 197, alpha: 1)!) + DirectionalLight(id: directionalLightId) + .intensity(0.5) + .direction(azimuthal: azimuth, polar: polarAngle) + .directionTransition(.zero) + .castShadows(true) + .shadowIntensity(1) + AmbientLight(id: ambientLightId) + .color(ambientColor) + .intensity(0.5) + } + } + + var animationProgress = 0.0 { + didSet { + // Calculate azimuth and polar angle based on animation progress + let azimuth = 180.0 - (360.0 * animationProgress) + let polarAngle = 45.0 * sin(animationProgress * Double.pi) + let color = generateSunColor(progress: animationProgress) + + updateMapState(azimuth: azimuth, polarAngle: polarAngle, ambientColor: color) + } + } + + var animation: AnimationData? + + class AnimationData { + let animationStart: CFTimeInterval + let animationEnd: CFTimeInterval + let displayLink: CADisplayLink + + init(start: CFTimeInterval = CACurrentMediaTime(), duration: CFTimeInterval, displayLink: CADisplayLink) { + self.animationStart = start + self.animationEnd = start + duration + self.displayLink = displayLink + + displayLink.add(to: .main, forMode: .default) + } + + func animationProgress(for time: CFTimeInterval) -> Double { + guard time < animationEnd else { return 1 } + let duration = time - animationStart + guard duration > 0 else { return 0 } + + let expectedDuration = animationEnd - animationStart + + return duration / expectedDuration + } + + deinit { + displayLink.remove(from: .main, forMode: .default) + } + } + + @objc func startSunAnimation() { + let duration: TimeInterval = 8 + + let displayLink = CADisplayLink(target: self, selector: #selector(tickAnimation(displayLink:))) + animation = AnimationData(duration: duration, displayLink: displayLink) + } + + @objc func tickAnimation(displayLink: CADisplayLink) { + guard let animation = animation else { return assertionFailure("DisplayLink should not trigger without animation") } + + animationProgress = animation.animationProgress(for: displayLink.targetTimestamp) + if animationProgress >= 1 { + self.animation = nil + } + } + + func generateSunColor(progress: Double) -> UIColor { + let yellowSunColor = UIColor(red: 255/255, green: 242/255, blue: 0/255, alpha: 1) + let orangeSunColor = UIColor(red: 255/255, green: 180/255, blue: 0/255, alpha: 1) + let darkOrangeSunColor = UIColor(red: 204/255, green: 51/255, blue: 0/255, alpha: 1) + + switch progress { + case 0..<0.2: + return interpolateColor(from: .white, + to: yellowSunColor, + with: (progress) / 0.2) + case 0.2..<0.4: + return interpolateColor(from: yellowSunColor, + to: .white, + with: (progress - 0.2) / 0.2) + case 0.4..<0.7: + return .white + case 0.7..<0.85: + return interpolateColor(from: .white, + to: orangeSunColor, + with: (progress - 0.7) / 0.15) + case 0.85...1.0: + return interpolateColor(from: orangeSunColor, + to: darkOrangeSunColor, + with: (progress - 0.85) / 0.15) + default: + return .purple + } + } + + func generateLightIntensityValue(progress: Double, acceptableValues: ClosedRange = 0.1...0.5) -> Double { + let intervalWidth = acceptableValues.upperBound - acceptableValues.lowerBound + + if progress <= 0.5 { + return acceptableValues.lowerBound + intervalWidth * (2 * progress) + } else { + return acceptableValues.upperBound - (progress - 0.5) * (2 * intervalWidth) + } + } + + func interpolateColor(from startColor: UIColor, to endColor: UIColor, with interpolationFactor: Double) -> UIColor { + var startRed: CGFloat = 0 + var startGreen: CGFloat = 0 + var startBlue: CGFloat = 0 + var startAlpha: CGFloat = 0 + startColor.getRed(&startRed, green: &startGreen, blue: &startBlue, alpha: &startAlpha) + + var endRed: CGFloat = 0 + var endGreen: CGFloat = 0 + var endBlue: CGFloat = 0 + var endAlpha: CGFloat = 0 + endColor.getRed(&endRed, green: &endGreen, blue: &endBlue, alpha: &endAlpha) + + let interpolatedRed = startRed + CGFloat(interpolationFactor) * (endRed - startRed) + let interpolatedGreen = startGreen + CGFloat(interpolationFactor) * (endGreen - startGreen) + let interpolatedBlue = startBlue + CGFloat(interpolationFactor) * (endBlue - startBlue) + let interpolatedAlpha = startAlpha + CGFloat(interpolationFactor) * (endAlpha - startAlpha) + + return UIColor(red: min(max(0, interpolatedRed), 1), + green: min(max(0, interpolatedGreen), 1), + blue: min(max(0, interpolatedBlue), 1), + alpha: min(max(0, interpolatedAlpha), 1)) + } +} diff --git a/Sources/Examples/All Examples/Lab/MapEventsExample.swift b/Sources/Examples/All Examples/Lab/MapEventsExample.swift new file mode 100644 index 000000000000..5f8b313e8a7a --- /dev/null +++ b/Sources/Examples/All Examples/Lab/MapEventsExample.swift @@ -0,0 +1,308 @@ +import UIKit +import MapboxMaps + +final class MapEventsExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private let tableView = UITableView() + private let cameraLabel = UILabel.makeCameraLabel() + private var clearButton = UIButton(type: .system) + private var cancelables = Set() + var entries = [NSAttributedString]() { + didSet { + tableView.reloadData() + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .systemBackground + + mapView = MapView(frame: view.bounds) + mapView.ornaments.options.scaleBar.visibility = .visible + view.addSubview(mapView) + + tableView.dataSource = self + tableView.register(LogCell.self, forCellReuseIdentifier: "cell") + tableView.allowsSelection = false + tableView.separatorStyle = .none + view.addSubview(tableView) + + clearButton.setTitle("Clear", for: .normal) + clearButton.addTarget(self, action: #selector(clear), for: .touchUpInside) + view.addSubview(clearButton) + + view.addSubview(cameraLabel) + + let map = mapView.mapboxMap! + logEvent(map.onMapLoaded) + logEvent(map.onMapLoadingError) + logEvent(map.onStyleLoaded) + logEvent(map.onStyleDataLoaded) + logEvent(map.onMapIdle) + logEvent(map.onSourceAdded) + logEvent(map.onSourceRemoved) + logEvent(map.onSourceDataLoaded) + logEvent(map.onStyleImageMissing) + logEvent(map.onStyleImageRemoveUnused) + // onResourceRequest produces too much logs for demonstration, uncomment it if needed. + // logEvent(mapView.mapboxMap.onResourceRequest) + + map.onCameraChanged.observe { [weak self] event in + self?.cameraLabel.attributedText = .formatted(cameraSate: event.cameraState) + self?.view.setNeedsLayout() + }.store(in: &cancelables) + } + + @objc private func clear() { + entries.removeAll() + } + + func logEvent(_ signal: Signal) { + signal.observe { [weak self] event in + self?.entries.append(.formatted(event: event)) + print("MapEvent: \(event.logString)") + }.store(in: &cancelables) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + let bounds = view.bounds.inset(by: UIEdgeInsets( + top: view.safeAreaInsets.top, left: 0, bottom: 0, right: 0)) + let halfHeight = bounds.height / 2 + 100 + + mapView.frame = CGRect(x: 0, y: bounds.minY, width: bounds.width, height: halfHeight) + + tableView.frame = CGRect(x: 0, y: bounds.minY + halfHeight, width: bounds.width, height: bounds.height - halfHeight) + + let buttonSize = clearButton.sizeThatFits(bounds.size) + clearButton.frame = CGRect( + origin: CGPoint( + x: bounds.width - buttonSize.width - 10, + y: bounds.minY + halfHeight + 10), + size: buttonSize) + + let labelSize = cameraLabel.sizeThatFits(bounds.size) + cameraLabel.frame = CGRect( + origin: CGPoint( + x: (bounds.width - labelSize.width) / 2, + y: bounds.minY + halfHeight - labelSize.height - 10), + size: labelSize) + } +} + +extension MapEventsExample: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { 1 } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { entries.count } + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! LogCell + cell.logLabel.attributedText = entries[entries.count - 1 - indexPath.row] + return cell + } +} + +private class LogCell: UITableViewCell { + let logLabel = UILabel() + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + logLabel.numberOfLines = 0 + logLabel.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(logLabel) + NSLayoutConstraint.activate([ + logLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 6), + logLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -6), + logLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 3), + logLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -3), + ]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +protocol LogableEvent { + var name: String { get } + var info: String { get } +} + +extension LogableEvent { + var logString: String { + return "[\(name)] \(info)" + } +} + +private extension NSAttributedString { + static func logString(_ text: String, bold: Bool = false) -> NSAttributedString { + var attributes = [NSAttributedString.Key: Any]() + attributes[.font] = UIFont.monospacedSystemFont(ofSize: 13, weight: bold ? .bold : .regular) + return NSAttributedString(string: text, attributes: attributes) + } + + static func formatted(cameraSate: CameraState) -> NSAttributedString { + let str = NSMutableAttributedString() + str.append(.logString("lat:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.center.latitude))\n")) + str.append(.logString("lon:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.center.longitude))\n")) + str.append(.logString("zoom:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.zoom))")) + if cameraSate.bearing != 0 { + str.append(.logString("\nbearing:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.bearing))")) + } + if cameraSate.pitch != 0 { + str.append(.logString("\npitch:", bold: true)) + str.append(.logString(" \(String(format: "%.2f", cameraSate.pitch))")) + } + return str + } + + static func formatted(event: LogableEvent) -> NSAttributedString { + let str = NSMutableAttributedString() + str.append(.logString(event.name, bold: true)) + if !event.info.isEmpty { + let withNewLines = event.info.replacingOccurrences(of: ", ", with: "\n") + str.append(.logString("\n\(withNewLines)")) + } + return str + } +} + +extension MapLoaded: LogableEvent { + var name: String { "MapLoaded" } + var info: String { "ti: \(timeInterval.log)" } +} + +extension MapIdle: LogableEvent { + var name: String { "MapIdle" } + var info: String { "ts: \(timestamp)" } +} + +extension MapLoadingError: LogableEvent { + var name: String { "MapLoadingError" } + var info: String { "ts: \(timestamp), type: \(type), message: \(message), sourceId: \(String(describing: sourceId)), tileId: \(tileId?.log ?? "nil")" } +} + +extension StyleLoaded: LogableEvent { + var name: String { "StyleLoaded" } + var info: String { "ti: \(timeInterval.log)" } +} + +extension StyleDataLoaded: LogableEvent { + var name: String { "StyleDataLoaded" } + var info: String { "ti: \(timeInterval.log), type: \(type)" } +} + +extension SourceAdded: LogableEvent { + var name: String { "SourceAdded" } + var info: String { "ts: \(timestamp), sourceId: \(sourceId)" } +} + +extension SourceRemoved: LogableEvent { + var name: String { "SourceRemoved" } + var info: String { "ts: \(timestamp), sourceId: \(sourceId)" } +} + +extension SourceDataLoaded: LogableEvent { + var name: String { "SourceDataLoaded" } + var info: String { "ti: \(timeInterval.log), sourceId: \(sourceId), tileId: \(tileId?.log ?? "nil"), dataID: \(dataId ?? "nil"), loaded: \(loaded.log)" } +} + +extension StyleImageMissing: LogableEvent { + var name: String { "StyleImageMissing" } + var info: String { "ts: \(timestamp), imageId: \(imageId)" } +} + +extension StyleImageRemoveUnused: LogableEvent { + var name: String { "StyleImageRemoveUnused" } + var info: String { "ts: \(timestamp), imageId: \(imageId)" } +} + +extension ResourceRequest: LogableEvent { + var name: String { "ResourceRequest" } + var info: String { "ti: \(timeInterval), source: \(source), url: \(request.url)" } +} + +extension Optional where Wrapped: CustomStringConvertible { + var log: String { + switch self { + case .none: return "nil" + case let .some(val): return "\(val)" + } + } +} + +extension EventTimeInterval { + var log: String { + "\(begin) - \(end)" + } +} + +extension CanonicalTileID { + var log: String { + "\(z)/\(x)/\(y)" + } +} + +extension CustomRasterSourceTileStatus { + var log: String { + switch self { + case .required: return "required" + case .optional: return "optional" + case .notNeeded: return "notNeeded" + default: return "unknown" + } + } +} + +extension StyleDataLoadedType: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .style: return "style" + case .sources: return "sources" + case .sprite: return "sprite" + default: return "unknown" + } + } +} + +extension SourceDataLoadedType: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .metadata: return "metadata" + case .tile: return "tile" + default: return "unknown" + } + } +} + +extension RequestDataSourceType: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .asset: return "asset" + case .database: return "database" + case .fileSystem: return "fileSystem" + case .network: return "network" + case .resourceLoader: return "resourceLoader" + default: return "unknown" + } + } +} + +private extension UILabel { + static func makeCameraLabel() -> UILabel { + let label = UILabel() + label.numberOfLines = 0 + label.backgroundColor = UIColor.systemBackground + label.layer.cornerRadius = 5 + label.layer.masksToBounds = true + return label + } +} diff --git a/Sources/Examples/All Examples/Lab/MapRecorderExample.swift b/Sources/Examples/All Examples/Lab/MapRecorderExample.swift new file mode 100644 index 000000000000..75de7130b9bd --- /dev/null +++ b/Sources/Examples/All Examples/Lab/MapRecorderExample.swift @@ -0,0 +1,42 @@ +import UIKit +@_spi(Experimental) import MapboxMaps + +final class MapRecorderExample: UIViewController, ExampleProtocol { + + private var mapView: MapView! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + mapView = MapView(frame: view.bounds) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + mapView.ornaments.options.scaleBar.visibility = .visible + + view.addSubview(mapView) + + mapView.mapboxMap.onStyleLoaded.observeNext { _ in + // Once the Style is loaded, create the ``MapRecorder`` and start the recording + guard let recorder = try? self.mapView.mapboxMap.makeRecorder() else { return } + recorder.start() + + // Build a new set of CameraOptions for the map to fly to + let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 45.4588, longitude: -73.581), zoom: 11, pitch: 35) + + self.mapView.camera.fly(to: cameraOptions, duration: 10, completion: { _ in + // When the camera animation is complete, stop the map recording + // Replay the camera animation twice at double speed by passing the recorded sequence returned from the stop method + let mapRecordingSequence = recorder.stop() + recorder.replay(recordedSequence: mapRecordingSequence, options: MapPlayerOptions(playbackCount: 2, playbackSpeedMultiplier: 2.0, avoidPlaybackPauses: false)) { + print(recorder.playbackState()) + } + }) + }.store(in: &cancelables) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} diff --git a/Sources/Examples/All Examples/Lab/ResizableImageExample.swift b/Sources/Examples/All Examples/Lab/ResizableImageExample.swift new file mode 100644 index 000000000000..39cf007a185a --- /dev/null +++ b/Sources/Examples/All Examples/Lab/ResizableImageExample.swift @@ -0,0 +1,109 @@ +import Foundation +import UIKit +import MapboxMaps + +/// An example showcasing of adding a resizable image to the style +/// and demonstrating how the image is stretched +final class ResizableImageExample: UIViewController, ExampleProtocol { + private static let center = CLLocationCoordinate2DMake(55.70651, 12.554729) + private static let layerId = "layer_id" + private static let textBase = "Hi! " + + private lazy var mapView: MapView = { + let mapInitOptions = MapInitOptions(cameraOptions: CameraOptions(center: Self.center, zoom: 9)) + let mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + return mapView + }() + private var cancelables = Set() + + private var appendTextCounter = 1 + private weak var timer: Timer? { + didSet { oldValue?.invalidate() } + } + + // MARK: - Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(mapView) + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.setupExample() + self?.startUpdatingIconText() + }.store(in: &cancelables) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } + + override func didMove(toParent parent: UIViewController?) { + super.didMove(toParent: parent) + + if parent == nil { + timer = nil + } + } + + // MARK: - Private + + private func setupExample() { + let geoJSONSourceId = "source_id" + + // create an image of a circle and specify the corners that should remain unchanged + let image = UIImage(named: "circle")! + .resizableImage(withCapInsets: UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)) + try? mapView.mapboxMap.addImage(image, id: "circle") + + // add a GeoJSON source with a single point to the style + var source = GeoJSONSource(id: geoJSONSourceId) + source.data = .feature(Feature(geometry: Point(Self.center))) + + try? mapView.mapboxMap.addSource(source) + + // add a symbol layer with the resizable icon image + var symbolLayer = SymbolLayer(id: Self.layerId, source: geoJSONSourceId) + symbolLayer.iconImage = .constant(.name("circle")) + // make sure the icon image is stretched both vertically and horizontally + symbolLayer.iconTextFit = .constant(.both) + symbolLayer.iconTextFitPadding = .constant([10, 10, 10, 10]) + symbolLayer.textField = .constant(Self.textBase) + + try? mapView.mapboxMap.addLayer(symbolLayer, layerPosition: .default) + } + + private func startUpdatingIconText() { + timer = Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { [weak self] _ in + self?.updateIconText() + } + } + + // Append some text to the layer's textField, stretching the icon image in both X and Y axes + private func updateIconText() { + guard mapView.mapboxMap.isStyleLoaded else { + return + } + + let layer = try? mapView.mapboxMap.layer(withId: Self.layerId, type: SymbolLayer.self) + + guard case .expression(let expression) = layer?.textField else { + return + } + + appendTextCounter += 1 + + guard let textLabel = expression.arguments.first?.description + .appending(Self.textBase) + .appending(appendTextCounter % 3 == 0 ? "\n" : "") else { + return + } + + try? mapView.mapboxMap.updateLayer(withId: Self.layerId, type: SymbolLayer.self) { layer in + layer.textField = .constant(textLabel) + } + } +} diff --git a/Sources/Examples/All Examples/Lab/ResizeMapViewExample.swift b/Sources/Examples/All Examples/Lab/ResizeMapViewExample.swift new file mode 100644 index 000000000000..f3cb55850bef --- /dev/null +++ b/Sources/Examples/All Examples/Lab/ResizeMapViewExample.swift @@ -0,0 +1,210 @@ +import Foundation +import UIKit +import MapboxMaps +import MapKit +import os + +final class ResizeMapViewExample: UIViewController, ExampleProtocol { + private let mapsStackView = UIStackView(frame: .zero) + private let mapsContentView: UIView = UIView(frame: .zero) + private var mapboxMapView: MapView! + + var cancellables: Set = [] + + var fullSizeConstraints: [NSLayoutConstraint] = [] + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + + mapboxMapView = MapView(frame: view.bounds) + + mapsStackView.backgroundColor = .red + mapsContentView.addSubview(mapsStackView) + view.addSubview(mapsContentView) + + navigationController?.setToolbarHidden(false, animated: false) + + mapsStackView.addArrangedSubview(mapboxMapView) + + mapsStackView.distribution = .fillEqually + + mapboxMapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.mapStyleDidLoad() + }.store(in: &cancellables) + + setupDefaultCamera() + applyAutolayout() + + let animationBehaviourButton = UIBarButtonItem( + title: nil, + style: .done, + target: self, + action: #selector(toggleAnimationBehaviour(_:)) + ) + navigationItem.rightBarButtonItem = animationBehaviourButton + syncAnimationBehaviourButton(animationBehaviourButton) + } + + func mapStyleDidLoad() { + os_log(.info, "MapView did load") + + updateToolbarItems() + + // The below line is used for internal testing purposes only. + finish() + } + + func updateToolbarItems(animated: Bool = true) { + setToolbarItems([ + UIBarButtonItem(title: "Start", style: .done, target: self, action: #selector(startAnimation)), + UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil), + UIBarButtonItem(title: "Delay start", style: .plain, target: self, action: #selector(startFrameAnimationsNow_PropertyAnimator)), + UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(startBackFrameAnimationsNow_PropertyAnimator)), + UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil), + UIBarButtonItem(title: "Manual", style: .plain, target: self, action: #selector(resizeWithoutAnimation)), + ], animated: animated) + } + + enum AnimationDirection { + case expanding + case shrinking + + mutating func toggle() { + switch self { + case .expanding: + self = .shrinking + case .shrinking: + self = .expanding + } + } + } + + func animateResizing(direction: AnimationDirection) { + switch direction { + case .expanding: + NSLayoutConstraint.activate(fullSizeConstraints) + case .shrinking: + NSLayoutConstraint.deactivate(fullSizeConstraints) + } + view.layoutIfNeeded() + } + + // MARK: - UIView.animate + @objc func startAnimation() { + os_log(.default, "Shrinking animation with UIView.animate") + UIView.animate(withDuration: 2, delay: 0) { + self.animateResizing(direction: .shrinking) + } completion: { _ in + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { + os_log(.default, "Expanding animation with UIView.animate") + UIView.animate(withDuration: 2, delay: 0) { + self.animateResizing(direction: .expanding) + } + } + } + } + + // MARK: UIViewPropertyAnimator + @objc + func startFrameAnimationsNow_PropertyAnimator() { + os_log(.info, "Shrinking animation with UIViewPropertyAnimator") + UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 1) { + self.animateResizing(direction: .shrinking) + } + } + + @objc + func startBackFrameAnimationsNow_PropertyAnimator() { + os_log(.info, "Expanding animation with UIViewPropertyAnimator") + UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 2, delay: 1) { + self.animateResizing(direction: .expanding) + } + } + + // MARK: Without animation + var lastManualResizingDirection: AnimationDirection = .expanding { + didSet { + animateResizing(direction: lastManualResizingDirection) + } + } + + @objc + func resizeWithoutAnimation() { + os_log(.default, "%@ without animation", lastManualResizingDirection == .shrinking ? "Shrinking" : "Expanding") + + lastManualResizingDirection.toggle() + } + + var animationBehaviour: MapView.ResizingAnimation = .automatic { + didSet { + mapboxMapView.resizingAnimation = animationBehaviour + } + } + + @objc + private func toggleAnimationBehaviour(_ sender: UIBarButtonItem) { + switch animationBehaviour { + case .automatic: + animationBehaviour = .none + case .none: + animationBehaviour = .automatic + } + + syncAnimationBehaviourButton(sender) + } + + private func syncAnimationBehaviourButton(_ button: UIBarButtonItem) { + switch animationBehaviour { + case .automatic: + button.image = UIImage(systemName: "arrow.up.arrow.down.circle.fill") + button.title = "Automatic" + case .none: + button.image = UIImage(systemName: "arrow.up.arrow.down.circle") + button.title = "None" + } + } + + // MARK: - + + func setupDefaultCamera() { + let london = (coordinates: CLLocationCoordinate2D(latitude: 51.4724912, longitude: -0.0334334), zoom: 12.0, span: 4800.0) + + mapboxMapView.mapboxMap.setCamera(to: CameraOptions(center: london.coordinates, zoom: london.zoom)) + } + + func applyAutolayout() { + for view in [mapsStackView, mapboxMapView, mapsContentView] { + view?.translatesAutoresizingMaskIntoConstraints = false + } + + let fullHeightConstraint = mapsStackView.heightAnchor.constraint(equalTo: mapsContentView.heightAnchor, multiplier: 1) + fullHeightConstraint.identifier = "Full height container" + let partialHeightConstraint = mapsStackView.heightAnchor.constraint(equalTo: mapsContentView.heightAnchor, multiplier: 0.5) + partialHeightConstraint.identifier = "Partial height container" + partialHeightConstraint.priority = .defaultLow + + let fullWidthConstraint = mapsStackView.widthAnchor.constraint(equalTo: mapsContentView.widthAnchor, multiplier: 1) + let partialWidthConstraint = mapsStackView.widthAnchor.constraint(equalTo: mapsContentView.widthAnchor, multiplier: 0.5) + partialWidthConstraint.priority = .defaultLow + + NSLayoutConstraint.activate([ + fullWidthConstraint, + partialWidthConstraint, + mapsStackView.centerXAnchor.constraint(equalTo: mapsContentView.centerXAnchor), + + fullHeightConstraint, + partialHeightConstraint, + mapsStackView.bottomAnchor.constraint(equalTo: mapsContentView.bottomAnchor), + + mapsContentView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + mapsContentView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + mapsContentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + mapsContentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + ]) + + fullSizeConstraints.append(fullWidthConstraint) + fullSizeConstraints.append(fullHeightConstraint) + } +} diff --git a/Apps/Examples/Examples/All Examples/LayerPositionExample.swift b/Sources/Examples/All Examples/LayerPositionExample.swift similarity index 77% rename from Apps/Examples/Examples/All Examples/LayerPositionExample.swift rename to Sources/Examples/All Examples/LayerPositionExample.swift index 1099e590fb96..3be3de293387 100644 --- a/Apps/Examples/Examples/All Examples/LayerPositionExample.swift +++ b/Sources/Examples/All Examples/LayerPositionExample.swift @@ -1,31 +1,31 @@ import UIKit import MapboxMaps -@objc(LayerPositionExample) - -public class LayerPositionExample: UIViewController, ExampleProtocol { - - internal var mapView: MapView! +final class LayerPositionExample: UIViewController, ExampleProtocol { + private var mapView: MapView! private var sourceIdentifier = "ploygon-geojson-source" private var source: GeoJSONSource! private var layer: FillLayer! + private var cancelables = Set() - override public func viewDidLoad() { + override func viewDidLoad() { super.viewDidLoad() // Center the map over the United States. let centerCoordinate = CLLocationCoordinate2D(latitude: 40.58058466412761, longitude: -97.734375) - let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 3)) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 3), styleURI: .streets) mapView = MapView(frame: view.bounds, mapInitOptions: options) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(mapView) + try! mapView.mapboxMap.setProjection(StyleProjection(name: .mercator)) + // Allows the view controller to receive information about map events. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in - self.setupExample() - } + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.setupExample() + }.store(in: &cancelables) // Add a button to change the position of layer addLayerPostionChangeButton() @@ -55,32 +55,33 @@ public class LayerPositionExample: UIViewController, ExampleProtocol { let alert = UIAlertController(title: "Polygon Layer", message: "Please select the position of polygon layer.", preferredStyle: .actionSheet) + alert.popoverPresentationController?.sourceView = sender alert.addAction(UIAlertAction(title: "Above state label", style: .default, handler: { [weak self] _ in guard let self = self else { return } - try? self.mapView.mapboxMap.style.removeLayer(withId: self.layer.id) - try? self.mapView.mapboxMap.style.addLayer(self.layer, + try? self.mapView.mapboxMap.removeLayer(withId: self.layer.id) + try? self.mapView.mapboxMap.addLayer(self.layer, layerPosition: .above("state-label")) })) alert.addAction(UIAlertAction(title: "Below state label", style: .default, handler: { [weak self] _ in guard let self = self else { return } - try? self.mapView.mapboxMap.style.removeLayer(withId: self.layer.id) - try? self.mapView.mapboxMap.style.addLayer(self.layer, + try? self.mapView.mapboxMap.removeLayer(withId: self.layer.id) + try? self.mapView.mapboxMap.addLayer(self.layer, layerPosition: .below("state-label")) })) alert.addAction(UIAlertAction(title: "Above all", style: .default, handler: { [weak self] _ in guard let self = self else { return } - try? self.mapView.mapboxMap.style.removeLayer(withId: self.layer.id) - try? self.mapView.mapboxMap.style.addLayer(self.layer, + try? self.mapView.mapboxMap.removeLayer(withId: self.layer.id) + try? self.mapView.mapboxMap.addLayer(self.layer, layerPosition: nil) })) alert.addAction(UIAlertAction(title: "Below all", style: .default, handler: { [weak self] _ in guard let self = self else { return } - try? self.mapView.mapboxMap.style.removeLayer(withId: self.layer.id) - try? self.mapView.mapboxMap.style.addLayer(self.layer, + try? self.mapView.mapboxMap.removeLayer(withId: self.layer.id) + try? self.mapView.mapboxMap.addLayer(self.layer, layerPosition: .at(0)) })) @@ -90,15 +91,14 @@ public class LayerPositionExample: UIViewController, ExampleProtocol { } // Wait for the style to load before adding data to it. - public func setupExample() { - layer = FillLayer(id: "polygon-layer") - layer.source = sourceIdentifier + func setupExample() { + layer = FillLayer(id: "polygon-layer", source: sourceIdentifier) // Apply basic styling to the fill layer. layer.fillColor = .constant(StyleColor(#colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1))) layer.fillOutlineColor = .constant(StyleColor(#colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1))) // Create a new GeoJSON data source which gets its data from a polygon. - source = GeoJSONSource() + source = GeoJSONSource(id: sourceIdentifier) source.data = .geometry(.polygon(.init([[ CLLocationCoordinate2DMake(32.91648534731439, -114.43359375), CLLocationCoordinate2DMake(32.91648534731439, -81.298828125), @@ -107,10 +107,10 @@ public class LayerPositionExample: UIViewController, ExampleProtocol { CLLocationCoordinate2DMake(32.91648534731439, -114.43359375) ]]))) - try! mapView.mapboxMap.style.addSource(source, id: sourceIdentifier) + try! mapView.mapboxMap.addSource(source) // If a layer position is not supplied, the layer is added above all other layers by default. - try! mapView.mapboxMap.style.addLayer(layer, layerPosition: nil) + try! mapView.mapboxMap.addLayer(layer, layerPosition: nil) // The below line is used for internal testing purposes only. finish() diff --git a/Sources/Examples/All Examples/LayerSlotExample.swift b/Sources/Examples/All Examples/LayerSlotExample.swift new file mode 100644 index 000000000000..ec26708c5d76 --- /dev/null +++ b/Sources/Examples/All Examples/LayerSlotExample.swift @@ -0,0 +1,83 @@ +import UIKit +import MapboxMaps + +final class LayerSlotExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private lazy var source = GeoJSONSource(id: "ploygon-geojson-source") + private lazy var layer = FillLayer(id: "polygon-layer", source: source.id) + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // Center the map over the United States. + let centerCoordinate = CLLocationCoordinate2D(latitude: 40.58058466412761, + longitude: -97.734375) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 3)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + // Allows the view controller to receive information about map events. + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + self?.setupExample() + }.store(in: &cancelables) + + // Add a control to change the slot of layer + addLayerSlotChangeControl() + } + + private func addLayerSlotChangeControl() { + // Set up a segemented control changing slots + let control = UISegmentedControl(items: ["Bottom", "Middle", "Top"]) + control.translatesAutoresizingMaskIntoConstraints = false + control.backgroundColor = .white + control.addTarget(self, action: #selector(segmentedControlValueChanged(_:)), for: .valueChanged) + control.selectedSegmentIndex = 0 + view.addSubview(control) + + // Set segmented control location + let horizontalConstraint = view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: control.bottomAnchor, + constant: 40) + let verticalConstraint = control.centerXAnchor.constraint(equalTo: view.centerXAnchor) + let minWidthConstraint = control.widthAnchor.constraint(greaterThanOrEqualToConstant: 300) + let leadingConstraint = control.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: view.leadingAnchor, multiplier: 1) + let trailingConstraint = control.trailingAnchor.constraint(lessThanOrEqualToSystemSpacingAfter: view.trailingAnchor, multiplier: 1) + NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, leadingConstraint, trailingConstraint, minWidthConstraint]) + } + + @objc private func segmentedControlValueChanged(_ sender: UISegmentedControl) { + guard let slotName = sender.titleForSegment(at: sender.selectedSegmentIndex)?.lowercased() else { return } + + try! mapView.mapboxMap.updateLayer(withId: self.layer.id, type: FillLayer.self, update: { layer in + layer.slot = Slot(rawValue: slotName) + }) + } + + // Wait for the style to load before adding data to it. + func setupExample() { + // Apply basic styling to the fill layer. + layer.fillColor = .constant(StyleColor(#colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1))) + layer.fillOutlineColor = .constant(StyleColor(#colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1))) + + // If a slot is not supplied, the layer is added above all other layers by default. + layer.slot = .bottom + + // Create a new GeoJSON data source which gets its data from a polygon. + source.data = .geometry(.polygon(.init([[ + CLLocationCoordinate2DMake(32.91648534731439, -114.43359375), + CLLocationCoordinate2DMake(32.91648534731439, -81.298828125), + CLLocationCoordinate2DMake(48.16608541901253, -81.298828125), + CLLocationCoordinate2DMake(48.16608541901253, -114.43359375), + CLLocationCoordinate2DMake(32.91648534731439, -114.43359375) + ]]))) + + try! mapView.mapboxMap.addSource(source) + + try! mapView.mapboxMap.addLayer(layer) + + // The below line is used for internal testing purposes only. + finish() + } +} diff --git a/Apps/Examples/Examples/All Examples/LiveDataExample.swift b/Sources/Examples/All Examples/LiveDataExample.swift similarity index 76% rename from Apps/Examples/Examples/All Examples/LiveDataExample.swift rename to Sources/Examples/All Examples/LiveDataExample.swift index 6c2335626e20..259bed23b379 100644 --- a/Apps/Examples/Examples/All Examples/LiveDataExample.swift +++ b/Sources/Examples/All Examples/LiveDataExample.swift @@ -1,12 +1,12 @@ import MapboxMaps +import UIKit -@objc(LiveDataExample) final class LiveDataExample: UIViewController, ExampleProtocol { // Display the current location of the International Space Station (ISS) - let url = URL(string: "https://api.wheretheiss.at/v1/satellites/25544")! - let sourceId = "ISS-source" - var mapView: MapView! - var issTimer: Timer? + private let url = URL(string: "https://api.wheretheiss.at/v1/satellites/25544")! + private var mapView: MapView! + private var issTimer: Timer? + private var cancelables = Set() struct Coordinates: Codable { let longitude: Double @@ -27,34 +27,33 @@ final class LiveDataExample: UIViewController, ExampleProtocol { view.addSubview(mapView) // Add the live data layer once the map has finished loading. - mapView.mapboxMap.onNext(event: .mapLoaded) { _ in + mapView.mapboxMap.onMapLoaded.observeNext { _ in self.addStyleLayer() // The following line is just for testing purposes. self.finish() - } + }.store(in: &cancelables) } func addStyleLayer() { // Create an empty geoJSON source to hold location data once // this information is received from the URL - var source = GeoJSONSource() - source.data = .empty + let source = GeoJSONSource(id: "ISS-source") - var issLayer = SymbolLayer(id: "iss-layer") - issLayer.source = sourceId + var issLayer = SymbolLayer(id: "iss-layer", source: source.id) - // Mapbox Streets contains an image named `rocket-15`. Use that image + // Mapbox Streets contains an image named `rocket`. Use that image // to represent the location of the ISS. - issLayer.iconImage = .constant(.name("rocket-15")) + issLayer.iconImage = .constant(.name("rocket")) do { - try mapView.mapboxMap.style.addSource(source, id: sourceId) - try mapView.mapboxMap.style.addLayer(issLayer) + try mapView.mapboxMap.addSource(source) + try mapView.mapboxMap.addLayer(issLayer) // Create a `Timer` that updates the `GeoJSONSource`. - issTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in - self.parseJSON { result in + issTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in + self?.parseJSON { [weak self] result in + guard let self = self else { return } switch result { case .success(let coordinates): @@ -63,7 +62,7 @@ final class LiveDataExample: UIViewController, ExampleProtocol { // Update geoJSON source to display new location of ISS let point = Point(locationCoordinates) let pointFeature = Feature(geometry: point) - try! self.mapView.mapboxMap.style.updateGeoJSONSource(withId: self.sourceId, geoJSON: .feature(pointFeature)) + self.mapView.mapboxMap.updateGeoJSONSource(withId: source.id, geoJSON: .feature(pointFeature)) // Update camera to follow ISS let issCamera = CameraOptions(center: locationCoordinates, zoom: 3) diff --git a/Sources/Examples/All Examples/LongTapAnimationExample.swift b/Sources/Examples/All Examples/LongTapAnimationExample.swift new file mode 100644 index 000000000000..1881b0a46281 --- /dev/null +++ b/Sources/Examples/All Examples/LongTapAnimationExample.swift @@ -0,0 +1,57 @@ +import MapboxMaps +import UIKit + +// This examples shows how to animate camera to a long-tapped coordinate. +final class LongTapAnimationExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + private var pointAnnotationManager: PointAnnotationManager! + private var cancelables = Set() + + override func viewDidLoad() { + super.viewDidLoad() + + // Center the map over the United States. + let centerCoordinate = CLLocationCoordinate2D(latitude: 39.368279, + longitude: -97.646484) + let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 2.4)) + + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(mapView) + + mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in + try? self?.mapView.mapboxMap.addImage(UIImage(named: "intermediate-pin")!, id: .blueMarker) + + // The following line is just for testing purposes. + self?.finish() + }.store(in: &cancelables) + + mapView.gestures.onMapLongPress.observe { [weak self] context in + self?.handleLongPress(at: context.point) + }.store(in: &cancelables) + + pointAnnotationManager = mapView.annotations.makePointAnnotationManager() + } + + private func handleLongPress(at location: CGPoint) { + haptic() + let coordinate = mapView.mapboxMap.coordinate(for: location) + + var annotation = PointAnnotation(point: Point(coordinate)) + annotation.iconImage = .blueMarker + annotation.iconOffset = [0, 12] + pointAnnotationManager.annotations.append(annotation) + + let camera = CameraOptions(center: coordinate) + mapView.camera.ease(to: camera, duration: 0.5) + } + + private func haptic() { + let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .heavy) + impactFeedbackGenerator.impactOccurred() + } +} + +private extension String { + static let blueMarker = "blue-marker" +} diff --git a/Sources/Examples/All Examples/ModelLayerExample.swift b/Sources/Examples/All Examples/ModelLayerExample.swift new file mode 100644 index 000000000000..ae26a2c5fe28 --- /dev/null +++ b/Sources/Examples/All Examples/ModelLayerExample.swift @@ -0,0 +1,68 @@ +import UIKit +@_spi(Experimental) import MapboxMaps + +@objc(ModelLayerExample) +final class ModelLayerExample: UIViewController, ExampleProtocol { + private var mapView: MapView! + + override func viewDidLoad() { + super.viewDidLoad() + + let cameraOptions = CameraOptions( + center: mid(Constants.duckCoordinates.coordinates, Constants.mapboxHelsinki.coordinates), + zoom: 16, + pitch: 45 + ) + let options = MapInitOptions(cameraOptions: cameraOptions) + mapView = MapView(frame: view.bounds, mapInitOptions: options) + mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + mapView.ornaments.options.scaleBar.visibility = .visible + + /// Create a Feature to hold the coordinates of the duck and car. + /// Both Features will be added to a GeoJSONSource below as a feature collection + var duckFeature = Feature(geometry: Constants.duckCoordinates) + duckFeature.properties = [Constants.modelIdKey: .string(Constants.duckModelId)] + var carFeature = Feature(geometry: Constants.mapboxHelsinki) + carFeature.properties = [Constants.modelIdKey: .string(Constants.carModelId)] + + mapView.mapboxMap.setMapStyleContent { + /// Add Models for both the duck and car using an id and a URL to the resource + Model(id: Constants.duckModelId, uri: Constants.duck) + Model(id: Constants.carModelId, uri: Constants.car) + + /// Add a GeoJSONSource to the map and add the two features with geometry information + GeoJSONSource(id: Constants.sourceId) + .data(.featureCollection(FeatureCollection(features: [duckFeature, carFeature]))) + + /// Add a Model visualization layer which displays the two models stored in the GeoJSONSource according to the set properties + ModelLayer(id: "model-layer-id", source: Constants.sourceId) + .modelId(Exp(.get) { Constants.modelIdKey }) + .modelType(.common3d) + .modelScale(x: 40, y: 40, z: 40) + .modelTranslation(x: 0, y: 0, z: 0) + .modelRotation(x: 0, y: 0, z: 90) + .modelOpacity(0.7) + } + + view.addSubview(mapView) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + // The below line is used for internal testing purposes only. + finish() + } +} + +extension ModelLayerExample { + private enum Constants { + static let mapboxHelsinki = Point(CLLocationCoordinate2D(latitude: 60.17195694011002, longitude: 24.945389069265598)) + static let duckCoordinates = Point(CLLocationCoordinate2D(latitude: mapboxHelsinki.coordinates.latitude + 0.002, longitude: mapboxHelsinki.coordinates.longitude - 0.002)) + static let modelIdKey = "model-id-key" + static let sourceId = "source-id" + static let duckModelId = "model-id-duck" + static let carModelId = "model-id-car" + static let duck = URL.init(string: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf") + static let car = Bundle.main.url(forResource: "sportcar", withExtension: "glb")! + } +} diff --git a/Sources/Examples/All Examples/NavigationSimulator.swift b/Sources/Examples/All Examples/NavigationSimulator.swift new file mode 100644 index 000000000000..836ade492bca --- /dev/null +++ b/Sources/Examples/All Examples/NavigationSimulator.swift @@ -0,0 +1,95 @@ +import Foundation +import CoreLocation +import MapboxMaps + +final class NavigationSimulator { + private let viewport: ViewportManager + private let route: LineString + + private lazy var followPuckViewPortState = viewport.makeFollowPuckViewportState( + options: FollowPuckViewportStateOptions(bearing: .course) + ) + + private let locationObservers = NSHashTable.weakObjects() + private var isStarted = false + private let routeLength: LocationDistance + private var routePointsToTravel: [LocationCoordinate2D] + + private var direction: LocationDirection + private var currentLocation: LocationCoordinate2D { + didSet { + direction = oldValue.direction(to: currentLocation) + startUpdatingLocation() + } + } + + init(viewport: ViewportManager, route: LineString) { + self.viewport = viewport + self.route = route + routeLength = route.distance()! + routePointsToTravel = route.coordinates + + currentLocation = routePointsToTravel.removeFirst() + direction = currentLocation.direction(to: routePointsToTravel[0]) + } + + func start() { + guard !isStarted else { return } + isStarted = true + + viewport.transition(to: followPuckViewPortState) { _ in + Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in + guard let self else { + return timer.invalidate() + } + + if !routePointsToTravel.isEmpty { + let nextPoint = self.routePointsToTravel.removeFirst() + currentLocation = nextPoint + } else { + // Journey completed. + timer.invalidate() + } + } + } + + startUpdatingLocation() + } + + func progressFromStart(to location: Location) -> Double { + route.distance(to: location.coordinate)! / routeLength + } + + private func startUpdatingLocation() { + let location = Location( + coordinate: currentLocation, + timestamp: Date(), + altitude: 0, + horizontalAccuracy: kCLLocationAccuracyBestForNavigation, + verticalAccuracy: kCLLocationAccuracyBestForNavigation, + speed: 0, + // Turf calculates bearing in decimal degrees within -180 to 180, + // while Apple's course requires value in decimal degrees from 0 - 359.9 + bearing: direction < 0 ? 360 + direction : direction, + floor: nil, + extra: nil) + + for consumer in locationObservers.allObjects { + (consumer as? LocationObserver)?.onLocationUpdateReceived(for: [location]) + } + } +} + +extension NavigationSimulator: LocationProvider { + func addLocationObserver(for observer: LocationObserver) { + locationObservers.add(observer) + } + + func removeLocationObserver(for observer: LocationObserver) { + locationObservers.remove(observer) + } + + func getLastObservedLocation() -> Location? { + Location(coordinate: currentLocation) + } +} diff --git a/Apps/Examples/Examples/All Examples/NavigationSimulatorExample.swift b/Sources/Examples/All Examples/NavigationSimulatorExample.swift similarity index 79% rename from Apps/Examples/Examples/All Examples/NavigationSimulatorExample.swift rename to Sources/Examples/All Examples/NavigationSimulatorExample.swift index d37eede71033..462592f30994 100644 --- a/Apps/Examples/Examples/All Examples/NavigationSimulatorExample.swift +++ b/Sources/Examples/All Examples/NavigationSimulatorExample.swift @@ -2,17 +2,12 @@ import UIKit @_spi(Experimental) import MapboxMaps final class NavigationSimulatorExample: UIViewController, ExampleProtocol { - private enum ID { - static let routeSource = "route-line-source-id" - static let routeLineLayer = "route-line-layer-id" - static let casingLineLayer = "route-casing-layer-id" - } - private var mapView: MapView! private var navigationSimulator: NavigationSimulator! + private var cancelables = Set() private lazy var routeSource: Source = { - var source = GeoJSONSource() + var source = GeoJSONSource(id: ID.routeSource) source.data = .geometry(Geometry(sampleRouteLine)) source.lineMetrics = true @@ -41,15 +36,19 @@ final class NavigationSimulatorExample: UIViewController, ExampleProtocol { private func configureMap() { navigationSimulator = NavigationSimulator(viewport: mapView.viewport, route: sampleRouteLine) - mapView.location.options.puckType = .puck2D(.makeDefault(showBearing: true)) - mapView.location.options.puckBearingSource = .course - mapView.location.overrideLocationProvider(with: navigationSimulator) - mapView.location.addPuckLocationConsumer(self) + let configuration = Puck2DConfiguration(topImage: UIImage(named: "dash-puck")!) + mapView.location.options.puckType = .puck2D(configuration) + mapView.location.options.puckBearing = .course + mapView.location.options.puckBearingEnabled = true + mapView.location.override(locationProvider: navigationSimulator) + mapView.location.onPuckRender.observe { [weak self] in + self?.onPuckRender(data: $0) + }.store(in: &cancelables) do { - try mapView.mapboxMap.style.addSource(routeSource, id: ID.routeSource) - try mapView.mapboxMap.style.addPersistentLayer(makeCasingLayer()) - try mapView.mapboxMap.style.addPersistentLayer(makeRouteLineLayer()) + try mapView.mapboxMap.addSource(routeSource) + try mapView.mapboxMap.addPersistentLayer(makeCasingLayer()) + try mapView.mapboxMap.addPersistentLayer(makeRouteLineLayer()) navigationSimulator.start() } catch { @@ -60,10 +59,11 @@ final class NavigationSimulatorExample: UIViewController, ExampleProtocol { // MARK: - Util private func makeRouteLineLayer() -> LineLayer { - var routeLayer = LineLayer(id: ID.routeLineLayer) - routeLayer.source = ID.routeSource + var routeLayer = LineLayer(id: ID.routeLineLayer, source: ID.routeSource) routeLayer.lineCap = .constant(.round) routeLayer.lineJoin = .constant(.round) + routeLayer.lineTrimColor = .constant(StyleColor(.lightGray)) + routeLayer.lineTrimFadeRange = .constant([0.0, 0.0001]) routeLayer.lineWidth = .expression( Exp(.interpolate) { Exp(.exponential) { @@ -125,8 +125,7 @@ final class NavigationSimulatorExample: UIViewController, ExampleProtocol { } private func makeCasingLayer() -> LineLayer { - var casingLayer = LineLayer(id: ID.casingLineLayer) - casingLayer.source = ID.routeSource + var casingLayer = LineLayer(id: ID.casingLineLayer, source: ID.routeSource) casingLayer.lineCap = .constant(.round) casingLayer.lineJoin = .constant(.round) @@ -197,15 +196,19 @@ final class NavigationSimulatorExample: UIViewController, ExampleProtocol { fatalError("Unable to decode Route GeoJSON source") } }() -} -extension NavigationSimulatorExample: PuckLocationConsumer { + private func onPuckRender(data: PuckRenderingData) { + let progress = navigationSimulator.progressFromStart(to: data.location) - func puckLocationUpdate(newLocation: Location) { - let style = mapView.mapboxMap.style - let progress = navigationSimulator.progressFromStart(to: newLocation) + try? mapView.mapboxMap.setLayerProperty(for: ID.routeLineLayer, property: "line-trim-offset", value: [0, progress]) + try? mapView.mapboxMap.setLayerProperty(for: ID.casingLineLayer, property: "line-trim-offset", value: [0, progress]) + } +} - try? style.setLayerProperty(for: ID.routeLineLayer, property: "line-trim-offset", value: [0, progress]) - try? style.setLayerProperty(for: ID.casingLineLayer, property: "line-trim-offset", value: [0, progress]) +extension NavigationSimulatorExample { + private enum ID { + static let routeSource = "route-line-source-id" + static let routeLineLayer = "route-line-layer-id" + static let casingLineLayer = "route-casing-layer-id" } } diff --git a/Apps/Examples/Examples/All Examples/OfflineManagerExample.storyboard b/Sources/Examples/All Examples/OfflineManagerExample.storyboard similarity index 90% rename from Apps/Examples/Examples/All Examples/OfflineManagerExample.storyboard rename to Sources/Examples/All Examples/OfflineManagerExample.storyboard index 9faaba826789..99effbfbbaf3 100644 --- a/Apps/Examples/Examples/All Examples/OfflineManagerExample.storyboard +++ b/Sources/Examples/All Examples/OfflineManagerExample.storyboard @@ -1,9 +1,9 @@ - + - + @@ -12,13 +12,23 @@ - + + + + + + + + + + + @@ -59,7 +69,7 @@