152 lines
5.7 KiB
Swift
152 lines
5.7 KiB
Swift
import Foundation
|
|
|
|
enum APIError: LocalizedError {
|
|
case missingAppKey
|
|
case invalidResponse
|
|
case server(status: Int, code: String?, message: String)
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .missingAppKey:
|
|
return "Brak klucza aplikacji. Uzupełnij Config/Secrets.xcconfig."
|
|
case .invalidResponse:
|
|
return "Serwer zwrócił nieprawidłową odpowiedź."
|
|
case let .server(_, _, message):
|
|
return message
|
|
}
|
|
}
|
|
|
|
var code: String? {
|
|
guard case let .server(_, code, _) = self else { return nil }
|
|
return code
|
|
}
|
|
}
|
|
|
|
final class APIClient {
|
|
private let baseURL = URL(string: "https://kasztelan.co.uk/wp-json/kasztelan/v1")!
|
|
private let session: URLSession
|
|
|
|
init(session: URLSession = .shared) {
|
|
self.session = session
|
|
}
|
|
|
|
private var appKey: String {
|
|
Bundle.main.object(forInfoDictionaryKey: "KASZTELANAppKey") as? String ?? ""
|
|
}
|
|
|
|
func login(login: String, password: String) async throws -> APIEnvelope {
|
|
try await request("POST", path: "/login", body: ["login": login, "password": password])
|
|
}
|
|
|
|
func register(_ form: RegistrationForm) async throws -> APIEnvelope {
|
|
try await request("POST", path: "/register", body: [
|
|
"first_name": form.firstName,
|
|
"last_name": form.lastName,
|
|
"email": form.email,
|
|
"password": form.password,
|
|
"mobile": form.mobile,
|
|
"kod_pocztowy": form.postcode,
|
|
"dob": form.dob,
|
|
"numer_karty": form.cardNumber,
|
|
"register_tos": form.acceptsTerms
|
|
])
|
|
}
|
|
|
|
func me(token: String) async throws -> APIEnvelope {
|
|
try await request("GET", path: "/me", token: token)
|
|
}
|
|
|
|
func logout(token: String) async throws {
|
|
let _: APIEnvelope = try await request("POST", path: "/logout", token: token, body: [:])
|
|
}
|
|
|
|
func assignCard(_ number: String, token: String) async throws -> APIEnvelope {
|
|
try await request("POST", path: "/card", token: token, body: ["numer_karty": number])
|
|
}
|
|
|
|
func generateCard(token: String) async throws -> APIEnvelope {
|
|
try await request("POST", path: "/card/generate", token: token, body: [:])
|
|
}
|
|
|
|
func updateProfile(_ form: ProfileForm, token: String) async throws -> APIEnvelope {
|
|
try await request("POST", path: "/profile", token: token, body: [
|
|
"first_name": form.firstName,
|
|
"last_name": form.lastName,
|
|
"email": form.email,
|
|
"mobile": form.mobile,
|
|
"kod_pocztowy": form.postcode,
|
|
"dob": form.dob
|
|
])
|
|
}
|
|
|
|
func changePassword(current: String, new: String, token: String) async throws {
|
|
let _: APIEnvelope = try await request("POST", path: "/password", token: token, body: [
|
|
"current_password": current,
|
|
"new_password": new
|
|
])
|
|
}
|
|
|
|
func deleteAccount(password: String, token: String) async throws {
|
|
let _: APIEnvelope = try await request("DELETE", path: "/account", token: token, body: ["password": password])
|
|
}
|
|
|
|
func notifications(after: Int, token: String) async throws -> APIEnvelope {
|
|
try await request("GET", path: "/notifications?after=\(max(0, after))", token: token)
|
|
}
|
|
|
|
func latestNotification(token: String) async throws -> APIEnvelope {
|
|
try await request("GET", path: "/notifications/latest", token: token)
|
|
}
|
|
|
|
func wallet(token: String) async throws -> APIEnvelope {
|
|
try await request("GET", path: "/wallet", token: token)
|
|
}
|
|
|
|
func saveWallet(payload: String, token: String) async throws -> APIEnvelope {
|
|
try await request("POST", path: "/wallet", token: token, body: ["payload": payload])
|
|
}
|
|
|
|
private func request(
|
|
_ method: String,
|
|
path: String,
|
|
token: String? = nil,
|
|
body: [String: Any]? = nil
|
|
) async throws -> APIEnvelope {
|
|
guard !appKey.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
|
throw APIError.missingAppKey
|
|
}
|
|
guard let url = URL(string: baseURL.absoluteString + path) else { throw APIError.invalidResponse }
|
|
var request = URLRequest(url: url)
|
|
request.httpMethod = method
|
|
request.timeoutInterval = 20
|
|
request.cachePolicy = .reloadIgnoringLocalCacheData
|
|
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
|
request.setValue("no-cache, no-store", forHTTPHeaderField: "Cache-Control")
|
|
request.setValue(appKey, forHTTPHeaderField: "X-Kasztelan-App-Key")
|
|
request.setValue("iOS", forHTTPHeaderField: "X-Kasztelan-Platform")
|
|
request.setValue(
|
|
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "",
|
|
forHTTPHeaderField: "X-Kasztelan-App-Version"
|
|
)
|
|
if let token, !token.isEmpty {
|
|
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
}
|
|
if let body {
|
|
request.httpBody = try JSONSerialization.data(withJSONObject: body)
|
|
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
|
}
|
|
|
|
let (data, response) = try await session.data(for: request)
|
|
guard let http = response as? HTTPURLResponse else { throw APIError.invalidResponse }
|
|
let envelope = (try? JSONDecoder().decode(APIEnvelope.self, from: data)) ?? APIEnvelope()
|
|
guard (200..<300).contains(http.statusCode), envelope.success != false else {
|
|
throw APIError.server(
|
|
status: http.statusCode,
|
|
code: envelope.code,
|
|
message: envelope.message ?? "Nie udało się połączyć z kontem."
|
|
)
|
|
}
|
|
return envelope
|
|
}
|
|
}
|