diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..b5e6f234
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,38 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - "*"
+
+concurrency:
+ group: ci-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ example:
+ runs-on: macos-12
+ strategy:
+ matrix:
+ xcode: ["14.1"]
+ steps:
+ - uses: actions/checkout@v3
+ - name: Select Xcode ${{ matrix.xcode }}
+ run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
+ - name: Setup Secrets.swift
+ run: cp Examples/Examples/_Secrets.swift Examples/Examples/Secrets.swift
+ - name: Build example
+ run: make build-example
+
+ test-library:
+ runs-on: macos-12
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Secrets.swift
+ run: cp Examples/Examples/_Secrets.swift Examples/Examples/Secrets.swift
+ - name: Test library
+ run: make test-library
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 1d28cee2..689f7498 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,4 +93,7 @@ iOSInjectionProject/
/.build
/Packages
/*.xcodeproj
-/.swiftpm
\ No newline at end of file
+# /.swiftpm
+
+Secrets.swift
+.env
diff --git a/.swiftformat b/.swiftformat
new file mode 100644
index 00000000..fc594a28
--- /dev/null
+++ b/.swiftformat
@@ -0,0 +1,12 @@
+--swiftversion 5.7
+--binarygrouping none
+--decimalgrouping none
+--hexgrouping none
+--indent 2
+--octalgrouping none
+--semicolons never
+--wraparguments before-first
+--wrapcollections before-first
+--wrapparameters before-first
+--extensionacl on-declarations
+--maxwidth 100
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Supabase.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Supabase.xcscheme
new file mode 100644
index 00000000..2b4e3972
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/Supabase.xcscheme
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Examples.xcodeproj/project.pbxproj b/Examples/Examples.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..ab418d8c
--- /dev/null
+++ b/Examples/Examples.xcodeproj/project.pbxproj
@@ -0,0 +1,450 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 56;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 793895CA2954ABFF0044F2B8 /* ExamplesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793895C92954ABFF0044F2B8 /* ExamplesApp.swift */; };
+ 793895CC2954ABFF0044F2B8 /* RootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 793895CB2954ABFF0044F2B8 /* RootView.swift */; };
+ 793895CE2954AC000044F2B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 793895CD2954AC000044F2B8 /* Assets.xcassets */; };
+ 793895D22954AC000044F2B8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 793895D12954AC000044F2B8 /* Preview Assets.xcassets */; };
+ 794EF1222955F26A008C9526 /* AddTodoListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794EF1212955F26A008C9526 /* AddTodoListView.swift */; };
+ 794EF1242955F3DE008C9526 /* TodoListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 794EF1232955F3DE008C9526 /* TodoListRow.swift */; };
+ 7956405C2954AC3E0088A06F /* Supabase in Frameworks */ = {isa = PBXBuildFile; productRef = 7956405B2954AC3E0088A06F /* Supabase */; };
+ 7956405E2954ADE00088A06F /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7956405D2954ADE00088A06F /* Secrets.swift */; };
+ 795640602954AE140088A06F /* AuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7956405F2954AE140088A06F /* AuthView.swift */; };
+ 795640622955AD2B0088A06F /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795640612955AD2B0088A06F /* HomeView.swift */; };
+ 795640662955AE9C0088A06F /* TodoListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795640652955AE9C0088A06F /* TodoListView.swift */; };
+ 795640682955AEB30088A06F /* Models.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795640672955AEB30088A06F /* Models.swift */; };
+ 7956406A2955AFBD0088A06F /* ErrorText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 795640692955AFBD0088A06F /* ErrorText.swift */; };
+ 7956406D2955B3500088A06F /* SwiftUINavigation in Frameworks */ = {isa = PBXBuildFile; productRef = 7956406C2955B3500088A06F /* SwiftUINavigation */; };
+ 795640702955B5190088A06F /* IdentifiedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 7956406F2955B5190088A06F /* IdentifiedCollections */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ 793895C62954ABFF0044F2B8 /* Examples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 793895C92954ABFF0044F2B8 /* ExamplesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExamplesApp.swift; sourceTree = ""; };
+ 793895CB2954ABFF0044F2B8 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = ""; };
+ 793895CD2954AC000044F2B8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 793895CF2954AC000044F2B8 /* Examples.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Examples.entitlements; sourceTree = ""; };
+ 793895D12954AC000044F2B8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 794EF1212955F26A008C9526 /* AddTodoListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTodoListView.swift; sourceTree = ""; };
+ 794EF1232955F3DE008C9526 /* TodoListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoListRow.swift; sourceTree = ""; };
+ 7956405D2954ADE00088A06F /* Secrets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Secrets.swift; sourceTree = ""; };
+ 7956405F2954AE140088A06F /* AuthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthView.swift; sourceTree = ""; };
+ 795640612955AD2B0088A06F /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; };
+ 795640652955AE9C0088A06F /* TodoListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoListView.swift; sourceTree = ""; };
+ 795640672955AEB30088A06F /* Models.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Models.swift; sourceTree = ""; };
+ 795640692955AFBD0088A06F /* ErrorText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorText.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 793895C32954ABFF0044F2B8 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 795640702955B5190088A06F /* IdentifiedCollections in Frameworks */,
+ 7956406D2955B3500088A06F /* SwiftUINavigation in Frameworks */,
+ 7956405C2954AC3E0088A06F /* Supabase in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 793895BD2954ABFF0044F2B8 = {
+ isa = PBXGroup;
+ children = (
+ 793895C82954ABFF0044F2B8 /* Examples */,
+ 793895C72954ABFF0044F2B8 /* Products */,
+ 7956405A2954AC3E0088A06F /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 793895C72954ABFF0044F2B8 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 793895C62954ABFF0044F2B8 /* Examples.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 793895C82954ABFF0044F2B8 /* Examples */ = {
+ isa = PBXGroup;
+ children = (
+ 793895CD2954AC000044F2B8 /* Assets.xcassets */,
+ 7956405F2954AE140088A06F /* AuthView.swift */,
+ 793895CF2954AC000044F2B8 /* Examples.entitlements */,
+ 793895C92954ABFF0044F2B8 /* ExamplesApp.swift */,
+ 793895D02954AC000044F2B8 /* Preview Content */,
+ 793895CB2954ABFF0044F2B8 /* RootView.swift */,
+ 7956405D2954ADE00088A06F /* Secrets.swift */,
+ 795640612955AD2B0088A06F /* HomeView.swift */,
+ 795640652955AE9C0088A06F /* TodoListView.swift */,
+ 795640672955AEB30088A06F /* Models.swift */,
+ 795640692955AFBD0088A06F /* ErrorText.swift */,
+ 794EF1212955F26A008C9526 /* AddTodoListView.swift */,
+ 794EF1232955F3DE008C9526 /* TodoListRow.swift */,
+ );
+ path = Examples;
+ sourceTree = "";
+ };
+ 793895D02954AC000044F2B8 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 793895D12954AC000044F2B8 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+ 7956405A2954AC3E0088A06F /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 793895C52954ABFF0044F2B8 /* Examples */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 793895D52954AC000044F2B8 /* Build configuration list for PBXNativeTarget "Examples" */;
+ buildPhases = (
+ 793895C22954ABFF0044F2B8 /* Sources */,
+ 793895C32954ABFF0044F2B8 /* Frameworks */,
+ 793895C42954ABFF0044F2B8 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Examples;
+ packageProductDependencies = (
+ 7956405B2954AC3E0088A06F /* Supabase */,
+ 7956406C2955B3500088A06F /* SwiftUINavigation */,
+ 7956406F2955B5190088A06F /* IdentifiedCollections */,
+ );
+ productName = Examples;
+ productReference = 793895C62954ABFF0044F2B8 /* Examples.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 793895BE2954ABFF0044F2B8 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1410;
+ LastUpgradeCheck = 1410;
+ TargetAttributes = {
+ 793895C52954ABFF0044F2B8 = {
+ CreatedOnToolsVersion = 14.1;
+ };
+ };
+ };
+ buildConfigurationList = 793895C12954ABFF0044F2B8 /* Build configuration list for PBXProject "Examples" */;
+ compatibilityVersion = "Xcode 14.0";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 793895BD2954ABFF0044F2B8;
+ packageReferences = (
+ 7956406B2955B3500088A06F /* XCRemoteSwiftPackageReference "swiftui-navigation" */,
+ 7956406E2955B5190088A06F /* XCRemoteSwiftPackageReference "swift-identified-collections" */,
+ );
+ productRefGroup = 793895C72954ABFF0044F2B8 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 793895C52954ABFF0044F2B8 /* Examples */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 793895C42954ABFF0044F2B8 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 793895D22954AC000044F2B8 /* Preview Assets.xcassets in Resources */,
+ 793895CE2954AC000044F2B8 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 793895C22954ABFF0044F2B8 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 793895CC2954ABFF0044F2B8 /* RootView.swift in Sources */,
+ 7956406A2955AFBD0088A06F /* ErrorText.swift in Sources */,
+ 794EF1242955F3DE008C9526 /* TodoListRow.swift in Sources */,
+ 794EF1222955F26A008C9526 /* AddTodoListView.swift in Sources */,
+ 7956405E2954ADE00088A06F /* Secrets.swift in Sources */,
+ 795640682955AEB30088A06F /* Models.swift in Sources */,
+ 795640662955AE9C0088A06F /* TodoListView.swift in Sources */,
+ 795640602954AE140088A06F /* AuthView.swift in Sources */,
+ 795640622955AD2B0088A06F /* HomeView.swift in Sources */,
+ 793895CA2954ABFF0044F2B8 /* ExamplesApp.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 793895D32954AC000044F2B8 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 793895D42954AC000044F2B8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 793895D62954AC000044F2B8 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = Examples/Examples.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"Examples/Preview Content\"";
+ DEVELOPMENT_TEAM = ELTTE7K8TT;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
+ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 13.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.supabase.Examples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = auto;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 793895D72954AC000044F2B8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_ENTITLEMENTS = Examples/Examples.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "\"Examples/Preview Content\"";
+ DEVELOPMENT_TEAM = ELTTE7K8TT;
+ ENABLE_HARDENED_RUNTIME = YES;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
+ "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault;
+ "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 16.1;
+ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
+ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 13.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.supabase.Examples;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = auto;
+ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 793895C12954ABFF0044F2B8 /* Build configuration list for PBXProject "Examples" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 793895D32954AC000044F2B8 /* Debug */,
+ 793895D42954AC000044F2B8 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 793895D52954AC000044F2B8 /* Build configuration list for PBXNativeTarget "Examples" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 793895D62954AC000044F2B8 /* Debug */,
+ 793895D72954AC000044F2B8 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 7956406B2955B3500088A06F /* XCRemoteSwiftPackageReference "swiftui-navigation" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/pointfreeco/swiftui-navigation.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 0.4.5;
+ };
+ };
+ 7956406E2955B5190088A06F /* XCRemoteSwiftPackageReference "swift-identified-collections" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/pointfreeco/swift-identified-collections.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 0.5.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 7956405B2954AC3E0088A06F /* Supabase */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = Supabase;
+ };
+ 7956406C2955B3500088A06F /* SwiftUINavigation */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 7956406B2955B3500088A06F /* XCRemoteSwiftPackageReference "swiftui-navigation" */;
+ productName = SwiftUINavigation;
+ };
+ 7956406F2955B5190088A06F /* IdentifiedCollections */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 7956406E2955B5190088A06F /* XCRemoteSwiftPackageReference "swift-identified-collections" */;
+ productName = IdentifiedCollections;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 793895BE2954ABFF0044F2B8 /* Project object */;
+}
diff --git a/Examples/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/Examples/Examples.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/Examples/Examples/AddTodoListView.swift b/Examples/Examples/AddTodoListView.swift
new file mode 100644
index 00000000..9325b461
--- /dev/null
+++ b/Examples/Examples/AddTodoListView.swift
@@ -0,0 +1,46 @@
+//
+// AddTodoListView.swift
+// Examples
+//
+// Created by Guilherme Souza on 23/12/22.
+//
+
+import SwiftUI
+
+struct AddTodoListView: View {
+ @Binding var request: CreateTodoRequest
+ let completion: (Result) -> Void
+
+ var body: some View {
+ Section {
+ TextField("Description", text: $request.description)
+ Button("Save") {
+ Task { await saveButtonTapped() }
+ }
+ }
+ }
+
+ func saveButtonTapped() async {
+ do {
+ let createdTodo: Todo = try await supabase.database.from("todos")
+ .insert(values: request, returning: .representation)
+ .single()
+ .execute()
+ .value
+ completion(.success(createdTodo))
+ } catch {
+ completion(.failure(error))
+ }
+ }
+}
+
+struct AddTodoListView_Previews: PreviewProvider {
+ static var previews: some View {
+ AddTodoListView(request: .constant(.init(
+ description: "",
+ isComplete: false,
+ ownerID: UUID()
+ ))) { _ in
+ }
+ }
+}
diff --git a/Examples/Examples/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/Examples/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 00000000..eb878970
--- /dev/null
+++ b/Examples/Examples/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Examples/Examples/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/Examples/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..532cd729
--- /dev/null
+++ b/Examples/Examples/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,63 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "platform" : "ios",
+ "size" : "1024x1024"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Examples/Examples/Assets.xcassets/Contents.json b/Examples/Examples/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/Examples/Examples/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Examples/Examples/AuthView.swift b/Examples/Examples/AuthView.swift
new file mode 100644
index 00000000..bb040dda
--- /dev/null
+++ b/Examples/Examples/AuthView.swift
@@ -0,0 +1,92 @@
+//
+// AuthView.swift
+// Examples
+//
+// Created by Guilherme Souza on 22/12/22.
+//
+
+import GoTrue
+import SwiftUI
+
+final class AuthController: ObservableObject {
+ @Published var session: Session?
+
+ var currentUserID: UUID {
+ guard let id = session?.user.id else {
+ preconditionFailure("Required session.")
+ }
+
+ return id
+ }
+}
+
+struct AuthView: View {
+ enum Mode {
+ case signIn, signUp
+ }
+
+ @EnvironmentObject var auth: AuthController
+
+ @State var email = ""
+ @State var password = ""
+ @State var mode: Mode = .signIn
+ @State var error: Error?
+
+ var body: some View {
+ Form {
+ Section {
+ TextField("Email", text: $email)
+ .keyboardType(.emailAddress)
+ .textContentType(.emailAddress)
+ .autocorrectionDisabled()
+ .textInputAutocapitalization(.never)
+ SecureField("Password", text: $password)
+ .textContentType(.password)
+ .autocorrectionDisabled()
+ .textInputAutocapitalization(.never)
+ Button(mode == .signIn ? "Sign in" : "Sign up") {
+ Task {
+ await primaryActionButtonTapped()
+ }
+ }
+
+ if let error {
+ ErrorText(error)
+ }
+ }
+
+ Section {
+ Button(
+ mode == .signIn ? "Don't have an account? Sign up." :
+ "Already have an account? Sign in."
+ ) {
+ withAnimation {
+ mode = mode == .signIn ? .signUp : .signIn
+ }
+ }
+ }
+ }
+ }
+
+ func primaryActionButtonTapped() async {
+ do {
+ error = nil
+ switch mode {
+ case .signIn:
+ try await supabase.auth.signIn(email: email, password: password)
+ case .signUp:
+ try await supabase.auth.signUp(email: email, password: password)
+ }
+ } catch {
+ withAnimation {
+ self.error = error
+ }
+ }
+ }
+}
+
+struct AuthView_Previews: PreviewProvider {
+ static var previews: some View {
+ AuthView()
+ }
+}
diff --git a/Examples/Examples/ErrorText.swift b/Examples/Examples/ErrorText.swift
new file mode 100644
index 00000000..98a4bb29
--- /dev/null
+++ b/Examples/Examples/ErrorText.swift
@@ -0,0 +1,28 @@
+//
+// ErrorText.swift
+// Examples
+//
+// Created by Guilherme Souza on 23/12/22.
+//
+
+import SwiftUI
+
+struct ErrorText: View {
+ let error: Error
+
+ init(_ error: Error) {
+ self.error = error
+ }
+
+ var body: some View {
+ Text(String(describing: error))
+ .foregroundColor(.red)
+ .font(.footnote)
+ }
+}
+
+struct ErrorText_Previews: PreviewProvider {
+ static var previews: some View {
+ ErrorText(NSError())
+ }
+}
diff --git a/Examples/Examples/Examples.entitlements b/Examples/Examples/Examples.entitlements
new file mode 100644
index 00000000..f2ef3ae0
--- /dev/null
+++ b/Examples/Examples/Examples.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.files.user-selected.read-only
+
+
+
diff --git a/Examples/Examples/ExamplesApp.swift b/Examples/Examples/ExamplesApp.swift
new file mode 100644
index 00000000..cc005e13
--- /dev/null
+++ b/Examples/Examples/ExamplesApp.swift
@@ -0,0 +1,40 @@
+//
+// ExamplesApp.swift
+// Examples
+//
+// Created by Guilherme Souza on 22/12/22.
+//
+
+import Supabase
+import SwiftUI
+
+@main
+struct ExamplesApp: App {
+ @State var supabaseInitialized = false
+ @StateObject var auth = AuthController()
+
+ var body: some Scene {
+ WindowGroup {
+ main
+ }
+ }
+
+ @ViewBuilder
+ var main: some View {
+ if supabaseInitialized {
+ RootView()
+ .environmentObject(auth)
+ } else {
+ ProgressView()
+ .task {
+ await supabase.auth.initialize()
+ supabaseInitialized = true
+ }
+ }
+ }
+}
+
+let supabase = SupabaseClient(
+ supabaseURL: Secrets.supabaseURL,
+ supabaseKey: Secrets.supabaseAnonKey
+)
diff --git a/Examples/Examples/HomeView.swift b/Examples/Examples/HomeView.swift
new file mode 100644
index 00000000..aff182ec
--- /dev/null
+++ b/Examples/Examples/HomeView.swift
@@ -0,0 +1,31 @@
+//
+// HomeView.swift
+// Examples
+//
+// Created by Guilherme Souza on 23/12/22.
+//
+
+import SwiftUI
+
+struct HomeView: View {
+ var body: some View {
+ NavigationStack {
+ TodoListView()
+ .toolbar {
+ ToolbarItem(placement: .cancellationAction) {
+ Button("Sign out") {
+ Task {
+ try! await supabase.auth.signOut()
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+struct HomeView_Previews: PreviewProvider {
+ static var previews: some View {
+ HomeView()
+ }
+}
diff --git a/Examples/Examples/Models.swift b/Examples/Examples/Models.swift
new file mode 100644
index 00000000..f260099f
--- /dev/null
+++ b/Examples/Examples/Models.swift
@@ -0,0 +1,39 @@
+import Foundation
+
+struct Todo: Identifiable, Hashable, Decodable {
+ let id: UUID
+ var description: String
+ var isComplete: Bool
+ let createdAt: Date
+
+ enum CodingKeys: String, CodingKey {
+ case id
+ case description
+ case isComplete = "is_complete"
+ case createdAt = "created_at"
+ }
+}
+
+struct CreateTodoRequest: Encodable {
+ var description: String
+ var isComplete: Bool
+ var ownerID: UUID
+
+ enum CodingKeys: String, CodingKey {
+ case description
+ case isComplete = "is_complete"
+ case ownerID = "owner_id"
+ }
+}
+
+struct UpdateTodoRequest: Encodable {
+ var description: String?
+ var isComplete: Bool?
+ var ownerID: UUID
+
+ enum CodingKeys: String, CodingKey {
+ case description
+ case isComplete = "is_complete"
+ case ownerID = "owner_id"
+ }
+}
diff --git a/Examples/Examples/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/Examples/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/Examples/Examples/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Examples/Examples/RootView.swift b/Examples/Examples/RootView.swift
new file mode 100644
index 00000000..3954f561
--- /dev/null
+++ b/Examples/Examples/RootView.swift
@@ -0,0 +1,39 @@
+//
+// RootView.swift
+// Examples
+//
+// Created by Guilherme Souza on 22/12/22.
+//
+
+import GoTrue
+import SwiftUI
+
+struct RootView: View {
+ @State var authEvent: AuthChangeEvent?
+ @EnvironmentObject var auth: AuthController
+
+ var body: some View {
+ Group {
+ if authEvent == .signedOut {
+ AuthView()
+ } else {
+ HomeView()
+ }
+ }
+ .task {
+ for await event in supabase.auth.authEventChange {
+ withAnimation {
+ authEvent = event
+ }
+
+ auth.session = try? await supabase.auth.session
+ }
+ }
+ }
+}
+
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ RootView()
+ }
+}
diff --git a/Examples/Examples/TodoListRow.swift b/Examples/Examples/TodoListRow.swift
new file mode 100644
index 00000000..566e90fb
--- /dev/null
+++ b/Examples/Examples/TodoListRow.swift
@@ -0,0 +1,39 @@
+//
+// TodoListRow.swift
+// Examples
+//
+// Created by Guilherme Souza on 23/12/22.
+//
+
+import SwiftUI
+
+struct TodoListRow: View {
+ let todo: Todo
+ let completeTapped: () -> Void
+
+ var body: some View {
+ HStack {
+ Text(todo.description)
+ Spacer()
+ Button {
+ completeTapped()
+ } label: {
+ Image(systemName: todo.isComplete ? "checkmark.circle.fill" : "circle")
+ }
+ .buttonStyle(.plain)
+ }
+ }
+}
+
+struct TodoListRow_Previews: PreviewProvider {
+ static var previews: some View {
+ TodoListRow(
+ todo: .init(
+ id: UUID(),
+ description: "",
+ isComplete: false,
+ createdAt: .now
+ )
+ ) {}
+ }
+}
diff --git a/Examples/Examples/TodoListView.swift b/Examples/Examples/TodoListView.swift
new file mode 100644
index 00000000..b4017d09
--- /dev/null
+++ b/Examples/Examples/TodoListView.swift
@@ -0,0 +1,150 @@
+//
+// TodoListView.swift
+// Examples
+//
+// Created by Guilherme Souza on 23/12/22.
+//
+
+import IdentifiedCollections
+import SwiftUI
+import SwiftUINavigation
+
+struct TodoListView: View {
+ @EnvironmentObject var auth: AuthController
+
+ @State var todos: IdentifiedArrayOf = []
+ @State var error: Error?
+
+ @State var createTodoRequest: CreateTodoRequest?
+
+ var body: some View {
+ List {
+ if let error {
+ ErrorText(error)
+ }
+
+ IfLet($createTodoRequest) { $createTodoRequest in
+ AddTodoListView(request: $createTodoRequest) { result in
+ withAnimation {
+ self.createTodoRequest = nil
+
+ switch result {
+ case let .success(todo):
+ error = nil
+ _ = todos.insert(todo, at: 0)
+ case let .failure(error):
+ self.error = error
+ }
+ }
+ }
+ }
+
+ ForEach(todos) { todo in
+ TodoListRow(todo: todo) {
+ Task {
+ await toggleCompletion(of: todo)
+ }
+ }
+ }
+ .onDelete { indexSet in
+ Task {
+ await delete(at: indexSet)
+ }
+ }
+ }
+ .animation(.default, value: todos)
+ .navigationTitle("Todos")
+ .toolbar {
+ ToolbarItem(placement: .primaryAction) {
+ if createTodoRequest == nil {
+ Button {
+ withAnimation {
+ createTodoRequest = .init(
+ description: "",
+ isComplete: false,
+ ownerID: auth.currentUserID
+ )
+ }
+ } label: {
+ Label("Add", systemImage: "plus")
+ }
+ } else {
+ Button("Cancel", role: .cancel) {
+ withAnimation {
+ createTodoRequest = nil
+ }
+ }
+ }
+ }
+ }
+ .task {
+ do {
+ error = nil
+ todos = IdentifiedArrayOf(
+ uniqueElements: try await supabase.database.from("todos")
+ .select()
+ .execute()
+ .value as [Todo]
+ )
+ } catch {
+ self.error = error
+ }
+ }
+ }
+
+ func toggleCompletion(of todo: Todo) async {
+ var updatedTodo = todo
+ updatedTodo.isComplete.toggle()
+ todos[id: todo.id] = updatedTodo
+
+ do {
+ error = nil
+
+ let updateRequest = UpdateTodoRequest(
+ isComplete: updatedTodo.isComplete,
+ ownerID: auth.currentUserID
+ )
+ updatedTodo = try await supabase.database.from("todos")
+ .update(values: updateRequest, returning: .representation)
+ .eq(column: "id", value: updatedTodo.id)
+ .single()
+ .execute()
+ .value
+ todos[id: updatedTodo.id] = updatedTodo
+ } catch {
+ // rollback old todo.
+ todos[id: todo.id] = todo
+
+ self.error = error
+ }
+ }
+
+ func delete(at offset: IndexSet) async {
+ let oldTodos = todos
+
+ do {
+ error = nil
+ let todosToDelete = offset.map { todos[$0] }
+
+ todos.remove(atOffsets: offset)
+
+ try await supabase.database.from("todos")
+ .delete()
+ .in(column: "id", value: todosToDelete.map(\.id))
+ .execute()
+ } catch {
+ self.error = error
+
+ // rollback todos on error.
+ todos = oldTodos
+ }
+ }
+}
+
+struct TodoListView_Previews: PreviewProvider {
+ static var previews: some View {
+ NavigationStack {
+ TodoListView()
+ }
+ }
+}
diff --git a/Examples/Examples/_Secrets.swift b/Examples/Examples/_Secrets.swift
new file mode 100644
index 00000000..a36e8dd6
--- /dev/null
+++ b/Examples/Examples/_Secrets.swift
@@ -0,0 +1,6 @@
+import Foundation
+
+enum Secrets {
+ static let supabaseURL = URL(string: "SUPABASE_URL")!
+ static let supabaseAnonKey = "SUPABASE_ANON_KEY"
+}
diff --git a/Examples/supabase/.gitignore b/Examples/supabase/.gitignore
new file mode 100644
index 00000000..773c7c3e
--- /dev/null
+++ b/Examples/supabase/.gitignore
@@ -0,0 +1,3 @@
+# Supabase
+.branches
+.temp
diff --git a/Examples/supabase/config.toml b/Examples/supabase/config.toml
new file mode 100644
index 00000000..187a7f5d
--- /dev/null
+++ b/Examples/supabase/config.toml
@@ -0,0 +1,71 @@
+# A string used to distinguish different Supabase projects on the same host. Defaults to the working
+# directory name when running `supabase init`.
+project_id = "Examples"
+
+[api]
+# Port to use for the API URL.
+port = 54321
+# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API
+# endpoints. public and storage are always included.
+schemas = []
+# Extra schemas to add to the search_path of every request.
+extra_search_path = ["extensions"]
+# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size
+# for accidental or malicious requests.
+max_rows = 1000
+
+[db]
+# Port to use for the local database URL.
+port = 54322
+# The database major version to use. This has to be the same as your remote database's. Run `SHOW
+# server_version;` on the remote database to check.
+major_version = 15
+
+[studio]
+# Port to use for Supabase Studio.
+port = 54323
+
+# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they
+# are monitored, and you can view the emails that would have been sent from the web interface.
+[inbucket]
+# Port to use for the email testing server web interface.
+port = 54324
+smtp_port = 54325
+pop3_port = 54326
+
+[storage]
+# The maximum file size allowed (e.g. "5MB", "500KB").
+file_size_limit = "50MiB"
+
+[auth]
+# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
+# in emails.
+site_url = "http://localhost:3000"
+# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
+additional_redirect_urls = ["https://localhost:3000"]
+# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one
+# week).
+jwt_expiry = 3600
+# Allow/disallow new user signups to your project.
+enable_signup = true
+
+[auth.email]
+# Allow/disallow new user signups via email to your project.
+enable_signup = true
+# If enabled, a user will be required to confirm any email change on both the old, and new email
+# addresses. If disabled, only the new email is required to confirm.
+double_confirm_changes = true
+# If enabled, users need to confirm their email address before signing in.
+enable_confirmations = false
+
+# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
+# `discord`, `facebook`, `github`, `gitlab`, `google`, `twitch`, `twitter`, `slack`, `spotify`.
+[auth.external.apple]
+enabled = false
+client_id = ""
+secret = ""
+# Overrides the default auth redirectUrl.
+redirect_uri = ""
+# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure,
+# or any other third-party OIDC providers.
+url = ""
diff --git a/Examples/supabase/functions/hello-world/index.ts b/Examples/supabase/functions/hello-world/index.ts
new file mode 100644
index 00000000..a9bc1c1e
--- /dev/null
+++ b/Examples/supabase/functions/hello-world/index.ts
@@ -0,0 +1,25 @@
+// Follow this setup guide to integrate the Deno language server with your editor:
+// https://deno.land/manual/getting_started/setup_your_environment
+// This enables autocomplete, go to definition, etc.
+
+import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
+
+console.log("Hello from Functions!")
+
+serve(async (req) => {
+ const { name } = await req.json()
+ const data = {
+ message: `Hello ${name}!`,
+ }
+
+ return new Response(
+ JSON.stringify(data),
+ { headers: { "Content-Type": "application/json" } },
+ )
+})
+
+// To invoke:
+// curl -i --location --request POST 'http://localhost:54321/functions/v1/' \
+// --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \
+// --header 'Content-Type: application/json' \
+// --data '{"name":"Functions"}'
diff --git a/Examples/supabase/migrations/20221223094509_init.sql b/Examples/supabase/migrations/20221223094509_init.sql
new file mode 100644
index 00000000..0bb7e8ce
--- /dev/null
+++ b/Examples/supabase/migrations/20221223094509_init.sql
@@ -0,0 +1,14 @@
+create table todos (
+ id uuid default uuid_generate_v4 () primary key not null,
+ description text not null,
+ is_complete boolean not null,
+ created_at timestamptz default (now() at time zone 'utc'::text) not null,
+ owner_id uuid references auth.users (id) not null
+);
+
+alter table todos enable row level security;
+
+create policy "Allow access to owner only" on todos as permissive
+ for all to authenticated
+ using (auth.uid () = owner_id)
+ with check (auth.uid () = owner_id)
diff --git a/Examples/supabase/seed.sql b/Examples/supabase/seed.sql
new file mode 100644
index 00000000..e69de29b
diff --git a/Makefile b/Makefile
index e83e8ebe..e3867858 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,16 @@
+PLATFORM_IOS = iOS Simulator,name=iPhone 14 Pro Max
+
+test-library:
+ xcodebuild test \
+ -workspace supabase-swift.xcworkspace \
+ -scheme Supabase \
+ -destination platform="$(PLATFORM_IOS)" || exit 1;
+
+build-example:
+ xcodebuild build \
+ -workspace supabase-swift.xcworkspace \
+ -scheme Examples \
+ -destination platform="$(PLATFORM_IOS)" || exit 1;
+
format:
- swift format \
- --in-place \
- --ignore-unparsable-files \
- --recursive \
- ./Sources Package.swift
+ @swiftformat .
diff --git a/Package.swift b/Package.swift
index ecc72329..3a2edca9 100644
--- a/Package.swift
+++ b/Package.swift
@@ -16,14 +16,14 @@ let package = Package(
.library(
name: "Supabase",
targets: ["Supabase"]
- )
+ ),
],
dependencies: [
- .package(url: "https://github.com/supabase-community/gotrue-swift", from: "0.1.0"),
+ .package(url: "https://github.com/supabase-community/gotrue-swift", from: "1.0.0"),
.package(url: "https://github.com/supabase-community/storage-swift.git", from: "0.1.0"),
.package(url: "https://github.com/supabase-community/realtime-swift.git", from: "0.0.1"),
.package(url: "https://github.com/supabase-community/postgrest-swift", from: "1.0.0"),
- .package(url: "https://github.com/supabase-community/functions-swift", from: "0.2.0"),
+ .package(url: "https://github.com/supabase-community/functions-swift", from: "1.0.0"),
],
targets: [
.target(
diff --git a/Sources/Supabase/SupabaseClient.swift b/Sources/Supabase/SupabaseClient.swift
index 6de08419..9860f3d7 100644
--- a/Sources/Supabase/SupabaseClient.swift
+++ b/Sources/Supabase/SupabaseClient.swift
@@ -86,7 +86,7 @@ public class SupabaseClient {
let isPlatform =
supabaseURL.absoluteString.contains("supabase.co")
- || supabaseURL.absoluteString.contains("supabase.in")
+ || supabaseURL.absoluteString.contains("supabase.in")
if isPlatform {
let urlParts = supabaseURL.absoluteString.split(separator: ".")
functionsURL = URL(string: "\(urlParts[0]).functions.\(urlParts[1]).\(urlParts[2])")!
@@ -110,12 +110,7 @@ public class SupabaseClient {
extension SupabaseClient: APIClientDelegate {
public func client(_: APIClient, willSendRequest request: inout URLRequest) async throws {
- if let session = try? await auth.session {
- request.setValue(
- "\(session.tokenType) \(session.accessToken)",
- forHTTPHeaderField: "Authorization"
- )
- }
+ request = await adapt(request: request)
}
}
diff --git a/Tests/SupabaseTests/SupabaseClientTests.swift b/Tests/SupabaseTests/SupabaseClientTests.swift
index d51bcbf5..37ff44e5 100644
--- a/Tests/SupabaseTests/SupabaseClientTests.swift
+++ b/Tests/SupabaseTests/SupabaseClientTests.swift
@@ -1,15 +1,24 @@
-import XCTest
@testable import Supabase
+import XCTest
final class SupabaseClientTests: XCTestCase {
func testFunctionsURL() {
- var client = SupabaseClient(supabaseURL: URL(string: "https://project-ref.supabase.co")!, supabaseKey: "ANON_KEY")
+ var client = SupabaseClient(
+ supabaseURL: URL(string: "https://project-ref.supabase.co")!,
+ supabaseKey: "ANON_KEY"
+ )
XCTAssertEqual(client.functionsURL.absoluteString, "https://project-ref.functions.supabase.co")
- client = SupabaseClient(supabaseURL: URL(string: "https://project-ref.supabase.in")!, supabaseKey: "ANON_KEY")
+ client = SupabaseClient(
+ supabaseURL: URL(string: "https://project-ref.supabase.in")!,
+ supabaseKey: "ANON_KEY"
+ )
XCTAssertEqual(client.functionsURL.absoluteString, "https://project-ref.functions.supabase.in")
- client = SupabaseClient(supabaseURL: URL(string: "https://custom-domain.com")!, supabaseKey: "ANON_KEY")
+ client = SupabaseClient(
+ supabaseURL: URL(string: "https://custom-domain.com")!,
+ supabaseKey: "ANON_KEY"
+ )
XCTAssertEqual(client.functionsURL.absoluteString, "https://custom-domain.com/functions/v1")
}
}
diff --git a/supabase-swift.xcworkspace/contents.xcworkspacedata b/supabase-swift.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..d6fe7ad5
--- /dev/null
+++ b/supabase-swift.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/supabase-swift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/supabase-swift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/supabase-swift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/supabase-swift.xcworkspace/xcshareddata/swiftpm/Package.resolved b/supabase-swift.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 00000000..4c0dc28f
--- /dev/null
+++ b/supabase-swift.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,140 @@
+{
+ "pins" : [
+ {
+ "identity" : "functions-swift",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/supabase-community/functions-swift",
+ "state" : {
+ "revision" : "c680cdfc53399376bdece299b1387b4fb3da514e",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "get",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kean/Get",
+ "state" : {
+ "revision" : "12830cc64f31789ae6f4352d2d51d03a25fc3741",
+ "version" : "2.1.6"
+ }
+ },
+ {
+ "identity" : "getextensions",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/binaryscraping/GetExtensions",
+ "state" : {
+ "revision" : "aa20f38721142eb6592b2c8f11179d32d7d70ae3",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "gotrue-swift",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/supabase-community/gotrue-swift",
+ "state" : {
+ "revision" : "9626a5833224b92126bd3f2350e3a8e2f4cb7cc5",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "keychainaccess",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kishikawakatsumi/KeychainAccess",
+ "state" : {
+ "revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
+ "version" : "4.2.2"
+ }
+ },
+ {
+ "identity" : "postgrest-swift",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/supabase-community/postgrest-swift",
+ "state" : {
+ "revision" : "a3c2d6a2ede94d2529edf063b056b1a66fd9cdc2",
+ "version" : "1.0.0"
+ }
+ },
+ {
+ "identity" : "realtime-swift",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/supabase-community/realtime-swift.git",
+ "state" : {
+ "revision" : "0b985c687fe963f6bd818ff77a35c27247b98bb4",
+ "version" : "0.0.2"
+ }
+ },
+ {
+ "identity" : "storage-swift",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/supabase-community/storage-swift.git",
+ "state" : {
+ "revision" : "04703e499ca258899d7ad45717efe75b8ddc09ab",
+ "version" : "0.1.0"
+ }
+ },
+ {
+ "identity" : "swift-case-paths",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/pointfreeco/swift-case-paths",
+ "state" : {
+ "revision" : "c3a42e8d1a76ff557cf565ed6d8b0aee0e6e75af",
+ "version" : "0.11.0"
+ }
+ },
+ {
+ "identity" : "swift-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-collections",
+ "state" : {
+ "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
+ "version" : "1.0.4"
+ }
+ },
+ {
+ "identity" : "swift-custom-dump",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/pointfreeco/swift-custom-dump",
+ "state" : {
+ "revision" : "dd86159e25c749873f144577e5d18309bf57534f",
+ "version" : "0.8.0"
+ }
+ },
+ {
+ "identity" : "swift-identified-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/pointfreeco/swift-identified-collections.git",
+ "state" : {
+ "revision" : "fd34c544ad27f3ba6b19142b348005bfa85b6005",
+ "version" : "0.6.0"
+ }
+ },
+ {
+ "identity" : "swiftui-navigation",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/pointfreeco/swiftui-navigation.git",
+ "state" : {
+ "revision" : "270a754308f5440be52fc295242eb7031638bd15",
+ "version" : "0.6.1"
+ }
+ },
+ {
+ "identity" : "urlqueryencoder",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/kean/URLQueryEncoder",
+ "state" : {
+ "revision" : "4ce950479707ea109f229d7230ec074a133b15d7",
+ "version" : "0.2.1"
+ }
+ },
+ {
+ "identity" : "xctest-dynamic-overlay",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
+ "state" : {
+ "revision" : "ace21305e0dd3a9e749aef79fef14be79a3b4669",
+ "version" : "0.8.2"
+ }
+ }
+ ],
+ "version" : 2
+}