From a5bd4ca150246a41d80272e1bbb49671877fbbf7 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 09:55:22 +0200 Subject: [PATCH 01/10] feat: make polling to par with android --- .gitignore | 1 + CONTRIBUTING.md | 12 +++ Example/.arkana.yml | 3 + Example/Podfile | 2 + Example/Podfile.lock | 13 ++- Example/SmileID.xcodeproj/project.pbxproj | 8 +- Example/SmileID/App/AppDelegate.swift | 12 ++- Example/SmileID/Info.plist | 4 +- Example/SmileID/Jobs/JobItemModel.swift | 22 +++-- Gemfile | 1 + Gemfile.lock | 9 +- .../Classes/Networking/SmileIDService.swift | 60 ++++++------ Tests/Networking/PollingTests.swift | 91 ++++++++++--------- 13 files changed, 146 insertions(+), 92 deletions(-) create mode 100644 Example/.arkana.yml diff --git a/.gitignore b/.gitignore index 96b37408c..da90223f2 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ archives *.mobileprovision *.sentryclirc .sentryclirc +**/ArkanaKeys/ # JetBrains/AppCode .idea diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 000cb70ac..6a615e770 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,6 +16,18 @@ We use [`rake`](https://github.com/ruby/rake) for task automation. - iOS 13 or higher - Xcode 14 or higher +## Sentry setup (Sample app only) + +```shell +export SENTRY_DSN='your_sentry_dsn' +``` +Run the following command to install the required variable for use in the example app + +```shell +bundle exec arkana -c .arkana.yml -l swift +``` +bundle exec rake test:package + ## SDK Tests ```shell diff --git a/Example/.arkana.yml b/Example/.arkana.yml new file mode 100644 index 000000000..fa424a892 --- /dev/null +++ b/Example/.arkana.yml @@ -0,0 +1,3 @@ +global_secrets: + - SENTRY_DSN +package_manager: cocoapods \ No newline at end of file diff --git a/Example/Podfile b/Example/Podfile index b630e9f91..7d0184227 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -6,6 +6,8 @@ target 'SmileID_Example' do pod 'netfox' pod 'Sentry' pod 'SwiftLint' + pod 'ArkanaKeys', :path => './ArkanaKeys/ArkanaKeys/' + pod 'ArkanaKeysInterfaces', :path => './ArkanaKeys/ArkanaKeysInterfaces/' target 'SmileID_Tests' do inherit! :search_paths diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 3db08726a..1370de83e 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,4 +1,7 @@ PODS: + - ArkanaKeys (1.0.0): + - ArkanaKeysInterfaces (~> 1.0.0) + - ArkanaKeysInterfaces (1.0.0) - lottie-ios (4.4.3) - netfox (1.21.0) - Sentry (8.31.1): @@ -11,6 +14,8 @@ PODS: - Zip (2.1.2) DEPENDENCIES: + - ArkanaKeys (from `./ArkanaKeys/ArkanaKeys/`) + - ArkanaKeysInterfaces (from `./ArkanaKeys/ArkanaKeysInterfaces/`) - netfox - Sentry - SmileID (from `../`) @@ -25,10 +30,16 @@ SPEC REPOS: - Zip EXTERNAL SOURCES: + ArkanaKeys: + :path: "./ArkanaKeys/ArkanaKeys/" + ArkanaKeysInterfaces: + :path: "./ArkanaKeys/ArkanaKeysInterfaces/" SmileID: :path: "../" SPEC CHECKSUMS: + ArkanaKeys: 356555f467c55ae40ba074c1b4d9cb5c38a55f3d + ArkanaKeysInterfaces: 81d21923368b058e2b6fd932ec96855166ef6d19 lottie-ios: fcb5e73e17ba4c983140b7d21095c834b3087418 netfox: 9d5cc727fe7576c4c7688a2504618a156b7d44b7 Sentry: 9c1188876ea1291d1a9db4b38c3f17ebd8e6985e @@ -36,6 +47,6 @@ SPEC CHECKSUMS: SwiftLint: 3fe909719babe5537c552ee8181c0031392be933 Zip: b3fef584b147b6e582b2256a9815c897d60ddc67 -PODFILE CHECKSUM: 8f7be2cf75babf479684f1e21d344f4c640486be +PODFILE CHECKSUM: ebb96bfc1b5a0c7f6061ff1e91ac138e67c1d9f4 COCOAPODS: 1.15.2 diff --git a/Example/SmileID.xcodeproj/project.pbxproj b/Example/SmileID.xcodeproj/project.pbxproj index 006587d84..94997566f 100644 --- a/Example/SmileID.xcodeproj/project.pbxproj +++ b/Example/SmileID.xcodeproj/project.pbxproj @@ -575,6 +575,8 @@ "${BUILT_PRODUCTS_DIR}/Zip/Zip.framework", "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework", "${BUILT_PRODUCTS_DIR}/netfox/netfox.framework", + "${BUILT_PRODUCTS_DIR}/ArkanaKeys/ArkanaKeys.framework", + "${BUILT_PRODUCTS_DIR}/ArkanaKeysInterfaces/ArkanaKeysInterfaces.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -583,6 +585,8 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Zip.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/netfox.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ArkanaKeys.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ArkanaKeysInterfaces.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -896,7 +900,7 @@ PRODUCT_NAME = "Smile ID"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.smileidentity.example-ios"; - SENTRY_DSN = "$(SENTRY_DSN)"; + ENABLE_SENTRY = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -929,7 +933,7 @@ PRODUCT_NAME = "Smile ID"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.smileidentity.example-ios"; - SENTRY_DSN = "$(SENTRY_DSN)"; + ENABLE_SENTRY = NO; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_SWIFT3_OBJC_INFERENCE = Default; diff --git a/Example/SmileID/App/AppDelegate.swift b/Example/SmileID/App/AppDelegate.swift index a9cc1818d..04f2b308b 100644 --- a/Example/SmileID/App/AppDelegate.swift +++ b/Example/SmileID/App/AppDelegate.swift @@ -3,6 +3,7 @@ import Sentry import SmileID import SwiftUI import UIKit +import ArkanaKeys @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -15,9 +16,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UINavigationBar.appearance().titleTextAttributes = [ NSAttributedString.Key.foregroundColor: UIColor.black ] - if let dsn = Bundle.main.object(forInfoDictionaryKey: "SentryDSN") as? String { + if(enableSentry()){ SentrySDK.start { options in - options.dsn = dsn + options.dsn = ArkanaKeys.Global().sENTRY_DSN options.debug = true options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 @@ -31,4 +32,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.makeKeyAndVisible() return true } + + func enableSentry() -> Bool { + guard let enableSentry = Bundle.main.object(forInfoDictionaryKey: "EnableSentry") as? String else { + return false + } + return enableSentry == "YES" + } } diff --git a/Example/SmileID/Info.plist b/Example/SmileID/Info.plist index d565eda1a..09bc02c40 100644 --- a/Example/SmileID/Info.plist +++ b/Example/SmileID/Info.plist @@ -26,8 +26,8 @@ We need access to your camera to verify your identity NSPhotoLibraryUsageDescription We need access to your photo library to save images - SentryDSN - $(SENTRY_DSN) + EnableSentry + $(ENABLE_SENTRY) UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/Example/SmileID/Jobs/JobItemModel.swift b/Example/SmileID/Jobs/JobItemModel.swift index 58cf7ad4a..e5519d9ce 100644 --- a/Example/SmileID/Jobs/JobItemModel.swift +++ b/Example/SmileID/Jobs/JobItemModel.swift @@ -34,12 +34,16 @@ class JobItemModel: ObservableObject { timestamp: authResponse.timestamp, signature: authResponse.signature ) - - let response = try await SmileID.api.pollJobStatus( + let pollStream = SmileID.api.pollJobStatus( request: request, interval: 1, numAttempts: 30 ) + var response : JobStatusResponse? = nil + + for try await res in pollStream { + response = res + } return JobData( jobType: job.jobType, @@ -47,13 +51,13 @@ class JobItemModel: ObservableObject { userId: job.userId, jobId: job.jobId, partnerId: job.partnerId, - jobComplete: response.jobComplete, - jobSuccess: response.jobSuccess, - code: response.code, - resultCode: response.result?.resultCode, - smileJobId: response.result?.smileJobId, - resultText: response.result?.resultText, - selfieImageUrl: response.imageLinks?.selfieImageUrl + jobComplete: response?.jobComplete ?? false, + jobSuccess: response?.jobSuccess ?? false, + code: response?.code, + resultCode: response?.result?.resultCode, + smileJobId: response?.result?.smileJobId, + resultText: response?.result?.resultText, + selfieImageUrl: response?.imageLinks?.selfieImageUrl ) } diff --git a/Gemfile b/Gemfile index b4640eec4..bba5a4a6f 100644 --- a/Gemfile +++ b/Gemfile @@ -2,5 +2,6 @@ source "https://rubygems.org" gem "fastlane" gem "xcodeproj" +gem "arkana" gem 'cocoapods', '~> 1.11.0' gem "rake", "~> 13.0.0" \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index b9c8390e2..a6b339a11 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,6 +14,10 @@ GEM algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) + arkana (2.1.1) + dotenv (~> 2.7) + rainbow (~> 3.1.1) + yaml (~> 0.2) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) @@ -220,6 +224,7 @@ GEM os (1.1.4) plist (3.7.0) public_suffix (4.0.7) + rainbow (3.1.1) rake (13.0.6) representable (3.2.0) declarative (< 0.1.0) @@ -270,6 +275,7 @@ GEM rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) + yaml (0.3.0) zeitwerk (2.6.11) PLATFORMS @@ -278,10 +284,11 @@ PLATFORMS x86_64-linux DEPENDENCIES + arkana cocoapods (~> 1.11.0) fastlane rake (~> 13.0.0) xcodeproj BUNDLED WITH - 2.3.11 + 2.5.6 diff --git a/Sources/SmileID/Classes/Networking/SmileIDService.swift b/Sources/SmileID/Classes/Networking/SmileIDService.swift index d40598553..bb2c219f9 100644 --- a/Sources/SmileID/Classes/Networking/SmileIDService.swift +++ b/Sources/SmileID/Classes/Networking/SmileIDService.swift @@ -97,35 +97,33 @@ public extension SmileIDServiceable { /// - numAttempts: The maximum number of polls before ending the flow func pollJobStatus( request: JobStatusRequest, - interval _: TimeInterval, + interval: TimeInterval, numAttempts: Int - ) async throws -> JobStatusResponse { - var lastError: Error? - var attemptCount = 0 - - func makeRequest() async throws -> JobStatusResponse { - attemptCount += 1 - - do { - let response: JobStatusResponse = try await SmileID.api.getJobStatus(request: request) - if response.jobComplete { - return response - } else if attemptCount < numAttempts { - return try await makeRequest() - } else { - throw SmileIDError.jobStatusTimeOut + ) -> AsyncThrowingStream, Error> { + AsyncThrowingStream { continuation in + Task { + var latestError: Error? + for _ in 0.. = try await SmileID.api.getJobStatus(request: request) + continuation.yield(response) + // Reset the error if the API response was successful + latestError = nil + if response.jobComplete { + break + } + } catch { + latestError = error + } + try await Task.sleep(nanoseconds: UInt64(interval * 1_000_000_000)) } - } catch { - lastError = error - if attemptCount < numAttempts { - return try await makeRequest() + if let latestError = latestError { + continuation.finish(throwing: latestError) } else { - throw lastError ?? error + continuation.finish() } } } - - return try await makeRequest() } /// Polls the server for the status of a SmartSelfie Job until it is complete. This should be called after @@ -141,8 +139,8 @@ public extension SmileIDServiceable { request: JobStatusRequest, interval: TimeInterval, numAttempts: Int - ) async throws -> SmartSelfieJobStatusResponse { - try await pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) + ) async throws -> AsyncThrowingStream { + return pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) } /// Polls the server for the status of a Document Verification Job until it is complete. This should be called after @@ -158,8 +156,8 @@ public extension SmileIDServiceable { request: JobStatusRequest, interval: TimeInterval, numAttempts: Int - ) async throws -> DocumentVerificationJobStatusResponse { - try await pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) + ) async throws -> AsyncThrowingStream { + return pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) } /// Polls the server for the status of a Biometric KYC Job until it is complete. This should be called after @@ -175,8 +173,8 @@ public extension SmileIDServiceable { request: JobStatusRequest, interval: TimeInterval, numAttempts: Int - ) async throws -> BiometricKycJobStatusResponse { - try await pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) + ) async throws -> AsyncThrowingStream { + return pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) } /// Polls the server for the status of a Enhanced Document Verification Job until it is complete. @@ -193,8 +191,8 @@ public extension SmileIDServiceable { request: JobStatusRequest, interval: TimeInterval, numAttempts: Int - ) async throws -> EnhancedDocumentVerificationJobStatusResponse { - try await pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) + ) async throws -> AsyncThrowingStream { + return pollJobStatus(request: request, interval: interval, numAttempts: numAttempts) } } diff --git a/Tests/Networking/PollingTests.swift b/Tests/Networking/PollingTests.swift index 6e82b62f5..8427d8f6f 100644 --- a/Tests/Networking/PollingTests.swift +++ b/Tests/Networking/PollingTests.swift @@ -27,67 +27,70 @@ final class PollingTests: XCTestCase { } func testPollJobStatus_Success( - pollFunction: (JobStatusRequest, TimeInterval, Int) async throws -> JobStatusResponse, + pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, expectedResponse: JobStatusResponse, requestBuilder: () -> JobStatusRequest - ) async { + ) async throws { let request = requestBuilder() let interval: TimeInterval = 1.0 let numAttempts = 3 - + + let stream = try await pollFunction(request, interval, numAttempts) + do { - let response = try await pollFunction(request, interval, numAttempts) - XCTAssertEqual(response.jobComplete, expectedResponse.jobComplete) + for try await response in stream { + if response.jobComplete { + XCTAssertEqual(response.jobComplete, expectedResponse.jobComplete) + return + } + } + XCTFail("Stream completed without a jobComplete response") } catch { XCTFail("Unexpected error: \(error)") } } - - func testPollingFunction_ErrorDuringPolling( - pollFunction: (JobStatusRequest, TimeInterval, Int) async throws -> JobStatusResponse, + + func testPollingFunction_ErrorDuringPolling( + pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, requestBuilder: () -> JobStatusRequest ) async { let request = requestBuilder() let interval: TimeInterval = 1.0 let numAttempts = 3 - - let expectation = XCTestExpectation(description: "Polling fails due to an error") - + MockHelper.shouldFail = true MockHelper.jobComplete = false - + do { - _ = try await pollFunction(request, interval, numAttempts) - XCTFail("No response should be received b/c an error occurs at first attempt") + let stream = try await pollFunction(request, interval, numAttempts) + for try await _ in stream { + XCTFail("No response should be received because an error occurs at first attempt") + } } catch { - expectation.fulfill() + // Expected to catch an error + XCTAssertNotNil(error) } - - await fulfillment(of: [expectation], timeout: 2.0) } - - func testPollingFunction_MaxAttemptsReached( - pollFunction: (JobStatusRequest, TimeInterval, Int) async throws -> JobStatusResponse, + + func testPollingFunction_MaxAttemptsReached( + pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, requestBuilder: () -> JobStatusRequest - ) async { + ) async throws { let request = requestBuilder() let interval: TimeInterval = 1.0 let numAttempts = 3 - - let expectation = XCTestExpectation( - description: "Polling fails due to reaching the maximum number of attempts" - ) - + MockHelper.shouldFail = false MockHelper.jobComplete = false - do { - let response = try await pollFunction(request, interval, numAttempts) - XCTAssertFalse(response.jobComplete, "Job is not complete") - } catch { - expectation.fulfill() + + let stream = try await pollFunction(request, interval, numAttempts) + + var responseCount = 0 + for try await response in stream { + XCTAssertFalse(response.jobComplete, "Job should not be complete") + responseCount += 1 } - - await fulfillment(of: [expectation], timeout: 2.0) + XCTAssertEqual(responseCount, numAttempts, "Should receive exactly \(numAttempts) responses") } func testPollSmartSelfieJobStatus() async throws { @@ -103,7 +106,7 @@ final class PollingTests: XCTestCase { ) } - await testPollJobStatus_Success( + try await testPollJobStatus_Success( pollFunction: mockService.pollSmartSelfieJobStatus, expectedResponse: expectedResponse, requestBuilder: requestBuilder @@ -114,13 +117,13 @@ final class PollingTests: XCTestCase { requestBuilder: requestBuilder ) - await testPollingFunction_MaxAttemptsReached( + try await testPollingFunction_MaxAttemptsReached( pollFunction: mockService.pollSmartSelfieJobStatus, requestBuilder: requestBuilder ) } - func testPollDocumentVerificationJobStatus() async { + func testPollDocumentVerificationJobStatus() async throws { let expectedResponse = DocumentVerificationJobStatusResponse(jobComplete: true) let requestBuilder = { JobStatusRequest( userId: "", @@ -133,7 +136,7 @@ final class PollingTests: XCTestCase { ) } - await testPollJobStatus_Success( + try await testPollJobStatus_Success( pollFunction: mockService.pollDocumentVerificationJobStatus, expectedResponse: expectedResponse, requestBuilder: requestBuilder @@ -144,13 +147,13 @@ final class PollingTests: XCTestCase { requestBuilder: requestBuilder ) - await testPollingFunction_MaxAttemptsReached( + try await testPollingFunction_MaxAttemptsReached( pollFunction: mockService.pollDocumentVerificationJobStatus, requestBuilder: requestBuilder ) } - func testPollBiometricKycJobStatus() async { + func testPollBiometricKycJobStatus() async throws { let expectedResponse = BiometricKycJobStatusResponse(jobComplete: true) let requestBuilder = { JobStatusRequest( userId: "", @@ -163,7 +166,7 @@ final class PollingTests: XCTestCase { ) } - await testPollJobStatus_Success( + try await testPollJobStatus_Success( pollFunction: mockService.pollBiometricKycJobStatus, expectedResponse: expectedResponse, requestBuilder: requestBuilder @@ -174,13 +177,13 @@ final class PollingTests: XCTestCase { requestBuilder: requestBuilder ) - await testPollingFunction_MaxAttemptsReached( + try await testPollingFunction_MaxAttemptsReached( pollFunction: mockService.pollBiometricKycJobStatus, requestBuilder: requestBuilder ) } - func testPollEnhancedDocumentVerificationJobStatus() async { + func testPollEnhancedDocumentVerificationJobStatus() async throws { let expectedResponse = EnhancedDocumentVerificationJobStatusResponse(jobComplete: true) let requestBuilder = { JobStatusRequest( userId: "", @@ -193,7 +196,7 @@ final class PollingTests: XCTestCase { ) } - await testPollJobStatus_Success( + try await testPollJobStatus_Success( pollFunction: mockService.pollEnhancedDocumentVerificationJobStatus, expectedResponse: expectedResponse, requestBuilder: requestBuilder @@ -204,7 +207,7 @@ final class PollingTests: XCTestCase { requestBuilder: requestBuilder ) - await testPollingFunction_MaxAttemptsReached( + try await testPollingFunction_MaxAttemptsReached( pollFunction: mockService.pollEnhancedDocumentVerificationJobStatus, requestBuilder: requestBuilder ) From c0b6d3feacc3809e065f42dd37b2c32759634375 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 10:09:44 +0200 Subject: [PATCH 02/10] fix: lint --- Example/SmileID/App/AppDelegate.swift | 8 ++++---- Example/SmileID/Jobs/JobItemModel.swift | 16 ++++++++-------- Tests/Networking/PollingTests.swift | 18 +++++++++--------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Example/SmileID/App/AppDelegate.swift b/Example/SmileID/App/AppDelegate.swift index 04f2b308b..57c9ea962 100644 --- a/Example/SmileID/App/AppDelegate.swift +++ b/Example/SmileID/App/AppDelegate.swift @@ -1,9 +1,9 @@ +import ArkanaKeys import netfox import Sentry import SmileID import SwiftUI import UIKit -import ArkanaKeys @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -14,9 +14,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { UINavigationBar.appearance().titleTextAttributes = [ - NSAttributedString.Key.foregroundColor: UIColor.black + NSAttributedString.Key.foregroundColor: UIColor.black, ] - if(enableSentry()){ + if enableSentry() { SentrySDK.start { options in options.dsn = ArkanaKeys.Global().sENTRY_DSN options.debug = true @@ -32,7 +32,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.makeKeyAndVisible() return true } - + func enableSentry() -> Bool { guard let enableSentry = Bundle.main.object(forInfoDictionaryKey: "EnableSentry") as? String else { return false diff --git a/Example/SmileID/Jobs/JobItemModel.swift b/Example/SmileID/Jobs/JobItemModel.swift index e5519d9ce..8bc77d253 100644 --- a/Example/SmileID/Jobs/JobItemModel.swift +++ b/Example/SmileID/Jobs/JobItemModel.swift @@ -39,8 +39,8 @@ class JobItemModel: ObservableObject { interval: 1, numAttempts: 30 ) - var response : JobStatusResponse? = nil - + var response: JobStatusResponse? = nil + for try await res in pollStream { response = res } @@ -67,18 +67,18 @@ class JobItemModel: ObservableObject { isLoading = true defer { isLoading = false } task = Task { - return try await getJobStatus() + try await getJobStatus() } - guard let task = self.task else { return } + guard let task = task else { return } let jobStatusResponse = try await task.value if let updatedJob = try dataStoreClient.updateJob(data: jobStatusResponse) { - self.job = updatedJob + job = updatedJob } } func cancelTask() { - self.isLoading = false - self.task?.cancel() - self.task = nil + isLoading = false + task?.cancel() + task = nil } } diff --git a/Tests/Networking/PollingTests.swift b/Tests/Networking/PollingTests.swift index 8427d8f6f..310a6e4a4 100644 --- a/Tests/Networking/PollingTests.swift +++ b/Tests/Networking/PollingTests.swift @@ -34,9 +34,9 @@ final class PollingTests: XCTestCase { let request = requestBuilder() let interval: TimeInterval = 1.0 let numAttempts = 3 - + let stream = try await pollFunction(request, interval, numAttempts) - + do { for try await response in stream { if response.jobComplete { @@ -49,7 +49,7 @@ final class PollingTests: XCTestCase { XCTFail("Unexpected error: \(error)") } } - + func testPollingFunction_ErrorDuringPolling( pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, requestBuilder: () -> JobStatusRequest @@ -57,10 +57,10 @@ final class PollingTests: XCTestCase { let request = requestBuilder() let interval: TimeInterval = 1.0 let numAttempts = 3 - + MockHelper.shouldFail = true MockHelper.jobComplete = false - + do { let stream = try await pollFunction(request, interval, numAttempts) for try await _ in stream { @@ -71,7 +71,7 @@ final class PollingTests: XCTestCase { XCTAssertNotNil(error) } } - + func testPollingFunction_MaxAttemptsReached( pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, requestBuilder: () -> JobStatusRequest @@ -79,12 +79,12 @@ final class PollingTests: XCTestCase { let request = requestBuilder() let interval: TimeInterval = 1.0 let numAttempts = 3 - + MockHelper.shouldFail = false MockHelper.jobComplete = false - + let stream = try await pollFunction(request, interval, numAttempts) - + var responseCount = 0 for try await response in stream { XCTAssertFalse(response.jobComplete, "Job should not be complete") From f2140d93ea5666b7e31d69117c8720864c613e97 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 10:12:08 +0200 Subject: [PATCH 03/10] fix: lint --- Tests/Networking/PollingTests.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Tests/Networking/PollingTests.swift b/Tests/Networking/PollingTests.swift index 310a6e4a4..aa3f9c91c 100644 --- a/Tests/Networking/PollingTests.swift +++ b/Tests/Networking/PollingTests.swift @@ -27,7 +27,8 @@ final class PollingTests: XCTestCase { } func testPollJobStatus_Success( - pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, + pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) + async throws -> AsyncThrowingStream, Error>, expectedResponse: JobStatusResponse, requestBuilder: () -> JobStatusRequest ) async throws { @@ -51,7 +52,8 @@ final class PollingTests: XCTestCase { } func testPollingFunction_ErrorDuringPolling( - pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, + pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) + async throws -> AsyncThrowingStream, Error>, requestBuilder: () -> JobStatusRequest ) async { let request = requestBuilder() @@ -73,7 +75,8 @@ final class PollingTests: XCTestCase { } func testPollingFunction_MaxAttemptsReached( - pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) async throws -> AsyncThrowingStream, Error>, + pollFunction: @escaping (JobStatusRequest, TimeInterval, Int) + async throws -> AsyncThrowingStream, Error>, requestBuilder: () -> JobStatusRequest ) async throws { let request = requestBuilder() From 259946374e1b631d711c44e0da4cedc02a3bc618 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 10:15:54 +0200 Subject: [PATCH 04/10] fix: lint --- Tests/Networking/PollingTests.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Tests/Networking/PollingTests.swift b/Tests/Networking/PollingTests.swift index aa3f9c91c..2dbc91a74 100644 --- a/Tests/Networking/PollingTests.swift +++ b/Tests/Networking/PollingTests.swift @@ -39,11 +39,9 @@ final class PollingTests: XCTestCase { let stream = try await pollFunction(request, interval, numAttempts) do { - for try await response in stream { - if response.jobComplete { - XCTAssertEqual(response.jobComplete, expectedResponse.jobComplete) - return - } + for try await response in stream where response.jobComplete { + XCTAssertEqual(response.jobComplete, expectedResponse.jobComplete) + return } XCTFail("Stream completed without a jobComplete response") } catch { From 989a86d002446eebfe0c53c266f370ec61cb0ae2 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 12:31:53 +0200 Subject: [PATCH 05/10] fix: ci arkana step --- .github/workflows/build-app.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml index b8e76e64d..b6c582035 100644 --- a/.github/workflows/build-app.yml +++ b/.github/workflows/build-app.yml @@ -49,6 +49,7 @@ jobs: - name: Build and Deploy to TestFlight 🚀 run: | cd example + bundle exec arkana -c .arkana.yml -l swift bundle exec fastlane releaseapp env: MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} From 408993300f5edd9e0d317a9f0ecadc999b4f2420 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 12:58:41 +0200 Subject: [PATCH 06/10] chore: ci fix --- Rakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Rakefile b/Rakefile index 9caa72a60..41eb570b0 100644 --- a/Rakefile +++ b/Rakefile @@ -109,6 +109,7 @@ namespace :provision do task :ios do Dir.chdir('Example') do sh 'bundle install' + sh 'bundle exec arkana -c .arkana.yml -l swift' sh 'pod install' sh 'bundle exec fastlane run_match' end From 0900a2914215dc4655fcd7dc888ed20b8afda862 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 13:04:05 +0200 Subject: [PATCH 07/10] chore: ci fix --- .github/workflows/sdk-primary.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/sdk-primary.yml b/.github/workflows/sdk-primary.yml index 2d00f594e..5409deee5 100644 --- a/.github/workflows/sdk-primary.yml +++ b/.github/workflows/sdk-primary.yml @@ -69,4 +69,6 @@ jobs: bundle exec rake test:spm bundle exec rake provision:ios bundle exec rake test:package + env: + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} From 9d715f89cf85c2936e6f0fb4d7de8c3418230855 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 16:30:31 +0200 Subject: [PATCH 08/10] feat: remove sentry from this branch --- .github/workflows/build-app.yml | 1 - .github/workflows/sdk-primary.yml | 2 -- .gitignore | 1 - CONTRIBUTING.md | 12 ------------ Example/.arkana.yml | 3 --- Example/Podfile | 2 -- Example/Podfile.lock | 13 +------------ Example/SmileID.xcodeproj/project.pbxproj | 8 ++------ Example/SmileID/App/AppDelegate.swift | 13 +++---------- Example/SmileID/Info.plist | 4 ++-- Gemfile | 3 +-- Gemfile.lock | 7 ------- Rakefile | 11 +++++------ 13 files changed, 14 insertions(+), 66 deletions(-) delete mode 100644 Example/.arkana.yml diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml index b6c582035..b8e76e64d 100644 --- a/.github/workflows/build-app.yml +++ b/.github/workflows/build-app.yml @@ -49,7 +49,6 @@ jobs: - name: Build and Deploy to TestFlight 🚀 run: | cd example - bundle exec arkana -c .arkana.yml -l swift bundle exec fastlane releaseapp env: MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} diff --git a/.github/workflows/sdk-primary.yml b/.github/workflows/sdk-primary.yml index 5409deee5..2d00f594e 100644 --- a/.github/workflows/sdk-primary.yml +++ b/.github/workflows/sdk-primary.yml @@ -69,6 +69,4 @@ jobs: bundle exec rake test:spm bundle exec rake provision:ios bundle exec rake test:package - env: - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} diff --git a/.gitignore b/.gitignore index da90223f2..96b37408c 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,6 @@ archives *.mobileprovision *.sentryclirc .sentryclirc -**/ArkanaKeys/ # JetBrains/AppCode .idea diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6a615e770..000cb70ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,18 +16,6 @@ We use [`rake`](https://github.com/ruby/rake) for task automation. - iOS 13 or higher - Xcode 14 or higher -## Sentry setup (Sample app only) - -```shell -export SENTRY_DSN='your_sentry_dsn' -``` -Run the following command to install the required variable for use in the example app - -```shell -bundle exec arkana -c .arkana.yml -l swift -``` -bundle exec rake test:package - ## SDK Tests ```shell diff --git a/Example/.arkana.yml b/Example/.arkana.yml deleted file mode 100644 index fa424a892..000000000 --- a/Example/.arkana.yml +++ /dev/null @@ -1,3 +0,0 @@ -global_secrets: - - SENTRY_DSN -package_manager: cocoapods \ No newline at end of file diff --git a/Example/Podfile b/Example/Podfile index 7d0184227..b630e9f91 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -6,8 +6,6 @@ target 'SmileID_Example' do pod 'netfox' pod 'Sentry' pod 'SwiftLint' - pod 'ArkanaKeys', :path => './ArkanaKeys/ArkanaKeys/' - pod 'ArkanaKeysInterfaces', :path => './ArkanaKeys/ArkanaKeysInterfaces/' target 'SmileID_Tests' do inherit! :search_paths diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 1370de83e..3db08726a 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,7 +1,4 @@ PODS: - - ArkanaKeys (1.0.0): - - ArkanaKeysInterfaces (~> 1.0.0) - - ArkanaKeysInterfaces (1.0.0) - lottie-ios (4.4.3) - netfox (1.21.0) - Sentry (8.31.1): @@ -14,8 +11,6 @@ PODS: - Zip (2.1.2) DEPENDENCIES: - - ArkanaKeys (from `./ArkanaKeys/ArkanaKeys/`) - - ArkanaKeysInterfaces (from `./ArkanaKeys/ArkanaKeysInterfaces/`) - netfox - Sentry - SmileID (from `../`) @@ -30,16 +25,10 @@ SPEC REPOS: - Zip EXTERNAL SOURCES: - ArkanaKeys: - :path: "./ArkanaKeys/ArkanaKeys/" - ArkanaKeysInterfaces: - :path: "./ArkanaKeys/ArkanaKeysInterfaces/" SmileID: :path: "../" SPEC CHECKSUMS: - ArkanaKeys: 356555f467c55ae40ba074c1b4d9cb5c38a55f3d - ArkanaKeysInterfaces: 81d21923368b058e2b6fd932ec96855166ef6d19 lottie-ios: fcb5e73e17ba4c983140b7d21095c834b3087418 netfox: 9d5cc727fe7576c4c7688a2504618a156b7d44b7 Sentry: 9c1188876ea1291d1a9db4b38c3f17ebd8e6985e @@ -47,6 +36,6 @@ SPEC CHECKSUMS: SwiftLint: 3fe909719babe5537c552ee8181c0031392be933 Zip: b3fef584b147b6e582b2256a9815c897d60ddc67 -PODFILE CHECKSUM: ebb96bfc1b5a0c7f6061ff1e91ac138e67c1d9f4 +PODFILE CHECKSUM: 8f7be2cf75babf479684f1e21d344f4c640486be COCOAPODS: 1.15.2 diff --git a/Example/SmileID.xcodeproj/project.pbxproj b/Example/SmileID.xcodeproj/project.pbxproj index 94997566f..006587d84 100644 --- a/Example/SmileID.xcodeproj/project.pbxproj +++ b/Example/SmileID.xcodeproj/project.pbxproj @@ -575,8 +575,6 @@ "${BUILT_PRODUCTS_DIR}/Zip/Zip.framework", "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework", "${BUILT_PRODUCTS_DIR}/netfox/netfox.framework", - "${BUILT_PRODUCTS_DIR}/ArkanaKeys/ArkanaKeys.framework", - "${BUILT_PRODUCTS_DIR}/ArkanaKeysInterfaces/ArkanaKeysInterfaces.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -585,8 +583,6 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Zip.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/netfox.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ArkanaKeys.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ArkanaKeysInterfaces.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -900,7 +896,7 @@ PRODUCT_NAME = "Smile ID"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.smileidentity.example-ios"; - ENABLE_SENTRY = YES; + SENTRY_DSN = "$(SENTRY_DSN)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -933,7 +929,7 @@ PRODUCT_NAME = "Smile ID"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match Development com.smileidentity.example-ios"; - ENABLE_SENTRY = NO; + SENTRY_DSN = "$(SENTRY_DSN)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_SWIFT3_OBJC_INFERENCE = Default; diff --git a/Example/SmileID/App/AppDelegate.swift b/Example/SmileID/App/AppDelegate.swift index 57c9ea962..7eca0f111 100644 --- a/Example/SmileID/App/AppDelegate.swift +++ b/Example/SmileID/App/AppDelegate.swift @@ -14,11 +14,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { UINavigationBar.appearance().titleTextAttributes = [ - NSAttributedString.Key.foregroundColor: UIColor.black, + NSAttributedString.Key.foregroundColor: UIColor.black ] - if enableSentry() { + if let dsn = Bundle.main.object(forInfoDictionaryKey: "SentryDSN") as? String { SentrySDK.start { options in - options.dsn = ArkanaKeys.Global().sENTRY_DSN + options.dsn = dsn options.debug = true options.tracesSampleRate = 1.0 options.profilesSampleRate = 1.0 @@ -32,11 +32,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate { window?.makeKeyAndVisible() return true } - - func enableSentry() -> Bool { - guard let enableSentry = Bundle.main.object(forInfoDictionaryKey: "EnableSentry") as? String else { - return false - } - return enableSentry == "YES" - } } diff --git a/Example/SmileID/Info.plist b/Example/SmileID/Info.plist index 09bc02c40..d565eda1a 100644 --- a/Example/SmileID/Info.plist +++ b/Example/SmileID/Info.plist @@ -26,8 +26,8 @@ We need access to your camera to verify your identity NSPhotoLibraryUsageDescription We need access to your photo library to save images - EnableSentry - $(ENABLE_SENTRY) + SentryDSN + $(SENTRY_DSN) UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/Gemfile b/Gemfile index bba5a4a6f..274dcb586 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,5 @@ source "https://rubygems.org" gem "fastlane" gem "xcodeproj" -gem "arkana" gem 'cocoapods', '~> 1.11.0' -gem "rake", "~> 13.0.0" \ No newline at end of file +gem "rake", "~> 13.0.0" diff --git a/Gemfile.lock b/Gemfile.lock index a6b339a11..e2016b655 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,10 +14,6 @@ GEM algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) - arkana (2.1.1) - dotenv (~> 2.7) - rainbow (~> 3.1.1) - yaml (~> 0.2) artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) @@ -224,7 +220,6 @@ GEM os (1.1.4) plist (3.7.0) public_suffix (4.0.7) - rainbow (3.1.1) rake (13.0.6) representable (3.2.0) declarative (< 0.1.0) @@ -275,7 +270,6 @@ GEM rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) - yaml (0.3.0) zeitwerk (2.6.11) PLATFORMS @@ -284,7 +278,6 @@ PLATFORMS x86_64-linux DEPENDENCIES - arkana cocoapods (~> 1.11.0) fastlane rake (~> 13.0.0) diff --git a/Rakefile b/Rakefile index 41eb570b0..ab1a47796 100644 --- a/Rakefile +++ b/Rakefile @@ -38,7 +38,7 @@ namespace :build do sh 'mv SmileID.zip SmileID.sha256 release/' end end - + namespace :test do desc 'Tests the package , processed the test results and tests spm compatibility' task all: ['package', 'process', 'spm','example'] @@ -62,7 +62,7 @@ namespace :test do xcodebuild('build -scheme "SmileID" -destination generic/platform=iOS',"SmileID.xcodeproj") end end - + namespace :lint do desc 'Lints swift files' task :swift do @@ -74,14 +74,14 @@ namespace :lint do sh 'pod lib lint SmileID.podspec' end end - + namespace :format do desc 'Formats swift files' task :swift do sh 'mint run swiftformat . --swiftversion 5.8' end end - + def xcodebuild(command, project = "Example/SmileID.xcworkspace") # Determine the project flag based on the file extension project_flag = if project.end_with?(".xcworkspace") @@ -109,7 +109,6 @@ namespace :provision do task :ios do Dir.chdir('Example') do sh 'bundle install' - sh 'bundle exec arkana -c .arkana.yml -l swift' sh 'pod install' sh 'bundle exec fastlane run_match' end @@ -125,4 +124,4 @@ def xcarchive(command) else sh "xcodebuild #{command}" end -end \ No newline at end of file +end From 6077e32d59c9b63a3cf93266be3e3b634342e7c6 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 16:37:30 +0200 Subject: [PATCH 09/10] feat: remove sentry fixes --- Example/SmileID/App/AppDelegate.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Example/SmileID/App/AppDelegate.swift b/Example/SmileID/App/AppDelegate.swift index 7eca0f111..a9cc1818d 100644 --- a/Example/SmileID/App/AppDelegate.swift +++ b/Example/SmileID/App/AppDelegate.swift @@ -1,4 +1,3 @@ -import ArkanaKeys import netfox import Sentry import SmileID From dc98712029303f0b50088a180c7dfa93458c0930 Mon Sep 17 00:00:00 2001 From: JNdhlovu Date: Tue, 6 Aug 2024 16:41:00 +0200 Subject: [PATCH 10/10] chore: update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e1019413..31756bda4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Release Notes +## 10.2.7 + +### Changed +* All polling methods now return a `AsyncThrowingStream, Error>` and instead of a timeout, if there is no error it'll return the last valid response and complete the stream. + ## 10.2.6 ### Changed