openref: 0.1 info: id: reference/supabase-swift title: Supabase Swift Client description: | Supabase Swift. specUrl: https://github.com/supabase/supabase/edit/master/apps/docs/spec/supabase_swift_v2.yml slugPrefix: '/' libraries: - id: 'Swift' version: '2.0.0' functions: - id: initializing title: 'Initializing' description: | You can initialize Supabase with the `SupabaseClient` by passing your `Project URL` and `Project Key`. You can find these under your `Project Settings` → `API Settings` The Supabase client is your entrypoint to the rest of the Supabase functionality and is the easiest way to interact with everything we offer within the Supabase ecosystem. examples: - id: initialize-client name: Initialize Client code: | ```swift import Supabase let client = SupabaseClient(supabaseURL: URL(string: "https://xyzcompany.supabase.co")!, supabaseKey: "your-publishable-key") ``` - id: initialize-client-custom-options name: Initialize Client with custom options code: | ```swift import Supabase let supabase = SupabaseClient( supabaseURL: URL(string: "https://xyzcompany.supabase.co")!, supabaseKey: "your-publishable-key", options: SupabaseClientOptions( db: .init( schema: "public" ), auth: .init( storage: MyCustomLocalStorage(), flowType: .pkce ), global: .init( headers: ["x-my-custom-header": "my-app-name"], session: URLSession.myCustomSession ) ) ) ``` - id: initialize-client-with-retries name: Initialize Client with automatic retries description: | PostgREST requests automatically retry on transient errors (HTTP 408, 409, 503, 504 and network failures) with exponential backoff. Retries are enabled by default and can be disabled per client. code: | ```swift import Supabase let supabase = SupabaseClient( supabaseURL: URL(string: "https://xyzcompany.supabase.co")!, supabaseKey: "your-publishable-key", options: SupabaseClientOptions( db: .init( // Disable automatic retries for this client retryEnabled: false ) ) ) ``` - id: initialize-client-with-logging name: Initialize Client with Logging code: | ```swift import Supabase struct AppLogger: SupabaseLogger { func log(message: SupabaseLogMessage) { print(message.description) } } let supabase = SupabaseClient( supabaseURL: URL(string: "https://xyzcompany.supabase.co")!, supabaseKey: "your-publishable-key", options: SupabaseClientOptions( global: SupabaseClientOptions.GlobalOptions( logger: AppLogger() ) ) ) ``` - id: api-schemas name: With custom schemas code: | ```swift import Supabase let supabase = SupabaseClient( supabaseURL: URL(string: "https://xyzcompany.supabase.co")!, supabaseKey: "your-publishable-key", options: SupabaseClientOptions( db: .init( // Provide a custom schema. Defaults to "public". schema: "other_schema" ) ) ) ``` description: | By default the API server points to the `public` schema. You can enable other database schemas within the Dashboard. Go to [Settings > API > Exposed schemas](/dashboard/project/_/settings/api) and add the schema which you want to expose to the API. Note: each client connection can only access a single schema, so the code above can access the `other_schema` schema but cannot access the `public` schema. - id: initialize-client-with-opentelemetry name: Initialize Client with OpenTelemetry tracing description: | Supabase Swift supports W3C `traceparent` header propagation via an opt-in SwiftPM package trait. When enabled, the active OpenTelemetry span's trace context is automatically injected into every outgoing request across PostgREST, Storage, Auth, Functions, and Realtime — no additional runtime configuration needed. Enable the `OpenTelemetry` trait in your `Package.swift` dependency declaration: code: | ```swift // Package.swift .package( url: "https://github.com/supabase/supabase-swift.git", from: "2.51.0", traits: ["OpenTelemetry"] ) ``` notes: | - Requires swift-tools-version 6.1 or later (trait support). - The trait is **off by default** — no OTel dependency is linked unless you opt in. - When no span is active, the header is not added; it is always safe to call unconditionally. - id: auth-api title: 'Overview' notes: | The auth methods can be accessed via the `supabase.auth` namespace. ### Handling deep links #### UIKit app lifecycle ```swift public func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { if let url = launchOptions?[.url] as? URL { supabase.auth.handle(url) } return true } func application( _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] ) -> Bool { supabase.auth.handle(url) return true } #### UIKit app lifecycle with scenes In your `SceneDelegate.swift`: ```swift func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let url = URLContexts.first?.url else { return } supabase.auth.handle(url) } ``` #### SwiftUI app lifecycle In your `AppDelegate.swift`: ```swift SomeView() .onOpenURL { url in supabase.auth.handle(url) } ``` examples: - id: create-auth-client name: Create auth client isSpotlight: true code: | ```swift let supabase = SupabaseClient(supabaseURL: URL(string: "https://xyzcompany.supabase.co")!, supabaseKey: "your-publishable-key") let auth = supabase.auth ``` - id: create-auth-client-with-custom-storage name: Create auth client with custom storage isSpotlight: true code: | ```swift let supabase = SupabaseClient( supabaseURL: URL(string: "https://xyzcompany.supabase.co")!, supabaseKey: "your-publishable-key", options: .init( auth: .init( MyCustomLocalStorage() ) ) ) let auth = supabase.auth ``` - id: sign-up title: 'signUp()' notes: | - By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](/dashboard/project/_/auth/providers). - **Confirm email** determines if users need to confirm their email address after signing up. - If **Confirm email** is enabled, a `user` is returned but `session` is null. - If **Confirm email** is disabled, both a `user` and a `session` are returned. - When the user confirms their email address, they are redirected to the [`SITE_URL`](/docs/guides/auth/redirect-urls) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](/dashboard/project/_/auth/url-configuration). - If signUp() is called for an existing confirmed user: - When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned. - When either **Confirm email** or **Confirm phone** (even when phone provider is disabled) is disabled, the error message, `User already registered` is returned. - To fetch the currently signed-in user, refer to [`getUser()`](/docs/reference/swift/get-user). overwriteParams: - name: email isOptional: true type: String description: One of `email` or `phone` must be provided. - name: phone isOptional: true type: String description: One of `email` or `phone` must be provided. - name: password type: String - name: data isOptional: true type: JSONObject description: > A custom data object to store additional user metadata. - name: redirectTo isOptional: true type: URL description: > Only for email signups. The redirect URL embedded in the email link. Must be a configured redirect URL for your Supabase instance. - name: captchaToken isOptional: true type: String examples: - id: sign-up name: Sign up with email and password isSpotlight: true code: | ```swift try await supabase.auth.signUp( email: "example@email.com", password: "example-password" ) ``` - id: sign-up-phone name: Sign up with a phone number and password (SMS) isSpotlight: true code: | ```swift try await supabase.auth.signUp( phone: "123456789", password: "example-password", channel: "sms" ) ``` - id: sign-up-phone-whatsapp name: Sign up with a phone number and password (whatsapp) isSpotlight: true description: | The user will be sent a WhatsApp message which contains a OTP. By default, a given user can only request a OTP once every 60 seconds. Note that a user will need to have a valid WhatsApp account that is linked to Twilio in order to use this feature. code: | ```swift try await supabase.auth.signUp( phone: "123456789", password: "example-password", channel: "whatsapp" ) ``` - id: sign-up-with-additional-user-metadata name: Sign up with additional user metadata isSpotlight: false description: | The custom data is defined as `[String: AnyJSON]`, where `AnyJSON` is a helper type defined in the library. code: | ```swift try await supabase.auth.signUp( email: "example@email.com", password: "example-password", data: [ "first_name": .string("John"), "age": .number(24) ] ) ``` - id: sign-up-with-redirect name: Sign up with a redirect URL description: | - You can provide a default redirect URL when initializing the client. - See [redirect URLs and wildcards](/docs/guides/auth/redirect-urls#use-wildcards-in-redirect-urls) to add additional redirect URLs to your project. code: | ```swift try await supabase.auth.signUp( email: "example@email.com", password: "example-password", redirectTo: URL(string: "https://example.com/welcome")! ) ``` - id: on-auth-state-change title: 'onAuthStateChange()' notes: | - Subscribes to important events occurring on the user's session. - Emitted events: - `INITIAL_SESSION` - Emitted right after the Supabase client is constructed and the initial session from storage is loaded. - `SIGNED_IN` - Emitted each time a user session is confirmed or re-established, including on user sign in. - Avoid making assumptions as to when this event is fired, this may occur even when the user is already signed in. Instead, check the user object attached to the event to see if a new user has signed in and update your application's UI. - `SIGNED_OUT` - Emitted when the user signs out. This can be after: - A call to `supabase.auth.signOut()`. - After the user's session has expired for any reason: - User has signed out on another device. - The session has reached its timebox limit or inactivity timeout. - User has signed in on another device with single session per user enabled. - Check the [User Sessions](/docs/guides/auth/sessions) docs for more information. - Use this to clean up any local storage your application has associated with the user. - `TOKEN_REFRESHED` - Emitted each time a new access and refresh token are fetched for the signed-in user. - It's best practice and highly recommended to extract the access token (JWT) and store it in memory for further use in your application. - Avoid frequent calls to `supabase.auth.session` for the same purpose. - There is a background process that keeps track of when the session should be refreshed so you will always receive valid tokens by listening to this event. - The frequency of this event is related to the JWT expiry limit configured on your project. - `USER_UPDATED` - Emitted each time the `supabase.auth.update(user:)` method finishes successfully. Listen to it to update your application's UI based on new profile information. - `PASSWORD_RECOVERY` - Emitted instead of the `SIGNED_IN` event when the user lands on a page that includes a password recovery link in the URL. - Use it to show a UI to the user where they can [reset their password](/docs/guides/auth/passwords#resetting-a-users-password-forgot-password). examples: - id: listen-to-auth-changes name: Listen to auth changes isSpotlight: true code: | ```swift // Using AsyncStream for await (event, session) in await supabase.auth.authStateChanges { print(event, session) } // Using Closure let subscription = await supabase.auth.onAuthStateChange { event, session in print(event, session) } // call remove() to remove subscription subscription.remove() ``` description: | - When using closure based, remember to call `remove()` on the returned subscription. - When using AsyncStream, it automatically removes the subscription when AsyncStream is canceled, or finishes. - id: list-to-a-specific-event name: Listen to a specific event code: | ```swift for await (_, session) in await supabase.auth.authStateChanges .filter({ $0.event == .signedIn }) { // handle signIn event. } ``` - id: sign-in-anonymously title: 'signInAnonymously()' notes: | - Returns an anonymous user - It is recommended to set up captcha for anonymous sign-ins to prevent abuse. You can pass in the captcha token in the `options` param. examples: - id: sign-in-anonymously name: Create an anonymous user isSpotlight: true code: | ```swift let session = try await supabase.auth.signInAnonymously(captchaToken: captchaToken) ``` - id: sign-in-anonymously-with-user-metadata name: Create an anonymous user with custom user metadata isSpotlight: false code: | ```swift let session = try await supabase.auth.signInAnonymously( data: customData, captchaToken: captchaToken ) ``` - id: sign-in-with-password title: 'signInWithPassword()' notes: | - Requires either an email and password or a phone number and password. overwriteParams: - name: email isOptional: true type: String description: One of `email` or `phone` must be provided. - name: phone isOptional: true type: String description: One of `email` or `phone` must be provided. - name: password type: String - name: captchaToken isOptional: true type: String examples: - id: sign-in-with-email-and-password name: Sign in with email and password isSpotlight: true code: | ```swift try await supabase.auth.signIn( email: "example@email.com", password: "example-password" ) ``` - id: sign-in-with-phone-and-password name: Sign in with phone and password isSpotlight: false code: | ```swift try await supabase.auth.signIn( phone: "+13334445555", password: "same-password" ) // After receiving a SMS with a OTP. try await supabase.auth.verifyOTP( phone: "+13334445555", token: "123456", type: .sms ) ``` - id: sign-in-with-otp title: 'signInWithOTP()' overwriteParams: - name: email isOptional: true type: String description: One of `email` or `phone` must be provided. - name: phone isOptional: true type: String description: One of `email` or `phone` must be provided. - name: redirectTo isOptional: true type: String description: > Only for email signups. The redirect URL embedded in the email link. Must be a configured redirect URL for your Supabase instance. - name: channel isOptional: true type: MessagingChannel description: > The channel to use for sending messages. Only for phone signups. - name: shouldCreateUser isOptional: true type: Bool description: > Whether to create the user if they don't already exist. Defaults to true. - name: data isOptional: true type: JSONObject description: > A custom data object to store additional user metadata. - name: captchaToken isOptional: true type: String notes: | - Requires either an email or phone number. - This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number. - If the user doesn't exist, `signInWithOTP()` will signup the user instead. To restrict this behavior, you can set `shouldCreateUser` to `false``. - If you're using an email, you can configure whether you want the user to receive a magiclink or a OTP. - If you're using phone, you can configure whether you want the user to receive a OTP. - The magic link's destination URL is determined by the [`SITE_URL`](/docs/guides/auth/redirect-urls). - See [redirect URLs and wildcards](/docs/guides/auth/redirect-urls#use-wildcards-in-redirect-urls) to add additional redirect URLs to your project. - Magic links and OTPs share the same implementation. To send users a one-time code instead of a magic link, [modify the magic link email template](/dashboard/project/_/auth/templates) to include `{{ .Token }}` instead of `{{ .ConfirmationURL }}`. - See our [Twilio Phone Auth Guide](/docs/guides/auth/phone-login?showSmsProvider=Twilio) for details about configuring WhatsApp sign in. examples: - id: sign-in-with-email name: Sign in with email isSpotlight: true description: The user will be sent an email which contains either a magiclink or a OTP or both. By default, a given user can only request a OTP once every 60 seconds. code: | ```swift try await supabase.auth.signInWithOTP( email: "example@email.com", redirectTo: URL(string: "my-app-scheme://")! ) ``` - id: sign-in-with-sms-otp name: Sign in with SMS OTP isSpotlight: false description: The user will be sent a SMS which contains a OTP. By default, a given user can only request a OTP once every 60 seconds. code: | ```swift try await supabase.auth.signInWithOTP(phone: "+13334445555") ``` - id: sign-in-with-whatsapp-otp name: Sign in with WhatsApp OTP isSpotlight: false description: The user will be sent a WhatsApp message which contains a OTP. By default, a given user can only request a OTP once every 60 seconds. Note that a user will need to have a valid WhatsApp account that is linked to Twilio in order to use this feature. code: | ```swift try await supabase.auth.signInWithOTP( phone: "+13334445555", channel: "whatsapp" ) ``` - id: sign-in-with-oauth title: 'signInWithOAuth()' overwriteParams: - name: provider type: Provider description: > The third-party provider. - name: redirectTo type: URL isOptional: true description: > A URL to send the user to after they are confirmed. - name: scopes type: String isOptional: true description: > A space-separated list of scopes granted to the OAuth application. - name: queryParams type: '[(name: String, value: String?)]' isOptional: true description: > Additional query params. - name: configure type: Callback isOptional: true description: > A custom configuration callback for opening the OAuth flow externally. notes: | - This method is used for signing in using a third-party provider. - Supabase supports many different [third-party providers](https://supabase.com/docs/guides/auth#providers). examples: - id: sign-in-using-ASWebAuthenticationSession name: Sign in with OAuth using ASWebAuthenticationSession isSpotlight: true description: | - Use `configure` parameter to customize the `ASWebAuthenticationSession` code: | ```swift let session = try await supabase.auth.signInWithOAuth( provider: .github ) { (session: ASWebAuthenticationSession) in // customize session } ``` - id: sign-in-using-generic-flow name: Sign in with OAuth and customize flow isSpotlight: true description: | - Use `launchFlow` parameter to customize the flow. code: | ```swift let session = try await supabase.auth.signInWithOAuth( provider: .github ) { url in // use url to start OAuth flow // and return a result url that contains the OAuth token. // ... return resultURL } ``` - id: sign-in-using-manual-implementation name: Sign in using a third-party provider isSpotlight: true description: | - getOAuthSignInURL() provides the URL which needs to be opened preferably in a [ASWebAuthenticationSession](ASWebAuthenticationSession) instance.. - The redirectTo URL, or `callbackURLScheme` needs to be setup correctly in your project under Authentication -> URL Configuration -> Redirect URLs. - When using `ASWebAuthenticationSession` or any other implementation, use the returning URL as input to `session(from:)` method. code: | ```swift let url = try await supabase.auth.getOAuthSignInURL(provider: .github, redirectTo: URL(string: "my-app-scheme://")) let session = ASWebAuthenticationSession(url: url, callbackURLScheme: "my-app-scheme") { url, error in guard let url else { return } Task { try await supabase.auth.session(from: url) } } session.presentationContextProvider = self // yours ASWebAuthenticationPresentationContextProviding implementation. session.start() ``` - id: sign-in-with-scopes name: Sign in with scopes isSpotlight: false description: | If you need additional data from an OAuth provider, you can include a space-separated list of scopes in your request to get back an OAuth provider token. You may also need to specify the scopes in the provider's OAuth app settings, depending on the provider. The list of scopes will be documented by the third-party provider you are using and specifying scopes will enable you to use the OAuth provider token to call additional APIs supported by the third-party provider to get more information. code: | ```swift let url = try await supabase.auth.signInWithOAuth( provider: .github, scopes: "repo gist notifications" ) ``` - id: sign-in-with-id-token title: 'signInWithIdToken()' examples: - id: sign-in-with-id-token name: 'Sign In using ID Token' description: Use this method to implement native Sign In With Apple. code: | ```swift let session = try await supabase.auth.signInWithIdToken( credentials: OpenIDConnectCredentials( provider: .apple, idToken: "your-id-token", nonce: "your nonce" ) ) ``` - id: sign-in-with-sso title: 'signInWithSSO()' overwriteParams: - name: providerId isOptional: true type: String description: > UUID of the SSO provider. One of `providerId` or `domain` is required. - name: domain isOptional: true type: String description: > Domain name of the organization to use SSO with. One of `providerId` or `domain` is required. - name: redirectTo type: String description: > The URL to redirect the user to after they have signed in. Must be a configured redirect URL for your Supabase instance. - name: captchaToken type: String notes: | - Before you can call this method you need to [establish a connection](/docs/guides/auth/enterprise-sso/auth-sso-saml#managing-saml-20-connections) to an identity provider. Use the [CLI commands](/docs/reference/cli/supabase-sso) to do this. - If you've associated an email domain to the identity provider, you can use the `domain` property to start a sign-in flow. - In case you need to use a different way to start the authentication flow with an identity provider, you can use the `providerId` property. For example: - Mapping specific user email addresses with an identity provider. - Using different hints to identity the identity provider to be used by the user, like a company-specific page, IP address or other tracking information. examples: - id: sign-in-with-domain name: Sign in with email domain isSpotlight: true code: | ```swift // You can extract the user's email domain and use it to trigger the // authentication flow with the correct identity provider. let url = try await await supabase.auth.signInWithSSO{ domain: "company.com" } // Open the URL using your preferred method to complete sign-in process. UIApplication.shared.open(url) ``` - id: sign-in-with-provider-uuid name: Sign in with provider UUID isSpotlight: true code: | ```swift // Useful when you need to map a user's sign in request according // to different rules that can't use email domains. let url = try await supabase.auth.signInWithSSO{ providerId: "21648a9d-8d5a-4555-a9d1-d6375dc14e92" } // Open the URL using your preferred method to complete sign-in process. UIApplication.shared.open(url) ``` - id: sign-in-with-web3 title: 'signInWithWeb3()' description: | Signs in a user via a signed Sign in with Ethereum (EIP-4361) or Sign in with Solana message. notes: | - Supports Ethereum (Sign-In with Ethereum) and Solana (Sign-In with Solana), both of which derive from the [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) standard. - Your app is responsible for building the message and obtaining the signature from the user's wallet (e.g. via a WalletConnect session or native wallet SDK) before calling this method. - For `Web3Chain.ethereum` the signature is a `0x`-prefixed hex encoded string. For `Web3Chain.solana` it is a base64 encoded string. examples: - id: sign-in-with-ethereum name: Sign in with an Ethereum wallet isSpotlight: true description: | Sign the EIP-4361 message with the user's Ethereum wallet, then pass the message and its hex encoded signature. code: | ```swift let session = try await supabase.auth.signInWithWeb3( credentials: Web3Credentials( chain: .ethereum, message: siweMessage, signature: signatureHex ) ) ``` - id: sign-in-with-solana name: Sign in with a Solana wallet description: | Sign the message with the user's Solana wallet, then pass the message and its base64 encoded signature. code: | ```swift let session = try await supabase.auth.signInWithWeb3( credentials: Web3Credentials( chain: .solana, message: siwsMessage, signature: signatureBase64 ) ) ``` - id: sign-in-with-passkey title: 'signInWithPasskey()' notes: | Signs the user in with a passkey (WebAuthn). Available on iOS 16+, macOS 13+, and visionOS 1+. - Drives the full WebAuthn ceremony end to end: fetches assertion options from the server, presents the native passkey UI via `AuthenticationServices`, and verifies the assertion. - Does not require an existing session. On success the session is persisted and a `signedIn` auth change event is emitted. - For lower-level control (custom authenticator, tvOS, watchOS), use `getPasskeyAuthenticationOptions()` + `verifyPasskeyAuthentication()` instead. - Passkey support is **experimental**. Opt in with `@_spi(Experimental) import Supabase`. The API may change in future releases. - Passkeys must be enabled for your project in the Dashboard under Authentication → Passkeys. examples: - id: sign-in-with-passkey name: Sign in with a passkey isSpotlight: true code: | ```swift // iOS 16+/macOS 13+ only. Must opt in: @_spi(Experimental) import Supabase let response = try await supabase.auth.signInWithPasskey( presentationAnchor: view.window! ) let session = response.session let user = response.user ``` - id: register-passkey title: 'registerPasskey()' notes: | Registers a new passkey (WebAuthn credential) for the signed-in user. Available on iOS 16+, macOS 13+, and visionOS 1+. - Drives the full WebAuthn ceremony end to end: fetches creation options from the server, presents the native passkey registration UI, and stores the credential. - Requires an authenticated, non-anonymous user. - For lower-level control, use `getPasskeyRegistrationOptions()` + `verifyPasskeyRegistration()` instead. - Passkey support is **experimental**. Opt in with `@_spi(Experimental) import Supabase`. examples: - id: register-passkey name: Register a passkey for the current user isSpotlight: true code: | ```swift // iOS 16+/macOS 13+ only. Must opt in: @_spi(Experimental) import Supabase let passkey = try await supabase.auth.registerPasskey( presentationAnchor: view.window! ) print("Registered passkey \(passkey.id)") ``` - id: sign-out title: 'signOut()' notes: | - In order to use the `signOut()` method, the user needs to be signed in first. examples: - id: sign-out name: Sign out isSpotlight: true code: | ```swift try await supabase.auth.signOut() ``` - id: verify-otp title: 'verifyOTP()' overwriteParams: - name: email isOptional: true type: String description: One of `phone`, `email`, or `token_hash` must be provided. - name: phone isOptional: true type: String description: One of `phone`, `email`, or `token_hash` must be provided. - name: token_hash isOptional: true type: String description: > The token hash from the user's email link. One of `phone`, `email`, or `token_hash` must be provided. - name: type type: EmailOTPType | MobileOTPType - name: token isOptional: true type: String description: The OTP sent to the user. Required if using `phone` or `email`. - name: redirectTo isOptional: true type: URL description: > A URL to redirect the user to after they are confirmed. Must be in your configured redirect URLs. - name: captchaToken isOptional: true type: String description: Deprecated. notes: | - The `verifyOTP` method takes in different verification types. If a phone number is used, the type can either be `sms` or `phone_change`. If an email address is used, the type can be one of the following: `signup`, `magiclink`, `recovery`, `invite`, `email_change`, or `email`. - The verification type used should be determined based on the corresponding auth method called before `verifyOTP` to sign up / sign-in a user. examples: - id: verify-sms-one-time-password(otp) name: Verify Sms One-Time Password (OTP) isSpotlight: true code: | ```swift try await supabase.auth.verifyOTP( phone: "+13334445555", token: "123456", type: .sms ) ``` - id: verify-signup-one-time-password(otp) name: Verify Signup One-Time Password (OTP) isSpotlight: false code: | ```swift try await supabase.auth.verifyOTP( email: "example@example-email.com", token: "123456", type: .signup ) ``` - id: get-session title: 'session' description: | - Returns the session, refreshing it if necessary. If no session can be found, a `GoTrueError.sessionNotFound` error is thrown. examples: - id: get-the-session-data name: Get the session data isSpotlight: true code: | ```swift try await supabase.auth.session ``` - id: get-current-session-no-validation name: Get the current session without validation isSpotlight: true description: The session returned by this property may be expired. code: | ```swift let session = supabase.auth.currentSession ``` - id: get-user title: 'user()' description: | - This method is useful for checking if the user is authorized because it validates the user's access token JWT on the server. - Fetches the user object from the database instead of local session. - Should be used only when you require the most current user data. For faster results, `session.user` is recommended. examples: - id: get-the-logged-in-user-with-the-current-existing-session name: Get the logged in user with the current existing session isSpotlight: true code: | ```swift let user = try await supabase.auth.user() ``` - id: get-the-logged-in-user-with-a-custom-access-token-jwt name: Get the logged in user with a custom access token jwt isSpotlight: false code: | ```swift let user = try await supabase.auth.user(jwt: "custom-jwt") ``` - id: get-current-user name: Get current user isSpotlight: true description: The user returned by this property may be outdated. code: | ```swift let user = supabase.auth.currentUser ``` - id: update-user title: 'updateUser()' notes: | - In order to use the `updateUser()` method, the user needs to be signed in first. - By default, email updates sends a confirmation link to both the user's current and new email. To only send a confirmation link to the user's new email, disable **Secure email change** in your project's [email auth provider settings](https://supabase.com/dashboard/project/_/auth/providers). examples: - id: update-the-email-for-an-authenticated-user name: Update the email for an authenticated user description: Sends a "Confirm Email Change" email to the new email address. isSpotlight: false code: | ```swift try await supabase.auth.update(user: UserAttributes(email: "new@email.com")) ``` response: | ```json { "user": { "id": "11111111-1111-1111-1111-111111111111", "aud": "authenticated", "role": "authenticated", "email": "example@email.com", "email_confirmed_at": "2024-01-01T00:00:00Z", "phone": "", "confirmed_at": "2024-01-01T00:00:00Z", "new_email": "new@email.com", "email_change_sent_at": "2024-01-01T00:00:00Z", "last_sign_in_at": "2024-01-01T00:00:00Z", "app_metadata": { "provider": "email", "providers": [ "email" ] }, "user_metadata": { "email": "example@email.com", "email_verified": false, "phone_verified": false, "sub": "11111111-1111-1111-1111-111111111111" }, "identities": [ { "identity_id": "22222222-2222-2222-2222-222222222222", "id": "11111111-1111-1111-1111-111111111111", "user_id": "11111111-1111-1111-1111-111111111111", "identity_data": { "email": "example@email.com", "email_verified": false, "phone_verified": false, "sub": "11111111-1111-1111-1111-111111111111" }, "provider": "email", "last_sign_in_at": "2024-01-01T00:00:00Z", "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "email": "example@email.com" } ], "created_at": "2024-01-01T00:00:00Z", "updated_at": "2024-01-01T00:00:00Z", "is_anonymous": false } } ``` - id: update-the-phone-for-an-authenticated-user name: Update the phone number for an authenticated user description: Sends a one-time password (OTP) to the new phone number. isSpotlight: false code: | ```swift try await supabase.auth.update( user: UserAttributes( phone: "123456789" ) ) ``` - id: update-the-password-for-an-authenticated-user name: Update the password for an authenticated user isSpotlight: false code: | ```swift try await supabase.auth.update(user: UserAttributes(password: "newPassw0rd?")) ``` - id: update-the-users-metadata name: Update the user's metadata isSpotlight: true code: | ```swift try await supabase.auth.update( user: UserAttributes( data: [ "hello": .string("world") ] ) ) ``` - id: update-password-with-reauthentication name: Update the user's password with a nonce description: | If **Secure password change** is enabled in your [project's email provider settings](/dashboard/project/_/auth/providers), updating the user's password would require a nonce if the user **hasn't recently signed in**. The nonce is sent to the user's email or phone number. A user is deemed recently signed in if the session was created in the last 24 hours. isSpotlight: true code: | ```swift try await supabase.auth.update( user: UserAttributes( password: "new password", nonce: "123456" ) ) ``` - id: get-user-identities title: 'userIdentities()' notes: | - The user needs to be signed in to call `userIdentities()`. examples: - id: get-user-identities name: Returns a list of identities linked to the user isSpotlight: true code: | ```swift let identities = try await supabase.auth.userIdentities() ``` - id: link-identity title: 'linkIdentity()' notes: | - The **Enable Manual Linking** option must be enabled from your [project's authentication settings](/dashboard/project/_/auth/providers). - The user needs to be signed in to call `linkIdentity()`. - If the candidate identity is already linked to the existing user or another user, `linkIdentity()` will fail. examples: - id: link-identity name: Link an identity to a user isSpotlight: true code: | ```swift try await supabase.auth.linkIdentity(provider: provider) ``` description: | Uses: - `NSWorkspace.shared.open` in macOS - `UIApplication.shared.open` in iOS, tvOS, visionOS, and macOS Catalyst - `WKExtension.shared().openSystemURL` in watchOS - id: link-identity-custom-urlopener name: Link an identity to a user with custom URL opening logic isSpotlight: true code: | ```swift try await supabase.auth.linkIdentity(provider: provider) { url in // custom URL opening logic } ``` - id: link-identity-with-id-token title: 'linkIdentityWithIdToken()' notes: | - The **Enable Manual Linking** option must be enabled from your [project's authentication settings](/dashboard/project/_/auth/providers). - The user needs to be signed in to call `linkIdentityWithIdToken()`. - If the candidate identity is already linked to the existing user or another user, `linkIdentityWithIdToken()` will fail. - This method allows you to link an OIDC identity using an ID token obtained from a provider like Apple, Google, etc. examples: - id: link-identity-with-id-token name: Link an OIDC identity using an ID token isSpotlight: true code: | ```swift try await supabase.auth.linkIdentityWithIdToken( credentials: OpenIDConnectCredentials( provider: .apple, idToken: idToken ) ) ``` description: | Link an OpenID Connect identity to the current user using an ID token. This is useful when you've obtained an ID token from a provider's SDK (like Sign in with Apple) and want to link it to the current user's account. - id: link-identity-with-id-token-and-nonce name: Link an OIDC identity with nonce code: | ```swift try await supabase.auth.linkIdentityWithIdToken( credentials: OpenIDConnectCredentials( provider: .apple, idToken: idToken, nonce: nonce ) ) ``` description: | For providers that support nonce verification (like Apple), you can include the nonce used during authentication. - id: unlink-identity title: 'unlinkIdentity()' notes: | - The **Enable Manual Linking** option must be enabled from your [project's authentication settings](/dashboard/project/_/auth/providers). - The user needs to be signed in to call `unlinkIdentity()`. - The user must have at least 2 identities in order to unlink an identity. - The identity to be unlinked must belong to the user. examples: - id: unlink-identity name: Unlink an identity isSpotlight: true code: | ```swift // retrieve all identities linked to a user let identities = try await supabase.auth.userIdentities() // find the google identity let googleIdentity = identities.first { $0.provider == .google } // unlink the google identity try await supabase.auth.unlinkIdentity(googleIdentity) ``` - id: send-password-reauthentication title: 'reauthenticate()' notes: | - This method is used together with `update(user:)` when a user's password needs to be updated. - If you require your user to reauthenticate before updating their password, you need to enable the **Secure password change** option in your [project's email provider settings](/dashboard/project/_/auth/providers). - A user is only require to reauthenticate before updating their password if **Secure password change** is enabled and the user **hasn't recently signed in**. A user is deemed recently signed in if the session was created in the last 24 hours. - This method will send a nonce to the user's email. If the user doesn't have a confirmed email address, the method will send the nonce to the user's confirmed phone number instead. examples: - id: send-reauthentication-nonce name: Send reauthentication nonce description: Sends a reauthentication nonce to the user's email or phone number. isSpotlight: true code: | ```swift try await supabase.auth.reauthenticate() ``` - id: resend-email-or-phone-otps title: 'resend()' notes: | - Resends a signup confirmation, email change, or phone change email to the user. - Passwordless sign-ins can be resent by calling the `signInWithOTP()` method again. - Password recovery emails can be resent by calling the `resetPasswordForEmail()` method again. - This method only resends an email or phone OTP to the user if there an initial signup, email change, or phone change request was made. - You can specify a redirect URL when you resend an email link using the `emailRedirectTo` option. examples: - id: resend-email-signup-confirmation name: Resend an email signup confirmation description: Resends the email signup confirmation to the user isSpotlight: true code: | ```swift try await supabase.auth.resend( email: "email@example.com", type: .signup, emailRedirectTo: URL(string: "my-app-scheme://") ) ``` - id: resend-phone-signup-confirmation name: Resend a phone signup confirmation description: Resends the phone signup confirmation email to the user code: | ```swift try await supabase.auth.resend( phone: "1234567890", type: .sms ) ``` - id: resend-email-change-email name: Resend email change email description: Resends the email change email to the user code: | ```swift try await supabase.auth.resend( email: "email@example.com", type: .emailChange ) ``` - id: resend-phone-change name: Resend phone change OTP description: Resends the phone change OTP to the user code: | ```swift try await supabase.auth.resend( phone: "1234567890", type: .phoneChange ) ``` - id: set-session title: 'setSession()' notes: | - `setSession()` takes in a refresh token and uses it to get a new session. - The refresh token can only be used once to obtain a new session. - [Refresh token rotation](/docs/reference/auth/config#refresh_token_rotation_enabled) is enabled by default on all projects to guard against replay attacks. - You can configure the [`REFRESH_TOKEN_REUSE_INTERVAL`](https://supabase.com/docs/reference/auth/config#refresh_token_reuse_interval) which provides a short window in which the same refresh token can be used multiple times in the event of concurrency or offline issues. examples: - id: refresh-the-session name: Refresh the session description: Sets the session data from refresh_token and returns current session or an error if the refresh_token is invalid. isSpotlight: true code: | ```swift try await supabase.auth.setSession(accessToken: "access_token", refreshToken: "refresh_token") ``` - id: refresh-session title: 'refreshSession()' notes: | - This method will refresh the session whether the current one is expired or not. examples: - id: refresh-session-using-the-current-session name: Refresh session using the current session isSpotlight: true code: | ```swift let session = try await supabase.auth.refreshSession() ``` - id: refresh-session-using-a-passed-in-session name: Refresh session using a refresh token isSpotlight: false code: | ```swift let session = try await supabase.auth.refreshSession(refreshToken: "custom-refresh-token") ``` - id: get-claims title: 'getClaims()' notes: | - Verifies a JWT and extracts its claims. - For symmetric JWTs (HS256), verification is performed server-side via the `getUser()` API. - For asymmetric JWTs (RS256), verification is performed client-side using Apple Security framework. - Uses a global JWKS cache shared across all clients with the same storage key for optimal performance. - Automatically handles key rotation by falling back to server-side verification when a JWK is not found. - The JWKS cache has a 10-minute TTL (time-to-live). overwriteParams: - name: jwt isOptional: true type: String description: > The JWT to verify. If not provided, uses the access token from the current session. - name: options isOptional: true type: GetClaimsOptions description: > Options for JWT verification. Can specify `allowExpired` to skip expiration check and `jwks` to provide custom JSON Web Key Set. examples: - id: get-claims-current-session name: Verify and get claims from current session isSpotlight: true code: | ```swift let response = try await supabase.auth.getClaims() print("User ID: \(response.claims.sub ?? "N/A")") print("Email: \(response.claims.email ?? "N/A")") print("Role: \(response.claims.role ?? "N/A")") ``` - id: get-claims-custom-jwt name: Verify and get claims from a specific JWT isSpotlight: false code: | ```swift let customToken = "eyJhbGci..." let response = try await supabase.auth.getClaims(jwt: customToken) ``` - id: get-claims-allow-expired name: Get claims from an expired JWT description: Useful for testing or extracting information from expired tokens. isSpotlight: false code: | ```swift let response = try await supabase.auth.getClaims( options: GetClaimsOptions(allowExpired: true) ) ``` - id: get-claims-custom-jwks name: Verify JWT with custom JWKS description: Provide a custom JSON Web Key Set for verification. isSpotlight: false code: | ```swift let customJWKS = JWKS(keys: [...]) let response = try await supabase.auth.getClaims( options: GetClaimsOptions(jwks: customJWKS) ) ``` - id: start-auto-refresh title: 'startAutoRefresh()' description: | Starts the automatic session refresh process. examples: - id: start-auto-refresh name: Start automatic session refresh isSpotlight: true code: | ```swift supabase.auth.startAutoRefresh() ``` - id: stop-auto-refresh title: 'stopAutoRefresh()' description: | Stops the automatic session refresh process. examples: - id: stop-auto-refresh name: Stop automatic session refresh isSpotlight: true code: | ```swift supabase.auth.stopAutoRefresh() ``` - id: exchange-code-for-session title: 'exchangeCodeForSession()' notes: | - Used when `flowType` is set to `pkce` in client options. examples: - id: exchange-auth-code name: Exchange Auth Code isSpotlight: true code: | ```swift try await supabase.auth.exchangeCodeForSession(authCode: "34e770dd-9ff9-416c-87fa-43b31d7ef225") ``` - id: auth-mfa-api title: 'Overview' notes: | This section contains methods commonly used for Multi-Factor Authentication (MFA) and are invoked behind the `supabase.auth.mfa` namespace. TOTP (time-based one-time password) is the stable 2nd factor. WebAuthn / passkey as a 2nd factor is **experimental** — opt in with `@_spi(Experimental) import Supabase`. The first-factor passkey API lives in the `auth` (not `auth.mfa`) namespace; see the [Auth Passkey](/docs/reference/swift/auth-passkey-api) section. We don't support recovery codes but we allow users to enroll more than 1 TOTP factor, with an upper limit of 10. Having a 2nd TOTP factor for recovery frees the user of the burden of having to store their recovery codes somewhere. It also reduces the attack surface since multiple recovery codes are usually generated compared to just having 1 backup TOTP factor. - id: mfa-enroll title: 'mfa.enroll()' notes: | - Supported factor types: `totp` (stable) and `webauthn` (**experimental** — opt in with `@_spi(Experimental) import Supabase`). The returned `id` should be used to create a challenge. - To create a challenge, see [`mfa.challenge()`](/docs/reference/swift/auth-mfa-challenge). - To verify a challenge, see [`mfa.verify()`](/docs/reference/swift/auth-mfa-verify). - To create and verify a challenge in a single step, see [`mfa.challengeAndVerify()`](/docs/reference/swift/auth-mfa-challengeandverify). - For a one-call WebAuthn enrollment on iOS 16+/macOS 13+, use `mfa.enrollWebAuthnFactor(friendlyName:presentationAnchor:)` instead. examples: - id: enroll-totp-factor name: Enroll a time-based, one-time password (TOTP) factor isSpotlight: true code: | ```swift let response = try await supabase.auth.mfa.enroll( params: MFAEnrollParams( issuer: "optional issuer", friendlyName: "optional friendly name" ) ) // Use the id to create a challenge. // The challenge can be verified by entering the code generated from the authenticator app. // The code will be generated upon scanning the qrCode or entering the secret into the authenticator app. let id = response.id let type = response.type let qrCode = response.totp?.qrCode let secret = response.totp?.secret let uri = response.totp?.uri ``` - id: enroll-webauthn-factor name: Enroll a WebAuthn (passkey) factor (iOS 16+/macOS 13+, experimental) code: | ```swift // @_spi(Experimental) import Supabase // enrollWebAuthnFactor drives the full ceremony: enroll → challenge → present native UI → verify. let verifyResponse = try await supabase.auth.mfa.enrollWebAuthnFactor( friendlyName: "My passkey", presentationAnchor: view.window! ) ``` - id: mfa-challenge title: 'mfa.challenge()' notes: | - An [enrolled factor](/docs/reference/swift/auth-mfa-enroll) is required before creating a challenge. - To verify a challenge, see [`mfa.verify()`](/docs/reference/swift/auth-mfa-verify). examples: - id: create-mfa-challenge name: Create a challenge for a factor isSpotlight: true code: | ```swift let response = try await supabase.auth.mfa.challenge( params: MFAChallengeParams( factorId: "34e770dd-9ff9-416c-87fa-43b31d7ef225" ) ) ``` - id: mfa-verify title: 'mfa.verify()' notes: | - To verify a challenge, please [create a challenge](/docs/reference/swift/auth-mfa-challenge) first. - For WebAuthn factors on iOS 16+/macOS 13+, `mfa.verifyWebAuthnFactor(factorId:presentationAnchor:)` drives the full challenge + native UI + verify flow in one call (**experimental** — opt in with `@_spi(Experimental) import Supabase`). examples: - id: verify-challenge name: Verify a challenge for a factor isSpotlight: true code: | ```swift let session = try await supabase.auth.mfa.verify( params: MFAVerifyParams( factorId: "34e770dd-9ff9-416c-87fa-43b31d7ef225", challengeId: "4034ae6f-a8ce-4fb5-8ee5-69a5863a7c15", code: "123456" ) ) ``` - id: verify-webauthn-factor name: Verify a WebAuthn factor (iOS 16+/macOS 13+, experimental) code: | ```swift // @_spi(Experimental) import Supabase // verifyWebAuthnFactor drives the full ceremony: challenge → present native UI → verify. let session = try await supabase.auth.mfa.verifyWebAuthnFactor( factorId: "34e770dd-9ff9-416c-87fa-43b31d7ef225", presentationAnchor: view.window! ) ``` - id: mfa-challenge-and-verify title: 'mfa.challengeAndVerify()' notes: | - An [enrolled factor](/docs/swift/javascript/auth-mfa-enroll) is required before invoking `challengeAndVerify()`. - Executes [`mfa.challenge()`](/docs/reference/swift/auth-mfa-challenge) and [`mfa.verify()`](/docs/reference/swift/auth-mfa-verify) in a single step. examples: - id: challenge-and-verify name: Create and verify a challenge for a factor isSpotlight: true code: | ```swift let session = try await supabase.auth.mfa.challengeAndVerify( params: MFAChallengeAndVerifyParams( factorId: "34e770dd-9ff9-416c-87fa-43b31d7ef225", code: "123456" ) ) ``` - id: mfa-unenroll title: 'mfa.unenroll()' notes: | - Since v2.41.1, the unenroll response uses `id` instead of `factorId` to match the server response format. If upgrading from an earlier version, update your code to use `response.id`. examples: - id: unenroll-a-factor name: Unenroll a factor isSpotlight: true code: | ```swift let response = try await supabase.auth.mfa.unenroll( params: MFAUnenrollParams( factorId: "34e770dd-9ff9-416c-87fa-43b31d7ef225" ) ) print(response.id) // ID of the unenrolled factor ``` - id: mfa-get-authenticator-assurance-level title: 'mfa.getAuthenticatorAssuranceLevel()' notes: | - Authenticator Assurance Level (AAL) is the measure of the strength of an authentication mechanism. - In Supabase, having an AAL of `aal1` refers to having the 1st factor of authentication such as an email and password or OAuth sign-in while `aal2` refers to the 2nd factor of authentication such as a time-based, one-time-password (TOTP). - If the user has a verified factor, the `nextLevel` field will return `aal2`, else, it will return `aal1`. examples: - id: get-aal name: Get the AAL details of a session isSpotlight: true code: | ```swift let aal = try await supabase.auth.mfa.getAuthenticatorAssuranceLevel() let currentLevel = aal.currentLevel let nextLevel = aal.nextLevel let currentAuthenticationMethods = aal.currentAuthenticationMethods ``` - id: mfa-list-factors title: 'mfa.listFactors()' examples: - id: list-factors name: List all factors for a user isSpotlight: true code: | ```swift let factors = try await supabase.auth.mfa.listFactors() ``` - id: passkey-api title: 'Overview' notes: | Lower-level passkey methods for custom authenticator flows and platforms where `AuthenticationServices` is unavailable (tvOS, watchOS). These methods handle the network side of the WebAuthn ceremony only — the caller is responsible for driving the platform authenticator between fetching options and submitting the credential response. For an end-to-end flow on iOS 16+/macOS 13+, prefer `signInWithPasskey(presentationAnchor:)` and `registerPasskey(presentationAnchor:)`. Passkey support is **experimental**. Opt in with `@_spi(Experimental) import Supabase`. The API may change in future releases. - id: passkey-list title: 'listPasskeys()' notes: | Returns the list of passkeys registered to the signed-in user. examples: - id: list-passkeys name: List the current user's passkeys isSpotlight: true code: | ```swift // @_spi(Experimental) import Supabase let passkeys: [PasskeyListItem] = try await supabase.auth.listPasskeys() ``` - id: passkey-update title: 'renamePasskey(id:friendlyName:)' notes: | Updates the friendly name of a passkey. Limited to 120 characters. params: - name: id isOptional: false type: String description: ID of the passkey to rename. - name: friendlyName isOptional: false type: String description: New human readable name. Limited to 120 characters. examples: - id: rename-passkey name: Rename a passkey isSpotlight: true code: | ```swift // @_spi(Experimental) import Supabase let passkey = try await supabase.auth.renamePasskey( id: "34e770dd-9ff9-416c-87fa-43b31d7ef225", friendlyName: "Work laptop" ) ``` - id: passkey-delete title: 'deletePasskey(id:)' notes: | Removes a passkey from the signed-in user's account. params: - name: id isOptional: false type: String description: ID of the passkey to delete. examples: - id: delete-passkey name: Delete a passkey isSpotlight: true code: | ```swift // @_spi(Experimental) import Supabase try await supabase.auth.deletePasskey(id: "34e770dd-9ff9-416c-87fa-43b31d7ef225") ``` - id: passkey-start-registration title: 'getPasskeyRegistrationOptions()' notes: | Fetches credential creation options to register a new passkey for the signed-in user. - Pass the returned `options` (W3C `PublicKeyCredentialCreationOptions`) to the platform authenticator. - After running the authenticator, submit the result with `verifyPasskeyRegistration(challengeId:credentialResponse:)`. examples: - id: start-passkey-registration name: Get passkey registration options isSpotlight: true code: | ```swift // @_spi(Experimental) import Supabase let options: PasskeyRegistrationOptions = try await supabase.auth.getPasskeyRegistrationOptions() // Hand options.options to the platform authenticator. ``` - id: passkey-verify-registration title: 'verifyPasskeyRegistration(challengeId:credentialResponse:)' notes: | Stores a newly created passkey for the signed-in user. params: - name: challengeId isOptional: false type: String description: The `challengeId` returned by `getPasskeyRegistrationOptions()`. - name: credentialResponse isOptional: false type: AnyJSON description: The W3C `RegistrationResponseJSON` credential produced by the authenticator. examples: - id: verify-passkey-registration name: Verify a passkey registration isSpotlight: true code: | ```swift // @_spi(Experimental) import Supabase let passkey: PasskeyListItem = try await supabase.auth.verifyPasskeyRegistration( challengeId: options.challengeId, credentialResponse: credential ) ``` - id: passkey-start-authentication title: 'getPasskeyAuthenticationOptions()' notes: | Fetches assertion options to authenticate with a passkey. - Does not require an existing session. - Pass the returned `options` (W3C `PublicKeyCredentialRequestOptions`) to the platform authenticator. - After running the authenticator, submit the result with `verifyPasskeyAuthentication(challengeId:credentialResponse:)`. examples: - id: start-passkey-authentication name: Get passkey authentication options isSpotlight: true code: | ```swift // @_spi(Experimental) import Supabase let options: PasskeyAuthenticationOptions = try await supabase.auth.getPasskeyAuthenticationOptions() // Hand options.options to the platform authenticator. ``` - id: passkey-verify-authentication title: 'verifyPasskeyAuthentication(challengeId:credentialResponse:)' notes: | Verifies a passkey assertion and establishes a session. On success the session is persisted and a `signedIn` auth change event is emitted. params: - name: challengeId isOptional: false type: String description: The `challengeId` returned by `getPasskeyAuthenticationOptions()`. - name: credentialResponse isOptional: false type: AnyJSON description: The W3C `AuthenticationResponseJSON` assertion produced by the authenticator. examples: - id: verify-passkey-authentication name: Verify a passkey sign in isSpotlight: true code: | ```swift // @_spi(Experimental) import Supabase let response: AuthResponse = try await supabase.auth.verifyPasskeyAuthentication( challengeId: options.challengeId, credentialResponse: credential ) let session = response.session let user = response.user ``` - id: admin-api title: 'Overview' notes: | - Any method under the `supabase.auth.admin` namespace requires a `secret` key. - These methods are considered admin methods and should be called on a trusted server. Never expose your `secret` key in the browser. examples: - id: create-auth-admin-client name: Create server-side auth client isSpotlight: true code: | ```swift import Supabase let supabase = SupabaseClient( supabaseURL: supabaseURL, supabaseKey: secretKey ) // Access auth admin api let adminAuthClient = supabase.auth.admin ``` - id: delete-user title: 'deleteUser()' notes: | - The `deleteUser()` method requires the user's ID, which maps to the `auth.users.id` column. examples: - id: removes-a-user name: Removes a user isSpotlight: true code: | ```swift try await supabase.auth.admin.deleteUser( id: "715ed5db-f090-4b8c-a067-640ecee36aa0" ) ``` - id: get-user-by-id title: 'getUserById()' description: | Get user by ID. notes: | - The `getUserById()` method requires a user's ID. examples: - id: get-user-by-id name: Get user by ID isSpotlight: true code: | ```swift let user = try await supabase.auth.admin.getUserById( "715ed5db-f090-4b8c-a067-640ecee36aa0" ) ``` - id: list-users title: 'listUsers()' description: | List all users in the system. examples: - id: list-users name: List users isSpotlight: true code: | ```swift let users = try await supabase.auth.admin.listUsers() ``` - id: list-users-with-pagination name: List users with pagination code: | ```swift let users = try await supabase.auth.admin.listUsers( params: PageParams( page: 2, perPage: 10 ) ) ``` - id: create-user title: 'createUser()' description: | Create a new user. examples: - id: create-user name: Create user isSpotlight: true code: | ```swift let user = try await supabase.auth.admin.createUser( attributes: AdminUserAttributes( email: "user@email.com", password: "password", emailConfirm: true ) ) ``` - id: update-user-by-id title: 'updateUserById()' description: | Update user by ID. examples: - id: update-user-by-id name: Update user by ID isSpotlight: true code: | ```swift let user = try await supabase.auth.admin.updateUserById( "715ed5db-f090-4b8c-a067-640ecee36aa0", attributes: AdminUserAttributes( email: "newemail@email.com" ) ) ``` - id: invite-user-by-email title: 'inviteUserByEmail()' description: | Send an invite link to the user's email address. examples: - id: invite-user-by-email name: Invite user by email isSpotlight: true code: | ```swift let user = try await supabase.auth.admin.inviteUserByEmail( "user@email.com", data: ["role": "admin"], redirectTo: URL(string: "https://example.com/welcome") ) ``` - id: generate-link title: 'generateLink()' description: | Generates an email link for a specific action without sending it. This is useful for custom admin functionality where you want to build the email or OTP flow yourself. notes: | - `GenerateLinkParams` exposes a static factory for each link type: `.signUp(email:password:redirectTo:)`, `.invite(email:redirectTo:)`, `.magicLink(email:redirectTo:)`, `.recovery(email:redirectTo:)`, `.emailChangeCurrent(email:newEmail:redirectTo:)`, and `.emailChangeNew(email:newEmail:redirectTo:)`. - `generateLink()` creates the user for `.signUp` and `.invite` if one doesn't already exist. examples: - id: generate-a-signup-link name: Generate a signup link isSpotlight: true code: | ```swift let response = try await supabase.auth.admin.generateLink( params: .signUp( email: "email@example.com", password: "secret" ) ) let actionLink = response.properties.actionLink ``` - id: generate-a-recovery-link name: Generate a recovery link code: | ```swift let response = try await supabase.auth.admin.generateLink( params: .recovery( email: "email@example.com", redirectTo: URL(string: "https://example.com/reset-password") ) ) ``` - id: auth-js-gotrueadminapi-signout title: 'signOut()' description: | Signs out a specific user by revoking their session(s), using that user's access token (JWT). notes: | - Unlike `supabase.auth.signOut()`, this method takes the target user's access token (JWT), not a user ID. - By default, `signOut()` uses the `.global` scope, which revokes every session for the user. Pass `.local` to revoke only the session tied to the given JWT, or `.others` to keep that session and revoke all the rest. examples: - id: sign-out-a-user name: Sign out a user isSpotlight: true code: | ```swift try await supabase.auth.admin.signOut(jwt: jwt) ``` - id: sign-out-a-user-with-scope name: Sign out a user with a scope code: | ```swift try await supabase.auth.admin.signOut(jwt: jwt, scope: .others) ``` - id: admin-oauth-list-clients title: 'admin.oauth.listClients()' description: | List all OAuth clients with optional pagination. notes: | - Requires `secret` key. - This method is part of the OAuth 2.1 server administration API. - Only works when the OAuth 2.1 server is enabled in your Supabase Auth configuration. overwriteParams: - name: params isOptional: true type: PageParams description: > Pagination parameters with `page` and `perPage` options. examples: - id: list-oauth-clients name: List all OAuth clients isSpotlight: true code: | ```swift let response = try await supabase.auth.admin.oauth.listClients() ``` - id: list-oauth-clients-paginated name: List OAuth clients with pagination isSpotlight: false code: | ```swift let response = try await supabase.auth.admin.oauth.listClients( params: PageParams(page: 1, perPage: 10) ) ``` - id: admin-oauth-create-client title: 'admin.oauth.createClient()' description: | Create a new OAuth client. notes: | - Requires `secret` key. - This method is part of the OAuth 2.1 server administration API. - Only works when the OAuth 2.1 server is enabled in your Supabase Auth configuration. overwriteParams: - name: params type: CreateOAuthClientParams description: > Parameters for creating the OAuth client including name, redirect URIs, and client type. examples: - id: create-oauth-client name: Create a new OAuth client isSpotlight: true code: | ```swift let client = try await supabase.auth.admin.oauth.createClient( params: CreateOAuthClientParams( name: "My OAuth App", redirectUris: ["https://example.com/callback"], clientType: .confidential ) ) ``` - id: admin-oauth-get-client title: 'admin.oauth.getClient()' description: | Get details of a specific OAuth client. notes: | - Requires `secret` key. - This method is part of the OAuth 2.1 server administration API. overwriteParams: - name: clientId type: String description: > The UUID of the OAuth client to retrieve. examples: - id: get-oauth-client name: Get OAuth client by ID isSpotlight: true code: | ```swift let client = try await supabase.auth.admin.oauth.getClient( clientId: "12345678-1234-1234-1234-123456789012" ) ``` - id: admin-oauth-delete-client title: 'admin.oauth.deleteClient()' description: | Delete an OAuth client. notes: | - Requires `secret` key. - This method is part of the OAuth 2.1 server administration API. - This action cannot be undone. overwriteParams: - name: clientId type: String description: > The UUID of the OAuth client to delete. examples: - id: delete-oauth-client name: Delete an OAuth client isSpotlight: true code: | ```swift try await supabase.auth.admin.oauth.deleteClient( clientId: "12345678-1234-1234-1234-123456789012" ) ``` - id: admin-oauth-regenerate-client-secret title: 'admin.oauth.regenerateClientSecret()' description: | Regenerate the secret for an OAuth client. notes: | - Requires `secret` key. - This method is part of the OAuth 2.1 server administration API. - The old secret will be immediately invalidated. - Make sure to update your application with the new secret. overwriteParams: - name: clientId type: String description: > The UUID of the OAuth client whose secret should be regenerated. examples: - id: regenerate-oauth-client-secret name: Regenerate OAuth client secret isSpotlight: true code: | ```swift let client = try await supabase.auth.admin.oauth.regenerateClientSecret( clientId: "12345678-1234-1234-1234-123456789012" ) // The response contains the new secret print("New secret: \(client.secret ?? "")") ``` - id: select title: 'Fetch data: select()' notes: | - By default, Supabase projects will return a maximum of 1,000 rows. This setting can be changed in Project API Settings. It's recommended that you keep it low to limit the payload size of accidental or malicious requests. You can use `range()` queries to paginate through your data. - `select()` can be combined with [Modifiers](/docs/reference/swift/using-modifiers) - `select()` can be combined with [Filters](/docs/reference/swift/using-filters) - If using the Supabase hosted platform `apikey` is technically a reserved keyword, since the API gateway will pluck it out for authentication. [It should be avoided as a column name](https://github.com/supabase/supabase/issues/5465). - The recommended solution for getting data is to use the value property which will return a decoded model. Create a `Codable` to easily decode your database responses. examples: - id: getting-your-data name: Getting your data isSpotlight: true code: | ```swift struct Instrument: Decodable { let id: Int let name: String } let instruments: [Instrument] = try await supabase .from("instruments") .select() .execute() .value ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": [ { "id": 1, "name": "violin" }, { "id": 2, "name": "viola" }, { "id": 3, "name": "cello" } ], "status": 200, "statusText": "OK" } ``` - id: selecting-specific-columns name: Selecting specific columns code: | ```swift struct Instrument: Decodable { let name: String } let instruments: [Instrument] = try await supabase .from("instruments") .select("name") .execute() .value ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": [ { "name": "violin" }, { "name": "viola" }, { "name": "cello" } ], "status": 200, "statusText": "OK" } ``` - id: query-foreign-tables name: Query foreign tables description: | If your database has foreign key relationships, you can query related tables too. code: | ```swift struct OrchestralSection: Decodable { let name: String let instruments: [Instrument] } struct Instrument: Decodable { let name: String } let orchestralSections: [OrchestralSection] = try await supabase .from("orchestral_sections") .select( """ name, instruments ( name ) """ ) .execute() .value ``` data: sql: | ```sql create table orchestral_sections (id int8 primary key, name text); create table instruments ( id int8 primary key, section_id int8 not null references orchestral_sections, name text ); insert into orchestral_sections (id, name) values (1, 'strings'), (2, 'woodwinds'); insert into instruments (id, section_id, name) values (1, 2, 'flute'), (2, 1, 'violin'); ``` response: | ```json { "data": [ { "name": "strings", "cities": [ { "name": "violin" } ] }, { "name": "woodwinds", "cities": [ { "name": "flute" } ] } ], "status": 200, "statusText": "OK" } ``` - id: query-foreign-tables-through-a-join-table name: Query foreign tables through a join table code: | ```swift struct User: Decodable { let name: String let teams: [Team] } struct Team: Decodable { let name: String } let users: [User] = try await supabase .from("users") .select( """ name, teams ( name ) """ ) .execute() .value ``` data: sql: | ```sql create table users ( id int8 primary key, name text ); create table teams ( id int8 primary key, name text ); -- join table create table users_teams ( user_id int8 not null references users, team_id int8 not null references teams, -- both foreign keys must be part of a composite primary key primary key (user_id, team_id) ); insert into users (id, name) values (1, 'Kiran'), (2, 'Evan'); insert into teams (id, name) values (1, 'Green'), (2, 'Blue'); insert into users_teams (user_id, team_id) values (1, 1), (1, 2), (2, 2); ``` response: | ```json { "data": [ { "name": "Kiran", "teams": [ { "name": "Green" }, { "name": "Blue" } ] }, { "name": "Evan", "teams": [ { "name": "Blue" } ] } ], "status": 200, "statusText": "OK" } ``` description: | If you're in a situation where your tables are **NOT** directly related, but instead are joined by a _join table_, you can still use the `select()` method to query the related data. The join table needs to have the foreign keys as part of its composite primary key. hideCodeBlock: true - id: query-the-same-foreign-table-multiple-times name: Query the same foreign table multiple times code: | ```swift struct Message: Decodable { let content: String let from: User let to: User } struct User: Decodable { let name: String } let messages: [Message] = try await supabase .from("messages") .select( """ content, from:sender_id(name), to:sended_id(name) """ ) .execute() .value ``` data: sql: | ```sql create table users (id int8 primary key, name text); create table messages ( sender_id int8 not null references users, receiver_id int8 not null references users, content text ); insert into users (id, name) values (1, 'Kiran'), (2, 'Evan'); insert into messages (sender_id, receiver_id, content) values (1, 2, '👋'); ``` response: | ```json { "data": [ { "content": "👋", "from": { "name": "Kiran" }, "to": { "name": "Evan" } } ], "status": 200, "statusText": "OK" } ``` description: | If you need to query the same foreign table twice, use the name of the joined column to identify which join to use. You can also give each column an alias. hideCodeBlock: true - id: filtering-through-foreign-tables name: Filtering through foreign tables code: | ```swift struct Instrument: Decodable { let name: String let orchestralSections: [OrchestralSection]? } struct OrchestralSection: Decodable { let name: String } let instruments: [Instrument] = try await supabase .from("instruments") .select("name, orchestral_sections(*)") .eq("orchestral_sections.name", value: "percussion") .execute() .value ``` data: sql: | ```sql create table orchestral_sections (id int8 primary key, name text); create table instruments ( id int8 primary key, section_id int8 not null references orchestral_sections, name text ); insert into orchestral_sections (id, name) values (1, 'strings'), (2, 'woodwinds'); insert into instruments (id, section_id, name) values (1, 2, 'flute'), (2, 1, 'violin'); ``` response: | ```json { "data": [ { "name": "flute", "orchestral_sections": null }, { "name": "violin", "orchestral_sections": null } ], "status": 200, "statusText": "OK" } ``` description: | If the filter on a foreign table's column is not satisfied, the foreign table returns `[]` or `null` but the parent table is not filtered out. If you want to filter out the parent table rows, use the `!inner` hint hideCodeBlock: true - id: querying-foreign-table-with-count name: Querying foreign table with count code: | ```swift struct OrchestralSection: Decodable { let id: UUID let name: String let instruments: [Instrument] } struct Instrument: Decodable { let count: Int } let orchestralSections: [OrchestralSection] = try await supabase .from("orchestral_sections") .select("*, instruments(count)") .execute() .value ``` data: sql: | ```sql create table orchestral_sections ( "id" "uuid" primary key default "extensions"."uuid_generate_v4"() not null, "name" text ); create table instruments ( "id" "uuid" primary key default "extensions"."uuid_generate_v4"() not null, "name" text, "section_id" "uuid" references public.orchestral_sections on delete cascade ); with section as ( insert into orchestral_sections (name) values ('strings') returning id ) insert into instruments (name, section_id) values ('violin', (select id from section)), ('viola', (select id from section)), ('cello', (select id from section)), ('double bass', (select id from section)); ``` response: | ```json [ { "id": "693694e7-d993-4360-a6d7-6294e325d9b6", "name": "strings", "instruments": [ { "count": 4 } ] } ] ``` description: | You can get the number of rows in a related table by using the **count** property. hideCodeBlock: true - id: querying-with-count-option name: Querying with count option code: | ```swift let count = try await supabase .from("instruments") .select("*", head: true, count: .exact) .execute() .count ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'dulcimer'), (2, 'harp'), (3, 'tuba'); ``` response: | ```json { "count": 3, "status": 200, "statusText": "OK" } ``` description: | You can get the number of rows by using the [count](/docs/reference/swift/select#parameters) option. hideCodeBlock: true - id: querying-json-data name: Querying JSON data code: | ```swift struct User: Decodable { let id: Int let name: String let city: String } let users: [User] = try await supabase .from("users") .select( """ id, name, address->city """ ) .execute() .value ``` data: sql: | ```sql create table users ( id int8 primary key, name text, address jsonb ); insert into users (id, name, address) values (1, 'Frodo', '{"city":"Hobbiton"}'); ``` response: | ```json { "data": [ { "id": 1, "name": "Frodo", "city": "Hobbiton" } ], "status": 200, "statusText": "OK" } ``` description: | You can select and filter data inside of [JSON](/docs/guides/database/json) columns. Postgres offers some [operators](/docs/guides/database/json#query-the-jsonb-data) for querying JSON data. hideCodeBlock: true - id: querying-foreign-table-with-inner-join name: Querying foreign table with inner join code: | ```swift struct Instrument: Decodable { let name: String } struct OrchestralSection: Decodable { let name: String let instruments: [Instrument] } let orchestralSections: [OrchestralSection] = try await supabase .from("orchestral_sections") .select("name, instruments!inner(name)") .eq("name", value: "strings") .execute() .value ``` data: sql: | ```sql create table orchestral_sections ( "id" "uuid" primary key default "extensions"."uuid_generate_v4"() not null, "name" text ); create table instruments ( "id" "uuid" primary key default "extensions"."uuid_generate_v4"() not null, "name" text, "section_id" "uuid" references public.orchestral_sections on delete cascade ); with section as ( insert into orchestral_sections (name) values ('strings') returning id ) insert into instruments (name, section_id) values ('violin', (select id from section)), ('viola', (select id from section)), ('cello', (select id from section)), ('double bass', (select id from section)); ``` response: | ```json { "data": [ { "name": "strings", "instruments": [ { "name": "violin" }, { "name": "viola" }, { "name": "cello" }, { "name": "double bass" } ] } ], "status": 200, "statusText": "OK" } ``` description: | If you don't want to return the foreign table contents, you can leave the parenthesis empty. Like `.select('name, instruments!inner()')`. hideCodeBlock: true - id: switching-schemas-per-query name: Switching schemas per query code: | ```swift try await supabase .schema("myschema") .from("mytable") .select() ``` data: sql: | ```sql create schema myschema; create table myschema.mytable ( id uuid primary key default gen_random_uuid(), data text ); insert into myschema.mytable (data) values ('mydata'); ``` response: | ```json [ { "id": "4162e008-27b0-4c0f-82dc-ccaeee9a624d", "data": "mydata" } ] ``` description: | In addition to setting the schema during initialization, you can also switch schemas on a per-query basis. Make sure you've set up your [database privileges and API settings](/docs/guides/api/using-custom-schemas). hideCodeBlock: true - id: insert title: 'Create data: insert()' examples: - id: create-a-record name: Create a record code: | ```swift struct Instrument: Encodable { let id: Int let name: String } let instrument = Instrument(id: 1, name: "ukelele") try await supabase .from("instruments") .insert(instrument) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); ``` response: | ```json { "status": 201, "statusText": "Created" } ``` hideCodeBlock: true isSpotlight: true - id: create-a-record-and-return-it name: Create a record and return it code: | ```swift struct Instrument: Codable { let id: Int let name: String } let instrument: Instrument = try await supabase .from("instruments") .insert(Instrument(id: 1, name: "banjo")) .select() // specify you want a single value returned, otherwise it returns a list. .single() .execute() .value ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); ``` response: | ```json { "data": [ { "id": 1, "name": "banjo" } ], "status": 201, "statusText": "Created" } ``` hideCodeBlock: true - id: bulk-create name: Bulk create code: | ```swift struct Instrument: Encodable { let id: Int let name: String } let instruments = [ Instrument(id: 1, name: "xylophone"), Instrument(id: 1, name: "tuba"), ] try await supabase .from("instruments") .insert(instruments) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); ``` response: | ```json { "error": { "code": "23505", "details": "Key (id)=(1) already exists.", "hint": null, "message": "duplicate key value violates unique constraint \"instruments_pkey\"" }, "status": 409, "statusText": "Conflict" } ``` description: | A bulk create operation is handled in a single transaction. If any of the inserts fail, none of the rows are inserted. hideCodeBlock: true - id: update title: 'Modify data: update()' notes: | - `update()` should always be combined with [Filters](/docs/reference/swift/using-filters) to target the item(s) you wish to update. examples: - id: updating-your-data name: Updating your data code: | ```swift try await supabase .from("instruments") .update(["name": "piano"]) .eq("id", value: 1) .execute() ``` notes: | Not always you need to create a `Encodable`` struct to define the object being updated, in this example we use a `[String: String]` type directly, since it conforms to `Encodable``. data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'harpsichord'); ``` response: | ```json { "status": 204, "statusText": "No Content" } ``` hideCodeBlock: true isSpotlight: true - id: update-a-record-and-return-it name: Update a record and return it code: | ```swift struct Instrument: Decodable { let id: Int let name: String } let instrument: Instrument = try await supabase .from("instruments") .update(["name": "piano"]) .eq("id", value: 1) .select() // If you know this query should return a single object, append a `single()` modifier to it. .single() .execute() .value ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'harpsichord'); ``` response: | ```json { "data": [ { "id": 1, "name": "piano" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true - id: updating-json-data name: Updating JSON data code: | ```swift struct User: Decodable { let id: Int let name: String let address: Address struct Address: Codable { let street: String let postcode: String } } struct UpdateUser: Encodable { let address: User.Address } let users: [User] = try await supabase .from("users") .update( UpdateUser( address: .init( street: "Melrose Place", postcode: "90210" ) ) ) .eq("address->postcode", value: "90210") .select() .execute() .value ``` data: sql: | ```sql create table users ( id int8 primary key, name text, address jsonb ); insert into users (id, name, address) values (1, 'Michael', '{ "postcode": "90210" }'); ``` response: | ```json { "data": [ { "id": 1, "name": "Michael", "address": { "street": "Melrose Place", "postcode": "90210" } } ], "status": 200, "statusText": "OK" } ``` description: | Postgres offers some [operators](/docs/guides/database/json#query-the-jsonb-data) for working with JSON data. Currently, it is only possible to update the entire JSON document. hideCodeBlock: true - id: upsert title: 'Upsert data: upsert()' notes: | - Primary keys must be included in `values` to use upsert. examples: - id: upsert-your-data name: Upsert your data code: | ```swift struct Instrument: Encodable { let id: Int let name: String } try await supabase .from("instruments") .upsert(Instrument(id: 1, name: "piano")) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'harpsichord'); ``` response: | ```json { "data": [ { "id": 1, "name": "piano" } ], "status": 201, "statusText": "Created" } ``` hideCodeBlock: true isSpotlight: true - id: bulk-upsert-your-data name: Bulk Upsert your data code: | ```swift struct Instrument: Encodable { let id: Int let name: String } try await supabase .from("instruments") .upsert([ Instrument(id: 1, name: "piano"), Instrument(id: 2, name: "harp"), ]) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'harpsichord'); ``` response: | ```json { "data": [ { "id": 1, "name": "piano" }, { "id": 2, "name": "harp" } ], "status": 201, "statusText": "Created" } ``` hideCodeBlock: true - id: upserting-into-tables-with-constraints name: Upserting into tables with constraints code: | ```swift struct User: Encodable { let id: Int let handle: String let displayName: String enum CodingKeys: String, CodingKey { case id case handle case displayName = "display_name" } } try await supabase .from("users") .upsert( User(id: 42, handle: "saoirse", displayName: "Saoirse"), onConflict: "handle" ) .execute() ``` data: sql: | ```sql create table users ( id int8 generated by default as identity primary key, handle text not null unique, display_name text ); insert into users (id, handle, display_name) values (1, 'saoirse', null); ``` response: | ```json { "error": { "code": "23505", "details": "Key (handle)=(saoirse) already exists.", "hint": null, "message": "duplicate key value violates unique constraint \"users_handle_key\"" }, "status": 409, "statusText": "Conflict" } ``` description: | In the following query, `upsert()` implicitly uses the `id` (primary key) column to determine conflicts. If there is no existing row with the same `id`, `upsert()` inserts a new row, which will fail in this case as there is already a row with `handle` `"saoirse"`. Using the `onConflict` option, you can instruct `upsert()` to use another column with a unique constraint to determine conflicts. hideCodeBlock: true - id: delete title: 'Delete data: delete()' notes: | - `delete()` should always be combined with [filters](/docs/reference/swift/using-filters) to target the item(s) you wish to delete. - If you use `delete()` with filters and you have [RLS](/docs/learn/auth-deep-dive/auth-row-level-security) enabled, only rows visible through `SELECT` policies are deleted. Note that by default no rows are visible, so you need at least one `SELECT`/`ALL` policy that makes the rows visible. examples: - id: delete-records name: Delete records code: | ```swift try await supabase .from("instruments") .delete() .eq("id", value: 1) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'mandolin'); ``` response: | ```json { "status": 204, "statusText": "No Content" } ``` hideCodeBlock: true isSpotlight: true - id: rpc title: 'Postgres functions: rpc()' description: | You can call Postgres functions as _Remote Procedure Calls_, logic in your database that you can execute from anywhere. Functions are useful when the logic rarely changes—like for password resets and updates. ```sql create or replace function hello_world() returns text as $$ select 'Hello world'; $$ language sql; ``` examples: - id: call-a-postgres-function-without-arguments name: Call a Postgres function without arguments code: | ```swift let value: String = try await supabase .rpc("hello_world") .execute() .value ``` data: sql: | ```sql create function hello_world() returns text as $$ select 'Hello world'; $$ language sql; ``` response: | ```json { "data": "Hello world", "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: call-a-postgres-function-with-arguments name: Call a Postgres function with arguments code: | ```swift let response: String = try await supabase .rpc("echo", params: ["say": "👋"]) .execute() .value ``` data: sql: | ```sql create function echo(say text) returns text as $$ select say; $$ language sql; ``` response: | ```json { "data": "👋", "status": 200, "statusText": "OK" } ``` hideCodeBlock: true - id: bulk-processing name: Bulk processing code: | ```swift let response: [Int] = try await supabase .rpc("add_one_each", params: ["arr": [1, 2, 3]]) .execute() .value ``` data: sql: | ```sql create function add_one_each(arr int[]) returns int[] as $$ select array_agg(n + 1) from unnest(arr) as n; $$ language sql; ``` response: | ```json { "data": [ 2, 3, 4 ], "status": 200, "statusText": "OK" } ``` description: | You can process large payloads by passing in an array as an argument. hideCodeBlock: true - id: call-a-postgres-function-with-filters name: Call a Postgres function with filters code: | ```swift struct Instrument: Decodable { let id: Int let name: String } let instrument: Instrument = await supabase .rpc("list_stored_instruments") .eq("id", value: 1) .single() .execute() .value ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'klaxon'), (2, 'marimba'); create function list_stored_countries() returns setof countries as $$ select * from countries; $$ language sql; ``` response: | ```json { "data": { "id": 1, "name": "klaxon" }, "status": 200, "statusText": "OK" } ``` description: | Postgres functions that return tables can also be combined with [Filters](/docs/reference/javascript/using-filters) and [Modifiers](/docs/reference/javascript/using-modifiers). hideCodeBlock: true - id: using-filters title: Using Filters description: | Filters allow you to only return rows that match certain conditions. Filters can be used on `select()`, `update()`, `upsert()`, and `delete()` queries. If a Postgres function returns a table response, you can also apply filters. Implement `URLQueryRepresentable` protocol in your own types to be able to use them as filter value. Supported filtes are: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`, `is`, `in`, `cs`, `cd`, `sl`, `sr`, `nxl`, `nxr`, `adj`, `ov`, `fts`, `plfts`, `phfts`, `wfts`. Check available operators in [PostgREST](https://postgrest.org/en/stable/references/api/tables_views.html#operators). examples: - id: applying-filters name: Applying Filters description: | Filters must be applied after any of `select()`, `update()`, `upsert()`, `delete()`, and `rpc()` and before [modifiers](/docs/reference/swift/using-modifiers). code: | ```swift try await supabase .from("cities") .select("name, country_id") .eq("name", value: "The Shire") // Correct try await supabase .from("citites") .eq("name", value: "The Shire") // Incorrect .select("name, country_id") ``` - id: chaining-filters name: Chaining description: | Filters can be chained together to produce advanced queries. For example, to query cities with population between 1,000 and 10,000: code: | ```swift try await supabase .from("cities") .select("name, country_id") .gte("population", value: 1000) .lt("population", value: 10000) ``` - id: conditional-chaining name: Conditional Chaining description: | Filters can be built up one step at a time and then executed. code: | ```swift let filterByName: String? = nil let filterPopLow: Int? = 1000 let filterPopHigh: Int? = 10000 var query = await supabase .from("cities") .select("name, country_id") if let filterByName { query = query.eq("name", value: filterByName) } if let filterPopLow { query = query.gte("population", value: filterPopLow) } if let filterPopHigh { query = query.lt("population", value: filterPopHigh) } struct Response: Decodable { // expected fields } let result: Response = try await query.execute().value ``` - id: filter-by-value-within-json-column name: Filter by values within a JSON column code: | ```swift try await supabase .from("users") .select() .eq("address->postcode", value: 90210) ``` data: sql: | ```sql create table users ( id int8 primary key, name text, address jsonb ); insert into users (id, name, address) values (1, 'Michael', '{ "postcode": 90210 }'), (2, 'Jane', null); ``` response: | ```json { "data": [ { "id": 1, "name": "Michael", "address": { "postcode": 90210 } } ], "status": 200, "statusText": "OK" } ``` - id: filter-foreign-tables name: Filter Foreign Tables code: | ```swift try await supabase .from("orchestral_sections") .select( """ name, instruments!inner ( name ) """ ) .eq("instruments.name", value: "flute") ``` data: sql: | ```sql create table orchestral_sections (id int8 primary key, name text); create table instruments ( id int8 primary key, section_id int8 not null references orchestral_sections, name text ); insert into orchestral_sections (id, name) values (1, 'strings'), (2, 'woodwinds'); insert into instruments (id, section_id, name) values (1, 2, 'flute'), (2, 1, 'violin'); ``` response: | ```json { "data": [ { "name": "woodwinds", "instruments": [ { "name": "flute" } ] } ], "status": 200, "statusText": "OK" } ``` description: | You can filter on foreign tables in your `select()` query using dot notation. - id: or title: or() notes: | or() expects you to use the raw PostgREST syntax for the filter names and values. ```swift .or(#"id.in.(5,6,7), arraycol.cs.{"a","b"}"#) // Use `()` for `in` filter, `{}` for array values and `cs` for `contains()`. .or(#"id.in.(5,6,7), arraycol.cd.{"a","b"}"#) // Use `cd` for `containedBy()` ``` examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("instruments") .select("name") .or("id.eq.2,name.eq.cello") ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": [ { "name": "viola" }, { "name": "cello" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: use-or-with-and name: Use `or` with `and` code: | ```swift try await supabase .from("instruments") .select("name") .or("id.gt.3,and(id.eq.1,name.eq.violin)") ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": [ { "name": "violin" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true - id: not title: not() description: | Finds all rows that don't satisfy the filter. notes: | - `.not()` expects you to use the raw [PostgREST syntax](https://postgrest.org/en/stable/api.html#horizontal-filtering-rows) for the filter names and values. ```swift .not("name", operator: .eq, value: "violin") .not("arraycol", operator: .cs, value: #"{"a","b"}"#) // Use Postgres array {} for array column and 'cs' for contains. .not("rangecol", operator: .cs, value: "(1,2]") // Use Postgres range syntax for range column. .not("id", operator: .in, value: "(6,7)") // Use Postgres list () and 'in' for in_ filter. .not("id", operator: .in, value: "(\(mylist.join(separator: ",")))") // You can insert a Swift list array. ``` examples: - id: with-select name: With `select()` isSpotlight: true code: | ```swift try await supabase .from("instruments") .select() .not("name", operator: .is, value: "") .execute() ``` - id: match title: match() examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("instruments") .select("name") .match(["id": 2, "name": "viola"]) ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": [ { "name": "viola" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: filter title: filter() notes: | filter() expects you to use the raw PostgREST syntax for the filter values. ```swift .filter("id", operator: "in", value: "(5,6,7)") // Use `()` for `in` filter .filter("arraycol", operator: "cs", value: #"{"a","b"}"#) // Use `cs` for `contains()`, `{}` for array values ``` examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("instruments") .select() .filter("name", operator: "in", value: #"("cello","guzheng")"#) ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": [ { "id": 3, "name": "cello" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: on-a-foreign-table name: On a foreign table code: | ```swift try await supabase .from("orchestral_sections") .select( """ name, instruments!inner ( name ) """ ) .filter("instruments.name", operator: "eq", value: "flute") ``` data: sql: | ```sql create table orchestral_sections (id int8 primary key, name text); create table instruments ( id int8 primary key, section_id int8 not null references orchestral_sections, name text ); insert into orchestral_sections (id, name) values (1, 'strings'), (2, 'woodwinds'); insert into instruments (id, section_id, name) values (1, 2, 'flute'), (2, 1, 'violin'); ``` response: | ```json { "data": [ { "name": "woodwinds", "cities": [ { "name": "flute" } ] } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true - id: eq title: eq() description: | Match only rows where `column` is equal to `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .eq("name", value: "The shire") ``` response: | ```json { "data": [ { "name": "The shire", "country_id": 554 } ], "status": 200, "statusText": "OK" } ``` - id: neq title: neq() description: | Match only rows where `column` is not equal to `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .neq("name", value: "Paris") ``` - id: gt title: gt() description: | Match only rows where `column` is greater than `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .gt("country_id", value: 250) ``` - id: gte title: gte() description: | Match only rows where `column` is greater than or equal to `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .gte("country_id", value: 250) ``` - id: lt title: lt() description: | Match only rows where `column` is less than `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .lt("country_id", value: 250) ``` - id: lte title: lte() description: | Match only rows where `column` is less than or equal to `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .lte("country_id", value: 250) ``` - id: like title: like() description: | Match only rows where `column` matches `pattern` case-sensitively. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .like("name", pattern: "%la%") ``` - id: ilike title: ilike() description: | Match only rows where `column` matches `pattern` case-insensitively. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .ilike("name", pattern: "%la%") ``` - id: is title: is() description: | Match only rows where `column` IS `value`. For non-null values, this is equivalent to the `eq` filter. For null values, use this instead of `eq`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .is("name", value: nil) ``` - id: in title: in() description: | Match only rows where `column` is included in the `values` array. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .in("name", values: ["Rio de Janeiro", "San Francisco"]) ``` - id: notin title: notIn() description: | Match only rows where `column` is not included in the `values` array. The negation of `in()`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, country_id") .notIn("name", values: ["Rio de Janeiro", "San Francisco"]) ``` - id: contains title: contains() description: | Match only rows where `column` contains every element appearing in `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, main_exports") .contains("main_exports", value: ["oil"]) ``` - id: containedBy title: containedBy() description: | Match only rows where every element in `column` appears in `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, main_exports") .containedBy("main_exports", value: ["cars", "food", "machine"]) ``` - id: overlaps title: overlaps() description: | Match only rows where `column` and `value` have an element in common. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("cities") .select("name, main_exports") .overlaps("main_exports", value: ["exports", "tourism"]) ``` - id: rangeLt title: rangeLt() description: | Match only rows where every element in `column` is less than any element in `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("reservations") .select() .rangeLt("during", value: "[2000-01-02 08:30, 2000-01-02 09:30)") ``` - id: rangeGt title: rangeGt() description: | Match only rows where every element in `column` is greater than any element in `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("reservations") .select() .rangeGt("during", value: "[2000-01-02 08:30, 2000-01-02 09:30)") ``` - id: rangeGte title: rangeGte() description: | Match only rows where every element in `column` is either contained in `value` or greater than any element in `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("reservations") .select() .rangeGte("during", value: "[2000-01-02 08:30, 2000-01-02 09:30)") ``` - id: rangeLte title: rangeLte() description: | Match only rows where every element in `column` is either contained in `value` or less than any element in `value`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("reservations") .select() .rangeLte("during", value: "[2000-01-02 08:30, 2000-01-02 09:30)") ``` - id: rangeAdjacent title: rangeAdjacent() description: | Match only rows where `column` is adjacent to the `value` range. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("reservations") .select() .rangeAdjacent("during", value: "[2000-01-02 08:30, 2000-01-02 09:30)") ``` - id: textSearch title: textSearch() description: | Match only rows where `column` matches the query string in `query`. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("quotes") .select("catchphrase") .textSearch("catchphrase", query: "'fat' & 'cat'", config: "english") ``` - id: using-modifiers title: Using Modifiers description: | Filters work on the row level—they allow you to return rows that only match certain conditions without changing the shape of the rows. Modifiers are everything that don't fit that definition—allowing you to change the format of the response (e.g. returning a CSV string). Modifiers must be specified after filters. Some modifiers only apply for queries that return rows (e.g., `select()` or `rpc()` on a function that returns a table response). - id: db-modifiers-select title: select() description: | Perform a SELECT on the query result. examples: - id: with-upsert name: With `upsert()` code: | ```swift try await supabase .from("instruments") .upsert(InstrumentModel(id: 1, name: "piano")) .select() .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'harpsichord'); ``` response: | ```json { "data": [ { "id": 1, "name": "piano" } ], "status": 201, "statusText": "Created" } ``` hideCodeBlock: true isSpotlight: true - id: order title: order() description: | Order the query result by column. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("instruments") .select("id, name") .order("id", ascending: false) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```swifton { "data": [ { "id": 3, "name": "cello" }, { "id": 2, "name": "viola" }, { "id": 1, "name": "viola" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: on-a-foreign-table name: On a foreign table code: | ```swift try await supabase .from("orchestral_sections") .select( """ name, instruments ( name ) """ ) .order("name", ascending: false, referencedTable: "instruments") .execute() ``` data: sql: | ```sql create table orchestral_sections (id int8 primary key, name text); create table instruments ( id int8 primary key, section_id int8 not null references orchestral_sections, name text ); insert into orchestral_sections (id, name) values (1, 'strings'), (2, 'woodwinds'); insert into instruments (id, section_id, name) values (1, 1, 'harp'), (2, 1, 'violin'); ``` response: | ```swifton { "data": [ { "name": "strings", "cities": [ { "name": "violin" }, { "name": "harp" } ] }, { "name": "woodwinds", "cities": [] } ], "status": 200, "statusText": "OK" } ``` description: | Ordering on foreign tables doesn't affect the ordering of the parent table. hideCodeBlock: true - id: order-parent-table-by-a-referenced-table name: Order parent table by a referenced table code: | ```swift try await supabase .from("instruments") .select( """ name, section:orchestral_sections ( name ) """ ) .order("section(name)", ascending: true) ``` data: sql: | ```sql create table orchestral_sections (id int8 primary key, name text); create table instruments ( id int8 primary key, section_id int8 not null references orchestral_sections, name text ); insert into orchestral_sections (id, name) values (1, 'strings'), (2, 'woodwinds'); insert into instruments (id, section_id, name) values (1, 1, 'violin'), (2, 2, 'flute'); ``` response: | ```swifton { "data": [ { "name": "violin", "country": { "name": "strings" } }, { "name": "flute", "country": { "name": "woodwinds" } } ], "status": 200, "statusText": "OK" } ``` description: | Ordering with `referenced_table(col)` affects the ordering of the parent table. hideCodeBlock: true - id: limit title: limit() description: | Limit the query result by count. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("instruments") .select("id, name") .limit(1) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": [ { "name": "violin" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: on-a-foreign-table name: On a foreign table code: | ```swift try await supabase .from("orchestral_sections") .select( """ name, instruments ( name ) """ ) .limit(1, referencedTable: "instruments") .execute() ``` data: sql: | ```sql create table orchestral_sections (id int8 primary key, name text); create table instruments ( id int8 primary key, section_id int8 not null references orchestral_sections, name text ); insert into orchestral_sections (id, name) values (1, 'woodwinds'); insert into instruments (id, section_id, name) values (1, 1, 'flute'), (2, 1, 'oboe'); ``` response: | ```json { "data": [ { "name": "woodwinds", "cities": [ { "name": "flute" } ] } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true - id: range title: range() description: | Limit the query result by from and to inclusively. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("instruments") .select("name") .range(from: 0, to: 1) .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```swifton { "data": [ { "name": "violin" }, { "name": "viola" } ], "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: max-affected title: maxAffected() description: | Set the maximum number of rows that can be affected by the query. Only available in PostgREST v13+ and only works with PATCH, DELETE methods and RPC calls. notes: | - This method sets `handling=strict` and `max-affected=` headers for row limit enforcement. - Only use with UPDATE (PATCH), DELETE operations, or RPC calls that modify data. - If the query would affect more rows than specified, PostgREST will return an error. - This is useful for preventing accidental bulk updates or deletes. examples: - id: with-update name: With `update()` code: | ```swift try await supabase .from("users") .update(["status": "active"]) .eq("id", value: 1) .maxAffected(1) .execute() ``` description: | Ensure that only one row is updated. If the query would update more than one row, an error is returned. isSpotlight: true - id: with-delete name: With `delete()` code: | ```swift try await supabase .from("users") .delete() .in("id", values: [1, 2, 3]) .maxAffected(3) .execute() ``` description: | Ensure that only three rows are deleted. If the query would delete more than three rows, an error is returned. - id: with-rpc name: With `rpc()` code: | ```swift try await supabase .rpc("delete_inactive_users") .maxAffected(10) .execute() ``` description: | Ensure that the RPC call affects at most 10 rows. Useful for limiting the impact of functions. - id: dry-run title: dryRun() description: | Executes the mutation but rolls back the transaction instead of committing it, so no changes are persisted. notes: | - The mutation runs and its result (including side effects such as triggers) is returned in the response, but the transaction is rolled back afterward. - Useful for testing mutations without touching real data. - Requires PostgREST's `db-tx-end` setting to allow client-controlled transaction rollback. examples: - id: with-update name: With `update()` isSpotlight: true code: | ```swift try await supabase .from("todos") .update(["done": true]) .eq("id", value: 1) .select() .dryRun() .execute() // Row is not actually updated in the database. ``` - id: single title: single() description: | By default PostgREST returns all JSON results in an array, even when there is only one item, use `single()` to return the first object unenclosed by an array. examples: - id: with-select name: With `select()` code: | ```swift try await supabase .from("instruments") .select("name") .limit(1) .single() .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": { "name": "violin" }, "status": 200, "statusText": "OK" } ``` hideCodeBlock: true isSpotlight: true - id: maybe-single title: maybeSingle() description: | Like `single()`, this sets the `application/vnd.pgrst.object+json` accept header so the server enforces a single result. Unlike `single()`, when the query does not match exactly one row the resulting `PGRST116` error is not thrown — the response `value` is `nil` instead. notes: | - PostgREST returns `PGRST116` both when zero rows match and when more than one row matches. `maybeSingle()` returns `nil` for either case; use `single()` for the strict variant that always throws when the query does not match exactly one row. examples: - id: with-select name: With `select()` isSpotlight: true code: | ```swift let todo: Todo? = try await supabase .from("todos") .select() .eq("id", value: 42) .maybeSingle() .execute() .value ``` - id: csv title: csv() examples: - id: return-data-as-csv name: Return data as CSV code: | ```swift try await supabase .from("instruments") .select() .csv() .execute() ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ```json { "data": "id,name\n1,violin\n2,viola\n3,cello", "status": 200, "statusText": "OK" } ``` description: | By default, the data is returned in JSON format, but can also be returned as Comma Separated Values. hideCodeBlock: true isSpotlight: true - id: strip-nulls title: stripNulls() description: | Strip null values from the response. notes: | - Requires PostgREST 11.2.0 or later. - Cannot be combined with `.csv()`. examples: - id: strip-nulls name: Strip null values isSpotlight: true code: | ```swift let data = try await supabase .from("characters") .select() .stripNulls() .execute() .value ``` - id: explain title: Using Explain description: | For debugging slow queries, you can get the [Postgres `EXPLAIN` execution plan](https://www.postgresql.org/docs/current/sql-explain.html) of a query using the `explain()` method. This works on any query, even for `rpc()` or writes. Explain is not enabled by default as it can reveal sensitive information about your database. It's best to only enable this for testing environments but if you wish to enable it for production you can provide additional protection by using a `pre-request` function. Follow the [Performance Debugging Guide](/docs/guides/database/debugging-performance) to enable the functionality on your project. notes: | The `format` parameter accepts an `ExplainFormat` value — either `.text` (default, human-readable) or `.json` (machine-readable JSON). examples: - id: get-execution-plan name: Get the execution plan code: | ```swift try await supabase .from("instruments") .select() .explain() .execute() .value ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ``` Aggregate (cost=33.34..33.36 rows=1 width=112) -> Limit (cost=0.00..18.33 rows=1000 width=40) -> Seq Scan on instruments (cost=0.00..22.00 rows=1200 width=40) ``` description: | By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter. hideCodeBlock: true isSpotlight: true - id: get-execution-plan-with-analyze-and-verbose name: Get the execution plan with analyze and verbose code: | ```swift try await supabase .from("instruments") .select() .explain( analyze: true, verbose: true ) .execute() .value ``` data: sql: | ```sql create table instruments (id int8 primary key, name text); insert into instruments (id, name) values (1, 'violin'), (2, 'viola'), (3, 'cello'); ``` response: | ``` Aggregate (cost=33.34..33.36 rows=1 width=112) (actual time=0.041..0.041 rows=1 loops=1) Output: NULL::bigint, count(ROW(instruments.id, instruments.name)), COALESCE(json_agg(ROW(instruments.id, instruments.name)), '[]'::json), NULLIF(current_setting('response.headers'::text, true), ''::text), NULLIF(current_setting('response.status'::text, true), ''::text) -> Limit (cost=0.00..18.33 rows=1000 width=40) (actual time=0.005..0.006 rows=3 loops=1) Output: instruments.id, instruments.name -> Seq Scan on public.instruments (cost=0.00..22.00 rows=1200 width=40) (actual time=0.004..0.005 rows=3 loops=1) Output: instruments.id, instruments.name Query Identifier: -4730654291623321173 Planning Time: 0.407 ms Execution Time: 0.119 ms ``` description: | By default, the data is returned in TEXT format, but can also be returned as JSON by using the `format` parameter. hideCodeBlock: true isSpotlight: false - id: get-execution-plan-json-format name: Get the execution plan as JSON code: | ```swift try await supabase .from("instruments") .select() .explain(format: .json) .execute() .value ``` description: | Use `format: .json` to get a machine-readable JSON execution plan. The `ExplainFormat` type accepts `.text` (default) or `.json`. isSpotlight: false - id: invoke title: invoke() description: | Invoke a Supabase Edge Function. notes: | - Requires an Authorization header. - When you pass in a body to your function, we automatically attach the Content-Type header for `String`, and `Data`. If it doesn't match any of these types we assume the payload is `json`, serialize it and attach the `Content-Type` header as `application/json`. You can override this behaviour by passing in a `Content-Type` header of your own. - When a region is specified, both the `x-region` header and `forceFunctionRegion` query parameter are set to ensure proper function routing. - By default, function invocations use a 150-second idle timeout. You can override this per-call by passing `timeoutInterval` to `FunctionInvokeOptions`. This only controls the client's request timeout — it cannot extend function execution beyond the platform's [150-second gateway idle timeout](/docs/guides/functions/limits), after which a 504 Gateway Timeout is returned regardless of the value passed. examples: - id: invocation-with-decodable name: Invocation with `Decodable` response isSpotlight: true code: | ```swift struct Response: Decodable { // Expected response definition } let response: Response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( body: ["foo": "bar"] ) ) ``` - id: invocation-with-custom-response name: Invocation with custom response isSpotlight: true code: | ```swift let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( body: ["foo": "bar"] ), decode: { data, response in String(data: data, encoding: .utf8) } ) print(type(of: response)) // String? ``` - id: invocation-with-streamed-response name: Invocation with streamed response description: | - Use this method to return an `AsyncStream` when the function returns `Content-Type` = `text/event-stream`. isSpotlight: true code: | ```swift var response = Data() for try await data try await supabase.functions._invokeWithStreamedResponse("hello") { response.append(data) } ``` - id: error-handling name: Error handling description: | A `FunctionsError` error is returned if your function throws an error, `FunctionsRelayError` if the Supabase Relay has an error processing your function and `FunctionsFetchError` if there is a network error in calling your function. - `httpError(code: Int, data: Data)` in case a non-2xx status code is returned by the edge function. - `relayError` in case the Supabase Relay has an error processing your function. isSpotlight: true code: | ```swift do { let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( body: ["foo": "bar"] ) ) } catch FunctionsError.httpError(let code, let data) { print("Function returned code \(code) with response \(String(data: data, encoding: .utf8) ?? "")") } catch FunctionsError.relayError { print("Relay error") } catch { print("Other error: \(error.localizedDescription)") } ``` - id: passing-custom-headers name: Passing custom headers description: | You can pass custom headers to your function. Note: supabase-swift automatically passes the `Authorization` header with the signed-in user's JWT. isSpotlight: true code: | ```swift let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( headers: [ "my-custom-header": "my-custom-header-value" ] ) ) ``` - id: regional-invocation name: Invoking a Function in the UsEast1 region description: | Here are the available regions: - `FunctionRegion.any` - `FunctionRegion.apNortheast1` - `FunctionRegion.apNortheast2` - `FunctionRegion.apSouth1` - `FunctionRegion.apSoutheast1` - `FunctionRegion.apSoutheast2` - `FunctionRegion.caCentral1` - `FunctionRegion.euCentral1` - `FunctionRegion.euWest1` - `FunctionRegion.euWest2` - `FunctionRegion.euWest3` - `FunctionRegion.saEast1` - `FunctionRegion.usEast1` - `FunctionRegion.usWest1` - `FunctionRegion.usWest2` isSpotlight: true code: | ```swift let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( body: ["foo": "bar"], region: .usEast1 ) ) ``` - id: calling-with-delete-verb name: Calling with DELETE HTTP verb description: | You can also set the HTTP verb to `DELETE` when calling your Edge Function. isSpotlight: true code: | ```swift let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( method: .delete, headers: [ "my-custom-header": "my-custom-header-value" ], body: ["foo": "bar"] ) ) ``` - id: calling-with-get-verb name: Calling with GET HTTP verb description: | You can also set the HTTP verb to `GET` when calling your Edge Function. isSpotlight: true code: | ```swift let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( method: .get, headers: [ "my-custom-header": "my-custom-header-value" ] ) ) ``` - id: additional-query-params name: Pass additional query params description: | You can pass additional query params when invoking a function. isSpotlight: true code: | ```swift let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( query: [URLQueryItem(name: "key", value: "value")] ) ) ``` - id: invocation-with-timeout-override name: Invocation with a custom timeout description: | Override the default 150-second idle timeout for a single invocation by passing `timeoutInterval`. isSpotlight: true code: | ```swift let response = try await supabase.functions .invoke( "hello", options: FunctionInvokeOptions( body: ["foo": "bar"], timeoutInterval: 30 ) ) ``` - id: subscribe title: on().subscribe() notes: | - By default, Broadcast and Presence are enabled for all projects. - By default, listening to database changes is disabled for new projects due to database performance and security concerns. You can turn it on by managing Realtime's [replication](/docs/guides/api#realtime-api-overview). - You can receive the "previous" data for updates and deletes by setting the table's `REPLICA IDENTITY` to `FULL` (e.g., `ALTER TABLE your_table REPLICA IDENTITY FULL;`). - Row level security is not applied to delete statements. When RLS is enabled and replica identity is set to full, only the primary key is sent to clients. - Use AsyncStream or callbacks for listening to changes. - Presence and Postgres change callbacks must be registered **before** calling `subscribe()`. Attempting to add them after the channel is subscribing or subscribed will trigger a warning and the callback will be rejected. examples: - id: listen-to-broadcast name: Listen to broadcast messages isSpotlight: true code: | ```swift let channel = supabase.channel("room1") let broadcastStream = channel.broadcastStream(event: "cursor-pos") await channel.subscribe() Task { for await message in broadcastStream { print("Cursor position received", message) } } await channel.broadcast( event: "cursor-pos", message: [ "x": .double(.random(in: 0...1)), "y": .double(.random(in: 0...1)) ] ) ``` - id: listen-to-broadcast-callback name: Listen to broadcast messages using callback isSpotlight: true code: | ```swift let channel = supabase.channel("room1") let subscription = channel.onBroadcast(event: "cursor-pos") { message in print("Cursor position received", message) } await channel.subscribe() await channel.broadcast( event: "cursor-pos", message: [ "x": .double(.random(in: 0...1)), "y": .double(.random(in: 0...1)) ] ) // remove subscription some time later subscription.cancel() ``` - id: broadcast-with-replay name: Configure broadcast with replay description: | Replay allows you to receive messages that were broadcast since a specific timestamp. The `meta` field in the message payload will indicate if a message is being replayed. isSpotlight: false code: | ```swift let config = RealtimeJoinConfig( broadcast: BroadcastJoinConfig( acknowledgeBroadcasts: true, receiveOwnBroadcasts: true, replay: ReplayOption( since: 1234567890, limit: 100 ) ) ) let channel = supabase.channel("my-channel", config: config) channel.onBroadcast { message in if let meta = message.payload["meta"] as? [String: Any], let replayed = meta["replayed"] as? Bool, replayed { print("Replayed message: \(meta["id"] ?? "")") } } await channel.subscribe() ``` - id: listen-to-presence-updates name: Listen to presence updates code: | ```swift struct PresenceState: Codable { let username: String } let channel = supabase.channel("channelId") let presenceChange = channel.presenceChange() await channel.subscribe() Task { for await presence in presenceChange { let joins = try presence.decodeJoins(as: PresenceState.self) let leaves = try presence.decodeLeaves(as: PresenceState.self) } } // Send your own state try await channel.track(PresenceState(username: "John")) - id: listen-to-presence-updates-callback name: Listen to presence updates using callback code: | ```swift struct PresenceState: Codable { let username: String } let channel = supabase.channel("channelId") let subscription = channel.onPresenceChange() { presence in do { let joins = try presence.decodeJoins(as: PresenceState.self) let leaves = try presence.decodeLeaves(as: PresenceState.self) } catch { // Handle decoding error } } await channel.subscribe() // Send your own state try await channel.track(PresenceState(username: "John")) // remove subscription some time later subscription.cancel() ``` - id: listen-to-all-database-changes name: Listen to all database changes code: | ```swift let channel = supabase.channel("channelId") let changeStream = channel.postgresChange(AnyAction.self, schema: "public") await channel.subscribe() for await change in changeStream { switch change { case .delete(let action): print("Deleted: \(action.oldRecord)") case .insert(let action): print("Inserted: \(action.record)") case .select(let action): print("Selected: \(action.record)") case .update(let action): print("Updated: \(action.oldRecord) with \(action.record)") } } ``` - id: listen-to-all-database-changes-callback name: Listen to all database changes using callback code: | ```swift let channel = supabase.channel("channelId") let subscription = channel.onPostgresChange(AnyAction.self, schema: "public") { change in switch change { case .delete(let action): print("Deleted: \(action.oldRecord)") case .insert(let action): print("Inserted: \(action.record)") case .select(let action): print("Selected: \(action.record)") case .update(let action): print("Updated: \(action.oldRecord) with \(action.record)") } } await channel.subscribe() // remove subscription some time later subscription.cancel() ``` - id: listen-to-a-specific-table name: Listen to a specific table code: | ```swift let channel = supabase.channel("channelId") let changeStream = channel.postgresChange( AnyAction.self, schema: "public", table: "users" ) await channel.subscribe() for await change in changeStream { switch change { case .delete(let action): print("Deleted: \(action.oldRecord)") case .insert(let action): print("Inserted: \(action.record)") case .select(let action): print("Selected: \(action.record)") case .update(let action): print("Updated: \(action.oldRecord) with \(action.record)") } } ``` - id: listen-to-a-specific-table-callback name: Listen to a specific table using callback code: | ```swift let channel = supabase.channel("channelId") let subscription = channel.onPostgresChange( AnyAction.self, schema: "public", table: "users" ) { change in switch change { case .delete(let action): print("Deleted: \(action.oldRecord)") case .insert(let action): print("Inserted: \(action.record)") case .select(let action): print("Selected: \(action.record)") case .update(let action): print("Updated: \(action.oldRecord) with \(action.record)") } } await channel.subscribe() // remove subscription some time later subscription.cancel() ``` - id: listen-to-inserts name: Listen to inserts code: | ```swift let channel = supabase.channel("channelId") let insertions = channel.postgresChange( InsertAction.self, schema: "public", table: "users" ) await channel.subscribe() for await insert in insertions { print("Inserted: \(insert.record)") } ``` - id: listen-to-inserts-callback name: Listen to inserts using callback code: | ```swift let channel = supabase.channel("channelId") let subscription = channel.onPostgresChange( InsertAction.self, schema: "public", table: "users" ) { insert in print("Inserted: \(insert.record)") } await channel.subscribe() // remove subscription some time later subscription.cancel() ``` - id: listen-to-updates name: Listen to updates description: | By default, Supabase will send only the updated record. If you want to receive the previous values as well you can enable full replication for the table you are listening too: ```sql alter table "your_table" replica identity full; ``` code: | ```swift let channel = supabase.channel("channelId") let updates = channel.postgresChange( UpdateAction.self, schema: "public", table: "users" ) await channel.subscribe() for await update in updates { print("Updated: \(update.oldRecord) with \(update.record)") } ``` - id: listen-to-updates-callback name: Listen to updates using callback description: | By default, Supabase will send only the updated record. If you want to receive the previous values as well you can enable full replication for the table you are listening too: ```sql alter table "your_table" replica identity full; ``` code: | ```swift let channel = supabase.channel("channelId") let subscription = channel.onPostgresChange( UpdateAction.self, schema: "public", table: "users" ) { update in print("Updated: \(update.oldRecord) with \(update.record)") } await channel.subscribe() // remove subscription some time later subscription.cancel() ``` - id: listen-to-deletes name: Listen to deletes description: | By default, Supabase does not send deleted records. If you want to receive the deleted record you can enable full replication for the table you are listening too: ```sql alter table "your_table" replica identity full; ``` code: | ```swift let channel = supabase.channel("channelId") let deletions = channel.postgresChange( DeleteAction.self, schema: "public", table: "users" ) await channel.subscribe() for await deletion in deletions { print("Deleted: \(deletion.oldRecord)") } ``` - id: listen-to-deletes-callback name: Listen to deletes using callback description: | By default, Supabase does not send deleted records. If you want to receive the deleted record you can enable full replication for the table you are listening too: ```sql alter table "your_table" replica identity full; ``` code: | ```swift let channel = supabase.channel("channelId") let subscription = channel.onPostgresChange( DeleteAction.self, schema: "public", table: "users" ) { deletion in print("Deleted: \(deletion.oldRecord)") } await channel.subscribe() // remove subscription some time later subscription.cancel() ``` - id: listening-to-row-level-changes name: Listen to row level changes code: | ```swift let channel = supabase.channel("channelId") let deletions = channel.postgresChange( DeleteAction.self, schema: "public", table: "users", filter: .eq(id, value: 1) ) await channel.subscribe() for await deletion in deletions { print("Deleted: \(deletion.oldRecord)") } ``` - id: listening-to-row-level-changes-callback name: Listen to row level changes using callback code: | ```swift let channel = supabase.channel("channelId") let subscription = channel.onPostgresChange( DeleteAction.self, schema: "public", table: "users", filter: .eq(id, value: 1) ) { deletion in print("Deleted: \(deletion.oldRecord)") } await channel.subscribe() // remove subscription some time later subscription.cancel() ``` - id: remove-channel description: | Unsubscribes and removes Realtime channel from Realtime client. title: 'removeChannel()' notes: | - Removing a channel is a great way to maintain the performance of your project's Realtime service as well as your database if you're listening to Postgres changes. - Supabase will automatically handle cleanup 30 seconds after a client is disconnected, but unused channels may cause degradation as more clients are simultaneously subscribed. - If you removed all channels, the client automatically disconnects from the Realtime websocket. This can be disabled in the Realtime config by setting `disconnectOnNoSubscriptions` to false. examples: - id: removes-a-channel name: Remove a channel isSpotlight: true code: | ```swift let channel = supabase.channel("channelId") //... await supabase.removeChannel(channel) ``` - id: unsubscribe-channel name: Unsubscribe from a channel isSpotlight: true code: | ```swift let channel = supabase.channel("channelId") //... await channel.unsubscribe() ``` - id: remove-all-channels title: removeAllChannels() $ref: '@supabase/supabase-js.index.SupabaseClient.removeAllChannels' notes: | Unsubscribes and removes all Realtime channels from Realtime client. - Removing channels is a great way to maintain the performance of your project's Realtime service as well as your database if you're listening to Postgres changes. Supabase will automatically handle cleanup 30 seconds after a client is disconnected, but unused channels may cause degradation as more clients are simultaneously subscribed. - If you removed all channels, the client automatically disconnects from the Realtime websocket. This can be disabled in the Realtime config by setting `disconnectOnNoSubscriptions` to false. examples: - id: remove-all-channels name: Remove all channels isSpotlight: true code: | ```swift await supabase.removeAllChannels() ``` - id: get-channels title: getChannels() $ref: '@supabase/supabase-js.index.SupabaseClient.getChannels' notes: | Returns all Realtime channels. examples: - id: get-all-channels name: Get all channels isSpotlight: true code: | ```swift let channels = supabase.channels ``` - id: file-buckets title: 'Overview' notes: | This section contains methods for working with File Buckets. # - id: analytics-buckets # title: 'Overview' # notes: | # This section contains methods for working with Analytics Buckets. # - id: vector-buckets # title: 'Overview' # notes: | # This section contains methods for working with Vector Buckets. - id: list-buckets title: listBuckets() notes: | - RLS policy permissions required: - `buckets` table permissions: `select` - `objects` table permissions: none - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: list-buckets name: List buckets isSpotlight: true code: | ```swift try await supabase.storage .listBuckets() ``` - id: get-bucket title: getBucket() notes: | - RLS policy permissions required: - `buckets` table permissions: `select` - `objects` table permissions: none - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: get-bucket name: Get bucket isSpotlight: true code: | ```swift let bucket = try await supabase.storage .getBucket("avatars") ``` - id: create-bucket title: createBucket() notes: | - RLS policy permissions required: - `buckets` table permissions: `insert` - `objects` table permissions: none - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: create-bucket name: Create bucket isSpotlight: true code: | ```swift try await supabase.storage .createBucket( "avatars", options: BucketOptions( isPublic: false, allowedMimeTypes: ["image/png"], fileSizeLimit: 1024 ) ) ``` - id: empty-bucket title: emptyBucket() notes: | - RLS policy permissions required: - `buckets` table permissions: `select` - `objects` table permissions: `select` and `delete` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: empty-bucket name: Empty bucket isSpotlight: true code: | ```swift try await supabase.storage .emptyBucket("avatars") ``` - id: update-bucket title: updateBucket() notes: | - RLS policy permissions required: - `buckets` table permissions: `select` and `update` - `objects` table permissions: none - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: update-bucket name: Update bucket isSpotlight: true code: | ```swift try await supabase.storage .updateBucket( "avatars", options: BucketOptions( isPublic: false, fileSizeLimit: 1024, allowedMimeTypes: ["image/png"] ) ) ``` - id: delete-bucket title: deleteBucket() notes: | - RLS policy permissions required: - `buckets` table permissions: `select` and `delete` - `objects` table permissions: none - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: delete-bucket name: Delete bucket isSpotlight: true code: | ```swift try await supabase.storage .deleteBucket("avatars") ``` - id: from-upload title: from.upload() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: only `insert` when you are uploading new files and `select`, `insert` and `update` when you are upserting files - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: upload-file name: Upload file isSpotlight: true code: | ```swift let fileName = "avatar1.png" try await supabase.storage .from("avatars") .upload( path: "public/\(fileName)", file: fileData, options: FileOptions( cacheControl: "3600", contentType: "image/png", upsert: false ) ) ``` - id: from-update title: from.update() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `update` and `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: update-file name: Update file isSpotlight: true code: | ```swift let fileName = "avatar1.png" try await supabase.storage .from("avatars") .update( path: "public/\(fileName)", file: fileData, options: FileOptions( cacheControl: "3600", contentType: "image/png", upsert: true ) ) ``` - id: from-move title: from.move() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `update` and `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: move-file name: Move file isSpotlight: true code: | ```swift try await supabase .storage .from("avatars") .move(from: "public/avatar1.png", to: "private/avatar2.png") ``` - id: from-copy title: from.copy() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `insert` and `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: copy-file name: Copy file isSpotlight: true code: | ```swift try await supabase .storage .from("avatars") .copy(from: "public/avatar1.png", to: "private/avatar2.png") ``` - id: from-create-signed-url title: from.createSignedUrl() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: create-signed-url name: Create Signed URL isSpotlight: true code: | ```swift let signedURL = try await supabase.storage .from("avatars") .createSignedURL(path: "folder/avatar1.png", expiresIn: 60) ``` - id: create-signed-url-with-transformations name: Create a signed URL for an asset with transformations isSpotlight: true code: | ```swift let signedURL = try await supabase.storage .from("avatars") .createSignedURL( path: "folder/avatar1.png", expiresIn: 60, transform: TransformOptions( width: 100, height: 100 ) ) ``` - id: create-signed-url-with-download name: Create a signed URL which triggers the download of the asset isSpotlight: true code: | ```swift let signedURL = try await supabase.storage .from("avatars") .createSignedURL( path: "folder/avatar1.png", expiresIn: 60, download: .withOriginalName ) ``` note: | Use `.withOriginalName` to download with the original file name, or `.named("custom.png")` to specify a custom file name. The `download: Bool` overload still compiles but is disfavored. - id: from-create-signed-urls title: from.createSignedUrls() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: create-signed-urls name: Create Signed URLs isSpotlight: true code: | ```swift let urls = try await supabase .storage .from("avatars") .createSignedURLs(paths: ["folder/avatar1.png", "folder/avatar2.png"], expiresIn: 60) ``` - id: from-create-signed-upload-url title: from.createSignedUploadURL() description: | Create a signed upload URL that can be used to upload files to a bucket. notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `insert` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: create-signed-upload-url name: Create signed upload URL isSpotlight: true code: | ```swift let signedUploadUrl = try await supabase.storage .from("avatars") .createSignedUploadURL(path: "folder/avatar1.png") ``` - id: create-signed-upload-url-with-options name: Create signed upload URL with options code: | ```swift let signedUploadUrl = try await supabase.storage .from("avatars") .createSignedUploadURL( path: "folder/avatar1.png", options: CreateSignedUploadURLOptions( upsert: true ) ) ``` - id: from-upload-to-signed-url title: from.uploadToSignedUrl() description: | Upload a file to a bucket using a signed URL. notes: | - Use this method to upload files using a signed upload URL created with `createSignedUploadURL()`. examples: - id: upload-to-signed-url name: Upload to signed URL isSpotlight: true code: | ```swift let fileData = "Hello World".data(using: .utf8)! try await supabase.storage .from("avatars") .uploadToSignedURL( "folder/avatar1.png", token: "your-signed-token", data: fileData, options: FileOptions( contentType: "text/plain" ) ) ``` - id: upload-file-to-signed-url name: Upload file from URL to signed URL code: | ```swift let fileURL = URL(fileURLWithPath: "/path/to/file.txt") try await supabase.storage .from("avatars") .uploadToSignedURL( "folder/avatar1.png", token: "your-signed-token", fileURL: fileURL, options: FileOptions( contentType: "text/plain" ) ) ``` - id: from-get-public-url title: from.getPublicUrl() notes: | - The bucket needs to be set to public, either via [updateBucket()](/docs/reference/javascript/storage-updatebucket) or by going to Storage on [supabase.com/dashboard](https://supabase.com/dashboard), clicking the overflow menu on a bucket and choosing "Make public" - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: none - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: returns-the-url-for-an-asset-in-a-public-bucket name: Returns the URL for an asset in a public bucket isSpotlight: true code: | ```swift let publicURL = try supabase.storage .from("public-bucket") .getPublicURL(path: "folder/avatar1.png") ``` - id: transform-asset-in-public-bucket name: Returns the URL for an asset in a public bucket with transformations isSpotlight: true code: | ```swift let publicURL = try supabase.storage .from("public-bucket") .getPublicURL( path: "folder/avatar1.png", options: TransformOptions( width: 100, height: 100 ) ) ``` - id: download-asset-in-public-bucket name: Returns the URL which triggers the download of an asset in a public bucket isSpotlight: true code: | ```swift let publicURL = try supabase.storage .from("public-bucket") .getPublicURL( path: "folder/avatar1.png", download: .withOriginalName ) ``` note: | Use `.withOriginalName` to download with the original file name, or `.named("custom.png")` to specify a custom file name. The `download: Bool` overload still compiles but is disfavored. - id: from-download title: from.download() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: download-file name: Download file isSpotlight: true code: | ```swift let data = try await supabase.storage .from("avatars") .download(path: "folder/avatar1.png") ``` - id: download-file-with-transformations name: Download file with transformations isSpotlight: true code: | ```swift let data = try await supabase.storage .from("avatars") .download( path: "folder/avatar1.png", options: TransformOptions( width: 100, height: 100, quality: 80 ) ) ``` - id: from-remove title: from.remove() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `delete` and `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: delete-file name: Delete file isSpotlight: true code: | ```swift try await supabase.storage .from("avatars") .remove(paths: ["folder/avatar1.png"]) ``` - id: from-list title: from.list() notes: | - RLS policy permissions required: - `buckets` table permissions: none - `objects` table permissions: `select` - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works examples: - id: list-files-in-a-bucket name: List files in a bucket isSpotlight: true code: | ```swift let files = try await supabase.storage .from("avatars") .list( path: "folder", options: SearchOptions( limit: 100, offset: 0, sortBy: SortBy(column: "name", order: "asc") ) ) ``` - id: search-files-in-a-bucket name: Search files in a bucket code: | ```swift let files = try await supabase.storage .from("avatars") .list( path: "folder", options: SearchOptions( limit: 100, offset: 0, sortBy: SortBy(column: "name", order: "asc"), search: "jon" ) ) ```