Skip to content

Commit

Permalink
feat: make polling to par with android (#211)
Browse files Browse the repository at this point in the history
* feat: make polling to par with android

* fix: lint

* fix: lint

* fix: lint

* fix: ci arkana step

* chore: ci fix

* chore: ci fix

* feat: remove sentry from this branch

* feat: remove sentry fixes

* chore: update changelog
  • Loading branch information
JNdhlovu authored Aug 6, 2024
1 parent c1245a3 commit 4a7148c
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 91 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Release Notes

## 10.2.7

### Changed
* All polling methods now return a `AsyncThrowingStream<JobStatusResponse<T>, 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
Expand Down
34 changes: 19 additions & 15 deletions Example/SmileID/Jobs/JobItemModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,30 @@ 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<JobResult>? = nil

for try await res in pollStream {
response = res
}

return JobData(
jobType: job.jobType,
timestamp: job.timestamp,
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
)
}

Expand All @@ -63,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
}
}
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source "https://rubygems.org"
gem "fastlane"
gem "xcodeproj"
gem 'cocoapods', '~> 1.11.0'
gem "rake", "~> 13.0.0"
gem "rake", "~> 13.0.0"
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -284,4 +284,4 @@ DEPENDENCIES
xcodeproj

BUNDLED WITH
2.3.11
2.5.6
10 changes: 5 additions & 5 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand All @@ -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
Expand All @@ -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")
Expand Down Expand Up @@ -124,4 +124,4 @@ def xcarchive(command)
else
sh "xcodebuild #{command}"
end
end
end
60 changes: 29 additions & 31 deletions Sources/SmileID/Classes/Networking/SmileIDService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,35 +97,33 @@ public extension SmileIDServiceable {
/// - numAttempts: The maximum number of polls before ending the flow
func pollJobStatus<T: JobResult>(
request: JobStatusRequest,
interval _: TimeInterval,
interval: TimeInterval,
numAttempts: Int
) async throws -> JobStatusResponse<T> {
var lastError: Error?
var attemptCount = 0

func makeRequest() async throws -> JobStatusResponse<T> {
attemptCount += 1

do {
let response: JobStatusResponse<T> = 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<JobStatusResponse<T>, Error> {
AsyncThrowingStream { continuation in
Task {
var latestError: Error?
for _ in 0..<numAttempts {
do {
let response: JobStatusResponse<T> = 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
Expand All @@ -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<SmartSelfieJobStatusResponse, Error> {
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
Expand All @@ -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<DocumentVerificationJobStatusResponse, Error> {
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
Expand All @@ -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<BiometricKycJobStatusResponse, Error> {
return pollJobStatus(request: request, interval: interval, numAttempts: numAttempts)
}

/// Polls the server for the status of a Enhanced Document Verification Job until it is complete.
Expand All @@ -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<EnhancedDocumentVerificationJobStatusResponse, Error> {
return pollJobStatus(request: request, interval: interval, numAttempts: numAttempts)
}
}

Expand Down
Loading

0 comments on commit 4a7148c

Please sign in to comment.