diff --git a/LICENSE b/LICENSE index 5479bb8..c1602fc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2024 Appwrite (https://appwrite.io) and individual contributors. +Copyright (c) 2025 Appwrite (https://appwrite.io) and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index b0310bc..f67b3d4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Swift Package Manager](https://img.shields.io/github/v/release/appwrite/sdk-for-apple.svg?color=green&style=flat-square) ![License](https://img.shields.io/github/license/appwrite/sdk-for-apple.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-1.6.0-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-1.6.1-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) @@ -31,7 +31,7 @@ Add the package to your `Package.swift` dependencies: ```swift dependencies: [ - .package(url: "git@github.com:appwrite/sdk-for-apple.git", from: "7.0.0"), + .package(url: "git@github.com:appwrite/sdk-for-apple.git", from: "7.1.1"), ], ``` diff --git a/Sources/Appwrite/Client.swift b/Sources/Appwrite/Client.swift index 13754d1..c85296d 100644 --- a/Sources/Appwrite/Client.swift +++ b/Sources/Appwrite/Client.swift @@ -23,7 +23,7 @@ open class Client { "x-sdk-name": "Apple", "x-sdk-platform": "client", "x-sdk-language": "apple", - "x-sdk-version": "7.0.0", + "x-sdk-version": "7.1.1", "x-appwrite-response-format": "1.6.0" ] @@ -249,6 +249,26 @@ open class Client { ) ?? "" } + /// + /// Sends a "ping" request to Appwrite to verify connectivity. + /// + /// @return String + /// @throws Exception + /// + open func ping() async throws -> String { + let apiPath: String = "/ping" + + let apiHeaders: [String: String] = [ + "content-type": "application/json" + ] + + return try await call( + method: "GET", + path: apiPath, + headers: apiHeaders + ) + } + /// /// Make an API call /// @@ -319,6 +339,8 @@ open class Client { } } + var data = try await response.body.collect(upTo: Int.max) + switch response.status.code { case 0..<400: if response.headers["Set-Cookie"].count > 0 { @@ -331,10 +353,11 @@ open class Client { switch T.self { case is Bool.Type: return true as! T + case is String.Type: + return (data.readString(length: data.readableBytes) ?? "") as! T case is ByteBuffer.Type: - return try await response.body.collect(upTo: Int.max) as! T + return data as! T default: - let data = try await response.body.collect(upTo: Int.max) if data.readableBytes == 0 { return true as! T } @@ -344,7 +367,6 @@ open class Client { } default: var message = "" - var data = try await response.body.collect(upTo: Int.max) var type = "" do { @@ -400,7 +422,7 @@ open class Client { var offset = 0 var result = [String:Any]() - if idParamName != nil && params[idParamName!] as! String != "unique()" { + if idParamName != nil { // Make a request to check if a file already exists do { let map = try await call( @@ -464,23 +486,23 @@ open class Client { if param is String || param is Int || param is Float + || param is Double || param is Bool || param is [String] || param is [Int] || param is [Float] + || param is [Double] || param is [Bool] || param is [String: Any] || param is [Int: Any] || param is [Float: Any] + || param is [Double: Any] || param is [Bool: Any] { encodedParams[key] = param - } else { - let value = try! (param as! Encodable).toJson() - - let range = value.index(value.startIndex, offsetBy: 1)..) -> Void]() + #if canImport(AuthenticationServices) + private static var currentAuthSession: ASWebAuthenticationSession? + #endif /// /// Authenticate Session with OAuth2 @@ -41,9 +39,29 @@ public class WebAuthComponent { ) { callbacks[callbackScheme] = onComplete - #if canImport(SwiftUI) - openURL(url) - #endif + #if canImport(AuthenticationServices) + currentAuthSession = ASWebAuthenticationSession( + url: url, + callbackURLScheme: callbackScheme + ) { callbackURL, error in + if let error = error { + cleanUp() + } else if let callbackURL = callbackURL { + // handle cookies here itself! + WebAuthComponent.handleIncomingCookie(from: callbackURL) + cleanUp() + } + } + + if let session = currentAuthSession { + /// Indicates that the session should be a private session. + session.prefersEphemeralWebBrowserSession = true + session.presentationContextProvider = PresentationContextProvider.shared + session.start() + } else { + print("Failed to create ASWebAuthenticationSession") + } + #endif } /// @@ -130,5 +148,24 @@ public class WebAuthComponent { callbacks.forEach { (_, callback) in callback(.failure(AppwriteError(message: "User cancelled login."))) } + + #if canImport(AuthenticationServices) + currentAuthSession = nil + #endif + } +} + +#if canImport(AuthenticationServices) +/// Presentation context for the ASWebAuthenticationSession. +class PresentationContextProvider: NSObject, ASWebAuthenticationPresentationContextProviding { + static let shared = PresentationContextProvider() + + func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + if let mainWindow = OSApplication.shared.windows.first { $0.isKeyWindow } { + return mainWindow + } + + return ASPresentationAnchor() } } +#endif \ No newline at end of file diff --git a/Sources/Appwrite/Services/Account.swift b/Sources/Appwrite/Services/Account.swift index 87e1b98..6155931 100644 --- a/Sources/Appwrite/Services/Account.swift +++ b/Sources/Appwrite/Services/Account.swift @@ -214,7 +214,7 @@ open class Account: Service { } /// - /// List Identities + /// List identities /// /// Get the list of identities for the currently logged in user. /// @@ -402,7 +402,7 @@ open class Account: Service { } /// - /// Create Authenticator + /// Create authenticator /// /// Add an authenticator app to be used as an MFA factor. Verify the /// authenticator using the [verify @@ -439,7 +439,7 @@ open class Account: Service { } /// - /// Verify Authenticator + /// Verify authenticator /// /// Verify an authenticator app after adding it using the [add /// authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) @@ -480,7 +480,7 @@ open class Account: Service { } /// - /// Verify Authenticator + /// Verify authenticator /// /// Verify an authenticator app after adding it using the [add /// authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) @@ -503,7 +503,7 @@ open class Account: Service { } /// - /// Delete Authenticator + /// Delete authenticator /// /// Delete an authenticator for a user by ID. /// @@ -531,7 +531,7 @@ open class Account: Service { } /// - /// Create MFA Challenge + /// Create MFA challenge /// /// Begin the process of MFA verification after sign-in. Finish the flow with /// [updateMfaChallenge](/docs/references/cloud/client-web/account#updateMfaChallenge) @@ -568,7 +568,7 @@ open class Account: Service { } /// - /// Create MFA Challenge (confirmation) + /// Create MFA challenge (confirmation) /// /// Complete the MFA challenge by providing the one-time password. Finish the /// process of MFA verification by providing the one-time password. To begin @@ -584,7 +584,7 @@ open class Account: Service { open func updateMfaChallenge( challengeId: String, otp: String - ) async throws -> Any { + ) async throws -> AppwriteModels.Session { let apiPath: String = "/account/mfa/challenge" let apiParams: [String: Any?] = [ @@ -596,15 +596,21 @@ open class Account: Service { "content-type": "application/json" ] + let converter: (Any) -> AppwriteModels.Session = { response in + return AppwriteModels.Session.from(map: response as! [String: Any]) + } + return try await client.call( method: "PUT", path: apiPath, headers: apiHeaders, - params: apiParams ) + params: apiParams, + converter: converter + ) } /// - /// List Factors + /// List factors /// /// List the factors available on the account to be used as a MFA challange. /// @@ -635,7 +641,7 @@ open class Account: Service { } /// - /// Get MFA Recovery Codes + /// Get MFA recovery codes /// /// Get recovery codes that can be used as backup for MFA flow. Before getting /// codes, they must be generated using @@ -669,7 +675,7 @@ open class Account: Service { } /// - /// Create MFA Recovery Codes + /// Create MFA recovery codes /// /// Generate recovery codes as backup for MFA flow. It's recommended to /// generate and show then immediately after user successfully adds their @@ -704,7 +710,7 @@ open class Account: Service { } /// - /// Regenerate MFA Recovery Codes + /// Regenerate MFA recovery codes /// /// Regenerate recovery codes that can be used as backup for MFA flow. Before /// regenerating codes, they must be first generated using @@ -1350,12 +1356,16 @@ open class Account: Service { let callbackScheme = "appwrite-callback-\(client.config["project"] ?? "")" _ = try await withCheckedThrowingContinuation { continuation in - WebAuthComponent.authenticate(url: url, callbackScheme: callbackScheme) { result in - continuation.resume(with: result) + /// main thread for PresentationContextProvider + DispatchQueue.main.async { + WebAuthComponent.authenticate(url: url, callbackScheme: callbackScheme) { result in + continuation.resume(with: result) + } } } - + return true + } /// @@ -1595,6 +1605,12 @@ open class Account: Service { /// /// Create push target /// + /// Use this endpoint to register a device for push notifications. Provide a + /// target ID (custom or generated using ID.unique()), a device identifier + /// (usually a device token), and optionally specify which provider should send + /// notifications to this target. The target is automatically linked to the + /// current session and includes device information like brand and model. + /// /// @param String targetId /// @param String identifier /// @param String providerId @@ -1634,6 +1650,12 @@ open class Account: Service { /// /// Update push target /// + /// Update the currently logged in user's push notification target. You can + /// modify the target's identifier (device token) and provider ID (token, + /// email, phone etc.). The target must exist and belong to the current user. + /// If you change the provider ID, notifications will be sent through the new + /// messaging provider instead. + /// /// @param String targetId /// @param String identifier /// @throws Exception @@ -1670,6 +1692,10 @@ open class Account: Service { /// /// Delete push target /// + /// Delete a push notification target for the currently logged in user. After + /// deletion, the device will no longer receive push notifications. The target + /// must exist and belong to the current user. + /// /// @param String targetId /// @throws Exception /// @return array @@ -1754,9 +1780,7 @@ open class Account: Service { /// [POST /// /v1/account/sessions/token](https://appwrite.io/docs/references/cloud/client-web/account#createSession) /// endpoint to complete the login process. The link sent to the user's email - /// address is valid for 1 hour. If you are on a mobile device you can leave - /// the URL parameter empty, so that the login completion will be handled by - /// your Appwrite instance by default. + /// address is valid for 1 hour. /// /// A user is limited to 10 active sessions at a time by default. [Learn more /// about session @@ -1849,12 +1873,16 @@ open class Account: Service { let callbackScheme = "appwrite-callback-\(client.config["project"] ?? "")" _ = try await withCheckedThrowingContinuation { continuation in - WebAuthComponent.authenticate(url: url, callbackScheme: callbackScheme) { result in - continuation.resume(with: result) + /// main thread for PresentationContextProvider + DispatchQueue.main.async { + WebAuthComponent.authenticate(url: url, callbackScheme: callbackScheme) { result in + continuation.resume(with: result) + } } } - + return true + } /// diff --git a/Sources/Appwrite/Services/Locale.swift b/Sources/Appwrite/Services/Locale.swift index 3ad37de..170e11a 100644 --- a/Sources/Appwrite/Services/Locale.swift +++ b/Sources/Appwrite/Services/Locale.swift @@ -45,7 +45,7 @@ open class Locale: Service { } /// - /// List Locale Codes + /// List locale codes /// /// List of all locale codes in [ISO /// 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). diff --git a/Sources/Appwrite/Services/Realtime.swift b/Sources/Appwrite/Services/Realtime.swift index 3bc2e9e..ecc4a05 100644 --- a/Sources/Appwrite/Services/Realtime.swift +++ b/Sources/Appwrite/Services/Realtime.swift @@ -7,11 +7,14 @@ open class Realtime : Service { private let TYPE_ERROR = "error" private let TYPE_EVENT = "event" + private let TYPE_PONG = "pong" private let DEBOUNCE_NANOS = 1_000_000 + private let HEARTBEAT_INTERVAL: UInt64 = 20_000_000_000 // 20 seconds in nanoseconds private var socketClient: WebSocketClient? = nil private var activeChannels = Set() private var activeSubscriptions = [Int: RealtimeCallback]() + private var heartbeatTask: Task? = nil let connectSync = DispatchQueue(label: "ConnectSync") @@ -20,6 +23,29 @@ open class Realtime : Service { private var subscriptionsCounter = 0 private var reconnect = true + private func startHeartbeat() { + stopHeartbeat() + heartbeatTask = Task { + do { + while !Task.isCancelled { + if let client = socketClient, client.isConnected { + client.send(text: #"{"type": "ping"}"#) + } + try await Task.sleep(nanoseconds: HEARTBEAT_INTERVAL) + } + } catch { + if !Task.isCancelled { + print("Heartbeat task failed: \(error.localizedDescription)") + } + } + } + } + + private func stopHeartbeat() { + heartbeatTask?.cancel() + heartbeatTask = nil + } + private func createSocket() async throws { guard activeChannels.count > 0 else { reconnect = false @@ -50,6 +76,8 @@ open class Realtime : Service { } private func closeSocket() async throws { + stopHeartbeat() + guard let client = socketClient, let group = client.threadGroup else { return @@ -163,6 +191,7 @@ extension Realtime: WebSocketClientDelegate { public func onOpen(channel: Channel) { self.reconnectAttempts = 0 + startHeartbeat() } public func onMessage(text: String) { @@ -172,6 +201,7 @@ extension Realtime: WebSocketClientDelegate { switch type { case TYPE_ERROR: try! handleResponseError(from: json) case TYPE_EVENT: handleResponseEvent(from: json) + case TYPE_PONG: break // Handle pong response if needed default: break } } @@ -179,6 +209,8 @@ extension Realtime: WebSocketClientDelegate { } public func onClose(channel: Channel, data: Data) async throws { + stopHeartbeat() + if (!reconnect) { reconnect = true return @@ -196,6 +228,7 @@ extension Realtime: WebSocketClientDelegate { } public func onError(error: Swift.Error?, status: HTTPResponseStatus?) { + stopHeartbeat() print(error?.localizedDescription ?? "Unknown error") } diff --git a/Sources/Appwrite/Services/Storage.swift b/Sources/Appwrite/Services/Storage.swift index 9823478..382ab83 100644 --- a/Sources/Appwrite/Services/Storage.swift +++ b/Sources/Appwrite/Services/Storage.swift @@ -200,7 +200,7 @@ open class Storage: Service { } /// - /// Delete File + /// Delete file /// /// Delete a file by its unique ID. Only users with write permissions have /// access to delete this resource. diff --git a/Sources/Appwrite/Services/Teams.swift b/Sources/Appwrite/Services/Teams.swift index b926efd..a4d3123 100644 --- a/Sources/Appwrite/Services/Teams.swift +++ b/Sources/Appwrite/Services/Teams.swift @@ -286,7 +286,8 @@ open class Teams: Service { /// List team memberships /// /// Use this endpoint to list a team's members using the team's ID. All team - /// members have read access to this endpoint. + /// members have read access to this endpoint. Hide sensitive attributes from + /// the response by toggling membership privacy in the Console. /// /// @param String teamId /// @param [String] queries @@ -401,7 +402,8 @@ open class Teams: Service { /// Get team membership /// /// Get a team member by the membership unique id. All team members have read - /// access for this resource. + /// access for this resource. Hide sensitive attributes from the response by + /// toggling membership privacy in the Console. /// /// @param String teamId /// @param String membershipId diff --git a/Sources/AppwriteEnums/AuthenticationFactor.swift b/Sources/AppwriteEnums/AuthenticationFactor.swift index c8f0559..1768544 100644 --- a/Sources/AppwriteEnums/AuthenticationFactor.swift +++ b/Sources/AppwriteEnums/AuthenticationFactor.swift @@ -1,13 +1,12 @@ import Foundation -public enum AuthenticationFactor: String, Codable { +public enum AuthenticationFactor: String, CustomStringConvertible { case email = "email" case phone = "phone" case totp = "totp" case recoverycode = "recoverycode" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/AuthenticatorType.swift b/Sources/AppwriteEnums/AuthenticatorType.swift index fd516fd..066e377 100644 --- a/Sources/AppwriteEnums/AuthenticatorType.swift +++ b/Sources/AppwriteEnums/AuthenticatorType.swift @@ -1,10 +1,9 @@ import Foundation -public enum AuthenticatorType: String, Codable { +public enum AuthenticatorType: String, CustomStringConvertible { case totp = "totp" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/Browser.swift b/Sources/AppwriteEnums/Browser.swift index dbc88fc..f1fd1d2 100644 --- a/Sources/AppwriteEnums/Browser.swift +++ b/Sources/AppwriteEnums/Browser.swift @@ -1,6 +1,6 @@ import Foundation -public enum Browser: String, Codable { +public enum Browser: String, CustomStringConvertible { case avantBrowser = "aa" case androidWebViewBeta = "an" case googleChrome = "ch" @@ -16,8 +16,7 @@ public enum Browser: String, Codable { case opera = "op" case operaNext = "on" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/CreditCard.swift b/Sources/AppwriteEnums/CreditCard.swift index 3720b54..0922fbc 100644 --- a/Sources/AppwriteEnums/CreditCard.swift +++ b/Sources/AppwriteEnums/CreditCard.swift @@ -1,6 +1,6 @@ import Foundation -public enum CreditCard: String, Codable { +public enum CreditCard: String, CustomStringConvertible { case americanExpress = "amex" case argencard = "argencard" case cabal = "cabal" @@ -18,8 +18,7 @@ public enum CreditCard: String, Codable { case mIR = "mir" case maestro = "maestro" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/ExecutionMethod.swift b/Sources/AppwriteEnums/ExecutionMethod.swift index b03f856..85111a6 100644 --- a/Sources/AppwriteEnums/ExecutionMethod.swift +++ b/Sources/AppwriteEnums/ExecutionMethod.swift @@ -1,6 +1,6 @@ import Foundation -public enum ExecutionMethod: String, Codable { +public enum ExecutionMethod: String, CustomStringConvertible { case gET = "GET" case pOST = "POST" case pUT = "PUT" @@ -8,8 +8,7 @@ public enum ExecutionMethod: String, Codable { case dELETE = "DELETE" case oPTIONS = "OPTIONS" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/Flag.swift b/Sources/AppwriteEnums/Flag.swift index c577fcf..53ebf33 100644 --- a/Sources/AppwriteEnums/Flag.swift +++ b/Sources/AppwriteEnums/Flag.swift @@ -1,6 +1,6 @@ import Foundation -public enum Flag: String, Codable { +public enum Flag: String, CustomStringConvertible { case afghanistan = "af" case angola = "ao" case albania = "al" @@ -197,8 +197,7 @@ public enum Flag: String, Codable { case zambia = "zm" case zimbabwe = "zw" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/ImageFormat.swift b/Sources/AppwriteEnums/ImageFormat.swift index f4f49ee..c31fb47 100644 --- a/Sources/AppwriteEnums/ImageFormat.swift +++ b/Sources/AppwriteEnums/ImageFormat.swift @@ -1,14 +1,15 @@ import Foundation -public enum ImageFormat: String, Codable { +public enum ImageFormat: String, CustomStringConvertible { case jpg = "jpg" case jpeg = "jpeg" case gif = "gif" case png = "png" case webp = "webp" + case heic = "heic" + case avif = "avif" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/ImageGravity.swift b/Sources/AppwriteEnums/ImageGravity.swift index 1997ace..386f93d 100644 --- a/Sources/AppwriteEnums/ImageGravity.swift +++ b/Sources/AppwriteEnums/ImageGravity.swift @@ -1,6 +1,6 @@ import Foundation -public enum ImageGravity: String, Codable { +public enum ImageGravity: String, CustomStringConvertible { case center = "center" case topLeft = "top-left" case top = "top" @@ -11,8 +11,7 @@ public enum ImageGravity: String, Codable { case bottom = "bottom" case bottomRight = "bottom-right" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteEnums/OAuthProvider.swift b/Sources/AppwriteEnums/OAuthProvider.swift index 3414684..e00ba46 100644 --- a/Sources/AppwriteEnums/OAuthProvider.swift +++ b/Sources/AppwriteEnums/OAuthProvider.swift @@ -1,6 +1,6 @@ import Foundation -public enum OAuthProvider: String, Codable { +public enum OAuthProvider: String, CustomStringConvertible { case amazon = "amazon" case apple = "apple" case auth0 = "auth0" @@ -41,8 +41,7 @@ public enum OAuthProvider: String, Codable { case zoom = "zoom" case mock = "mock" - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(rawValue) + public var description: String { + return rawValue } } diff --git a/Sources/AppwriteModels/Document.swift b/Sources/AppwriteModels/Document.swift index 94069f9..c0c49c6 100644 --- a/Sources/AppwriteModels/Document.swift +++ b/Sources/AppwriteModels/Document.swift @@ -20,7 +20,7 @@ public class Document { public let updatedAt: String /// Document permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). - public let permissions: [Any] + public let permissions: [String] /// Additional properties public let data: T @@ -31,7 +31,7 @@ public class Document { databaseId: String, createdAt: String, updatedAt: String, - permissions: [Any], + permissions: [String], data: T ) { self.id = id @@ -62,7 +62,7 @@ public class Document { databaseId: map["$databaseId"] as! String, createdAt: map["$createdAt"] as! String, updatedAt: map["$updatedAt"] as! String, - permissions: map["$permissions"] as! [Any], + permissions: map["$permissions"] as! [String], data: try! JSONDecoder().decode(T.self, from: JSONSerialization.data(withJSONObject: map, options: [])) ) } diff --git a/Sources/AppwriteModels/Execution.swift b/Sources/AppwriteModels/Execution.swift index 0f04430..d589342 100644 --- a/Sources/AppwriteModels/Execution.swift +++ b/Sources/AppwriteModels/Execution.swift @@ -14,7 +14,7 @@ public class Execution { public let updatedAt: String /// Execution roles. - public let permissions: [Any] + public let permissions: [String] /// Function ID. public let functionId: String @@ -60,7 +60,7 @@ public class Execution { id: String, createdAt: String, updatedAt: String, - permissions: [Any], + permissions: [String], functionId: String, trigger: String, status: String, @@ -121,7 +121,7 @@ public class Execution { id: map["$id"] as! String, createdAt: map["$createdAt"] as! String, updatedAt: map["$updatedAt"] as! String, - permissions: map["$permissions"] as! [Any], + permissions: map["$permissions"] as! [String], functionId: map["functionId"] as! String, trigger: map["trigger"] as! String, status: map["status"] as! String, diff --git a/Sources/AppwriteModels/File.swift b/Sources/AppwriteModels/File.swift index 04a5b89..db896ab 100644 --- a/Sources/AppwriteModels/File.swift +++ b/Sources/AppwriteModels/File.swift @@ -17,7 +17,7 @@ public class File { public let updatedAt: String /// File permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). - public let permissions: [Any] + public let permissions: [String] /// File name. public let name: String @@ -43,7 +43,7 @@ public class File { bucketId: String, createdAt: String, updatedAt: String, - permissions: [Any], + permissions: [String], name: String, signature: String, mimeType: String, @@ -86,7 +86,7 @@ public class File { bucketId: map["bucketId"] as! String, createdAt: map["$createdAt"] as! String, updatedAt: map["$updatedAt"] as! String, - permissions: map["$permissions"] as! [Any], + permissions: map["$permissions"] as! [String], name: map["name"] as! String, signature: map["signature"] as! String, mimeType: map["mimeType"] as! String, diff --git a/Sources/AppwriteModels/Membership.swift b/Sources/AppwriteModels/Membership.swift index 55fb199..805cb47 100644 --- a/Sources/AppwriteModels/Membership.swift +++ b/Sources/AppwriteModels/Membership.swift @@ -16,10 +16,10 @@ public class Membership { /// User ID. public let userId: String - /// User name. + /// User name. Hide this attribute by toggling membership privacy in the Console. public let userName: String - /// User email address. + /// User email address. Hide this attribute by toggling membership privacy in the Console. public let userEmail: String /// Team ID. @@ -37,11 +37,11 @@ public class Membership { /// User confirmation status, true if the user has joined the team or false otherwise. public let confirm: Bool - /// Multi factor authentication status, true if the user has MFA enabled or false otherwise. + /// Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console. public let mfa: Bool /// User list of roles - public let roles: [Any] + public let roles: [String] init( @@ -57,7 +57,7 @@ public class Membership { joined: String, confirm: Bool, mfa: Bool, - roles: [Any] + roles: [String] ) { self.id = id self.createdAt = createdAt @@ -106,7 +106,7 @@ public class Membership { joined: map["joined"] as! String, confirm: map["confirm"] as! Bool, mfa: map["mfa"] as! Bool, - roles: map["roles"] as! [Any] + roles: map["roles"] as! [String] ) } } diff --git a/Sources/AppwriteModels/MfaRecoveryCodes.swift b/Sources/AppwriteModels/MfaRecoveryCodes.swift index 8dd063b..314e11c 100644 --- a/Sources/AppwriteModels/MfaRecoveryCodes.swift +++ b/Sources/AppwriteModels/MfaRecoveryCodes.swift @@ -5,11 +5,11 @@ import JSONCodable public class MfaRecoveryCodes { /// Recovery codes. - public let recoveryCodes: [Any] + public let recoveryCodes: [String] init( - recoveryCodes: [Any] + recoveryCodes: [String] ) { self.recoveryCodes = recoveryCodes } @@ -22,7 +22,7 @@ public class MfaRecoveryCodes { public static func from(map: [String: Any] ) -> MfaRecoveryCodes { return MfaRecoveryCodes( - recoveryCodes: map["recoveryCodes"] as! [Any] + recoveryCodes: map["recoveryCodes"] as! [String] ) } } diff --git a/Sources/AppwriteModels/Session.swift b/Sources/AppwriteModels/Session.swift index 7214468..86f4a7c 100644 --- a/Sources/AppwriteModels/Session.swift +++ b/Sources/AppwriteModels/Session.swift @@ -83,7 +83,7 @@ public class Session { public let current: Bool /// Returns a list of active session factors. - public let factors: [Any] + public let factors: [String] /// Secret used to authenticate the user. Only included if the request was made with an API key public let secret: String @@ -119,7 +119,7 @@ public class Session { countryCode: String, countryName: String, current: Bool, - factors: [Any], + factors: [String], secret: String, mfaUpdatedAt: String ) { @@ -216,7 +216,7 @@ public class Session { countryCode: map["countryCode"] as! String, countryName: map["countryName"] as! String, current: map["current"] as! Bool, - factors: map["factors"] as! [Any], + factors: map["factors"] as! [String], secret: map["secret"] as! String, mfaUpdatedAt: map["mfaUpdatedAt"] as! String ) diff --git a/Sources/AppwriteModels/Target.swift b/Sources/AppwriteModels/Target.swift index 087eca6..2e81790 100644 --- a/Sources/AppwriteModels/Target.swift +++ b/Sources/AppwriteModels/Target.swift @@ -28,6 +28,9 @@ public class Target { /// The target identifier. public let identifier: String + /// Is the target expired. + public let expired: Bool + init( id: String, @@ -37,7 +40,8 @@ public class Target { userId: String, providerId: String?, providerType: String, - identifier: String + identifier: String, + expired: Bool ) { self.id = id self.createdAt = createdAt @@ -47,6 +51,7 @@ public class Target { self.providerId = providerId self.providerType = providerType self.identifier = identifier + self.expired = expired } public func toMap() -> [String: Any] { @@ -58,7 +63,8 @@ public class Target { "userId": userId as Any, "providerId": providerId as Any, "providerType": providerType as Any, - "identifier": identifier as Any + "identifier": identifier as Any, + "expired": expired as Any ] } @@ -71,7 +77,8 @@ public class Target { userId: map["userId"] as! String, providerId: map["providerId"] as? String, providerType: map["providerType"] as! String, - identifier: map["identifier"] as! String + identifier: map["identifier"] as! String, + expired: map["expired"] as! Bool ) } } diff --git a/Sources/AppwriteModels/User.swift b/Sources/AppwriteModels/User.swift index d12c343..0229dc5 100644 --- a/Sources/AppwriteModels/User.swift +++ b/Sources/AppwriteModels/User.swift @@ -32,7 +32,7 @@ public class User { public let status: Bool /// Labels for the user. - public let labels: [Any] + public let labels: [String] /// Password update time in ISO 8601 format. public let passwordUpdate: String @@ -72,7 +72,7 @@ public class User { hashOptions: Any?, registration: String, status: Bool, - labels: [Any], + labels: [String], passwordUpdate: String, email: String, phone: String, @@ -139,7 +139,7 @@ public class User { hashOptions: map["hashOptions"] as? Any, registration: map["registration"] as! String, status: map["status"] as! Bool, - labels: map["labels"] as! [Any], + labels: map["labels"] as! [String], passwordUpdate: map["passwordUpdate"] as! String, email: map["email"] as! String, phone: map["phone"] as! String, diff --git a/docs/examples/account/update-mfa-challenge.md b/docs/examples/account/update-mfa-challenge.md index a237537..1c5874f 100644 --- a/docs/examples/account/update-mfa-challenge.md +++ b/docs/examples/account/update-mfa-challenge.md @@ -6,7 +6,7 @@ let client = Client() let account = Account(client) -let result = try await account.updateMfaChallenge( +let session = try await account.updateMfaChallenge( challengeId: "", otp: "" ) diff --git a/example-uikit/UIKitExample/ViewController.swift b/example-uikit/UIKitExample/ViewController.swift index b1d2f14..656825f 100644 --- a/example-uikit/UIKitExample/ViewController.swift +++ b/example-uikit/UIKitExample/ViewController.swift @@ -2,7 +2,7 @@ import UIKit import NIO import Appwrite -let host = "https://demo.appwrite.io/v1" +let host = "https://cloud.appwrite.io/v1" class ViewController: UIViewController { @@ -72,8 +72,8 @@ class ViewController: UIViewController { do { let response = try await account.createOAuth2Session( provider: "facebook", - success: "https://demo.appwrite.io/auth/oauth2/success", - failure: "https://demo.appwrite.io/auth/oauth2/failure" + success: "https://cloud.appwrite.io/auth/oauth2/success", + failure: "https://cloud.appwrite.io/auth/oauth2/failure" ) self.response = String(describing: response) } catch {