Skip to content

Commit

Permalink
Convert AnyPublisher to async/await (#183)
Browse files Browse the repository at this point in the history
* update code formatting to remove warnings.

* convert functions on networking interfaces from AnyPublisher to async throwing functions. (#180)

* Update Core Networking Implementation (#181)

* remove use of async bridge function

* update the core urlsession implementation of send and upload methods of rest service.

* remove URLUploadSessionPublisher interface and implementation

* update core networking implementation based on the async await changes made to the networking interfaces.

* update unit tests to use new async methods

* fix some code formatting issues.

* update the url session to use a protocol that is replaceable with a mock

* update the url session to use a protocol that is replaceable with a mock

* fix the issues with failing polling tests.

* update changelog.

* some code formatting

* cleanup import statement.

* restore disable lint rule

* Make change log more descriptive.

* update podspec file. update changelog. update SmileID.swift file and run pod install in example.
  • Loading branch information
tobitech authored Jun 18, 2024
1 parent a722a1f commit 11a8dd9
Show file tree
Hide file tree
Showing 22 changed files with 372 additions and 483 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Release Notes

## 10.2.0

#### Changed

* **Breaking Change:** Updated the networking layer to use Swift's `async/await` instead of Combine's `AnyPublisher` and now return `async` functions. This improves readability and aligns with modern Swift concurrency practices.
* All instances where these methods are used have been updated accordingly.

## 10.1.6

#### Added
Expand Down
4 changes: 2 additions & 2 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ PODS:
- Sentry (8.26.0):
- Sentry/Core (= 8.26.0)
- Sentry/Core (8.26.0)
- SmileID (10.1.6):
- SmileID (10.2.0):
- lottie-ios (~> 4.4.2)
- Zip (~> 2.1.0)
- SwiftLint (0.55.1)
Expand Down Expand Up @@ -32,7 +32,7 @@ SPEC CHECKSUMS:
lottie-ios: fcb5e73e17ba4c983140b7d21095c834b3087418
netfox: 9d5cc727fe7576c4c7688a2504618a156b7d44b7
Sentry: 74a073c71c998117edb08f56f443c83570a31bed
SmileID: a3dd288193c84518e4f7c0fe9383de685afbac6e
SmileID: bfcd426e9a47560eb949c18591ad032a8d8cde0d
SwiftLint: 3fe909719babe5537c552ee8181c0031392be933
Zip: b3fef584b147b6e582b2256a9815c897d60ddc67

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ class BiometricKycWithIdInputScreenViewModel: ObservableObject {
}
Task {
do {
let authResponse = try await SmileID.api.authenticate(request: authRequest).async()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
let productsConfigRequest = ProductsConfigRequest(
timestamp: authResponse.timestamp,
signature: authResponse.signature
)
let productsConfigResponse = try await SmileID.api.getProductsConfig(
request: productsConfigRequest
).async()
)
let supportedCountries = productsConfigResponse.idSelection.biometricKyc
let servicesResponse = try await SmileID.api.getServices().async()
let servicesResponse = try await SmileID.api.getServices()
let servicesCountryInfo = servicesResponse.hostedWeb.biometricKyc
// sort by country name
let countryList = servicesCountryInfo
Expand Down Expand Up @@ -74,7 +74,7 @@ class BiometricKycWithIdInputScreenViewModel: ObservableObject {
}
Task {
do {
let authResponse = try await SmileID.api.authenticate(request: authRequest).async()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
if authResponse.consentInfo?.consentRequired == true {
DispatchQueue.main.async {
self.step = .consent(
Expand Down
65 changes: 27 additions & 38 deletions Example/SmileID/DocumentSelectorViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,35 @@ class DocumentSelectorViewModel: ObservableObject {
fatalError("Only Document Verification jobs are supported")
}
self.jobType = jobType
let authRequest = AuthenticationRequest(
jobType: jobType,
enrollment: false,
userId: generateUserId()
)
let services = SmileID.api.getServices()
subscriber = SmileID.api.authenticate(request: authRequest)
.flatMap { authResponse in
let productRequest = ProductsConfigRequest(
timestamp: authResponse.timestamp,
signature: authResponse.signature
}

@MainActor
func getServices() async throws {
do {
let authRequest = AuthenticationRequest(
jobType: jobType,
enrollment: false,
userId: generateUserId()
)
let servicesResponse = try await SmileID.api.getServices()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
let productRequest = ProductsConfigRequest(
timestamp: authResponse.timestamp,
signature: authResponse.signature
)
let validDocumentsResponse = try await SmileID.api.getValidDocuments(request: productRequest)
let supportedDocuments = servicesResponse.hostedWeb.enhancedDocumentVerification
if jobType == .enhancedDocumentVerification {
self.idTypes = self.filteredForEnhancedDocumentVerification(
allDocuments: validDocumentsResponse.validDocuments,
supportedDocuments: supportedDocuments
)
return SmileID.api.getValidDocuments(request: productRequest)
} else {
self.idTypes = validDocumentsResponse.validDocuments
}
.zip(services)
.sink(
receiveCompletion: { completion in
switch completion {
case .failure(let error):
DispatchQueue.main.async {
self.errorMessage = error.localizedDescription
}
default:
break
}
},
receiveValue: { (validDocumentsResponse, servicesResponse) in
let supportedDocuments = servicesResponse.hostedWeb.enhancedDocumentVerification
DispatchQueue.main.async {
if jobType == .enhancedDocumentVerification {
self.idTypes = self.filteredForEnhancedDocumentVerification(
allDocuments: validDocumentsResponse.validDocuments,
supportedDocuments: supportedDocuments
)
} else {
self.idTypes = validDocumentsResponse.validDocuments
}
}
}
)
} catch {
self.errorMessage = error.localizedDescription
}
}

private func filteredForEnhancedDocumentVerification(
Expand Down
5 changes: 5 additions & 0 deletions Example/SmileID/DocumentVerificationIdTypeSelector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,10 @@ struct DocumentVerificationIdTypeSelector: View {
}
}
}
.onAppear {
Task {
try await self.viewModel.getServices()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ class EnhancedKycWithIdInputScreenViewModel: ObservableObject {
DispatchQueue.main.async { self.step = .loading("Loading ID Types…") }
Task {
do {
let authResponse = try await SmileID.api.authenticate(request: authRequest).async()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
let productsConfigRequest = ProductsConfigRequest(
timestamp: authResponse.timestamp,
signature: authResponse.signature
)
let productsConfigResponse = try await SmileID.api.getProductsConfig(
request: productsConfigRequest
).async()
)
let supportedCountries = productsConfigResponse.idSelection.enhancedKyc
let servicesResponse = try await SmileID.api.getServices().async()
let servicesResponse = try await SmileID.api.getServices()
let servicesCountryInfo = servicesResponse.hostedWeb.enhancedKyc
// sort by country name
let countryList = servicesCountryInfo
Expand Down Expand Up @@ -75,7 +75,7 @@ class EnhancedKycWithIdInputScreenViewModel: ObservableObject {
}
Task {
do {
let authResponse = try await SmileID.api.authenticate(request: authRequest).async()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
if authResponse.consentInfo?.consentRequired == true {
DispatchQueue.main.async {
self.step = .consent(
Expand Down Expand Up @@ -132,7 +132,7 @@ class EnhancedKycWithIdInputScreenViewModel: ObservableObject {
jobId: jobId,
userId: userId
)
let authResponse = try await SmileID.api.authenticate(request: authRequest).async()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
let enhancedKycRequest = EnhancedKycRequest(
country: idInfo.country,
idType: idInfo.idType!,
Expand All @@ -147,7 +147,7 @@ class EnhancedKycWithIdInputScreenViewModel: ObservableObject {
)
enhancedKycResponse = try await SmileID.api.doEnhancedKyc(
request: enhancedKycRequest
).async()
)
DispatchQueue.main.async { self.step = .processing(.success) }
} catch {
self.error = error
Expand Down
6 changes: 3 additions & 3 deletions SmileID.podspec
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Pod::Spec.new do |s|
s.name = 'SmileID'
s.version = '10.1.6'
s.version = '10.2.0'
s.summary = 'The Official Smile Identity iOS SDK.'
s.homepage = 'https://docs.usesmileid.com/integration-options/mobile/ios-v10-beta'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'Japhet' => 'japhet@usesmileid.com', 'Juma Allan' => 'juma@usesmileid.com', 'Vansh Gandhi' => 'vansh@usesmileid.com'}
s.source = { :git => "https://github.com/smileidentity/ios.git", :tag => "v10.1.6" }
s.source = { :git => "https://github.com/smileidentity/ios.git", :tag => "v10.2.0" }
s.ios.deployment_target = '13.0'
s.dependency 'Zip', '~> 2.1.0'
s.dependency 'lottie-ios', '~> 4.4.2'
Expand All @@ -14,4 +14,4 @@ Pod::Spec.new do |s|
s.resource_bundles = {
'SmileID_SmileID' => ['Sources/SmileID/Resources/**/*.{storyboard,storyboardc,xib,nib,xcassets,json,png,ttf,lproj,xcprivacy}']
}
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ internal class OrchestratedBiometricKycViewModel: ObservableObject {
partnerParams: extraPartnerParams
)
}
let authResponse = try await SmileID.api.authenticate(request: authRequest).async()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
let prepUploadRequest = PrepUploadRequest(
partnerParams: authResponse.partnerParams.copy(extras: extraPartnerParams),
allowNewEnroll: String(allowNewEnroll), // TODO: - Fix when Michael changes this to boolean
Expand All @@ -109,11 +109,11 @@ internal class OrchestratedBiometricKycViewModel: ObservableObject {
)
let prepUploadResponse = try await SmileID.api.prepUpload(
request: prepUploadRequest
).async()
)
_ = try await SmileID.api.upload(
zip: zip,
to: prepUploadResponse.uploadUrl
).async()
)
didSubmitBiometricJob = true
do {
try LocalStorage.moveToSubmittedJobs(jobId: self.jobId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,18 +179,18 @@ internal class IOrchestratedDocumentVerificationViewModel<T, U: JobResult>: Obse
partnerParams: extraPartnerParams
)
}
let authResponse = try await SmileID.api.authenticate(request: authRequest).async()
let authResponse = try await SmileID.api.authenticate(request: authRequest)
let prepUploadRequest = PrepUploadRequest(
partnerParams: authResponse.partnerParams.copy(extras: self.extraPartnerParams),
allowNewEnroll: String(allowNewEnroll), // TODO: - Fix when Michael changes this to boolean
timestamp: authResponse.timestamp,
signature: authResponse.signature
)
let prepUploadResponse = try await SmileID.api.prepUpload(request: prepUploadRequest).async()
let prepUploadResponse = try await SmileID.api.prepUpload(request: prepUploadRequest)
_ = try await SmileID.api.upload(
zip: zip,
to: prepUploadResponse.uploadUrl
).async()
)
didSubmitJob = true
do {
try LocalStorage.moveToSubmittedJobs(jobId: self.jobId)
Expand Down
40 changes: 20 additions & 20 deletions Sources/SmileID/Classes/Networking/APIError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ public enum SmileIDError: Error {
extension SmileIDError: LocalizedError {
public var errorDescription: String? {
switch self {
case .encode(let error):
return String(describing: error)
case .request(let error):
return String(describing: error)
case .decode(let error):
return String(describing: error)
case .unknown(let message):
return message
case .httpError(let statusCode, let data):
return "HTTP Error with status code \(statusCode) and \(String(describing: data))"
case .api(_, let message):
return message
case .jobStatusTimeOut:
return "Job submitted successfully but polling job status timed out"
case .consentDenied:
return "Consent Denied"
case .invalidJobId:
return "Invalid jobId or not found"
case .fileNotFound(let message):
return message
case .encode(let error):
return String(describing: error)
case .request(let error):
return String(describing: error)
case .decode(let error):
return String(describing: error)
case .unknown(let message):
return message
case .httpError(let statusCode, let data):
return "HTTP Error with status code \(statusCode) and \(String(describing: data))"
case .api(_, let message):
return message
case .jobStatusTimeOut:
return "Job submitted successfully but polling job status timed out"
case .consentDenied:
return "Consent Denied"
case .invalidJobId:
return "Invalid jobId or not found"
case .fileNotFound(let message):
return message
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions Sources/SmileID/Classes/Networking/RestServiceClient.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import Combine
import Foundation

protocol RestServiceClient {
func send<T: Decodable>(request: RestRequest) -> AnyPublisher<T, Error>
func multipart<T: Decodable>(request: RestRequest) -> AnyPublisher<T, Error>
func upload(request: RestRequest) -> AnyPublisher<UploadResponse, Error>
func send<T: Decodable>(request: RestRequest) async throws -> T
func multipart<T: Decodable>(request: RestRequest) async throws -> T
func upload(request: RestRequest) async throws -> AsyncThrowingStream<UploadResponse, Error>
}
Loading

0 comments on commit 11a8dd9

Please sign in to comment.