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 +}