Skip to content

Improve network handling in API wrapper #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions Sources/SnapAuth/ApiClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,46 @@ struct SnapAuthClient {
path: String,
body: Encodable,
type: T.Type
) async -> SAWrappedResponse<T>? {
) async -> Result<T, SnapAuthError> where T: Decodable {
let url = urlBase.appendingPathComponent(path)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "content-type")
request.setValue(basic, forHTTPHeaderField: "Authorization")
let json = try! JSONEncoder().encode(body)
request.httpBody = json
logger?.debug("--> \(String(decoding: json, as: UTF8.self))")

let (data, response) = try! await URLSession.shared.data(for: request)
do {
let json = try JSONEncoder().encode(body)
request.httpBody = json
logger?.debug("--> \(String(decoding: json, as: UTF8.self))")
} catch {
return .failure(.sdkEncodingError)
}

let data: Data
let response: URLResponse
do {
(data, response) = try await URLSession.shared.data(for: request)
} catch {
return .failure(.networkInterruption)
}

let jsonString = String(data: data, encoding: .utf8)
logger?.debug("<-- \(jsonString ?? "not a string")")

// This allows skipping custom decodable implementations
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
guard let parsed = try? decoder.decode(
SAWrappedResponse<T>.self,
from: data)
guard let parsed = try? decoder.decode(SAWrappedResponse<T>.self, from: data)
else {
logger?.error("Decoding request failed")
// TODO: return some sort of failure SAResponse
return nil
logger?.error("Decoding response failed")
return .failure(.malformedResposne)
}

return parsed
guard let wrapped = parsed.result else {
// TODO: match all of the docuemented error types
return .failure(.badRequest)
}
return .success(wrapped)
}

}
15 changes: 15 additions & 0 deletions Sources/SnapAuth/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,18 @@ public enum AuthenticationError: Error {

case asAuthorizationError
}


public enum SnapAuthError: Error {
/// The network request was disrupted.
case networkInterruption

/// The SDK received a response from SnapAuth, but it arrived in an unexpected format. If you encounter this, please reach out to us.
case malformedResposne

/// The SDK was unable to encode data to send to SnapAuth. If you ever encounter this, please reach out to us.
case sdkEncodingError

/// The request was valid and understood, but processing was refused.
case badRequest
}
28 changes: 10 additions & 18 deletions Sources/SnapAuth/SnapAuth+ASACD.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,23 +112,19 @@ extension SnapAuth: ASAuthorizationControllerDelegate {
let body = SAProcessRegisterRequest(credential: credential)

Task {
let tokenResponse = await api.makeRequest(
let response = await api.makeRequest(
path: "/registration/process",
body: body,
type: SAProcessAuthResponse.self)
guard tokenResponse != nil else {
guard case let .success(processAuth) = response else {
logger.debug("no/invalid process response")
// TODO: delegate failure (network error?)
return
}
guard tokenResponse!.result != nil else {
// TODO: bubble this up
// TODO: bubble this up via delegate failure (network error?)
return
}
logger.debug("got token response")
let rewrapped = SnapAuthTokenInfo(
token: tokenResponse!.result!.token,
expiresAt: tokenResponse!.result!.expiresAt)
token: processAuth.token,
expiresAt: processAuth.expiresAt)

await delegate?.snapAuth(didFinishRegistration: .success(rewrapped))
}
Expand Down Expand Up @@ -162,23 +158,19 @@ extension SnapAuth: ASAuthorizationControllerDelegate {
logger.debug("made a body")
// logger.debug("user id \(assertion.userID.base64EncodedString())")
Task {
let tokenResponse = await api.makeRequest(
let response = await api.makeRequest(
path: "/auth/process",
body: body,
type: SAProcessAuthResponse.self)
guard tokenResponse != nil else {
guard case let .success(authResponse) = response else {
logger.debug("no/invalid process response")
// TODO: delegate failure (network error?)
return
}
guard tokenResponse!.result != nil else {
// TODO: bubble this up
// TODO: bubble this up via delegate failure (network error?)
return
}
logger.debug("got token response")
let rewrapped = SnapAuthTokenInfo(
token: tokenResponse!.result!.token,
expiresAt: tokenResponse!.result!.expiresAt)
token: authResponse.token,
expiresAt: authResponse.expiresAt)

await delegate?.snapAuth(didFinishAuthentication: .success(rewrapped))
}
Expand Down
16 changes: 8 additions & 8 deletions Sources/SnapAuth/SnapAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,18 @@ public class SnapAuth: NSObject { // NSObject for ASAuthorizationControllerDeleg
state = .registering

let body = SACreateRegisterOptionsRequest(user: nil)
let options = await api.makeRequest(
let response = await api.makeRequest(
path: "/registration/createOptions",
body: body,
type: SACreateRegisterOptionsResponse.self)!
type: SACreateRegisterOptionsResponse.self)

guard options.result != nil else {
guard case let .success(options) = response else {
// TODO: bubble error
return
}

let authRequests = buildRegisterRequests(
from: options.result!,
from: options,
name: name,
displayName: displayName,
authenticators: authenticators)
Expand Down Expand Up @@ -159,20 +159,20 @@ public class SnapAuth: NSObject { // NSObject for ASAuthorizationControllerDeleg

let body = ["user": user]

let parsed = await api.makeRequest(
let response = await api.makeRequest(
path: "/auth/createOptions",
body: body,
type: SACreateAuthOptionsResponse.self)!
type: SACreateAuthOptionsResponse.self)


guard parsed.result != nil else {
guard case let .success(options) = response else {
// TODO: bubble error
return
}

logger.debug("before controller")
let authRequests = buildAuthRequests(
from: parsed.result!,
from: options,
authenticators: authenticators)

// Set up the native controller and start the request(s).
Expand Down