From a7256bb486548d0b3719ee4bb3d9cc09c2ca696f Mon Sep 17 00:00:00 2001 From: Alfonso Grillo Date: Fri, 14 Jul 2023 10:19:38 +0200 Subject: [PATCH] Move Map Libre API key to Xcode Cloud (#1313) * Add config_production lane * Add maplibre to .gitignore * Add setupMapLibreKey * Add documentation * Fix failing UTs * Fix more UTs * Cleanup * Add secrets.xcconfig * Cleanup gitignore file * Update post-checkout hook * Cleanup SetupProject * Update project * Remove leftover in SetupProject * Cleanup project.yml * Add fastlane-plugin-xcconfig * Improve test * Update docs --- .githooks/post-checkout | 3 ++ ElementX.xcodeproj/project.pbxproj | 12 +++++ .../Sources/Application/AppSettings.swift | 2 +- ElementX/Sources/Other/InfoPlistReader.swift | 6 +++ ElementX/SupportingFiles/Info.plist | 2 + ElementX/SupportingFiles/target.yml | 5 +++ Gemfile.lock | 4 +- README.md | 4 ++ .../Sources/HomeScreenViewModelTests.swift | 5 +-- .../NotificationManagerTests.swift | 44 ++++++++++++++++--- ci_scripts/ci_post_clone.sh | 4 +- docs/FORKING.md | 32 ++++++++++++++ fastlane/Fastfile | 18 ++++++++ fastlane/Pluginfile | 1 + fastlane/README.md | 8 ++++ project.yml | 1 + secrets.xcconfig | 20 +++++++++ 17 files changed, 159 insertions(+), 12 deletions(-) create mode 100644 docs/FORKING.md create mode 100644 secrets.xcconfig diff --git a/.githooks/post-checkout b/.githooks/post-checkout index 93dc37d0db..5363630097 100755 --- a/.githooks/post-checkout +++ b/.githooks/post-checkout @@ -4,3 +4,6 @@ git lfs post-checkout "$@" #!/bin/bash export PATH="$PATH:/opt/homebrew/bin" + +# ignores updates of 'secrets.xcconfig' to avoid pushing sensitive data by mistake +git update-index --assume-unchanged secrets.xcconfig \ No newline at end of file diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index d58c67d936..9b81320791 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -967,6 +967,7 @@ 40B21E611DADDEF00307E7AC /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; 4132F882A984ED971338EE9D /* ReportContentScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenUITests.swift; sourceTree = ""; }; 4151163F666ED94FD959475A /* NotificationName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationName.swift; sourceTree = ""; }; + 41553551C55AD59885840F0E /* secrets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = secrets.xcconfig; sourceTree = ""; }; 4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreen.swift; sourceTree = ""; }; 421E716C521F96D24ECE69B3 /* NoticeRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItem.swift; sourceTree = ""; }; 421FA93BCC2840E66E4F306F /* NotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -2004,6 +2005,7 @@ isa = PBXGroup; children = ( 5D26A086A8278D39B5756D6F /* project.yml */, + 41553551C55AD59885840F0E /* secrets.xcconfig */, 99B9B46F2D621380428E68F7 /* ElementX */, A4852B57D55D71EEBFCD931D /* UnitTests */, C0FAC17D4DD7D3A502822550 /* UITests */, @@ -3590,6 +3592,14 @@ path = Timeline; sourceTree = ""; }; + "TEMP_FFE5FDBA-B4DD-4FF7-B172-18026F248E20" /* element-x-ios */ = { + isa = PBXGroup; + children = ( + 41553551C55AD59885840F0E /* secrets.xcconfig */, + ); + path = "element-x-ios"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -4822,6 +4832,7 @@ }; 62E1B7866DF0ED442C39A83B /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 41553551C55AD59885840F0E /* secrets.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = ElementX/SupportingFiles/ElementX.entitlements; @@ -4845,6 +4856,7 @@ }; 6897D5BC19A2EA6ABD57DE7E /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 41553551C55AD59885840F0E /* secrets.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = ElementX/SupportingFiles/ElementX.entitlements; diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index b4d1fa059b..d25eb8e605 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -205,7 +205,7 @@ final class AppSettings { let darkTileMapStyleURL: URL = "https://api.maptiler.com/maps/dea61faf-292b-4774-9660-58fcef89a7f3" // maptiler api key - let mapTilerApiKey = "fU3vlMsMn4Jb6dnEIFsx" + let mapTilerApiKey = InfoPlistReader.main.mapLibreAPIKey // maptiler geocoding url let geocodingURLFormatString = "https://api.maptiler.com/geocoding/%f,%f.json" diff --git a/ElementX/Sources/Other/InfoPlistReader.swift b/ElementX/Sources/Other/InfoPlistReader.swift index 4e2bcc9592..a0b43c51cf 100644 --- a/ElementX/Sources/Other/InfoPlistReader.swift +++ b/ElementX/Sources/Other/InfoPlistReader.swift @@ -23,6 +23,7 @@ struct InfoPlistReader { static let keychainAccessGroupIdentifier = "keychainAccessGroupIdentifier" static let bundleShortVersion = "CFBundleShortVersionString" static let bundleDisplayName = "CFBundleDisplayName" + static let mapLibreAPIKey = "mapLibreAPIKey" } /// Info.plist reader on the bundle object that contains the current executable. @@ -76,6 +77,11 @@ struct InfoPlistReader { infoPlistStringValue(forKey: Keys.bundleDisplayName) } + /// Map Libre API Key + var mapLibreAPIKey: String { + infoPlistStringValue(forKey: Keys.mapLibreAPIKey) + } + private func infoPlistStringValue(forKey key: String) -> String { guard let result = bundle.object(forInfoDictionaryKey: key) as? String else { fatalError("Add \(key) into your target's Info.plst") diff --git a/ElementX/SupportingFiles/Info.plist b/ElementX/SupportingFiles/Info.plist index 9c7933379a..702b33e18c 100644 --- a/ElementX/SupportingFiles/Info.plist +++ b/ElementX/SupportingFiles/Info.plist @@ -60,5 +60,7 @@ $(BASE_BUNDLE_IDENTIFIER) keychainAccessGroupIdentifier $(KEYCHAIN_ACCESS_GROUP_IDENTIFIER) + mapLibreAPIKey + $(MAPLIBRE_API_KEY) diff --git a/ElementX/SupportingFiles/target.yml b/ElementX/SupportingFiles/target.yml index 4dde9748c9..09059695e8 100644 --- a/ElementX/SupportingFiles/target.yml +++ b/ElementX/SupportingFiles/target.yml @@ -45,6 +45,10 @@ targets: type: application platform: iOS + configFiles: + Debug: ../../secrets.xcconfig + Release: ../../secrets.xcconfig + info: path: ../SupportingFiles/Info.plist properties: @@ -76,6 +80,7 @@ targets: BGTaskSchedulerPermittedIdentifiers: [ io.element.elementx.background.refresh ] + mapLibreAPIKey: $(MAPLIBRE_API_KEY) settings: diff --git a/Gemfile.lock b/Gemfile.lock index ba4ddfe0a5..42311168dd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -123,6 +123,7 @@ GEM rest-client (~> 2.0, >= 2.0.2) fastlane-plugin-sentry (1.15.0) os (~> 1.1, >= 1.1.4) + fastlane-plugin-xcconfig (2.0.0) fastlane-plugin-xcodegen (1.1.0) fastlane-plugin-brew (~> 0.1.1) gh_inspector (1.1.3) @@ -263,9 +264,10 @@ DEPENDENCIES fastlane-plugin-browserstack fastlane-plugin-diawi! fastlane-plugin-sentry + fastlane-plugin-xcconfig fastlane-plugin-xcodegen slather xcode-install BUNDLED WITH - 2.3.26 + 2.4.16 diff --git a/README.md b/README.md index 0af56be75c..64df142dd3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,10 @@ When you are experiencing an issue on ElementX iOS, please first search in [GitH and then in [#element-x-ios:matrix.org](https://matrix.to/#/#element-x-ios:matrix.org). If after your research you still have a question, ask at [#element-x-ios:matrix.org](https://matrix.to/#/#element-x-ios:matrix.org). Otherwise feel free to create a GitHub issue if you encounter a bug or a crash, by explaining clearly in detail what happened. You can also perform bug reporting (Rageshake) from the Element application by shaking your phone or going to the application settings. This is especially recommended when you encounter a crash. +## Forking + +Please read our [forking guide](docs/FORKING.md). + ## Copyright & License Copyright (c) 2022 New Vector Ltd diff --git a/UnitTests/Sources/HomeScreenViewModelTests.swift b/UnitTests/Sources/HomeScreenViewModelTests.swift index 77ef30f43c..f8950e4c5b 100644 --- a/UnitTests/Sources/HomeScreenViewModelTests.swift +++ b/UnitTests/Sources/HomeScreenViewModelTests.swift @@ -88,10 +88,9 @@ class HomeScreenViewModelTests: XCTestCase { let room: RoomProxyMock = .init(with: .init(id: mockRoomId, displayName: "Some room")) room.leaveRoomClosure = { .failure(.failedLeavingRoom) } clientProxy.roomForIdentifierMocks[mockRoomId] = room - let deferred = deferFulfillment(context.$viewState.first(), message: "viewState should be published.") context.send(viewAction: .confirmLeaveRoom(roomIdentifier: mockRoomId)) - try await deferred.fulfill() - XCTAssertNotNil(context.alertInfo) + let state = await context.nextViewState() + XCTAssertNotNil(state?.bindings.alertInfo) } func testLeaveRoomSuccess() async throws { diff --git a/UnitTests/Sources/NotificationManager/NotificationManagerTests.swift b/UnitTests/Sources/NotificationManager/NotificationManagerTests.swift index a9225071ac..2c0db99e8e 100644 --- a/UnitTests/Sources/NotificationManager/NotificationManagerTests.swift +++ b/UnitTests/Sources/NotificationManager/NotificationManagerTests.swift @@ -179,27 +179,59 @@ final class NotificationManagerTests: XCTestCase { } func test_MessageNotificationsRemoval() async throws { + let notificationPublisher = NotificationCenter.default.publisher(for: .roomMarkedAsRead).first() + var cancellables: Set = .init() + let expectation1 = expectation(description: #function) + notificationPublisher + .sink { _ in + expectation1.fulfill() + } + .store(in: &cancellables) + // No interaction if the object is nil or of the wrong type NotificationCenter.default.post(name: .roomMarkedAsRead, object: nil) - try await Task.sleep(for: .microseconds(200)) + await fulfillment(of: [expectation1]) XCTAssertEqual(notificationCenter.deliveredNotificationsCallsCount, 0) XCTAssertEqual(notificationCenter.removeDeliveredNotificationsCallsCount, 0) - + + let expectation2 = expectation(description: #function) + notificationPublisher + .sink { _ in + expectation2.fulfill() + } + .store(in: &cancellables) + NotificationCenter.default.post(name: .roomMarkedAsRead, object: 1) - try await Task.sleep(for: .microseconds(200)) + await fulfillment(of: [expectation2]) XCTAssertEqual(notificationCenter.deliveredNotificationsCallsCount, 0) XCTAssertEqual(notificationCenter.removeDeliveredNotificationsCallsCount, 0) - + + let expectation3 = expectation(description: #function) + notificationPublisher + .sink { _ in + expectation3.fulfill() + } + .store(in: &cancellables) + // The center calls the delivered and the removal functions when an id is passed NotificationCenter.default.post(name: .roomMarkedAsRead, object: "RoomID") - try await Task.sleep(for: .microseconds(200)) + await fulfillment(of: [expectation3]) XCTAssertEqual(notificationCenter.deliveredNotificationsCallsCount, 1) XCTAssertEqual(notificationCenter.removeDeliveredNotificationsCallsCount, 1) } func test_InvitesNotificationsRemoval() async throws { + let notificationPublisher = NotificationCenter.default.publisher(for: .invitesScreenAppeared).first() + let expectation = expectation(description: #function) + var cancellables: Set = .init() + notificationPublisher + .sink { _ in + expectation.fulfill() + } + .store(in: &cancellables) + NotificationCenter.default.post(name: .invitesScreenAppeared, object: nil) - try await Task.sleep(for: .microseconds(200)) + await fulfillment(of: [expectation]) XCTAssertEqual(notificationCenter.deliveredNotificationsCallsCount, 1) XCTAssertEqual(notificationCenter.removeDeliveredNotificationsCallsCount, 1) } diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh index 323593baa0..dc65096617 100755 --- a/ci_scripts/ci_post_clone.sh +++ b/ci_scripts/ci_post_clone.sh @@ -8,4 +8,6 @@ install_xcode_cloud_brew_dependencies if [ "$CI_WORKFLOW" = "Nightly" ]; then bundle exec fastlane config_nightly -fi \ No newline at end of file +else + bundle exec fastlane config_production +fi diff --git a/docs/FORKING.md b/docs/FORKING.md new file mode 100644 index 0000000000..e616f4dee2 --- /dev/null +++ b/docs/FORKING.md @@ -0,0 +1,32 @@ +# Forking + +### Update the bundle identifier / app display name + +To change the bundle identifier and the app display name for your app, open the `project.yml` file in the project root folder and change these settings: + +``` +BASE_BUNDLE_IDENTIFIER: io.element.elementx +APP_DISPLAY_NAME: Element X +``` + +After the changes run `xcodegen` to propagate them. + +### Setup the location sharing + +The location sharing feature on Element X is currently integrated with [MapLibre](https://maplibre.org). + +The MapLibre SDK requires an API key to work, so you need to get one for yourself. + +After you get an API key, you need to configure the project by adding it inside the file `secrets.xconfig` in the project root folder. After you are done, the file should contain a setting like this: + +``` +MAPLIBRE_API_KEY = your_map_libre_key +``` + +It’s not recommended to push your API key in your repository since other people may get it. + +One way to avoid pushing the API key by mistake is running on your machine the command: +``` +git update-index assume-unchanged secrets.xcconfig +``` +this will prevent pushing any update of the file`secrets.xcconfig`. \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 7b3ba3bb8b..cf952d607d 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -180,6 +180,9 @@ lane :config_nightly do data = YAML.load_file target_file_path data["settings"]["BASE_APP_GROUP_IDENTIFIER"] = "io.element.nightly" data["settings"]["BASE_BUNDLE_IDENTIFIER"] = "io.element.elementx.nightly" + + config_maplibre() + File.open(target_file_path, 'w') { |f| YAML.dump(data, f) } xcodegen(spec: "project.yml") @@ -193,6 +196,11 @@ lane :config_nightly do update_app_icon(caption_text: "Nightly #{release_version}", modulate: "100,20,100") end +lane :config_production do + config_maplibre() + xcodegen(spec: "project.yml") +end + lane :upload_dsyms_to_sentry do |options| auth_token = ENV["SENTRY_AUTH_TOKEN"] UI.user_error!("Invalid Sentry Auth token.") unless !auth_token.to_s.empty? @@ -409,5 +417,15 @@ private_lane :create_simulator_if_necessary do |options| rescue sh("xcrun simctl create '#{simulator_name}' #{simulator_type}") end +end +private_lane :config_maplibre do + api_key = ENV["MAPLIBRE_API_KEY"] + UI.user_error!("Invalid Map Libre API key.") unless !api_key.to_s.empty? + + set_xcconfig_value( + path: './secrets.xcconfig', + name: 'MAPLIBRE_API_KEY', + value: api_key + ) end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile index af36e2a026..44bbc84323 100644 --- a/fastlane/Pluginfile +++ b/fastlane/Pluginfile @@ -6,3 +6,4 @@ gem 'fastlane-plugin-diawi', git: 'https://github.com/mhtranbn/fastlane-plugin-d gem 'fastlane-plugin-xcodegen' gem 'fastlane-plugin-sentry' gem 'fastlane-plugin-browserstack' +gem 'fastlane-plugin-xcconfig' diff --git a/fastlane/README.md b/fastlane/README.md index abcda903f7..8608a7ea49 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -69,6 +69,14 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do +### config_production + +```sh +[bundle exec] fastlane config_production +``` + + + ### upload_dsyms_to_sentry ```sh diff --git a/project.yml b/project.yml index 44b894d29c..3ecc743535 100644 --- a/project.yml +++ b/project.yml @@ -4,6 +4,7 @@ attributes: fileGroups: - project.yml + - secrets.xcconfig options: groupSortPosition: bottom diff --git a/secrets.xcconfig b/secrets.xcconfig new file mode 100644 index 0000000000..5eb6c81379 --- /dev/null +++ b/secrets.xcconfig @@ -0,0 +1,20 @@ +// +// Copyright 2023 New Vector Ltd +// +// 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. +// + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 + +MAPLIBRE_API_KEY = your_key