diff --git a/.github/GoogleService-Info-CI.plist b/.github/GoogleService-Info-CI.plist new file mode 100644 index 00000000..5d96fe50 --- /dev/null +++ b/.github/GoogleService-Info-CI.plist @@ -0,0 +1,38 @@ + + + + + CLIENT_ID + 000000000000-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.000000000000-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ANDROID_CLIENT_ID + 000000000000-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.apps.googleusercontent.com + API_KEY + AAAAAAAAAAA_AAAAA_AAAAAAAAAAAAAAAAAAAAA + GCM_SENDER_ID + 000000000000 + PLIST_VERSION + 1 + BUNDLE_ID + com.google.firebase.sample.build + PROJECT_ID + aaaaaa-aaa-aaaaa + STORAGE_BUCKET + aaaaaa-aaa-aaaaa.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:000000000000:ios:aaaaaaaaaaaaaaaa + DATABASE_URL + https://aaaaaa-aaa-aaaaa.firebaseio.com + + \ No newline at end of file diff --git a/.github/workflows/appcheck.yml b/.github/workflows/appcheck.yml new file mode 100644 index 00000000..e1db0f5a --- /dev/null +++ b/.github/workflows/appcheck.yml @@ -0,0 +1,41 @@ +on: + pull_request: + paths: + - 'appcheck/**' + - '.github/workflows/appcheck.yml' +name: App Check +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cd appcheck + xcodebuild -project AppCheckSnippets.xcodeproj clean build -scheme AppCheckSnippetsSwift -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} + objc-build: + name: ObjC build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build ObjC snippets + run: | + cd appcheck + xcodebuild -project AppCheckSnippets.xcodeproj clean build -scheme AppCheckSnippetsObjC -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml new file mode 100644 index 00000000..644f018c --- /dev/null +++ b/.github/workflows/core.yml @@ -0,0 +1,25 @@ +on: + pull_request: + paths: + - 'firoptions/**' + - '.github/workflows/core.yml' +name: Analytics + Core +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cp .github/GoogleService-Info-CI.plist firoptions/FiroptionConfiguration/GoogleService-Info.plist + cd firoptions + xcodebuild -project FiroptionConfiguration.xcodeproj clean build -scheme FiroptionConfiguration -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/crashlytics.yml b/.github/workflows/crashlytics.yml new file mode 100644 index 00000000..4c3b982e --- /dev/null +++ b/.github/workflows/crashlytics.yml @@ -0,0 +1,41 @@ +on: + pull_request: + paths: + - 'crashlytics/**' + - '.github/workflows/crashlytics.yml' +name: Crashlytics +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cd crashlytics + xcodebuild -project CrashlyticsExample.xcodeproj clean build -scheme CrashlyticsExampleSwift -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} + objc-build: + name: ObjC build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build ObjC snippets + run: | + cd crashlytics + xcodebuild -project CrashlyticsExample.xcodeproj clean build -scheme CrashlyticsExample -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/database.yml b/.github/workflows/database.yml new file mode 100644 index 00000000..fe3f424c --- /dev/null +++ b/.github/workflows/database.yml @@ -0,0 +1,24 @@ +on: + pull_request: + paths: + - 'database/**' + - '.github/workflows/database.yml' +name: Database +jobs: + swift-build: + name: Build combined snippets + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build snippets + run: | + cd database + xcodebuild -project DatabaseReference.xcodeproj clean build -scheme DatabaseReference -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/firestore.yml b/.github/workflows/firestore.yml new file mode 100644 index 00000000..37d9f810 --- /dev/null +++ b/.github/workflows/firestore.yml @@ -0,0 +1,43 @@ +on: + pull_request: + paths: + - 'firestore/**' + - '.github/workflows/firestore.yml' +name: Firestore +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cp .github/GoogleService-Info-CI.plist firestore/swift/firestore-smoketest/GoogleService-Info.plist + cd firestore/swift + xcodebuild -project firestore-smoketest.xcodeproj clean build -scheme firestore-smoketest -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} + objc-build: + name: ObjC build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build ObjC snippets + run: | + cp .github/GoogleService-Info-CI.plist firestore/objc/GoogleService-Info.plist + cd firestore/objc + xcodebuild -project firestore-smoketest-objc.xcodeproj clean build -scheme firestore-smoketest-objc -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/functions.yml b/.github/workflows/functions.yml new file mode 100644 index 00000000..fe0cd5e3 --- /dev/null +++ b/.github/workflows/functions.yml @@ -0,0 +1,41 @@ +on: + pull_request: + paths: + - 'functions/**' + - '.github/workflows/functions.yml' +name: Functions +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cd functions + xcodebuild -project FunctionsExample.xcodeproj clean build -scheme FunctionsExampleSwift -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} + objc-build: + name: ObjC build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build ObjC snippets + run: | + cd functions + xcodebuild -project FunctionsExample.xcodeproj clean build -scheme FunctionsExample -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/installations.yml b/.github/workflows/installations.yml new file mode 100644 index 00000000..8286a823 --- /dev/null +++ b/.github/workflows/installations.yml @@ -0,0 +1,24 @@ +on: + pull_request: + paths: + - 'installations/**' + - '.github/workflows/installations.yml' +name: Installations +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cd installations/ + xcodebuild -project InstallationsSnippets.xcodeproj clean build -scheme InstallationsSnippets -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} \ No newline at end of file diff --git a/.github/workflows/ml-functions.yml b/.github/workflows/ml-functions.yml new file mode 100644 index 00000000..f28a921d --- /dev/null +++ b/.github/workflows/ml-functions.yml @@ -0,0 +1,41 @@ +on: + pull_request: + paths: + - 'ml-functions/**' + - '.github/workflows/ml-functions.yml' +name: Functions (ML) +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cd ml-functions + xcodebuild -project MLFunctionsExample.xcodeproj clean build -scheme MLFunctionsExampleSwift -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} + objc-build: + name: ObjC build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build ObjC snippets + run: | + cd ml-functions + xcodebuild -project MLFunctionsExample.xcodeproj clean build -scheme MLFunctionsExample -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/storage.yml b/.github/workflows/storage.yml new file mode 100644 index 00000000..73949ab2 --- /dev/null +++ b/.github/workflows/storage.yml @@ -0,0 +1,43 @@ +on: + pull_request: + paths: + - 'storage/**' + - '.github/workflows/storage.yml' +name: Storage +jobs: + swift-build: + name: Swift build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + sudo xcode-select -switch /Applications/Xcode_16.1.app/Contents/Developer + cd storage + xcodebuild -project StorageReference.xcodeproj clean build -scheme StorageReferenceSwift -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} + objc-build: + name: ObjC build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 16'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build ObjC snippets + run: | + sudo xcode-select -switch /Applications/Xcode_16.1.app/Contents/Developer + cd storage + xcodebuild -project StorageReference.xcodeproj clean build -scheme StorageReference -destination "${destination}" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.github/workflows/vertexai.yml b/.github/workflows/vertexai.yml new file mode 100644 index 00000000..30b61ab6 --- /dev/null +++ b/.github/workflows/vertexai.yml @@ -0,0 +1,24 @@ +on: + pull_request: + paths: + - 'vertexai/**' + - '.github/workflows/vertexai.yml' +name: VertexAI +jobs: + snippets-build: + name: snippets build + runs-on: macOS-latest + strategy: + matrix: + destination: ['platform=iOS Simulator,OS=latest,name=iPhone 15', 'platform=OS X'] + steps: + - name: Checkout + uses: actions/checkout@master + - name: Install deps + run: brew install xcbeautify + - name: Build Swift snippets + run: | + cd vertexai + xcodebuild -project VertexAISnippets.xcodeproj -scheme VertexAISnippets clean build -destination "${destination}" CODE_SIGNING_REQUIRED=NO | xcbeautify --renderer github-actions + env: + destination: ${{ matrix.destination }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2cf08328..00000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -osx_image: xcode10 -language: objective-c - -cache: - - bundler - - cocoapods - -before_install: - - gem install xcpretty - - pod install --repo-update - -script: ./build.sh diff --git a/appcheck/AppCheckSnippets.xcodeproj/project.pbxproj b/appcheck/AppCheckSnippets.xcodeproj/project.pbxproj new file mode 100644 index 00000000..319930a4 --- /dev/null +++ b/appcheck/AppCheckSnippets.xcodeproj/project.pbxproj @@ -0,0 +1,660 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 4265C38C26AF71E100BD1DB2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4265C38B26AF71E100BD1DB2 /* AppDelegate.m */; }; + 4265C38F26AF71E100BD1DB2 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4265C38E26AF71E100BD1DB2 /* SceneDelegate.m */; }; + 4265C39226AF71E100BD1DB2 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4265C39126AF71E100BD1DB2 /* ViewController.m */; }; + 4265C39526AF71E100BD1DB2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4265C39326AF71E100BD1DB2 /* Main.storyboard */; }; + 4265C39726AF71E300BD1DB2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4265C39626AF71E300BD1DB2 /* Assets.xcassets */; }; + 4265C39A26AF71E300BD1DB2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4265C39826AF71E300BD1DB2 /* LaunchScreen.storyboard */; }; + 4265C39D26AF71E300BD1DB2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4265C39C26AF71E300BD1DB2 /* main.m */; }; + 4265C3A826AF723800BD1DB2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4265C3A726AF723800BD1DB2 /* AppDelegate.swift */; }; + 4265C3AA26AF723800BD1DB2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4265C3A926AF723800BD1DB2 /* SceneDelegate.swift */; }; + 4265C3AC26AF723800BD1DB2 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4265C3AB26AF723800BD1DB2 /* ViewController.swift */; }; + 4265C3AF26AF723800BD1DB2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4265C3AD26AF723800BD1DB2 /* Main.storyboard */; }; + 4265C3B126AF723900BD1DB2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4265C3B026AF723900BD1DB2 /* Assets.xcassets */; }; + 4265C3B426AF723900BD1DB2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4265C3B226AF723900BD1DB2 /* LaunchScreen.storyboard */; }; + 4265C3BA26AF74F800BD1DB2 /* AppAttestProviderFactories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4265C3B926AF74F800BD1DB2 /* AppAttestProviderFactories.swift */; }; + 4265C3BC26AF775A00BD1DB2 /* AppAttestProviderFactories.m in Sources */ = {isa = PBXBuildFile; fileRef = 4265C3BB26AF775A00BD1DB2 /* AppAttestProviderFactories.m */; }; + 4265C3BE26AF7A6600BD1DB2 /* YourCustomAppCheckProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4265C3BD26AF7A6600BD1DB2 /* YourCustomAppCheckProvider.swift */; }; + 4265C3C026AF7A7E00BD1DB2 /* YourCustomAppCheckProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 4265C3BF26AF7A7E00BD1DB2 /* YourCustomAppCheckProvider.m */; }; + 8D7726082D2874A100537A0B /* FirebaseAppCheck in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7726072D2874A100537A0B /* FirebaseAppCheck */; }; + 8D77260A2D2874A100537A0B /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7726092D2874A100537A0B /* FirebaseCore */; }; + 8D77260D2D2874BA00537A0B /* FirebaseAppCheck in Frameworks */ = {isa = PBXBuildFile; productRef = 8D77260C2D2874BA00537A0B /* FirebaseAppCheck */; }; + 8D77260F2D2874BA00537A0B /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D77260E2D2874BA00537A0B /* FirebaseCore */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 4265C38726AF71E100BD1DB2 /* AppCheckSnippetsObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppCheckSnippetsObjC.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4265C38A26AF71E100BD1DB2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 4265C38B26AF71E100BD1DB2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 4265C38D26AF71E100BD1DB2 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + 4265C38E26AF71E100BD1DB2 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + 4265C39026AF71E100BD1DB2 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 4265C39126AF71E100BD1DB2 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 4265C39426AF71E100BD1DB2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 4265C39626AF71E300BD1DB2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4265C39926AF71E300BD1DB2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4265C39B26AF71E300BD1DB2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4265C39C26AF71E300BD1DB2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 4265C3A526AF723800BD1DB2 /* AppCheckSnippetsSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppCheckSnippetsSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 4265C3A726AF723800BD1DB2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4265C3A926AF723800BD1DB2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 4265C3AB26AF723800BD1DB2 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 4265C3AE26AF723800BD1DB2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 4265C3B026AF723900BD1DB2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4265C3B326AF723900BD1DB2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4265C3B526AF723900BD1DB2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4265C3B926AF74F800BD1DB2 /* AppAttestProviderFactories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAttestProviderFactories.swift; sourceTree = ""; }; + 4265C3BB26AF775A00BD1DB2 /* AppAttestProviderFactories.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppAttestProviderFactories.m; sourceTree = ""; }; + 4265C3BD26AF7A6600BD1DB2 /* YourCustomAppCheckProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YourCustomAppCheckProvider.swift; sourceTree = ""; }; + 4265C3BF26AF7A7E00BD1DB2 /* YourCustomAppCheckProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = YourCustomAppCheckProvider.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4265C38426AF71E100BD1DB2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D77260A2D2874A100537A0B /* FirebaseCore in Frameworks */, + 8D7726082D2874A100537A0B /* FirebaseAppCheck in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4265C3A226AF723800BD1DB2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D77260F2D2874BA00537A0B /* FirebaseCore in Frameworks */, + 8D77260D2D2874BA00537A0B /* FirebaseAppCheck in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4265C38826AF71E100BD1DB2 /* Products */ = { + isa = PBXGroup; + children = ( + 4265C38726AF71E100BD1DB2 /* AppCheckSnippetsObjC.app */, + 4265C3A526AF723800BD1DB2 /* AppCheckSnippetsSwift.app */, + ); + name = Products; + sourceTree = ""; + }; + 4265C38926AF71E100BD1DB2 /* AppCheckSnippetsObjC */ = { + isa = PBXGroup; + children = ( + 4265C38A26AF71E100BD1DB2 /* AppDelegate.h */, + 4265C38B26AF71E100BD1DB2 /* AppDelegate.m */, + 4265C38D26AF71E100BD1DB2 /* SceneDelegate.h */, + 4265C38E26AF71E100BD1DB2 /* SceneDelegate.m */, + 4265C39026AF71E100BD1DB2 /* ViewController.h */, + 4265C39126AF71E100BD1DB2 /* ViewController.m */, + 4265C39326AF71E100BD1DB2 /* Main.storyboard */, + 4265C39626AF71E300BD1DB2 /* Assets.xcassets */, + 4265C39826AF71E300BD1DB2 /* LaunchScreen.storyboard */, + 4265C39B26AF71E300BD1DB2 /* Info.plist */, + 4265C39C26AF71E300BD1DB2 /* main.m */, + 4265C3BB26AF775A00BD1DB2 /* AppAttestProviderFactories.m */, + 4265C3BF26AF7A7E00BD1DB2 /* YourCustomAppCheckProvider.m */, + ); + path = AppCheckSnippetsObjC; + sourceTree = ""; + }; + 4265C3A626AF723800BD1DB2 /* AppCheckSnippetsSwift */ = { + isa = PBXGroup; + children = ( + 4265C3A726AF723800BD1DB2 /* AppDelegate.swift */, + 4265C3A926AF723800BD1DB2 /* SceneDelegate.swift */, + 4265C3AB26AF723800BD1DB2 /* ViewController.swift */, + 4265C3AD26AF723800BD1DB2 /* Main.storyboard */, + 4265C3B026AF723900BD1DB2 /* Assets.xcassets */, + 4265C3B226AF723900BD1DB2 /* LaunchScreen.storyboard */, + 4265C3B526AF723900BD1DB2 /* Info.plist */, + 4265C3B926AF74F800BD1DB2 /* AppAttestProviderFactories.swift */, + 4265C3BD26AF7A6600BD1DB2 /* YourCustomAppCheckProvider.swift */, + ); + path = AppCheckSnippetsSwift; + sourceTree = ""; + }; + 42A83E6D26AF6E5C00097CA3 = { + isa = PBXGroup; + children = ( + 4265C38926AF71E100BD1DB2 /* AppCheckSnippetsObjC */, + 4265C3A626AF723800BD1DB2 /* AppCheckSnippetsSwift */, + 8D77260B2D2874BA00537A0B /* Frameworks */, + 4265C38826AF71E100BD1DB2 /* Products */, + ); + sourceTree = ""; + }; + 8D77260B2D2874BA00537A0B /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4265C38626AF71E100BD1DB2 /* AppCheckSnippetsObjC */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4265C3A026AF71E300BD1DB2 /* Build configuration list for PBXNativeTarget "AppCheckSnippetsObjC" */; + buildPhases = ( + 4265C38326AF71E100BD1DB2 /* Sources */, + 4265C38426AF71E100BD1DB2 /* Frameworks */, + 4265C38526AF71E100BD1DB2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AppCheckSnippetsObjC; + productName = AppCheckSnippetsObjC; + productReference = 4265C38726AF71E100BD1DB2 /* AppCheckSnippetsObjC.app */; + productType = "com.apple.product-type.application"; + }; + 4265C3A426AF723800BD1DB2 /* AppCheckSnippetsSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4265C3B626AF723900BD1DB2 /* Build configuration list for PBXNativeTarget "AppCheckSnippetsSwift" */; + buildPhases = ( + 4265C3A126AF723800BD1DB2 /* Sources */, + 4265C3A226AF723800BD1DB2 /* Frameworks */, + 4265C3A326AF723800BD1DB2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AppCheckSnippetsSwift; + productName = AppCheckSnippetsSwift; + productReference = 4265C3A526AF723800BD1DB2 /* AppCheckSnippetsSwift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 42A83E6E26AF6E5C00097CA3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1250; + LastUpgradeCheck = 1250; + TargetAttributes = { + 4265C38626AF71E100BD1DB2 = { + CreatedOnToolsVersion = 12.5; + }; + 4265C3A426AF723800BD1DB2 = { + CreatedOnToolsVersion = 12.5; + }; + }; + }; + buildConfigurationList = 42A83E7126AF6E5C00097CA3 /* Build configuration list for PBXProject "AppCheckSnippets" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 42A83E6D26AF6E5C00097CA3; + packageReferences = ( + 8D7726062D2874A100537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); + productRefGroup = 4265C38826AF71E100BD1DB2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4265C38626AF71E100BD1DB2 /* AppCheckSnippetsObjC */, + 4265C3A426AF723800BD1DB2 /* AppCheckSnippetsSwift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4265C38526AF71E100BD1DB2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4265C39A26AF71E300BD1DB2 /* LaunchScreen.storyboard in Resources */, + 4265C39726AF71E300BD1DB2 /* Assets.xcassets in Resources */, + 4265C39526AF71E100BD1DB2 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4265C3A326AF723800BD1DB2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4265C3B426AF723900BD1DB2 /* LaunchScreen.storyboard in Resources */, + 4265C3B126AF723900BD1DB2 /* Assets.xcassets in Resources */, + 4265C3AF26AF723800BD1DB2 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4265C38326AF71E100BD1DB2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4265C39226AF71E100BD1DB2 /* ViewController.m in Sources */, + 4265C3C026AF7A7E00BD1DB2 /* YourCustomAppCheckProvider.m in Sources */, + 4265C38C26AF71E100BD1DB2 /* AppDelegate.m in Sources */, + 4265C39D26AF71E300BD1DB2 /* main.m in Sources */, + 4265C3BC26AF775A00BD1DB2 /* AppAttestProviderFactories.m in Sources */, + 4265C38F26AF71E100BD1DB2 /* SceneDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4265C3A126AF723800BD1DB2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4265C3BE26AF7A6600BD1DB2 /* YourCustomAppCheckProvider.swift in Sources */, + 4265C3BA26AF74F800BD1DB2 /* AppAttestProviderFactories.swift in Sources */, + 4265C3AC26AF723800BD1DB2 /* ViewController.swift in Sources */, + 4265C3A826AF723800BD1DB2 /* AppDelegate.swift in Sources */, + 4265C3AA26AF723800BD1DB2 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 4265C39326AF71E100BD1DB2 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4265C39426AF71E100BD1DB2 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 4265C39826AF71E300BD1DB2 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4265C39926AF71E300BD1DB2 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 4265C3AD26AF723800BD1DB2 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4265C3AE26AF723800BD1DB2 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 4265C3B226AF723900BD1DB2 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4265C3B326AF723900BD1DB2 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4265C39E26AF71E300BD1DB2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_STYLE = Automatic; + 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; + INFOPLIST_FILE = AppCheckSnippetsObjC/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.example.AppCheckSnippetsObjC; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4265C39F26AF71E300BD1DB2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_STYLE = Automatic; + 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; + INFOPLIST_FILE = AppCheckSnippetsObjC/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.example.AppCheckSnippetsObjC; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4265C3B726AF723900BD1DB2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_STYLE = Automatic; + 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; + INFOPLIST_FILE = AppCheckSnippetsSwift/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.example.AppCheckSnippetsSwift; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4265C3B826AF723900BD1DB2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_STYLE = Automatic; + 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; + INFOPLIST_FILE = AppCheckSnippetsSwift/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.google.example.AppCheckSnippetsSwift; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 42A83E7226AF6E5C00097CA3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + 42A83E7326AF6E5C00097CA3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4265C3A026AF71E300BD1DB2 /* Build configuration list for PBXNativeTarget "AppCheckSnippetsObjC" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4265C39E26AF71E300BD1DB2 /* Debug */, + 4265C39F26AF71E300BD1DB2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4265C3B626AF723900BD1DB2 /* Build configuration list for PBXNativeTarget "AppCheckSnippetsSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4265C3B726AF723900BD1DB2 /* Debug */, + 4265C3B826AF723900BD1DB2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 42A83E7126AF6E5C00097CA3 /* Build configuration list for PBXProject "AppCheckSnippets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 42A83E7226AF6E5C00097CA3 /* Debug */, + 42A83E7326AF6E5C00097CA3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7726062D2874A100537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7726072D2874A100537A0B /* FirebaseAppCheck */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726062D2874A100537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAppCheck; + }; + 8D7726092D2874A100537A0B /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726062D2874A100537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + 8D77260C2D2874BA00537A0B /* FirebaseAppCheck */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726062D2874A100537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAppCheck; + }; + 8D77260E2D2874BA00537A0B /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726062D2874A100537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 42A83E6E26AF6E5C00097CA3 /* Project object */; +} diff --git a/appcheck/AppCheckSnippetsObjC/AppAttestProviderFactories.m b/appcheck/AppCheckSnippetsObjC/AppAttestProviderFactories.m new file mode 100644 index 00000000..2d892ab9 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/AppAttestProviderFactories.m @@ -0,0 +1,48 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import FirebaseAppCheck; + +// [START appcheck_simple_appattest_factory] +@interface YourSimpleAppCheckProviderFactory : NSObject +@end + +@implementation YourSimpleAppCheckProviderFactory + +- (nullable id)createProviderWithApp:(nonnull FIRApp *)app { + return [[FIRAppAttestProvider alloc] initWithApp:app]; +} + +@end +// [END appcheck_simple_appattest_factory] + +// [START appcheck_appattest_factory] +@interface YourAppCheckProviderFactory : NSObject +@end + +@implementation YourAppCheckProviderFactory + +- (nullable id)createProviderWithApp:(nonnull FIRApp *)app { + if (@available(iOS 14.0, *)) { + return [[FIRAppAttestProvider alloc] initWithApp:app]; + } else { + return [[FIRDeviceCheckProvider alloc] initWithApp:app]; + } +} + +@end +// [START appcheck_appattest_factory] diff --git a/appcheck/AppCheckSnippetsObjC/AppDelegate.h b/appcheck/AppCheckSnippetsObjC/AppDelegate.h new file mode 100644 index 00000000..c142da10 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/AppDelegate.h @@ -0,0 +1,23 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface AppDelegate : UIResponder + + +@end + diff --git a/appcheck/AppCheckSnippetsObjC/AppDelegate.m b/appcheck/AppCheckSnippetsObjC/AppDelegate.m new file mode 100644 index 00000000..2bf07506 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/AppDelegate.m @@ -0,0 +1,107 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "AppDelegate.h" +@import FirebaseCore; +@import FirebaseAppCheck; + +@interface AppDelegate () +@end + +@interface YourAppCheckProviderFactory : NSObject +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + return YES; +} + +- (void)initCustom { + // [START appcheck_initialize_custom] + YourAppCheckProviderFactory *providerFactory = + [[YourAppCheckProviderFactory alloc] init]; + [FIRAppCheck setAppCheckProviderFactory:providerFactory]; + + [FIRApp configure]; + // [END appcheck_initialize_custom] +} + +- (void)initDebug { + // [START appcheck_initialize_debug] + FIRAppCheckDebugProviderFactory *providerFactory = + [[FIRAppCheckDebugProviderFactory alloc] init]; + [FIRAppCheck setAppCheckProviderFactory:providerFactory]; + + // Use Firebase library to configure APIs + [FIRApp configure]; + // [END appcheck_initialize_debug] +} + +- (void)nonFirebaseBackend { + // [START appcheck_nonfirebase] + [[FIRAppCheck appCheck] tokenForcingRefresh:NO + completion:^(FIRAppCheckToken * _Nullable token, + NSError * _Nullable error) { + if (error != nil) { + // Handle any errors if the token was not retrieved. + NSLog(@"Unable to retrieve App Check token: %@", error); + return; + } + if (token == nil) { + NSLog(@"Unable to retrieve App Check token."); + return; + } + + // Get the raw App Check token string. + NSString *tokenString = token.token; + + // Include the App Check token with requests to your server. + NSURL *url = [[NSURL alloc] initWithString:@"https://yourbackend.example.com/yourApiEndpoint"]; + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + [request setHTTPMethod:@"GET"]; + [request setValue:tokenString forHTTPHeaderField:@"X-Firebase-AppCheck"]; + + NSURLSessionDataTask *task = + [[NSURLSession sharedSession] dataTaskWithRequest:request + completionHandler:^(NSData * _Nullable data, + NSURLResponse * _Nullable response, + NSError * _Nullable error) { + // Handle response from your backend. + }]; + [task resume]; + }]; + // [END appcheck_nonfirebase] +} + +#pragma mark - UISceneSession lifecycle + + +- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; +} + + +- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. +} + + +@end diff --git a/appcheck/AppCheckSnippetsObjC/Assets.xcassets/AccentColor.colorset/Contents.json b/appcheck/AppCheckSnippetsObjC/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/appcheck/AppCheckSnippetsObjC/Assets.xcassets/AppIcon.appiconset/Contents.json b/appcheck/AppCheckSnippetsObjC/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/appcheck/AppCheckSnippetsObjC/Assets.xcassets/Contents.json b/appcheck/AppCheckSnippetsObjC/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/appcheck/AppCheckSnippetsObjC/Base.lproj/LaunchScreen.storyboard b/appcheck/AppCheckSnippetsObjC/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appcheck/AppCheckSnippetsObjC/Base.lproj/Main.storyboard b/appcheck/AppCheckSnippetsObjC/Base.lproj/Main.storyboard new file mode 100644 index 00000000..808a21ce --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appcheck/AppCheckSnippetsObjC/Info.plist b/appcheck/AppCheckSnippetsObjC/Info.plist new file mode 100644 index 00000000..72bf2c4f --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/appcheck/AppCheckSnippetsObjC/SceneDelegate.h b/appcheck/AppCheckSnippetsObjC/SceneDelegate.h new file mode 100644 index 00000000..a0ab2fd4 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/SceneDelegate.h @@ -0,0 +1,24 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface SceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow * window; + +@end + diff --git a/appcheck/AppCheckSnippetsObjC/SceneDelegate.m b/appcheck/AppCheckSnippetsObjC/SceneDelegate.m new file mode 100644 index 00000000..b6032205 --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/SceneDelegate.m @@ -0,0 +1,66 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SceneDelegate.h" + +@interface SceneDelegate () + +@end + +@implementation SceneDelegate + + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). +} + + +- (void)sceneDidDisconnect:(UIScene *)scene { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). +} + + +- (void)sceneDidBecomeActive:(UIScene *)scene { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. +} + + +- (void)sceneWillResignActive:(UIScene *)scene { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). +} + + +- (void)sceneWillEnterForeground:(UIScene *)scene { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. +} + + +- (void)sceneDidEnterBackground:(UIScene *)scene { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. +} + + +@end diff --git a/invites/InvitesExample/ViewController.h b/appcheck/AppCheckSnippetsObjC/ViewController.h similarity index 91% rename from invites/InvitesExample/ViewController.h rename to appcheck/AppCheckSnippetsObjC/ViewController.h index 179f8331..838d309e 100644 --- a/invites/InvitesExample/ViewController.h +++ b/appcheck/AppCheckSnippetsObjC/ViewController.h @@ -1,5 +1,5 @@ // -// Copyright (c) Google Inc. +// Copyright (c) 2021 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,8 +14,10 @@ // limitations under the License. // -@import UIKit; +#import @interface ViewController : UIViewController + @end + diff --git a/appcheck/AppCheckSnippetsObjC/ViewController.m b/appcheck/AppCheckSnippetsObjC/ViewController.m new file mode 100644 index 00000000..d25919ef --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/ViewController.m @@ -0,0 +1,31 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + + +@end diff --git a/appcheck/AppCheckSnippetsObjC/YourCustomAppCheckProvider.m b/appcheck/AppCheckSnippetsObjC/YourCustomAppCheckProvider.m new file mode 100644 index 00000000..95a5821d --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/YourCustomAppCheckProvider.m @@ -0,0 +1,73 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import FirebaseAppCheck; + +// [START appcheck_custom_provider] +@interface YourCustomAppCheckProvider : NSObject + +@property FIRApp *app; + +- (id)initWithApp:(FIRApp *)app; + +@end + +@implementation YourCustomAppCheckProvider + +- (id)initWithApp:app { + self = [super init]; + if (self) { + self.app = app; + } + return self; +} + +- (void)getTokenWithCompletion:(nonnull void (^)(FIRAppCheckToken * _Nullable, + NSError * _Nullable))handler { + dispatch_async(dispatch_get_main_queue(), ^{ + // Logic to exchange proof of authenticity for an App Check token. + // [START_EXCLUDE] + double expirationFromServer = 1234.0; + NSString *tokenFromServer = @"token"; + // [END_EXCLUDE] + + // Create FIRAppCheckToken object. + NSTimeInterval exp = expirationFromServer; + FIRAppCheckToken *token + = [[FIRAppCheckToken alloc] initWithToken:tokenFromServer + expirationDate:[NSDate dateWithTimeIntervalSince1970:exp]]; + + // Pass the token or error to the completion handler. + handler(token, nil); + }); +} + +@end +// [END appcheck_custom_provider] + +// [START appcheck_custom_provider_factory] +@interface YourCustomAppCheckProviderFactory : NSObject +@end + +@implementation YourCustomAppCheckProviderFactory + +- (nullable id)createProviderWithApp:(FIRApp *)app { + return [[YourCustomAppCheckProvider alloc] initWithApp:app]; +} + +@end +// [END appcheck_custom_provider_factory] diff --git a/appcheck/AppCheckSnippetsObjC/main.m b/appcheck/AppCheckSnippetsObjC/main.m new file mode 100644 index 00000000..a440f36e --- /dev/null +++ b/appcheck/AppCheckSnippetsObjC/main.m @@ -0,0 +1,27 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + NSString * appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +} diff --git a/appcheck/AppCheckSnippetsSwift/AppAttestProviderFactories.swift b/appcheck/AppCheckSnippetsSwift/AppAttestProviderFactories.swift new file mode 100644 index 00000000..cb209737 --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/AppAttestProviderFactories.swift @@ -0,0 +1,38 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseCore +import FirebaseAppCheck + +// [START appcheck_simple_appattest_factory] +class YourSimpleAppCheckProviderFactory: NSObject, AppCheckProviderFactory { + func createProvider(with app: FirebaseApp) -> AppCheckProvider? { + return AppAttestProvider(app: app) + } +} +// [END appcheck_simple_appattest_factory] + +// [START appcheck_appattest_factory] +class YourAppCheckProviderFactory: NSObject, AppCheckProviderFactory { + func createProvider(with app: FirebaseApp) -> AppCheckProvider? { + if #available(iOS 14.0, *) { + return AppAttestProvider(app: app) + } else { + return DeviceCheckProvider(app: app) + } + } +} +// [END appcheck_appattest_factory] diff --git a/appcheck/AppCheckSnippetsSwift/AppDelegate.swift b/appcheck/AppCheckSnippetsSwift/AppDelegate.swift new file mode 100644 index 00000000..5ea067fc --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/AppDelegate.swift @@ -0,0 +1,79 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseCore +import FirebaseAppCheck + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + return true + } + + func initCustom() { + // [START appcheck_initialize_custom] + let providerFactory = YourAppCheckProviderFactory() + AppCheck.setAppCheckProviderFactory(providerFactory) + + FirebaseApp.configure() + // [END appcheck_initialize_custom] + } + + func initDebug() { + // [START appcheck_initialize_debug] + let providerFactory = AppCheckDebugProviderFactory() + AppCheck.setAppCheckProviderFactory(providerFactory) + + FirebaseApp.configure() + // [END appcheck_initialize_debug] + } + + func nonFirebaseBackend() async { + // [START appcheck_nonfirebase] + + do { + let token = try await AppCheck.appCheck().token(forcingRefresh: false) + + // Get the raw App Check token string. + let tokenString = token.token + + // Include the App Check token with requests to your server. + let url = URL(string: "https://yourbackend.example.com/yourApiEndpoint")! + var request = URLRequest(url: url) + request.httpMethod = "GET" + request.setValue(tokenString, forHTTPHeaderField: "X-Firebase-AppCheck") + + let task = URLSession.shared.dataTask(with: request) { data, response, error in + // Handle response from your backend. + } + task.resume() + } catch(let error) { + print("Unable to retrieve App Check token: \(error)") + return + } + // [END appcheck_nonfirebase] + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + +} + diff --git a/appcheck/AppCheckSnippetsSwift/Assets.xcassets/AccentColor.colorset/Contents.json b/appcheck/AppCheckSnippetsSwift/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/appcheck/AppCheckSnippetsSwift/Assets.xcassets/AppIcon.appiconset/Contents.json b/appcheck/AppCheckSnippetsSwift/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/appcheck/AppCheckSnippetsSwift/Assets.xcassets/Contents.json b/appcheck/AppCheckSnippetsSwift/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/appcheck/AppCheckSnippetsSwift/Base.lproj/LaunchScreen.storyboard b/appcheck/AppCheckSnippetsSwift/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appcheck/AppCheckSnippetsSwift/Base.lproj/Main.storyboard b/appcheck/AppCheckSnippetsSwift/Base.lproj/Main.storyboard new file mode 100644 index 00000000..25a76385 --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/appcheck/AppCheckSnippetsSwift/Info.plist b/appcheck/AppCheckSnippetsSwift/Info.plist new file mode 100644 index 00000000..5b531f7b --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/appcheck/AppCheckSnippetsSwift/SceneDelegate.swift b/appcheck/AppCheckSnippetsSwift/SceneDelegate.swift new file mode 100644 index 00000000..dad05973 --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/SceneDelegate.swift @@ -0,0 +1,61 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/ViewController.swift b/appcheck/AppCheckSnippetsSwift/ViewController.swift similarity index 66% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration/ViewController.swift rename to appcheck/AppCheckSnippetsSwift/ViewController.swift index e549ffb3..546de4a8 100644 --- a/firoptions/FiroptionConfiguration/FiroptionConfiguration/ViewController.swift +++ b/appcheck/AppCheckSnippetsSwift/ViewController.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Google Inc. +// Copyright (c) 2021 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,15 +18,10 @@ import UIKit class ViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } } diff --git a/appcheck/AppCheckSnippetsSwift/YourCustomAppCheckProvider.swift b/appcheck/AppCheckSnippetsSwift/YourCustomAppCheckProvider.swift new file mode 100644 index 00000000..ae4971cc --- /dev/null +++ b/appcheck/AppCheckSnippetsSwift/YourCustomAppCheckProvider.swift @@ -0,0 +1,62 @@ +// +// Copyright (c) 2021 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseCore +import FirebaseAppCheck + +// [START appcheck_custom_provider] +class YourCustomAppCheckProvider: NSObject, AppCheckProvider { + var app: FirebaseApp + + init(withFirebaseApp app: FirebaseApp) { + self.app = app + super.init() + } + + func getToken() async throws -> AppCheckToken { + let getTokenTask = Task { () -> AppCheckToken in + // [START_EXCLUDE] + let expirationFromServer = 1000.0 + let tokenFromServer = "token" + // [END_EXCLUDE] + + // Create AppCheckToken object. + let exp = Date(timeIntervalSince1970: expirationFromServer) + let token = AppCheckToken( + token: tokenFromServer, + expirationDate: exp + ) + + if Date() > exp { + throw NSError(domain: "ExampleError", code: 1, userInfo: nil) + } + + return token + } + + return try await getTokenTask.value + } + +} +// [END appcheck_custom_provider] + +// [START appcheck_custom_provider_factory] +class YourCustomAppCheckProviderFactory: NSObject, AppCheckProviderFactory { + func createProvider(with app: FirebaseApp) -> AppCheckProvider? { + return YourCustomAppCheckProvider(withFirebaseApp: app) + } +} +// [END appcheck_custom_provider_factory] diff --git a/crashlytics/CrashlyticsExample.xcodeproj/project.pbxproj b/crashlytics/CrashlyticsExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7c014915 --- /dev/null +++ b/crashlytics/CrashlyticsExample.xcodeproj/project.pbxproj @@ -0,0 +1,534 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 8D7726132D2876F400537A0B /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7726122D2876F400537A0B /* FirebaseCore */; }; + 8D7726152D2876F400537A0B /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7726142D2876F400537A0B /* FirebaseCrashlytics */; }; + 8D7726172D2876FB00537A0B /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7726162D2876FB00537A0B /* FirebaseCore */; }; + 8D7726192D2876FB00537A0B /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7726182D2876FB00537A0B /* FirebaseCrashlytics */; }; + 8D8FA34322F4CAB100213E06 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA34222F4CAB100213E06 /* AppDelegate.m */; }; + 8D8FA34622F4CAB100213E06 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA34522F4CAB100213E06 /* ViewController.m */; }; + 8D8FA34922F4CAB100213E06 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA34722F4CAB100213E06 /* Main.storyboard */; }; + 8D8FA34B22F4CAB200213E06 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA34A22F4CAB200213E06 /* Assets.xcassets */; }; + 8D8FA34E22F4CAB200213E06 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA34C22F4CAB200213E06 /* LaunchScreen.storyboard */; }; + 8D8FA35122F4CAB200213E06 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA35022F4CAB200213E06 /* main.m */; }; + 8D8FA35E22F4CAF700213E06 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA35D22F4CAF700213E06 /* AppDelegate.swift */; }; + 8D8FA36022F4CAF700213E06 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA35F22F4CAF700213E06 /* ViewController.swift */; }; + 8D8FA36322F4CAF700213E06 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA36122F4CAF700213E06 /* Main.storyboard */; }; + 8D8FA36522F4CAF800213E06 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA36422F4CAF800213E06 /* Assets.xcassets */; }; + 8D8FA36822F4CAF800213E06 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA36622F4CAF800213E06 /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 8D8FA33E22F4CAB100213E06 /* CrashlyticsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CrashlyticsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D8FA34122F4CAB100213E06 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 8D8FA34222F4CAB100213E06 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 8D8FA34422F4CAB100213E06 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 8D8FA34522F4CAB100213E06 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 8D8FA34822F4CAB100213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8D8FA34A22F4CAB200213E06 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D8FA34D22F4CAB200213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8D8FA34F22F4CAB200213E06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D8FA35022F4CAB200213E06 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 8D8FA35B22F4CAF700213E06 /* CrashlyticsExampleSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CrashlyticsExampleSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D8FA35D22F4CAF700213E06 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 8D8FA35F22F4CAF700213E06 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 8D8FA36222F4CAF700213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8D8FA36422F4CAF800213E06 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D8FA36722F4CAF800213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8D8FA36922F4CAF800213E06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D8FA33B22F4CAB100213E06 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7726132D2876F400537A0B /* FirebaseCore in Frameworks */, + 8D7726152D2876F400537A0B /* FirebaseCrashlytics in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D8FA35822F4CAF700213E06 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7726172D2876FB00537A0B /* FirebaseCore in Frameworks */, + 8D7726192D2876FB00537A0B /* FirebaseCrashlytics in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D7726112D2876F400537A0B /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 8D8FA33522F4CAB100213E06 = { + isa = PBXGroup; + children = ( + 8D8FA34022F4CAB100213E06 /* CrashlyticsExample */, + 8D8FA35C22F4CAF700213E06 /* CrashlyticsExampleSwift */, + 8D7726112D2876F400537A0B /* Frameworks */, + 8D8FA33F22F4CAB100213E06 /* Products */, + ); + sourceTree = ""; + }; + 8D8FA33F22F4CAB100213E06 /* Products */ = { + isa = PBXGroup; + children = ( + 8D8FA33E22F4CAB100213E06 /* CrashlyticsExample.app */, + 8D8FA35B22F4CAF700213E06 /* CrashlyticsExampleSwift.app */, + ); + name = Products; + sourceTree = ""; + }; + 8D8FA34022F4CAB100213E06 /* CrashlyticsExample */ = { + isa = PBXGroup; + children = ( + 8D8FA34122F4CAB100213E06 /* AppDelegate.h */, + 8D8FA34222F4CAB100213E06 /* AppDelegate.m */, + 8D8FA34422F4CAB100213E06 /* ViewController.h */, + 8D8FA34522F4CAB100213E06 /* ViewController.m */, + 8D8FA34722F4CAB100213E06 /* Main.storyboard */, + 8D8FA34A22F4CAB200213E06 /* Assets.xcassets */, + 8D8FA34C22F4CAB200213E06 /* LaunchScreen.storyboard */, + 8D8FA34F22F4CAB200213E06 /* Info.plist */, + 8D8FA35022F4CAB200213E06 /* main.m */, + ); + path = CrashlyticsExample; + sourceTree = ""; + }; + 8D8FA35C22F4CAF700213E06 /* CrashlyticsExampleSwift */ = { + isa = PBXGroup; + children = ( + 8D8FA35D22F4CAF700213E06 /* AppDelegate.swift */, + 8D8FA35F22F4CAF700213E06 /* ViewController.swift */, + 8D8FA36122F4CAF700213E06 /* Main.storyboard */, + 8D8FA36422F4CAF800213E06 /* Assets.xcassets */, + 8D8FA36622F4CAF800213E06 /* LaunchScreen.storyboard */, + 8D8FA36922F4CAF800213E06 /* Info.plist */, + ); + path = CrashlyticsExampleSwift; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D8FA33D22F4CAB100213E06 /* CrashlyticsExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D8FA35422F4CAB200213E06 /* Build configuration list for PBXNativeTarget "CrashlyticsExample" */; + buildPhases = ( + 8D8FA33A22F4CAB100213E06 /* Sources */, + 8D8FA33B22F4CAB100213E06 /* Frameworks */, + 8D8FA33C22F4CAB100213E06 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CrashlyticsExample; + productName = CrashlyticsExample; + productReference = 8D8FA33E22F4CAB100213E06 /* CrashlyticsExample.app */; + productType = "com.apple.product-type.application"; + }; + 8D8FA35A22F4CAF700213E06 /* CrashlyticsExampleSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D8FA36C22F4CAF800213E06 /* Build configuration list for PBXNativeTarget "CrashlyticsExampleSwift" */; + buildPhases = ( + 8D8FA35722F4CAF700213E06 /* Sources */, + 8D8FA35822F4CAF700213E06 /* Frameworks */, + 8D8FA35922F4CAF700213E06 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CrashlyticsExampleSwift; + productName = CrashlyticsExampleSwift; + productReference = 8D8FA35B22F4CAF700213E06 /* CrashlyticsExampleSwift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8D8FA33622F4CAB100213E06 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1020; + LastUpgradeCheck = 1230; + ORGANIZATIONNAME = Firebase; + TargetAttributes = { + 8D8FA33D22F4CAB100213E06 = { + CreatedOnToolsVersion = 10.2; + }; + 8D8FA35A22F4CAF700213E06 = { + CreatedOnToolsVersion = 10.2; + }; + }; + }; + buildConfigurationList = 8D8FA33922F4CAB100213E06 /* Build configuration list for PBXProject "CrashlyticsExample" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8D8FA33522F4CAB100213E06; + packageReferences = ( + 8D7726102D2876EB00537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); + productRefGroup = 8D8FA33F22F4CAB100213E06 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D8FA33D22F4CAB100213E06 /* CrashlyticsExample */, + 8D8FA35A22F4CAF700213E06 /* CrashlyticsExampleSwift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D8FA33C22F4CAB100213E06 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA34E22F4CAB200213E06 /* LaunchScreen.storyboard in Resources */, + 8D8FA34B22F4CAB200213E06 /* Assets.xcassets in Resources */, + 8D8FA34922F4CAB100213E06 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D8FA35922F4CAF700213E06 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA36822F4CAF800213E06 /* LaunchScreen.storyboard in Resources */, + 8D8FA36522F4CAF800213E06 /* Assets.xcassets in Resources */, + 8D8FA36322F4CAF700213E06 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D8FA33A22F4CAB100213E06 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA34622F4CAB100213E06 /* ViewController.m in Sources */, + 8D8FA35122F4CAB200213E06 /* main.m in Sources */, + 8D8FA34322F4CAB100213E06 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D8FA35722F4CAF700213E06 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA36022F4CAF700213E06 /* ViewController.swift in Sources */, + 8D8FA35E22F4CAF700213E06 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 8D8FA34722F4CAB100213E06 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA34822F4CAB100213E06 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8D8FA34C22F4CAB200213E06 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA34D22F4CAB200213E06 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 8D8FA36122F4CAF700213E06 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA36222F4CAF700213E06 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8D8FA36622F4CAF800213E06 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA36722F4CAF800213E06 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 8D8FA35222F4CAB200213E06 /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 8D8FA35322F4CAB200213E06 /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8D8FA35522F4CAB200213E06 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CrashlyticsExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.CrashlyticsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D8FA35622F4CAB200213E06 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CrashlyticsExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.CrashlyticsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 8D8FA36A22F4CAF800213E06 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CrashlyticsExampleSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.CrashlyticsExampleSwift; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D8FA36B22F4CAF800213E06 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = CrashlyticsExampleSwift/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.CrashlyticsExampleSwift; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8D8FA33922F4CAB100213E06 /* Build configuration list for PBXProject "CrashlyticsExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D8FA35222F4CAB200213E06 /* Debug */, + 8D8FA35322F4CAB200213E06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D8FA35422F4CAB200213E06 /* Build configuration list for PBXNativeTarget "CrashlyticsExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D8FA35522F4CAB200213E06 /* Debug */, + 8D8FA35622F4CAB200213E06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D8FA36C22F4CAF800213E06 /* Build configuration list for PBXNativeTarget "CrashlyticsExampleSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D8FA36A22F4CAF800213E06 /* Debug */, + 8D8FA36B22F4CAF800213E06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7726102D2876EB00537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7726122D2876F400537A0B /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726102D2876EB00537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + 8D7726142D2876F400537A0B /* FirebaseCrashlytics */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726102D2876EB00537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCrashlytics; + }; + 8D7726162D2876FB00537A0B /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726102D2876EB00537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + 8D7726182D2876FB00537A0B /* FirebaseCrashlytics */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7726102D2876EB00537A0B /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCrashlytics; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 8D8FA33622F4CAB100213E06 /* Project object */; +} diff --git a/crashlytics/CrashlyticsExample/AppDelegate.h b/crashlytics/CrashlyticsExample/AppDelegate.h new file mode 100644 index 00000000..a9861fa2 --- /dev/null +++ b/crashlytics/CrashlyticsExample/AppDelegate.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/crashlytics/CrashlyticsExample/AppDelegate.m b/crashlytics/CrashlyticsExample/AppDelegate.m new file mode 100644 index 00000000..69381fe8 --- /dev/null +++ b/crashlytics/CrashlyticsExample/AppDelegate.m @@ -0,0 +1,34 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseCore; + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [FIRApp configure]; + return YES; +} + + +@end diff --git a/invites/InvitesExampleSwift/Images.xcassets/AppIcon.appiconset/Contents.json b/crashlytics/CrashlyticsExample/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 67% rename from invites/InvitesExampleSwift/Images.xcassets/AppIcon.appiconset/Contents.json rename to crashlytics/CrashlyticsExample/Assets.xcassets/AppIcon.appiconset/Contents.json index 36d2c80d..d8db8d65 100644 --- a/invites/InvitesExampleSwift/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/crashlytics/CrashlyticsExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", @@ -30,6 +40,16 @@ "size" : "60x60", "scale" : "3x" }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, { "idiom" : "ipad", "size" : "29x29", @@ -59,6 +79,16 @@ "idiom" : "ipad", "size" : "76x76", "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/crashlytics/CrashlyticsExample/Assets.xcassets/Contents.json b/crashlytics/CrashlyticsExample/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/crashlytics/CrashlyticsExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/crashlytics/CrashlyticsExample/Base.lproj/LaunchScreen.storyboard b/crashlytics/CrashlyticsExample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..bfa36129 --- /dev/null +++ b/crashlytics/CrashlyticsExample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crashlytics/CrashlyticsExample/Base.lproj/Main.storyboard b/crashlytics/CrashlyticsExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..942f0bc4 --- /dev/null +++ b/crashlytics/CrashlyticsExample/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crashlytics/CrashlyticsExample/Info.plist b/crashlytics/CrashlyticsExample/Info.plist new file mode 100644 index 00000000..16be3b68 --- /dev/null +++ b/crashlytics/CrashlyticsExample/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/invites/InvitesExample/SignInViewController.h b/crashlytics/CrashlyticsExample/ViewController.h similarity index 87% rename from invites/InvitesExample/SignInViewController.h rename to crashlytics/CrashlyticsExample/ViewController.h index 3369cd1e..f543b8a0 100644 --- a/invites/InvitesExample/SignInViewController.h +++ b/crashlytics/CrashlyticsExample/ViewController.h @@ -1,5 +1,5 @@ // -// Copyright (c) Google Inc. +// Copyright (c) 2019 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,8 +13,11 @@ // See the License for the specific language governing permissions and // limitations under the License. // + #import -@interface SignInViewController : UIViewController +@interface ViewController : UIViewController + + +@end -@end \ No newline at end of file diff --git a/crashlytics/CrashlyticsExample/ViewController.m b/crashlytics/CrashlyticsExample/ViewController.m new file mode 100644 index 00000000..3137e140 --- /dev/null +++ b/crashlytics/CrashlyticsExample/ViewController.m @@ -0,0 +1,139 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseCrashlytics; + +#import "ViewController.h" + +@interface ViewController () + +@end + +// [START forceCrash] +@implementation ViewController +- (void)viewDidLoad { + [super viewDidLoad]; + + // Do any additional setup after loading the view, typically from a nib. + + UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + button.frame = CGRectMake(20, 50, 100, 30); + [button setTitle:@"Crash" forState:UIControlStateNormal]; + [button addTarget:self action:@selector(crashButtonTapped:) + forControlEvents:UIControlEventTouchUpInside]; + [self.view addSubview:button]; +} + +- (IBAction)crashButtonTapped:(id)sender { + @[][1]; +} +// [END forceCrash] + +- (void)customizeStackTraces { + // [START customizeStackTraces] + FIRExceptionModel *model = + [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."]; + model.stackTrace = @[ + [FIRStackFrame stackFrameWithSymbol:@"makeError" file:@"handler.js" line:495], + [FIRStackFrame stackFrameWithSymbol:@"then" file:@"routes.js" line:102], + [FIRStackFrame stackFrameWithSymbol:@"main" file:@"app.js" line:12], + ]; + + [[FIRCrashlytics crashlytics] recordExceptionModel:model]; + // [END customizeStackTraces] +} + +- (void)customizeStackTracesAddress { + // [START customizeStackTracesAddress] + FIRExceptionModel *model = + [FIRExceptionModel exceptionModelWithName:@"FooException" reason:@"There was a foo."]; + model.stackTrace = @[ + [FIRStackFrame stackFrameWithAddress:0xfa12123], + [FIRStackFrame stackFrameWithAddress:12412412], + [FIRStackFrame stackFrameWithAddress:194129124], + ]; + + + [[FIRCrashlytics crashlytics] recordExceptionModel:model]; + // [END customizeStackTracesAddress] +} + +- (void)setCustomKey { + // [START setCustomKey] + // Set int_key to 100. + [[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"]; + + // Set str_key to "hello". + [[FIRCrashlytics crashlytics] setCustomValue:@"hello" forKey:@"str_key"]; + // [END setCustomKey] +} + +- (void)setCustomValue { + // [START setCustomValue] + [[FIRCrashlytics crashlytics] setCustomValue:@(100) forKey:@"int_key"]; + + // Set int_key to 50 from 100. + [[FIRCrashlytics crashlytics] setCustomValue:@(50) forKey:@"int_key"]; + // [END setCustomValue] +} + +- (void)setCustomKeys { + // [START setCustomKeys] + NSDictionary *keysAndValues = + @{@"string key" : @"string value", + @"string key 2" : @"string value 2", + @"boolean key" : @(YES), + @"boolean key 2" : @(NO), + @"float key" : @(1.01), + @"float key 2" : @(2.02)}; + + [[FIRCrashlytics crashlytics] setCustomKeysAndValues: keysAndValues]; + // [END setCustomKeys] +} + +- (void)enableOptIn { + // [START enableOptIn] + [[FIRCrashlytics crashlytics] setCrashlyticsCollectionEnabled:YES]; + // [END enableOptIn] +} + +- (void)logExceptions { + // [START createError] + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"The request failed.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The response returned a 404.", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Does this page exist?", nil), + @"ProductID": @"123456", + @"View": @"MainView", + }; + + NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain + code:-1001 + userInfo:userInfo]; + // [END createError] + + // [START recordError] + [[FIRCrashlytics crashlytics] recordError:error]; + // [END recordError] +} + +- (void)setUserId { + // [START setUserId] + [[FIRCrashlytics crashlytics] setUserID:@"123456789"]; + // [END setUserId] +} + +@end diff --git a/crashlytics/CrashlyticsExample/main.m b/crashlytics/CrashlyticsExample/main.m new file mode 100644 index 00000000..8d31108d --- /dev/null +++ b/crashlytics/CrashlyticsExample/main.m @@ -0,0 +1,24 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/crashlytics/CrashlyticsExampleSwift/AppDelegate.swift b/crashlytics/CrashlyticsExampleSwift/AppDelegate.swift new file mode 100644 index 00000000..6a384187 --- /dev/null +++ b/crashlytics/CrashlyticsExampleSwift/AppDelegate.swift @@ -0,0 +1,31 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseCore + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FirebaseApp.configure() + return true + } + +} + diff --git a/crashlytics/CrashlyticsExampleSwift/Assets.xcassets/AppIcon.appiconset/Contents.json b/crashlytics/CrashlyticsExampleSwift/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/crashlytics/CrashlyticsExampleSwift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/crashlytics/CrashlyticsExampleSwift/Assets.xcassets/Contents.json b/crashlytics/CrashlyticsExampleSwift/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/crashlytics/CrashlyticsExampleSwift/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/crashlytics/CrashlyticsExampleSwift/Base.lproj/LaunchScreen.storyboard b/crashlytics/CrashlyticsExampleSwift/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..bfa36129 --- /dev/null +++ b/crashlytics/CrashlyticsExampleSwift/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crashlytics/CrashlyticsExampleSwift/Base.lproj/Main.storyboard b/crashlytics/CrashlyticsExampleSwift/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f1bcf384 --- /dev/null +++ b/crashlytics/CrashlyticsExampleSwift/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crashlytics/CrashlyticsExampleSwift/Info.plist b/crashlytics/CrashlyticsExampleSwift/Info.plist new file mode 100644 index 00000000..16be3b68 --- /dev/null +++ b/crashlytics/CrashlyticsExampleSwift/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/crashlytics/CrashlyticsExampleSwift/ViewController.swift b/crashlytics/CrashlyticsExampleSwift/ViewController.swift new file mode 100644 index 00000000..55f61401 --- /dev/null +++ b/crashlytics/CrashlyticsExampleSwift/ViewController.swift @@ -0,0 +1,135 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseCrashlytics + +// [START forceCrash] +class ViewController: UIViewController { + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view, typically from a nib. + + let button = UIButton(type: .roundedRect) + button.frame = CGRect(x: 20, y: 50, width: 100, height: 30) + button.setTitle("Crash", for: []) + button.addTarget(self, action: #selector(self.crashButtonTapped(_:)), for: .touchUpInside) + view.addSubview(button) + } + + @IBAction func crashButtonTapped(_ sender: AnyObject) { + fatalError() + } + // [END forceCrash] + + // [START crashlytics_define] + lazy var crashlytics = Crashlytics.crashlytics() + // [END crashlytics_define] + + + func customizeStackTraces() { + // [START customizeStackTraces] + var ex = ExceptionModel(name:"FooException", reason:"There was a foo.") + ex.stackTrace = [ + StackFrame(symbol:"makeError", file:"handler.js", line:495), + StackFrame(symbol:"then", file:"routes.js", line:102), + StackFrame(symbol:"main", file:"app.js", line:12), + ] + + crashlytics.record(exceptionModel:ex) + // [END customizeStackTraces] + } + + func customizeStackTracesAddress() { + // [START customizeStackTracesAddress] + var ex = ExceptionModel.init(name:"FooException", reason:"There was a foo.") + ex.stackTrace = [ + StackFrame(address:0xfa12123), + StackFrame(address:12412412), + StackFrame(address:194129124), + ] + + crashlytics.record(exceptionModel:ex) + // [END customizeStackTracesAddress] + } + + func setCustomKey() { + // [START setCustomKey] + // Set int_key to 100. + Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key") + + // Set str_key to "hello". + Crashlytics.crashlytics().setCustomValue("hello", forKey: "str_key") + // [END setCustomKey] + } + + func setCustomValue() { + // [START setCustomValue] + Crashlytics.crashlytics().setCustomValue(100, forKey: "int_key") + + // Set int_key to 50 from 100. + Crashlytics.crashlytics().setCustomValue(50, forKey: "int_key") + // [END setCustomValue] + } + + func setCustomKeys() { + // [START setCustomKeys] + let keysAndValues = [ + "string key" : "string value", + "string key 2" : "string value 2", + "boolean key" : true, + "boolean key 2" : false, + "float key" : 1.01, + "float key 2" : 2.02 + ] as [String : Any] + + Crashlytics.crashlytics().setCustomKeysAndValues(keysAndValues) + // [END setCustomKeys] + } + + func enableOptIn() { + // [START enableOptIn] + Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true) + // [END enableOptIn] + } + + func logExceptions() { + // [START createError] + let userInfo = [ + NSLocalizedDescriptionKey: NSLocalizedString("The request failed.", comment: ""), + NSLocalizedFailureReasonErrorKey: NSLocalizedString("The response returned a 404.", comment: ""), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString("Does this page exist?", comment: ""), + "ProductID": "123456", + "View": "MainView" + ] + + let error = NSError.init(domain: NSCocoaErrorDomain, + code: -1001, + userInfo: userInfo) + // [END createError] + + // [START recordError] + Crashlytics.crashlytics().record(error: error) + // [END recordError] + } + + func setUserId() { + // [START setUserId] + Crashlytics.crashlytics().setUserID("123456789") + // [END setUserId] + } +} diff --git a/database/DatabaseReference.xcodeproj/project.pbxproj b/database/DatabaseReference.xcodeproj/project.pbxproj index 1401de77..f4c85ed6 100644 --- a/database/DatabaseReference.xcodeproj/project.pbxproj +++ b/database/DatabaseReference.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -11,6 +11,8 @@ 8D19B3751EA7D49400451CA7 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D19B3731EA7D49400451CA7 /* ViewController.m */; }; 8D19B3781EA7D4A300451CA7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D19B3761EA7D4A300451CA7 /* AppDelegate.swift */; }; 8D19B3791EA7D4A300451CA7 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D19B3771EA7D4A300451CA7 /* ViewController.swift */; }; + 8D474FD62D28A9CF000B5C38 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 8D474FD52D28A9CF000B5C38 /* FirebaseAuth */; }; + 8D474FD82D28A9CF000B5C38 /* FirebaseDatabase in Frameworks */ = {isa = PBXBuildFile; productRef = 8D474FD72D28A9CF000B5C38 /* FirebaseDatabase */; }; 8DDD574C1EA6D39D00DD14EB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8DDD574A1EA6D39D00DD14EB /* Main.storyboard */; }; 8DDD574E1EA6D39D00DD14EB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8DDD574D1EA6D39D00DD14EB /* Assets.xcassets */; }; 8DDD57511EA6D39D00DD14EB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8DDD574F1EA6D39D00DD14EB /* LaunchScreen.storyboard */; }; @@ -35,12 +37,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D474FD82D28A9CF000B5C38 /* FirebaseDatabase in Frameworks */, + 8D474FD62D28A9CF000B5C38 /* FirebaseAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 8D474FD42D28A9CF000B5C38 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 8D5CA1551EA6D5490024095C /* ObjC */ = { isa = PBXGroup; children = ( @@ -65,6 +76,7 @@ isa = PBXGroup; children = ( 8DDD57451EA6D39D00DD14EB /* DatabaseReference */, + 8D474FD42D28A9CF000B5C38 /* Frameworks */, 8DDD57441EA6D39D00DD14EB /* Products */, ); sourceTree = ""; @@ -117,7 +129,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1230; ORGANIZATIONNAME = "Morgan Chen"; TargetAttributes = { 8DDD57421EA6D39D00DD14EB = { @@ -132,10 +144,14 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); mainGroup = 8DDD573A1EA6D39D00DD14EB; + packageReferences = ( + 8D474FD32D28A9C3000B5C38 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 8DDD57441EA6D39D00DD14EB /* Products */; projectDirPath = ""; projectRoot = ""; @@ -233,12 +249,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -278,10 +295,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -293,12 +312,16 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; INFOPLIST_FILE = DatabaseReference/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.referencecode.DatabaseReference; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -308,11 +331,15 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; INFOPLIST_FILE = DatabaseReference/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.referencecode.DatabaseReference; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -338,6 +365,30 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D474FD32D28A9C3000B5C38 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D474FD52D28A9CF000B5C38 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 8D474FD32D28A9C3000B5C38 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 8D474FD72D28A9CF000B5C38 /* FirebaseDatabase */ = { + isa = XCSwiftPackageProductDependency; + package = 8D474FD32D28A9C3000B5C38 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseDatabase; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 8DDD573B1EA6D39D00DD14EB /* Project object */; } diff --git a/database/DatabaseReference.xcworkspace/contents.xcworkspacedata b/database/DatabaseReference.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index b4e48b12..00000000 --- a/database/DatabaseReference.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/database/DatabaseReference/objc/AppDelegate.m b/database/DatabaseReference/objc/AppDelegate.m index b08eec1b..91461ffc 100644 --- a/database/DatabaseReference/objc/AppDelegate.m +++ b/database/DatabaseReference/objc/AppDelegate.m @@ -14,7 +14,8 @@ // limitations under the License. // -@import Firebase; +@import FirebaseCore; +@import FirebaseDatabase; #import "AppDelegate.h" diff --git a/database/DatabaseReference/objc/ViewController.m b/database/DatabaseReference/objc/ViewController.m index 457667d0..fa1844f1 100644 --- a/database/DatabaseReference/objc/ViewController.m +++ b/database/DatabaseReference/objc/ViewController.m @@ -13,7 +13,9 @@ // See the License for the specific language governing permissions and // limitations under the License. // -@import Firebase; + +@import FirebaseAuth; +@import FirebaseDatabase; #import "ViewController.h" @@ -25,16 +27,6 @@ @interface ViewController () @implementation ViewController -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view. -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - - (void)persistenceReference { // [START keep_synchronized] FIRDatabaseReference *scoresRef = [[FIRDatabase database] referenceWithPath:@"scores"]; @@ -161,4 +153,28 @@ - (void)writeNewUserWithCompletion:(FIRUser *)user withName:(NSString *)username // [END rtdb_write_new_user_completion] } +- (void)singleUseGetDataForUserId:(NSString *)uid { + FIRDatabaseReference *ref = [FIRDatabase database].reference; + // [START single_value_get_data] + NSString *userPath = [NSString stringWithFormat:@"users/%@/username", uid]; + [[ref child:userPath] getDataWithCompletionBlock:^(NSError * _Nullable error, FIRDataSnapshot * _Nonnull snapshot) { + if (error) { + NSLog(@"Received an error %@", error); + return; + } + NSString *userName = snapshot.value; + }]; + // [END single_value_get_data] +} + +- (void)incrementStarsForPost:(NSString *)postID byUser: (NSString *) userID { + // [START rtdb_post_stars_increment] + NSDictionary *updates = @{[NSString stringWithFormat: @"posts/%@/stars/%@", postID, userID]: @TRUE, + [NSString stringWithFormat: @"posts/%@/starCount", postID]: [FIRServerValue increment:@1], + [NSString stringWithFormat: @"user-posts/%@/stars/%@", postID, userID]: @TRUE, + [NSString stringWithFormat: @"user-posts/%@/starCount", postID]: [FIRServerValue increment:@1]}; + [[[FIRDatabase database] reference] updateChildValues:updates]; + // [END rtdb_post_stars_increment] +} + @end diff --git a/database/DatabaseReference/swift/AppDelegate.swift b/database/DatabaseReference/swift/AppDelegate.swift index a30a1b46..60171b17 100644 --- a/database/DatabaseReference/swift/AppDelegate.swift +++ b/database/DatabaseReference/swift/AppDelegate.swift @@ -16,7 +16,8 @@ import UIKit -import Firebase +import FirebaseCore +import FirebaseDatabase @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -24,7 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() diff --git a/database/DatabaseReference/swift/ViewController.swift b/database/DatabaseReference/swift/ViewController.swift index 12cab099..84b6291f 100644 --- a/database/DatabaseReference/swift/ViewController.swift +++ b/database/DatabaseReference/swift/ViewController.swift @@ -16,22 +16,13 @@ import UIKit -import Firebase +import FirebaseAuth +import FirebaseDatabase class ViewController: UIViewController { var ref: DatabaseReference! - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - func persistenceReference() { // [START keep_synchronized] let scoresRef = Database.database().reference(withPath: "scores") @@ -103,39 +94,61 @@ class ViewController: UIViewController { // [END clock_skew] } - func writeNewUser(_ user: Firebase.User, withUsername username: String) { + func writeNewUser(_ user: FirebaseAuth.User, withUsername username: String) { // [START rtdb_write_new_user] ref.child("users").child(user.uid).setValue(["username": username]) // [END rtdb_write_new_user] } - func writeNewUserWithCompletion(_ user: Firebase.User, withUsername username: String) { + func writeNewUserWithCompletion(_ user: FirebaseAuth.User, withUsername username: String) async { // [START rtdb_write_new_user_completion] - ref.child("users").child(user.uid).setValue(["username": username]) { - (error:Error?, ref:DatabaseReference) in - if let error = error { - print("Data could not be saved: \(error).") - } else { - print("Data saved successfully!") - } + do { + try await ref.child("users").child(user.uid).setValue(["username": username]) + print("Data saved successfully!") + } catch { + print("Data could not be saved: \(error).") } // [END rtdb_write_new_user_completion] } - func emulatorSettings(Database: Database) { - // [START rtdb_emulator_connect] + func singleUseFetchData(uid: String) async { + let ref = Database.database().reference() + // [START single_value_get_data] + do { + let snapshot = try await ref.child("users/\(uid)/username").getData() + let userName = snapshot.value as? String ?? "Unknown" + } catch { + print(error) + } + // [END single_value_get_data] + } + + func emulatorSettings() { + // [START rtdb_emulator_connect] // In almost all cases the ns (namespace) is your project ID. - let db = Database.database(url:@"http://localhost:9000?ns=YOUR_DATABASE_NAMESPACE") - // [END rtdb_emulator_connect] + let db = Database.database(url:"http://127.0.0.1:9000?ns=YOUR_DATABASE_NAMESPACE") + // [END rtdb_emulator_connect] } - func flushRealtimeDatabase(Database: Database) { - // [START rtdb_emulator_flush] + func flushRealtimeDatabase() { + // [START rtdb_emulator_flush] // With a DatabaseReference, write nil to clear the database. - Database.database().reference().setValue(nil); + Database.database().reference().setValue(nil) // [END rtdb_emulator_flush] } + func incrementStars(forPost postID: String, byUser userID: String) { + // [START rtdb_post_stars_increment] + let updates = [ + "posts/\(postID)/stars/\(userID)": true, + "posts/\(postID)/starCount": ServerValue.increment(1), + "user-posts/\(postID)/stars/\(userID)": true, + "user-posts/\(postID)/starCount": ServerValue.increment(1) + ] as [String : Any] + Database.database().reference().updateChildValues(updates) + // [END rtdb_post_stars_increment] + } + } func combinedExample() { @@ -151,7 +164,7 @@ func combinedExample() { connectedRef.observe(.value, with: { snapshot in // only handle connection established (or I've reconnected after a loss of connection) - guard let connected = snapshot.value as? Bool, connected else { return } + guard snapshot.value as? Bool ?? false else { return } // add this device to my connections list let con = myConnectionsRef.childByAutoId() diff --git a/database/Podfile b/database/Podfile deleted file mode 100644 index cb4e2823..00000000 --- a/database/Podfile +++ /dev/null @@ -1,8 +0,0 @@ -# Firebase Database ReferenceCode -use_frameworks! -platform :ios, '7.0' -pod 'Firebase/Database' -pod 'Firebase/Auth' - -target 'DatabaseReference' do -end diff --git a/database/Podfile.lock b/database/Podfile.lock deleted file mode 100644 index 57373b70..00000000 --- a/database/Podfile.lock +++ /dev/null @@ -1,77 +0,0 @@ -PODS: - - Firebase/Auth (4.13.0): - - Firebase/Core - - FirebaseAuth (= 4.6.1) - - Firebase/Core (4.13.0): - - FirebaseAnalytics (= 4.2.0) - - FirebaseCore (= 4.0.20) - - Firebase/Database (4.13.0): - - Firebase/Core - - FirebaseDatabase (= 4.1.5) - - FirebaseAnalytics (4.2.0): - - FirebaseCore (~> 4.0) - - FirebaseInstanceID (~> 2.0) - - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - - nanopb (~> 0.3) - - FirebaseAuth (4.6.1): - - FirebaseAnalytics (~> 4.2) - - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" - - GTMSessionFetcher/Core (~> 1.1) - - FirebaseCore (4.0.20): - - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - - FirebaseDatabase (4.1.5): - - FirebaseAnalytics (~> 4.1) - - FirebaseCore (~> 4.0) - - leveldb-library (~> 1.18) - - FirebaseInstanceID (2.0.10): - - FirebaseCore (~> 4.0) - - GoogleToolboxForMac/DebugUtils (2.1.4): - - GoogleToolboxForMac/Defines (= 2.1.4) - - GoogleToolboxForMac/Defines (2.1.4) - - "GoogleToolboxForMac/NSData+zlib (2.1.4)": - - GoogleToolboxForMac/Defines (= 2.1.4) - - "GoogleToolboxForMac/NSDictionary+URLArguments (2.1.4)": - - GoogleToolboxForMac/DebugUtils (= 2.1.4) - - GoogleToolboxForMac/Defines (= 2.1.4) - - "GoogleToolboxForMac/NSString+URLArguments (= 2.1.4)" - - "GoogleToolboxForMac/NSString+URLArguments (2.1.4)" - - GTMSessionFetcher/Core (1.2.2) - - leveldb-library (1.22) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - -DEPENDENCIES: - - Firebase/Auth - - Firebase/Database - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - Firebase - - FirebaseAnalytics - - FirebaseAuth - - FirebaseCore - - FirebaseDatabase - - FirebaseInstanceID - - GoogleToolboxForMac - - GTMSessionFetcher - - leveldb-library - - nanopb - -SPEC CHECKSUMS: - Firebase: 5ec5e863d269d82d66b4bf56856726f8fb8f0fb3 - FirebaseAnalytics: 7ef69e76a5142f643aeb47c780e1cdce4e23632e - FirebaseAuth: bf22cacf22c60ab454bf2636f556d8892b10b53f - FirebaseCore: 90cb1c53d69b556f112a1bf72b5fcfaad7650790 - FirebaseDatabase: 5f0bc6134c5c237cf55f9e1249d406770a75eafd - FirebaseInstanceID: 8d20d890d65c917f9f7d9950b6e10a760ad34321 - GoogleToolboxForMac: 91c824d21e85b31c2aae9bb011c5027c9b4e738f - GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - -PODFILE CHECKSUM: 2d6d3f5d6efef8839436d4bcca6d595b46547d30 - -COCOAPODS: 1.7.5 diff --git a/dl-invites-sample/InvitesSample/AppDelegate.swift b/dl-invites-sample/InvitesSample/AppDelegate.swift index 3678f797..1f056d87 100644 --- a/dl-invites-sample/InvitesSample/AppDelegate.swift +++ b/dl-invites-sample/InvitesSample/AppDelegate.swift @@ -16,7 +16,7 @@ import UIKit -import Firebase +import FirebaseCore @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/dl-invites-sample/Podfile b/dl-invites-sample/Podfile index 356db6a6..145c4968 100644 --- a/dl-invites-sample/Podfile +++ b/dl-invites-sample/Podfile @@ -5,7 +5,6 @@ target 'InvitesSample' do use_frameworks! pod 'Firebase/DynamicLinks' - pod 'Firebase/Core' target 'InvitesSampleTests' do inherit! :search_paths @@ -24,6 +23,5 @@ target 'InvitesSampleObjC' do use_frameworks! pod 'Firebase/DynamicLinks' - pod 'Firebase/Core' end diff --git a/dl-invites-sample/Podfile.lock b/dl-invites-sample/Podfile.lock index 4c43404a..a38be10b 100644 --- a/dl-invites-sample/Podfile.lock +++ b/dl-invites-sample/Podfile.lock @@ -1,109 +1,57 @@ PODS: - - Firebase/Core (6.8.0): + - Firebase/CoreOnly (6.34.0): + - FirebaseCore (= 6.10.4) + - Firebase/DynamicLinks (6.34.0): - Firebase/CoreOnly - - FirebaseAnalytics (= 6.1.1) - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/DynamicLinks (6.8.0): - - Firebase/CoreOnly - - FirebaseDynamicLinks (~> 4.0.4) - - FirebaseAnalytics (6.1.1): - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.1.1) - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - FirebaseAnalyticsInterop (1.4.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseDynamicLinks (4.0.4): - - FirebaseAnalyticsInterop (~> 1.3) - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (4.2.4): - - FirebaseCore (~> 6.0) - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/UserDefaults (~> 6.0) - - GoogleAppMeasurement (6.1.1): - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleUtilities/AppDelegateSwizzler (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): + - FirebaseDynamicLinks (~> 4.3.1) + - FirebaseCore (6.10.4): + - FirebaseCoreDiagnostics (~> 1.6) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - FirebaseCoreDiagnostics (1.7.0): + - GoogleDataTransport (~> 7.4) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - nanopb (~> 1.30906.0) + - FirebaseDynamicLinks (4.3.1): + - FirebaseCore (~> 6.10) + - GoogleDataTransport (7.5.1): + - nanopb (~> 1.30906.0) + - GoogleUtilities/Environment (6.7.2): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (6.7.2): - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.2.5): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.2.5)" - - GoogleUtilities/Reachability (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.2.5): - - GoogleUtilities/Logger - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) + - nanopb (1.30906.0): + - nanopb/decode (= 1.30906.0) + - nanopb/encode (= 1.30906.0) + - nanopb/decode (1.30906.0) + - nanopb/encode (1.30906.0) + - PromisesObjC (1.2.12) DEPENDENCIES: - - Firebase/Core - Firebase/DynamicLinks SPEC REPOS: - https://github.com/cocoapods/specs.git: + trunk: - Firebase - - FirebaseAnalytics - - FirebaseAnalyticsInterop - FirebaseCore - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - FirebaseDynamicLinks - - FirebaseInstanceID - - GoogleAppMeasurement - GoogleDataTransport - - GoogleDataTransportCCTSupport - GoogleUtilities - nanopb + - PromisesObjC SPEC CHECKSUMS: - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseAnalytics: 843c7f64a8f9c79f0d03281197ebe7bb1d58d477 - FirebaseAnalyticsInterop: d48b6ab67bcf016a05e55b71fc39c61c0cb6b7f3 - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseDynamicLinks: 585355f9de854b847059188bb845b7dceae18cd5 - FirebaseInstanceID: 88932a31aba5a56cfd3a7541706436c71f7f4598 - GoogleAppMeasurement: 86a82f0e1f20b8eedf8e20326530138fd71409de - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 + Firebase: c23a36d9e4cdf7877dfcba8dd0c58add66358999 + FirebaseCore: d3a978a3cfa3240bf7e4ba7d137fdf5b22b628ec + FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 + FirebaseDynamicLinks: 6eac37d86910382eafb6315d952cc44c9e176094 + GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 + GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc + PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 -PODFILE CHECKSUM: ea2286f19740542810d85972377bee24b5e93a82 +PODFILE CHECKSUM: 7de02ed38e9465e2c05bb12085a971c35543c261 -COCOAPODS: 1.7.5 +COCOAPODS: 1.15.2 diff --git a/firestore/objc/Podfile b/firestore/objc/Podfile deleted file mode 100644 index b939fbea..00000000 --- a/firestore/objc/Podfile +++ /dev/null @@ -1,18 +0,0 @@ -# Uncomment the next line to define a global platform for your project -# platform :ios, '9.0' - -target 'firestore-smoketest-objc' do - - use_frameworks! - - # Pods for firestore-smoketest - pod 'Firebase/Core' - pod 'Firebase/Auth' - pod 'Firebase/Firestore' - - target 'firestore-smoketest-objcTests' do - inherit! :search_paths - # Pods for testing - end - -end diff --git a/firestore/objc/Podfile.lock b/firestore/objc/Podfile.lock deleted file mode 100644 index 6fab61b5..00000000 --- a/firestore/objc/Podfile.lock +++ /dev/null @@ -1,170 +0,0 @@ -PODS: - - BoringSSL-GRPC (0.0.3): - - BoringSSL-GRPC/Implementation (= 0.0.3) - - BoringSSL-GRPC/Interface (= 0.0.3) - - BoringSSL-GRPC/Implementation (0.0.3): - - BoringSSL-GRPC/Interface (= 0.0.3) - - BoringSSL-GRPC/Interface (0.0.3) - - Firebase/Auth (6.8.0): - - Firebase/CoreOnly - - FirebaseAuth (~> 6.2.3) - - Firebase/Core (6.8.0): - - Firebase/CoreOnly - - FirebaseAnalytics (= 6.1.1) - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/Firestore (6.8.0): - - Firebase/CoreOnly - - FirebaseFirestore (~> 1.5.0) - - FirebaseAnalytics (6.1.1): - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.1.1) - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - FirebaseAuth (6.2.3): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.2) - - GoogleUtilities/AppDelegateSwizzler (~> 6.2) - - GoogleUtilities/Environment (~> 6.2) - - GTMSessionFetcher/Core (~> 1.1) - - FirebaseAuthInterop (1.0.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseFirestore (1.5.0): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.2) - - FirebaseFirestore/abseil-cpp (= 1.5.0) - - "gRPC-C++ (= 0.0.9)" - - leveldb-library (~> 1.22) - - nanopb (~> 0.3.901) - - Protobuf (~> 3.1) - - FirebaseFirestore/abseil-cpp (1.5.0): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.2) - - "gRPC-C++ (= 0.0.9)" - - leveldb-library (~> 1.22) - - nanopb (~> 0.3.901) - - Protobuf (~> 3.1) - - FirebaseInstanceID (4.2.4): - - FirebaseCore (~> 6.0) - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/UserDefaults (~> 6.0) - - GoogleAppMeasurement (6.1.1): - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleUtilities/AppDelegateSwizzler (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.2.5): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.2.5)" - - GoogleUtilities/Reachability (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.2.5): - - GoogleUtilities/Logger - - "gRPC-C++ (0.0.9)": - - "gRPC-C++/Implementation (= 0.0.9)" - - "gRPC-C++/Interface (= 0.0.9)" - - "gRPC-C++/Implementation (0.0.9)": - - "gRPC-C++/Interface (= 0.0.9)" - - gRPC-Core (= 1.21.0) - - nanopb (~> 0.3) - - "gRPC-C++/Interface (0.0.9)" - - gRPC-Core (1.21.0): - - gRPC-Core/Implementation (= 1.21.0) - - gRPC-Core/Interface (= 1.21.0) - - gRPC-Core/Implementation (1.21.0): - - BoringSSL-GRPC (= 0.0.3) - - gRPC-Core/Interface (= 1.21.0) - - nanopb (~> 0.3) - - gRPC-Core/Interface (1.21.0) - - GTMSessionFetcher/Core (1.2.2) - - leveldb-library (1.22) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - - Protobuf (3.9.0) - -DEPENDENCIES: - - Firebase/Auth - - Firebase/Core - - Firebase/Firestore - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - BoringSSL-GRPC - - Firebase - - FirebaseAnalytics - - FirebaseAuth - - FirebaseAuthInterop - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - - FirebaseFirestore - - FirebaseInstanceID - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleDataTransportCCTSupport - - GoogleUtilities - - "gRPC-C++" - - gRPC-Core - - GTMSessionFetcher - - leveldb-library - - nanopb - - Protobuf - -SPEC CHECKSUMS: - BoringSSL-GRPC: db8764df3204ccea016e1c8dd15d9a9ad63ff318 - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseAnalytics: 843c7f64a8f9c79f0d03281197ebe7bb1d58d477 - FirebaseAuth: e7f86c2dfc57281cd01f7da5e4b40e01e4510a4a - FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseFirestore: c5873e279490fbe02239ab2cdfb91c2d546261cc - FirebaseInstanceID: 88932a31aba5a56cfd3a7541706436c71f7f4598 - GoogleAppMeasurement: 86a82f0e1f20b8eedf8e20326530138fd71409de - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - "gRPC-C++": 9dfe7b44821e7b3e44aacad2af29d2c21f7cde83 - gRPC-Core: c9aef9a261a1247e881b18059b84d597293c9947 - GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - Protobuf: 1097ca58584c8d9be81bfbf2c5ff5975648dd87a - -PODFILE CHECKSUM: 6e8f8775e6e1f54c9a7ec4d1131f8b81f76c1a9c - -COCOAPODS: 1.7.5 diff --git a/firestore/objc/firestore-smoketest-objc.xcodeproj/project.pbxproj b/firestore/objc/firestore-smoketest-objc.xcodeproj/project.pbxproj index 003801c2..2c8e8536 100644 --- a/firestore/objc/firestore-smoketest-objc.xcodeproj/project.pbxproj +++ b/firestore/objc/firestore-smoketest-objc.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -18,6 +18,9 @@ 8D70FC2A1F4CAE1B00C7F603 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D70FC291F4CAE1B00C7F603 /* Assets.xcassets */; }; 8D70FC2D1F4CAE1B00C7F603 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D70FC2B1F4CAE1B00C7F603 /* LaunchScreen.storyboard */; }; 8D70FC381F4CAE1B00C7F603 /* firestore_smoketest_objcTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D70FC371F4CAE1B00C7F603 /* firestore_smoketest_objcTests.m */; }; + 8D79519D2D28ABAD000FD694 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 8D79519C2D28ABAD000FD694 /* FirebaseAuth */; }; + 8D79519F2D28ABAD000FD694 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D79519E2D28ABAD000FD694 /* FirebaseFirestore */; }; + 8D9644E7260D565A002A46C9 /* FIRSolutionsBundleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D9644E6260D565A002A46C9 /* FIRSolutionsBundleViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -51,6 +54,8 @@ 8D70FC331F4CAE1B00C7F603 /* firestore-smoketest-objcTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "firestore-smoketest-objcTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 8D70FC371F4CAE1B00C7F603 /* firestore_smoketest_objcTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = firestore_smoketest_objcTests.m; sourceTree = ""; }; 8D70FC391F4CAE1B00C7F603 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D9644E5260D565A002A46C9 /* FIRSolutionsBundleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FIRSolutionsBundleViewController.h; sourceTree = ""; }; + 8D9644E6260D565A002A46C9 /* FIRSolutionsBundleViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRSolutionsBundleViewController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,6 +63,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D79519D2D28ABAD000FD694 /* FirebaseAuth in Frameworks */, + 8D79519F2D28ABAD000FD694 /* FirebaseFirestore in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -77,6 +84,7 @@ 8D4F2EF1201162EE002ED308 /* GoogleService-Info.plist */, 8D70FC1C1F4CAE1B00C7F603 /* firestore-smoketest-objc */, 8D70FC361F4CAE1B00C7F603 /* firestore-smoketest-objcTests */, + 8D79519B2D28ABAD000FD694 /* Frameworks */, 8D70FC1B1F4CAE1B00C7F603 /* Products */, ); sourceTree = ""; @@ -103,6 +111,8 @@ 8D669A7420F423B200F08512 /* FIRSolutionCountersViewController.m */, 8D669A7620F423B200F08512 /* FIRSolutionArraysViewController.h */, 8D669A7520F423B200F08512 /* FIRSolutionArraysViewController.m */, + 8D9644E5260D565A002A46C9 /* FIRSolutionsBundleViewController.h */, + 8D9644E6260D565A002A46C9 /* FIRSolutionsBundleViewController.m */, 8D70FC261F4CAE1B00C7F603 /* Main.storyboard */, 8D70FC291F4CAE1B00C7F603 /* Assets.xcassets */, 8D70FC2B1F4CAE1B00C7F603 /* LaunchScreen.storyboard */, @@ -129,6 +139,13 @@ path = "firestore-smoketest-objcTests"; sourceTree = ""; }; + 8D79519B2D28ABAD000FD694 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -173,7 +190,7 @@ 8D70FC121F4CAE1B00C7F603 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1110; ORGANIZATIONNAME = Firebase; TargetAttributes = { 8D70FC191F4CAE1B00C7F603 = { @@ -189,13 +206,16 @@ }; buildConfigurationList = 8D70FC151F4CAE1B00C7F603 /* Build configuration list for PBXProject "firestore-smoketest-objc" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 8D70FC111F4CAE1B00C7F603; + packageReferences = ( + 8D79519A2D28AB9C000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 8D70FC1B1F4CAE1B00C7F603 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -238,6 +258,7 @@ 8D669A7B20F423B200F08512 /* FIRSolutionArraysViewController.m in Sources */, 8D669A7A20F423B200F08512 /* FIRSolutionCountersViewController.m in Sources */, 8D70FC1F1F4CAE1B00C7F603 /* main.m in Sources */, + 8D9644E7260D565A002A46C9 /* FIRSolutionsBundleViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -283,21 +304,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -332,21 +362,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -376,7 +415,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "firestore-smoketest-objc/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.firebase.firestore-smoketest-objc"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -387,7 +429,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "firestore-smoketest-objc/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.firebase.firestore-smoketest-objc"; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -398,7 +443,11 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = "firestore-smoketest-objcTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.firebase.firestore-smoketest-objcTests"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/firestore-smoketest-objc.app/firestore-smoketest-objc"; @@ -410,7 +459,11 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = "firestore-smoketest-objcTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.firebase.firestore-smoketest-objcTests"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/firestore-smoketest-objc.app/firestore-smoketest-objc"; @@ -448,6 +501,30 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D79519A2D28AB9C000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D79519C2D28ABAD000FD694 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 8D79519A2D28AB9C000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 8D79519E2D28ABAD000FD694 /* FirebaseFirestore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D79519A2D28AB9C000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFirestore; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 8D70FC121F4CAE1B00C7F603 /* Project object */; } diff --git a/firestore/objc/firestore-smoketest-objc/AppDelegate.m b/firestore/objc/firestore-smoketest-objc/AppDelegate.m index c24d8c25..cf614966 100644 --- a/firestore/objc/firestore-smoketest-objc/AppDelegate.m +++ b/firestore/objc/firestore-smoketest-objc/AppDelegate.m @@ -16,7 +16,8 @@ #import "AppDelegate.h" -@import Firebase; +@import FirebaseCore; +@import FirebaseFirestore; @interface AppDelegate () diff --git a/firestore/objc/firestore-smoketest-objc/FIRSolutionAggregationViewController.m b/firestore/objc/firestore-smoketest-objc/FIRSolutionAggregationViewController.m index e0b45df2..0ce39f68 100644 --- a/firestore/objc/firestore-smoketest-objc/FIRSolutionAggregationViewController.m +++ b/firestore/objc/firestore-smoketest-objc/FIRSolutionAggregationViewController.m @@ -14,7 +14,7 @@ // limitations under the License. // -@import Firebase; +@import FirebaseFirestore; #import "FIRSolutionAggregationViewController.h" diff --git a/firestore/objc/firestore-smoketest-objc/FIRSolutionArraysViewController.m b/firestore/objc/firestore-smoketest-objc/FIRSolutionArraysViewController.m index a36a7831..9671a517 100644 --- a/firestore/objc/firestore-smoketest-objc/FIRSolutionArraysViewController.m +++ b/firestore/objc/firestore-smoketest-objc/FIRSolutionArraysViewController.m @@ -14,7 +14,7 @@ // limitations under the License. // -@import Firebase; +@import FirebaseFirestore; #import "FIRSolutionArraysViewController.h" diff --git a/firestore/objc/firestore-smoketest-objc/FIRSolutionCountersViewController.m b/firestore/objc/firestore-smoketest-objc/FIRSolutionCountersViewController.m index 34cbfd52..2e788385 100644 --- a/firestore/objc/firestore-smoketest-objc/FIRSolutionCountersViewController.m +++ b/firestore/objc/firestore-smoketest-objc/FIRSolutionCountersViewController.m @@ -14,7 +14,7 @@ // limitations under the License. // -@import Firebase; +@import FirebaseFirestore; #import "FIRSolutionCountersViewController.h" diff --git a/firestore/objc/firestore-smoketest-objc/FIRSolutionsBundleViewController.h b/firestore/objc/firestore-smoketest-objc/FIRSolutionsBundleViewController.h new file mode 100644 index 00000000..daef62ef --- /dev/null +++ b/firestore/objc/firestore-smoketest-objc/FIRSolutionsBundleViewController.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRSolutionsBundleViewController : UIViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/firestore/objc/firestore-smoketest-objc/FIRSolutionsBundleViewController.m b/firestore/objc/firestore-smoketest-objc/FIRSolutionsBundleViewController.m new file mode 100644 index 00000000..2e3efe55 --- /dev/null +++ b/firestore/objc/firestore-smoketest-objc/FIRSolutionsBundleViewController.m @@ -0,0 +1,154 @@ +// +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseFirestore; + +#import "FIRSolutionsBundleViewController.h" + +@interface FIRSolutionsBundleViewController () + +@end + +@implementation FIRSolutionsBundleViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +// [START fs_bundle_load] +// Utility function for errors when loading bundles. +- (NSError *)bundleLoadErrorWithReason:(NSString *)reason { + return [NSError errorWithDomain:@"FIRSampleErrorDomain" + code:0 + userInfo:@{NSLocalizedFailureReasonErrorKey: reason}]; +} + +// Loads a remote bundle from the provided url. +- (void)fetchRemoteBundleForFirestore:(FIRFirestore *)firestore + fromURL:(NSURL *)url + completion:(void (^)(FIRLoadBundleTaskProgress *_Nullable, + NSError *_Nullable))completion { + NSInputStream *inputStream = [NSInputStream inputStreamWithURL:url]; + if (inputStream == nil) { + // Unable to create input stream. + NSError *error = + [self bundleLoadErrorWithReason: + [NSString stringWithFormat:@"Unable to create stream from the given url: %@", url]]; + completion(nil, error); + return; + } + + [firestore loadBundleStream:inputStream + completion:^(FIRLoadBundleTaskProgress * _Nullable progress, + NSError * _Nullable error) { + if (progress == nil) { + completion(nil, error); + return; + } + + if (progress.state == FIRLoadBundleTaskStateSuccess) { + completion(progress, nil); + } else { + NSError *concreteError = + [self bundleLoadErrorWithReason: + [NSString stringWithFormat: + @"Expected bundle load to be completed, but got %ld instead", + (long)progress.state]]; + completion(nil, concreteError); + } + completion(nil, nil); + }]; +} + +// Loads a bundled query. +- (void)loadQueryNamed:(NSString *)queryName + fromRemoteBundleURL:(NSURL *)url + withFirestore:(FIRFirestore *)firestore + completion:(void (^)(FIRQuery *_Nullable, NSError *_Nullable))completion { + [self fetchRemoteBundleForFirestore:firestore + fromURL:url + completion:^(FIRLoadBundleTaskProgress *progress, NSError *error) { + if (error != nil) { + completion(nil, error); + return; + } + + [firestore getQueryNamed:queryName completion:^(FIRQuery *query) { + if (query == nil) { + NSString *errorReason = + [NSString stringWithFormat:@"Could not find query named %@", queryName]; + NSError *error = [self bundleLoadErrorWithReason:errorReason]; + completion(nil, error); + return; + } + completion(query, nil); + }]; + }]; +} + +- (void)runStoriesQuery { + NSString *queryName = @"latest-stories-query"; + FIRFirestore *firestore = [FIRFirestore firestore]; + NSURL *bundleURL = [NSURL URLWithString:@"https://example.com/createBundle"]; + [self loadQueryNamed:queryName + fromRemoteBundleURL:bundleURL + withFirestore:firestore + completion:^(FIRQuery *query, NSError *error) { + // Handle query results + }]; +} +// [END fs_bundle_load] + +// [START fs_simple_bundle_load] +// Load a bundle from a local URL. +- (void)loadBundleFromBundleURL:(NSURL *)bundleURL { + FIRFirestore *firestore = [FIRFirestore firestore]; + NSError *error; + NSData *data = [NSData dataWithContentsOfURL:bundleURL options:kNilOptions error:&error]; + if (error != nil) { + NSLog(@"%@", error); + return; + } + [firestore loadBundle:data]; +} +// [END fs_simple_bundle_load] + +// [START fs_named_query] +- (void)runNamedQuery { + FIRFirestore *firestore = [FIRFirestore firestore]; + [firestore getQueryNamed:@"coll-query" completion:^(FIRQuery *_Nullable query) { + [query getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) { + // ... + }]; + }]; +} +// [END fs_named_query] + +// [START bundle_observe_progress] +- (void)observeProgressOfLoadBundleTask:(FIRLoadBundleTask *)loadBundleTask { + NSInteger handle = [loadBundleTask addObserver:^(FIRLoadBundleTaskProgress *progress) { + NSLog(@"Loaded %ld bytes out of %ld total", + (long)progress.bytesLoaded, + (long)progress.totalBytes); + }]; + + // ... + [loadBundleTask removeObserverWithHandle:handle]; +} +// [END bundle_observe_progress] + +@end diff --git a/firestore/objc/firestore-smoketest-objc/ViewController.m b/firestore/objc/firestore-smoketest-objc/ViewController.m index 68c50774..977c25d5 100644 --- a/firestore/objc/firestore-smoketest-objc/ViewController.m +++ b/firestore/objc/firestore-smoketest-objc/ViewController.m @@ -80,7 +80,9 @@ - (void)viewDidLoad { - (void)setupCacheSize { // [START fs_setup_cache] FIRFirestoreSettings *settings = [FIRFirestore firestore].settings; - settings.cacheSizeBytes = kFIRFirestoreCacheSizeUnlimited; + // Set cache size to 100 MB + settings.cacheSettings = + [[FIRPersistentCacheSettings alloc] initWithSizeBytes:@(100 * 1024 * 1024)]; [FIRFirestore firestore].settings = settings; // [END fs_setup_cache] } @@ -756,6 +758,21 @@ - (void)getMultipleAll { // [END get_multiple_all] } +- (void)getMultipleAllSubcollection { + // [START get_multiple_all_subcollection] + [[self.db collectionWithPath:@"cities/SF/landmarks"] + getDocumentsWithCompletion:^(FIRQuerySnapshot *snapshot, NSError *error) { + if (error != nil) { + NSLog(@"Error getting documents: %@", error); + } else { + for (FIRDocumentSnapshot *document in snapshot.documents) { + NSLog(@"%@ => %@", document.documentID, document.data); + } + } + }]; + // [END get_multiple_all_subcollection] +} + - (void)listenMultiple { // [START listen_multiple] [[[self.db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"] @@ -852,15 +869,18 @@ - (void)simpleQueries { // Create a query against the collection. FIRQuery *query = [citiesRef queryWhereField:@"state" isEqualTo:@"CA"]; // [END simple_queries] + // [START simple_query_not_equal] + query = [citiesRef queryWhereField:@"capital" isNotEqualTo:@NO]; + // [END simple_query_not_equal] NSLog(@"%@", query); } - (void)exampleFilters { FIRCollectionReference *citiesRef = [self.db collectionWithPath:@"cities"]; // [START example_filters] - [citiesRef queryWhereField:@"state" isEqualTo:@"CA"]; - [citiesRef queryWhereField:@"population" isLessThan:@100000]; - [citiesRef queryWhereField:@"name" isGreaterThanOrEqualTo:@"San Francisco"]; + FIRQuery *stateQuery = [citiesRef queryWhereField:@"state" isEqualTo:@"CA"]; + FIRQuery *populationQuery = [citiesRef queryWhereField:@"population" isLessThan:@100000]; + FIRQuery *nameQuery = [citiesRef queryWhereField:@"name" isGreaterThanOrEqualTo:@"San Francisco"]; // [END example_filters] } @@ -872,7 +892,7 @@ - (void)onlyCapitals { NSLog(@"%@", capitalCities); } --(void)arrayContainsFilter { +- (void)arrayContainsFilter { FIRCollectionReference *citiesRef = [self.db collectionWithPath:@"cities"]; // [START array_contains_filter] [citiesRef queryWhereField:@"state" arrayContains:@"west_coast"]; @@ -952,6 +972,30 @@ - (void)invalidFilterAndOrder { // [END invalid_filter_and_order] } +- (void)arrayContainsAnyQueries { + // [START array_contains_any_filter] + FIRCollectionReference *citiesRef = [self.db collectionWithPath:@"cities"]; + + [citiesRef queryWhereField:@"regions" arrayContainsAny:@[@"west_coast", @"east_coast"]]; + // [END array_contains_any_filter] +} + +- (void)inQueries { + // [START in_filter] + FIRCollectionReference *citiesRef = [self.db collectionWithPath:@"cities"]; + + [citiesRef queryWhereField:@"country" in:@[@"USA", @"Japan"]]; + // [END in_filter] + + // [START in_filter_with_array] + [citiesRef queryWhereField:@"regions" in:@[@[@"west_coast"], @[@"east_coast"]]]; + // [END in_filter_with_array] + + // [START not_in_filter] + [citiesRef queryWhereField:@"country" notIn:@[@"USA", @"Japan"]]; + // [END not_in_filter] +} + // ======================================================================================= // ====== https://firebase.google.com/preview/firestore/client/enable-offline ============ // ======================================================================================= @@ -959,7 +1003,15 @@ - (void)invalidFilterAndOrder { - (void)enableOffline { // [START enable_offline] FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init]; - settings.persistenceEnabled = YES; + + // Use memory-only cache + settings.cacheSettings = [[FIRMemoryCacheSettings alloc] + initWithGarbageCollectorSettings:[[FIRMemoryLRUGCSettings alloc] init]]; + + // Use persistent disk cache (default behavior) + // This example uses 100 MB. + settings.cacheSettings = [[FIRPersistentCacheSettings alloc] + initWithSizeBytes:@(100 * 1024 * 1024)]; // Any additional options // ... @@ -1115,4 +1167,218 @@ - (void)emulatorSettings { // [END fs_emulator_connect] } +- (void)countAggregateCollection { + // [START count_aggregate_collection] + FIRCollectionReference *query = [self.db collectionWithPath:@"cities"]; + [query.count aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *snapshot, + NSError *error) { + if (error != nil) { + NSLog(@"Error fetching count: %@", error); + } else { + NSLog(@"Cities count: %@", snapshot.count); + } + }]; + // [END count_aggregate_collection] +} + +- (void)countAggregateQuery { + // [START count_aggregate_query] + FIRQuery *query = + [[self.db collectionWithPath:@"cities"] + queryWhereField:@"state" + isEqualTo:@"CA"]; + [query.count aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *snapshot, + NSError *error) { + if (error != nil) { + NSLog(@"Error fetching count: %@", error); + } else { + NSLog(@"Cities count: %@", snapshot.count); + } + }]; + // [END count_aggregate_query] +} + +- (void)sumAggregateCollection { + // [START sum_aggregate_collection] + FIRQuery *query = [self.db collectionWithPath:@"cities"]; + FIRAggregateQuery *aggregateQuery = [query aggregate:@[ + [FIRAggregateField aggregateFieldForSumOfField:@"population"]]]; + [aggregateQuery aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *snapshot, + NSError *error) { + if (error != nil) { + NSLog(@"Error fetching aggregate: %@", error); + } else { + NSLog(@"Sum: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"population"]]); + } + }]; + // [END sum_aggregate_collection] +} + +- (void)sumAggregateQuery { + // [START sum_aggregate_query] + FIRQuery *query = [[self.db collectionWithPath:@"cities"] + queryWhereFilter:[FIRFilter filterWhereField:@"capital" isEqualTo:@YES]]; + FIRAggregateQuery *aggregateQuery = [query aggregate:@[ + [FIRAggregateField aggregateFieldForSumOfField:@"population"]]]; + [aggregateQuery aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *snapshot, + NSError *error) { + if (error != nil) { + NSLog(@"Error fetching aggregate: %@", error); + } else { + NSLog(@"Sum: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"population"]]); + } + }]; + // [END sum_aggregate_query] +} + +- (void)averageAggregateCollection { + // [START average_aggregate_collection] + FIRQuery *query = [self.db collectionWithPath:@"cities"]; + FIRAggregateQuery *aggregateQuery = [query aggregate:@[ + [FIRAggregateField aggregateFieldForAverageOfField:@"population"]]]; + [aggregateQuery aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *snapshot, + NSError *error) { + if (error != nil) { + NSLog(@"Error fetching aggregate: %@", error); + } else { + NSLog(@"Avg: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]); + } + }]; + // [END average_aggregate_collection] +} + +- (void)averageAggregateQuery { + // [START average_aggregate_query] + FIRQuery *query = [[self.db collectionWithPath:@"cities"] + queryWhereFilter:[FIRFilter filterWhereField:@"capital" isEqualTo:@YES]]; + FIRAggregateQuery *aggregateQuery = [query aggregate:@[ + [FIRAggregateField aggregateFieldForAverageOfField:@"population"]]]; + [aggregateQuery aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *snapshot, + NSError *error) { + if (error != nil) { + NSLog(@"Error fetching aggregate: %@", error); + } else { + NSLog(@"Avg: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]); + } + }]; + // [END average_aggregate_query] +} + +- (void)multiAggregateCollection { + // [START multi_aggregate_collection] + FIRQuery *query = [self.db collectionWithPath:@"cities"]; + FIRAggregateQuery *aggregateQuery = [query aggregate:@[ + [FIRAggregateField aggregateFieldForCount], + [FIRAggregateField aggregateFieldForSumOfField:@"population"], + [FIRAggregateField aggregateFieldForAverageOfField:@"population"]]]; + [aggregateQuery aggregationWithSource:FIRAggregateSourceServer + completion:^(FIRAggregateQuerySnapshot *snapshot, + NSError *error) { + if (error != nil) { + NSLog(@"Error fetching aggregate: %@", error); + } else { + NSLog(@"Count: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]]); + NSLog(@"Sum: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"population"]]); + NSLog(@"Avg: %@", [snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"population"]]); + } + }]; + // [END multi_aggregate_collection] +} + +- (void)orQuery { + // [START or_query] + FIRCollectionReference *collection = [self.db collectionWithPath:@"cities"]; + FIRQuery *query = [collection queryWhereFilter:[FIRFilter andFilterWithFilters:@[ + [FIRFilter filterWhereField:@"state" isEqualTo:@"CA"], + [FIRFilter orFilterWithFilters:@[ + [FIRFilter filterWhereField:@"capital" isEqualTo:@YES], + [FIRFilter filterWhereField:@"population" isGreaterThanOrEqualTo:@1000000] + ]] + ]]]; + // [END or_query] +} + +- (void)orQueryDisjunctions { + FIRCollectionReference *collection = [self.db collectionWithPath:@"cities"]; + + // [START one_disjunction] + [collection queryWhereField:@"a" isEqualTo:@1]; + // [END one_disjunction] + + // [START two_disjunctions] + [collection queryWhereFilter:[FIRFilter orFilterWithFilters:@[ + [FIRFilter filterWhereField:@"a" isEqualTo:@1], + [FIRFilter filterWhereField:@"b" isEqualTo:@2] + ]]]; + // [END two_disjunctions] + + // [START four_disjunctions] + [collection queryWhereFilter:[FIRFilter orFilterWithFilters:@[ + [FIRFilter andFilterWithFilters:@[ + [FIRFilter filterWhereField:@"a" isEqualTo:@1], + [FIRFilter filterWhereField:@"c" isEqualTo:@3] + ]], + [FIRFilter andFilterWithFilters:@[ + [FIRFilter filterWhereField:@"a" isEqualTo:@1], + [FIRFilter filterWhereField:@"d" isEqualTo:@4] + ]], + [FIRFilter andFilterWithFilters:@[ + [FIRFilter filterWhereField:@"b" isEqualTo:@2], + [FIRFilter filterWhereField:@"c" isEqualTo:@3] + ]], + [FIRFilter andFilterWithFilters:@[ + [FIRFilter filterWhereField:@"b" isEqualTo:@2], + [FIRFilter filterWhereField:@"d" isEqualTo:@4] + ]], + ]]]; + // [END four_disjunctions] + + // [START four_disjunctions_compact] + [collection queryWhereFilter:[FIRFilter andFilterWithFilters:@[ + [FIRFilter orFilterWithFilters:@[ + [FIRFilter filterWhereField:@"a" isEqualTo:@1], + [FIRFilter filterWhereField:@"b" isEqualTo:@2] + ]], + [FIRFilter orFilterWithFilters:@[ + [FIRFilter filterWhereField:@"c" isEqualTo:@3], + [FIRFilter filterWhereField:@"d" isEqualTo:@4] + ]] + ]]]; + // [END four_disjunctions_compact] + + // [START 20_disjunctions] + [collection queryWhereFilter:[FIRFilter orFilterWithFilters:@[ + [FIRFilter filterWhereField:@"a" in:@[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10]], + [FIRFilter filterWhereField:@"b" in:@[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10]] + ]]]; + // [END 20_disjunctions] + + // [START 10_disjunctions] + [collection queryWhereFilter:[FIRFilter andFilterWithFilters:@[ + [FIRFilter filterWhereField:@"a" in: @[@1, @2, @3, @4, @5]], + [FIRFilter orFilterWithFilters:@[ + [FIRFilter filterWhereField:@"b" isEqualTo:@2], + [FIRFilter filterWhereField:@"c" isEqualTo:@3] + ]] + ]]]; + // [END 10_disjunctions] +} + +- (void)illegalDisjunctions { + FIRCollectionReference *collection = [self.db collectionWithPath:@"cities"]; + + // [START 20_disjunctions] + [collection queryWhereFilter:[FIRFilter andFilterWithFilters:@[ + [FIRFilter filterWhereField:@"a" in:@[@1, @2, @3, @4, @5]], + [FIRFilter filterWhereField:@"b" in:@[@1, @2, @3, @4, @5, @6, @7, @8, @9, @10]] + ]]]; + // [END 20_disjunctions] +} + @end diff --git a/firestore/swift/Podfile b/firestore/swift/Podfile index a0a674fa..5acf781b 100644 --- a/firestore/swift/Podfile +++ b/firestore/swift/Podfile @@ -1,15 +1,19 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# No longer used since the project migrated to SPM, but this file still exists +# for the GeoFire snippet below. +platform :ios, '14.0' target 'firestore-smoketest' do # Comment this line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for firestore-smoketest - pod 'Firebase/Core' pod 'Firebase/Auth' pod 'Firebase/Firestore' + # [START geofire_pod] + pod 'GeoFire/Utils' + # [END geofire_pod] + target 'firestore-smoketestTests' do inherit! :search_paths # Pods for testing diff --git a/firestore/swift/Podfile.lock b/firestore/swift/Podfile.lock deleted file mode 100644 index 540ef514..00000000 --- a/firestore/swift/Podfile.lock +++ /dev/null @@ -1,170 +0,0 @@ -PODS: - - BoringSSL-GRPC (0.0.3): - - BoringSSL-GRPC/Implementation (= 0.0.3) - - BoringSSL-GRPC/Interface (= 0.0.3) - - BoringSSL-GRPC/Implementation (0.0.3): - - BoringSSL-GRPC/Interface (= 0.0.3) - - BoringSSL-GRPC/Interface (0.0.3) - - Firebase/Auth (6.8.0): - - Firebase/CoreOnly - - FirebaseAuth (~> 6.2.3) - - Firebase/Core (6.8.0): - - Firebase/CoreOnly - - FirebaseAnalytics (= 6.1.1) - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/Firestore (6.8.0): - - Firebase/CoreOnly - - FirebaseFirestore (~> 1.5.0) - - FirebaseAnalytics (6.1.1): - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.1.1) - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - FirebaseAuth (6.2.3): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.2) - - GoogleUtilities/AppDelegateSwizzler (~> 6.2) - - GoogleUtilities/Environment (~> 6.2) - - GTMSessionFetcher/Core (~> 1.1) - - FirebaseAuthInterop (1.0.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseFirestore (1.5.0): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.2) - - FirebaseFirestore/abseil-cpp (= 1.5.0) - - "gRPC-C++ (= 0.0.9)" - - leveldb-library (~> 1.22) - - nanopb (~> 0.3.901) - - Protobuf (~> 3.1) - - FirebaseFirestore/abseil-cpp (1.5.0): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.2) - - "gRPC-C++ (= 0.0.9)" - - leveldb-library (~> 1.22) - - nanopb (~> 0.3.901) - - Protobuf (~> 3.1) - - FirebaseInstanceID (4.2.4): - - FirebaseCore (~> 6.0) - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/UserDefaults (~> 6.0) - - GoogleAppMeasurement (6.1.1): - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleUtilities/AppDelegateSwizzler (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.2.5): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.2.5)" - - GoogleUtilities/Reachability (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.2.5): - - GoogleUtilities/Logger - - "gRPC-C++ (0.0.9)": - - "gRPC-C++/Implementation (= 0.0.9)" - - "gRPC-C++/Interface (= 0.0.9)" - - "gRPC-C++/Implementation (0.0.9)": - - "gRPC-C++/Interface (= 0.0.9)" - - gRPC-Core (= 1.21.0) - - nanopb (~> 0.3) - - "gRPC-C++/Interface (0.0.9)" - - gRPC-Core (1.21.0): - - gRPC-Core/Implementation (= 1.21.0) - - gRPC-Core/Interface (= 1.21.0) - - gRPC-Core/Implementation (1.21.0): - - BoringSSL-GRPC (= 0.0.3) - - gRPC-Core/Interface (= 1.21.0) - - nanopb (~> 0.3) - - gRPC-Core/Interface (1.21.0) - - GTMSessionFetcher/Core (1.2.2) - - leveldb-library (1.22) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - - Protobuf (3.9.0) - -DEPENDENCIES: - - Firebase/Auth - - Firebase/Core - - Firebase/Firestore - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - BoringSSL-GRPC - - Firebase - - FirebaseAnalytics - - FirebaseAuth - - FirebaseAuthInterop - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - - FirebaseFirestore - - FirebaseInstanceID - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleDataTransportCCTSupport - - GoogleUtilities - - "gRPC-C++" - - gRPC-Core - - GTMSessionFetcher - - leveldb-library - - nanopb - - Protobuf - -SPEC CHECKSUMS: - BoringSSL-GRPC: db8764df3204ccea016e1c8dd15d9a9ad63ff318 - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseAnalytics: 843c7f64a8f9c79f0d03281197ebe7bb1d58d477 - FirebaseAuth: e7f86c2dfc57281cd01f7da5e4b40e01e4510a4a - FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseFirestore: c5873e279490fbe02239ab2cdfb91c2d546261cc - FirebaseInstanceID: 88932a31aba5a56cfd3a7541706436c71f7f4598 - GoogleAppMeasurement: 86a82f0e1f20b8eedf8e20326530138fd71409de - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - "gRPC-C++": 9dfe7b44821e7b3e44aacad2af29d2c21f7cde83 - gRPC-Core: c9aef9a261a1247e881b18059b84d597293c9947 - GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - Protobuf: 1097ca58584c8d9be81bfbf2c5ff5975648dd87a - -PODFILE CHECKSUM: 67831f10b93a19c58541fb5f3fa7845a32fd6ca9 - -COCOAPODS: 1.7.5 diff --git a/firestore/swift/firestore-smoketest.xcodeproj/project.pbxproj b/firestore/swift/firestore-smoketest.xcodeproj/project.pbxproj index c4fa8dbe..90eb7522 100644 --- a/firestore/swift/firestore-smoketest.xcodeproj/project.pbxproj +++ b/firestore/swift/firestore-smoketest.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -17,6 +17,14 @@ 3EABFB2D1E254C8F00F4BBED /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3EABFB2B1E254C8F00F4BBED /* LaunchScreen.storyboard */; }; 3EABFB381E254C8F00F4BBED /* firestore_smoketestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EABFB371E254C8F00F4BBED /* firestore_smoketestTests.swift */; }; 3EABFB431E254C8F00F4BBED /* firestore_smoketestUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EABFB421E254C8F00F4BBED /* firestore_smoketestUITests.swift */; }; + 8D15627E2EA0474400D3B017 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 8D15627D2EA0474400D3B017 /* FirebaseAuth */; }; + 8D1562802EA0474400D3B017 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D15627F2EA0474400D3B017 /* FirebaseFirestore */; }; + 8D7951A32D28AC82000FD694 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951A22D28AC82000FD694 /* FirebaseAuth */; }; + 8D7951A52D28AC82000FD694 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951A42D28AC82000FD694 /* FirebaseFirestore */; }; + 8D7951A82D28ADBE000FD694 /* GeoFireUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951A72D28ADBE000FD694 /* GeoFireUtils */; }; + 8D7C36932EA069B70083BAF5 /* PipelineSnippets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D7C36922EA069B00083BAF5 /* PipelineSnippets.swift */; }; + 8D864B17260D3947008E85B3 /* SolutionBundles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D864B16260D3947008E85B3 /* SolutionBundles.swift */; }; + 8D864B35260D55C1008E85B3 /* SolutionGeoPointViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DFC116F258A2B8E00D080CB /* SolutionGeoPointViewController.swift */; }; 8DA9B4AB201165C800EC29CD /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8DA9B4AA201165C800EC29CD /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ @@ -54,7 +62,10 @@ 3EABFB3E1E254C8F00F4BBED /* firestore-smoketestUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "firestore-smoketestUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 3EABFB421E254C8F00F4BBED /* firestore_smoketestUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = firestore_smoketestUITests.swift; sourceTree = ""; }; 3EABFB441E254C8F00F4BBED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D7C36922EA069B00083BAF5 /* PipelineSnippets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PipelineSnippets.swift; sourceTree = ""; }; + 8D864B16260D3947008E85B3 /* SolutionBundles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolutionBundles.swift; sourceTree = ""; }; 8DA9B4AA201165C800EC29CD /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 8DFC116F258A2B8E00D080CB /* SolutionGeoPointViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolutionGeoPointViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,6 +73,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D7951A32D28AC82000FD694 /* FirebaseAuth in Frameworks */, + 8D7951A82D28ADBE000FD694 /* GeoFireUtils in Frameworks */, + 8D7951A52D28AC82000FD694 /* FirebaseFirestore in Frameworks */, + 8D1562802EA0474400D3B017 /* FirebaseFirestore in Frameworks */, + 8D15627E2EA0474400D3B017 /* FirebaseAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -88,6 +104,7 @@ 3EABFB211E254C8F00F4BBED /* firestore-smoketest */, 3EABFB361E254C8F00F4BBED /* firestore-smoketestTests */, 3EABFB411E254C8F00F4BBED /* firestore-smoketestUITests */, + 8D7951A12D28AC82000FD694 /* Frameworks */, 3EABFB201E254C8F00F4BBED /* Products */, ); sourceTree = ""; @@ -105,6 +122,7 @@ 3EABFB211E254C8F00F4BBED /* firestore-smoketest */ = { isa = PBXGroup; children = ( + 8D7C36922EA069B00083BAF5 /* PipelineSnippets.swift */, 8DA9B4AA201165C800EC29CD /* GoogleService-Info.plist */, 3EABFB221E254C8F00F4BBED /* AppDelegate.swift */, 3EABFB241E254C8F00F4BBED /* ViewController.swift */, @@ -115,6 +133,8 @@ 3E3694B41EFDB0F2009C9125 /* SolutionArraysViewController.swift */, 3E3694B61EFDB3BF009C9125 /* SolutionAggregationViewController.swift */, 3E3694B81EFDC039009C9125 /* SolutionCountersViewController.swift */, + 8DFC116F258A2B8E00D080CB /* SolutionGeoPointViewController.swift */, + 8D864B16260D3947008E85B3 /* SolutionBundles.swift */, ); path = "firestore-smoketest"; sourceTree = ""; @@ -137,6 +157,13 @@ path = "firestore-smoketestUITests"; sourceTree = ""; }; + 8D7951A12D28AC82000FD694 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -199,8 +226,9 @@ 3EABFB171E254C8E00F4BBED /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0800; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1610; ORGANIZATIONNAME = Firebase; TargetAttributes = { 3EABFB1E1E254C8E00F4BBED = { @@ -224,13 +252,17 @@ }; buildConfigurationList = 3EABFB1A1E254C8E00F4BBED /* Build configuration list for PBXProject "firestore-smoketest" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 3EABFB161E254C8E00F4BBED; + packageReferences = ( + 8D7951A62D28ADAE000FD694 /* XCRemoteSwiftPackageReference "geofire-objc" */, + 8D15627C2EA0474400D3B017 /* XCLocalSwiftPackageReference "../../../firebase-ios-sdk" */, + ); productRefGroup = 3EABFB201E254C8F00F4BBED /* Products */; projectDirPath = ""; projectRoot = ""; @@ -277,7 +309,10 @@ files = ( 3EABFB251E254C8F00F4BBED /* ViewController.swift in Sources */, 3E3694B51EFDB0F2009C9125 /* SolutionArraysViewController.swift in Sources */, + 8D864B17260D3947008E85B3 /* SolutionBundles.swift in Sources */, 3E3694B71EFDB3BF009C9125 /* SolutionAggregationViewController.swift in Sources */, + 8D864B35260D55C1008E85B3 /* SolutionGeoPointViewController.swift in Sources */, + 8D7C36932EA069B70083BAF5 /* PipelineSnippets.swift in Sources */, 3EABFB231E254C8F00F4BBED /* AppDelegate.swift in Sources */, 3E3694B91EFDC039009C9125 /* SolutionCountersViewController.swift in Sources */, ); @@ -358,6 +393,7 @@ 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; @@ -369,6 +405,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -383,12 +420,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.6; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -417,6 +455,7 @@ 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; @@ -428,6 +467,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -436,10 +476,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.6; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -451,11 +493,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; "GCC_WARN_64_TO_32_BIT_CONVERSION[arch=*64]" = YES; INFOPLIST_FILE = "firestore-smoketest/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firestore-smoketest"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -466,11 +511,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; "GCC_WARN_64_TO_32_BIT_CONVERSION[arch=*64]" = YES; INFOPLIST_FILE = "firestore-smoketest/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 16.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firestore-smoketest"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = 1; }; name = Release; @@ -478,10 +526,13 @@ 3EABFB4B1E254C8F00F4BBED /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = "firestore-smoketestTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firestore-smoketestTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -492,10 +543,13 @@ 3EABFB4C1E254C8F00F4BBED /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = "firestore-smoketestTests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firestore-smoketestTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -506,9 +560,12 @@ 3EABFB4E1E254C8F00F4BBED /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; INFOPLIST_FILE = "firestore-smoketestUITests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firestore-smoketestUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -519,9 +576,12 @@ 3EABFB4F1E254C8F00F4BBED /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; INFOPLIST_FILE = "firestore-smoketestUITests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firestore-smoketestUITests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -569,6 +629,58 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 8D15627C2EA0474400D3B017 /* XCLocalSwiftPackageReference "../../../firebase-ios-sdk" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../../../firebase-ios-sdk"; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7951A02D28AC75000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "~/dev/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; + 8D7951A62D28ADAE000FD694 /* XCRemoteSwiftPackageReference "geofire-objc" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/geofire-objc"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D15627D2EA0474400D3B017 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + productName = FirebaseAuth; + }; + 8D15627F2EA0474400D3B017 /* FirebaseFirestore */ = { + isa = XCSwiftPackageProductDependency; + productName = FirebaseFirestore; + }; + 8D7951A22D28AC82000FD694 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A02D28AC75000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 8D7951A42D28AC82000FD694 /* FirebaseFirestore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A02D28AC75000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFirestore; + }; + 8D7951A72D28ADBE000FD694 /* GeoFireUtils */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A62D28ADAE000FD694 /* XCRemoteSwiftPackageReference "geofire-objc" */; + productName = GeoFireUtils; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 3EABFB171E254C8E00F4BBED /* Project object */; } diff --git a/firestore/swift/firestore-smoketest.xcodeproj/xcshareddata/xcschemes/firestore-smoketest.xcscheme b/firestore/swift/firestore-smoketest.xcodeproj/xcshareddata/xcschemes/firestore-smoketest.xcscheme new file mode 100644 index 00000000..8affb89d --- /dev/null +++ b/firestore/swift/firestore-smoketest.xcodeproj/xcshareddata/xcschemes/firestore-smoketest.xcscheme @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/firestore/swift/firestore-smoketest/AppDelegate.swift b/firestore/swift/firestore-smoketest/AppDelegate.swift index 1fac4ba5..f2f5723d 100644 --- a/firestore/swift/firestore-smoketest/AppDelegate.swift +++ b/firestore/swift/firestore-smoketest/AppDelegate.swift @@ -19,13 +19,13 @@ import UIKit import FirebaseCore import FirebaseFirestore -@UIApplicationMain +@main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // [START default_firestore] FirebaseApp.configure() diff --git a/firestore/swift/firestore-smoketest/PipelineSnippets.swift b/firestore/swift/firestore-smoketest/PipelineSnippets.swift new file mode 100644 index 00000000..0c4aa486 --- /dev/null +++ b/firestore/swift/firestore-smoketest/PipelineSnippets.swift @@ -0,0 +1,1380 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseFirestore +import CoreLocation + +public class PipelineSnippets { + + lazy var db = { + Firestore.firestore(database: "enterprise") + }() + + public func runAllSnippets() { + pipelineConcepts() + } + + func stagesExpressionsExample() async throws { + // [START stages_expressions_example] + guard let cutoffDate = Calendar.current.date(byAdding: .month, value: -1, to: Date()) else { + return + } + let snapshot = try await db.pipeline() + .collection("productViews") + .where(Field("viewedAt").greaterThan(cutoffDate.timeIntervalSince1970)) + .aggregate([Field("productId").countDistinct().as("uniqueProductViews")]) + .execute() + // [END stages_expressions_example] + print(snapshot) + } + + func basicRead() async { + // [START basic_read] + do { + // Initialize a Firestore Pipeline instance and specify the "users" collection as the + // input stage. + let snapshot = try await db.pipeline() + .collection("users") + .execute() // Execute the pipeline to retrieve documents. + + // Iterate through the documents in the pipeline results, similar to a regular query + // snapshot. + for result in snapshot.results { + print("\(result.id ?? "no ID") => \(result.data)") + } + } catch { + print("Error getting documents with pipeline: \(error)") + } + // [END basic_read] + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#concepts + func pipelineConcepts() { + // [START pipeline_concepts] + let pipeline = db.pipeline() + // Step 1: Start a query with collection scope + .collection("cities") + // Step 2: Filter the collection + .where(Field("population").greaterThan(100000)) + // Step 3: Sort the remaining documents + .sort([Field("name").ascending()]) + // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have + // unintentional results. + .limit(10) + // [END pipeline_concepts] + print(pipeline) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#initialization + func pipelineInitialization() { + // [START pipeline_initialization] + let firestore = Firestore.firestore(database: "enterprise") + let pipeline = firestore.pipeline() + // [END pipeline_initialization] + print(pipeline) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#field_vs_constant_references/ + func fieldVsConstants() { + // [START field_or_constant] + let pipeline = db.pipeline() + .collection("cities") + .where(Field("name").equal(Constant("Toronto"))) + // [END field_or_constant] + print(pipeline) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#input_stages + func inputStages() async throws { + // [START input_stages] + var results: Pipeline.Snapshot + + // Return all restaurants in San Francisco + results = try await db.pipeline().collection("cities/sf/restaurants").execute() + + // Return all restaurants + results = try await db.pipeline().collectionGroup("restaurants").execute() + + // Return all documents across all collections in the database (the entire database) + results = try await db.pipeline().database().execute() + + // Batch read of 3 documents + results = try await db.pipeline().documents([ + db.collection("cities").document("SF"), + db.collection("cities").document("DC"), + db.collection("cities").document("NY") + ]).execute() + // [END input_stages] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#where + func wherePipeline() async throws { + // [START pipeline_where] + var results: Pipeline.Snapshot + + results = try await db.pipeline().collection("books") + .where(Field("rating").equal(5)) + .where(Field("published").lessThan(1900)) + .execute() + + results = try await db.pipeline().collection("books") + .where(Field("rating").equal(5) && Field("published").lessThan(1900)) + .execute() + // [END pipeline_where] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#aggregate_distinct + func aggregateGroups() async throws { + // [START aggregate_groups] + let results = try await db.pipeline() + .collection("books") + .aggregate([ + Field("rating").average().as("avg_rating") + ], groups: [ + Field("genre") + ]) + .execute() + // [END aggregate_groups] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#aggregate_distinct + func aggregateDistinct() async throws { + // [START aggregate_distinct] + let results = try await db.pipeline() + .collection("books") + .distinct([ + Field("author").toUpper().as("author"), + Field("genre") + ]) + .execute() + // [END aggregate_distinct] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#sort + func sort() async throws { + // [START sort] + let results = try await db.pipeline() + .collection("books") + .sort([ + Field("release_date").descending(), Field("author").ascending() + ]) + .execute() + // [END sort] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#sort + func sortComparison() { + // [START sort_comparison] + let query = db.collection("cities") + .order(by: "state") + .order(by: "population", descending: true) + + let pipeline = db.pipeline() + .collection("books") + .sort([ + Field("release_date").descending(), Field("author").ascending() + ]) + // [END sort_comparison] + print(query) + print(pipeline) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#functions + func functions() async throws { + // [START functions_example] + var results: Pipeline.Snapshot + + // Type 1: Scalar (for use in non-aggregation stages) + // Example: Return the min store price for each book. + results = try await db.pipeline().collection("books") + .select([ + Field("current").logicalMinimum(["updated"]).as("price_min") + ]) + .execute() + + // Type 2: Aggregation (for use in aggregate stages) + // Example: Return the min price of all books. + results = try await db.pipeline().collection("books") + .aggregate([Field("price").minimum().as("min_price")]) + .execute() + // [END functions_example] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#creating_indexes + func creatingIndexes() async throws { + // [START query_example] + let results = try await db.pipeline() + .collection("books") + .where(Field("published").lessThan(1900)) + .where(Field("genre").equal("Science Fiction")) + .where(Field("rating").greaterThan(4.3)) + .sort([Field("published").descending()]) + .execute() + // [END query_example] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#existing_sparse_indexes + func sparseIndexes() async throws { + // [START sparse_index_example] + let results = try await db.pipeline() + .collection("books") + .where(Field("category").like("%fantasy%")) + .execute() + // [END sparse_index_example] + print(results) + } + + func sparseIndexes2() async throws { + // [START sparse_index_example_2] + let results = try await db.pipeline() + .collection("books") + .sort([Field("release_date").ascending()]) + .execute() + // [END sparse_index_example_2] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#covered_queries_secondary_indexes + func coveredQuery() async throws { + // [START covered_query] + let results = try await db.pipeline() + .collection("books") + .where(Field("category").like("%fantasy%")) + .where(Field("title").exists()) + .where(Field("author").exists()) + .select([Field("title"), Field("author")]) + .execute() + // [END covered_query] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/overview#pagination + func pagination() async throws { + // [START pagination_not_supported_preview] + // Existing pagination via `start(at:)` + let query = db.collection("cities").order(by: "population").start(at: [1000000]) + + // Private preview workaround using pipelines + let pipeline = db.pipeline() + .collection("cities") + .where(Field("population").greaterThanOrEqual(1000000)) + .sort([Field("population").descending()]) + // [END pagination_not_supported_preview] + print(query) + print(pipeline) + } + + // http://cloud.google.com/firestore/docs/pipeline/stages/input/collection#example + func collectionStage() async throws { + // [START collection_example] + let results = try await db.pipeline() + .collection("users/bob/games") + .sort([Field("name").ascending()]) + .execute() + // [END collection_example] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/input/collection_group + func collectionGroupStage() async throws { + // [START collection_group_example] + let results = try await db.pipeline() + .collectionGroup("games") + .sort([Field("name").ascending()]) + .execute() + // [END collection_group_example] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/input/database + func databaseStage() async throws { + // [START database_example] + // Count all documents in the database + let results = try await db.pipeline() + .database() + .aggregate([CountAll().as("total")]) + .execute() + // [END database_example] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/input/documents + func documentsStage() async throws { + // [START documents_example] + let results = try await db.pipeline() + .documents([ + db.collection("cities").document("SF"), + db.collection("cities").document("DC"), + db.collection("cities").document("NY") + ]).execute() + // [END documents_example] + print(results) + } + + func replaceWithStage() async throws { + // [START initial_data] + try await db.collection("cities").document("SF").setData([ + "name": "San Francisco", + "population": 800000, + "location": [ + "country": "USA", + "state": "California" + ] + ]) + try await db.collection("cities").document("TO").setData([ + "name": "Toronto", + "population": 3000000, + "province": "ON", + "location": [ + "country": "Canada", + "province": "Ontario" + ] + ]) + try await db.collection("cities").document("NY").setData([ + "name": "New York", + "location": [ + "country": "USA", + "state": "New York" + ] + ]) + try await db.collection("cities").document("AT").setData([ + "name": "Atlantis", + ]) + // [END initial_data] + + // [START full_replace] + let names = try await db.pipeline() + .collection("cities") + .replace(with: Field("location")) + .execute() + // [END full_replace] + + // [START map_merge_overwrite] + // unsupported in client SDKs for now + // [END map_merge_overwrite] + print(names) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/transformation/sample#examples + func sampleStage() async throws { + // [START sample_example] + var results: Pipeline.Snapshot + + // Get a sample of 100 documents in a database + results = try await db.pipeline() + .database() + .sample(count: 100) + .execute() + + // Randomly shuffle a list of 3 documents + results = try await db.pipeline() + .documents([ + db.collection("cities").document("SF"), + db.collection("cities").document("NY"), + db.collection("cities").document("DC"), + ]) + .sample(count: 3) + .execute() + // [END sample_example] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/transformation/sample#examples_2 + func samplePercent() async throws { + // [START sample_percent] + // Get a sample of on average 50% of the documents in the database + let results = try await db.pipeline() + .database() + .sample(percentage: 0.5) + .execute() + // [END sample_percent] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/transformation/union#examples + func unionStage() async throws { + // [START union_stage] + let results = try await db.pipeline() + .collection("cities/SF/restaurants") + .where(Field("type").equal("Chinese")) + .union(with: db.pipeline() + .collection("cities/NY/restaurants") + .where(Field("type").equal("Italian"))) + .where(Field("rating").greaterThanOrEqual(4.5)) + .sort([Field("__name__").descending()]) + .execute() + // [END union_stage] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/transformation/unnest#examples + func unnestStage() async throws { + // [START unnest_stage] + let results = try await db.pipeline() + .database() + .unnest(Field("arrayField").as("unnestedArrayField"), indexField: "index") + .execute() + // [END unnest_stage] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/stages/transformation/unnest#examples + func unnestStageEmptyOrNonArray() async throws { + // [START unnest_edge_cases] + // Input + // { identifier : 1, neighbors: [ "Alice", "Cathy" ] } + // { identifier : 2, neighbors: [] } + // { identifier : 3, neighbors: "Bob" } + + let results = try await db.pipeline() + .database() + .unnest(Field("neighbors").as("unnestedNeighbors"), indexField: "index") + .execute() + + // Output + // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 } + // { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 } + // { identifier: 3, neighbors: "Bob", index: null} + // [END unnest_edge_cases] + print(results) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/aggregate_functions#count + func countFunction() async throws { + // [START count_function] + // Total number of books in the collection + let countAll = try await db.pipeline() + .collection("books") + .aggregate([CountAll().as("count")]) + .execute() + + // Number of books with nonnull `ratings` field + let countField = try await db.pipeline() + .collection("books") + .aggregate([Field("ratings").count().as("count")]) + .execute() + // [END count_function] + print(countAll) + print(countField) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/aggregate_functions#count_if + func countIfFunction() async throws { + // [START count_if] + let result = try await db.pipeline() + .collection("books") + .aggregate([ + AggregateFunction("count_if", [Field("rating").greaterThan(4)]).as("filteredCount") + ]) + .execute() + // [END count_if] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/aggregate_functions#count_distinct + func countDistinctFunction() async throws { + // [START count_distinct] + let result = try await db.pipeline() + .collection("books") + .aggregate([AggregateFunction("count_distinct", [Field("author")]).as("unique_authors")]) + .execute() + // [END count_distinct] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/aggregate_functions#sum + func sumFunction() async throws { + // [START sum_function] + let result = try await db.pipeline() + .collection("cities") + .aggregate([Field("population").sum().as("totalPopulation")]) + .execute() + // [END sum_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/aggregate_functions#avg + func avgFunction() async throws { + // [START avg_function] + let result = try await db.pipeline() + .collection("cities") + .aggregate([Field("population").average().as("averagePopulation")]) + .execute() + // [END avg_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/aggregate_functions#min + func minFunction() async throws { + // [START min_function] + let result = try await db.pipeline() + .collection("books") + .aggregate([Field("price").minimum().as("minimumPrice")]) + .execute() + // [END min_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/aggregate_functions#max + func maxFunction() async throws { + // [START max_function] + let result = try await db.pipeline() + .collection("books") + .aggregate([Field("price").maximum().as("maximumPrice")]) + .execute() + // [END max_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#add + func addFunction() async throws { + // [START add_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("soldBooks").add(Field("unsoldBooks")).as("totalBooks")]) + .execute() + // [END add_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#subtract + func subtractFunction() async throws { + // [START subtract_function] + let storeCredit = 7 + let result = try await db.pipeline() + .collection("books") + .select([Field("price").subtract(Constant(storeCredit)).as("totalCost")]) + .execute() + // [END subtract_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#multiply + func multiplyFunction() async throws { + // [START multiply_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("price").multiply(Field("soldBooks")).as("revenue")]) + .execute() + // [END multiply_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#divide + func divideFunction() async throws { + // [START divide_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("ratings").divide(Field("soldBooks")).as("reviewRate")]) + .execute() + // [END divide_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#mod + func modFunction() async throws { + // [START mod_function] + let displayCapacity = 1000 + let result = try await db.pipeline() + .collection("books") + .select([Field("unsoldBooks").mod(Constant(displayCapacity)).as("warehousedBooks")]) + .execute() + // [END mod_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#ceil + func ceilFunction() async throws { + // [START ceil_function] + let booksPerShelf = 100 + let result = try await db.pipeline() + .collection("books") + .select([ + Field("unsoldBooks").divide(Constant(booksPerShelf)).ceil().as("requiredShelves") + ]) + .execute() + // [END ceil_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#floor + func floorFunction() async throws { + // [START floor_function] + let result = try await db.pipeline() + .collection("books") + .addFields([ + Field("wordCount").divide(Field("pages")).floor().as("wordsPerPage") + ]) + .execute() + // [END floor_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#round + func roundFunction() async throws { + // [START round_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("soldBooks").multiply(Field("price")).round().as("partialRevenue")]) + .aggregate([Field("partialRevenue").sum().as("totalRevenue")]) + .execute() + // [END round_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#pow + func powFunction() async throws { + // [START pow_function] + let googleplex = CLLocation(latitude: 37.4221, longitude: 122.0853) + let result = try await db.pipeline() + .collection("cities") + .addFields([ + Field("lat").subtract(Constant(googleplex.coordinate.latitude)) + .multiply(111 /* km per degree */) + .pow(2) + .as("latitudeDifference"), + Field("lng").subtract(Constant(googleplex.coordinate.latitude)) + .multiply(111 /* km per degree */) + .pow(2) + .as("longitudeDifference") + ]) + .select([ + Field("latitudeDifference").add(Field("longitudeDifference")).sqrt() + // Inaccurate for large distances or close to poles + .as("approximateDistanceToGoogle") + ]) + .execute() + // [END pow_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#sqrt + func sqrtFunction() async throws { + // [START sqrt_function] + let googleplex = CLLocation(latitude: 37.4221, longitude: 122.0853) + let result = try await db.pipeline() + .collection("cities") + .addFields([ + Field("lat").subtract(Constant(googleplex.coordinate.latitude)) + .multiply(111 /* km per degree */) + .pow(2) + .as("latitudeDifference"), + Field("lng").subtract(Constant(googleplex.coordinate.latitude)) + .multiply(111 /* km per degree */) + .pow(2) + .as("longitudeDifference") + ]) + .select([ + Field("latitudeDifference").add(Field("longitudeDifference")).sqrt() + // Inaccurate for large distances or close to poles + .as("approximateDistanceToGoogle") + ]) + .execute() + // [END sqrt_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#exp + func expFunction() async throws { + // [START exp_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("rating").exp().as("expRating")]) + .execute() + // [END exp_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#ln + func lnFunction() async throws { + // [START ln_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("rating").ln().as("lnRating")]) + .execute() + // [END ln_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/arithmetic_functions#log + func logFunction() async throws { + // [START log_function] + // Not supported on iOS + // END log_function] + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/array_functions#array_concat + func arrayConcat() async throws { + // [START array_concat] + let result = try await db.pipeline() + .collection("books") + .select([Field("genre").arrayConcat([Field("subGenre")]).as("allGenres")]) + .execute() + // [END array_concat] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/array_functions#array_contains + func arrayContains() async throws { + // [START array_contains] + let result = try await db.pipeline() + .collection("books") + .select([Field("genre").arrayContains(Constant("mystery")).as("isMystery")]) + .execute() + // [END array_contains] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/array_functions#array_contains_all + func arrayContainsAll() async throws { + // [START array_contains_all] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("genre") + .arrayContainsAll([Constant("fantasy"), Constant("adventure")]) + .as("isFantasyAdventure") + ]) + .execute() + // [END array_contains_all] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/array_functions#array_contains_any + func arrayContainsAny() async throws { + // [START array_contains_any] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("genre") + .arrayContainsAny([Constant("fantasy"), Constant("nonfiction")]) + .as("isMysteryOrFantasy") + ]) + .execute() + // [END array_contains_any] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/array_functions#array_length + func arrayLength() async throws { + // [START array_length] + let result = try await db.pipeline() + .collection("books") + .select([Field("genre").arrayLength().as("genreCount")]) + .execute() + // [END array_length] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/array_functions#array_reverse + func arrayReverse() async throws { + // [START array_reverse] + let result = try await db.pipeline() + .collection("books") + .select([Field("genre").arrayReverse().as("reversedGenres")]) + .execute() + // [END array_reverse] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/comparison_functions#eq + func equalFunction() async throws { + // [START equal_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("rating").equal(5).as("hasPerfectRating")]) + .execute() + // [END equal_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/comparison_functions#gt + func greaterThanFunction() async throws { + // [START greater_than] + let result = try await db.pipeline() + .collection("books") + .select([Field("rating").greaterThan(4).as("hasHighRating")]) + .execute() + // [END greater_than] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/comparison_functions#gte + func greaterThanOrEqualToFunction() async throws { + // [START greater_or_equal] + let result = try await db.pipeline() + .collection("books") + .select([Field("published").greaterThanOrEqual(1900).as("publishedIn20thCentury")]) + .execute() + // [END greater_or_equal] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/comparison_functions#lt + func lessThanFunction() async throws { + // [START less_than] + let result = try await db.pipeline() + .collection("books") + .select([Field("published").lessThan(1923).as("isPublicDomainProbably")]) + .execute() + // [END less_than] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/comparison_functions#lte + func lessThanOrEqualToFunction() async throws { + // [START less_or_equal] + let result = try await db.pipeline() + .collection("books") + .select([Field("rating").lessThanOrEqual(2).as("hasBadRating")]) + .execute() + // [END less_or_equal] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/comparison_functions#neq + func notEqualFunction() async throws { + // [START not_equal] + let result = try await db.pipeline() + .collection("books") + .select([Field("title").notEqual("1984").as("not1984")]) + .execute() + // [END not_equal] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/debugging_functions#exists + func existsFunction() async throws { + // [START exists_function] + let result = try await db.pipeline() + .collection("books") + .select([Field("rating").exists().as("hasRating")]) + .execute() + // [END exists_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#and + func andFunction() async throws { + // [START and_function] + let result = try await db.pipeline() + .collection("books") + .select([ + (Field("rating").greaterThan(4) && Field("price").lessThan(10)) + .as("under10Recommendation") + ]) + .execute() + // [END and_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#or + func orFunction() async throws { + // [START or_function] + let result = try await db.pipeline() + .collection("books") + .select([ + (Field("genre").equal("Fantasy") || Field("tags").arrayContains("adventure")) + .as("matchesSearchFilters") + ]) + .execute() + // [END or_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#xor + func xorFunction() async throws { + // [START xor_function] + let result = try await db.pipeline() + .collection("books") + .select([ + (Field("tags").arrayContains("magic") ^ Field("tags").arrayContains("nonfiction")) + .as("matchesSearchFilters") + ]) + .execute() + // [END xor_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#not + func notFunction() async throws { + // [START not_function] + let result = try await db.pipeline() + .collection("books") + .select([ + (!Field("tags").arrayContains("nonfiction")) + .as("isFiction") + ]) + .execute() + // [END not_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#cond + func condFunction() async throws { + // [START cond_function] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("tags").arrayConcat([ + ConditionalExpression( + Field("pages").greaterThan(100), + then: Constant("longRead"), + else: Constant("shortRead") + ) + ]).as("extendedTags") + ]) + .execute() + // [END cond_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#eq_any + func equalAnyFunction() async throws { + // [START eq_any] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("genre").equalAny(["Science Fiction", "Psychological Thriller"]) + .as("matchesGenreFilters") + ]) + .execute() + // [END eq_any] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#not_eq_any + func notEqualAnyFunction() async throws { + // [START not_eq_any] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("author").notEqualAny(["George Orwell", "F. Scott Fitzgerald"]) + .as("byExcludedAuthors") + ]) + .execute() + // [END not_eq_any] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#max + func maxLogicalFunction() async throws { + // [START max_logical_function] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("rating").logicalMaximum([1]).as("flooredRating") + ]) + .execute() + // [END max_logical_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/logical_functions#min + func minLogicalFunction() async throws { + // [START min_logical_function] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("rating").logicalMinimum([5]).as("cappedRating") + ]) + .execute() + // [END min_logical_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/map_functions#map_get + func mapGetFunction() async throws { + // [START map_get] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("awards").mapGet("pulitzer").as("hasPulitzerAward") + ]) + .execute() + // [END map_get] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#byte_length + func byteLengthFunction() async throws { + // [START byte_length] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("title").byteLength().as("titleByteLength") + ]) + .execute() + // [END byte_length] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#char_length + func charLengthFunction() async throws { + // [START char_length] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("title").charLength().as("titleCharLength") + ]) + .execute() + // [END char_length] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#starts_with + func startsWithFunction() async throws { + // [START starts_with] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("title").startsWith("The") + .as("needsSpecialAlphabeticalSort") + ]) + .execute() + // [END starts_with] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#ends_with + func endsWithFunction() async throws { + // [START ends_with] + let result = try await db.pipeline() + .collection("inventory/devices/laptops") + .select([ + Field("name").endsWith("16 inch") + .as("16InLaptops") + ]) + .execute() + // [END ends_with] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#like + func likeFunction() async throws { + // [START like] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("genre").like("%Fiction") + .as("anyFiction") + ]) + .execute() + // [END like] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#regex_contains + func regexContainsFunction() async throws { + // [START regex_contains] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("title").regexContains("Firestore (Enterprise|Standard)") + .as("isFirestoreRelated") + ]) + .execute() + // [END regex_contains] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#regex_match + func regexMatchFunction() async throws { + // [START regex_match] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("title").regexMatch("Firestore (Enterprise|Standard)") + .as("isFirestoreExactly") + ]) + .execute() + // [END regex_match] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#str_concat + func strConcatFunction() async throws { + // [START str_concat] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("title").concat([" by ", Field("author")]) + .as("fullyQualifiedTitle") + ]) + .execute() + // [END str_concat] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#str_contains + func strContainsFunction() async throws { + // [START string_contains] + let result = try await db.pipeline() + .collection("articles") + .select([ + Field("body").stringContains("Firestore") + .as("isFirestoreRelated") + ]) + .execute() + // [END string_contains] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#to_upper + func toUpperFunction() async throws { + // [START to_upper] + let result = try await db.pipeline() + .collection("authors") + .select([ + Field("name").toUpper() + .as("uppercaseName") + ]) + .execute() + // [END to_upper] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#to_lower + func toLowerFunction() async throws { + // [START to_lower] + let result = try await db.pipeline() + .collection("authors") + .select([ + Field("genre").toLower().equal("fantasy") + .as("isFantasy") + ]) + .execute() + // [END to_lower] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#substr + func substrFunction() async throws { + // [START substr_function] + let result = try await db.pipeline() + .collection("books") + .where(Field("title").startsWith("The ")) + .select([ + Field("title").substring(position: 4) + .as("titleWithoutLeadingThe") + ]) + .execute() + // [END substr_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#str_reverse + func strReverseFunction() async throws { + // [START str_reverse] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("name").reverse().as("reversedName") + ]) + .execute() + // [END str_reverse] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#str_trim + func strTrimFunction() async throws { + // [START trim_function] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("name").trim(" \n\t").as("whitespaceTrimmedName") + ]) + .execute() + // [END trim_function] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#str_replace + func strReplaceFunction() async throws { + // not yet supported until GA + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/string_functions#str_split + func strSplitFunction() async throws { + // not yet supported until GA + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#unix_micros_to_timestamp + func unixMicrosToTimestampFunction() async throws { + // [START unix_micros_timestamp] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("createdAtMicros").unixMicrosToTimestamp().as("createdAtString") + ]) + .execute() + // [END unix_micros_timestamp] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#unix_millis_to_timestamp + func unixMillisToTimestampFunction() async throws { + // [START unix_millis_timestamp] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("createdAtMillis").unixMillisToTimestamp().as("createdAtString") + ]) + .execute() + // [END unix_millis_timestamp] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#unix_seconds_to_timestamp + func unixSecondsToTimestampFunction() async throws { + // [START unix_seconds_timestamp] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("createdAtSeconds").unixSecondsToTimestamp().as("createdAtString") + ]) + .execute() + // [END unix_seconds_timestamp] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#timestamp_add + func timestampAddFunction() async throws { + // [START timestamp_add] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("createdAt").timestampAdd(3653, .day).as("expiresAt") + ]) + .execute() + // [END timestamp_add] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#timestamp_sub + func timestampSubFunction() async throws { + // [START timestamp_sub] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("expiresAt").timestampSubtract(14, .day).as("sendWarningTimestamp") + ]) + .execute() + // [END timestamp_sub] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#timestamp_to_unix_micros + func timestampToUnixMicrosFunction() async throws { + // [START timestamp_unix_micros] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("dateString").timestampToUnixMicros().as("unixMicros") + ]) + .execute() + // [END timestamp_unix_micros] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#timestamp_to_unix_millis + func timestampToUnixMillisFunction() async throws { + // [START timestamp_unix_millis] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("dateString").timestampToUnixMillis().as("unixMillis") + ]) + .execute() + // [END timestamp_unix_millis] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/timestamp_functions#timestamp_to_unix_seconds + func timestampToUnixSecondsFunction() async throws { + // [START timestamp_unix_seconds] + let result = try await db.pipeline() + .collection("documents") + .select([ + Field("dateString").timestampToUnixSeconds().as("unixSeconds") + ]) + .execute() + // [END timestamp_unix_seconds] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/vector_functions#cosine_distance + func cosineDistanceFunction() async throws { + // [START cosine_distance] + let sampleVector = [0.0, 1, 2, 3, 4, 5] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("embedding").cosineDistance(sampleVector).as("cosineDistance") + ]) + .execute() + // [END cosine_distance] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/vector_functions#dot_product + func dotProductFunction() async throws { + // [START dot_product] + let sampleVector = [0.0, 1, 2, 3, 4, 5] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("embedding").dotProduct(sampleVector).as("dotProduct") + ]) + .execute() + // [END dot_product] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/vector_functions#euclidean_distance + func euclideanDistanceFunction() async throws { + // [START euclidean_distance] + let sampleVector = [0.0, 1, 2, 3, 4, 5] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("embedding").euclideanDistance(sampleVector).as("euclideanDistance") + ]) + .execute() + // [END euclidean_distance] + print(result) + } + + // https://cloud.google.com/firestore/docs/pipeline/functions/vector_functions#vector_length + func vectorLengthFunction() async throws { + // [START vector_length] + let result = try await db.pipeline() + .collection("books") + .select([ + Field("embedding").vectorLength().as("vectorLength") + ]) + .execute() + // [END vector_length] + print(result) + } +} diff --git a/firestore/swift/firestore-smoketest/SolutionAggregationViewController.swift b/firestore/swift/firestore-smoketest/SolutionAggregationViewController.swift index c8e02157..743c861f 100644 --- a/firestore/swift/firestore-smoketest/SolutionAggregationViewController.swift +++ b/firestore/swift/firestore-smoketest/SolutionAggregationViewController.swift @@ -19,85 +19,79 @@ import UIKit import FirebaseCore import FirebaseFirestore -class SolutionAggregationViewController: UIViewController { - - var db: Firestore! - - // [START restaurant_struct] - struct Restaurant { - - let name: String - let avgRating: Float - let numRatings: Int - - init(name: String, avgRating: Float, numRatings: Int) { - self.name = name - self.avgRating = avgRating - self.numRatings = numRatings - } - - } - - let arinell = Restaurant(name: "Arinell Pizza", avgRating: 4.65, numRatings: 683) - // [END restaurant_struct] - - struct Rating { - let rating: Float - } - - override func viewDidLoad() { - super.viewDidLoad() - db = Firestore.firestore() +class SolutionAggregationViewController: @unchecked Sendable { + + nonisolated(unsafe) lazy var db: Firestore = { + Firestore.firestore() + }() + + // [START restaurant_struct] + struct Restaurant { + + let name: String + let avgRating: Float + let numRatings: Int + + } + + let arinell = Restaurant(name: "Arinell Pizza", avgRating: 4.65, numRatings: 683) + // [END restaurant_struct] + + struct Rating { + let rating: Float + } + + func getRatingsSubcollection() async { + // [START get_ratings_subcollection] + do { + let snapshot = try await db.collection("restaurants") + .document("arinell-pizza") + .collection("ratings") + .getDocuments() + print(snapshot) + } catch { + print(error) } - - func getRatingsSubcollection() { - // [START get_ratings_subcollection] - db.collection("restaurants") - .document("arinell-pizza") - .collection("ratings") - .getDocuments() { (querySnapshot, err) in - - // ... - + // [END get_ratings_subcollection] + } + + // [START add_rating_transaction] + func addRatingTransaction(restaurantRef: DocumentReference, rating: Float) async { + let ratingRef: DocumentReference = restaurantRef.collection("ratings").document() + + do { + let _ = try await db.runTransaction({ (transaction, errorPointer) -> Any? in + do { + let restaurantDocument = try transaction.getDocument(restaurantRef).data() + guard var restaurantData = restaurantDocument else { return nil } + + // Compute new number of ratings + let numRatings = restaurantData["numRatings"] as! Int + let newNumRatings = numRatings + 1 + + // Compute new average rating + let avgRating = restaurantData["avgRating"] as! Float + let oldRatingTotal = avgRating * Float(numRatings) + let newAvgRating = (oldRatingTotal + rating) / Float(newNumRatings) + + // Set new restaurant info + restaurantData["numRatings"] = newNumRatings + restaurantData["avgRating"] = newAvgRating + + // Commit to Firestore + transaction.setData(restaurantData, forDocument: restaurantRef) + transaction.setData(["rating": rating], forDocument: ratingRef) + } catch { + // Error getting restaurant data + // ... } - // [END get_ratings_subcollection] - } - // [START add_rating_transaction] - func addRatingTransaction(restaurantRef: DocumentReference, rating: Float) { - let ratingRef: DocumentReference = restaurantRef.collection("ratings").document() - - db.runTransaction({ (transaction, errorPointer) -> Any? in - do { - let restaurantDocument = try transaction.getDocument(restaurantRef).data() - guard var restaurantData = restaurantDocument else { return nil } - - // Compute new number of ratings - let numRatings = restaurantData["numRatings"] as! Int - let newNumRatings = numRatings + 1 - - // Compute new average rating - let avgRating = restaurantData["avgRating"] as! Float - let oldRatingTotal = avgRating * Float(numRatings) - let newAvgRating = (oldRatingTotal + rating) / Float(newNumRatings) - - // Set new restaurant info - restaurantData["numRatings"] = newNumRatings - restaurantData["avgRating"] = newAvgRating - - // Commit to Firestore - transaction.setData(restaurantData, forDocument: restaurantRef) - transaction.setData(["rating": rating], forDocument: ratingRef) - } catch { - // Error getting restaurant data - // ... - } - - return nil - }) { (object, err) in - // ... - } + return nil + }) + } catch { + // ... } - // [END add_rating_transaction] + } + // [END add_rating_transaction] } diff --git a/firestore/swift/firestore-smoketest/SolutionArraysViewController.swift b/firestore/swift/firestore-smoketest/SolutionArraysViewController.swift index 4f51b610..aece8f89 100644 --- a/firestore/swift/firestore-smoketest/SolutionArraysViewController.swift +++ b/firestore/swift/firestore-smoketest/SolutionArraysViewController.swift @@ -21,94 +21,86 @@ import FirebaseFirestore class SolutionArraysViewController: UIViewController { - var db: Firestore! - - override func viewDidLoad() { - super.viewDidLoad() - db = Firestore.firestore() - } - - func queryInCategory() { - // [START query_in_category] - db.collection("posts") - .whereField("categories.cats", isEqualTo: true) - .getDocuments() { (querySnapshot, err) in - - // ... - - } - // [END query_in_category] - } - - func queryInCategoryTimestamp() { - // [START query_in_category_timestamp_invalid] - db.collection("posts") - .whereField("categories.cats", isEqualTo: true) - .order(by: "timestamp") - // [END query_in_category_timestamp_invalid] - - // [START query_in_category_timestamp] - db.collection("posts") - .whereField("categories.cats", isGreaterThan: 0) - .order(by: "categories.cats") - // [END query_in_category_timestamp] + var db: Firestore! + + override func viewDidLoad() { + super.viewDidLoad() + db = Firestore.firestore() + } + + func queryInCategory() async { + // [START query_in_category] + do { + let querySnapshot = try await db.collection("posts") + .whereField("categories.cats", isEqualTo: true) + .getDocuments() + // Do something with the snapshot + print(querySnapshot) + } catch { + print("Error: \(error)") } - - // [START post_with_array] - struct PostArray { - - let title: String - let categories: [String] - - init(title: String, categories: [String]) { - self.title = title - self.categories = categories - } - + // [END query_in_category] + } + + func queryInCategoryTimestamp() { + // [START query_in_category_timestamp_invalid] + db.collection("posts") + .whereField("categories.cats", isEqualTo: true) + .order(by: "timestamp") + // [END query_in_category_timestamp_invalid] + + // [START query_in_category_timestamp] + db.collection("posts") + .whereField("categories.cats", isGreaterThan: 0) + .order(by: "categories.cats") + // [END query_in_category_timestamp] + } + + // [START post_with_array] + struct PostArray { + + let title: String + let categories: [String] + + init(title: String, categories: [String]) { + self.title = title + self.categories = categories } - let myArrayPost = PostArray(title: "My great post", - categories: ["technology", "opinion", "cats"]) - // [END post_with_array] + } - // [START post_with_dict] - struct PostDict { + let myArrayPost = PostArray(title: "My great post", + categories: ["technology", "opinion", "cats"]) + // [END post_with_array] - let title: String - let categories: [String: Bool] + // [START post_with_dict] + struct PostDict { - init(title: String, categories: [String: Bool]) { - self.title = title - self.categories = categories - } + let title: String + let categories: [String: Bool] - } - - let post = PostDict(title: "My great post", categories: [ - "technology": true, - "opinion": true, - "cats": true - ]) - // [END post_with_dict] + } - // [START post_with_dict_advanced] - struct PostDictAdvanced { + let post = PostDict(title: "My great post", categories: [ + "technology": true, + "opinion": true, + "cats": true + ]) + // [END post_with_dict] - let title: String - let categories: [String: UInt64] + // [START post_with_dict_advanced] + struct PostDictAdvanced { - init(title: String, categories: [String: UInt64]) { - self.title = title - self.categories = categories - } + let title: String + let categories: [String: UInt64] - } + } - let dictPost = PostDictAdvanced(title: "My great post", categories: [ - "technology": 1502144665, - "opinion": 1502144665, - "cats": 1502144665 - ]) - // [END post_with_dict_advanced] + let dictPost = PostDictAdvanced(title: "My great post", categories: [ + "technology": 1502144665, + "opinion": 1502144665, + "cats": 1502144665 + ]) + // [END post_with_dict_advanced] } diff --git a/firestore/swift/firestore-smoketest/SolutionBundles.swift b/firestore/swift/firestore-smoketest/SolutionBundles.swift new file mode 100644 index 00000000..39a839a3 --- /dev/null +++ b/firestore/swift/firestore-smoketest/SolutionBundles.swift @@ -0,0 +1,112 @@ +// +// Copyright (c) 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseFirestore + +class SolutionBundles { + + // [START fs_bundle_load] + // Utility function for errors when loading bundles. + func bundleLoadError(reason: String) -> NSError { + return NSError(domain: "FIRSampleErrorDomain", + code: 0, + userInfo: [NSLocalizedFailureReasonErrorKey: reason]) + } + + func fetchRemoteBundle(for firestore: Firestore, + from url: URL) async throws -> LoadBundleTaskProgress { + guard let inputStream = InputStream(url: url) else { + let error = self.bundleLoadError(reason: "Unable to create stream from the given url: \(url)") + throw error + } + + return try await firestore.loadBundle(inputStream) + } + + // Fetches a specific named query from the provided bundle. + func loadQuery(named queryName: String, + fromRemoteBundle bundleURL: URL, + with store: Firestore) async throws -> Query { + let _ = try await fetchRemoteBundle(for: store, from: bundleURL) + if let query = await store.getQuery(named: queryName) { + return query + } else { + throw bundleLoadError(reason: "Could not find query named \(queryName)") + } + } + + // Load a query and fetch its results from a bundle. + func runStoriesQuery() async { + let queryName = "latest-stories-query" + let firestore = Firestore.firestore() + let remoteBundle = URL(string: "https://example.com/createBundle")! + + do { + let query = try await loadQuery(named: queryName, + fromRemoteBundle: remoteBundle, + with: firestore) + let snapshot = try await query.getDocuments() + print(snapshot) + // handle query results + } catch { + print(error) + } + } + // [END fs_bundle_load] + + // [START fs_simple_bundle_load] + func loadBundle(from bundleURL: URL) { + let firestore = Firestore.firestore() + let data: Data + do { + try data = Data(contentsOf: bundleURL) + } catch { + print(error) + return + } + firestore.loadBundle(data) + } + // [END fs_simple_bundle_load] + + // [START fs_named_query] + func runNamedQuery() async { + let firestore = Firestore.firestore() + let queryName = "coll-query" + do { + guard let query = await firestore.getQuery(named: queryName) else { + throw bundleLoadError(reason: "Could not find query named \(queryName)") + } + let snapshot = try await query.getDocuments() + print(snapshot) + // ... + } catch { + print(error) + } + } + // [END fs_named_query] + + // [START bundle_observe_progress] + func observeProgress(of loadBundleTask: LoadBundleTask) { + let handle = loadBundleTask.addObserver { progress in + print("Loaded \(progress.bytesLoaded) bytes out of \(progress.totalBytes) total") + } + + // ... + loadBundleTask.removeObserverWith(handle: handle) + } + // [END bundle_observe_progress] + +} diff --git a/firestore/swift/firestore-smoketest/SolutionCountersViewController.swift b/firestore/swift/firestore-smoketest/SolutionCountersViewController.swift index 85809860..ab8df095 100644 --- a/firestore/swift/firestore-smoketest/SolutionCountersViewController.swift +++ b/firestore/swift/firestore-smoketest/SolutionCountersViewController.swift @@ -21,71 +21,72 @@ import FirebaseFirestore class SolutionCountersController: UIViewController { - var db: Firestore! + var db: Firestore! - override func viewDidLoad() { - super.viewDidLoad() - db = Firestore.firestore() - } + override func viewDidLoad() { + super.viewDidLoad() + db = Firestore.firestore() + } - // [START counter_structs] - // counters/${ID} - struct Counter { - let numShards: Int + // [START counter_structs] + // counters/${ID} + struct Counter { + let numShards: Int - init(numShards: Int) { - self.numShards = numShards - } + init(numShards: Int) { + self.numShards = numShards } + } - // counters/${ID}/shards/${NUM} - struct Shard { - let count: Int + // counters/${ID}/shards/${NUM} + struct Shard { + let count: Int - init(count: Int) { - self.count = count - } + init(count: Int) { + self.count = count } - // [END counter_structs] + } + // [END counter_structs] - // [START create_counter] - func createCounter(ref: DocumentReference, numShards: Int) { - ref.setData(["numShards": numShards]){ (err) in - for i in 0...numShards { - ref.collection("shards").document(String(i)).setData(["count": 0]) - } - } + // [START create_counter] + func createCounter(ref: DocumentReference, numShards: Int) async { + do { + try await ref.setData(["numShards": numShards]) + for i in 0...numShards { + try await ref.collection("shards").document(String(i)).setData(["count": 0]) + } + } catch { + // ... } - // [END create_counter] + } + // [END create_counter] - // [START increment_counter] - func incrementCounter(ref: DocumentReference, numShards: Int) { - // Select a shard of the counter at random - let shardId = Int(arc4random_uniform(UInt32(numShards))) - let shardRef = ref.collection("shards").document(String(shardId)) + // [START increment_counter] + func incrementCounter(ref: DocumentReference, numShards: Int) { + // Select a shard of the counter at random + let shardId = Int(arc4random_uniform(UInt32(numShards))) + let shardRef = ref.collection("shards").document(String(shardId)) - shardRef.updateData([ - "count": FieldValue.increment(Int64(1)) - ]) - } - // [END increment_counter] + shardRef.updateData([ + "count": FieldValue.increment(Int64(1)) + ]) + } + // [END increment_counter] - // [START get_count] - func getCount(ref: DocumentReference) { - ref.collection("shards").getDocuments() { (querySnapshot, err) in - var totalCount = 0 - if err != nil { - // Error getting shards - // ... - } else { - for document in querySnapshot!.documents { - let count = document.data()["count"] as! Int - totalCount += count - } - } + // [START get_count] + func getCount(ref: DocumentReference) async { + do { + let querySnapshot = try await ref.collection("shards").getDocuments() + var totalCount = 0 + for document in querySnapshot.documents { + let count = document.data()["count"] as! Int + totalCount += count + } - print("Total count is \(totalCount)") - } + print("Total count is \(totalCount)") + } catch { + // handle error } - // [END get_count] + } + // [END get_count] } diff --git a/firestore/swift/firestore-smoketest/SolutionGeoPointViewController.swift b/firestore/swift/firestore-smoketest/SolutionGeoPointViewController.swift new file mode 100644 index 00000000..963f3f78 --- /dev/null +++ b/firestore/swift/firestore-smoketest/SolutionGeoPointViewController.swift @@ -0,0 +1,115 @@ +// +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +import FirebaseCore +import FirebaseFirestore +import GeoFireUtils +import CoreLocation + +class SolutionGeoPointController: UIViewController { + + var db: Firestore! + + override func viewDidLoad() { + super.viewDidLoad() + db = Firestore.firestore() + } + + func storeGeoHash() { + // [START fs_geo_add_hash] + // Compute the GeoHash for a lat/lng point + let latitude = 51.5074 + let longitude = 0.12780 + let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) + + let hash = GFUtils.geoHash(forLocation: location) + + // Add the hash and the lat/lng to the document. We will use the hash + // for queries and the lat/lng for distance comparisons. + let documentData: [String: Any] = [ + "geohash": hash, + "lat": latitude, + "lng": longitude + ] + + let londonRef = db.collection("cities").document("LON") + londonRef.updateData(documentData) { error in + // ... + } + // [END fs_geo_add_hash] + } + + func geoQuery() async { + // [START fs_geo_query_hashes] + // Find cities within 50km of London + let center = CLLocationCoordinate2D(latitude: 51.5074, longitude: 0.1278) + let radiusInM: Double = 50 * 1000 + + // Each item in 'bounds' represents a startAt/endAt pair. We have to issue + // a separate query for each pair. There can be up to 9 pairs of bounds + // depending on overlap, but in most cases there are 4. + let queryBounds = GFUtils.queryBounds(forLocation: center, + withRadius: radiusInM) + let queries = queryBounds.map { bound -> Query in + return db.collection("cities") + .order(by: "geohash") + .start(at: [bound.startValue]) + .end(at: [bound.endValue]) + } + + @Sendable func fetchMatchingDocs(from query: Query, + center: CLLocationCoordinate2D, + radiusInMeters: Double) async throws -> [QueryDocumentSnapshot] { + let snapshot = try await query.getDocuments() + // Collect all the query results together into a single list + return snapshot.documents.filter { document in + let lat = document.data()["lat"] as? Double ?? 0 + let lng = document.data()["lng"] as? Double ?? 0 + let coordinates = CLLocation(latitude: lat, longitude: lng) + let centerPoint = CLLocation(latitude: center.latitude, longitude: center.longitude) + + // We have to filter out a few false positives due to GeoHash accuracy, but + // most will match + let distance = GFUtils.distance(from: centerPoint, to: coordinates) + return distance <= radiusInM + } + } + + // After all callbacks have executed, matchingDocs contains the result. Note that this code + // executes all queries serially, which may not be optimal for performance. + do { + let matchingDocs = try await withThrowingTaskGroup(of: [QueryDocumentSnapshot].self) { group -> [QueryDocumentSnapshot] in + for query in queries { + group.addTask { + try await fetchMatchingDocs(from: query, center: center, radiusInMeters: radiusInM) + } + } + var matchingDocs = [QueryDocumentSnapshot]() + for try await documents in group { + matchingDocs.append(contentsOf: documents) + } + return matchingDocs + } + + print("Docs matching geoquery: \(matchingDocs)") + } catch { + print("Unable to fetch snapshot data. \(error)") + } + // [END fs_geo_query_hashes] + } +} diff --git a/firestore/swift/firestore-smoketest/ViewController.swift b/firestore/swift/firestore-smoketest/ViewController.swift index 56405181..1d29b193 100644 --- a/firestore/swift/firestore-smoketest/ViewController.swift +++ b/firestore/swift/firestore-smoketest/ViewController.swift @@ -21,1209 +21,1460 @@ import FirebaseFirestore class ViewController: UIViewController { - var db: Firestore! + var smokeTests: SmokeTests! - override func viewDidLoad() { - super.viewDidLoad() + override func viewDidLoad() { + super.viewDidLoad() - // [START setup] - let settings = FirestoreSettings() + // [START setup] + let settings = FirestoreSettings() - Firestore.firestore().settings = settings - // [END setup] - db = Firestore.firestore() - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - } - - @IBAction func didTouchSmokeTestButton(_ sender: AnyObject) { - // Quickstart - addAdaLovelace() - addAlanTuring() - getCollection() - listenForUsers() - - // Structure Data - demonstrateReferences() - - // Save Data - setDocument() - dataTypes() - setData() - addDocument() - newDocument() - updateDocument() - createIfMissing() - updateDocumentNested() - deleteDocument() - deleteCollection() - deleteField() - serverTimestamp() - serverTimestampOptions() - simpleTransaction() - transaction() - writeBatch() - - // Retrieve Data - exampleData() - exampleDataCollectionGroup() - getDocument() - customClassGetDocument() - listenDocument() - listenDocumentLocal() - listenWithMetadata() - getMultiple() - getMultipleAll() - listenMultiple() - listenDiffs() - listenState() - detachListener() - handleListenErrors() - - // Query Data - simpleQueries() - exampleFilters() - onlyCapitals() - chainFilters() - validRangeFilters() - - // Can't run this since it throws a fatal error - // invalidRangeFilters() - - orderAndLimit() - orderAndLimitDesc() - orderMultiple() - filterAndOrder() - validFilterAndOrder() - - // Can't run this since it throws a fatal error - // invalidFilterAndOrder() - - // Enable Offline - // Can't run this since it throws a fatal error - // enableOffline() - listenToOffline() - toggleOffline() - setupCacheSize() - - // Cursors - simpleCursor() - snapshotCursor() - paginate() - multiCursor() - } - - @IBAction func didTouchDeleteButton(_ sender: AnyObject) { - deleteCollection(collection: "users") - deleteCollection(collection: "cities") - } + Firestore.firestore().settings = settings + // [END setup] + smokeTests = SmokeTests(Firestore.firestore()) + } - private func deleteCollection(collection: String) { - db.collection(collection).getDocuments() { (querySnapshot, err) in - if let err = err { - print("Error getting documents: \(err)") - return - } - - for document in querySnapshot!.documents { - print("Deleting \(document.documentID) => \(document.data())") - document.reference.delete() - } - } - } + @IBAction func didTouchSmokeTestButton(_ sender: AnyObject) { + smokeTests.runAllSmokeTests() + } - private func setupCacheSize() { - // [START fs_setup_cache] - let settings = Firestore.firestore().settings - settings.cacheSizeBytes = FirestoreCacheSizeUnlimited - Firestore.firestore().settings = settings - // [END fs_setup_cache] - } + @IBAction func didTouchDeleteButton(_ sender: AnyObject) { + smokeTests.deleteCollection(collection: "users") + smokeTests.deleteCollection(collection: "cities") + } - // ======================================================================================= - // ======== https://firebase.google.com/preview/firestore/client/quickstart ============== - // ======================================================================================= - - private func addAdaLovelace() { - // [START add_ada_lovelace] - // Add a new document with a generated ID - var ref: DocumentReference? = nil - ref = db.collection("users").addDocument(data: [ - "first": "Ada", - "last": "Lovelace", - "born": 1815 - ]) { err in - if let err = err { - print("Error adding document: \(err)") - } else { - print("Document added with ID: \(ref!.documentID)") - } - } - // [END add_ada_lovelace] - } - - private func addAlanTuring() { - var ref: DocumentReference? = nil - - // [START add_alan_turing] - // Add a second document with a generated ID. - ref = db.collection("users").addDocument(data: [ - "first": "Alan", - "middle": "Mathison", - "last": "Turing", - "born": 1912 - ]) { err in - if let err = err { - print("Error adding document: \(err)") - } else { - print("Document added with ID: \(ref!.documentID)") - } - } - // [END add_alan_turing] - } - - private func getCollection() { - // [START get_collection] - db.collection("users").getDocuments() { (querySnapshot, err) in - if let err = err { - print("Error getting documents: \(err)") - } else { - for document in querySnapshot!.documents { - print("\(document.documentID) => \(document.data())") - } - } - } - // [END get_collection] - } +} - private func listenForUsers() { - // [START listen_for_users] - // Listen to a query on a collection. - // - // We will get a first snapshot with the initial results and a new - // snapshot each time there is a change in the results. - db.collection("users") - .whereField("born", isLessThan: 1900) - .addSnapshotListener { querySnapshot, error in - guard let snapshot = querySnapshot else { - print("Error retreiving snapshots \(error!)") - return - } - print("Current users born before 1900: \(snapshot.documents.map { $0.data() })") - } - // [END listen_for_users] - } +class SmokeTests: @unchecked Sendable { + + nonisolated(unsafe) private let db: Firestore + + init(_ db: Firestore) { + self.db = db + } + + func runAllSmokeTests() { + // Quickstart + Task { + await addAdaLovelace() + await addAlanTuring() + await getCollection() + listenForUsers() + } + + // Structure Data + demonstrateReferences() + + // Save Data + Task { + await setDocument() + await dataTypes() + setData() + await addDocument() + newDocument() + await updateDocument() + createIfMissing() + await updateDocumentNested() + await deleteDocument() + deleteCollection() + await deleteField() + await serverTimestamp() + serverTimestampOptions() + await simpleTransaction() + await transaction() + await writeBatch() + } + + // Retrieve Data + Task { + exampleData() + exampleDataCollectionGroup() + await getDocument() + await customClassGetDocument() + listenDocument() + listenDocumentLocal() + listenWithMetadata() + await getMultiple() + await getMultipleAll() + listenMultiple() + listenDiffs() + listenState() + detachListener() + handleListenErrors() + } + + // Query Data + simpleQueries() + exampleFilters() + onlyCapitals() + chainFilters() + validRangeFilters() + + // IN Queries + arrayContainsAnyQueries() + inQueries() + + // Can't run this since it throws a fatal error + // invalidRangeFilters() + + orderAndLimit() + orderAndLimitDesc() + orderMultiple() + filterAndOrder() + validFilterAndOrder() + + // Can't run this since it throws a fatal error + // invalidFilterAndOrder() + + // Enable Offline + // Can't run this since it throws a fatal error + // enableOffline() + listenToOffline() + toggleOffline() + setupCacheSize() + + // Cursors + simpleCursor() + snapshotCursor() + paginate() + multiCursor() + } + + func deleteCollection(collection: String) { + db.collection(collection).getDocuments() { (querySnapshot, err) in + if let err = err { + print("Error getting documents: \(err)") + return + } - // ======================================================================================= - // ======= https://firebase.google.com/preview/firestore/client/structure-data =========== - // ======================================================================================= - - - private func demonstrateReferences() { - // [START doc_reference] - let alovelaceDocumentRef = db.collection("users").document("alovelace") - // [END doc_reference] - print(alovelaceDocumentRef) - - // [START collection_reference] - let usersCollectionRef = db.collection("users") - // [END collection_reference] - print(usersCollectionRef) - - // [START subcollection_reference] - let messageRef = db - .collection("rooms").document("roomA") - .collection("messages").document("message1") - // [END subcollection_reference] - print(messageRef) - - // [START path_reference] - let aLovelaceDocumentReference = db.document("users/alovelace") - // [END path_reference] - print(aLovelaceDocumentReference) + for document in querySnapshot!.documents { + print("Deleting \(document.documentID) => \(document.data())") + document.reference.delete() + } } - - // ======================================================================================= - // ========= https://firebase.google.com/preview/firestore/client/save-data ============== - // ======================================================================================= - - private func setDocument() { - // [START set_document] - // Add a new document in collection "cities" - db.collection("cities").document("LA").setData([ - "name": "Los Angeles", - "state": "CA", - "country": "USA" - ]) { err in - if let err = err { - print("Error writing document: \(err)") - } else { - print("Document successfully written!") - } + } + + private func setupCacheSize() { + // [START fs_setup_cache] + let settings = Firestore.firestore().settings + // Set cache size to 100 MB + settings.cacheSettings = PersistentCacheSettings(sizeBytes: 100 * 1024 * 1024 as NSNumber) + Firestore.firestore().settings = settings + // [END fs_setup_cache] + } + + // ======================================================================================= + // ======== https://firebase.google.com/preview/firestore/client/quickstart ============== + // ======================================================================================= + + private func addAdaLovelace() async { + // [START add_ada_lovelace] + // Add a new document with a generated ID + do { + let ref = try await db.collection("users").addDocument(data: [ + "first": "Ada", + "last": "Lovelace", + "born": 1815 + ]) + print("Document added with ID: \(ref.documentID)") + } catch { + print("Error adding document: \(error)") + } + // [END add_ada_lovelace] + } + + private func addAlanTuring() async { + // [START add_alan_turing] + // Add a second document with a generated ID. + do { + let ref = try await db.collection("users").addDocument(data: [ + "first": "Alan", + "middle": "Mathison", + "last": "Turing", + "born": 1912 + ]) + print("Document added with ID: \(ref.documentID)") + } catch { + print("Error adding document: \(error)") + } + // [END add_alan_turing] + } + + private func getCollection() async { + // [START get_collection] + do { + let snapshot = try await db.collection("users").getDocuments() + for document in snapshot.documents { + print("\(document.documentID) => \(document.data())") + } + } catch { + print("Error getting documents: \(error)") + } + // [END get_collection] + } + + private func listenForUsers() { + // [START listen_for_users] + // Listen to a query on a collection. + // + // We will get a first snapshot with the initial results and a new + // snapshot each time there is a change in the results. + db.collection("users") + .whereField("born", isLessThan: 1900) + .addSnapshotListener { querySnapshot, error in + guard let snapshot = querySnapshot else { + print("Error retreiving snapshots \(error!)") + return } - // [END set_document] - } - - private func dataTypes() { - // [START data_types] - let docData: [String: Any] = [ - "stringExample": "Hello world!", - "booleanExample": true, - "numberExample": 3.14159265, - "dateExample": Timestamp(date: Date()), - "arrayExample": [5, true, "hello"], - "nullExample": NSNull(), - "objectExample": [ - "a": 5, - "b": [ - "nested": "foo" - ] - ] + print("Current users born before 1900: \(snapshot.documents.map { $0.data() })") + } + // [END listen_for_users] + } + + // ======================================================================================= + // ======= https://firebase.google.com/preview/firestore/client/structure-data =========== + // ======================================================================================= + + + private func demonstrateReferences() { + // [START doc_reference] + let alovelaceDocumentRef = db.collection("users").document("alovelace") + // [END doc_reference] + print(alovelaceDocumentRef) + + // [START collection_reference] + let usersCollectionRef = db.collection("users") + // [END collection_reference] + print(usersCollectionRef) + + // [START subcollection_reference] + let messageRef = db + .collection("rooms").document("roomA") + .collection("messages").document("message1") + // [END subcollection_reference] + print(messageRef) + + // [START path_reference] + let aLovelaceDocumentReference = db.document("users/alovelace") + // [END path_reference] + print(aLovelaceDocumentReference) + } + + // ======================================================================================= + // ========= https://firebase.google.com/preview/firestore/client/save-data ============== + // ======================================================================================= + + private func setDocument() async { + // [START set_document] + // Add a new document in collection "cities" + do { + try await db.collection("cities").document("LA").setData([ + "name": "Los Angeles", + "state": "CA", + "country": "USA" + ]) + print("Document successfully written!") + } catch { + print("Error writing document: \(error)") + } + // [END set_document] + } + + private func setDocumentWithCodable() { + // [START set_document_codable] + let city = City(name: "Los Angeles", + state: "CA", + country: "USA", + isCapital: false, + population: 5000000) + + do { + try db.collection("cities").document("LA").setData(from: city) + } catch let error { + print("Error writing city to Firestore: \(error)") + } + // [END set_document_codable] + } + + private func dataTypes() async { + // [START data_types] + let docData: [String: Any] = [ + "stringExample": "Hello world!", + "booleanExample": true, + "numberExample": 3.14159265, + "dateExample": Timestamp(date: Date()), + "arrayExample": [5, true, "hello"], + "nullExample": NSNull(), + "objectExample": [ + "a": 5, + "b": [ + "nested": "foo" ] - db.collection("data").document("one").setData(docData) { err in - if let err = err { - print("Error writing document: \(err)") - } else { - print("Document successfully written!") - } + ] + ] + do { + try await db.collection("data").document("one").setData(docData) + print("Document successfully written!") + } catch { + print("Error writing document: \(error)") + } + // [END data_types] + } + + private func setData() { + let data: [String: Any] = [:] + + // [START set_data] + db.collection("cities").document("new-city-id").setData(data) + // [END set_data] + } + + private func addDocument() async { + // [START add_document] + // Add a new document with a generated id. + do { + let ref = try await db.collection("cities").addDocument(data: [ + "name": "Tokyo", + "country": "Japan" + ]) + print("Document added with ID: \(ref.documentID)") + } catch { + print("Error adding document: \(error)") + } + // [END add_document] + } + + private func newDocument() { + // [START new_document] + let newCityRef = db.collection("cities").document() + + // later... + newCityRef.setData([ + // [START_EXCLUDE] + "name": "Some City Name" + // [END_EXCLUDE] + ]) + // [END new_document] + } + + private func updateDocument() async { + // [START update_document] + let washingtonRef = db.collection("cities").document("DC") + + // Set the "capital" field of the city 'DC' + do { + try await washingtonRef.updateData([ + "capital": true + ]) + print("Document successfully updated") + } catch { + print("Error updating document: \(error)") + } + // [END update_document] + } + + private func updateDocumentArray() { + // [START update_document_array] + let washingtonRef = db.collection("cities").document("DC") + + // Atomically add a new region to the "regions" array field. + washingtonRef.updateData([ + "regions": FieldValue.arrayUnion(["greater_virginia"]) + ]) + + // Atomically remove a region from the "regions" array field. + washingtonRef.updateData([ + "regions": FieldValue.arrayRemove(["east_coast"]) + ]) + // [END update_document_array] + } + + private func updateDocumentIncrement() { + // [START update_document-increment] + let washingtonRef = db.collection("cities").document("DC") + + // Atomically increment the population of the city by 50. + // Note that increment() with no arguments increments by 1. + washingtonRef.updateData([ + "population": FieldValue.increment(Int64(50)) + ]) + // [END update_document-increment] + } + + private func createIfMissing() { + // [START create_if_missing] + // Update one field, creating the document if it does not exist. + db.collection("cities").document("BJ").setData([ "capital": true ], merge: true) + // [END create_if_missing] + } + + private func updateDocumentNested() async { + // [START update_document_nested] + // Create an initial document to update. + let frankDocRef = db.collection("users").document("frank") + do { + try await frankDocRef.setData([ + "name": "Frank", + "favorites": [ "food": "Pizza", "color": "Blue", "subject": "recess" ], + "age": 12 + ]) + + // To update age and favorite color: + try await frankDocRef.updateData([ + "age": 13, + "favorites.color": "Red" + ]) + print("Document successfully updated") + } catch { + print("Error updating document: \(error)") + } + // [END update_document_nested] + } + + private func deleteDocument() async { + // [START delete_document] + do { + try await db.collection("cities").document("DC").delete() + print("Document successfully removed!") + } catch { + print("Error removing document: \(error)") + } + // [END delete_document] + } + + private func deleteCollection() { + // [START delete_collection] + func delete(collection: CollectionReference, batchSize: Int = 100, completion: @escaping (Error?) -> ()) { + // Limit query to avoid out-of-memory errors on large collections. + // When deleting a collection guaranteed to fit in memory, batching can be avoided entirely. + collection.limit(to: batchSize).getDocuments { (docset, error) in + // An error occurred. + guard let docset = docset else { + completion(error) + return } - // [END data_types] - } - - private func setData() { - let data: [String: Any] = [:] - - // [START set_data] - db.collection("cities").document("new-city-id").setData(data) - // [END set_data] - } - - private func addDocument() { - // [START add_document] - // Add a new document with a generated id. - var ref: DocumentReference? = nil - ref = db.collection("cities").addDocument(data: [ - "name": "Tokyo", - "country": "Japan" - ]) { err in - if let err = err { - print("Error adding document: \(err)") - } else { - print("Document added with ID: \(ref!.documentID)") - } + // There's nothing to delete. + guard docset.count > 0 else { + completion(nil) + return } - // [END add_document] - } - private func newDocument() { - // [START new_document] - let newCityRef = db.collection("cities").document() - - // later... - newCityRef.setData([ - // [START_EXCLUDE] - "name": "Some City Name" - // [END_EXCLUDE] - ]) - // [END new_document] - } - - private func updateDocument() { - // [START update_document] - let washingtonRef = db.collection("cities").document("DC") - - // Set the "capital" field of the city 'DC' - washingtonRef.updateData([ - "capital": true - ]) { err in - if let err = err { - print("Error updating document: \(err)") - } else { - print("Document successfully updated") - } + let batch = collection.firestore.batch() + docset.documents.forEach { batch.deleteDocument($0.reference) } + + batch.commit { (batchError) in + if let batchError = batchError { + // Stop the deletion process and handle the error. Some elements + // may have been deleted. + completion(batchError) + } else { + delete(collection: collection, batchSize: batchSize, completion: completion) + } } - // [END update_document] - } - - private func updateDocumentArray() { - // [START update_document_array] - let washingtonRef = db.collection("cities").document("DC") - - // Atomically add a new region to the "regions" array field. - washingtonRef.updateData([ - "regions": FieldValue.arrayUnion(["greater_virginia"]) - ]) - - // Atomically remove a region from the "regions" array field. - washingtonRef.updateData([ - "regions": FieldValue.arrayRemove(["east_coast"]) - ]) - // [END update_document_array] - } - - private func updateDocumentIncrement() { - // [START update_document-increment] - let washingtonRef = db.collection("cities").document("DC") - - // Atomically incrememnt the population of the city by 50. - // Note that increment() with no arguments increments by 1. - washingtonRef.updateData([ - "population": FieldValue.increment(Int64(50)) - ]) - // [END update_document-increment] - } - - private func createIfMissing() { - // [START create_if_missing] - // Update one field, creating the document if it does not exist. - db.collection("cities").document("BJ").setData([ "capital": true ], merge: true) - // [END create_if_missing] + } } - - private func updateDocumentNested() { - // [START update_document_nested] - // Create an initial document to update. - let frankDocRef = db.collection("users").document("frank") - frankDocRef.setData([ - "name": "Frank", - "favorites": [ "food": "Pizza", "color": "Blue", "subject": "recess" ], - "age": 12 - ]) - - // To update age and favorite color: - db.collection("users").document("frank").updateData([ - "age": 13, - "favorites.color": "Red" - ]) { err in - if let err = err { - print("Error updating document: \(err)") - } else { - print("Document successfully updated") - } + // [END delete_collection] + } + + private func deleteField() async { + // [START delete_field] + do { + + try await db.collection("cities").document("BJ").updateData([ + "capital": FieldValue.delete(), + ]) + print("Document successfully updated") + } catch { + print("Error updating document: \(error)") + } + // [END delete_field] + } + + private func serverTimestamp() async { + // [START server_timestamp] + do { + try await db.collection("objects").document("some-id").updateData([ + "lastUpdated": FieldValue.serverTimestamp(), + ]) + print("Document successfully updated") + } catch { + print("Error updating document: \(error)") + } + // [END server_timestamp] + } + + private func serverTimestampOptions() { + // [START server_timestamp_options] + // Perform an update followed by an immediate read without waiting for the update to + // complete. Due to the snapshot options we will get two results: one with an estimated + // timestamp and one with a resolved server timestamp. + let docRef = db.collection("objects").document("some-id") + docRef.updateData(["timestamp": FieldValue.serverTimestamp()]) + + docRef.addSnapshotListener { (snapshot, error) in + guard let timestamp = snapshot?.data(with: .estimate)?["timestamp"] else { return } + guard let pendingWrites = snapshot?.metadata.hasPendingWrites else { return } + print("Timestamp: \(timestamp), pending: \(pendingWrites)") + } + // [END server_timestamp_options] + } + + private func simpleTransaction() async { + // [START simple_transaction] + let sfReference = db.collection("cities").document("SF") + + do { + let _ = try await db.runTransaction({ (transaction, errorPointer) -> Any? in + let sfDocument: DocumentSnapshot + do { + try sfDocument = transaction.getDocument(sfReference) + } catch let fetchError as NSError { + errorPointer?.pointee = fetchError + return nil } - // [END update_document_nested] - } - private func deleteDocument() { - // [START delete_document] - db.collection("cities").document("DC").delete() { err in - if let err = err { - print("Error removing document: \(err)") - } else { - print("Document successfully removed!") - } + guard let oldPopulation = sfDocument.data()?["population"] as? Int else { + let error = NSError( + domain: "AppErrorDomain", + code: -1, + userInfo: [ + NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(sfDocument)" + ] + ) + errorPointer?.pointee = error + return nil } - // [END delete_document] - } - private func deleteCollection() { - // [START delete_collection] - func delete(collection: CollectionReference, batchSize: Int = 100, completion: @escaping (Error?) -> ()) { - // Limit query to avoid out-of-memory errors on large collections. - // When deleting a collection guaranteed to fit in memory, batching can be avoided entirely. - collection.limit(to: batchSize).getDocuments { (docset, error) in - // An error occurred. - guard let docset = docset else { - completion(error) - return - } - // There's nothing to delete. - guard docset.count > 0 else { - completion(nil) - return - } - - let batch = collection.firestore.batch() - docset.documents.forEach { batch.deleteDocument($0.reference) } - - batch.commit { (batchError) in - if let batchError = batchError { - // Stop the deletion process and handle the error. Some elements - // may have been deleted. - completion(batchError) - } else { - delete(collection: collection, batchSize: batchSize, completion: completion) - } - } - } + // Note: this could be done without a transaction + // by updating the population using FieldValue.increment() + transaction.updateData(["population": oldPopulation + 1], forDocument: sfReference) + return nil + }) + print("Transaction successfully committed!") + } catch { + print("Transaction failed: \(error)") + } + // [END simple_transaction] + } + + private func transaction() async { + // [START transaction] + let sfReference = db.collection("cities").document("SF") + + do { + let object = try await db.runTransaction({ (transaction, errorPointer) -> Any? in + let sfDocument: DocumentSnapshot + do { + try sfDocument = transaction.getDocument(sfReference) + } catch let fetchError as NSError { + errorPointer?.pointee = fetchError + return nil } - // [END delete_collection] - } - private func deleteField() { - // [START delete_field] - db.collection("cities").document("BJ").updateData([ - "capital": FieldValue.delete(), - ]) { err in - if let err = err { - print("Error updating document: \(err)") - } else { - print("Document successfully updated") - } + guard let oldPopulation = sfDocument.data()?["population"] as? Int else { + let error = NSError( + domain: "AppErrorDomain", + code: -1, + userInfo: [ + NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(sfDocument)" + ] + ) + errorPointer?.pointee = error + return nil } - // [END delete_field] - } - private func serverTimestamp() { - // [START server_timestamp] - db.collection("objects").document("some-id").updateData([ - "lastUpdated": FieldValue.serverTimestamp(), - ]) { err in - if let err = err { - print("Error updating document: \(err)") - } else { - print("Document successfully updated") - } + // Note: this could be done without a transaction + // by updating the population using FieldValue.increment() + let newPopulation = oldPopulation + 1 + guard newPopulation <= 1000000 else { + let error = NSError( + domain: "AppErrorDomain", + code: -2, + userInfo: [NSLocalizedDescriptionKey: "Population \(newPopulation) too big"] + ) + errorPointer?.pointee = error + return nil } - // [END server_timestamp] - } - private func serverTimestampOptions() { - // [START server_timestamp_options] - // Perform an update followed by an immediate read without waiting for the update to - // complete. Due to the snapshot options we will get two results: one with an estimated - // timestamp and one with a resolved server timestamp. - let docRef = db.collection("objects").document("some-id") - docRef.updateData(["timestamp": FieldValue.serverTimestamp()]) - - docRef.addSnapshotListener { (snapshot, error) in - guard let timestamp = snapshot?.data(with: .estimate)?["timestamp"] else { return } - guard let pendingWrites = snapshot?.metadata.hasPendingWrites else { return } - print("Timestamp: \(timestamp), pending: \(pendingWrites)") - } - // [END server_timestamp_options] + transaction.updateData(["population": newPopulation], forDocument: sfReference) + return newPopulation + }) + print("Population increased to \(object!)") + } catch { + print("Error updating population: \(error)") + } + // [END transaction] + } + + private func writeBatch() async { + // [START write_batch] + // Get new write batch + let batch = db.batch() + + // Set the value of 'NYC' + let nycRef = db.collection("cities").document("NYC") + batch.setData([:], forDocument: nycRef) + + // Update the population of 'SF' + let sfRef = db.collection("cities").document("SF") + batch.updateData(["population": 1000000 ], forDocument: sfRef) + + // Delete the city 'LA' + let laRef = db.collection("cities").document("LA") + batch.deleteDocument(laRef) + + // Commit the batch + do { + try await batch.commit() + print("Batch write succeeded.") + } catch { + print("Error writing batch: \(error)") + } + // [END write_batch] + } + + // ======================================================================================= + // ======= https://firebase.google.com/preview/firestore/client/retrieve-data ============ + // ======================================================================================= + + private func exampleData() { + // [START example_data] + let citiesRef = db.collection("cities") + + citiesRef.document("SF").setData([ + "name": "San Francisco", + "state": "CA", + "country": "USA", + "capital": false, + "population": 860000, + "regions": ["west_coast", "norcal"] + ]) + citiesRef.document("LA").setData([ + "name": "Los Angeles", + "state": "CA", + "country": "USA", + "capital": false, + "population": 3900000, + "regions": ["west_coast", "socal"] + ]) + citiesRef.document("DC").setData([ + "name": "Washington D.C.", + "country": "USA", + "capital": true, + "population": 680000, + "regions": ["east_coast"] + ]) + citiesRef.document("TOK").setData([ + "name": "Tokyo", + "country": "Japan", + "capital": true, + "population": 9000000, + "regions": ["kanto", "honshu"] + ]) + citiesRef.document("BJ").setData([ + "name": "Beijing", + "country": "China", + "capital": true, + "population": 21500000, + "regions": ["jingjinji", "hebei"] + ]) + // [END example_data] + } + + private func exampleDataCollectionGroup() { + // [START fs_collection_group_query_data_setup] + let citiesRef = db.collection("cities") + + var data = ["name": "Golden Gate Bridge", "type": "bridge"] + citiesRef.document("SF").collection("landmarks").addDocument(data: data) + + data = ["name": "Legion of Honor", "type": "museum"] + citiesRef.document("SF").collection("landmarks").addDocument(data: data) + + data = ["name": "Griffith Park", "type": "park"] + citiesRef.document("LA").collection("landmarks").addDocument(data: data) + + data = ["name": "The Getty", "type": "museum"] + citiesRef.document("LA").collection("landmarks").addDocument(data: data) + + data = ["name": "Lincoln Memorial", "type": "memorial"] + citiesRef.document("DC").collection("landmarks").addDocument(data: data) + + data = ["name": "National Air and Space Museum", "type": "museum"] + citiesRef.document("DC").collection("landmarks").addDocument(data: data) + + data = ["name": "Ueno Park", "type": "park"] + citiesRef.document("TOK").collection("landmarks").addDocument(data: data) + + data = ["name": "National Museum of Nature and Science", "type": "museum"] + citiesRef.document("TOK").collection("landmarks").addDocument(data: data) + + data = ["name": "Jingshan Park", "type": "park"] + citiesRef.document("BJ").collection("landmarks").addDocument(data: data) + + data = ["name": "Beijing Ancient Observatory", "type": "museum"] + citiesRef.document("BJ").collection("landmarks").addDocument(data: data) + // [END fs_collection_group_query_data_setup] + } + + private func getDocument() async { + // [START get_document] + let docRef = db.collection("cities").document("SF") + + do { + let document = try await docRef.getDocument() + if document.exists { + let dataDescription = document.data().map(String.init(describing:)) ?? "nil" + print("Document data: \(dataDescription)") + } else { + print("Document does not exist") + } + } catch { + print("Error getting document: \(error)") } + // [END get_document] + } - private func simpleTransaction() { - // [START simple_transaction] - let sfReference = db.collection("cities").document("SF") - - db.runTransaction({ (transaction, errorPointer) -> Any? in - let sfDocument: DocumentSnapshot - do { - try sfDocument = transaction.getDocument(sfReference) - } catch let fetchError as NSError { - errorPointer?.pointee = fetchError - return nil - } - - guard let oldPopulation = sfDocument.data()?["population"] as? Int else { - let error = NSError( - domain: "AppErrorDomain", - code: -1, - userInfo: [ - NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(sfDocument)" - ] - ) - errorPointer?.pointee = error - return nil - } - - // Note: this could be done without a transaction - // by updating the population using FieldValue.increment() - transaction.updateData(["population": oldPopulation + 1], forDocument: sfReference) - return nil - }) { (object, error) in - if let error = error { - print("Transaction failed: \(error)") - } else { - print("Transaction successfully committed!") - } - } - // [END simple_transaction] - } + private func getDocumentWithOptions() async { + // [START get_document_options] + let docRef = db.collection("cities").document("SF") - private func transaction() { - // [START transaction] - let sfReference = db.collection("cities").document("SF") - - db.runTransaction({ (transaction, errorPointer) -> Any? in - let sfDocument: DocumentSnapshot - do { - try sfDocument = transaction.getDocument(sfReference) - } catch let fetchError as NSError { - errorPointer?.pointee = fetchError - return nil - } - - guard let oldPopulation = sfDocument.data()?["population"] as? Int else { - let error = NSError( - domain: "AppErrorDomain", - code: -1, - userInfo: [ - NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(sfDocument)" - ] - ) - errorPointer?.pointee = error - return nil - } - - // Note: this could be done without a transaction - // by updating the population using FieldValue.increment() - let newPopulation = oldPopulation + 1 - guard newPopulation <= 1000000 else { - let error = NSError( - domain: "AppErrorDomain", - code: -2, - userInfo: [NSLocalizedDescriptionKey: "Population \(newPopulation) too big"] - ) - errorPointer?.pointee = error - return nil - } - - transaction.updateData(["population": newPopulation], forDocument: sfReference) - return newPopulation - }) { (object, error) in - if let error = error { - print("Error updating population: \(error)") - } else { - print("Population increased to \(object!)") - } + do { + // Force the SDK to fetch the document from the cache. Could also specify + // FirestoreSource.server or FirestoreSource.default. + let document = try await docRef.getDocument(source: .cache) + if document.exists { + let dataDescription = document.data().map(String.init(describing:)) ?? "nil" + print("Cached document data: \(dataDescription)") + } else { + print("Document does not exist in cache") + } + } catch { + print("Error getting document: \(error)") + } + // [END get_document_options] + } + + private func customClassGetDocument() async { + // [START custom_type] + let docRef = db.collection("cities").document("BJ") + + do { + let city = try await docRef.getDocument(as: City.self) + print("City: \(city)") + } catch { + print("Error decoding city: \(error)") + } + // [END custom_type] + } + + private func listenDocument() { + // [START listen_document] + db.collection("cities").document("SF") + .addSnapshotListener { documentSnapshot, error in + guard let document = documentSnapshot else { + print("Error fetching document: \(error!)") + return } - // [END transaction] - } - - private func writeBatch() { - // [START write_batch] - // Get new write batch - let batch = db.batch() - - // Set the value of 'NYC' - let nycRef = db.collection("cities").document("NYC") - batch.setData([:], forDocument: nycRef) - - // Update the population of 'SF' - let sfRef = db.collection("cities").document("SF") - batch.updateData(["population": 1000000 ], forDocument: sfRef) - - // Delete the city 'LA' - let laRef = db.collection("cities").document("LA") - batch.deleteDocument(laRef) - - // Commit the batch - batch.commit() { err in - if let err = err { - print("Error writing batch \(err)") - } else { - print("Batch write succeeded.") - } + guard let data = document.data() else { + print("Document data was empty.") + return } - // [END write_batch] - } - - // ======================================================================================= - // ======= https://firebase.google.com/preview/firestore/client/retrieve-data ============ - // ======================================================================================= - - private func exampleData() { - // [START example_data] - let citiesRef = db.collection("cities") - - citiesRef.document("SF").setData([ - "name": "San Francisco", - "state": "CA", - "country": "USA", - "capital": false, - "population": 860000, - "regions": ["west_coast", "norcal"] - ]) - citiesRef.document("LA").setData([ - "name": "Los Angeles", - "state": "CA", - "country": "USA", - "capital": false, - "population": 3900000, - "regions": ["west_coast", "socal"] - ]) - citiesRef.document("DC").setData([ - "name": "Washington D.C.", - "country": "USA", - "capital": true, - "population": 680000, - "regions": ["east_coast"] - ]) - citiesRef.document("TOK").setData([ - "name": "Tokyo", - "country": "Japan", - "capital": true, - "population": 9000000, - "regions": ["kanto", "honshu"] - ]) - citiesRef.document("BJ").setData([ - "name": "Beijing", - "country": "China", - "capital": true, - "population": 21500000, - "regions": ["jingjinji", "hebei"] - ]) - // [END example_data] - } - - private func exampleDataCollectionGroup() { - // [START fs_collection_group_query_data_setup] - let citiesRef = db.collection("cities") - - var data = ["name": "Golden Gate Bridge", "type": "bridge"] - citiesRef.document("SF").collection("landmarks").addDocument(data: data) - - data = ["name": "Legion of Honor", "type": "museum"] - citiesRef.document("SF").collection("landmarks").addDocument(data: data) - - data = ["name": "Griffith Park", "type": "park"] - citiesRef.document("LA").collection("landmarks").addDocument(data: data) - - data = ["name": "The Getty", "type": "museum"] - citiesRef.document("LA").collection("landmarks").addDocument(data: data) - - data = ["name": "Lincoln Memorial", "type": "memorial"] - citiesRef.document("DC").collection("landmarks").addDocument(data: data) - - data = ["name": "National Air and Space Museum", "type": "museum"] - citiesRef.document("DC").collection("landmarks").addDocument(data: data) - - data = ["name": "Ueno Park", "type": "park"] - citiesRef.document("TOK").collection("landmarks").addDocument(data: data) - - data = ["name": "National Museum of Nature and Science", "type": "museum"] - citiesRef.document("TOK").collection("landmarks").addDocument(data: data) - - data = ["name": "Jingshan Park", "type": "park"] - citiesRef.document("BJ").collection("landmarks").addDocument(data: data) - - data = ["name": "Beijing Ancient Observatory", "type": "museum"] - citiesRef.document("BJ").collection("landmarks").addDocument(data: data) - // [END fs_collection_group_query_data_setup] - } - - private func getDocument() { - // [START get_document] - let docRef = db.collection("cities").document("SF") - - docRef.getDocument { (document, error) in - if let document = document, document.exists { - let dataDescription = document.data().map(String.init(describing:)) ?? "nil" - print("Document data: \(dataDescription)") - } else { - print("Document does not exist") - } + print("Current data: \(data)") + } + // [END listen_document] + } + + private func listenDocumentLocal() { + // [START listen_document_local] + db.collection("cities").document("SF") + .addSnapshotListener { documentSnapshot, error in + guard let document = documentSnapshot else { + print("Error fetching document: \(error!)") + return } - // [END get_document] - } - - private func getDocumentWithOptions() { - // [START get_document_options] - let docRef = db.collection("cities").document("SF") - - // Force the SDK to fetch the document from the cache. Could also specify - // FirestoreSource.server or FirestoreSource.default. - docRef.getDocument(source: .cache) { (document, error) in - if let document = document { - let dataDescription = document.data().map(String.init(describing:)) ?? "nil" - print("Cached document data: \(dataDescription)") - } else { - print("Document does not exist in cache") + let source = document.metadata.hasPendingWrites ? "Local" : "Server" + print("\(source) data: \(document.data() ?? [:])") + } + // [END listen_document_local] + } + + private func listenWithMetadata() { + // [START listen_with_metadata] + // Listen to document metadata. + db.collection("cities").document("SF") + .addSnapshotListener(includeMetadataChanges: true) { documentSnapshot, error in + // ... + } + // [END listen_with_metadata] + } + + private func getMultiple() async { + // [START get_multiple] + do { + let querySnapshot = try await db.collection("cities").whereField("capital", isEqualTo: true) + .getDocuments() + for document in querySnapshot.documents { + print("\(document.documentID) => \(document.data())") + } + } catch { + print("Error getting documents: \(error)") + } + // [END get_multiple] + } + + private func getMultipleAll() async { + // [START get_multiple_all] + do { + let querySnapshot = try await db.collection("cities").getDocuments() + for document in querySnapshot.documents { + print("\(document.documentID) => \(document.data())") + } + } catch { + print("Error getting documents: \(error)") + } + // [END get_multiple_all] + } + + private func getMultipleAllSubcollection() async { + // [START get_multiple_all_subcollection] + do { + let querySnapshot = try await db.collection("cities/SF/landmarks").getDocuments() + for document in querySnapshot.documents { + print("\(document.documentID) => \(document.data())") + } + } catch { + print("Error getting documents: \(error)") + } + // [END get_multiple_all_subcollection] + } + + private func listenMultiple() { + // [START listen_multiple] + db.collection("cities").whereField("state", isEqualTo: "CA") + .addSnapshotListener { querySnapshot, error in + guard let documents = querySnapshot?.documents else { + print("Error fetching documents: \(error!)") + return } + let cities = documents.compactMap { $0["name"] } + print("Current cities in CA: \(cities)") } - // [END get_document_options] - } - - private func customClassGetDocument() { - // [START custom_type] - let docRef = db.collection("cities").document("BJ") - - docRef.getDocument { (document, error) in - if let city = document.flatMap({ - $0.data().flatMap({ (data) in - return City(dictionary: data) - }) - }) { - print("City: \(city)") - } else { - print("Document does not exist") - } + // [END listen_multiple] + } + + private func listenDiffs() { + // [START listen_diffs] + db.collection("cities").whereField("state", isEqualTo: "CA") + .addSnapshotListener { querySnapshot, error in + guard let snapshot = querySnapshot else { + print("Error fetching snapshots: \(error!)") + return } - // [END custom_type] - } - - private func listenDocument() { - // [START listen_document] - db.collection("cities").document("SF") - .addSnapshotListener { documentSnapshot, error in - guard let document = documentSnapshot else { - print("Error fetching document: \(error!)") - return - } - guard let data = document.data() else { - print("Document data was empty.") - return - } - print("Current data: \(data)") - } - // [END listen_document] - } - - private func listenDocumentLocal() { - // [START listen_document_local] - db.collection("cities").document("SF") - .addSnapshotListener { documentSnapshot, error in - guard let document = documentSnapshot else { - print("Error fetching document: \(error!)") - return - } - let source = document.metadata.hasPendingWrites ? "Local" : "Server" - print("\(source) data: \(document.data() ?? [:])") - } - // [END listen_document_local] - } - - private func listenWithMetadata() { - // [START listen_with_metadata] - // Listen to document metadata. - db.collection("cities").document("SF") - .addSnapshotListener(includeMetadataChanges: true) { documentSnapshot, error in - // ... - } - // [END listen_with_metadata] - } - - private func getMultiple() { - // [START get_multiple] - db.collection("cities").whereField("capital", isEqualTo: true) - .getDocuments() { (querySnapshot, err) in - if let err = err { - print("Error getting documents: \(err)") - } else { - for document in querySnapshot!.documents { - print("\(document.documentID) => \(document.data())") - } - } + snapshot.documentChanges.forEach { diff in + if (diff.type == .added) { + print("New city: \(diff.document.data())") + } + if (diff.type == .modified) { + print("Modified city: \(diff.document.data())") + } + if (diff.type == .removed) { + print("Removed city: \(diff.document.data())") + } } - // [END get_multiple] - } - - private func getMultipleAll() { - // [START get_multiple_all] - db.collection("cities").getDocuments() { (querySnapshot, err) in - if let err = err { - print("Error getting documents: \(err)") - } else { - for document in querySnapshot!.documents { - print("\(document.documentID) => \(document.data())") - } - } + } + // [END listen_diffs] + } + + private func listenState() { + // [START listen_state] + db.collection("cities").whereField("state", isEqualTo: "CA") + .addSnapshotListener { querySnapshot, error in + guard let snapshot = querySnapshot else { + print("Error fetching documents: \(error!)") + return } - // [END get_multiple_all] - } - - private func listenMultiple() { - // [START listen_multiple] - db.collection("cities").whereField("state", isEqualTo: "CA") - .addSnapshotListener { querySnapshot, error in - guard let documents = querySnapshot?.documents else { - print("Error fetching documents: \(error!)") - return - } - let cities = documents.map { $0["name"]! } - print("Current cities in CA: \(cities)") - } - // [END listen_multiple] - } - - private func listenDiffs() { - // [START listen_diffs] - db.collection("cities").whereField("state", isEqualTo: "CA") - .addSnapshotListener { querySnapshot, error in - guard let snapshot = querySnapshot else { - print("Error fetching snapshots: \(error!)") - return - } - snapshot.documentChanges.forEach { diff in - if (diff.type == .added) { - print("New city: \(diff.document.data())") - } - if (diff.type == .modified) { - print("Modified city: \(diff.document.data())") - } - if (diff.type == .removed) { - print("Removed city: \(diff.document.data())") - } - } - } - // [END listen_diffs] - } - - private func listenState() { - // [START listen_state] - db.collection("cities").whereField("state", isEqualTo: "CA") - .addSnapshotListener { querySnapshot, error in - guard let snapshot = querySnapshot else { - print("Error fetching documents: \(error!)") - return - } - snapshot.documentChanges.forEach { diff in - if (diff.type == .added) { - print("New city: \(diff.document.data())") - } - } - - if !snapshot.metadata.isFromCache { - print("Synced with server state.") - } - } - // [END listen_state] - } - - private func detachListener() { - // [START detach_listener] - let listener = db.collection("cities").addSnapshotListener { querySnapshot, error in - // [START_EXCLUDE] - // [END_EXCLUDE] + snapshot.documentChanges.forEach { diff in + if (diff.type == .added) { + print("New city: \(diff.document.data())") + } } - // ... - - // Stop listening to changes - listener.remove() - // [END detach_listener] - } - - private func handleListenErrors() { - // [START handle_listen_errors] - db.collection("cities") - .addSnapshotListener { querySnapshot, error in - if let error = error { - print("Error retreiving collection: \(error)") - } - } - // [END handle_listen_errors] - } - - // ======================================================================================= - // ======== https://firebase.google.com/preview/firestore/client/query-data ============== - // ======================================================================================= - - private func simpleQueries() { - // [START simple_queries] - // Create a reference to the cities collection - let citiesRef = db.collection("cities") - - // Create a query against the collection. - let query = citiesRef.whereField("state", isEqualTo: "CA") - // [END simple_queries] - - print(query) - } - - private func exampleFilters() { - let citiesRef = db.collection("cities") - - // [START example_filters] - citiesRef.whereField("state", isEqualTo: "CA") - citiesRef.whereField("population", isLessThan: 100000) - citiesRef.whereField("name", isGreaterThanOrEqualTo: "San Francisco") - // [END example_filters] - } - - private func onlyCapitals() { - // [START only_capitals] - let capitalCities = db.collection("cities").whereField("capital", isEqualTo: true) - // [END only_capitals] - print(capitalCities) - } - - private func arrayContainsFilter() { - let citiesRef = db.collection("cities") - - // [START array_contains_filter] - citiesRef - .whereField("regions", arrayContains: "west_coast") - // [END array_contains_filter] - } - - private func chainFilters() { - let citiesRef = db.collection("cities") - - // [START chain_filters] - citiesRef - .whereField("state", isEqualTo: "CO") - .whereField("name", isEqualTo: "Denver") - citiesRef - .whereField("state", isEqualTo: "CA") - .whereField("population", isLessThan: 1000000) - // [END chain_filters] - } - - private func validRangeFilters() { - let citiesRef = db.collection("cities") - - // [START valid_range_filters] - citiesRef - .whereField("state", isGreaterThanOrEqualTo: "CA") - .whereField("state", isLessThanOrEqualTo: "IN") - citiesRef - .whereField("state", isEqualTo: "CA") - .whereField("population", isGreaterThan: 1000000) - // [END valid_range_filters] - } - - private func invalidRangeFilters() throws { - let citiesRef = db.collection("cities") - - // [START invalid_range_filters] - citiesRef - .whereField("state", isGreaterThanOrEqualTo: "CA") - .whereField("population", isGreaterThan: 1000000) - // [END invalid_range_filters] - } - - private func orderAndLimit() { - let citiesRef = db.collection("cities") - - // [START order_and_limit] - citiesRef.order(by: "name").limit(to: 3) - // [END order_and_limit] - } - - private func orderAndLimitDesc() { - let citiesRef = db.collection("cities") - - // [START order_and_limit_desc] - citiesRef.order(by: "name", descending: true).limit(to: 3) - // [END order_and_limit_desc] - } - - private func orderMultiple() { - let citiesRef = db.collection("cities") - - // [START order_multiple] - citiesRef - .order(by: "state") - .order(by: "population", descending: true) - // [END order_multiple] - } - - private func filterAndOrder() { - let citiesRef = db.collection("cities") - - // [START filter_and_order] - citiesRef - .whereField("population", isGreaterThan: 100000) - .order(by: "population") - .limit(to: 2) - // [END filter_and_order] - } - - private func validFilterAndOrder() { - let citiesRef = db.collection("cities") - - // [START valid_filter_and_order] - citiesRef - .whereField("population", isGreaterThan: 100000) - .order(by: "population") - // [END valid_filter_and_order] - } - - private func invalidFilterAndOrder() throws { - let citiesRef = db.collection("cities") + if !snapshot.metadata.isFromCache { + print("Synced with server state.") + } + } + // [END listen_state] + } - // [START invalid_filter_and_order] - citiesRef - .whereField("population", isGreaterThan: 100000) - .order(by: "country") - // [END invalid_filter_and_order] + private func detachListener() { + // [START detach_listener] + let listener = db.collection("cities").addSnapshotListener { querySnapshot, error in + // [START_EXCLUDE] + // [END_EXCLUDE] } + // ... - // ======================================================================================= - // ====== https://firebase.google.com/preview/firestore/client/enable-offline ============ - // ======================================================================================= - - private func enableOffline() { - // [START enable_offline] - let settings = FirestoreSettings() - settings.isPersistenceEnabled = true - - // Any additional options - // ... + // Stop listening to changes + listener.remove() + // [END detach_listener] + } - // Enable offline data persistence - let db = Firestore.firestore() - db.settings = settings - // [END enable_offline] - } - - private func listenToOffline() { - let db = Firestore.firestore() - // [START listen_to_offline] - // Listen to metadata updates to receive a server snapshot even if - // the data is the same as the cached data. - db.collection("cities").whereField("state", isEqualTo: "CA") - .addSnapshotListener(includeMetadataChanges: true) { querySnapshot, error in - guard let snapshot = querySnapshot else { - print("Error retreiving snapshot: \(error!)") - return - } - - for diff in snapshot.documentChanges { - if diff.type == .added { - print("New city: \(diff.document.data())") - } - } - - let source = snapshot.metadata.isFromCache ? "local cache" : "server" - print("Metadata: Data fetched from \(source)") + private func handleListenErrors() { + // [START handle_listen_errors] + db.collection("cities") + .addSnapshotListener { querySnapshot, error in + if let error = error { + print("Error retreiving collection: \(error)") } - // [END listen_to_offline] - } - - private func toggleOffline() { - // [START disable_network] - Firestore.firestore().disableNetwork { (error) in - // Do offline things - // ... + } + // [END handle_listen_errors] + } + + // ======================================================================================= + // ======== https://firebase.google.com/preview/firestore/client/query-data ============== + // ======================================================================================= + + private func simpleQueries() { + // [START simple_queries] + // Create a reference to the cities collection + let citiesRef = db.collection("cities") + + // Create a query against the collection. + let query = citiesRef.whereField("state", isEqualTo: "CA") + // [END simple_queries] + + // [START simple_query_not_equal] + let notEqualQuery = citiesRef.whereField("capital", isNotEqualTo: false) + // [END simple_query_not_equal] + + print(query) + } + + private func exampleFilters() { + let citiesRef = db.collection("cities") + + // [START example_filters] + let stateQuery = citiesRef.whereField("state", isEqualTo: "CA") + let populationQuery = citiesRef.whereField("population", isLessThan: 100000) + let nameQuery = citiesRef.whereField("name", isGreaterThanOrEqualTo: "San Francisco") + // [END example_filters] + } + + private func onlyCapitals() { + // [START only_capitals] + let capitalCities = db.collection("cities").whereField("capital", isEqualTo: true) + // [END only_capitals] + print(capitalCities) + } + + private func arrayContainsFilter() { + let citiesRef = db.collection("cities") + + // [START array_contains_filter] + citiesRef + .whereField("regions", arrayContains: "west_coast") + // [END array_contains_filter] + } + + private func chainFilters() { + let citiesRef = db.collection("cities") + + // [START chain_filters] + citiesRef + .whereField("state", isEqualTo: "CO") + .whereField("name", isEqualTo: "Denver") + citiesRef + .whereField("state", isEqualTo: "CA") + .whereField("population", isLessThan: 1000000) + // [END chain_filters] + } + + private func validRangeFilters() { + let citiesRef = db.collection("cities") + + // [START valid_range_filters] + citiesRef + .whereField("state", isGreaterThanOrEqualTo: "CA") + .whereField("state", isLessThanOrEqualTo: "IN") + citiesRef + .whereField("state", isEqualTo: "CA") + .whereField("population", isGreaterThan: 1000000) + // [END valid_range_filters] + } + + private func invalidRangeFilters() throws { + let citiesRef = db.collection("cities") + + // [START invalid_range_filters] + citiesRef + .whereField("state", isGreaterThanOrEqualTo: "CA") + .whereField("population", isGreaterThan: 1000000) + // [END invalid_range_filters] + } + + private func orderAndLimit() { + let citiesRef = db.collection("cities") + + // [START order_and_limit] + citiesRef.order(by: "name").limit(to: 3) + // [END order_and_limit] + } + + private func orderAndLimitDesc() { + let citiesRef = db.collection("cities") + + // [START order_and_limit_desc] + citiesRef.order(by: "name", descending: true).limit(to: 3) + // [END order_and_limit_desc] + } + + private func orderMultiple() { + let citiesRef = db.collection("cities") + + // [START order_multiple] + citiesRef + .order(by: "state") + .order(by: "population", descending: true) + // [END order_multiple] + } + + private func filterAndOrder() { + let citiesRef = db.collection("cities") + + // [START filter_and_order] + citiesRef + .whereField("population", isGreaterThan: 100000) + .order(by: "population") + .limit(to: 2) + // [END filter_and_order] + } + + private func validFilterAndOrder() { + let citiesRef = db.collection("cities") + + // [START valid_filter_and_order] + citiesRef + .whereField("population", isGreaterThan: 100000) + .order(by: "population") + // [END valid_filter_and_order] + } + + private func invalidFilterAndOrder() throws { + let citiesRef = db.collection("cities") + + // [START invalid_filter_and_order] + citiesRef + .whereField("population", isGreaterThan: 100000) + .order(by: "country") + // [END invalid_filter_and_order] + } + + private func arrayContainsAnyQueries() { + // [START array_contains_any_filter] + let citiesRef = db.collection("cities") + citiesRef.whereField("regions", arrayContainsAny: ["west_coast", "east_coast"]) + // [END array_contains_any_filter] + } + + private func inQueries() { + // [START in_filter] + let citiesRef = db.collection("cities") + + citiesRef.whereField("country", in: ["USA", "Japan"]) + // [END in_filter] + + // [START in_filter_with_array] + citiesRef.whereField("regions", in: [["west_coast"], ["east_coast"]]) + // [END in_filter_with_array] + + // [START not_in_filter] + citiesRef.whereField("country", notIn: ["USA", "Japan"]) + // [END not_in_filter] + } + + // ======================================================================================= + // ====== https://firebase.google.com/preview/firestore/client/enable-offline ============ + // ======================================================================================= + + private func enableOffline() { + // [START enable_offline] + let settings = FirestoreSettings() + + // Use memory-only cache + settings.cacheSettings = + MemoryCacheSettings(garbageCollectorSettings: MemoryLRUGCSettings()) + + // Use persistent disk cache, with 100 MB cache size + settings.cacheSettings = PersistentCacheSettings(sizeBytes: 100 * 1024 * 1024 as NSNumber) + + // Any additional options + // ... + + // Enable offline data persistence + let db = Firestore.firestore() + db.settings = settings + // [END enable_offline] + } + + private func listenToOffline() { + let db = Firestore.firestore() + // [START listen_to_offline] + // Listen to metadata updates to receive a server snapshot even if + // the data is the same as the cached data. + db.collection("cities").whereField("state", isEqualTo: "CA") + .addSnapshotListener(includeMetadataChanges: true) { querySnapshot, error in + guard let snapshot = querySnapshot else { + print("Error retreiving snapshot: \(error!)") + return } - // [END disable_network] - // [START enable_network] - Firestore.firestore().enableNetwork { (error) in - // Do online things - // ... + for diff in snapshot.documentChanges { + if diff.type == .added { + print("New city: \(diff.document.data())") + } } - // [END enable_network] - } - // ======================================================================================= - // ====== https://firebase.google.com/preview/firestore/client/cursors =================== - // ======================================================================================= - - private func simpleCursor() { - let db = Firestore.firestore() - - // [START cursor_greater_than] - // Get all cities with population over one million, ordered by population. - db.collection("cities") - .order(by: "population") - .start(at: [1000000]) - // [END cursor_greater_than] - - // [START cursor_less_than] - // Get all cities with population less than one million, ordered by population. - db.collection("cities") - .order(by: "population") - .end(at: [1000000]) - // [END cursor_less_than] - } - - private func snapshotCursor() { - let db = Firestore.firestore() - - // [START snapshot_cursor] - db.collection("cities") - .document("SF") - .addSnapshotListener { (document, error) in - guard let document = document else { - print("Error retreving cities: \(error.debugDescription)") - return - } - - // Get all cities with a population greater than or equal to San Francisco. - let sfSizeOrBigger = db.collection("cities") - .order(by: "population") - .start(atDocument: document) + let source = snapshot.metadata.isFromCache ? "local cache" : "server" + print("Metadata: Data fetched from \(source)") + } + // [END listen_to_offline] + } + + private func toggleOffline() { + // [START disable_network] + Firestore.firestore().disableNetwork { (error) in + // Do offline things + // ... + } + // [END disable_network] + + // [START enable_network] + Firestore.firestore().enableNetwork { (error) in + // Do online things + // ... + } + // [END enable_network] + } + + // ======================================================================================= + // ====== https://firebase.google.com/preview/firestore/client/cursors =================== + // ======================================================================================= + + private func simpleCursor() { + let db = Firestore.firestore() + + // [START cursor_greater_than] + // Get all cities with population over one million, ordered by population. + db.collection("cities") + .order(by: "population") + .start(at: [1000000]) + // [END cursor_greater_than] + + // [START cursor_less_than] + // Get all cities with population less than one million, ordered by population. + db.collection("cities") + .order(by: "population") + .end(at: [1000000]) + // [END cursor_less_than] + } + + private func snapshotCursor() { + let db = Firestore.firestore() + + // [START snapshot_cursor] + db.collection("cities") + .document("SF") + .addSnapshotListener { (document, error) in + guard let document = document else { + print("Error retreving cities: \(error.debugDescription)") + return } - // [END snapshot_cursor] - } - private func paginate() { - let db = Firestore.firestore() - - // [START paginate] - // Construct query for first 25 cities, ordered by population - let first = db.collection("cities") - .order(by: "population") - .limit(to: 25) - - first.addSnapshotListener { (snapshot, error) in - guard let snapshot = snapshot else { - print("Error retreving cities: \(error.debugDescription)") - return - } - - guard let lastSnapshot = snapshot.documents.last else { - // The collection is empty. - return - } - - // Construct a new query starting after this document, - // retrieving the next 25 cities. - let next = db.collection("cities") - .order(by: "population") - .start(afterDocument: lastSnapshot) - - // Use the query for pagination. - // ... - } - // [END paginate] - } + // Get all cities with a population greater than or equal to San Francisco. + let sfSizeOrBigger = db.collection("cities") + .order(by: "population") + .start(atDocument: document) + } + // [END snapshot_cursor] + } + + private func paginate() { + let db = Firestore.firestore() + + // [START paginate] + // Construct query for first 25 cities, ordered by population + let first = db.collection("cities") + .order(by: "population") + .limit(to: 25) + + first.addSnapshotListener { (snapshot, error) in + guard let snapshot = snapshot else { + print("Error retreving cities: \(error.debugDescription)") + return + } - private func multiCursor() { - let db = Firestore.firestore() - - // [START multi_cursor] - // Will return all Springfields - db.collection("cities") - .order(by: "name") - .order(by: "state") - .start(at: ["Springfield"]) - - // Will return "Springfield, Missouri" and "Springfield, Wisconsin" - db.collection("cities") - .order(by: "name") - .order(by: "state") - .start(at: ["Springfield", "Missouri"]) - // [END multi_cursor] - } + guard let lastSnapshot = snapshot.documents.last else { + // The collection is empty. + return + } - private func collectionGroupQuery() { - // [START fs_collection_group_query] - db.collectionGroup("landmarks").whereField("type", isEqualTo: "museum").getDocuments { (snapshot, error) in - // [START_EXCLUDE] - print(snapshot?.documents.count ?? 0) - // [END_EXCLUDE] - } - // [END fs_collection_group_query] - } + // Construct a new query starting after this document, + // retrieving the next 25 cities. + let next = db.collection("cities") + .order(by: "population") + .start(afterDocument: lastSnapshot) + + // Use the query for pagination. + // ... + } + // [END paginate] + } + + private func multiCursor() { + let db = Firestore.firestore() + + // [START multi_cursor] + // Will return all Springfields + db.collection("cities") + .order(by: "name") + .order(by: "state") + .start(at: ["Springfield"]) + + // Will return "Springfield, Missouri" and "Springfield, Wisconsin" + db.collection("cities") + .order(by: "name") + .order(by: "state") + .start(at: ["Springfield", "Missouri"]) + // [END multi_cursor] + } + + private func collectionGroupQuery() { + // [START fs_collection_group_query] + db.collectionGroup("landmarks").whereField("type", isEqualTo: "museum").getDocuments { (snapshot, error) in + // [START_EXCLUDE] + print(snapshot?.documents.count ?? 0) + // [END_EXCLUDE] + } + // [END fs_collection_group_query] + } + + private func emulatorSettings() { + // [START fs_emulator_connect] + let settings = Firestore.firestore().settings + settings.host = "127.0.0.1:8080" + settings.cacheSettings = MemoryCacheSettings() + settings.isSSLEnabled = false + Firestore.firestore().settings = settings + // [END fs_emulator_connect] + } + + // MARK: Aggregation queries + private func countAggregateCollection() async { + // [START count_aggregate_collection] + let query = db.collection("cities") + let countQuery = query.count + do { + let snapshot = try await countQuery.getAggregation(source: .server) + print(snapshot.count) + } catch { + print(error) + } + // [END count_aggregate_collection] + } + + private func countAggregateQuery() async { + // [START count_aggregate_query] + let query = db.collection("cities").whereField("state", isEqualTo: "CA") + let countQuery = query.count + do { + let snapshot = try await countQuery.getAggregation(source: .server) + print(snapshot.count) + } catch { + print(error) + } + // [END count_aggregate_query] + } + + private func sumAggregateCollection() async { + // [START sum_aggregate_collection] + let query = db.collection("cities") + let aggregateQuery = query.aggregate([AggregateField.sum("population")]) + do { + let snapshot = try await aggregateQuery.getAggregation(source: .server) + print(snapshot.get(AggregateField.sum("population"))) + } catch { + print(error) + } + // [END sum_aggregate_collection] + } + + private func sumAggregateQuery() async { + // [START sum_aggregate_query] + let query = db.collection("cities").whereField("capital", isEqualTo: true) + let aggregateQuery = query.aggregate([AggregateField.sum("population")]) + do { + let snapshot = try await aggregateQuery.getAggregation(source: .server) + print(snapshot.get(AggregateField.sum("population"))) + } catch { + print(error) + } + // [END sum_aggregate_query] + } + + private func averageAggregateCollection() async { + // [START average_aggregate_collection] + let query = db.collection("cities") + let aggregateQuery = query.aggregate([AggregateField.average("population")]) + do { + let snapshot = try await aggregateQuery.getAggregation(source: .server) + print(snapshot.get(AggregateField.average("population"))) + } catch { + print(error) + } + // [END average_aggregate_collection] + } + + private func averageAggregateQuery() async { + // [START average_aggregate_query] + let query = db.collection("cities").whereField("capital", isEqualTo: true) + let aggregateQuery = query.aggregate([AggregateField.average("population")]) + do { + let snapshot = try await aggregateQuery.getAggregation(source: .server) + print(snapshot.get(AggregateField.average("population"))) + } catch { + print(error) + } + // [END average_aggregate_query] + } + + private func multiAggregateCollection() async { + // [START multi_aggregate_collection] + let query = db.collection("cities") + let aggregateQuery = query.aggregate([ + AggregateField.count(), + AggregateField.sum("population"), + AggregateField.average("population")]) + do { + let snapshot = try await aggregateQuery.getAggregation(source: .server) + print("Count: \(snapshot.get(AggregateField.count()))") + print("Sum: \(snapshot.get(AggregateField.sum("population")))") + print("Average: \(snapshot.get(AggregateField.average("population")))") + } catch { + print(error) + } + // [END multi_aggregate_collection] + } + + private func orQuery() { + // [START or_query] + let query = db.collection("cities").whereFilter(Filter.andFilter([ + Filter.whereField("state", isEqualTo: "CA"), + Filter.orFilter([ + Filter.whereField("capital", isEqualTo: true), + Filter.whereField("population", isGreaterOrEqualTo: 1000000) + ]) + ])) + // [END or_query] + } + + private func orQueryDisjunctions() { + let collection = db.collection("cities") + + // [START one_disjunction] + collection.whereField("a", isEqualTo: 1) + // [END one_disjunction] + + // [START two_disjunctions] + collection.whereFilter(Filter.orFilter([ + Filter.whereField("a", isEqualTo: 1), + Filter.whereField("b", isEqualTo: 2) + ])) + // [END two_disjunctions] + + // [START four_disjunctions] + collection.whereFilter(Filter.orFilter([ + Filter.andFilter([ + Filter.whereField("a", isEqualTo: 1), + Filter.whereField("c", isEqualTo: 3) + ]), + Filter.andFilter([ + Filter.whereField("a", isEqualTo: 1), + Filter.whereField("d", isEqualTo: 4) + ]), + Filter.andFilter([ + Filter.whereField("b", isEqualTo: 2), + Filter.whereField("c", isEqualTo: 3) + ]), + Filter.andFilter([ + Filter.whereField("b", isEqualTo: 2), + Filter.whereField("d", isEqualTo: 4) + ]) + ])) + // [END four_disjunctions] + + // [START four_disjunctions_compact] + collection.whereFilter(Filter.andFilter([ + Filter.orFilter([ + Filter.whereField("a", isEqualTo: 1), + Filter.whereField("b", isEqualTo: 2) + ]), + Filter.orFilter([ + Filter.whereField("c", isEqualTo: 3), + Filter.whereField("d", isEqualTo: 4) + ]), + ])) + // [END four_disjunctions_compact] + + // [START 20_disjunctions] + collection.whereFilter(Filter.orFilter([ + Filter.whereField("a", in: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + Filter.whereField("b", in: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + ])) + // [END 20_disjunctions] + + // [START 10_disjunctions] + collection.whereFilter(Filter.andFilter([ + Filter.whereField("a", in: [1, 2, 3, 4, 5]), + Filter.orFilter([ + Filter.whereField("b", isEqualTo: 2), + Filter.whereField("c", isEqualTo: 3) + ]) + ])) + // [END 10_disjunctions] + } + + // This method crashes, so don't include it in the smoketest. + func illegalDisjunctions() { + let collection = db.collection("cities") + + // [START 50_disjunctions] + collection.whereFilter(Filter.andFilter([ + Filter.whereField("a", in: [1, 2, 3, 4, 5]), + Filter.whereField("b", in: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + ])) + // [END 50_disjunctions] + } - private func emulatorSettings() { - // [START fs_emulator_connect] - let settings = Firestore.firestore().settings - settings.host = "localhost:8080" - settings.isPersistenceEnabled = false - settings.isSSLEnabled = false - Firestore.firestore().settings = settings - // [END fs_emulator_connect] - } } -fileprivate struct City { +// [START codable_struct] +public struct City: Codable, Sendable { - let name: String - let state: String? - let country: String? - let capital: Bool? - let population: Int64? + let name: String + let state: String? + let country: String? + let isCapital: Bool? + let population: Int64? - init?(dictionary: [String: Any]) { - guard let name = dictionary["name"] as? String else { return nil } - self.name = name - - self.state = dictionary["state"] as? String - self.country = dictionary["country"] as? String - self.capital = dictionary["capital"] as? Bool - self.population = dictionary["population"] as? Int64 - } + enum CodingKeys: String, CodingKey { + case name + case state + case country + case isCapital = "capital" + case population + } } - +// [END codable_struct] diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration.xcodeproj/project.pbxproj b/firoptions/FiroptionConfiguration.xcodeproj/project.pbxproj similarity index 68% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration.xcodeproj/project.pbxproj rename to firoptions/FiroptionConfiguration.xcodeproj/project.pbxproj index 2500f165..6c7323c0 100644 --- a/firoptions/FiroptionConfiguration/FiroptionConfiguration.xcodeproj/project.pbxproj +++ b/firoptions/FiroptionConfiguration.xcodeproj/project.pbxproj @@ -3,10 +3,19 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ + 8D7951AC2D28AF88000FD694 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951AB2D28AF88000FD694 /* FirebaseAnalytics */; }; + 8D7951AE2D28AF88000FD694 /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951AD2D28AF88000FD694 /* FirebaseCore */; }; + 8D7951B02D28AF88000FD694 /* FirebaseDatabase in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951AF2D28AF88000FD694 /* FirebaseDatabase */; }; + 8D7951B22D28AFF4000FD694 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951B12D28AFF4000FD694 /* FirebaseAnalytics */; }; + 8D7951B42D28AFF4000FD694 /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951B32D28AFF4000FD694 /* FirebaseCore */; }; + 8D7951B62D28B003000FD694 /* FirebaseDatabase in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951B52D28B003000FD694 /* FirebaseDatabase */; }; + 8DFC20162410844B004392AD /* AnalyticsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DFC20152410844B004392AD /* AnalyticsHelper.m */; }; + A19F764427EDED14002DE108 /* ISImpressionData.m in Sources */ = {isa = PBXBuildFile; fileRef = A19F764027EDED14002DE108 /* ISImpressionData.m */; }; + A19F764527EDED14002DE108 /* MAAd.m in Sources */ = {isa = PBXBuildFile; fileRef = A19F764327EDED14002DE108 /* MAAd.m */; }; EFEC9D631E01BF310021BDF9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFEC9D621E01BF310021BDF9 /* AppDelegate.swift */; }; EFEC9D651E01BF310021BDF9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFEC9D641E01BF310021BDF9 /* ViewController.swift */; }; EFEC9D681E01BF310021BDF9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EFEC9D661E01BF310021BDF9 /* Main.storyboard */; }; @@ -23,6 +32,13 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 8DFC20132410844A004392AD /* FiroptionConfiguration-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "FiroptionConfiguration-Bridging-Header.h"; sourceTree = ""; }; + 8DFC20142410844B004392AD /* AnalyticsHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnalyticsHelper.h; sourceTree = ""; }; + 8DFC20152410844B004392AD /* AnalyticsHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AnalyticsHelper.m; sourceTree = ""; }; + A19F764027EDED14002DE108 /* ISImpressionData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ISImpressionData.m; sourceTree = ""; }; + A19F764127EDED14002DE108 /* ISImpressionData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ISImpressionData.h; sourceTree = ""; }; + A19F764227EDED14002DE108 /* MAAd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MAAd.h; sourceTree = ""; }; + A19F764327EDED14002DE108 /* MAAd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MAAd.m; sourceTree = ""; }; EFEC9D5F1E01BF310021BDF9 /* FiroptionConfiguration.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FiroptionConfiguration.app; sourceTree = BUILT_PRODUCTS_DIR; }; EFEC9D621E01BF310021BDF9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; EFEC9D641E01BF310021BDF9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -41,6 +57,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D7951B02D28AF88000FD694 /* FirebaseDatabase in Frameworks */, + 8D7951AE2D28AF88000FD694 /* FirebaseCore in Frameworks */, + 8D7951AC2D28AF88000FD694 /* FirebaseAnalytics in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -48,17 +67,28 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D7951B62D28B003000FD694 /* FirebaseDatabase in Frameworks */, + 8D7951B42D28AFF4000FD694 /* FirebaseCore in Frameworks */, + 8D7951B22D28AFF4000FD694 /* FirebaseAnalytics in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 8D7951AA2D28AF88000FD694 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; EFEC9D561E01BF310021BDF9 = { isa = PBXGroup; children = ( EFEC9D9B1E01EEB60021BDF9 /* Dev */, EFEC9D611E01BF310021BDF9 /* FiroptionConfiguration */, + 8D7951AA2D28AF88000FD694 /* Frameworks */, EFEC9D601E01BF310021BDF9 /* Products */, ); sourceTree = ""; @@ -79,10 +109,17 @@ EFEC9D951E01EE9F0021BDF9 /* MyGoogleService.plist */, EFEC9D621E01BF310021BDF9 /* AppDelegate.swift */, EFEC9D641E01BF310021BDF9 /* ViewController.swift */, + 8DFC20142410844B004392AD /* AnalyticsHelper.h */, + 8DFC20152410844B004392AD /* AnalyticsHelper.m */, + A19F764127EDED14002DE108 /* ISImpressionData.h */, + A19F764027EDED14002DE108 /* ISImpressionData.m */, + A19F764227EDED14002DE108 /* MAAd.h */, + A19F764327EDED14002DE108 /* MAAd.m */, EFEC9D661E01BF310021BDF9 /* Main.storyboard */, EFEC9D691E01BF310021BDF9 /* Assets.xcassets */, EFEC9D6B1E01BF310021BDF9 /* LaunchScreen.storyboard */, EFEC9D6E1E01BF310021BDF9 /* Info.plist */, + 8DFC20132410844A004392AD /* FiroptionConfiguration-Bridging-Header.h */, ); path = FiroptionConfiguration; sourceTree = ""; @@ -138,13 +175,15 @@ EFEC9D571E01BF310021BDF9 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0820; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 1610; ORGANIZATIONNAME = Google; TargetAttributes = { EFEC9D5E1E01BF310021BDF9 = { CreatedOnToolsVersion = 8.2; DevelopmentTeam = EQHXZ8M8AV; + LastSwiftMigration = 1130; ProvisioningStyle = Automatic; }; EFEC9D771E01C6560021BDF9 = { @@ -156,13 +195,16 @@ }; buildConfigurationList = EFEC9D5A1E01BF310021BDF9 /* Build configuration list for PBXProject "FiroptionConfiguration" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = EFEC9D561E01BF310021BDF9; + packageReferences = ( + 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = EFEC9D601E01BF310021BDF9 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -204,8 +246,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A19F764527EDED14002DE108 /* MAAd.m in Sources */, + A19F764427EDED14002DE108 /* ISImpressionData.m in Sources */, EFEC9D651E01BF310021BDF9 /* ViewController.swift in Sources */, EFEC9D631E01BF310021BDF9 /* AppDelegate.swift in Sources */, + 8DFC20162410844B004392AD /* AnalyticsHelper.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -249,15 +294,24 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -266,6 +320,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -280,7 +335,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -299,15 +354,24 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -316,6 +380,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -324,10 +389,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -337,12 +403,18 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = EQHXZ8M8AV; INFOPLIST_FILE = FiroptionConfiguration/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.devrel.FiroptionConfiguration; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_OBJC_BRIDGING_HEADER = "FiroptionConfiguration/FiroptionConfiguration-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -350,12 +422,17 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; DEVELOPMENT_TEAM = EQHXZ8M8AV; INFOPLIST_FILE = FiroptionConfiguration/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.devrel.FiroptionConfiguration; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_OBJC_BRIDGING_HEADER = "FiroptionConfiguration/FiroptionConfiguration-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -365,10 +442,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = EQHXZ8M8AV; INFOPLIST_FILE = "$(SRCROOT)/FiroptionConfiguration/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.devrel.FiroptionConfigurationDev; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -378,10 +458,13 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; DEVELOPMENT_TEAM = EQHXZ8M8AV; INFOPLIST_FILE = "$(SRCROOT)/FiroptionConfiguration/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.devrel.FiroptionConfigurationDev; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -416,6 +499,50 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7951AB2D28AF88000FD694 /* FirebaseAnalytics */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAnalytics; + }; + 8D7951AD2D28AF88000FD694 /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + 8D7951AF2D28AF88000FD694 /* FirebaseDatabase */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseDatabase; + }; + 8D7951B12D28AFF4000FD694 /* FirebaseAnalytics */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAnalytics; + }; + 8D7951B32D28AFF4000FD694 /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + 8D7951B52D28B003000FD694 /* FirebaseDatabase */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951A92D28AF6D000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseDatabase; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = EFEC9D571E01BF310021BDF9 /* Project object */; } diff --git a/firoptions/FiroptionConfiguration/AnalyticsHelper.h b/firoptions/FiroptionConfiguration/AnalyticsHelper.h new file mode 100644 index 00000000..dd2ec27e --- /dev/null +++ b/firoptions/FiroptionConfiguration/AnalyticsHelper.h @@ -0,0 +1,23 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +NS_ASSUME_NONNULL_BEGIN + +@interface AnalyticsHelper: NSObject + +@end +NS_ASSUME_NONNULL_END diff --git a/firoptions/FiroptionConfiguration/AnalyticsHelper.m b/firoptions/FiroptionConfiguration/AnalyticsHelper.m new file mode 100644 index 00000000..4e60417c --- /dev/null +++ b/firoptions/FiroptionConfiguration/AnalyticsHelper.m @@ -0,0 +1,326 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseAnalytics; + +#import "AnalyticsHelper.h" + +// Importing simulated 3rd party ad_impressions to ensure this project compiles. Not required to implement Firebase ad_impression tracking +#import "MAAd.h" +#import "ISImpressionData.h" + +@implementation AnalyticsHelper + +- (void)logInAppPurchase { + // [START ecommerce_purchase] + [FIRAnalytics logEventWithName:kFIREventPurchase + parameters:@{ + kFIRParameterCoupon: @"SummerPromo", + kFIRParameterCurrency: @"JPY", + kFIRParameterValue: @10000, + kFIRParameterShipping: @500, + kFIRParameterTransactionID: @"192803301", + }]; + // [END ecommerce_purchase] +} + +- (void)enhancedEcommerce { + // Implementation + + // [START create_items] + // A pair of jeggings + NSMutableDictionary *jeggings = [@{ + kFIRParameterItemID: @"SKU_123", + kFIRParameterItemName: @"jeggings", + kFIRParameterItemCategory: @"pants", + kFIRParameterItemVariant: @"black", + kFIRParameterItemBrand: @"Google", + kFIRParameterPrice: @9.99, + } mutableCopy]; + + // A pair of boots + NSMutableDictionary *boots = [@{ + kFIRParameterItemID: @"SKU_456", + kFIRParameterItemName: @"boots", + kFIRParameterItemCategory: @"shoes", + kFIRParameterItemVariant: @"brown", + kFIRParameterItemBrand: @"Google", + kFIRParameterPrice: @24.99, + } mutableCopy]; + + // A pair of socks + NSMutableDictionary *socks = [@{ + kFIRParameterItemID: @"SKU_789", + kFIRParameterItemName: @"ankle_socks", + kFIRParameterItemCategory: @"socks", + kFIRParameterItemVariant: @"red", + kFIRParameterItemBrand: @"Google", + kFIRParameterPrice: @5.99, + } mutableCopy]; + // [END create_items] + + // Selecting a product from a list + + // [START view_item_list] + // Add item indexes + jeggings[kFIRParameterIndex] = @1; + boots[kFIRParameterIndex] = @2; + socks[kFIRParameterIndex] = @3; + + // Prepare ecommerce parameters + NSMutableDictionary *itemList = [@{ + kFIRParameterItemListID: @"L001", + kFIRParameterItemListName: @"Related products", + } mutableCopy]; + + // Add items array + itemList[kFIRParameterItems] = @[jeggings, boots, socks]; + + // Log view item list event + [FIRAnalytics logEventWithName:kFIREventViewItemList parameters:itemList]; + // [END view_item_list] + + // [START select_item] + // Prepare ecommerce parameters + NSMutableDictionary *selectedItem = [@{ + kFIRParameterItemListID: @"L001", + kFIRParameterItemListName: @"Related products", + } mutableCopy]; + + // Add items array + selectedItem[kFIRParameterItems] = @[jeggings]; + + // Log select item event + [FIRAnalytics logEventWithName:kFIREventSelectItem parameters:selectedItem]; + // [END select_item] + + // Viewing product details + + // [START view_product_details] + // Prepare ecommerce parameters + NSMutableDictionary *productDetails = [@{ + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @9.99 + } mutableCopy]; + + // Add items array + productDetails[kFIRParameterItems] = @[jeggings]; + + // Log view item event + [FIRAnalytics logEventWithName:kFIREventViewItem parameters:productDetails]; + // [END view_product_details] + + // Adding/Removing a product from shopping cart + + // [START add_to_cart_wishlist] + // Specify order quantity + jeggings[kFIRParameterQuantity] = @2; + + // Prepare item detail params + NSMutableDictionary *itemDetails = [@{ + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @19.98 + } mutableCopy]; + + // Add items + itemDetails[kFIRParameterItems] = @[jeggings]; + + // Log an event when product is added to wishlist + [FIRAnalytics logEventWithName:kFIREventAddToWishlist parameters:itemDetails]; + + // Log an event when product is added to cart + [FIRAnalytics logEventWithName:kFIREventAddToCart parameters:itemDetails]; + // [END add_to_cart_wishlist] + + // [START view_cart] + // Specify order quantity + jeggings[kFIRParameterQuantity] = @2; + boots[kFIRParameterQuantity] = @1; + + // Prepare order parameters + NSMutableDictionary *orderParameters = [@{ + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @44.97 + } mutableCopy]; + + // Add items array + orderParameters[kFIRParameterItems] = @[jeggings, boots]; + + // Log event when cart is viewed + [FIRAnalytics logEventWithName:kFIREventViewCart parameters:orderParameters]; + // [END view_cart] + + // [START remove_from_cart] + // Specify removal quantity + boots[kFIRParameterQuantity] = @1; + + // Prepare params + NSMutableDictionary *removeParams = [@{ + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @24.99 + } mutableCopy]; + + // Add items + removeParams[kFIRParameterItems] = @[boots]; + + // Log removal event + [FIRAnalytics logEventWithName:kFIREventRemoveFromCart parameters:removeParams]; + // [END remove_from_cart] + + // Initiating the checkout process + + // [START start_checkout] + // Prepare checkout params + NSMutableDictionary *checkoutParams = [@{ + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @14.98, + kFIRParameterCoupon: @"SUMMER_FUN" + } mutableCopy]; + + // Add items + checkoutParams[kFIRParameterItems] = @[jeggings]; + + // Log checkout event + [FIRAnalytics logEventWithName:kFIREventBeginCheckout parameters:checkoutParams]; + // [END start_checkout] + + // [START add_shipping] + // Prepare shipping params + NSMutableDictionary *shippingParams = [@{ + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @14.98, + kFIRParameterCoupon: @"SUMMER_FUN", + kFIRParameterShippingTier: @"Ground" + } mutableCopy]; + + // Add items + shippingParams[kFIRParameterItems] = @[jeggings]; + + // Log added shipping info event + [FIRAnalytics logEventWithName:kFIREventAddShippingInfo parameters:shippingParams]; + // [END add_shipping] + + // [START add_payment] + // Prepare payment params + NSMutableDictionary *paymentParams = [@{ + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @14.98, + kFIRParameterCoupon: @"SUMMER_FUN", + kFIRParameterPaymentType: @"Visa" + } mutableCopy]; + + // Add items + paymentParams[kFIRParameterItems] = @[jeggings]; + + // Log added payment info event + [FIRAnalytics logEventWithName:kFIREventAddPaymentInfo parameters:paymentParams]; + // [END add_payment] + + // Making a purchase or issuing a refund + + // [START log_purchase] + // Prepare purchase params + NSMutableDictionary *purchaseParams = [@{ + kFIRParameterTransactionID: @"T12345", + kFIRParameterAffiliation: @"Google Store", + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @14.98, + kFIRParameterTax: @2.58, + kFIRParameterShipping: @5.34, + kFIRParameterCoupon: @"SUMMER_FUN" + } mutableCopy]; + + // Add items + purchaseParams[kFIRParameterItems] = @[jeggings]; + + // Log purchase event + [FIRAnalytics logEventWithName:kFIREventPurchase parameters:purchaseParams]; + // [END log_purchase] + + // [START log_refund] + // Prepare refund params + NSMutableDictionary *refundParams = [@{ + kFIRParameterTransactionID: @"T12345", + kFIRParameterCurrency: @"USD", + kFIRParameterValue: @9.99, + } mutableCopy]; + + // (Optional) for partial refunds, define the item ID and quantity of refunded items + NSDictionary *refundedProduct = @{ + kFIRParameterItemID: @"SKU_123", + kFIRParameterQuantity: @1, + }; + + // Add items + refundParams[kFIRParameterItems] = @[refundedProduct]; + + // Log refund event + [FIRAnalytics logEventWithName:kFIREventRefund parameters:refundParams]; + // [END log_refund] + + // Applying promotions + + // [START apply_promo] + // Prepare promotion parameters + NSMutableDictionary *promoParams = [@{ + kFIRParameterPromotionID: @"T12345", + kFIRParameterPromotionName: @"Summer Sale", + kFIRParameterCreativeName: @"summer2020_promo.jpg", + kFIRParameterCreativeSlot: @"featured_app_1", + kFIRParameterLocationID: @"HERO_BANNER", + } mutableCopy]; + + // Add items + promoParams[kFIRParameterItems] = @[jeggings]; + + // Log event when promotion is displayed + [FIRAnalytics logEventWithName:kFIREventViewPromotion parameters:promoParams]; + + // Log event when promotion is selected + [FIRAnalytics logEventWithName:kFIREventSelectPromotion parameters:promoParams]; + // [END apply_promo] +} + +// MARK: ad_impression +// Log ad_impression for sharing advertising impression data. +// [START log_ad_impression_applovin] +- (void)didPayRevenueForAd:(MAAd *)impressionData { + [FIRAnalytics logEventWithName:kFIREventAdImpression + parameters: @{ + kFIRParameterAdPlatform: @"AppLovin", + kFIRParameterAdSource: impressionData.networkName, + kFIRParameterAdFormat: impressionData.format, + kFIRParameterAdUnitName: impressionData.adUnitIdentifier, + kFIRParameterCurrency: @"USD", // All Applovin revenue is sent in USD + kFIRParameterValue: impressionData.revenue + }]; +} +// [END log_ad_impression_applovin] + +// [START log_ad_impression_ironsource] +- (void)impressionDataDidSucceed:(ISImpressionData *)impressionData { + [FIRAnalytics logEventWithName:kFIREventAdImpression + parameters:@{ + kFIRParameterAdPlatform: @"ironSource", + kFIRParameterAdSource: impressionData.ad_network, + kFIRParameterAdFormat: impressionData.ad_unit, + kFIRParameterAdUnitName: impressionData.instance_name, + kFIRParameterCurrency: @"USD", + kFIRParameterValue: impressionData.revenue + }]; +} +// [END log_ad_impression_ironsource] +@end diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/AppDelegate.swift b/firoptions/FiroptionConfiguration/AppDelegate.swift similarity index 70% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration/AppDelegate.swift rename to firoptions/FiroptionConfiguration/AppDelegate.swift index 30dd8ae0..9fad7a29 100644 --- a/firoptions/FiroptionConfiguration/FiroptionConfiguration/AppDelegate.swift +++ b/firoptions/FiroptionConfiguration/AppDelegate.swift @@ -15,7 +15,7 @@ // import UIKit -import Firebase +import FirebaseCore import FirebaseDatabase @UIApplicationMain @@ -24,7 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // [START default_configure] // Use the default GoogleService-Info.plist. @@ -36,21 +36,31 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // [START default_configure_file] // Load a named file. - let filePath = Bundle.main.path(forResource: "MyGoogleService", ofType: "plist") - guard let fileopts = FirebaseOptions(contentsOfFile: filePath!) - else { assert(false, "Couldn't load config file") } - FirebaseApp.configure(options: fileopts) + guard + let filePath = Bundle.main.path(forResource: "MyGoogleService", ofType: "plist"), + let fileOptions = FirebaseOptions(contentsOfFile: filePath) + else { fatalError("Couldn't load config file.") } + FirebaseApp.configure(options: fileOptions) // [END default_configure_file] // Note: this one is not deleted, so is the default below. // [START default_configure_vars] - // Configure with manual options. - let secondaryOptions = FirebaseOptions(googleAppID: "1:27992087142:ios:2a4732a34787067a", gcmSenderID: "27992087142") - secondaryOptions.bundleID = "com.google.firebase.devrel.FiroptionConfiguration" + // Configure with manual options. Note that projectID and apiKey, though not + // required by the initializer, are mandatory. + let secondaryOptions = FirebaseOptions(googleAppID: "1:27992087142:ios:2a4732a34787067a", + gcmSenderID: "27992087142") secondaryOptions.apiKey = "AIzaSyBicqfAZPvMgC7NZkjayUEsrepxuXzZDsk" + secondaryOptions.projectID = "projectid-12345" + + // The other options are not mandatory, but may be required + // for specific Firebase products. + secondaryOptions.bundleID = "com.google.firebase.devrel.FiroptionConfiguration" secondaryOptions.clientID = "27992087142-ola6qe637ulk8780vl8mo5vogegkm23n.apps.googleusercontent.com" secondaryOptions.databaseURL = "https://myproject.firebaseio.com" secondaryOptions.storageBucket = "myproject.appspot.com" + secondaryOptions.deepLinkURLScheme = "myapp://" + secondaryOptions.storageBucket = "projectid-12345.appspot.com" + secondaryOptions.appGroupID = nil // [END default_configure_vars] @@ -60,7 +70,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Retrieve a previous created named app. guard let secondary = FirebaseApp.app(name: "secondary") - else { assert(false, "Could not retrieve secondary app") } + else { fatalError("Could not retrieve secondary app") } // Retrieve a Real Time Database client configured against a specific app. @@ -71,7 +81,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let defaultDb = Database.database() guard let defapp = FirebaseApp.app() - else { assert(false, "Could not retrieve default app") } + else { fatalError("Could not retrieve default app") } assert(secondaryDb.app == secondary) assert(defaultDb.app == defapp) @@ -79,6 +89,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } - } diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/Assets.xcassets/AppIcon.appiconset/Contents.json b/firoptions/FiroptionConfiguration/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration/Assets.xcassets/AppIcon.appiconset/Contents.json rename to firoptions/FiroptionConfiguration/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/Base.lproj/LaunchScreen.storyboard b/firoptions/FiroptionConfiguration/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration/Base.lproj/LaunchScreen.storyboard rename to firoptions/FiroptionConfiguration/Base.lproj/LaunchScreen.storyboard diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/Base.lproj/Main.storyboard b/firoptions/FiroptionConfiguration/Base.lproj/Main.storyboard similarity index 100% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration/Base.lproj/Main.storyboard rename to firoptions/FiroptionConfiguration/Base.lproj/Main.storyboard diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration-Bridging-Header.h b/firoptions/FiroptionConfiguration/FiroptionConfiguration-Bridging-Header.h new file mode 100644 index 00000000..1b2cb5d6 --- /dev/null +++ b/firoptions/FiroptionConfiguration/FiroptionConfiguration-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/firoptions/FiroptionConfiguration/FiroptionConfiguration.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index ee044f00..00000000 --- a/firoptions/FiroptionConfiguration/FiroptionConfiguration.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/GoogleService-Info.plist b/firoptions/FiroptionConfiguration/FiroptionConfiguration/GoogleService-Info.plist deleted file mode 100644 index 7383dabf..00000000 --- a/firoptions/FiroptionConfiguration/FiroptionConfiguration/GoogleService-Info.plist +++ /dev/null @@ -1,40 +0,0 @@ - - - - - AD_UNIT_ID_FOR_BANNER_TEST - ca-app-pub-3940256099942544/2934735716 - AD_UNIT_ID_FOR_INTERSTITIAL_TEST - ca-app-pub-3940256099942544/4411468910 - CLIENT_ID - 27992087142-ola6qe637ulk8780vl8mo5vogegkm23n.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.27992087142-ola6qe637ulk8780vl8mo5vogegkm23n - API_KEY - AIzaSyBicqfAZPvMgC7NZkjayUEsrepxuXzZDsk - GCM_SENDER_ID - 27992087142 - PLIST_VERSION - 1 - BUNDLE_ID - com.google.firebase.devrel.FiroptionConfiguration - PROJECT_ID - referencecodeproject - STORAGE_BUCKET - referencecodeproject.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:27992087142:ios:2a4732a34787067a - DATABASE_URL - https://referencecodeproject.firebaseio.com - - \ No newline at end of file diff --git a/firoptions/FiroptionConfiguration/FiroptionConfigurationDev/GoogleService-Info.plist b/firoptions/FiroptionConfiguration/FiroptionConfigurationDev/GoogleService-Info.plist deleted file mode 100644 index 369b7275..00000000 --- a/firoptions/FiroptionConfiguration/FiroptionConfigurationDev/GoogleService-Info.plist +++ /dev/null @@ -1,40 +0,0 @@ - - - - - AD_UNIT_ID_FOR_BANNER_TEST - ca-app-pub-3940256099942544/2934735716 - AD_UNIT_ID_FOR_INTERSTITIAL_TEST - ca-app-pub-3940256099942544/4411468910 - CLIENT_ID - 27992087142-s70q70uud698s86nae5iml0ildcvck9d.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.27992087142-s70q70uud698s86nae5iml0ildcvck9d - API_KEY - AIzaSyBicqfAZPvMgC7NZkjayUEsrepxuXzZDsk - GCM_SENDER_ID - 27992087142 - PLIST_VERSION - 1 - BUNDLE_ID - com.google.firebase.devrel.FiroptionConfigurationDev - PROJECT_ID - referencecodeproject - STORAGE_BUCKET - referencecodeproject.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:27992087142:ios:db637d668463ee8e - DATABASE_URL - https://referencecodeproject.firebaseio.com - - \ No newline at end of file diff --git a/firoptions/FiroptionConfiguration/ISImpressionData.h b/firoptions/FiroptionConfiguration/ISImpressionData.h new file mode 100644 index 00000000..24e159f9 --- /dev/null +++ b/firoptions/FiroptionConfiguration/ISImpressionData.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#import + +@interface ISImpressionData: NSObject +// Simulated ad_impression structures from mediation platforms to ensure that this project compiles. +// Ironsource Sample Publisher Ad Impression +@property (nonatomic, strong) NSString *ad_network; +@property (nonatomic, strong) NSString *ad_unit; +@property (nonatomic, strong) NSString *instance_name; +@property (nonatomic) NSNumber *revenue; + +@end diff --git a/firoptions/FiroptionConfiguration/ISImpressionData.m b/firoptions/FiroptionConfiguration/ISImpressionData.m new file mode 100644 index 00000000..85d13c01 --- /dev/null +++ b/firoptions/FiroptionConfiguration/ISImpressionData.m @@ -0,0 +1,23 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "ISImpressionData.h" +// Simulated ad_impression structures from mediation platforms to ensure that this project compiles +// Ironsource Sample Publisher Ad Impression +@implementation ISImpressionData + +@end diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/Info.plist b/firoptions/FiroptionConfiguration/Info.plist similarity index 100% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration/Info.plist rename to firoptions/FiroptionConfiguration/Info.plist diff --git a/firoptions/FiroptionConfiguration/MAAd.h b/firoptions/FiroptionConfiguration/MAAd.h new file mode 100644 index 00000000..b39f5cbc --- /dev/null +++ b/firoptions/FiroptionConfiguration/MAAd.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#import + +@interface MAAd:NSObject +// Simulated ad_impression structures from mediation platforms to ensure that this project compiles +// AppLovin Sample Publisher Ad Impression +@property (nonatomic, strong) NSString *adUnitIdentifier; +@property (nonatomic, strong) NSString *networkName; +@property (nonatomic, strong) NSString *format; +@property (nonatomic) NSNumber *revenue; + +@end diff --git a/firoptions/FiroptionConfiguration/MAAd.m b/firoptions/FiroptionConfiguration/MAAd.m new file mode 100644 index 00000000..3b441d19 --- /dev/null +++ b/firoptions/FiroptionConfiguration/MAAd.m @@ -0,0 +1,22 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "MAAd.h" +// Simulated ad_impression structures from mediation platforms to ensure that this project compiles +@implementation MAAd + +@end diff --git a/firoptions/FiroptionConfiguration/FiroptionConfiguration/MyGoogleService.plist b/firoptions/FiroptionConfiguration/MyGoogleService.plist similarity index 100% rename from firoptions/FiroptionConfiguration/FiroptionConfiguration/MyGoogleService.plist rename to firoptions/FiroptionConfiguration/MyGoogleService.plist diff --git a/firoptions/FiroptionConfiguration/Podfile b/firoptions/FiroptionConfiguration/Podfile deleted file mode 100644 index 99971c5b..00000000 --- a/firoptions/FiroptionConfiguration/Podfile +++ /dev/null @@ -1,12 +0,0 @@ -# Uncomment the next line to define a global platform for your project -# platform :ios, '9.0' - -target 'FiroptionConfiguration' do - # Comment the next line if you're not using Swift and don't want to use dynamic frameworks - use_frameworks! - - # Pods for FiroptionConfiguration - pod 'Firebase/Core' - pod 'Firebase/Database' - -end diff --git a/firoptions/FiroptionConfiguration/Podfile.lock b/firoptions/FiroptionConfiguration/Podfile.lock deleted file mode 100644 index bf75f472..00000000 --- a/firoptions/FiroptionConfiguration/Podfile.lock +++ /dev/null @@ -1,113 +0,0 @@ -PODS: - - Firebase/Core (6.8.0): - - Firebase/CoreOnly - - FirebaseAnalytics (= 6.1.1) - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/Database (6.8.0): - - Firebase/CoreOnly - - FirebaseDatabase (~> 6.1.0) - - FirebaseAnalytics (6.1.1): - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.1.1) - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - FirebaseAuthInterop (1.0.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseDatabase (6.1.0): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.0) - - leveldb-library (~> 1.18) - - FirebaseInstanceID (4.2.4): - - FirebaseCore (~> 6.0) - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/UserDefaults (~> 6.0) - - GoogleAppMeasurement (6.1.1): - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleUtilities/AppDelegateSwizzler (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.2.5): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.2.5)" - - GoogleUtilities/Reachability (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.2.5): - - GoogleUtilities/Logger - - leveldb-library (1.22) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - -DEPENDENCIES: - - Firebase/Core - - Firebase/Database - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - Firebase - - FirebaseAnalytics - - FirebaseAuthInterop - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - - FirebaseDatabase - - FirebaseInstanceID - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleDataTransportCCTSupport - - GoogleUtilities - - leveldb-library - - nanopb - -SPEC CHECKSUMS: - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseAnalytics: 843c7f64a8f9c79f0d03281197ebe7bb1d58d477 - FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseDatabase: 518cd94286de2ee999e19383a2a6ae04c81ce993 - FirebaseInstanceID: 88932a31aba5a56cfd3a7541706436c71f7f4598 - GoogleAppMeasurement: 86a82f0e1f20b8eedf8e20326530138fd71409de - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - -PODFILE CHECKSUM: a19f89b9a2d7ad92954885375c3e7dcafa52d969 - -COCOAPODS: 1.7.5 diff --git a/firoptions/FiroptionConfiguration/ViewController.swift b/firoptions/FiroptionConfiguration/ViewController.swift new file mode 100644 index 00000000..b8eeab6d --- /dev/null +++ b/firoptions/FiroptionConfiguration/ViewController.swift @@ -0,0 +1,376 @@ +// +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseAnalytics + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + func logInAppPurchaseEvent() { + // [START ecommerce_purchase] + Analytics.logEvent(AnalyticsEventPurchase, parameters: [ + AnalyticsParameterCoupon: "SummerPromo", + AnalyticsParameterCurrency: "JPY", + AnalyticsParameterValue: 10000, + AnalyticsParameterShipping: 500, + AnalyticsParameterTransactionID: "192803301", + ]) + // [END ecommerce_purchase] + } + + func enhancedEcommerce() { + // Implementation + + // [START create_items] + // A pair of jeggings + var jeggings: [String: Any] = [ + AnalyticsParameterItemID: "SKU_123", + AnalyticsParameterItemName: "jeggings", + AnalyticsParameterItemCategory: "pants", + AnalyticsParameterItemVariant: "black", + AnalyticsParameterItemBrand: "Google", + AnalyticsParameterPrice: 9.99, + ] + + // A pair of boots + var boots: [String: Any] = [ + AnalyticsParameterItemID: "SKU_456", + AnalyticsParameterItemName: "boots", + AnalyticsParameterItemCategory: "shoes", + AnalyticsParameterItemVariant: "brown", + AnalyticsParameterItemBrand: "Google", + AnalyticsParameterPrice: 24.99, + ] + + // A pair of socks + var socks: [String: Any] = [ + AnalyticsParameterItemID: "SKU_789", + AnalyticsParameterItemName: "ankle_socks", + AnalyticsParameterItemCategory: "socks", + AnalyticsParameterItemVariant: "red", + AnalyticsParameterItemBrand: "Google", + AnalyticsParameterPrice: 5.99, + ] + // [END create_items] + + // Selecting a product from a list + + // [START view_item_list] + // Add item indexes + jeggings[AnalyticsParameterIndex] = 1 + boots[AnalyticsParameterIndex] = 2 + socks[AnalyticsParameterIndex] = 3 + + // Prepare ecommerce parameters + var itemList: [String: Any] = [ + AnalyticsParameterItemListID: "L001", + AnalyticsParameterItemListName: "Related products", + ] + + // Add items array + itemList[AnalyticsParameterItems] = [jeggings, boots, socks] + + // Log view item list event + Analytics.logEvent(AnalyticsEventViewItemList, parameters: itemList) + // [END view_item_list] + + // [START select_item] + // Prepare ecommerce parameters + var selectedItem: [String: Any] = [ + AnalyticsParameterItemListID: "L001", + AnalyticsParameterItemListName: "Related products", + ] + + // Add items array + selectedItem[AnalyticsParameterItems] = [jeggings] + + // Log select item event + Analytics.logEvent(AnalyticsEventSelectItem, parameters: selectedItem) + // [END select_item] + + // Viewing product details + + // [START view_product_details] + // Prepare ecommerce parameters + var productDetails: [String: Any] = [ + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 9.99 + ] + + // Add items array + productDetails[AnalyticsParameterItems] = [jeggings] + + // Log view item event + Analytics.logEvent(AnalyticsEventViewItem, parameters: productDetails) + // [END view_product_details] + + // Adding/Removing a product from shopping cart + + // [START add_to_cart_wishlist] + // Specify order quantity + jeggings[AnalyticsParameterQuantity] = 2 + + // Prepare item detail params + var itemDetails: [String: Any] = [ + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 19.98 + ] + + // Add items + itemDetails[AnalyticsParameterItems] = [jeggings] + + // Log an event when product is added to wishlist + Analytics.logEvent(AnalyticsEventAddToWishlist, parameters: itemDetails) + + // Log an event when product is added to cart + Analytics.logEvent(AnalyticsEventAddToCart, parameters: itemDetails) + // [END add_to_cart_wishlist] + + // [START view_cart] + // Specify order quantity + jeggings[AnalyticsParameterQuantity] = 2 + boots[AnalyticsParameterQuantity] = 1 + + // Prepare order parameters + var orderParameters: [String: Any] = [ + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 44.97 + ] + + // Add items array + orderParameters[AnalyticsParameterItems] = [jeggings, boots] + + // Log event when cart is viewed + Analytics.logEvent(AnalyticsEventViewCart, parameters: orderParameters) + // [END view_cart] + + // [START remove_from_cart] + // Specify removal quantity + boots[AnalyticsParameterQuantity] = 1 + + // Prepare params + var removeParams: [String: Any] = [ + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 24.99 + ] + + // Add items + removeParams[AnalyticsParameterItems] = [boots] + + // Log removal event + Analytics.logEvent(AnalyticsEventRemoveFromCart, parameters: removeParams) + // [END remove_from_cart] + + // Initiating the checkout process + + // [START start_checkout] + // Prepare checkout params + var checkoutParams: [String: Any] = [ + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 14.98, + AnalyticsParameterCoupon: "SUMMER_FUN" + ]; + + // Add items + checkoutParams[AnalyticsParameterItems] = [jeggings] + + // Log checkout event + Analytics.logEvent(AnalyticsEventBeginCheckout, parameters: checkoutParams) + // [END start_checkout] + + // [START add_shipping] + // Prepare shipping params + var shippingParams: [String: Any] = [ + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 14.98, + AnalyticsParameterCoupon: "SUMMER_FUN", + AnalyticsParameterShippingTier: "Ground" + ] + + // Add items + shippingParams[AnalyticsParameterItems] = [jeggings] + + // Log added shipping info event + Analytics.logEvent(AnalyticsEventAddShippingInfo, parameters: shippingParams) + // [END add_shipping] + + // [START add_payment] + // Prepare payment params + var paymentParams: [String: Any] = [ + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 14.98, + AnalyticsParameterCoupon: "SUMMER_FUN", + AnalyticsParameterPaymentType: "Visa" + ] + + // Add items + paymentParams[AnalyticsParameterItems] = [jeggings] + + // Log added payment info event + Analytics.logEvent(AnalyticsEventAddPaymentInfo, parameters: paymentParams) + // [END add_payment] + + // Making a purchase or issuing a refund + + // [START log_purchase] + // Prepare purchase params + var purchaseParams: [String: Any] = [ + AnalyticsParameterTransactionID: "T12345", + AnalyticsParameterAffiliation: "Google Store", + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 14.98, + AnalyticsParameterTax: 2.58, + AnalyticsParameterShipping: 5.34, + AnalyticsParameterCoupon: "SUMMER_FUN" + ] + + // Add items + purchaseParams[AnalyticsParameterItems] = [jeggings] + + // Log purchase event + Analytics.logEvent(AnalyticsEventPurchase, parameters: purchaseParams) + // [END log_purchase] + + // [START log_refund] + // Prepare refund params + var refundParams: [String: Any] = [ + AnalyticsParameterTransactionID: "T12345", + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: 9.99, + ] + + // (Optional) for partial refunds, define the item ID and quantity of refunded items + let refundedProduct: [String: Any] = [ + AnalyticsParameterItemID: "SKU_123", + AnalyticsParameterQuantity: 1, + ]; + + // Add items + refundParams[AnalyticsParameterItems] = [refundedProduct] + + // Log refund event + Analytics.logEvent(AnalyticsEventRefund, parameters: refundParams) + // [END log_refund] + + // Applying promotions + + // [START apply_promo] + // Prepare promotion parameters + var promoParams: [String: Any] = [ + AnalyticsParameterPromotionID: "T12345", + AnalyticsParameterPromotionName:"Summer Sale", + AnalyticsParameterCreativeName: "summer2020_promo.jpg", + AnalyticsParameterCreativeSlot: "featured_app_1", + AnalyticsParameterLocationID: "HERO_BANNER", + ] + + // Add items + promoParams[AnalyticsParameterItems] = [jeggings] + + // Log event when promotion is displayed + Analytics.logEvent(AnalyticsEventViewPromotion, parameters: promoParams) + + // Log event when promotion is selected + Analytics.logEvent(AnalyticsEventSelectPromotion, parameters: promoParams) + // [END apply_promo] + } + // MARK: ad_impression + // Log ad_impression for sharing advertising impression data. + // Simulated ad_impression structures from mediation platforms to ensure that this project compiles + // Ironsource Sample Pub Ad Impression + struct ISImpressionData { + let ad_network: String? + let ad_unit: String? + let instance_name: String? + let revenue: Double? + } + // MoPub Sample Pub Ad Impression + struct MPMoPubAd {} + // MoPub Sample Pub Ad Impression Data + struct MPImpressionData { + let adUnitName: String + let adUnitFormat: String + let publisherRevenue: Double + let currency: String + let networkName: String + let precision: String + } + // AppLovin Sample Pub Ad Impression + struct MAAd { + let adUnitIdentifier: String + let format: String + let revenue: String + let networkName: String + } + // [START log_ad_impression_mopub] + func mopubAd(_ ad: MPMoPubAd, didTrackImpressionWith impressionData: MPImpressionData?) { + if let impressionData = impressionData { + Analytics.logEvent( + AnalyticsEventAdImpression, + parameters: [ + AnalyticsParameterAdPlatform: "MoPub", + AnalyticsParameterAdUnitName: impressionData.adUnitName, + AnalyticsParameterAdFormat: impressionData.adUnitFormat, + AnalyticsParameterValue: impressionData.publisherRevenue, + AnalyticsParameterCurrency: impressionData.currency, + AnalyticsParameterAdSource: impressionData.networkName, + "precision": impressionData.precision, + ]) + } + } + // [END log_ad_impression_mopub] + + // [START log_ad_impression_ironsource] + func impressionDataDidSucceed(_ impressionData: ISImpressionData!) { + Analytics.logEvent( + AnalyticsEventAdImpression, + parameters: [ + AnalyticsParameterAdPlatform: "ironSource", + AnalyticsParameterAdSource: impressionData.ad_network ?? "No ad_network", + AnalyticsParameterAdFormat: impressionData.ad_unit ?? "No ad_unit", + AnalyticsParameterAdUnitName: impressionData.instance_name ?? "No instance_name", + AnalyticsParameterCurrency: "USD", + AnalyticsParameterValue: impressionData.revenue ?? 0, + ]) + } + // [END log_ad_impression_ironsource] + + // [START log_ad_impression_applovin] + func didPayRevenue(_ impressionData: MAAd?) { + if let impressionData = impressionData { + Analytics.logEvent( + AnalyticsEventAdImpression, + parameters: [ + AnalyticsParameterAdPlatform: "AppLovin", + AnalyticsParameterAdUnitName: impressionData.adUnitIdentifier, + AnalyticsParameterAdFormat: impressionData.format, + AnalyticsParameterValue: impressionData.revenue, + AnalyticsParameterCurrency: "USD", // All Applovin revenue is sent in USD + AnalyticsParameterAdSource: impressionData.networkName, + ]) + } + } + // [END log_ad_impression_applovin] +} diff --git a/functions/FunctionsExample.xcodeproj/project.pbxproj b/functions/FunctionsExample.xcodeproj/project.pbxproj index 54bcb58e..207b4f35 100644 --- a/functions/FunctionsExample.xcodeproj/project.pbxproj +++ b/functions/FunctionsExample.xcodeproj/project.pbxproj @@ -3,10 +3,12 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ + 8D7951BA2D28B078000FD694 /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951B92D28B078000FD694 /* FirebaseFunctions */; }; + 8D7951BC2D28B0C4000FD694 /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951BB2D28B0C4000FD694 /* FirebaseFunctions */; }; 8D8FA34322F4CAB100213E06 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA34222F4CAB100213E06 /* AppDelegate.m */; }; 8D8FA34622F4CAB100213E06 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA34522F4CAB100213E06 /* ViewController.m */; }; 8D8FA34922F4CAB100213E06 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA34722F4CAB100213E06 /* Main.storyboard */; }; @@ -45,6 +47,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D7951BC2D28B0C4000FD694 /* FirebaseFunctions in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -52,17 +55,26 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D7951BA2D28B078000FD694 /* FirebaseFunctions in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 8D7951B82D28B078000FD694 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 8D8FA33522F4CAB100213E06 = { isa = PBXGroup; children = ( 8D8FA34022F4CAB100213E06 /* FunctionsExample */, 8D8FA35C22F4CAF700213E06 /* FunctionsExampleSwift */, + 8D7951B82D28B078000FD694 /* Frameworks */, 8D8FA33F22F4CAB100213E06 /* Products */, ); sourceTree = ""; @@ -148,8 +160,9 @@ 8D8FA33622F4CAB100213E06 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1020; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1610; ORGANIZATIONNAME = Firebase; TargetAttributes = { 8D8FA33D22F4CAB100213E06 = { @@ -169,6 +182,9 @@ Base, ); mainGroup = 8D8FA33522F4CAB100213E06; + packageReferences = ( + 8D7951B72D28B070000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 8D8FA33F22F4CAB100213E06 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -286,6 +302,7 @@ 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; @@ -297,6 +314,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -345,6 +363,7 @@ 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; @@ -356,6 +375,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -368,6 +388,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; name = Release; @@ -473,6 +494,30 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7951B72D28B070000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7951B92D28B078000FD694 /* FirebaseFunctions */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951B72D28B070000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFunctions; + }; + 8D7951BB2D28B0C4000FD694 /* FirebaseFunctions */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951B72D28B070000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFunctions; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 8D8FA33622F4CAB100213E06 /* Project object */; } diff --git a/functions/FunctionsExample/AppDelegate.m b/functions/FunctionsExample/AppDelegate.m index ccf63ce3..69381fe8 100644 --- a/functions/FunctionsExample/AppDelegate.m +++ b/functions/FunctionsExample/AppDelegate.m @@ -14,7 +14,7 @@ // limitations under the License. // -@import Firebase; +@import FirebaseCore; #import "AppDelegate.h" diff --git a/functions/FunctionsExample/ViewController.m b/functions/FunctionsExample/ViewController.m index 5225905a..43c0b699 100644 --- a/functions/FunctionsExample/ViewController.m +++ b/functions/FunctionsExample/ViewController.m @@ -14,7 +14,7 @@ // limitations under the License. // -@import Firebase; +@import FirebaseFunctions; #import "ViewController.h" @@ -31,7 +31,7 @@ - (void)viewDidLoad { - (void)emulatorSettings { // [START functions_emulator_connect] - [[FIRFunctions functions] useFunctionsEmulatorOrigin:@"http://localhost:5001"]; + [[FIRFunctions functions] useEmulatorWithHost:@"localhost" port:5001]; // [END functions_emulator_connect] } diff --git a/functions/FunctionsExampleSwift/AppDelegate.swift b/functions/FunctionsExampleSwift/AppDelegate.swift index 26ad4232..6a384187 100644 --- a/functions/FunctionsExampleSwift/AppDelegate.swift +++ b/functions/FunctionsExampleSwift/AppDelegate.swift @@ -15,7 +15,7 @@ // import UIKit -import Firebase +import FirebaseCore @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/functions/FunctionsExampleSwift/ViewController.swift b/functions/FunctionsExampleSwift/ViewController.swift index 68aeb153..0a0c6c25 100644 --- a/functions/FunctionsExampleSwift/ViewController.swift +++ b/functions/FunctionsExampleSwift/ViewController.swift @@ -15,7 +15,7 @@ // import UIKit -import Firebase +import FirebaseFunctions class ViewController: UIViewController { @@ -26,7 +26,7 @@ class ViewController: UIViewController { func emulatorSettings() { // [START functions_emulator_connect] - Functions.functions().useFunctionsEmulator(origin: "http://localhost:5001") + Functions.functions().useEmulator(withHost: "localhost", port: 5001) // [END functions_emulator_connect] } diff --git a/functions/Podfile b/functions/Podfile deleted file mode 100644 index 070ed351..00000000 --- a/functions/Podfile +++ /dev/null @@ -1,18 +0,0 @@ -# Uncomment the next line to define a global platform for your project -# platform :ios, '9.0' - -target 'FunctionsExample' do - # Comment the next line if you don't want to use dynamic frameworks - use_frameworks! - - pod 'Firebase/Functions' - -end - -target 'FunctionsExampleSwift' do - # Comment the next line if you don't want to use dynamic frameworks - use_frameworks! - - pod 'Firebase/Functions' - -end diff --git a/functions/Podfile.lock b/functions/Podfile.lock deleted file mode 100644 index a409714c..00000000 --- a/functions/Podfile.lock +++ /dev/null @@ -1,69 +0,0 @@ -PODS: - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/Functions (6.8.0): - - Firebase/CoreOnly - - FirebaseFunctions (~> 2.5.1) - - FirebaseAuthInterop (1.0.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseFunctions (2.5.1): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.0) - - GTMSessionFetcher/Core (~> 1.1) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): - - GoogleUtilities/Environment - - GTMSessionFetcher/Core (1.2.2) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - -DEPENDENCIES: - - Firebase/Functions - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - Firebase - - FirebaseAuthInterop - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - - FirebaseFunctions - - GoogleDataTransport - - GoogleDataTransportCCTSupport - - GoogleUtilities - - GTMSessionFetcher - - nanopb - -SPEC CHECKSUMS: - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseFunctions: 5af7c35d1c5e41608fecbb667eb6c4e672e318d0 - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - -PODFILE CHECKSUM: 0bf21181414ed35c7e5fc96da7dd0f72b77e7b34 - -COCOAPODS: 1.7.5 diff --git a/inappmessaging/FIAMReference/FIAMReference.xcodeproj/project.pbxproj b/inappmessaging/FIAMReference/FIAMReference.xcodeproj/project.pbxproj index 7d111c20..0003f2ea 100644 --- a/inappmessaging/FIAMReference/FIAMReference.xcodeproj/project.pbxproj +++ b/inappmessaging/FIAMReference/FIAMReference.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -20,12 +20,11 @@ 3E64A7C32294513E003EE93A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3E64A7C12294513E003EE93A /* LaunchScreen.storyboard */; }; 3E64A7C9229456A3003EE93A /* CardActionFiamDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E64A7C8229456A3003EE93A /* CardActionFiamDelegate.swift */; }; 3E64A7D522946983003EE93A /* CardActionFiamDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E64A7D422946983003EE93A /* CardActionFiamDelegate.m */; }; - 7B57A0C81AD819D38114C0DA /* Pods_FIAMReferenceSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5CB090B898D1808D8BFB45E /* Pods_FIAMReferenceSwift.framework */; }; - 9DC7047D28AC0715DA8758B9 /* Pods_FIAMReference.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F31B861CDBF9DB7CD403440 /* Pods_FIAMReference.framework */; }; + 8D7951C02D2C8885000FD694 /* FirebaseInAppMessaging-Beta in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951BF2D2C8885000FD694 /* FirebaseInAppMessaging-Beta */; }; + 8D7951C22D2C888E000FD694 /* FirebaseInAppMessaging-Beta in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951C12D2C888E000FD694 /* FirebaseInAppMessaging-Beta */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 0F31B861CDBF9DB7CD403440 /* Pods_FIAMReference.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FIAMReference.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3E64A798229376BB003EE93A /* FIAMReference.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FIAMReference.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3E64A79B229376BB003EE93A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 3E64A79C229376BB003EE93A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -46,11 +45,6 @@ 3E64A7C8229456A3003EE93A /* CardActionFiamDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardActionFiamDelegate.swift; sourceTree = ""; }; 3E64A7D322946983003EE93A /* CardActionFiamDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CardActionFiamDelegate.h; sourceTree = ""; }; 3E64A7D422946983003EE93A /* CardActionFiamDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CardActionFiamDelegate.m; sourceTree = ""; }; - 60DA3A39F9552E8B37AD597C /* Pods-FIAMReferenceSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FIAMReferenceSwift.release.xcconfig"; path = "Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift.release.xcconfig"; sourceTree = ""; }; - 8D026225BF1D8381A23B9CDD /* Pods-FIAMReferenceSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FIAMReferenceSwift.debug.xcconfig"; path = "Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift.debug.xcconfig"; sourceTree = ""; }; - 9E2817EDB630021664AEF7F0 /* Pods-FIAMReference.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FIAMReference.release.xcconfig"; path = "Target Support Files/Pods-FIAMReference/Pods-FIAMReference.release.xcconfig"; sourceTree = ""; }; - B20FEECC6333640D763A6374 /* Pods-FIAMReference.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FIAMReference.debug.xcconfig"; path = "Target Support Files/Pods-FIAMReference/Pods-FIAMReference.debug.xcconfig"; sourceTree = ""; }; - F5CB090B898D1808D8BFB45E /* Pods_FIAMReferenceSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FIAMReferenceSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,7 +52,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9DC7047D28AC0715DA8758B9 /* Pods_FIAMReference.framework in Frameworks */, + 8D7951C02D2C8885000FD694 /* FirebaseInAppMessaging-Beta in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -66,7 +60,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7B57A0C81AD819D38114C0DA /* Pods_FIAMReferenceSwift.framework in Frameworks */, + 8D7951C22D2C888E000FD694 /* FirebaseInAppMessaging-Beta in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -79,8 +73,6 @@ 3E64A79A229376BB003EE93A /* FIAMReference */, 3E64A7B72294513E003EE93A /* FIAMReferenceSwift */, 3E64A799229376BB003EE93A /* Products */, - CE6E66E48A82916264FE3F5B /* Pods */, - A53E5F3D908E32CBF3BBB4EC /* Frameworks */, ); sourceTree = ""; }; @@ -125,27 +117,6 @@ path = FIAMReferenceSwift; sourceTree = ""; }; - A53E5F3D908E32CBF3BBB4EC /* Frameworks */ = { - isa = PBXGroup; - children = ( - 0F31B861CDBF9DB7CD403440 /* Pods_FIAMReference.framework */, - F5CB090B898D1808D8BFB45E /* Pods_FIAMReferenceSwift.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - CE6E66E48A82916264FE3F5B /* Pods */ = { - isa = PBXGroup; - children = ( - B20FEECC6333640D763A6374 /* Pods-FIAMReference.debug.xcconfig */, - 9E2817EDB630021664AEF7F0 /* Pods-FIAMReference.release.xcconfig */, - 8D026225BF1D8381A23B9CDD /* Pods-FIAMReferenceSwift.debug.xcconfig */, - 60DA3A39F9552E8B37AD597C /* Pods-FIAMReferenceSwift.release.xcconfig */, - ); - name = Pods; - path = ../Pods; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -153,12 +124,9 @@ isa = PBXNativeTarget; buildConfigurationList = 3E64A7AE229376BC003EE93A /* Build configuration list for PBXNativeTarget "FIAMReference" */; buildPhases = ( - 46E292585FA315EF59F64499 /* [CP] Check Pods Manifest.lock */, 3E64A794229376BB003EE93A /* Sources */, 3E64A795229376BB003EE93A /* Frameworks */, 3E64A796229376BB003EE93A /* Resources */, - 65339EA2723DC26D2F6AB3B9 /* [CP] Embed Pods Frameworks */, - C7965CB691942633E444DB3C /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -173,12 +141,9 @@ isa = PBXNativeTarget; buildConfigurationList = 3E64A7C72294513E003EE93A /* Build configuration list for PBXNativeTarget "FIAMReferenceSwift" */; buildPhases = ( - BBF5C5D44E878689104526D9 /* [CP] Check Pods Manifest.lock */, 3E64A7B22294513E003EE93A /* Sources */, 3E64A7B32294513E003EE93A /* Frameworks */, 3E64A7B42294513E003EE93A /* Resources */, - C45D95F46F4377C48D861896 /* [CP] Embed Pods Frameworks */, - 771EE35B3102DD3769ED7A38 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -195,8 +160,9 @@ 3E64A790229376BB003EE93A /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1000; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1610; ORGANIZATIONNAME = Google; TargetAttributes = { 3E64A797229376BB003EE93A = { @@ -216,6 +182,9 @@ Base, ); mainGroup = 3E64A78F229376BB003EE93A; + packageReferences = ( + 8D7951BD2D2C887B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 3E64A799229376BB003EE93A /* Products */; projectDirPath = ""; projectRoot = ""; @@ -249,121 +218,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 46E292585FA315EF59F64499 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-FIAMReference-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 65339EA2723DC26D2F6AB3B9 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReference/Pods-FIAMReference-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReference/Pods-FIAMReference-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FIAMReference/Pods-FIAMReference-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 771EE35B3102DD3769ED7A38 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - BBF5C5D44E878689104526D9 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-FIAMReferenceSwift-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C45D95F46F4377C48D861896 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FIAMReferenceSwift/Pods-FIAMReferenceSwift-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - C7965CB691942633E444DB3C /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReference/Pods-FIAMReference-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-FIAMReference/Pods-FIAMReference-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FIAMReference/Pods-FIAMReference-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 3E64A794229376BB003EE93A /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -450,6 +304,7 @@ 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; @@ -461,6 +316,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -475,7 +331,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -509,6 +365,7 @@ 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; @@ -520,6 +377,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -528,17 +386,17 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; VALIDATE_PRODUCT = YES; }; name = Release; }; 3E64A7AF229376BC003EE93A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B20FEECC6333640D763A6374 /* Pods-FIAMReference.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; @@ -555,7 +413,6 @@ }; 3E64A7B0229376BC003EE93A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9E2817EDB630021664AEF7F0 /* Pods-FIAMReference.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; @@ -572,7 +429,6 @@ }; 3E64A7C52294513E003EE93A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 8D026225BF1D8381A23B9CDD /* Pods-FIAMReferenceSwift.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; @@ -592,7 +448,6 @@ }; 3E64A7C62294513E003EE93A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 60DA3A39F9552E8B37AD597C /* Pods-FIAMReferenceSwift.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; @@ -641,6 +496,30 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7951BD2D2C887B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7951BF2D2C8885000FD694 /* FirebaseInAppMessaging-Beta */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951BD2D2C887B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = "FirebaseInAppMessaging-Beta"; + }; + 8D7951C12D2C888E000FD694 /* FirebaseInAppMessaging-Beta */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951BD2D2C887B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = "FirebaseInAppMessaging-Beta"; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 3E64A790229376BB003EE93A /* Project object */; } diff --git a/inappmessaging/FIAMReference/FIAMReference/AppDelegate.h b/inappmessaging/FIAMReference/FIAMReference/AppDelegate.h index ec87de4b..a9861fa2 100644 --- a/inappmessaging/FIAMReference/FIAMReference/AppDelegate.h +++ b/inappmessaging/FIAMReference/FIAMReference/AppDelegate.h @@ -15,8 +15,6 @@ // #import -#import "CardActionFiamDelegate.h" -@import FirebaseInAppMessaging; @interface AppDelegate : UIResponder diff --git a/inappmessaging/FIAMReference/FIAMReference/AppDelegate.m b/inappmessaging/FIAMReference/FIAMReference/AppDelegate.m index b785ec87..708c1619 100644 --- a/inappmessaging/FIAMReference/FIAMReference/AppDelegate.m +++ b/inappmessaging/FIAMReference/FIAMReference/AppDelegate.m @@ -15,7 +15,11 @@ // #import "AppDelegate.h" -@import Firebase; + +@import FirebaseCore; +@import FirebaseInAppMessaging; + +#import "CardActionFiamDelegate.h" @interface AppDelegate () diff --git a/inappmessaging/FIAMReference/FIAMReference/CardActionFiamDelegate.m b/inappmessaging/FIAMReference/FIAMReference/CardActionFiamDelegate.m index 929f145d..7d6a0335 100644 --- a/inappmessaging/FIAMReference/FIAMReference/CardActionFiamDelegate.m +++ b/inappmessaging/FIAMReference/FIAMReference/CardActionFiamDelegate.m @@ -39,3 +39,20 @@ - (void)messageDismissed:(nonnull FIRInAppMessagingDisplayMessage *)inAppMessage @end // [END fiam_card_action_delegate] + +@interface ExampleCardActionDelegate: NSObject +@end + + +// [START fiam_card_action_delegate_bundles] +@implementation ExampleCardActionDelegate + +- (void)messageClicked:(nonnull FIRInAppMessagingDisplayMessage *)inAppMessage { + NSDictionary *appData = inAppMessage.appData; + NSLog(@"Message data: %@", appData); + // ... +} + +@end +// [END fiam_card_action_delegate_bundles] + diff --git a/inappmessaging/FIAMReference/FIAMReferenceSwift/AppDelegate.swift b/inappmessaging/FIAMReference/FIAMReferenceSwift/AppDelegate.swift index 05d94ae2..f867bf44 100644 --- a/inappmessaging/FIAMReference/FIAMReferenceSwift/AppDelegate.swift +++ b/inappmessaging/FIAMReference/FIAMReferenceSwift/AppDelegate.swift @@ -15,7 +15,7 @@ // import UIKit -import Firebase +import FirebaseCore import FirebaseInAppMessaging @UIApplicationMain diff --git a/inappmessaging/FIAMReference/FIAMReferenceSwift/CardActionFiamDelegate.swift b/inappmessaging/FIAMReference/FIAMReferenceSwift/CardActionFiamDelegate.swift index 0a777bea..6349e9c6 100644 --- a/inappmessaging/FIAMReference/FIAMReferenceSwift/CardActionFiamDelegate.swift +++ b/inappmessaging/FIAMReference/FIAMReferenceSwift/CardActionFiamDelegate.swift @@ -14,7 +14,6 @@ // limitations under the License. // -import Foundation import FirebaseInAppMessaging // [START fiam_card_action_delegate] @@ -25,7 +24,7 @@ class CardActionFiamDelegate : NSObject, InAppMessagingDisplayDelegate { } func messageDismissed(_ inAppMessage: InAppMessagingDisplayMessage, - dismissType: FIRInAppMessagingDismissType) { + dismissType: InAppMessagingDismissType) { // ... } @@ -39,3 +38,15 @@ class CardActionFiamDelegate : NSObject, InAppMessagingDisplayDelegate { } // [END fiam_card_action_delegate] + + +// [START fiam_card_action_delegate_bundles] +class CardActionDelegate : NSObject, InAppMessagingDisplayDelegate { + + func messageClicked(_ inAppMessage: InAppMessagingDisplayMessage) { + // Get data bundle from the inapp message + let appData = inAppMessage.appData + // ... + } +} +// [END fiam_card_action_delegate_bundles] diff --git a/inappmessaging/Podfile b/inappmessaging/Podfile deleted file mode 100644 index 30c17274..00000000 --- a/inappmessaging/Podfile +++ /dev/null @@ -1,14 +0,0 @@ -project 'FIAMReference/FIAMReference.xcodeproj/' - -platform :ios, '8.0' -use_frameworks! - -pod 'Firebase' -pod 'Firebase/InAppMessagingDisplay' - -target 'FIAMReference' do -end - -target 'FIAMReferenceSwift' do - use_frameworks! -end diff --git a/inappmessaging/Podfile.lock b/inappmessaging/Podfile.lock deleted file mode 100644 index 19ff0e08..00000000 --- a/inappmessaging/Podfile.lock +++ /dev/null @@ -1,118 +0,0 @@ -PODS: - - Firebase (6.8.0): - - Firebase/Core (= 6.8.0) - - Firebase/Core (6.8.0): - - Firebase/CoreOnly - - FirebaseAnalytics (= 6.1.1) - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/InAppMessagingDisplay (6.8.0): - - Firebase/CoreOnly - - FirebaseInAppMessagingDisplay (~> 0.15.4) - - FirebaseAnalytics (6.1.1): - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (~> 4.2) - - GoogleAppMeasurement (= 6.1.1) - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - FirebaseAnalyticsInterop (1.4.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseInAppMessaging (0.15.4): - - FirebaseAnalyticsInterop (~> 1.3) - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (~> 4.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - FirebaseInAppMessagingDisplay (0.15.4): - - FirebaseCore (~> 6.2) - - FirebaseInAppMessaging (>= 0.15.0) - - FirebaseInstanceID (4.2.4): - - FirebaseCore (~> 6.0) - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/UserDefaults (~> 6.0) - - GoogleAppMeasurement (6.1.1): - - GoogleUtilities/AppDelegateSwizzler (~> 6.0) - - GoogleUtilities/MethodSwizzler (~> 6.0) - - GoogleUtilities/Network (~> 6.0) - - "GoogleUtilities/NSData+zlib (~> 6.0)" - - nanopb (~> 0.3) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleUtilities/AppDelegateSwizzler (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/Network (6.2.5): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (6.2.5)" - - GoogleUtilities/Reachability (6.2.5): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (6.2.5): - - GoogleUtilities/Logger - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - -DEPENDENCIES: - - Firebase - - Firebase/InAppMessagingDisplay - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - Firebase - - FirebaseAnalytics - - FirebaseAnalyticsInterop - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - - FirebaseInAppMessaging - - FirebaseInAppMessagingDisplay - - FirebaseInstanceID - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleDataTransportCCTSupport - - GoogleUtilities - - nanopb - -SPEC CHECKSUMS: - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseAnalytics: 843c7f64a8f9c79f0d03281197ebe7bb1d58d477 - FirebaseAnalyticsInterop: d48b6ab67bcf016a05e55b71fc39c61c0cb6b7f3 - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseInAppMessaging: 54bcfe1cf2a48a8f5cdcac487a585c0e9b440d9a - FirebaseInAppMessagingDisplay: 66267552890939d394a961ee4cac5cdfd41f17bb - FirebaseInstanceID: 88932a31aba5a56cfd3a7541706436c71f7f4598 - GoogleAppMeasurement: 86a82f0e1f20b8eedf8e20326530138fd71409de - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - -PODFILE CHECKSUM: ee1d0162c4b114166138141943031a3ae6c42221 - -COCOAPODS: 1.7.5 diff --git a/installations/InstallationsSnippets.xcodeproj/project.pbxproj b/installations/InstallationsSnippets.xcodeproj/project.pbxproj new file mode 100644 index 00000000..c66d3adb --- /dev/null +++ b/installations/InstallationsSnippets.xcodeproj/project.pbxproj @@ -0,0 +1,523 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 8D7951C62D2C8A15000FD694 /* FirebaseInstallations in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951C52D2C8A15000FD694 /* FirebaseInstallations */; }; + 8DC74B6624A3DABF004C5F44 /* ObjCSnippets.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DC74B6524A3DABF004C5F44 /* ObjCSnippets.m */; }; + 8DF2A72824A3D78300737F46 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF2A72724A3D78300737F46 /* AppDelegate.swift */; }; + 8DF2A72A24A3D78300737F46 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF2A72924A3D78300737F46 /* SceneDelegate.swift */; }; + 8DF2A72C24A3D78300737F46 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF2A72B24A3D78300737F46 /* ContentView.swift */; }; + 8DF2A72E24A3D78300737F46 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8DF2A72D24A3D78300737F46 /* Assets.xcassets */; }; + 8DF2A73124A3D78300737F46 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8DF2A73024A3D78300737F46 /* Preview Assets.xcassets */; }; + 8DF2A73424A3D78300737F46 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8DF2A73224A3D78300737F46 /* LaunchScreen.storyboard */; }; + 8DF2A73F24A3D78300737F46 /* InstallationsSnippetsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DF2A73E24A3D78300737F46 /* InstallationsSnippetsTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8DF2A73B24A3D78300737F46 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8DF2A71C24A3D78300737F46 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DF2A72324A3D78300737F46; + remoteInfo = InstallationsSnippets; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 8DC74B6324A3DABE004C5F44 /* InstallationsSnippets-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "InstallationsSnippets-Bridging-Header.h"; sourceTree = ""; }; + 8DC74B6424A3DABF004C5F44 /* ObjCSnippets.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ObjCSnippets.h; sourceTree = ""; }; + 8DC74B6524A3DABF004C5F44 /* ObjCSnippets.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjCSnippets.m; sourceTree = ""; }; + 8DF2A72424A3D78300737F46 /* InstallationsSnippets.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InstallationsSnippets.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8DF2A72724A3D78300737F46 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 8DF2A72924A3D78300737F46 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 8DF2A72B24A3D78300737F46 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 8DF2A72D24A3D78300737F46 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8DF2A73024A3D78300737F46 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 8DF2A73324A3D78300737F46 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8DF2A73524A3D78300737F46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8DF2A73A24A3D78300737F46 /* InstallationsSnippetsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InstallationsSnippetsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8DF2A73E24A3D78300737F46 /* InstallationsSnippetsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationsSnippetsTests.swift; sourceTree = ""; }; + 8DF2A74024A3D78300737F46 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DF2A72124A3D78300737F46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7951C62D2C8A15000FD694 /* FirebaseInstallations in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DF2A73724A3D78300737F46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D7951C42D2C8A15000FD694 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 8DF2A71B24A3D78300737F46 = { + isa = PBXGroup; + children = ( + 8DF2A72624A3D78300737F46 /* InstallationsSnippets */, + 8DF2A73D24A3D78300737F46 /* InstallationsSnippetsTests */, + 8D7951C42D2C8A15000FD694 /* Frameworks */, + 8DF2A72524A3D78300737F46 /* Products */, + ); + sourceTree = ""; + }; + 8DF2A72524A3D78300737F46 /* Products */ = { + isa = PBXGroup; + children = ( + 8DF2A72424A3D78300737F46 /* InstallationsSnippets.app */, + 8DF2A73A24A3D78300737F46 /* InstallationsSnippetsTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 8DF2A72624A3D78300737F46 /* InstallationsSnippets */ = { + isa = PBXGroup; + children = ( + 8DF2A72724A3D78300737F46 /* AppDelegate.swift */, + 8DF2A72924A3D78300737F46 /* SceneDelegate.swift */, + 8DF2A72B24A3D78300737F46 /* ContentView.swift */, + 8DC74B6424A3DABF004C5F44 /* ObjCSnippets.h */, + 8DC74B6524A3DABF004C5F44 /* ObjCSnippets.m */, + 8DF2A72D24A3D78300737F46 /* Assets.xcassets */, + 8DF2A73224A3D78300737F46 /* LaunchScreen.storyboard */, + 8DF2A73524A3D78300737F46 /* Info.plist */, + 8DF2A72F24A3D78300737F46 /* Preview Content */, + 8DC74B6324A3DABE004C5F44 /* InstallationsSnippets-Bridging-Header.h */, + ); + path = InstallationsSnippets; + sourceTree = ""; + }; + 8DF2A72F24A3D78300737F46 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 8DF2A73024A3D78300737F46 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 8DF2A73D24A3D78300737F46 /* InstallationsSnippetsTests */ = { + isa = PBXGroup; + children = ( + 8DF2A73E24A3D78300737F46 /* InstallationsSnippetsTests.swift */, + 8DF2A74024A3D78300737F46 /* Info.plist */, + ); + path = InstallationsSnippetsTests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DF2A72324A3D78300737F46 /* InstallationsSnippets */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8DF2A74324A3D78300737F46 /* Build configuration list for PBXNativeTarget "InstallationsSnippets" */; + buildPhases = ( + 8DF2A72024A3D78300737F46 /* Sources */, + 8DF2A72124A3D78300737F46 /* Frameworks */, + 8DF2A72224A3D78300737F46 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = InstallationsSnippets; + productName = InstallationsSnippets; + productReference = 8DF2A72424A3D78300737F46 /* InstallationsSnippets.app */; + productType = "com.apple.product-type.application"; + }; + 8DF2A73924A3D78300737F46 /* InstallationsSnippetsTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8DF2A74624A3D78300737F46 /* Build configuration list for PBXNativeTarget "InstallationsSnippetsTests" */; + buildPhases = ( + 8DF2A73624A3D78300737F46 /* Sources */, + 8DF2A73724A3D78300737F46 /* Frameworks */, + 8DF2A73824A3D78300737F46 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8DF2A73C24A3D78300737F46 /* PBXTargetDependency */, + ); + name = InstallationsSnippetsTests; + productName = InstallationsSnippetsTests; + productReference = 8DF2A73A24A3D78300737F46 /* InstallationsSnippetsTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8DF2A71C24A3D78300737F46 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1140; + LastUpgradeCheck = 1610; + ORGANIZATIONNAME = Firebase; + TargetAttributes = { + 8DF2A72324A3D78300737F46 = { + CreatedOnToolsVersion = 11.4; + LastSwiftMigration = 1140; + }; + 8DF2A73924A3D78300737F46 = { + CreatedOnToolsVersion = 11.4; + TestTargetID = 8DF2A72324A3D78300737F46; + }; + }; + }; + buildConfigurationList = 8DF2A71F24A3D78300737F46 /* Build configuration list for PBXProject "InstallationsSnippets" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8DF2A71B24A3D78300737F46; + packageReferences = ( + 8D7951C32D2C89F0000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); + productRefGroup = 8DF2A72524A3D78300737F46 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DF2A72324A3D78300737F46 /* InstallationsSnippets */, + 8DF2A73924A3D78300737F46 /* InstallationsSnippetsTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DF2A72224A3D78300737F46 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DF2A73424A3D78300737F46 /* LaunchScreen.storyboard in Resources */, + 8DF2A73124A3D78300737F46 /* Preview Assets.xcassets in Resources */, + 8DF2A72E24A3D78300737F46 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DF2A73824A3D78300737F46 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DF2A72024A3D78300737F46 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DF2A72824A3D78300737F46 /* AppDelegate.swift in Sources */, + 8DC74B6624A3DABF004C5F44 /* ObjCSnippets.m in Sources */, + 8DF2A72A24A3D78300737F46 /* SceneDelegate.swift in Sources */, + 8DF2A72C24A3D78300737F46 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8DF2A73624A3D78300737F46 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DF2A73F24A3D78300737F46 /* InstallationsSnippetsTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 8DF2A73C24A3D78300737F46 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DF2A72324A3D78300737F46 /* InstallationsSnippets */; + targetProxy = 8DF2A73B24A3D78300737F46 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 8DF2A73224A3D78300737F46 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8DF2A73324A3D78300737F46 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 8DF2A74124A3D78300737F46 /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = 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; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8DF2A74224A3D78300737F46 /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = 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; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8DF2A74424A3D78300737F46 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"InstallationsSnippets/Preview Content\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = InstallationsSnippets/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.InstallationsSnippets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "InstallationsSnippets/InstallationsSnippets-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8DF2A74524A3D78300737F46 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"InstallationsSnippets/Preview Content\""; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = InstallationsSnippets/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.InstallationsSnippets; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "InstallationsSnippets/InstallationsSnippets-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 8DF2A74724A3D78300737F46 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = InstallationsSnippetsTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.InstallationsSnippetsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/InstallationsSnippets.app/InstallationsSnippets"; + }; + name = Debug; + }; + 8DF2A74824A3D78300737F46 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = InstallationsSnippetsTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.InstallationsSnippetsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/InstallationsSnippets.app/InstallationsSnippets"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8DF2A71F24A3D78300737F46 /* Build configuration list for PBXProject "InstallationsSnippets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DF2A74124A3D78300737F46 /* Debug */, + 8DF2A74224A3D78300737F46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8DF2A74324A3D78300737F46 /* Build configuration list for PBXNativeTarget "InstallationsSnippets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DF2A74424A3D78300737F46 /* Debug */, + 8DF2A74524A3D78300737F46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8DF2A74624A3D78300737F46 /* Build configuration list for PBXNativeTarget "InstallationsSnippetsTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8DF2A74724A3D78300737F46 /* Debug */, + 8DF2A74824A3D78300737F46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7951C32D2C89F0000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7951C52D2C8A15000FD694 /* FirebaseInstallations */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951C32D2C89F0000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseInstallations; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 8DF2A71C24A3D78300737F46 /* Project object */; +} diff --git a/installations/InstallationsSnippets/AppDelegate.swift b/installations/InstallationsSnippets/AppDelegate.swift new file mode 100644 index 00000000..f0d3cd6e --- /dev/null +++ b/installations/InstallationsSnippets/AppDelegate.swift @@ -0,0 +1,94 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseCore +import FirebaseInstallations + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FirebaseApp.configure() + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + var installationIDObserver: NSObjectProtocol? + func handleInstallationIDChange() { + // [START handle_installation_id_change] + installationIDObserver = NotificationCenter.default.addObserver( + forName: .InstallationIDDidChange, + object: nil, + queue: nil + ) { (notification) in + // Fetch new Installation ID + Task { + await self.fetchInstallationToken() + } + } + // [END handle_installation_id_change] + } + + func fetchInstallationID() async { + // [START fetch_installation_id] + do { + let id = try await Installations.installations().installationID() + print("Installation ID: \(id)") + } catch { + print("Error fetching id: \(error)") + } + // [END fetch_installation_id] + } + + func fetchInstallationToken() async { + // [START fetch_installation_token] + do { + let result = try await Installations.installations() + .authTokenForcingRefresh(true) + print("Installation auth token: \(result.authToken)") + } catch { + print("Error fetching token: \(error)") + } + // [END fetch_installation_token] + } + + func deleteInstallation() async { + // [START delete_installation] + do { + try await Installations.installations().delete() + print("Installation deleted"); + } catch { + print("Error deleting installation: \(error)") + } + // [END delete_installation] + } + +} + diff --git a/installations/InstallationsSnippets/Assets.xcassets/AppIcon.appiconset/Contents.json b/installations/InstallationsSnippets/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/installations/InstallationsSnippets/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/installations/InstallationsSnippets/Assets.xcassets/Contents.json b/installations/InstallationsSnippets/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/installations/InstallationsSnippets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/installations/InstallationsSnippets/Base.lproj/LaunchScreen.storyboard b/installations/InstallationsSnippets/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/installations/InstallationsSnippets/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installations/InstallationsSnippets/ContentView.swift b/installations/InstallationsSnippets/ContentView.swift new file mode 100644 index 00000000..29e56014 --- /dev/null +++ b/installations/InstallationsSnippets/ContentView.swift @@ -0,0 +1,29 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, World!") + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/installations/InstallationsSnippets/Info.plist b/installations/InstallationsSnippets/Info.plist new file mode 100644 index 00000000..9742bf0f --- /dev/null +++ b/installations/InstallationsSnippets/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/installations/InstallationsSnippets/InstallationsSnippets-Bridging-Header.h b/installations/InstallationsSnippets/InstallationsSnippets-Bridging-Header.h new file mode 100644 index 00000000..1b2cb5d6 --- /dev/null +++ b/installations/InstallationsSnippets/InstallationsSnippets-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/installations/InstallationsSnippets/ObjCSnippets.h b/installations/InstallationsSnippets/ObjCSnippets.h new file mode 100644 index 00000000..92d86163 --- /dev/null +++ b/installations/InstallationsSnippets/ObjCSnippets.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ObjCSnippets : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/installations/InstallationsSnippets/ObjCSnippets.m b/installations/InstallationsSnippets/ObjCSnippets.m new file mode 100644 index 00000000..3c8b80a5 --- /dev/null +++ b/installations/InstallationsSnippets/ObjCSnippets.m @@ -0,0 +1,78 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseInstallations; + +#import "ObjCSnippets.h" + +@interface ObjCSnippets () +@property(nonatomic, nullable) id installationIDObserver; +@end + +@implementation ObjCSnippets + +- (void)handleInstallationIDChange { + // [START handle_installation_id_change] + __weak __auto_type weakSelf = self; + self.installationIDObserver = [[NSNotificationCenter defaultCenter] + addObserverForName: FIRInstallationIDDidChangeNotification + object:nil + queue:nil + usingBlock:^(NSNotification * _Nonnull notification) { + // Fetch new Installation ID + [weakSelf fetchInstallationsID]; + }]; + // [END handle_installation_id_change] +} + +- (void)fetchInstallationsID { + // [START fetch_installation_id] + [[FIRInstallations installations] installationIDWithCompletion:^(NSString *identifier, NSError *error) { + if (error != nil) { + NSLog(@"Error fetching Installation ID %@", error); + return; + } + NSLog(@"Installation ID: %@", identifier); + }]; + // [END fetch_installation_id] +} + +- (void)fetchInstallationsToken { + // [START fetch_installation_token] + [[FIRInstallations installations] authTokenForcingRefresh:true + completion:^(FIRInstallationsAuthTokenResult *result, NSError *error) { + if (error != nil) { + NSLog(@"Error fetching Installation token %@", error); + return; + } + NSLog(@"Installation auth token: %@", [result authToken]); + }]; + // [END fetch_installation_token] +} + +- (void)deleteInstallations { + // [START delete_installation] + [[FIRInstallations installations] deleteWithCompletion:^(NSError *error) { + if (error != nil) { + NSLog(@"Error deleting Installation %@", error); + return; + } + NSLog(@"Installation deleted"); + }]; + // [END delete_installation] +} + +@end diff --git a/installations/InstallationsSnippets/Preview Content/Preview Assets.xcassets/Contents.json b/installations/InstallationsSnippets/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/installations/InstallationsSnippets/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/installations/InstallationsSnippets/SceneDelegate.swift b/installations/InstallationsSnippets/SceneDelegate.swift new file mode 100644 index 00000000..a85e0eb5 --- /dev/null +++ b/installations/InstallationsSnippets/SceneDelegate.swift @@ -0,0 +1,72 @@ +// +// Copyright (c) 2020 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView() + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/installations/InstallationsSnippetsTests/Info.plist b/installations/InstallationsSnippetsTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/installations/InstallationsSnippetsTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/installations/InstallationsSnippetsTests/InstallationsSnippetsTests.swift b/installations/InstallationsSnippetsTests/InstallationsSnippetsTests.swift new file mode 100644 index 00000000..acbaf080 --- /dev/null +++ b/installations/InstallationsSnippetsTests/InstallationsSnippetsTests.swift @@ -0,0 +1,34 @@ +// +// InstallationsSnippetsTests.swift +// InstallationsSnippetsTests +// +// Created by Morgan Chen on 6/24/20. +// Copyright © 2020 Firebase. All rights reserved. +// + +import XCTest +@testable import InstallationsSnippets + +class InstallationsSnippetsTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/invites/InvitesExample.xcodeproj/project.pbxproj b/invites/InvitesExample.xcodeproj/project.pbxproj deleted file mode 100644 index 9f49bc09..00000000 --- a/invites/InvitesExample.xcodeproj/project.pbxproj +++ /dev/null @@ -1,920 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1073478F20315A0F004A66D1 /* InvitesExampleSwiftUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1073478E20315A0F004A66D1 /* InvitesExampleSwiftUITests.swift */; }; - 1073485320333BDE004A66D1 /* InvitesExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1073485220333BDE004A66D1 /* InvitesExampleUITests.m */; }; - 4B1A6C781B0A752100E1EDA2 /* InvitesExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B1A6C771B0A752100E1EDA2 /* InvitesExampleTests.m */; }; - 4B1F2A361B01375C00F69A92 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1F2A351B01375C00F69A92 /* SignInViewController.swift */; }; - 4B97B2591AD7145E0036DD6C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B97B2581AD7145E0036DD6C /* AppDelegate.swift */; }; - 4B97B25B1AD7145E0036DD6C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B97B25A1AD7145E0036DD6C /* ViewController.swift */; }; - 4BA30A161AE81F5A00E5CDE9 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99E21ACF472000C4DC2C /* SystemConfiguration.framework */; }; - 4BA30A171AE81F5A00E5CDE9 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99E01ACF470F00C4DC2C /* MessageUI.framework */; }; - 4BA30A181AE81F5A00E5CDE9 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99DE1ACF46FA00C4DC2C /* CoreMotion.framework */; }; - 4BA30A191AE81F5A00E5CDE9 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99DC1ACF46F400C4DC2C /* CoreLocation.framework */; }; - 4BA30A1A1AE81F5A00E5CDE9 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99DA1ACF46EC00C4DC2C /* AssetsLibrary.framework */; }; - 4BA30A1B1AE81F5A00E5CDE9 /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99D81ACF46E500C4DC2C /* AddressBook.framework */; }; - 4BC032E41ACF33B70092326A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC032E31ACF33B70092326A /* main.m */; }; - 4BC032E71ACF33B70092326A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC032E61ACF33B70092326A /* AppDelegate.m */; }; - 4BC032EA1ACF33B70092326A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BC032E91ACF33B70092326A /* ViewController.m */; }; - 4BCF1C421AFBFA8D0039B15C /* SignInViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BCF1C411AFBFA8D0039B15C /* SignInViewController.m */; }; - 4BDB99D91ACF46E500C4DC2C /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99D81ACF46E500C4DC2C /* AddressBook.framework */; }; - 4BDB99DB1ACF46EC00C4DC2C /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99DA1ACF46EC00C4DC2C /* AssetsLibrary.framework */; }; - 4BDB99DD1ACF46F400C4DC2C /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99DC1ACF46F400C4DC2C /* CoreLocation.framework */; }; - 4BDB99DF1ACF46FA00C4DC2C /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99DE1ACF46FA00C4DC2C /* CoreMotion.framework */; }; - 4BDB99E11ACF470F00C4DC2C /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99E01ACF470F00C4DC2C /* MessageUI.framework */; }; - 4BDB99E31ACF472000C4DC2C /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BDB99E21ACF472000C4DC2C /* SystemConfiguration.framework */; }; - EF4BB4D81B05B454003A1AC6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B5CBBFB1AE9DBF700D822CE /* Main.storyboard */; }; - EFB292011B0F1F430021C8AC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EFB291FD1B0F1F430021C8AC /* Images.xcassets */; }; - EFB292021B0F1F430021C8AC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EFB291FD1B0F1F430021C8AC /* Images.xcassets */; }; - EFB292031B0F1F430021C8AC /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = EFB291FE1B0F1F430021C8AC /* LaunchScreen.xib */; }; - EFB292041B0F1F430021C8AC /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = EFB291FE1B0F1F430021C8AC /* LaunchScreen.xib */; }; - EFB292061B0F20100021C8AC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B5CBBFB1AE9DBF700D822CE /* Main.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 1073479120315A0F004A66D1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4BC032D61ACF33B70092326A /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4B97B2521AD7145E0036DD6C; - remoteInfo = InvitesExampleSwift; - }; - 1073485520333BDE004A66D1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4BC032D61ACF33B70092326A /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4BC032DD1ACF33B70092326A; - remoteInfo = InvitesExample; - }; - 4B1A6C791B0A752100E1EDA2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 4BC032D61ACF33B70092326A /* Project object */; - proxyType = 1; - remoteGlobalIDString = 4BC032DD1ACF33B70092326A; - remoteInfo = AppInvitesExample; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 105A4F501D91C5A500B5DE54 /* InvitesExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = InvitesExample.entitlements; sourceTree = ""; }; - 1073478C20315A0F004A66D1 /* InvitesExampleSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InvitesExampleSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 1073478E20315A0F004A66D1 /* InvitesExampleSwiftUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesExampleSwiftUITests.swift; sourceTree = ""; }; - 1073479020315A0F004A66D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 1073485020333BDE004A66D1 /* InvitesExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InvitesExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 1073485220333BDE004A66D1 /* InvitesExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InvitesExampleUITests.m; sourceTree = ""; }; - 1073485420333BDE004A66D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 108F89D31D47B0090009615B /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 4B1A6C731B0A752100E1EDA2 /* InvitesExample.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = InvitesExample.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 4B1A6C771B0A752100E1EDA2 /* InvitesExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InvitesExampleTests.m; sourceTree = ""; }; - 4B1F2A351B01375C00F69A92 /* SignInViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignInViewController.swift; sourceTree = ""; }; - 4B5CBBFC1AE9DBF700D822CE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 4B97B2531AD7145E0036DD6C /* InvitesExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InvitesExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4B97B2581AD7145E0036DD6C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 4B97B25A1AD7145E0036DD6C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; - 4B97B25F1AD7145E0036DD6C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - 4BC032DE1ACF33B70092326A /* InvitesExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InvitesExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4BC032E31ACF33B70092326A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 4BC032E51ACF33B70092326A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 4BC032E61ACF33B70092326A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 4BC032E81ACF33B70092326A /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - 4BC032E91ACF33B70092326A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; - 4BCF1C411AFBFA8D0039B15C /* SignInViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignInViewController.m; sourceTree = ""; }; - 4BCF1C431AFC01A40039B15C /* SignInViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignInViewController.h; sourceTree = ""; }; - 4BDB99D81ACF46E500C4DC2C /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; - 4BDB99DA1ACF46EC00C4DC2C /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; - 4BDB99DC1ACF46F400C4DC2C /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; - 4BDB99DE1ACF46FA00C4DC2C /* CoreMotion.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMotion.framework; path = System/Library/Frameworks/CoreMotion.framework; sourceTree = SDKROOT; }; - 4BDB99E01ACF470F00C4DC2C /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; - 4BDB99E21ACF472000C4DC2C /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; - EFB291FD1B0F1F430021C8AC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; - EFB291FE1B0F1F430021C8AC /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 1073478920315A0F004A66D1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 1073484D20333BDE004A66D1 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4B1A6C701B0A752100E1EDA2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4B97B2501AD7145E0036DD6C /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4BA30A161AE81F5A00E5CDE9 /* SystemConfiguration.framework in Frameworks */, - 4BA30A171AE81F5A00E5CDE9 /* MessageUI.framework in Frameworks */, - 4BA30A181AE81F5A00E5CDE9 /* CoreMotion.framework in Frameworks */, - 4BA30A191AE81F5A00E5CDE9 /* CoreLocation.framework in Frameworks */, - 4BA30A1A1AE81F5A00E5CDE9 /* AssetsLibrary.framework in Frameworks */, - 4BA30A1B1AE81F5A00E5CDE9 /* AddressBook.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4BC032DB1ACF33B70092326A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4BDB99E31ACF472000C4DC2C /* SystemConfiguration.framework in Frameworks */, - 4BDB99E11ACF470F00C4DC2C /* MessageUI.framework in Frameworks */, - 4BDB99DF1ACF46FA00C4DC2C /* CoreMotion.framework in Frameworks */, - 4BDB99DD1ACF46F400C4DC2C /* CoreLocation.framework in Frameworks */, - 4BDB99DB1ACF46EC00C4DC2C /* AssetsLibrary.framework in Frameworks */, - 4BDB99D91ACF46E500C4DC2C /* AddressBook.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 1073478D20315A0F004A66D1 /* InvitesExampleSwiftUITests */ = { - isa = PBXGroup; - children = ( - 1073478E20315A0F004A66D1 /* InvitesExampleSwiftUITests.swift */, - 1073479020315A0F004A66D1 /* Info.plist */, - ); - path = InvitesExampleSwiftUITests; - sourceTree = ""; - }; - 1073485120333BDE004A66D1 /* InvitesExampleUITests */ = { - isa = PBXGroup; - children = ( - 1073485220333BDE004A66D1 /* InvitesExampleUITests.m */, - 1073485420333BDE004A66D1 /* Info.plist */, - ); - path = InvitesExampleUITests; - sourceTree = ""; - }; - 4B1A6C741B0A752100E1EDA2 /* InvitesExampleTests */ = { - isa = PBXGroup; - children = ( - 4B1A6C771B0A752100E1EDA2 /* InvitesExampleTests.m */, - ); - path = InvitesExampleTests; - sourceTree = ""; - }; - 4B97B2551AD7145E0036DD6C /* InvitesExampleSwift */ = { - isa = PBXGroup; - children = ( - 4B97B2581AD7145E0036DD6C /* AppDelegate.swift */, - 4B1F2A351B01375C00F69A92 /* SignInViewController.swift */, - 4B97B25A1AD7145E0036DD6C /* ViewController.swift */, - 4B97B25F1AD7145E0036DD6C /* Images.xcassets */, - ); - path = InvitesExampleSwift; - sourceTree = ""; - }; - 4BC032D51ACF33B70092326A = { - isa = PBXGroup; - children = ( - 4BC032E01ACF33B70092326A /* InvitesExample */, - 4B97B2551AD7145E0036DD6C /* InvitesExampleSwift */, - 4B1A6C741B0A752100E1EDA2 /* InvitesExampleTests */, - 1073478D20315A0F004A66D1 /* InvitesExampleSwiftUITests */, - 1073485120333BDE004A66D1 /* InvitesExampleUITests */, - 4BC032DF1ACF33B70092326A /* Products */, - E27D599993EDB54E213FA076 /* Frameworks */, - ); - sourceTree = ""; - }; - 4BC032DF1ACF33B70092326A /* Products */ = { - isa = PBXGroup; - children = ( - 4BC032DE1ACF33B70092326A /* InvitesExample.app */, - 4B97B2531AD7145E0036DD6C /* InvitesExample.app */, - 4B1A6C731B0A752100E1EDA2 /* InvitesExample.xctest */, - 1073478C20315A0F004A66D1 /* InvitesExampleSwiftUITests.xctest */, - 1073485020333BDE004A66D1 /* InvitesExampleUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 4BC032E01ACF33B70092326A /* InvitesExample */ = { - isa = PBXGroup; - children = ( - 105A4F501D91C5A500B5DE54 /* InvitesExample.entitlements */, - 4BC032E51ACF33B70092326A /* AppDelegate.h */, - 4BC032E61ACF33B70092326A /* AppDelegate.m */, - 4BCF1C431AFC01A40039B15C /* SignInViewController.h */, - 4BCF1C411AFBFA8D0039B15C /* SignInViewController.m */, - 4BC032E81ACF33B70092326A /* ViewController.h */, - 4BC032E91ACF33B70092326A /* ViewController.m */, - 4BC032E11ACF33B70092326A /* Supporting Files */, - ); - path = InvitesExample; - sourceTree = ""; - }; - 4BC032E11ACF33B70092326A /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 108F89D31D47B0090009615B /* Info.plist */, - EFB291FB1B0F1F430021C8AC /* shared */, - 4B5CBBFB1AE9DBF700D822CE /* Main.storyboard */, - 4BC032E31ACF33B70092326A /* main.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - E27D599993EDB54E213FA076 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 4BDB99E21ACF472000C4DC2C /* SystemConfiguration.framework */, - 4BDB99E01ACF470F00C4DC2C /* MessageUI.framework */, - 4BDB99DE1ACF46FA00C4DC2C /* CoreMotion.framework */, - 4BDB99DC1ACF46F400C4DC2C /* CoreLocation.framework */, - 4BDB99DA1ACF46EC00C4DC2C /* AssetsLibrary.framework */, - 4BDB99D81ACF46E500C4DC2C /* AddressBook.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - EFB291FB1B0F1F430021C8AC /* shared */ = { - isa = PBXGroup; - children = ( - EFB291FD1B0F1F430021C8AC /* Images.xcassets */, - EFB291FE1B0F1F430021C8AC /* LaunchScreen.xib */, - ); - name = shared; - path = ../../shared; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 1073478B20315A0F004A66D1 /* InvitesExampleSwiftUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1073479320315A0F004A66D1 /* Build configuration list for PBXNativeTarget "InvitesExampleSwiftUITests" */; - buildPhases = ( - 1073478820315A0F004A66D1 /* Sources */, - 1073478920315A0F004A66D1 /* Frameworks */, - 1073478A20315A0F004A66D1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 1073479220315A0F004A66D1 /* PBXTargetDependency */, - ); - name = InvitesExampleSwiftUITests; - productName = InvitesExampleSwiftUITests; - productReference = 1073478C20315A0F004A66D1 /* InvitesExampleSwiftUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; - 1073484F20333BDE004A66D1 /* InvitesExampleUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1073485720333BDE004A66D1 /* Build configuration list for PBXNativeTarget "InvitesExampleUITests" */; - buildPhases = ( - 1073484C20333BDE004A66D1 /* Sources */, - 1073484D20333BDE004A66D1 /* Frameworks */, - 1073484E20333BDE004A66D1 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 1073485620333BDE004A66D1 /* PBXTargetDependency */, - ); - name = InvitesExampleUITests; - productName = InvitesExampleUITests; - productReference = 1073485020333BDE004A66D1 /* InvitesExampleUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; - 4B1A6C721B0A752100E1EDA2 /* InvitesExampleTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4B1A6C7D1B0A752100E1EDA2 /* Build configuration list for PBXNativeTarget "InvitesExampleTests" */; - buildPhases = ( - 4B1A6C6F1B0A752100E1EDA2 /* Sources */, - 4B1A6C701B0A752100E1EDA2 /* Frameworks */, - 4B1A6C711B0A752100E1EDA2 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 4B1A6C7A1B0A752100E1EDA2 /* PBXTargetDependency */, - ); - name = InvitesExampleTests; - productName = AppInvitesExampleTests; - productReference = 4B1A6C731B0A752100E1EDA2 /* InvitesExample.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 4B97B2521AD7145E0036DD6C /* InvitesExampleSwift */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4B97B2721AD7145E0036DD6C /* Build configuration list for PBXNativeTarget "InvitesExampleSwift" */; - buildPhases = ( - 4B97B24F1AD7145E0036DD6C /* Sources */, - 4B97B2511AD7145E0036DD6C /* Resources */, - 4B97B2501AD7145E0036DD6C /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = InvitesExampleSwift; - productName = AppInvitesExampleSwift; - productReference = 4B97B2531AD7145E0036DD6C /* InvitesExample.app */; - productType = "com.apple.product-type.application"; - }; - 4BC032DD1ACF33B70092326A /* InvitesExample */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4BC033011ACF33B70092326A /* Build configuration list for PBXNativeTarget "InvitesExample" */; - buildPhases = ( - 4BC032DA1ACF33B70092326A /* Sources */, - 4BC032DC1ACF33B70092326A /* Resources */, - 4BC032DB1ACF33B70092326A /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = InvitesExample; - productName = AppInvitesExample; - productReference = 4BC032DE1ACF33B70092326A /* InvitesExample.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 4BC032D61ACF33B70092326A /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0930; - ORGANIZATIONNAME = "Google Inc."; - TargetAttributes = { - 1073478B20315A0F004A66D1 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1000; - ProvisioningStyle = Automatic; - TestTargetID = 4B97B2521AD7145E0036DD6C; - }; - 1073484F20333BDE004A66D1 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Automatic; - TestTargetID = 4BC032DD1ACF33B70092326A; - }; - 4B1A6C721B0A752100E1EDA2 = { - CreatedOnToolsVersion = 6.3.1; - LastSwiftMigration = 0800; - ProvisioningStyle = Automatic; - TestTargetID = 4BC032DD1ACF33B70092326A; - }; - 4B97B2521AD7145E0036DD6C = { - LastSwiftMigration = 1000; - ProvisioningStyle = Automatic; - }; - 4BC032DD1ACF33B70092326A = { - CreatedOnToolsVersion = 6.2; - LastSwiftMigration = 0800; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 4BC032D91ACF33B70092326A /* Build configuration list for PBXProject "InvitesExample" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 4BC032D51ACF33B70092326A; - productRefGroup = 4BC032DF1ACF33B70092326A /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 4BC032DD1ACF33B70092326A /* InvitesExample */, - 4B97B2521AD7145E0036DD6C /* InvitesExampleSwift */, - 4B1A6C721B0A752100E1EDA2 /* InvitesExampleTests */, - 1073478B20315A0F004A66D1 /* InvitesExampleSwiftUITests */, - 1073484F20333BDE004A66D1 /* InvitesExampleUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 1073478A20315A0F004A66D1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 1073484E20333BDE004A66D1 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4B1A6C711B0A752100E1EDA2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4B97B2511AD7145E0036DD6C /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - EFB292061B0F20100021C8AC /* Main.storyboard in Resources */, - EFB292041B0F1F430021C8AC /* LaunchScreen.xib in Resources */, - EFB292021B0F1F430021C8AC /* Images.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4BC032DC1ACF33B70092326A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - EF4BB4D81B05B454003A1AC6 /* Main.storyboard in Resources */, - EFB292011B0F1F430021C8AC /* Images.xcassets in Resources */, - EFB292031B0F1F430021C8AC /* LaunchScreen.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 1073478820315A0F004A66D1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 1073478F20315A0F004A66D1 /* InvitesExampleSwiftUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 1073484C20333BDE004A66D1 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 1073485320333BDE004A66D1 /* InvitesExampleUITests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4B1A6C6F1B0A752100E1EDA2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4B1A6C781B0A752100E1EDA2 /* InvitesExampleTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4B97B24F1AD7145E0036DD6C /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4B97B25B1AD7145E0036DD6C /* ViewController.swift in Sources */, - 4B97B2591AD7145E0036DD6C /* AppDelegate.swift in Sources */, - 4B1F2A361B01375C00F69A92 /* SignInViewController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4BC032DA1ACF33B70092326A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4BCF1C421AFBFA8D0039B15C /* SignInViewController.m in Sources */, - 4BC032EA1ACF33B70092326A /* ViewController.m in Sources */, - 4BC032E71ACF33B70092326A /* AppDelegate.m in Sources */, - 4BC032E41ACF33B70092326A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 1073479220315A0F004A66D1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4B97B2521AD7145E0036DD6C /* InvitesExampleSwift */; - targetProxy = 1073479120315A0F004A66D1 /* PBXContainerItemProxy */; - }; - 1073485620333BDE004A66D1 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4BC032DD1ACF33B70092326A /* InvitesExample */; - targetProxy = 1073485520333BDE004A66D1 /* PBXContainerItemProxy */; - }; - 4B1A6C7A1B0A752100E1EDA2 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 4BC032DD1ACF33B70092326A /* InvitesExample */; - targetProxy = 4B1A6C791B0A752100E1EDA2 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 4B5CBBFB1AE9DBF700D822CE /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4B5CBBFC1AE9DBF700D822CE /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 1073479420315A0F004A66D1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = InvitesExampleSwiftUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExampleSwiftUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = InvitesExampleSwift; - }; - name = Debug; - }; - 1073479520315A0F004A66D1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = InvitesExampleSwiftUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExampleSwiftUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = InvitesExampleSwift; - }; - name = Release; - }; - 1073485820333BDE004A66D1 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = dwarf; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = InvitesExampleUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExampleUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = InvitesExample; - }; - name = Debug; - }; - 1073485920333BDE004A66D1 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; - CODE_SIGN_STYLE = Automatic; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = gnu11; - INFOPLIST_FILE = InvitesExampleUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExampleUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = InvitesExample; - }; - name = Release; - }; - 4B1A6C7B1B0A752100E1EDA2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = InvitesExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.google.samples.quickstart.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = InvitesExample; - PROVISIONING_PROFILE_SPECIFIER = ""; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/InvitesExample.app/InvitesExample"; - }; - name = Debug; - }; - 4B1A6C7C1B0A752100E1EDA2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = ""; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = InvitesExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.google.samples.quickstart.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = InvitesExample; - PROVISIONING_PROFILE_SPECIFIER = ""; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/InvitesExample.app/InvitesExample"; - }; - name = Release; - }; - 4B97B2731AD7145E0036DD6C /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = InvitesExample/InvitesExample.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = InvitesExample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExample; - PRODUCT_NAME = InvitesExample; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - VALID_ARCHS = "armv7 armv7s arm64"; - }; - name = Debug; - }; - 4B97B2741AD7145E0036DD6C /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = InvitesExample/InvitesExample.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = InvitesExample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExample; - PRODUCT_NAME = InvitesExample; - PROVISIONING_PROFILE_SPECIFIER = ""; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - VALID_ARCHS = "armv7 armv7s arm64"; - }; - name = Release; - }; - 4BC032FF1ACF33B70092326A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_BITCODE = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - 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; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 4BC033001ACF33B70092326A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_BITCODE = NO; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - 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; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 4BC033021ACF33B70092326A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_ENTITLEMENTS = InvitesExample/InvitesExample.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = InvitesExample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExample; - PRODUCT_NAME = InvitesExample; - PROVISIONING_PROFILE = ""; - PROVISIONING_PROFILE_SPECIFIER = ""; - }; - name = Debug; - }; - 4BC033031ACF33B70092326A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_ENTITLEMENTS = InvitesExample/InvitesExample.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = ""; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = InvitesExample/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.InvitesExample; - PRODUCT_NAME = InvitesExample; - PROVISIONING_PROFILE_SPECIFIER = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 1073479320315A0F004A66D1 /* Build configuration list for PBXNativeTarget "InvitesExampleSwiftUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1073479420315A0F004A66D1 /* Debug */, - 1073479520315A0F004A66D1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1073485720333BDE004A66D1 /* Build configuration list for PBXNativeTarget "InvitesExampleUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 1073485820333BDE004A66D1 /* Debug */, - 1073485920333BDE004A66D1 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4B1A6C7D1B0A752100E1EDA2 /* Build configuration list for PBXNativeTarget "InvitesExampleTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4B1A6C7B1B0A752100E1EDA2 /* Debug */, - 4B1A6C7C1B0A752100E1EDA2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4B97B2721AD7145E0036DD6C /* Build configuration list for PBXNativeTarget "InvitesExampleSwift" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4B97B2731AD7145E0036DD6C /* Debug */, - 4B97B2741AD7145E0036DD6C /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4BC032D91ACF33B70092326A /* Build configuration list for PBXProject "InvitesExample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4BC032FF1ACF33B70092326A /* Debug */, - 4BC033001ACF33B70092326A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4BC033011ACF33B70092326A /* Build configuration list for PBXNativeTarget "InvitesExample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4BC033021ACF33B70092326A /* Debug */, - 4BC033031ACF33B70092326A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 4BC032D61ACF33B70092326A /* Project object */; -} diff --git a/invites/InvitesExample/AppDelegate.m b/invites/InvitesExample/AppDelegate.m deleted file mode 100644 index 71a3eab2..00000000 --- a/invites/InvitesExample/AppDelegate.m +++ /dev/null @@ -1,102 +0,0 @@ -// -// Copyright (c) Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "AppDelegate.h" -@import Firebase; -@import GoogleSignIn; - -@implementation AppDelegate - -// [START configure] -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // Use Firebase library to configure APIs - [FIRApp configure]; - return YES; -} -// [END configure] - -// [START openurl] -- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url - options:(NSDictionary *)options { - return [self application:app - openURL:url - sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] - annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; -} - -- (BOOL)application:(UIApplication *)application - openURL:(NSURL *)url - sourceApplication:(NSString *)sourceApplication - annotation:(id)annotation { - if ([[GIDSignIn sharedInstance] handleURL:url - sourceApplication:sourceApplication - annotation:annotation]) { - return YES; - } - // Handle App Invite requests - return [FIRInvites handleUniversalLink:url - completion:^(FIRReceivedInvite * _Nullable receivedInvite, - NSError * _Nullable error) { - // [START_EXCLUDE] - if (receivedInvite) { - [self showAlertViewWithInvite:receivedInvite]; - } - // [END_EXCLUDE] - }]; -} -// [END openurl] - -// [START continueuseractivity] -- (BOOL)application:(UIApplication *)application -continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler: -#if defined(__IPHONE_12_0) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0) -(nonnull void (^)(NSArray> *_Nullable))restorationHandler { -#else - (nonnull void (^)(NSArray *_Nullable))restorationHandler { -#endif // __IPHONE_12_0 - // Handle App Invite requests - return [FIRInvites handleUniversalLink:userActivity.webpageURL - completion:^(FIRReceivedInvite * _Nullable receivedInvite, - NSError * _Nullable error) { - // [START_EXCLUDE] - if (receivedInvite) { - [self showAlertViewWithInvite:receivedInvite]; - } - // [END_EXCLUDE] - }]; -} -// [END continueuseractivity] - -- (void)showAlertViewWithInvite:(FIRReceivedInvite *)invite { - UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:nil]; - NSString *matchType = invite.matchType == FIRReceivedInviteMatchTypeWeak ? @"weak" : @"strong"; - NSString *message = - [NSString stringWithFormat:@"Invite ID: %@\nDeep-link: %@\nMatch Type: %@", - invite.inviteId, invite.deepLink, matchType]; - - UIAlertController *alertController = - [UIAlertController alertControllerWithTitle:@"Invite" - message:message - preferredStyle:UIAlertControllerStyleAlert]; - [alertController addAction:okAction]; - [self.window.rootViewController presentViewController:alertController - animated:YES - completion:nil]; -} -@end diff --git a/invites/InvitesExample/Base.lproj/Main.storyboard b/invites/InvitesExample/Base.lproj/Main.storyboard deleted file mode 100644 index e94cae05..00000000 --- a/invites/InvitesExample/Base.lproj/Main.storyboard +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - - - - - - - Roboto-Bold - - - Roboto-Regular - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/invites/InvitesExample/SignInViewController.m b/invites/InvitesExample/SignInViewController.m deleted file mode 100644 index d0a7857b..00000000 --- a/invites/InvitesExample/SignInViewController.m +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright (c) Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "SignInViewController.h" -#import "ViewController.h" -@import GoogleSignIn; -@import Firebase; - - -@interface SignInViewController () -@property(weak, nonatomic) IBOutlet GIDSignInButton *signInButton; -@property(weak, nonatomic) IBOutlet UILabel *bgText; -@end - -@implementation SignInViewController - -- (void)viewDidAppear:(BOOL)animated { - [super viewDidAppear:animated]; - - _bgText.text = @"Invites\niOS demo"; - - [GIDSignIn sharedInstance].clientID = [FIRApp defaultApp].options.clientID; - - // Sign the user in automatically - [GIDSignIn sharedInstance].uiDelegate = self; - [[GIDSignIn sharedInstance] signInSilently]; - - // TODO(developer): Configure the sign-in button look/feel - [GIDSignIn sharedInstance].delegate = self; -} - -- (void)signIn:(GIDSignIn *)signIn - didSignInForUser:(GIDGoogleUser *)user - withError:(NSError *)error { - if (error == nil) { - // User Successfully signed in. - // TODO: Remove async after, when GIDSignIn is started getting called after dissmissVC - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.35 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self performSegueWithIdentifier:@"SignedInScreen" sender:self]; - }); - } else { - // Something went wrong; for example, the user could haved clicked cancel. - NSLog(@"%@", error.localizedDescription); - } -} - -- (UIStatusBarStyle)preferredStatusBarStyle { - return UIStatusBarStyleLightContent; -} - -- (IBAction)unwindToSignIn:(UIStoryboardSegue *)sender { - [GIDSignIn sharedInstance].delegate = self; -} -@end diff --git a/invites/InvitesExample/ViewController.m b/invites/InvitesExample/ViewController.m deleted file mode 100644 index e6b6e70a..00000000 --- a/invites/InvitesExample/ViewController.m +++ /dev/null @@ -1,172 +0,0 @@ -// -// Copyright (c) Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import "ViewController.h" -@import GoogleSignIn; -@import Firebase; - -// [START viewcontroller_interfaces] -@interface ViewController () -// [END viewcontroller_interfaces] -// [START viewcontroller_vars] -@property(weak, nonatomic) IBOutlet GIDSignInButton *signInButton; -@property(weak, nonatomic) IBOutlet UIButton *signOutButton; -@property(weak, nonatomic) IBOutlet UIButton *disconnectButton; -@property(weak, nonatomic) IBOutlet UIButton *inviteButton; -@property(weak, nonatomic) IBOutlet UILabel *statusText; -// [END viewcontroler_vars] -@end - -@implementation ViewController - -// [START viewdidload] -- (void)viewDidLoad { - [super viewDidLoad]; - - // TODO(developer) Configure the sign-in button look/feel - [GIDSignIn sharedInstance].delegate = self; - [GIDSignIn sharedInstance].uiDelegate = self; - - // Sign in automatically. - [[GIDSignIn sharedInstance] signInSilently]; - - [self setupUI]; - [self toggleAuthUI]; -} -// [END viewdidload] - -- (void)setupUI { - float grayValue = (204.0 / 255); - UIColor *grayColor = [UIColor colorWithRed:grayValue green:grayValue blue:grayValue alpha:1.0]; - - _inviteButton.layer.cornerRadius = 3; - _inviteButton.layer.shadowRadius = 1; - _inviteButton.layer.shadowOffset = CGSizeMake(0, 0.5); - _inviteButton.layer.shadowColor = [UIColor blackColor].CGColor; - _inviteButton.layer.shadowOpacity = .7; - - _signOutButton.layer.borderWidth = .5; - _signOutButton.layer.borderColor = grayColor.CGColor; - _signOutButton.layer.cornerRadius = 2; - _signOutButton.layer.shadowRadius = .5; - _signOutButton.layer.shadowOffset = CGSizeMake(0, 0.5); - _signOutButton.layer.shadowColor = [UIColor blackColor].CGColor; - _signOutButton.layer.shadowOpacity = .4; - - _disconnectButton.layer.borderWidth = .5; - _disconnectButton.layer.borderColor = grayColor.CGColor; - _disconnectButton.layer.cornerRadius = 2; - _disconnectButton.layer.shadowRadius = .5; - _disconnectButton.layer.shadowOffset = CGSizeMake(0, 0.5); - _disconnectButton.layer.shadowColor = [UIColor blackColor].CGColor; - _disconnectButton.layer.shadowOpacity = .4; -} - -// [START signin_handler] -- (void)signIn:(GIDSignIn *)signIn - didSignInForUser:(GIDGoogleUser *)user - withError:(NSError *)error { - // Perform any operations on signed in user here. - if (user.profile.name) { - _statusText.text = [NSString stringWithFormat:@"Signed in as %@", user.profile.name]; - } else { - _statusText.text = @"Signed in, profile name is not set"; - } - [self toggleAuthUI]; -} -// [END signin_handler] - -// [START disconnect_handler] -- (void)signIn:(GIDSignIn *)signIn - didDisconnectWithUser:(GIDGoogleUser *)user - withError:(NSError *)error { - // Perform any operations when the user disconnects from app here. - _statusText.text = @"Disconnected user"; - [self toggleAuthUI]; -} -// [END disconnect_handler] - -// [START signout_tapped] -- (IBAction)signOutTapped:(id)sender { - [[GIDSignIn sharedInstance] signOut]; - _statusText.text = @"Signed out"; - [self toggleAuthUI]; -} -// [END signout_tapped] - -// [START disconnect_tapped] -- (IBAction)disconnectTapped:(id)sender { - [[GIDSignIn sharedInstance] disconnect]; -} -// [END disconnect_tapped] - -// [START invite_tapped] -- (IBAction)inviteTapped:(id)sender { - id inviteDialog = [FIRInvites inviteDialog]; - [inviteDialog setInviteDelegate:self]; - - // NOTE: You must have the App Store ID set in your developer console project - // in order for invitations to successfully be sent. - NSString *message = - [NSString stringWithFormat:@"Try this out!\n -%@", - [GIDSignIn sharedInstance].currentUser.profile.name]; - - // A message hint for the dialog. Note this manifests differently depending on the - // received invitation type. For example, in an email invite this appears as the subject. - [inviteDialog setMessage:message]; - - // Title for the dialog, this is what the user sees before sending the invites. - [inviteDialog setTitle:@"Invites Example"]; - [inviteDialog setDeepLink:@"app_url"]; - [inviteDialog setCallToActionText:@"Install!"]; - [inviteDialog setCustomImage:@"https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"]; - [inviteDialog open]; -} -// [END invite_tapped] - -// [START invite_finished] -- (void)inviteFinishedWithInvitations:(NSArray *)invitationIds error:(NSError *)error { - if (error) { - NSLog(@"%@", error.localizedDescription); - } else { - NSLog(@"%li invites sent", invitationIds.count); - } -} -// [END invite_finished] - -// [START toggle_auth] -- (void)toggleAuthUI { - if ([GIDSignIn sharedInstance].currentUser.authentication == nil) { - // Not signed in - _signInButton.enabled = YES; - _signOutButton.enabled = NO; - _disconnectButton.enabled = NO; - _inviteButton.enabled = NO; - [self performSegueWithIdentifier:@"SignedOutScreen" sender:self]; - } else { - // Signed in - _signInButton.enabled = NO; - _signOutButton.enabled = YES; - _disconnectButton.enabled = YES; - _inviteButton.enabled = YES; - } -} -// [END toggle_auth] - -- (UIStatusBarStyle)preferredStatusBarStyle { - return UIStatusBarStyleLightContent; -} -@end diff --git a/invites/InvitesExampleSwift/AppDelegate.swift b/invites/InvitesExampleSwift/AppDelegate.swift deleted file mode 100644 index 39c085c7..00000000 --- a/invites/InvitesExampleSwift/AppDelegate.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// Copyright (c) Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -import UIKit -import Firebase -import GoogleSignIn - -@UIApplicationMain -class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? - - // [START configure] - func application(_ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - // Use Firebase library to configure APIs - FirebaseApp.configure() - return true - } - // [END configure] - - // [START openurl] - @available(iOS 9.0, *) - func application(_ application: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool { - return self.application(application, open: url, sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String, annotation: "") - } - - func application(_ application: UIApplication, - open url: URL, sourceApplication: String?, annotation: Any) -> Bool { - if GIDSignIn.sharedInstance().handle(url, sourceApplication: sourceApplication, annotation: annotation) { - return true - } - - return Invites.handleUniversalLink(url) { invite, error in - // [START_EXCLUDE] - if let error = error { - print(error.localizedDescription) - return - } - if let invite = invite { - self.showAlertView(withInvite: invite) - } - // [END_EXCLUDE] - } - } - // [END openurl] - - // [START continueuseractivity] - func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { - return Invites.handleUniversalLink(userActivity.webpageURL!) { invite, error in - // [START_EXCLUDE] - if let error = error { - print(error.localizedDescription) - return - } - if let invite = invite { - self.showAlertView(withInvite: invite) - } - // [END_EXCLUDE] - } - } - // [END continueuseractivity] - - func showAlertView(withInvite invite: ReceivedInvite) { - let okAction = UIAlertAction(title: "OK", style: .default, handler: nil) - let matchType = invite.matchType == .weak ? "weak" : "strong" - let message = "Invite ID: \(invite.inviteId)\nDeep-link: \(invite.deepLink)\nMatch Type: \(matchType)" - let alertController = UIAlertController(title: "Invite", message: message, preferredStyle: .alert) - alertController.addAction(okAction) - self.window?.rootViewController?.present(alertController, animated: true, completion: nil) - } -} - diff --git a/invites/InvitesExampleSwift/SignInViewController.swift b/invites/InvitesExampleSwift/SignInViewController.swift deleted file mode 100644 index 0194820d..00000000 --- a/invites/InvitesExampleSwift/SignInViewController.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -import UIKit -import Firebase -import GoogleSignIn - -// Match the ObjC symbol name inside Main.storyboard. -@objc(SignInViewController) - -class SignInViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate { - - @IBOutlet weak var signInButton: GIDSignInButton! - @IBOutlet weak var bgText: UILabel! - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - bgText.text = "Invites\niOS demo" - - GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID - GIDSignIn.sharedInstance().delegate = self - GIDSignIn.sharedInstance().uiDelegate = self - GIDSignIn.sharedInstance().signInSilently() - } - - func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) { - guard error == nil else { - // Something went wrong; for example, the user could haved clicked cancel. - print("\(error.localizedDescription)") - return - } - // User Successfully signed in. - // TODO: Remove async after, when GIDSignIn is started getting called after dissmissVC - DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { - self.performSegue(withIdentifier: "SignedInScreen", sender: self) - } - } - - @IBAction func unwindToSignIn(_ sender: UIStoryboardSegue) { - GIDSignIn.sharedInstance().delegate = self - } - - // Sets the status bar to white. - override var preferredStatusBarStyle: UIStatusBarStyle { - return .lightContent - } -} diff --git a/invites/InvitesExampleSwift/ViewController.swift b/invites/InvitesExampleSwift/ViewController.swift deleted file mode 100644 index 837eef95..00000000 --- a/invites/InvitesExampleSwift/ViewController.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// Copyright (c) Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -import UIKit - -import GoogleSignIn -import Firebase - -// Match the ObjC symbol name inside Main.storyboard. -@objc(ViewController) -// [START viewcontroller_interfaces] -class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate, InviteDelegate { -// [END viewcontroller_interfaces] - // [START viewcontroller_vars] - @IBOutlet weak var signOutButton: UIButton! - @IBOutlet weak var disconnectButton: UIButton! - @IBOutlet weak var inviteButton: UIButton! - @IBOutlet weak var statusText: UILabel! - // [END viewcontroller_vars] - - // [START viewdidload] - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - GIDSignIn.sharedInstance().delegate = self - GIDSignIn.sharedInstance().uiDelegate = self - GIDSignIn.sharedInstance().signInSilently() - toggleAuthUI() - } - // [END viewdidload] - - // [START signin_handler] - func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) { - if let error = error { - print("\(error.localizedDescription)") - } else { - // User Successfully signed in. - if let name = user.profile.name { - statusText.text = "Signed in as \(name)" - } else { - statusText.text = "Signed in, profile name is not set" - } - } - toggleAuthUI() - } - // [END signin_handler] - - // [START signout_tapped] - @IBAction func signOutTapped(_ sender: AnyObject) { - GIDSignIn.sharedInstance().signOut() - statusText.text = "Signed out" - toggleAuthUI() - } - // [END signout_tapped] - - // [START disconnect_tapped] - @IBAction func disconnectTapped(_ sender: AnyObject) { - GIDSignIn.sharedInstance().disconnect() - statusText.text = "Disconnected" - toggleAuthUI() - } - - func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) { - toggleAuthUI() - } - // [END disconnect_tapped] - - // [START invite_tapped] - @IBAction func inviteTapped(_ sender: AnyObject) { - if let invite = Invites.inviteDialog() { - invite.setInviteDelegate(self) - - // NOTE: You must have the App Store ID set in your developer console project - // in order for invitations to successfully be sent. - - // A message hint for the dialog. Note this manifests differently depending on the - // received invitation type. For example, in an email invite this appears as the subject. - invite.setMessage("Try this out!\n -\(GIDSignIn.sharedInstance().currentUser.profile.name)") - // Title for the dialog, this is what the user sees before sending the invites. - invite.setTitle("Invites Example") - invite.setDeepLink("app_url") - invite.setCallToActionText("Install!") - invite.setCustomImage("https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png") - invite.open() - } - } - // [END invite_tapped] - - // [START toggle_auth] - func toggleAuthUI() { - if GIDSignIn.sharedInstance().hasAuthInKeychain() { - // Signed in - signOutButton.isEnabled = true - disconnectButton.isEnabled = true - inviteButton.isEnabled = true - } else { - signOutButton.isEnabled = false - disconnectButton.isEnabled = false - inviteButton.isEnabled = false - self.performSegue(withIdentifier: "SignedOutScreen", sender:self) - } - } - // [END toggle_auth] - - // [START invite_finished] - func inviteFinished(withInvitations invitationIds: [String], error: Error?) { - if let error = error { - print("Failed: " + error.localizedDescription) - } else { - print("\(invitationIds.count) invites sent") - } - } - // [END invite_finished] - - // Sets the status bar to white. - override var preferredStatusBarStyle: UIStatusBarStyle { - return UIStatusBarStyle.lightContent - } -} diff --git a/invites/InvitesExampleSwiftUITests/InvitesExampleSwiftUITests.swift b/invites/InvitesExampleSwiftUITests/InvitesExampleSwiftUITests.swift deleted file mode 100644 index b33f5428..00000000 --- a/invites/InvitesExampleSwiftUITests/InvitesExampleSwiftUITests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// InvitesExampleSwiftUITests.swift -// InvitesExampleSwiftUITests -// -// Created by Ibrahim Ulukaya on 2/12/18. -// Copyright © 2018 Google Inc. All rights reserved. -// - -import XCTest - -class InvitesExampleSwiftUITests: XCTestCase { - - override func setUp() { - super.setUp() - - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. - XCUIApplication().launch() - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - -} diff --git a/invites/InvitesExampleTests/InvitesExampleTests.m b/invites/InvitesExampleTests/InvitesExampleTests.m deleted file mode 100644 index ce8e581b..00000000 --- a/invites/InvitesExampleTests/InvitesExampleTests.m +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright (c) Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#import -#import -@import Firebase; - -@interface InvitesExampleTests : XCTestCase - -@end - -@implementation InvitesExampleTests - -- (void)testAppWasConfigured { - // Check that the FIROptions matches what we read from the file. - [FIRApp configure]; - NSDictionary *configPlist = - [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] - pathForResource:@"GoogleService-Info" - ofType:@"plist"]]; - - // Check the client ID is configured for iOS target app. - NSString* actual = [FIRApp defaultApp].options.clientID; - NSString* expected = configPlist[@"CLIENT_ID"]; - - XCTAssertEqualObjects(actual, expected); -} - -@end diff --git a/invites/Podfile b/invites/Podfile deleted file mode 100644 index 7d5b6695..00000000 --- a/invites/Podfile +++ /dev/null @@ -1,11 +0,0 @@ -use_frameworks! -platform :ios, '8.0' -pod 'Firebase/Core' -pod 'Firebase/Invites' - -target 'InvitesExample' do -end -target 'InvitesExampleSwift' do -end -target 'InvitesExampleTests' do -end diff --git a/invites/Podfile.lock b/invites/Podfile.lock deleted file mode 100644 index 546ef389..00000000 --- a/invites/Podfile.lock +++ /dev/null @@ -1,154 +0,0 @@ -PODS: - - Firebase/Core (5.20.2): - - Firebase/CoreOnly - - FirebaseAnalytics (= 5.8.1) - - Firebase/CoreOnly (5.20.2): - - FirebaseCore (= 5.4.1) - - Firebase/Invites (5.20.2): - - Firebase/CoreOnly - - FirebaseInvites (= 3.0.1) - - FirebaseAnalytics (5.8.1): - - FirebaseCore (~> 5.4) - - FirebaseInstanceID (~> 3.8) - - GoogleAppMeasurement (= 5.8.1) - - GoogleUtilities/AppDelegateSwizzler (~> 5.2) - - GoogleUtilities/MethodSwizzler (~> 5.2) - - GoogleUtilities/Network (~> 5.2) - - "GoogleUtilities/NSData+zlib (~> 5.2)" - - nanopb (~> 0.3) - - FirebaseAnalyticsInterop (1.4.0) - - FirebaseCore (5.4.1): - - GoogleUtilities/Environment (~> 5.2) - - GoogleUtilities/Logger (~> 5.2) - - FirebaseDynamicLinks (3.4.3): - - FirebaseAnalytics (~> 5.1) - - FirebaseAnalyticsInterop (~> 1.0) - - FirebaseCore (~> 5.2) - - FirebaseInstanceID (3.8.1): - - FirebaseCore (~> 5.2) - - GoogleUtilities/Environment (~> 5.2) - - GoogleUtilities/UserDefaults (~> 5.2) - - FirebaseInvites (3.0.1): - - FirebaseAnalytics (~> 5.1) - - FirebaseDynamicLinks (~> 3.0) - - GoogleAPIClientForREST (~> 1.0) - - GoogleSignIn (~> 4.2) - - GoogleToolboxForMac/Logger (~> 2.1) - - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" - - "GoogleToolboxForMac/NSString+URLArguments (~> 2.1)" - - GoogleToolboxForMac/StringEncoding (~> 2.1) - - GoogleToolboxForMac/URLBuilder (~> 2.1) - - GTMOAuth2 (~> 1.0) - - GTMSessionFetcher/Core (~> 1.1) - - GTMSessionFetcher/Full (~> 1.1) - - Protobuf (~> 3.5) - - GoogleAPIClientForREST (1.3.10): - - GoogleAPIClientForREST/Core (= 1.3.10) - - GTMSessionFetcher (>= 1.1.7) - - GoogleAPIClientForREST/Core (1.3.10): - - GTMSessionFetcher (>= 1.1.7) - - GoogleAppMeasurement (5.8.1): - - GoogleUtilities/AppDelegateSwizzler (~> 5.2) - - GoogleUtilities/MethodSwizzler (~> 5.2) - - GoogleUtilities/Network (~> 5.2) - - "GoogleUtilities/NSData+zlib (~> 5.2)" - - nanopb (~> 0.3) - - GoogleSignIn (4.4.0): - - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" - - "GoogleToolboxForMac/NSString+URLArguments (~> 2.1)" - - GTMSessionFetcher/Core (~> 1.1) - - GoogleToolboxForMac/Core (2.2.1): - - GoogleToolboxForMac/Defines (= 2.2.1) - - GoogleToolboxForMac/DebugUtils (2.2.1): - - GoogleToolboxForMac/Defines (= 2.2.1) - - GoogleToolboxForMac/Defines (2.2.1) - - GoogleToolboxForMac/Logger (2.2.1): - - GoogleToolboxForMac/Defines (= 2.2.1) - - "GoogleToolboxForMac/NSDictionary+URLArguments (2.2.1)": - - GoogleToolboxForMac/DebugUtils (= 2.2.1) - - GoogleToolboxForMac/Defines (= 2.2.1) - - "GoogleToolboxForMac/NSString+URLArguments (= 2.2.1)" - - "GoogleToolboxForMac/NSString+URLArguments (2.2.1)" - - GoogleToolboxForMac/StringEncoding (2.2.1): - - GoogleToolboxForMac/Defines (= 2.2.1) - - GoogleToolboxForMac/URLBuilder (2.2.1): - - GoogleToolboxForMac/Core (= 2.2.1) - - GoogleToolboxForMac/Defines (= 2.2.1) - - "GoogleToolboxForMac/NSDictionary+URLArguments (= 2.2.1)" - - "GoogleToolboxForMac/NSString+URLArguments (= 2.2.1)" - - GoogleUtilities/AppDelegateSwizzler (5.8.0): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (5.8.0) - - GoogleUtilities/Logger (5.8.0): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (5.8.0): - - GoogleUtilities/Logger - - GoogleUtilities/Network (5.8.0): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (5.8.0)" - - GoogleUtilities/Reachability (5.8.0): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (5.8.0): - - GoogleUtilities/Logger - - GTMOAuth2 (1.1.6): - - GTMSessionFetcher (~> 1.1) - - GTMSessionFetcher (1.2.2): - - GTMSessionFetcher/Full (= 1.2.2) - - GTMSessionFetcher/Core (1.2.2) - - GTMSessionFetcher/Full (1.2.2): - - GTMSessionFetcher/Core (= 1.2.2) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - - Protobuf (3.9.0) - -DEPENDENCIES: - - Firebase/Core - - Firebase/Invites - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - Firebase - - FirebaseAnalytics - - FirebaseAnalyticsInterop - - FirebaseCore - - FirebaseDynamicLinks - - FirebaseInstanceID - - FirebaseInvites - - GoogleAPIClientForREST - - GoogleAppMeasurement - - GoogleSignIn - - GoogleToolboxForMac - - GoogleUtilities - - GTMOAuth2 - - GTMSessionFetcher - - nanopb - - Protobuf - -SPEC CHECKSUMS: - Firebase: 0c8cf33f266410c61ab3e2265cfa412200351d9c - FirebaseAnalytics: ece1aa57a4f43c64d53a648b5a5e05151aae947b - FirebaseAnalyticsInterop: d48b6ab67bcf016a05e55b71fc39c61c0cb6b7f3 - FirebaseCore: f1a9a8be1aee4bf71a2fc0f4096df6788bdfda61 - FirebaseDynamicLinks: f7a8717e2c7ea652923882a2d60b0045d6fd99d5 - FirebaseInstanceID: a122b0c258720cf250551bb2bedf48c699f80d90 - FirebaseInvites: f13ed69fae140e705baec1a59ff127334b841a8a - GoogleAPIClientForREST: 4acfffd77f1c3c8741b6be9eaed0e603278efbde - GoogleAppMeasurement: ffe513e90551844a739e7bcbb1d2aca1c28a4338 - GoogleSignIn: 7ff245e1a7b26d379099d3243a562f5747e23d39 - GoogleToolboxForMac: b3553629623a3b1bff17f555e736cd5a6d95ad55 - GoogleUtilities: 04fce34bcd5620c1ee76fb79172105c74a4df335 - GTMOAuth2: e8b6512c896235149df975c41d9a36c868ab7fba - GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - Protobuf: 1097ca58584c8d9be81bfbf2c5ff5975648dd87a - -PODFILE CHECKSUM: 95b5db7fdcb60afeb38ea4713dda46fe9aa0fb56 - -COCOAPODS: 1.7.5 diff --git a/invites/README.md b/invites/README.md deleted file mode 100644 index 40a80b03..00000000 --- a/invites/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Firebase Invites - -Firebase Invites is deprecated. You can create cross-platform invitation links that survive app installation using [Firebase Dynamic Links][fdl]. Please see [Migration Guide][migration] for more details. - -For an example of how to use Dynamic Links to invite users to your application, see [Invite Users to Your App][user-to-user]. - -[fdl]:https://firebase.google.com/docs/dynamic-links/ -[migration]:https://firebase.google.com/docs/invites/deprecation -[user-to-user]:https://firebase.google.com/docs/dynamic-links/use-cases/user-to-user diff --git a/invites/Screenshot/app-invites-sample.png b/invites/Screenshot/app-invites-sample.png deleted file mode 100644 index ffcd244b..00000000 Binary files a/invites/Screenshot/app-invites-sample.png and /dev/null differ diff --git a/ml-functions/MLFunctionsExample.xcodeproj/project.pbxproj b/ml-functions/MLFunctionsExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7a47ff3e --- /dev/null +++ b/ml-functions/MLFunctionsExample.xcodeproj/project.pbxproj @@ -0,0 +1,527 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 8D7951CA2D2C8AAA000FD694 /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951C92D2C8AAA000FD694 /* FirebaseFunctions */; }; + 8D7951CC2D2C8AB2000FD694 /* FirebaseFunctions in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951CB2D2C8AB2000FD694 /* FirebaseFunctions */; }; + 8D8FA34322F4CAB100213E06 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA34222F4CAB100213E06 /* AppDelegate.m */; }; + 8D8FA34622F4CAB100213E06 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA34522F4CAB100213E06 /* ViewController.m */; }; + 8D8FA34922F4CAB100213E06 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA34722F4CAB100213E06 /* Main.storyboard */; }; + 8D8FA34B22F4CAB200213E06 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA34A22F4CAB200213E06 /* Assets.xcassets */; }; + 8D8FA34E22F4CAB200213E06 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA34C22F4CAB200213E06 /* LaunchScreen.storyboard */; }; + 8D8FA35122F4CAB200213E06 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA35022F4CAB200213E06 /* main.m */; }; + 8D8FA35E22F4CAF700213E06 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA35D22F4CAF700213E06 /* AppDelegate.swift */; }; + 8D8FA36022F4CAF700213E06 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D8FA35F22F4CAF700213E06 /* ViewController.swift */; }; + 8D8FA36322F4CAF700213E06 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA36122F4CAF700213E06 /* Main.storyboard */; }; + 8D8FA36522F4CAF800213E06 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA36422F4CAF800213E06 /* Assets.xcassets */; }; + 8D8FA36822F4CAF800213E06 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D8FA36622F4CAF800213E06 /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 8D8FA33E22F4CAB100213E06 /* MLFunctionsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLFunctionsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D8FA34122F4CAB100213E06 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 8D8FA34222F4CAB100213E06 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 8D8FA34422F4CAB100213E06 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 8D8FA34522F4CAB100213E06 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 8D8FA34822F4CAB100213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8D8FA34A22F4CAB200213E06 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D8FA34D22F4CAB200213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8D8FA34F22F4CAB200213E06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D8FA35022F4CAB200213E06 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 8D8FA35B22F4CAF700213E06 /* MLFunctionsExampleSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MLFunctionsExampleSwift.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D8FA35D22F4CAF700213E06 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 8D8FA35F22F4CAF700213E06 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 8D8FA36222F4CAF700213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8D8FA36422F4CAF800213E06 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D8FA36722F4CAF800213E06 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8D8FA36922F4CAF800213E06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D8FA33B22F4CAB100213E06 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7951CA2D2C8AAA000FD694 /* FirebaseFunctions in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D8FA35822F4CAF700213E06 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7951CC2D2C8AB2000FD694 /* FirebaseFunctions in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D7951C82D2C8AAA000FD694 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 8D8FA33522F4CAB100213E06 = { + isa = PBXGroup; + children = ( + 8D8FA34022F4CAB100213E06 /* MLFunctionsExample */, + 8D8FA35C22F4CAF700213E06 /* MLFunctionsExampleSwift */, + 8D7951C82D2C8AAA000FD694 /* Frameworks */, + 8D8FA33F22F4CAB100213E06 /* Products */, + ); + sourceTree = ""; + }; + 8D8FA33F22F4CAB100213E06 /* Products */ = { + isa = PBXGroup; + children = ( + 8D8FA33E22F4CAB100213E06 /* MLFunctionsExample.app */, + 8D8FA35B22F4CAF700213E06 /* MLFunctionsExampleSwift.app */, + ); + name = Products; + sourceTree = ""; + }; + 8D8FA34022F4CAB100213E06 /* MLFunctionsExample */ = { + isa = PBXGroup; + children = ( + 8D8FA34122F4CAB100213E06 /* AppDelegate.h */, + 8D8FA34222F4CAB100213E06 /* AppDelegate.m */, + 8D8FA34422F4CAB100213E06 /* ViewController.h */, + 8D8FA34522F4CAB100213E06 /* ViewController.m */, + 8D8FA34722F4CAB100213E06 /* Main.storyboard */, + 8D8FA34A22F4CAB200213E06 /* Assets.xcassets */, + 8D8FA34C22F4CAB200213E06 /* LaunchScreen.storyboard */, + 8D8FA34F22F4CAB200213E06 /* Info.plist */, + 8D8FA35022F4CAB200213E06 /* main.m */, + ); + path = MLFunctionsExample; + sourceTree = ""; + }; + 8D8FA35C22F4CAF700213E06 /* MLFunctionsExampleSwift */ = { + isa = PBXGroup; + children = ( + 8D8FA35D22F4CAF700213E06 /* AppDelegate.swift */, + 8D8FA35F22F4CAF700213E06 /* ViewController.swift */, + 8D8FA36122F4CAF700213E06 /* Main.storyboard */, + 8D8FA36422F4CAF800213E06 /* Assets.xcassets */, + 8D8FA36622F4CAF800213E06 /* LaunchScreen.storyboard */, + 8D8FA36922F4CAF800213E06 /* Info.plist */, + ); + path = MLFunctionsExampleSwift; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D8FA33D22F4CAB100213E06 /* MLFunctionsExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D8FA35422F4CAB200213E06 /* Build configuration list for PBXNativeTarget "MLFunctionsExample" */; + buildPhases = ( + 8D8FA33A22F4CAB100213E06 /* Sources */, + 8D8FA33B22F4CAB100213E06 /* Frameworks */, + 8D8FA33C22F4CAB100213E06 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MLFunctionsExample; + productName = MLFunctionsExample; + productReference = 8D8FA33E22F4CAB100213E06 /* MLFunctionsExample.app */; + productType = "com.apple.product-type.application"; + }; + 8D8FA35A22F4CAF700213E06 /* MLFunctionsExampleSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D8FA36C22F4CAF800213E06 /* Build configuration list for PBXNativeTarget "MLFunctionsExampleSwift" */; + buildPhases = ( + 8D8FA35722F4CAF700213E06 /* Sources */, + 8D8FA35822F4CAF700213E06 /* Frameworks */, + 8D8FA35922F4CAF700213E06 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MLFunctionsExampleSwift; + productName = MLFunctionsExampleSwift; + productReference = 8D8FA35B22F4CAF700213E06 /* MLFunctionsExampleSwift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8D8FA33622F4CAB100213E06 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1020; + LastUpgradeCheck = 1610; + ORGANIZATIONNAME = Firebase; + TargetAttributes = { + 8D8FA33D22F4CAB100213E06 = { + CreatedOnToolsVersion = 10.2; + }; + 8D8FA35A22F4CAF700213E06 = { + CreatedOnToolsVersion = 10.2; + }; + }; + }; + buildConfigurationList = 8D8FA33922F4CAB100213E06 /* Build configuration list for PBXProject "MLFunctionsExample" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8D8FA33522F4CAB100213E06; + packageReferences = ( + 8D7951C72D2C8A9B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); + productRefGroup = 8D8FA33F22F4CAB100213E06 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D8FA33D22F4CAB100213E06 /* MLFunctionsExample */, + 8D8FA35A22F4CAF700213E06 /* MLFunctionsExampleSwift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D8FA33C22F4CAB100213E06 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA34E22F4CAB200213E06 /* LaunchScreen.storyboard in Resources */, + 8D8FA34B22F4CAB200213E06 /* Assets.xcassets in Resources */, + 8D8FA34922F4CAB100213E06 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D8FA35922F4CAF700213E06 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA36822F4CAF800213E06 /* LaunchScreen.storyboard in Resources */, + 8D8FA36522F4CAF800213E06 /* Assets.xcassets in Resources */, + 8D8FA36322F4CAF700213E06 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D8FA33A22F4CAB100213E06 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA34622F4CAB100213E06 /* ViewController.m in Sources */, + 8D8FA35122F4CAB200213E06 /* main.m in Sources */, + 8D8FA34322F4CAB100213E06 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D8FA35722F4CAF700213E06 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D8FA36022F4CAF700213E06 /* ViewController.swift in Sources */, + 8D8FA35E22F4CAF700213E06 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 8D8FA34722F4CAB100213E06 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA34822F4CAB100213E06 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8D8FA34C22F4CAB200213E06 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA34D22F4CAB200213E06 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 8D8FA36122F4CAF700213E06 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA36222F4CAF700213E06 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8D8FA36622F4CAF800213E06 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8D8FA36722F4CAF800213E06 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 8D8FA35222F4CAB200213E06 /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = 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; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 8D8FA35322F4CAB200213E06 /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = 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; + IPHONEOS_DEPLOYMENT_TARGET = 12.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8D8FA35522F4CAB200213E06 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = MLFunctionsExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.MLFunctionsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D8FA35622F4CAB200213E06 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = MLFunctionsExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.MLFunctionsExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 8D8FA36A22F4CAF800213E06 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = MLFunctionsExampleSwift/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.MLFunctionsExampleSwift; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D8FA36B22F4CAF800213E06 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = MLFunctionsExampleSwift/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.MLFunctionsExampleSwift; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8D8FA33922F4CAB100213E06 /* Build configuration list for PBXProject "MLFunctionsExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D8FA35222F4CAB200213E06 /* Debug */, + 8D8FA35322F4CAB200213E06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D8FA35422F4CAB200213E06 /* Build configuration list for PBXNativeTarget "MLFunctionsExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D8FA35522F4CAB200213E06 /* Debug */, + 8D8FA35622F4CAB200213E06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D8FA36C22F4CAF800213E06 /* Build configuration list for PBXNativeTarget "MLFunctionsExampleSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D8FA36A22F4CAF800213E06 /* Debug */, + 8D8FA36B22F4CAF800213E06 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7951C72D2C8A9B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7951C92D2C8AAA000FD694 /* FirebaseFunctions */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951C72D2C8A9B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFunctions; + }; + 8D7951CB2D2C8AB2000FD694 /* FirebaseFunctions */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951C72D2C8A9B000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFunctions; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 8D8FA33622F4CAB100213E06 /* Project object */; +} diff --git a/ml-functions/MLFunctionsExample/AppDelegate.h b/ml-functions/MLFunctionsExample/AppDelegate.h new file mode 100644 index 00000000..a9861fa2 --- /dev/null +++ b/ml-functions/MLFunctionsExample/AppDelegate.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/ml-functions/MLFunctionsExample/AppDelegate.m b/ml-functions/MLFunctionsExample/AppDelegate.m new file mode 100644 index 00000000..69381fe8 --- /dev/null +++ b/ml-functions/MLFunctionsExample/AppDelegate.m @@ -0,0 +1,34 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseCore; + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [FIRApp configure]; + return YES; +} + + +@end diff --git a/ml-functions/MLFunctionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/ml-functions/MLFunctionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/ml-functions/MLFunctionsExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ml-functions/MLFunctionsExample/Assets.xcassets/Contents.json b/ml-functions/MLFunctionsExample/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/ml-functions/MLFunctionsExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ml-functions/MLFunctionsExample/Base.lproj/LaunchScreen.storyboard b/ml-functions/MLFunctionsExample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..bfa36129 --- /dev/null +++ b/ml-functions/MLFunctionsExample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ml-functions/MLFunctionsExample/Base.lproj/Main.storyboard b/ml-functions/MLFunctionsExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..942f0bc4 --- /dev/null +++ b/ml-functions/MLFunctionsExample/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ml-functions/MLFunctionsExample/Info.plist b/ml-functions/MLFunctionsExample/Info.plist new file mode 100644 index 00000000..16be3b68 --- /dev/null +++ b/ml-functions/MLFunctionsExample/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ml-functions/MLFunctionsExample/ViewController.h b/ml-functions/MLFunctionsExample/ViewController.h new file mode 100644 index 00000000..f543b8a0 --- /dev/null +++ b/ml-functions/MLFunctionsExample/ViewController.h @@ -0,0 +1,23 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface ViewController : UIViewController + + +@end + diff --git a/ml-functions/MLFunctionsExample/ViewController.m b/ml-functions/MLFunctionsExample/ViewController.m new file mode 100644 index 00000000..2d5b80c9 --- /dev/null +++ b/ml-functions/MLFunctionsExample/ViewController.m @@ -0,0 +1,169 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseFunctions; + +#import "ViewController.h" + +@interface ViewController () +// [START ml_functions_define] +@property(strong, nonatomic) FIRFunctions *functions; +// [END ml_functions_define] +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // [START ml_functions_init] + self.functions = [FIRFunctions functions]; + // [END ml_functions_init] +} + +- (void)prepareData:(UIImage *)uiImage { + // [START base64encodeImage] + NSData *imageData = UIImageJPEGRepresentation(uiImage, 1.0f); + NSString *base64encodedImage = + [imageData base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength]; + // [END base64encodeImage] +} + +- (void)prepareLabelData:(NSString *)base64encodedImage { + // [START prepareLabelData] + NSDictionary *requestData = @{ + @"image": @{@"content": base64encodedImage}, + @"features": @{@"maxResults": @5, @"type": @"LABEL_DETECTION"} + }; + // [END prepareLabelData] +} + +- (void)prepareTextData:(NSString *)base64encodedImage { + // [START prepareTextData] + NSDictionary *requestData = @{ + @"image": @{@"content": base64encodedImage}, + @"features": @{@"type": @"TEXT_DETECTION"}, + @"imageContext": @{@"languageHints": @[@"en"]} + }; + // [END prepareTextData] + NSDictionary *textDataWithHints = @{ + @"image": @{@"content": base64encodedImage}, + @"features": @{@"type": @"TEXT_DETECTION"}, + @"imageContext": @{@"languageHints": @[@"en"]} + }; + NSDictionary *documentTextData = @{ + @"image": @{@"content": base64encodedImage}, + @"features": @{@"type": @"DOCUMENT_TEXT_DETECTION"} + }; +} + +- (void)prepareLandmarkData:(NSString *)base64encodedImage { + // [START prepareLandmarkData] + NSDictionary *requestData = @{ + @"image": @{@"content": base64encodedImage}, + @"features": @{@"maxResults": @5, @"type": @"LANDMARK_DETECTION"} + }; + // [END prepareLandmarkData] +} + +- (void)annotateImage:(NSDictionary *)requestData { + + // [START function_annotateImage] + [[_functions HTTPSCallableWithName:@"annotateImage"] + callWithObject:requestData + completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) { + if (error) { + if ([error.domain isEqualToString:@"com.firebase.functions"]) { + FIRFunctionsErrorCode code = error.code; + NSString *message = error.localizedDescription; + NSObject *details = error.userInfo[@"details"]; + } + // ... + } + // Function completed succesfully + // Get information about labeled objects + + }]; + // [END function_annotateImage] +} + +- (void)getLabeledObjectsFromResult:(FIRHTTPSCallableResult *)result { + // [START getLabeledObjectsFrom] + NSArray *labelArray = result.data[@"labelAnnotations"]; + for (NSDictionary *labelObj in labelArray) { + NSString *text = labelObj[@"description"]; + NSString *entityId = labelObj[@"mid"]; + NSNumber *confidence = labelObj[@"score"]; + } + // [END getLabeledObjectsFrom] +} + +- (void)getRecognizedTextsFromResult:(FIRHTTPSCallableResult *)result { + // [START getRecognizedTextsFrom] + NSDictionary *annotation = result.data[@"fullTextAnnotation"]; + if (!annotation) { return; } + NSLog(@"\nComplete annotation:"); + NSLog(@"\n%@", annotation[@"text"]); + // [END getRecognizedTextsFrom] + + // [START getRecognizedTextsFrom_details] + for (NSDictionary *page in annotation[@"pages"]) { + NSMutableString *pageText = [NSMutableString new]; + for (NSDictionary *block in page[@"blocks"]) { + NSMutableString *blockText = [NSMutableString new]; + for (NSDictionary *paragraph in block[@"paragraphs"]) { + NSMutableString *paragraphText = [NSMutableString new]; + for (NSDictionary *word in paragraph[@"words"]) { + NSMutableString *wordText = [NSMutableString new]; + for (NSDictionary *symbol in word[@"symbols"]) { + NSString *text = symbol[@"text"]; + [wordText appendString:text]; + NSLog(@"Symbol text: %@ (confidence: %@\n", text, symbol[@"confidence"]); + } + NSLog(@"Word text: %@ (confidence: %@\n\n", wordText, word[@"confidence"]); + NSLog(@"Word bounding box: %@\n", word[@"boundingBox"]); + [paragraphText appendString:wordText]; + } + NSLog(@"\nParagraph: \n%@\n", paragraphText); + NSLog(@"Paragraph bounding box: %@\n", paragraph[@"boundingBox"]); + NSLog(@"Paragraph Confidence: %@\n", paragraph[@"confidence"]); + [blockText appendString:paragraphText]; + } + [pageText appendString:blockText]; + } + } + // [END getRecognizedTextsFrom_details] +} + +- (void)getRecognizedLandmarksFromResult:(FIRHTTPSCallableResult *)result { + // [START getRecognizedLandmarksFrom] + NSArray *labelArray = result.data[@"landmarkAnnotations"]; + for (NSDictionary *labelObj in labelArray) { + NSString *landmarkName = labelObj[@"description"]; + NSString *entityId = labelObj[@"mid"]; + NSNumber *score = labelObj[@"score"]; + NSArray *bounds = labelObj[@"boundingPoly"]; + // Multiple locations are possible, e.g., the location of the depicted + // landmark and the location the picture was taken. + NSArray *locations = labelObj[@"locations"]; + for (NSDictionary *location in locations) { + NSNumber *latitude = location[@"latLng"][@"latitude"]; + NSNumber *longitude = location[@"latLng"][@"longitude"]; + } + } + // [END getRecognizedLandmarksFrom] +} + +@end diff --git a/ml-functions/MLFunctionsExample/main.m b/ml-functions/MLFunctionsExample/main.m new file mode 100644 index 00000000..8d31108d --- /dev/null +++ b/ml-functions/MLFunctionsExample/main.m @@ -0,0 +1,24 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/ml-functions/MLFunctionsExampleSwift/AppDelegate.swift b/ml-functions/MLFunctionsExampleSwift/AppDelegate.swift new file mode 100644 index 00000000..26ad4232 --- /dev/null +++ b/ml-functions/MLFunctionsExampleSwift/AppDelegate.swift @@ -0,0 +1,31 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import Firebase + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FirebaseApp.configure() + return true + } + +} + diff --git a/ml-functions/MLFunctionsExampleSwift/Assets.xcassets/AppIcon.appiconset/Contents.json b/ml-functions/MLFunctionsExampleSwift/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/ml-functions/MLFunctionsExampleSwift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ml-functions/MLFunctionsExampleSwift/Assets.xcassets/Contents.json b/ml-functions/MLFunctionsExampleSwift/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/ml-functions/MLFunctionsExampleSwift/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ml-functions/MLFunctionsExampleSwift/Base.lproj/LaunchScreen.storyboard b/ml-functions/MLFunctionsExampleSwift/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..bfa36129 --- /dev/null +++ b/ml-functions/MLFunctionsExampleSwift/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ml-functions/MLFunctionsExampleSwift/Base.lproj/Main.storyboard b/ml-functions/MLFunctionsExampleSwift/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f1bcf384 --- /dev/null +++ b/ml-functions/MLFunctionsExampleSwift/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ml-functions/MLFunctionsExampleSwift/Info.plist b/ml-functions/MLFunctionsExampleSwift/Info.plist new file mode 100644 index 00000000..16be3b68 --- /dev/null +++ b/ml-functions/MLFunctionsExampleSwift/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ml-functions/MLFunctionsExampleSwift/ViewController.swift b/ml-functions/MLFunctionsExampleSwift/ViewController.swift new file mode 100644 index 00000000..37204bce --- /dev/null +++ b/ml-functions/MLFunctionsExampleSwift/ViewController.swift @@ -0,0 +1,174 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseFunctions + +class ViewController: UIViewController { + // [START ml_functions_define] + lazy var functions = Functions.functions() + // [START ml_functions_define] + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + func prepareData(uiImage: UIImage) { + // [START base64encodeImage] + guard let imageData = uiImage.jpegData(compressionQuality: 1.0) else { return } + let base64encodedImage = imageData.base64EncodedString() + // [END base64encodeImage] + } + + func prepareLabelData(base64encodedImage: String) { + // [START prepareLabelData] + let requestData = [ + "image": ["content": base64encodedImage], + "features": ["maxResults": 5, "type": "LABEL_DETECTION"] + ] + // [END prepareLabelData] + } + + func prepareTextData(base64encodedImage: String) { + let textDataWithHints = [ + "image": ["content": base64encodedImage], + "features": ["type": "TEXT_DETECTION"], + "imageContext": ["languageHints": ["en"]] + ] + let documentTextData = [ + "image": ["content": base64encodedImage], + "features": ["type": "DOCUMENT_TEXT_DETECTION"] + ] + // [START prepareTextData] + let requestData = [ + "image": ["content": base64encodedImage], + "features": ["type": "TEXT_DETECTION"], + "imageContext": ["languageHints": ["en"]] + ] + // [END prepareTextData] + } + + func prepareLandmarkData(base64encodedImage: String) { + // [START prepareLandmarkData] + let requestData = [ + "image": ["content": base64encodedImage], + "features": ["maxResults": 5, "type": "LANDMARK_DETECTION"] + ] + // [END prepareLandmarkData] + } + + func annotateImage(requestData: Any) async { + // [START function_annotateImage] + do { + let result = try await functions.httpsCallable("annotateImage").call(requestData) + print(result) + } catch { + if let error = error as NSError? { + if error.domain == FunctionsErrorDomain { + let code = FunctionsErrorCode(rawValue: error.code) + let message = error.localizedDescription + let details = error.userInfo[FunctionsErrorDetailsKey] + } + // ... + } + } + // [END function_annotateImage] + } + + func getLabeledObjectsFrom(_ result: HTTPSCallableResult?) { + // [START getLabeledObjectsFrom] + if let labelArray = (result?.data as? [String: Any])?["labelAnnotations"] as? [[String:Any]] { + for labelObj in labelArray { + let text = labelObj["description"] + let entityId = labelObj["mid"] + let confidence = labelObj["score"] + } + } + // [END getLabeledObjectsFrom] + } + + func getRecognizedTextsFrom(_ result: HTTPSCallableResult?) { + // [START function_getRecognizedTexts] + let annotation = result.flatMap { $0.data as? [String: Any] } + .flatMap { $0["fullTextAnnotation"] } + .flatMap { $0 as? [String: Any] } + guard let annotation = annotation else { return } + + if let text = annotation["text"] as? String { + print("Complete annotation: \(text)") + } + // [END function_getRecognizedTexts] + + // [START function_getRecognizedTexts_details] + guard let pages = annotation["pages"] as? [[String: Any]] else { return } + for page in pages { + var pageText = "" + guard let blocks = page["blocks"] as? [[String: Any]] else { continue } + for block in blocks { + var blockText = "" + guard let paragraphs = block["paragraphs"] as? [[String: Any]] else { continue } + for paragraph in paragraphs { + var paragraphText = "" + guard let words = paragraph["words"] as? [[String: Any]] else { continue } + for word in words { + var wordText = "" + guard let symbols = word["symbols"] as? [[String: Any]] else { continue } + for symbol in symbols { + let text = symbol["text"] as? String ?? "" + let confidence = symbol["confidence"] as? Float ?? 0.0 + wordText += text + print("Symbol text: \(text) (confidence: \(confidence)%n") + } + let confidence = word["confidence"] as? Float ?? 0.0 + print("Word text: \(wordText) (confidence: \(confidence)%n%n") + let boundingBox = word["boundingBox"] as? [Float] ?? [0.0, 0.0, 0.0, 0.0] + print("Word bounding box: \(boundingBox.description)%n") + paragraphText += wordText + } + print("%nParagraph: %n\(paragraphText)%n") + let boundingBox = paragraph["boundingBox"] as? [Float] ?? [0.0, 0.0, 0.0, 0.0] + print("Paragraph bounding box: \(boundingBox)%n") + let confidence = paragraph["confidence"] as? Float ?? 0.0 + print("Paragraph Confidence: \(confidence)%n") + blockText += paragraphText + } + pageText += blockText + } + } + // [END function_getRecognizedTexts_details] + } + + func getRecognizedLandmarksFrom(_ result: HTTPSCallableResult?) { + // [START getRecognizedLandmarksFrom] + if let labelArray = (result?.data as? [String: Any])?["landmarkAnnotations"] as? [[String:Any]] { + for labelObj in labelArray { + let landmarkName = labelObj["description"] + let entityId = labelObj["mid"] + let score = labelObj["score"] + let bounds = labelObj["boundingPoly"] + // Multiple locations are possible, e.g., the location of the depicted + // landmark and the location the picture was taken. + guard let locations = labelObj["locations"] as? [[String: [String: Any]]] else { continue } + for location in locations { + let latitude = location["latLng"]?["latitude"] + let longitude = location["latLng"]?["longitude"] + } + } + } + // [END getRecognizedLandmarksFrom] + } +} diff --git a/mlkit/Podfile.lock b/mlkit/Podfile.lock index 0add4796..55f2c6d8 100644 --- a/mlkit/Podfile.lock +++ b/mlkit/Podfile.lock @@ -1,137 +1,134 @@ PODS: - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/MLVision (6.8.0): + - Firebase/CoreOnly (6.34.0): + - FirebaseCore (= 6.10.4) + - Firebase/MLVision (6.34.0): - Firebase/CoreOnly - - FirebaseMLVision (~> 0.18.0) - - Firebase/MLVisionAutoML (6.8.0): + - FirebaseMLVision (~> 0.21.0) + - Firebase/MLVisionAutoML (6.34.0): - Firebase/CoreOnly - - FirebaseMLVisionAutoML (~> 0.18.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseInstanceID (4.2.4): - - FirebaseCore (~> 6.0) - - GoogleUtilities/Environment (~> 6.0) - - GoogleUtilities/UserDefaults (~> 6.0) - - FirebaseMLCommon (0.18.0): - - FirebaseCore (~> 6.2) - - FirebaseInstanceID (~> 4.2) + - FirebaseMLVisionAutoML (~> 0.21.0) + - FirebaseCore (6.10.4): + - FirebaseCoreDiagnostics (~> 1.6) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - FirebaseCoreDiagnostics (1.7.0): + - GoogleDataTransport (~> 7.4) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - nanopb (~> 1.30906.0) + - FirebaseInstallations (1.7.0): + - FirebaseCore (~> 6.10) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - PromisesObjC (~> 1.2) + - FirebaseMLCommon (0.21.0): + - FirebaseCore (~> 6.9) + - FirebaseInstallations (~> 1.5) - GoogleToolboxForMac/Logger (~> 2.1) - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)" - GoogleUtilities/UserDefaults (~> 6.0) - GTMSessionFetcher/Core (~> 1.1) - - Protobuf (~> 3.5) - - FirebaseMLVision (0.18.0): - - FirebaseCore (~> 6.2) - - FirebaseMLCommon (~> 0.18) + - Protobuf (~> 3.12) + - FirebaseMLVision (0.21.0): + - FirebaseCore (~> 6.9) + - FirebaseMLCommon (~> 0.21) - GoogleAPIClientForREST/Core (~> 1.3) - GoogleAPIClientForREST/Vision (~> 1.3) - GoogleToolboxForMac/Logger (~> 2.1) - "GoogleToolboxForMac/NSData+zlib (~> 2.1)" - GTMSessionFetcher/Core (~> 1.1) - - Protobuf (~> 3.5) - - FirebaseMLVisionAutoML (0.18.0): - - FirebaseCore (~> 6.2) - - FirebaseMLCommon (~> 0.18) - - FirebaseMLVision (~> 0.18) + - Protobuf (~> 3.12) + - FirebaseMLVisionAutoML (0.21.0): + - FirebaseCore (~> 6.9) + - FirebaseMLCommon (~> 0.21) + - FirebaseMLVision (~> 0.21) - GTMSessionFetcher/Core (~> 1.1) - - TensorFlowLiteObjC (~> 1.14.0) - - GoogleAPIClientForREST/Core (1.3.10): - - GTMSessionFetcher (>= 1.1.7) - - GoogleAPIClientForREST/Vision (1.3.10): + - TensorFlowLiteObjC (~> 2.1.0) + - GoogleAPIClientForREST/Core (1.7.0): + - GTMSessionFetcher (< 2.0, >= 1.6.1) + - GoogleAPIClientForREST/Vision (1.7.0): - GoogleAPIClientForREST/Core - - GTMSessionFetcher (>= 1.1.7) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleToolboxForMac/DebugUtils (2.2.1): - - GoogleToolboxForMac/Defines (= 2.2.1) - - GoogleToolboxForMac/Defines (2.2.1) - - GoogleToolboxForMac/Logger (2.2.1): - - GoogleToolboxForMac/Defines (= 2.2.1) - - "GoogleToolboxForMac/NSData+zlib (2.2.1)": - - GoogleToolboxForMac/Defines (= 2.2.1) - - "GoogleToolboxForMac/NSDictionary+URLArguments (2.2.1)": - - GoogleToolboxForMac/DebugUtils (= 2.2.1) - - GoogleToolboxForMac/Defines (= 2.2.1) - - "GoogleToolboxForMac/NSString+URLArguments (= 2.2.1)" - - "GoogleToolboxForMac/NSString+URLArguments (2.2.1)" - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): + - GTMSessionFetcher (< 2.0, >= 1.6.1) + - GoogleDataTransport (7.5.1): + - nanopb (~> 1.30906.0) + - GoogleToolboxForMac/DebugUtils (2.3.2): + - GoogleToolboxForMac/Defines (= 2.3.2) + - GoogleToolboxForMac/Defines (2.3.2) + - GoogleToolboxForMac/Logger (2.3.2): + - GoogleToolboxForMac/Defines (= 2.3.2) + - "GoogleToolboxForMac/NSData+zlib (2.3.2)": + - GoogleToolboxForMac/Defines (= 2.3.2) + - "GoogleToolboxForMac/NSDictionary+URLArguments (2.3.2)": + - GoogleToolboxForMac/DebugUtils (= 2.3.2) + - GoogleToolboxForMac/Defines (= 2.3.2) + - "GoogleToolboxForMac/NSString+URLArguments (= 2.3.2)" + - "GoogleToolboxForMac/NSString+URLArguments (2.3.2)" + - GoogleUtilities/Environment (6.7.2): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (6.7.2): - GoogleUtilities/Environment - - GoogleUtilities/UserDefaults (6.2.5): + - GoogleUtilities/UserDefaults (6.7.2): - GoogleUtilities/Logger - - GTMSessionFetcher (1.2.2): - - GTMSessionFetcher/Full (= 1.2.2) - - GTMSessionFetcher/Core (1.2.2) - - GTMSessionFetcher/Full (1.2.2): - - GTMSessionFetcher/Core (= 1.2.2) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - - Protobuf (3.9.0) - - TensorFlowLiteC (1.14.0) - - TensorFlowLiteObjC (1.14.0): - - TensorFlowLiteC (= 1.14.0) + - GTMSessionFetcher (1.7.2): + - GTMSessionFetcher/Full (= 1.7.2) + - GTMSessionFetcher/Core (1.7.2) + - GTMSessionFetcher/Full (1.7.2): + - GTMSessionFetcher/Core (= 1.7.2) + - nanopb (1.30906.0): + - nanopb/decode (= 1.30906.0) + - nanopb/encode (= 1.30906.0) + - nanopb/decode (1.30906.0) + - nanopb/encode (1.30906.0) + - PromisesObjC (1.2.12) + - Protobuf (3.26.1) + - TensorFlowLiteC (2.1.0) + - TensorFlowLiteObjC (2.1.0): + - TensorFlowLiteC (= 2.1.0) DEPENDENCIES: - Firebase/MLVision - Firebase/MLVisionAutoML SPEC REPOS: - https://github.com/cocoapods/specs.git: + trunk: - Firebase - FirebaseCore - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - - FirebaseInstanceID + - FirebaseInstallations - FirebaseMLCommon - FirebaseMLVision - FirebaseMLVisionAutoML - GoogleAPIClientForREST - GoogleDataTransport - - GoogleDataTransportCCTSupport - GoogleToolboxForMac - GoogleUtilities - GTMSessionFetcher - nanopb + - PromisesObjC - Protobuf - TensorFlowLiteC - TensorFlowLiteObjC SPEC CHECKSUMS: - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseInstanceID: 88932a31aba5a56cfd3a7541706436c71f7f4598 - FirebaseMLCommon: 45b456098f5d38c0fe023dd6dca3ee0f91256b2d - FirebaseMLVision: 33745e08da6adc5375c7979b581b3521dc057f74 - FirebaseMLVisionAutoML: 31f0cde3476fea96a3bdf45d5ac87db2358abf29 - GoogleAPIClientForREST: 4acfffd77f1c3c8741b6be9eaed0e603278efbde - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleToolboxForMac: b3553629623a3b1bff17f555e736cd5a6d95ad55 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - Protobuf: 1097ca58584c8d9be81bfbf2c5ff5975648dd87a - TensorFlowLiteC: a6702011fb661928634e59565b5fe262f69692d0 - TensorFlowLiteObjC: ece71b1e78f3df5952996c1606934b1d5538c875 + Firebase: c23a36d9e4cdf7877dfcba8dd0c58add66358999 + FirebaseCore: d3a978a3cfa3240bf7e4ba7d137fdf5b22b628ec + FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 + FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 + FirebaseMLCommon: f42d067ca7fed962e09f02ba94a24e06ee50d933 + FirebaseMLVision: fd7cdd33d067c976c16fd521e7e92c26c980c947 + FirebaseMLVisionAutoML: 869af2667c30ac90bd8aefd14e92f8c99b69862b + GoogleAPIClientForREST: 2d4140915e2ec6d1bcb39c9df4a534c75706c595 + GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 + GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34 + GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + GTMSessionFetcher: 5595ec75acf5be50814f81e9189490412bad82ba + nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc + PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 + Protobuf: a53f5173a603075b3522a5c50be63a67a5f3353a + TensorFlowLiteC: 215603d4426d93810eace5947e317389554a4b66 + TensorFlowLiteObjC: 2e074eb41084037aeda9a8babe111fdd3ac99974 PODFILE CHECKSUM: 4b071879fc5f9391b9325777ac56ce58e70e8b8d -COCOAPODS: 1.7.5 +COCOAPODS: 1.15.2 diff --git a/mlkit/objc-snippets/AutoMLVision.m b/mlkit/objc-snippets/AutoMLVision.m index effb048e..49b07e39 100644 --- a/mlkit/objc-snippets/AutoMLVision.m +++ b/mlkit/objc-snippets/AutoMLVision.m @@ -15,7 +15,7 @@ // #import -@import Firebase; +@import FirebaseMLVision; @interface AutoMLVision : NSObject diff --git a/mlkit/objc-snippets/ImagePreparation.m b/mlkit/objc-snippets/ImagePreparation.m index 430342fd..421dbb40 100644 --- a/mlkit/objc-snippets/ImagePreparation.m +++ b/mlkit/objc-snippets/ImagePreparation.m @@ -16,7 +16,7 @@ #import @import AVFoundation; -@import Firebase; +@import FirebaseMLVision; @interface ImagePreparation : NSObject diff --git a/mlkit/swift-snippets/AutoMLVision.swift b/mlkit/swift-snippets/AutoMLVision.swift index a717a031..64180aa8 100644 --- a/mlkit/swift-snippets/AutoMLVision.swift +++ b/mlkit/swift-snippets/AutoMLVision.swift @@ -14,7 +14,7 @@ // limitations under the License. // -import Firebase +import FirebaseMLVision class AutoMLVision { func label(image: VisionImage) { diff --git a/mlkit/swift-snippets/ImagePreparation.swift b/mlkit/swift-snippets/ImagePreparation.swift index c3819a7e..a4e6bce0 100644 --- a/mlkit/swift-snippets/ImagePreparation.swift +++ b/mlkit/swift-snippets/ImagePreparation.swift @@ -17,7 +17,7 @@ import Foundation import AVFoundation -import Firebase +import FirebaseMLVision class ImagePreparation { func createImage(uiImage: UIImage) -> VisionImage { diff --git a/invites/InvitesExample/AppDelegate.h b/qs-snippets/DatabaseExample/AppDelegate.h similarity index 88% rename from invites/InvitesExample/AppDelegate.h rename to qs-snippets/DatabaseExample/AppDelegate.h index 409c657c..b6060b5c 100644 --- a/invites/InvitesExample/AppDelegate.h +++ b/qs-snippets/DatabaseExample/AppDelegate.h @@ -1,5 +1,5 @@ // -// Copyright (c) Google Inc. +// Copyright (c) 2015 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,6 +18,6 @@ @interface AppDelegate : UIResponder -@property(strong, nonatomic) UIWindow *window; +@property(nonatomic, strong) UIWindow *window; @end diff --git a/qs-snippets/DatabaseExample/AppDelegate.m b/qs-snippets/DatabaseExample/AppDelegate.m new file mode 100644 index 00000000..2643e7d2 --- /dev/null +++ b/qs-snippets/DatabaseExample/AppDelegate.m @@ -0,0 +1,30 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "AppDelegate.h" +@import FirebaseCore; + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // [START initialize_firebase] + [FIRApp configure]; + // [END initialize_firebase] + return YES; +} + +@end diff --git a/qs-snippets/DatabaseExample/Base.lproj/Main.storyboard b/qs-snippets/DatabaseExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..af653da9 --- /dev/null +++ b/qs-snippets/DatabaseExample/Base.lproj/Main.storyboard @@ -0,0 +1,458 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qs-snippets/DatabaseExample/Comment.h b/qs-snippets/DatabaseExample/Comment.h new file mode 100644 index 00000000..33582f6b --- /dev/null +++ b/qs-snippets/DatabaseExample/Comment.h @@ -0,0 +1,27 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface Comment : NSObject +@property(strong, nonatomic) NSString *uid; +@property(strong, nonatomic) NSString *author; +@property(strong, nonatomic) NSString *text; + +- (instancetype)initWithUid:(NSString *)uid + andAuthor:(NSString *)author + andText:(NSString *)text NS_DESIGNATED_INITIALIZER; +@end diff --git a/qs-snippets/DatabaseExample/Comment.m b/qs-snippets/DatabaseExample/Comment.m new file mode 100644 index 00000000..832c1fa2 --- /dev/null +++ b/qs-snippets/DatabaseExample/Comment.m @@ -0,0 +1,35 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "Comment.h" + +@implementation Comment + +- (instancetype)init { + return [self initWithUid:@"" andAuthor:@"" andText:@""]; +} + +- (instancetype)initWithUid:(NSString *)uid andAuthor:(NSString *)author andText:(NSString *)text { + self = [super init]; + if (self) { + self.uid = uid; + self.author = author; + self.text = text; + } + return self; +} + +@end diff --git a/qs-snippets/DatabaseExample/DatabaseExampleSwift-Bridging-Header.h b/qs-snippets/DatabaseExample/DatabaseExampleSwift-Bridging-Header.h new file mode 100644 index 00000000..3a24de6f --- /dev/null +++ b/qs-snippets/DatabaseExample/DatabaseExampleSwift-Bridging-Header.h @@ -0,0 +1,19 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "UIViewController+Alerts.h" diff --git a/qs-snippets/DatabaseExample/Images.xcassets/Contents.json b/qs-snippets/DatabaseExample/Images.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/qs-snippets/DatabaseExample/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/Contents.json b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/Contents.json new file mode 100644 index 00000000..4bd32cab --- /dev/null +++ b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_account_circle.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_account_circle_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_account_circle_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle.png new file mode 100644 index 00000000..0c1202de Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle_2x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle_2x.png new file mode 100644 index 00000000..f26b201a Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle_2x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle_3x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle_3x.png new file mode 100644 index 00000000..3cc0a63e Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_account_circle.imageset/ic_account_circle_3x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/Contents.json b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/Contents.json new file mode 100644 index 00000000..a065596e --- /dev/null +++ b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_history.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_history_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_history_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history.png new file mode 100644 index 00000000..9fade8bb Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history_2x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history_2x.png new file mode 100644 index 00000000..f6096cab Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history_2x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history_3x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history_3x.png new file mode 100644 index 00000000..f837fda0 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_history.imageset/ic_history_3x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/Contents.json b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/Contents.json new file mode 100644 index 00000000..8328848a --- /dev/null +++ b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_person.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_person_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_person_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person.png new file mode 100644 index 00000000..57da32af Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person_2x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person_2x.png new file mode 100644 index 00000000..360a32f2 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person_2x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person_3x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person_3x.png new file mode 100644 index 00000000..f1e14849 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_person.imageset/ic_person_3x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/Contents.json b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/Contents.json new file mode 100644 index 00000000..4cb52f80 --- /dev/null +++ b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_star.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_star_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_star_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star.png new file mode 100644 index 00000000..a728afe6 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star_2x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star_2x.png new file mode 100644 index 00000000..c636ce8e Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star_2x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star_3x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star_3x.png new file mode 100644 index 00000000..54d30659 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_star.imageset/ic_star_3x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/Contents.json b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/Contents.json new file mode 100644 index 00000000..fd678197 --- /dev/null +++ b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_star_border.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_star_border_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_star_border_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border.png new file mode 100644 index 00000000..b7538480 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border_2x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border_2x.png new file mode 100644 index 00000000..4f978e73 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border_2x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border_3x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border_3x.png new file mode 100644 index 00000000..f10d4274 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_star_border.imageset/ic_star_border_3x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/Contents.json b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/Contents.json new file mode 100644 index 00000000..51bfeed3 --- /dev/null +++ b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_whatshot.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_whatshot_2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_whatshot_3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot.png new file mode 100644 index 00000000..31b1981f Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot_2x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot_2x.png new file mode 100644 index 00000000..e9ae8267 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot_2x.png differ diff --git a/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot_3x.png b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot_3x.png new file mode 100644 index 00000000..a14dcd69 Binary files /dev/null and b/qs-snippets/DatabaseExample/Images.xcassets/ic_whatshot.imageset/ic_whatshot_3x.png differ diff --git a/qs-snippets/DatabaseExample/Info.plist b/qs-snippets/DatabaseExample/Info.plist new file mode 100644 index 00000000..30d7c486 --- /dev/null +++ b/qs-snippets/DatabaseExample/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIcons + + CFBundleIcons~ipad + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/qs-snippets/DatabaseExample/MyPostsViewController.h b/qs-snippets/DatabaseExample/MyPostsViewController.h new file mode 100644 index 00000000..e21af88d --- /dev/null +++ b/qs-snippets/DatabaseExample/MyPostsViewController.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "PostListViewController.h" + +@interface MyPostsViewController : PostListViewController +@end diff --git a/qs-snippets/DatabaseExample/MyPostsViewController.m b/qs-snippets/DatabaseExample/MyPostsViewController.m new file mode 100644 index 00000000..37209cd7 --- /dev/null +++ b/qs-snippets/DatabaseExample/MyPostsViewController.m @@ -0,0 +1,25 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MyPostsViewController.h" + +@implementation MyPostsViewController + +- (FIRDatabaseQuery *) getQuery { + return [[self.ref child:@"user-posts"] child:[super getUid]]; +} + +@end diff --git a/qs-snippets/DatabaseExample/MyTopPostsViewController.h b/qs-snippets/DatabaseExample/MyTopPostsViewController.h new file mode 100644 index 00000000..ef84e226 --- /dev/null +++ b/qs-snippets/DatabaseExample/MyTopPostsViewController.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MyPostsViewController.h" + +@interface MyTopPostsViewController : PostListViewController + +@end diff --git a/qs-snippets/DatabaseExample/MyTopPostsViewController.m b/qs-snippets/DatabaseExample/MyTopPostsViewController.m new file mode 100644 index 00000000..1dff6b0d --- /dev/null +++ b/qs-snippets/DatabaseExample/MyTopPostsViewController.m @@ -0,0 +1,31 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MyTopPostsViewController.h" + +@implementation MyTopPostsViewController + +- (FIRDatabaseQuery *) getQuery { + // [START my_top_posts_query] + // My top posts by number of stars + FIRDatabaseQuery *myTopPostsQuery = [[[self.ref child:@"user-posts"] + child:[super getUid]] + queryOrderedByChild:@"starCount"]; + // [END my_top_posts_query] + return myTopPostsQuery; +} + +@end diff --git a/qs-snippets/DatabaseExample/NewPostViewController.h b/qs-snippets/DatabaseExample/NewPostViewController.h new file mode 100644 index 00000000..c2b8957f --- /dev/null +++ b/qs-snippets/DatabaseExample/NewPostViewController.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import UIKit; +@import FirebaseDatabase; + +@interface NewPostViewController + : UIViewController + +@property(strong, nonatomic) FIRDatabaseReference *ref; + +@end diff --git a/qs-snippets/DatabaseExample/NewPostViewController.m b/qs-snippets/DatabaseExample/NewPostViewController.m new file mode 100644 index 00000000..7134999e --- /dev/null +++ b/qs-snippets/DatabaseExample/NewPostViewController.m @@ -0,0 +1,101 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "NewPostViewController.h" +#import "User.h" +#import "Post.h" +#import "UIViewController+Alerts.h" +@import FirebaseDatabase; +@import FirebaseAuth; + +@interface NewPostViewController () + @property (weak) IBOutlet UITextView *bodyTextView; + @property (weak) IBOutlet UITextField *titleTextField; +@end + +@implementation NewPostViewController + +#pragma mark - UIViewController lifecycle methods +- (void)viewDidLoad { + [super viewDidLoad]; + + // [START create_database_reference] + self.ref = [[FIRDatabase database] reference]; + // [END create_database_reference] + + UIToolbar *doneBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)]; + doneBar.autoresizingMask = UIViewAutoresizingFlexibleWidth; + UIBarButtonItem *flex = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace + target:nil + action:nil]; + UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithTitle:@"Post" + style:UIBarButtonItemStylePlain + target:self + action:@selector(didTapShare:)]; + done.tintColor = [UIColor colorWithRed:1.0 green:143.0/255.0 blue:0.0 alpha:1.0]; + doneBar.items = @[flex, done, flex]; + [doneBar sizeToFit]; + _bodyTextView.inputAccessoryView = doneBar; + _titleTextField.inputAccessoryView = doneBar; +} + +- (IBAction)didTapShare:(id)sender { + if (self.titleTextField.text.length == 0 || self.bodyTextView.text.length == 0) { + [self showMessagePrompt:@"Neither title nor body can be empty."]; + return; + } + // [START single_value_read] + NSString *userID = [FIRAuth auth].currentUser.uid; + [[[_ref child:@"users"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { + // Get user value + User *user = [[User alloc] initWithUsername:snapshot.value[@"username"]]; + + // [START_EXCLUDE] + // Write new post + [self writeNewPost:userID + username:user.username + title:self.titleTextField.text + body:self.bodyTextView.text]; + // Finish this Activity, back to the stream + [self.navigationController popViewControllerAnimated:YES]; + // [END_EXCLUDE] + } withCancelBlock:^(NSError * _Nonnull error) { + NSLog(@"%@", error.localizedDescription); + }]; + // [END single_value_read] +} + +- (void)writeNewPost:(NSString *)userID username:(NSString *)username title:(NSString *)title body:(NSString *)body { + // Create new post at /user-posts/$userid/$postid and at + // /posts/$postid simultaneously + // [START write_fan_out] + NSString *key = [[_ref child:@"posts"] childByAutoId].key; + NSDictionary *post = @{@"uid": userID, + @"author": username, + @"title": title, + @"body": body}; + NSDictionary *childUpdates = @{[@"/posts/" stringByAppendingString:key]: post, + [NSString stringWithFormat:@"/user-posts/%@/%@/", userID, key]: post}; + [_ref updateChildValues:childUpdates]; + // [END write_fan_out] +} + +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [textField resignFirstResponder]; + return NO; +} + +@end diff --git a/qs-snippets/DatabaseExample/Post.h b/qs-snippets/DatabaseExample/Post.h new file mode 100644 index 00000000..b2f585cc --- /dev/null +++ b/qs-snippets/DatabaseExample/Post.h @@ -0,0 +1,32 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface Post : NSObject +@property(strong, nonatomic) NSString *uid; +@property(strong, nonatomic) NSString *author; +@property(strong, nonatomic) NSString *title; +@property(strong, nonatomic) NSString *body; +@property(assign, nonatomic) int starCount; +@property(strong, nonatomic) NSDictionary *stars; + +- (instancetype)initWithUid:(NSString *)uid + andAuthor:(NSString *)author + andTitle:(NSString *)title + andBody:(NSString *)body NS_DESIGNATED_INITIALIZER; + +@end diff --git a/qs-snippets/DatabaseExample/Post.m b/qs-snippets/DatabaseExample/Post.m new file mode 100644 index 00000000..a61e78db --- /dev/null +++ b/qs-snippets/DatabaseExample/Post.m @@ -0,0 +1,38 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "Post.h" + +@implementation Post + +- (instancetype)init { + return [self initWithUid:@"" andAuthor:@"" andTitle:@"" andBody:@""]; +} + +- (instancetype)initWithUid:(NSString *)uid + andAuthor:(NSString *)author + andTitle:(NSString *)title + andBody:(NSString *)body { + self = [super init]; + if (self) { + self.uid = uid; + self.author = author; + self.title = title; + self.body = body; + } + return self; +} +@end diff --git a/qs-snippets/DatabaseExample/PostDataSource.h b/qs-snippets/DatabaseExample/PostDataSource.h new file mode 100644 index 00000000..b6ee1b8f --- /dev/null +++ b/qs-snippets/DatabaseExample/PostDataSource.h @@ -0,0 +1,23 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@import FirebaseDatabaseUI; + +@interface PostDataSource : FUITableViewDataSource + +@end diff --git a/qs-snippets/DatabaseExample/PostDataSource.m b/qs-snippets/DatabaseExample/PostDataSource.m new file mode 100644 index 00000000..f355a55a --- /dev/null +++ b/qs-snippets/DatabaseExample/PostDataSource.m @@ -0,0 +1,54 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "PostDataSource.h" + +@import FirebaseDatabase; + +@implementation PostDataSource + +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { + return YES; +} + +- (void)tableView:(UITableView *)tableView +commitEditingStyle:(UITableViewCellEditingStyle)editingStyle +forRowAtIndexPath:(NSIndexPath *)indexPath { + if (editingStyle == UITableViewCellEditingStyleDelete) { + [[self snapshotAtIndex:indexPath.row].ref removeValue]; + } +} + +- (NSInteger)tableView:(UITableView *)tableView + numberOfRowsInSection:(NSInteger)section { + if (self.count != 0) { + tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; + tableView.backgroundView = nil; + } + return self.count; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + UILabel *noDataLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, tableView.bounds.size.height)]; + noDataLabel.text = @"No posts yet - why not add one?"; + noDataLabel.textColor = [UIColor blackColor]; + noDataLabel.textAlignment = NSTextAlignmentCenter; + tableView.backgroundView = noDataLabel; + tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + return 1; +} + +@end diff --git a/qs-snippets/DatabaseExample/PostDetailTableViewController.h b/qs-snippets/DatabaseExample/PostDetailTableViewController.h new file mode 100644 index 00000000..15dd912c --- /dev/null +++ b/qs-snippets/DatabaseExample/PostDetailTableViewController.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface PostDetailTableViewController : UITableViewController +@property (strong, nonatomic) NSString *postKey; +@end diff --git a/qs-snippets/DatabaseExample/PostDetailTableViewController.m b/qs-snippets/DatabaseExample/PostDetailTableViewController.m new file mode 100644 index 00000000..3cb481aa --- /dev/null +++ b/qs-snippets/DatabaseExample/PostDetailTableViewController.m @@ -0,0 +1,185 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "Post.h" +#import "PostDetailTableViewController.h" +#import "PostTableViewCell.h" + +@import FirebaseDatabase; +@import FirebaseAuth; + +static const int kSectionComments = 2; +static const int kSectionSend = 1; +static const int kSectionPost = 0; + +@interface PostDetailTableViewController () +@property (strong, nonatomic) NSMutableArray *comments; +@property (strong, nonatomic) UITextField *commentField; +@property (strong, nonatomic) Post *post; +@property (strong, nonatomic) FIRDatabaseReference *postRef; +@property (strong, nonatomic) FIRDatabaseReference *commentsRef; +@end + +@implementation PostDetailTableViewController + + FIRDatabaseHandle _refHandle; + +// UITextViewDelegate protocol method +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [textField resignFirstResponder]; + return YES; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + FIRDatabaseReference *ref = [FIRDatabase database].reference; + self.postRef = [[ref child:@"posts"] child:_postKey]; + self.commentsRef = [[ref child:@"post-comments"] child:_postKey]; + self.comments = [[NSMutableArray alloc] init]; + self.post = [[Post alloc] init]; + UINib *nib = [UINib nibWithNibName:@"PostTableViewCell" bundle:nil]; + [self.tableView registerNib:nib forCellReuseIdentifier:@"post"]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self.comments removeAllObjects]; + // [START child_event_listener] + // Listen for new comments in the Firebase database + [_commentsRef + observeEventType:FIRDataEventTypeChildAdded + withBlock:^(FIRDataSnapshot *snapshot) { + [self.comments addObject:snapshot]; + [self.tableView insertRowsAtIndexPaths:@[ + [NSIndexPath indexPathForRow:self.comments.count - 1 inSection:kSectionComments] + ] + withRowAnimation:UITableViewRowAnimationAutomatic]; + }]; + // Listen for deleted comments in the Firebase database + [_commentsRef + observeEventType:FIRDataEventTypeChildRemoved + withBlock:^(FIRDataSnapshot *snapshot) { + int index = [self indexOfMessage:snapshot]; + [self.comments removeObjectAtIndex:index]; + [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:kSectionComments]] + withRowAnimation:UITableViewRowAnimationAutomatic]; + }]; + // [END child_event_listener] + + // [START post_value_event_listener] + _refHandle = [_postRef observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { + NSDictionary *postDict = snapshot.value; + // [START_EXCLUDE] + [self.post setValuesForKeysWithDictionary:postDict]; + [self.tableView reloadData]; + self.navigationItem.title = self.post.title; + // [END_EXCLUDE] + }]; + // [END post_value_event_listener] +} + +- (int) indexOfMessage:(FIRDataSnapshot *)snapshot { + int index = 0; + for (FIRDataSnapshot *comment in _comments) { + if ([snapshot.key isEqualToString:comment.key]) { + return index; + } + ++index; + } + return -1; +} +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [self.postRef removeObserverWithHandle:_refHandle]; + [self.commentsRef removeAllObservers]; + [[[[FIRDatabase database].reference child:@"users"] child:[FIRAuth auth].currentUser.uid] removeAllObservers]; +} + +#pragma mark - Table view data source + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return 3; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if (section == kSectionPost || section == kSectionSend ) { + return 1; + } else if (section == kSectionComments) { + return _comments.count; + } + NSAssert(NO, @"Unexpected section"); + return 0; +} +- (IBAction)didTapSend:(UIButton *)sender { + [self textFieldShouldReturn:_commentField]; + _commentField.enabled = NO; + sender.enabled = NO; + NSString *uid = [FIRAuth auth].currentUser.uid; + [[[[FIRDatabase database].reference child:@"users"] child:uid] + observeSingleEventOfType:FIRDataEventTypeValue + withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { + NSDictionary *user = snapshot.value; + NSString *username = user[@"username"]; + NSDictionary *comment = @{@"uid": uid, + @"author": username, + @"text": self.commentField.text}; + [[self.commentsRef childByAutoId] setValue:comment]; + self.commentField.text = @""; + self.commentField.enabled = YES; + sender.enabled = YES; + }]; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell; + if (indexPath.section == kSectionPost) { + cell = [tableView dequeueReusableCellWithIdentifier:@"post" forIndexPath: indexPath]; + PostTableViewCell *postcell = (PostTableViewCell *)cell; + postcell.authorLabel.text = _post.author; + postcell.postTitle.text = _post.title; + postcell.postBody.text = _post.body; + NSString *imageName = _post.stars[[self getUid]] ? @"ic_star" : @"ic_star_border"; + [postcell.starButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal]; + postcell.numStarsLabel.text = [NSString stringWithFormat:@"%d", _post.starCount]; + postcell.postKey = _postKey; + + } else if (indexPath.section == kSectionComments) { + cell = [tableView dequeueReusableCellWithIdentifier:@"comment" forIndexPath: indexPath]; + NSDictionary *comment = _comments[indexPath.row].value; + cell.textLabel.text = comment[@"author"]; + cell.detailTextLabel.text = comment[@"text"]; + } else if (indexPath.section == kSectionSend) { + cell = [tableView dequeueReusableCellWithIdentifier:@"send" forIndexPath: indexPath]; + _commentField = [(UITextField *) cell viewWithTag:7]; + } else { + [NSException raise:NSInternalInconsistencyException format:@"Wrong section %ld", (long)indexPath.section]; + } + return cell; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == kSectionPost) { + return 160; + } + return 56; +} + +- (NSString *) getUid { + return [FIRAuth auth].currentUser.uid; +} + +@end diff --git a/qs-snippets/DatabaseExample/PostListViewController.h b/qs-snippets/DatabaseExample/PostListViewController.h new file mode 100644 index 00000000..f432ef5c --- /dev/null +++ b/qs-snippets/DatabaseExample/PostListViewController.h @@ -0,0 +1,29 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import FirebaseDatabaseUI; +@import FirebaseDatabase; + +@interface PostListViewController : UIViewController +// [START define_database_reference] +@property (strong, nonatomic) FIRDatabaseReference *ref; +// [END define_database_reference] +@property (strong, nonatomic) FUITableViewDataSource *dataSource; + +@property (weak, nonatomic) IBOutlet UITableView *tableView; +@property (NS_NONATOMIC_IOSONLY, getter=getUid, readonly, copy) NSString *uid; +@end diff --git a/qs-snippets/DatabaseExample/PostListViewController.m b/qs-snippets/DatabaseExample/PostListViewController.m new file mode 100644 index 00000000..70d4f4c4 --- /dev/null +++ b/qs-snippets/DatabaseExample/PostListViewController.m @@ -0,0 +1,93 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "PostListViewController.h" +#import "Post.h" +#import "PostTableViewCell.h" +#import "PostDataSource.h" +#import "PostDetailTableViewController.h" + +@import FirebaseDatabase; +@import FirebaseAuth; + +@implementation PostListViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + // [START create_database_reference] + self.ref = [[FIRDatabase database] reference]; + // [END create_database_reference] + + NSString *identifier = @"post"; + UINib *nib = [UINib nibWithNibName:@"PostTableViewCell" bundle:nil]; + [self.tableView registerNib:nib forCellReuseIdentifier:identifier]; + + self.dataSource = [[PostDataSource alloc] initWithQuery:[self getQuery] + populateCell:^UITableViewCell * _Nonnull(UITableView * _Nonnull tableView, + NSIndexPath * _Nonnull indexPath, + FIRDataSnapshot * _Nonnull snap) { + Post *post = [[Post alloc] initWithUid:snap.value[@"uid"] + andAuthor:snap.value[@"author"] + andTitle:snap.value[@"title"] + andBody:snap.value[@"body"]]; + PostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; + cell.authorImage.image = [UIImage imageNamed:@"ic_account_circle"]; + cell.authorLabel.text = post.author; + NSString *imageName = post.stars[[self getUid]] ? @"ic_star" : @"ic_star_border"; + [cell.starButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal]; + cell.numStarsLabel.text = [NSString stringWithFormat:@"%d", post.starCount]; + cell.postTitle.text = post.title; + cell.postBody.text = post.body; + return cell; + }]; + + [self.dataSource bindToView:self.tableView]; + self.tableView.delegate = self; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [self performSegueWithIdentifier:@"detail" sender:indexPath]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self.tableView reloadData]; +} + +-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + return 150; +} + +- (NSString *) getUid { + return [FIRAuth auth].currentUser.uid; +} + +- (FIRDatabaseQuery *) getQuery { + return self.ref; +} + +-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + NSIndexPath *indexPath = sender; + PostDetailTableViewController *detail = segue.destinationViewController; + detail.postKey = [self.dataSource snapshotAtIndex:indexPath.row].key; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [[self getQuery] removeAllObservers]; +} +@end diff --git a/qs-snippets/DatabaseExample/PostTableViewCell.h b/qs-snippets/DatabaseExample/PostTableViewCell.h new file mode 100644 index 00000000..8f833783 --- /dev/null +++ b/qs-snippets/DatabaseExample/PostTableViewCell.h @@ -0,0 +1,28 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import FirebaseDatabase; + +@interface PostTableViewCell : UITableViewCell +@property (weak, nonatomic) IBOutlet UIImageView *authorImage; +@property (weak, nonatomic) IBOutlet UILabel *authorLabel; +@property (weak, nonatomic) IBOutlet UIButton *starButton; +@property (weak, nonatomic) IBOutlet UILabel *numStarsLabel; +@property (weak, nonatomic) IBOutlet UILabel *postTitle; +@property (weak, nonatomic) IBOutlet UITextView *postBody; +@property (weak, nonatomic) NSString *postKey; +@end diff --git a/qs-snippets/DatabaseExample/PostTableViewCell.m b/qs-snippets/DatabaseExample/PostTableViewCell.m new file mode 100644 index 00000000..834ba0df --- /dev/null +++ b/qs-snippets/DatabaseExample/PostTableViewCell.m @@ -0,0 +1,83 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "PostTableViewCell.h" +@import FirebaseDatabase; +@import FirebaseAuth; + +@interface PostTableViewCell () + @property (strong, nonatomic) FIRDatabaseReference *postRef; +@end + +@implementation PostTableViewCell + +- (IBAction)didTapStarButton:(id)sender { + if (!self.postKey) { + // We don't know the identifier of this post, so just return. + return; + } + self.postRef = [[[[FIRDatabase database] reference] child:@"posts"] child:self.postKey]; + [self incrementStarsForRef:self.postRef]; + [self.postRef observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { + NSString *uid = snapshot.value[@"uid"]; + FIRDatabaseReference *ref = [[[[[FIRDatabase database] reference] + child:@"user-posts"] + child:uid] child:self.postKey]; + [self incrementStarsForRef:ref]; + }]; +} + +- (void)incrementStarsForRef:(FIRDatabaseReference *)ref { + // [START post_stars_transaction] + [ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData * _Nonnull currentData) { + NSMutableDictionary *post = currentData.value; + if (!post || [post isEqual:[NSNull null]]) { + return [FIRTransactionResult successWithValue:currentData]; + } + + NSMutableDictionary *stars = post[@"stars"]; + if (!stars) { + stars = [[NSMutableDictionary alloc] initWithCapacity:1]; + } + NSString *uid = [FIRAuth auth].currentUser.uid; + int starCount = [post[@"starCount"] intValue]; + if (stars[uid]) { + // Unstar the post and remove self from stars + starCount--; + [stars removeObjectForKey:uid]; + } else { + // Star the post and add self to stars + starCount++; + stars[uid] = @YES; + } + post[@"stars"] = stars; + post[@"starCount"] = @(starCount); + + // Set value and report transaction success + currentData.value = post; + return [FIRTransactionResult successWithValue:currentData]; + } andCompletionBlock:^(NSError * _Nullable error, + BOOL committed, + FIRDataSnapshot * _Nullable snapshot) { + // Transaction completed + if (error) { + NSLog(@"%@", error.localizedDescription); + } + }]; + // [END post_stars_transaction] +} + +@end diff --git a/qs-snippets/DatabaseExample/PostTableViewCell.xib b/qs-snippets/DatabaseExample/PostTableViewCell.xib new file mode 100644 index 00000000..58274553 --- /dev/null +++ b/qs-snippets/DatabaseExample/PostTableViewCell.xib @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qs-snippets/DatabaseExample/RecentPostsViewController.h b/qs-snippets/DatabaseExample/RecentPostsViewController.h new file mode 100644 index 00000000..583c6a36 --- /dev/null +++ b/qs-snippets/DatabaseExample/RecentPostsViewController.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "PostListViewController.h" + +@interface RecentPostsViewController : PostListViewController + +@end diff --git a/qs-snippets/DatabaseExample/RecentPostsViewController.m b/qs-snippets/DatabaseExample/RecentPostsViewController.m new file mode 100644 index 00000000..690f0acd --- /dev/null +++ b/qs-snippets/DatabaseExample/RecentPostsViewController.m @@ -0,0 +1,30 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "RecentPostsViewController.h" + +@implementation RecentPostsViewController + +- (FIRDatabaseQuery *) getQuery { + // [START recent_posts_query] + // Last 100 posts, these are automatically the 100 most recent + // due to sorting by push() keys + FIRDatabaseQuery *recentPostsQuery = [[self.ref child:@"posts"] queryLimitedToFirst:100]; + // [END recent_posts_query] + return recentPostsQuery; +} + +@end diff --git a/qs-snippets/DatabaseExample/SignInViewController.h b/qs-snippets/DatabaseExample/SignInViewController.h new file mode 100644 index 00000000..30f61d29 --- /dev/null +++ b/qs-snippets/DatabaseExample/SignInViewController.h @@ -0,0 +1,22 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import FirebaseDatabase; + +@interface SignInViewController : UIViewController +@property (strong, nonatomic) FIRDatabaseReference *ref; +@end diff --git a/qs-snippets/DatabaseExample/SignInViewController.m b/qs-snippets/DatabaseExample/SignInViewController.m new file mode 100644 index 00000000..e1db6d30 --- /dev/null +++ b/qs-snippets/DatabaseExample/SignInViewController.m @@ -0,0 +1,149 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SignInViewController.h" +#import "UIViewController+Alerts.h" +@import FirebaseDatabase; +@import FirebaseAuth; + +@interface SignInViewController () +@property (weak, nonatomic) IBOutlet UITextField *emailField; +@property (weak, nonatomic) IBOutlet UITextField *passwordField; +@end + +@implementation SignInViewController + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self.view endEditing:YES]; +} + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + if ([FIRAuth auth].currentUser) { + [self performSegueWithIdentifier:@"signIn" sender:nil]; + } + _ref = [[FIRDatabase database] reference]; +} + +- (IBAction)didTapEmailLogin:(id)sender { + [self showSpinner:^{ + [[FIRAuth auth] signInWithEmail:self.emailField.text + password:self.passwordField.text + completion:^(FIRAuthDataResult * _Nullable authResult, NSError *error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [[[self.ref child:@"users"] child:authResult.user.uid] + observeEventType:FIRDataEventTypeValue + withBlock:^(FIRDataSnapshot * _Nonnull snapshot) { + if (![snapshot exists]) { + [self promptForNewUserName:authResult.user]; + } else { + [self performSegueWithIdentifier:@"signIn" + sender:nil]; + } + }]; + }]; + }]; + }]; +} + +- (void)promptForNewUserName:(FIRUser *)user { + [self showTextInputPromptWithMessage:@"Username:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable username) { + if (!userPressedOK || !username.length) { + return; + } + [self showSpinner:^{ + FIRUserProfileChangeRequest *changeRequest =[user profileChangeRequest]; + changeRequest.displayName = username; + [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [[[self.ref child:@"users"] child:[FIRAuth auth].currentUser.uid] + setValue:@{@"username": username}]; + [self performSegueWithIdentifier:@"signIn" sender:nil]; + }]; + }]; + }]; + }]; + +} + +- (IBAction)didTapSignUp:(id)sender { + [self showTextInputPromptWithMessage:@"Email:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) { + if (!userPressedOK || !email.length) { + return; + } + [self showTextInputPromptWithMessage:@"Password:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) { + if (!userPressedOK || !password.length) { + return; + } + [self showTextInputPromptWithMessage:@"Username:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable username) { + if (!userPressedOK || !username.length) { + return; + } + [self showSpinner:^{ + [[FIRAuth auth] createUserWithEmail:email password:password + completion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + }]; + [self showSpinner:^{ + FIRUserProfileChangeRequest *changeRequest = + [[FIRAuth auth].currentUser profileChangeRequest]; + changeRequest.displayName = username; + [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + // [START basic_write] + [[[self.ref child:@"users"] child:authResult.user.uid] + setValue:@{@"username": username}]; + // [END basic_write] + [self performSegueWithIdentifier:@"signIn" sender:nil]; + }]; + }]; + }]; + }]; + }]; + }]; + }]; + }]; +} + + +#pragma mark - UITextFieldDelegate protocol methods +- (BOOL)textFieldShouldReturn:(UITextField *)textField { + [self didTapEmailLogin:nil]; + return YES; +} + +@end diff --git a/qs-snippets/DatabaseExample/TabBarController.h b/qs-snippets/DatabaseExample/TabBarController.h new file mode 100644 index 00000000..01893bda --- /dev/null +++ b/qs-snippets/DatabaseExample/TabBarController.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface TabBarController : UITabBarController + +@end diff --git a/qs-snippets/DatabaseExample/TabBarController.m b/qs-snippets/DatabaseExample/TabBarController.m new file mode 100644 index 00000000..ba4847e6 --- /dev/null +++ b/qs-snippets/DatabaseExample/TabBarController.m @@ -0,0 +1,36 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "TabBarController.h" +@import FirebaseDatabase; +@import FirebaseAuth; + + +@implementation TabBarController + + +- (void)didMoveToParentViewController:(UIViewController *)parent { + if (parent == nil) { + NSError *signOutError; + BOOL status = [[FIRAuth auth] signOut:&signOutError]; + if (!status) { + NSLog(@"Error signing out: %@", signOutError); + return; + } + } +} + +@end diff --git a/qs-snippets/DatabaseExample/UIViewController+Alerts.h b/qs-snippets/DatabaseExample/UIViewController+Alerts.h new file mode 100644 index 00000000..843b9533 --- /dev/null +++ b/qs-snippets/DatabaseExample/UIViewController+Alerts.h @@ -0,0 +1,60 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/*! @typedef AlertPromptCompletionBlock + @brief The type of callback used to report text input prompt results. + */ +typedef void (^AlertPromptCompletionBlock)(BOOL userPressedOK, NSString *_Nullable userInput); + +/*! @class Alerts + @brief Wrapper for @c UIAlertController and @c UIAlertView for backwards compatability with + iOS 6+. + */ +@interface UIViewController (Alerts) + +/*! @fn showMessagePrompt: + @brief Displays an alert with an 'OK' button and a message. + @param message The message to display. + */ +- (void)showMessagePrompt:(NSString *)message; + +/*! @fn showTextInputPromptWithMessage:completionBlock: + @brief Shows a prompt with a text field and 'OK'/'Cancel' buttons. + @param message The message to display. + @param completion A block to call when the user taps 'OK' or 'Cancel'. + */ +- (void)showTextInputPromptWithMessage:(NSString *)message + completionBlock:(AlertPromptCompletionBlock)completion; + +/*! @fn showSpinner + @brief Shows the please wait spinner. + @param completion Called after the spinner has been hidden. + */ +- (void)showSpinner:(nullable void(^)(void))completion; + +/*! @fn hideSpinner + @brief Hides the please wait spinner. + @param completion Called after the spinner has been hidden. + */ +- (void)hideSpinner:(nullable void(^)(void))completion; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/qs-snippets/DatabaseExample/UIViewController+Alerts.m b/qs-snippets/DatabaseExample/UIViewController+Alerts.m new file mode 100644 index 00000000..783086c6 --- /dev/null +++ b/qs-snippets/DatabaseExample/UIViewController+Alerts.m @@ -0,0 +1,283 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "UIViewController+Alerts.h" + +#import + +/*! @var kPleaseWaitAssociatedObjectKey + @brief Key used to identify the "please wait" spinner associated object. + */ +static NSString *const kPleaseWaitAssociatedObjectKey = +@"_UIViewControllerAlertCategory_PleaseWaitScreenAssociatedObject"; + +/*! @var kOK + @brief Text for an 'OK' button. + */ +static NSString *const kOK = @"OK"; + +/*! @var kCancel + @brief Text for an 'Cancel' button. + */ +static NSString *const kCancel = @"Cancel"; + +/*! @class SimpleTextPromptDelegate + @brief A @c UIAlertViewDelegate which allows @c UIAlertView to be used with blocks more easily. + */ +@interface SimpleTextPromptDelegate : NSObject + +/*! @fn init + @brief Please use initWithCompletionHandler. + */ +- (nullable instancetype)init NS_UNAVAILABLE; + +/*! @fn initWithCompletionHandler: + @brief Designated initializer. + @param completionHandler The block to call when the alert view is dismissed. + */ +- (nullable instancetype)initWithCompletionHandler:(AlertPromptCompletionBlock)completionHandler +NS_DESIGNATED_INITIALIZER; + +@end + +@implementation UIViewController (Alerts) + +/*! @fn supportsAlertController + @brief Determines if the current platform supports @c UIAlertController. + @return YES if the current platform supports @c UIAlertController. + */ +- (BOOL)supportsAlertController { + return NSClassFromString(@"UIAlertController") != nil; +} + +- (void)showMessagePrompt:(NSString *)message { + if ([self supportsAlertController]) { + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:nil + message:message + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = + [UIAlertAction actionWithTitle:kOK style:UIAlertActionStyleDefault handler:nil]; + [alert addAction:okAction]; + [self presentViewController:alert animated:YES completion:nil]; + } else { + UIAlertView *alert = + [[UIAlertView alloc] initWithTitle:nil + message:message + delegate:nil + cancelButtonTitle:nil + otherButtonTitles:kOK, nil]; + [alert show]; + } +} + +- (void)showTextInputPromptWithMessage:(NSString *)message + completionBlock:(AlertPromptCompletionBlock)completion { + if ([self supportsAlertController]) { + UIAlertController *prompt = + [UIAlertController alertControllerWithTitle:nil + message:message + preferredStyle:UIAlertControllerStyleAlert]; + __weak UIAlertController *weakPrompt = prompt; + UIAlertAction *cancelAction = + [UIAlertAction actionWithTitle:kCancel + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * _Nonnull action) { + completion(NO, nil); + }]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:kOK + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + UIAlertController *strongPrompt = weakPrompt; + completion(YES, strongPrompt.textFields[0].text); + }]; + [prompt addTextFieldWithConfigurationHandler:nil]; + [prompt addAction:cancelAction]; + [prompt addAction:okAction]; + [self presentViewController:prompt animated:YES completion:nil]; + } else { + SimpleTextPromptDelegate *prompt = + [[SimpleTextPromptDelegate alloc] initWithCompletionHandler:completion]; + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil + message:message + delegate:prompt + cancelButtonTitle:@"Cancel" + otherButtonTitles:@"Ok", nil]; + alertView.alertViewStyle = UIAlertViewStylePlainTextInput; + [alertView show]; + } +} + +- (void)showSpinner:(nullable void(^)(void))completion { + if ([self supportsAlertController]) { + [self showModernSpinner:completion]; + } else { + [self showIOS7Spinner:completion]; + } +} + +- (void)showModernSpinner:(nullable void (^)(void))completion { + UIAlertController *pleaseWaitAlert = + objc_getAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey)); + if (pleaseWaitAlert) { + if (completion) { + completion(); + } + return; + } + pleaseWaitAlert = [UIAlertController alertControllerWithTitle:nil + message:@"Please Wait...\n\n\n\n" + preferredStyle:UIAlertControllerStyleAlert]; + + UIActivityIndicatorView *spinner = + [[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + spinner.color = [UIColor blackColor]; + spinner.center = CGPointMake(pleaseWaitAlert.view.bounds.size.width / 2, + pleaseWaitAlert.view.bounds.size.height / 2); + spinner.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | + UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleLeftMargin | + UIViewAutoresizingFlexibleRightMargin; + [spinner startAnimating]; + [pleaseWaitAlert.view addSubview:spinner]; + + objc_setAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey), + pleaseWaitAlert, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + [self presentViewController:pleaseWaitAlert animated:YES completion:completion]; +} + +- (void)showIOS7Spinner:(nullable void (^)(void))completion { + UIWindow *pleaseWaitWindow = + objc_getAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey)); + + if (pleaseWaitWindow) { + if (completion) { + completion(); + } + return; + } + + pleaseWaitWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + pleaseWaitWindow.backgroundColor = [UIColor clearColor]; + pleaseWaitWindow.windowLevel = UIWindowLevelStatusBar - 1; + + UIView *pleaseWaitView = [[UIView alloc] initWithFrame:pleaseWaitWindow.bounds]; + pleaseWaitView.autoresizingMask = UIViewAutoresizingFlexibleWidth | + UIViewAutoresizingFlexibleHeight; + pleaseWaitView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5]; + UIActivityIndicatorView *spinner = + [[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; + spinner.center = pleaseWaitView.center; + [pleaseWaitView addSubview:spinner]; + [spinner startAnimating]; + + pleaseWaitView.layer.opacity = 0.0; + [self.view addSubview:pleaseWaitView]; + + [pleaseWaitWindow addSubview:pleaseWaitView]; + + [pleaseWaitWindow makeKeyAndVisible]; + + objc_setAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey), + pleaseWaitWindow, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + [UIView animateWithDuration:0.5f animations:^{ + pleaseWaitView.layer.opacity = 1.0f; + } completion:^(BOOL finished) { + if (completion) { + completion(); + } + }]; +} + +- (void)hideSpinner:(nullable void(^)(void))completion { + if ([self supportsAlertController]) { + [self hideModernSpinner:completion]; + } else { + [self hideIOS7Spinner:completion]; + } +} + +- (void)hideModernSpinner:(nullable void(^)(void))completion { + UIAlertController *pleaseWaitAlert = + objc_getAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey)); + + [pleaseWaitAlert dismissViewControllerAnimated:YES completion:completion]; + + objc_setAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey), + nil, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)hideIOS7Spinner:(nullable void(^)(void))completion { + UIWindow *pleaseWaitWindow = + objc_getAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey)); + + UIView *pleaseWaitView; + pleaseWaitView = pleaseWaitWindow.subviews.firstObject; + + [UIView animateWithDuration:0.5f animations:^{ + pleaseWaitView.layer.opacity = 0.0f; + } completion:^(BOOL finished) { + [pleaseWaitWindow resignKeyWindow]; + objc_setAssociatedObject(self, + (__bridge const void *)(kPleaseWaitAssociatedObjectKey), + nil, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + if (completion) { + completion(); + } + }]; +} + +@end + +@implementation SimpleTextPromptDelegate { + AlertPromptCompletionBlock _completionHandler; + SimpleTextPromptDelegate *_retainedSelf; +} + +- (instancetype)initWithCompletionHandler:(AlertPromptCompletionBlock)completionHandler { + self = [super init]; + if (self) { + _completionHandler = completionHandler; + _retainedSelf = self; + } + return self; +} + +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { + if (buttonIndex == alertView.firstOtherButtonIndex) { + _completionHandler(YES, [alertView textFieldAtIndex:0].text); + } else { + _completionHandler(NO, nil); + } + _completionHandler = nil; + _retainedSelf = nil; +} + +@end diff --git a/qs-snippets/DatabaseExample/User.h b/qs-snippets/DatabaseExample/User.h new file mode 100644 index 00000000..4872357c --- /dev/null +++ b/qs-snippets/DatabaseExample/User.h @@ -0,0 +1,24 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface User : NSObject +@property(strong, nonatomic) NSString *username; + +- (instancetype)initWithUsername:(NSString *)username NS_DESIGNATED_INITIALIZER; + +@end diff --git a/qs-snippets/DatabaseExample/User.m b/qs-snippets/DatabaseExample/User.m new file mode 100644 index 00000000..10758ef1 --- /dev/null +++ b/qs-snippets/DatabaseExample/User.m @@ -0,0 +1,33 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "User.h" + +@implementation User + +- (instancetype)init { + return [self initWithUsername:@""]; +} + +- (instancetype)initWithUsername:(NSString *)username { + self = [super init]; + if (self) { + self.username = username; + } + return self; +} + +@end diff --git a/invites/InvitesExample/main.m b/qs-snippets/DatabaseExample/main.m similarity index 100% rename from invites/InvitesExample/main.m rename to qs-snippets/DatabaseExample/main.m diff --git a/qs-snippets/DatabaseExampleSwift/AppDelegate.swift b/qs-snippets/DatabaseExampleSwift/AppDelegate.swift new file mode 100644 index 00000000..c3e96a60 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/AppDelegate.swift @@ -0,0 +1,32 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseCore + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication + .LaunchOptionsKey: Any]?) -> Bool { + // [START initialize_firebase] + FirebaseApp.configure() + // [END initialize_firebase] + return true + } +} diff --git a/qs-snippets/DatabaseExampleSwift/Comment.swift b/qs-snippets/DatabaseExampleSwift/Comment.swift new file mode 100644 index 00000000..6e0f3b77 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/Comment.swift @@ -0,0 +1,33 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class Comment: NSObject { + var uid: String + var author: String + var text: String + + init(uid: String, author: String, text: String) { + self.uid = uid + self.author = author + self.text = text + } + + override convenience init() { + self.init(uid: "", author: "", text: "") + } +} diff --git a/qs-snippets/DatabaseExampleSwift/MyPostsViewController.swift b/qs-snippets/DatabaseExampleSwift/MyPostsViewController.swift new file mode 100644 index 00000000..8a683a97 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/MyPostsViewController.swift @@ -0,0 +1,25 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase + +@objc(MyPostsViewController) +class MyPostsViewController: PostListViewController { + override func getQuery() -> DatabaseQuery { + return (ref?.child("user-posts").child(getUid()))! + } +} diff --git a/qs-snippets/DatabaseExampleSwift/MyTopPostsViewController.swift b/qs-snippets/DatabaseExampleSwift/MyTopPostsViewController.swift new file mode 100644 index 00000000..6e8475b5 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/MyTopPostsViewController.swift @@ -0,0 +1,29 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase + +@objc(MyTopPostsViewController) +class MyTopPostsViewController: PostListViewController { + override func getQuery() -> DatabaseQuery { + // [START my_top_posts_query] + // My top posts by number of stars + let myTopPostsQuery = ref.child("user-posts").child(getUid()).queryOrdered(byChild: "starCount") + // [END my_top_posts_query] + return myTopPostsQuery + } +} diff --git a/qs-snippets/DatabaseExampleSwift/NewPostViewController.swift b/qs-snippets/DatabaseExampleSwift/NewPostViewController.swift new file mode 100644 index 00000000..27db38b7 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/NewPostViewController.swift @@ -0,0 +1,106 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase +import FirebaseAuth + +@objc(NewPostViewController) +class NewPostViewController: UIViewController, UITextFieldDelegate { + var ref: DatabaseReference! + @IBOutlet var bodyTextView: UITextView! + @IBOutlet var titleTextField: UITextField! + + // UIView lifecycle methods + override func viewDidLoad() { + super.viewDidLoad() + + // [START create_database_reference] + ref = Database.database().reference() + // [END create_database_reference] + + let doneBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 320, height: 44)) + doneBar.autoresizingMask = .flexibleWidth + let flex = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let done = UIBarButtonItem( + title: "Post", + style: .plain, + target: self, + action: #selector(didTapShare) + ) + done.tintColor = UIColor(red: 1.0, green: 143.0 / 255.0, blue: 0.0, alpha: 1.0) + doneBar.items = [flex, done, flex] + doneBar.sizeToFit() + bodyTextView.inputAccessoryView = doneBar + titleTextField.inputAccessoryView = doneBar + } + + @IBAction func didTapShare(_ sender: AnyObject) { + guard titleTextField.hasText, bodyTextView.hasText else { + let alert = UIAlertController( + title: nil, + message: "Neither title nor body can be empty.", + preferredStyle: .alert + ) + alert.addAction(UIAlertAction(title: "OK", style: .default)) + present(alert, animated: true, completion: nil) + return + } + // [START single_value_read] + let userID = Auth.auth().currentUser?.uid + ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { snapshot in + // Get user value + let value = snapshot.value as? NSDictionary + let username = value?["username"] as? String ?? "" + let user = User(username: username) + + // [START_EXCLUDE] + // Write new post + self.writeNewPost( + withUserID: userID!, + username: user.username, + title: self.titleTextField.text!, + body: self.bodyTextView.text + ) + // Finish this Activity, back to the stream + _ = self.navigationController?.popViewController(animated: true) + // [END_EXCLUDE] + }) { error in + print(error.localizedDescription) + } + // [END single_value_read] + } + + func writeNewPost(withUserID userID: String, username: String, title: String, body: String) { + // Create new post at /user-posts/$userid/$postid and at + // /posts/$postid simultaneously + // [START write_fan_out] + guard let key = ref.child("posts").childByAutoId().key else { return } + let post = ["uid": userID, + "author": username, + "title": title, + "body": body] + let childUpdates = ["/posts/\(key)": post, + "/user-posts/\(userID)/\(key)/": post] + ref.updateChildValues(childUpdates) + // [END write_fan_out] + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return false + } +} diff --git a/qs-snippets/DatabaseExampleSwift/Post.swift b/qs-snippets/DatabaseExampleSwift/Post.swift new file mode 100644 index 00000000..57686e41 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/Post.swift @@ -0,0 +1,54 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase + +class Post: NSObject { + var uid: String + var author: String + var title: String + var body: String + var starCount: AnyObject? + var stars: [String: Bool]? + + init(uid: String, author: String, title: String, body: String) { + self.uid = uid + self.author = author + self.title = title + self.body = body + starCount = 0 as AnyObject? + } + + init?(snapshot: DataSnapshot) { + guard let dict = snapshot.value as? [String: Any] else { return nil } + guard let uid = dict["uid"] as? String else { return nil } + guard let author = dict["author"] as? String else { return nil } + guard let title = dict["title"] as? String else { return nil } + guard let body = dict["body"] as? String else { return nil } + let starCount = dict["starCount"] as? Int ?? 0 + + self.uid = uid + self.author = author + self.title = title + self.body = body + self.starCount = starCount as AnyObject? + } + + override convenience init() { + self.init(uid: "", author: "", title: "", body: "") + } +} diff --git a/qs-snippets/DatabaseExampleSwift/PostDataSource.swift b/qs-snippets/DatabaseExampleSwift/PostDataSource.swift new file mode 100644 index 00000000..61397be1 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/PostDataSource.swift @@ -0,0 +1,50 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabaseUI + +class PostDataSource: FUITableViewDataSource { + override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { + return true + } + + override func tableView(_ tableView: UITableView, + commit editingStyle: UITableViewCell.EditingStyle, + forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + snapshot(at: indexPath.row).ref.removeValue() + } + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if count != 0 { + tableView.separatorStyle = .singleLine + tableView.backgroundView = nil + } + return Int(count) + } + + override func numberOfSections(in tableView: UITableView) -> Int { + let noDataLabel = UILabel(frame: CGRect(origin: .zero, size: tableView.bounds.size)) + noDataLabel.text = "No posts yet - why not add one?" + noDataLabel.textColor = UIColor.black + noDataLabel.textAlignment = .center + tableView.backgroundView = noDataLabel + tableView.separatorStyle = .none + return 1 + } +} diff --git a/qs-snippets/DatabaseExampleSwift/PostDetailTableViewController.swift b/qs-snippets/DatabaseExampleSwift/PostDetailTableViewController.swift new file mode 100644 index 00000000..182faf3f --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/PostDetailTableViewController.swift @@ -0,0 +1,190 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase +import FirebaseAuth + +@objc(PostDetailTableViewController) +class PostDetailTableViewController: UITableViewController, UITextFieldDelegate { + let kSectionComments = 2 + let kSectionSend = 1 + let kSectionPost = 0 + + var postKey = "" + var comments: [DataSnapshot] = [] + var commentField: UITextField? + var post: Post = Post() + lazy var ref: DatabaseReference = Database.database().reference() + var postRef: DatabaseReference! + var commentsRef: DatabaseReference! + var refHandle: DatabaseHandle? + + // UITextViewDelegate protocol method + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + return true + } + + override func viewDidLoad() { + super.viewDidLoad() + postRef = ref.child("posts").child(postKey) + commentsRef = ref.child("post-comments").child(postKey) + let nib = UINib(nibName: "PostTableViewCell", bundle: nil) + tableView.register(nib, forCellReuseIdentifier: "post") + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + comments.removeAll() + // [START child_event_listener] + // Listen for new comments in the Firebase database + commentsRef.observe(.childAdded, with: { (snapshot) -> Void in + self.comments.append(snapshot) + self.tableView.insertRows( + at: [IndexPath(row: self.comments.count - 1, section: self.kSectionComments)], + with: UITableView.RowAnimation.automatic + ) + }) + // Listen for deleted comments in the Firebase database + commentsRef.observe(.childRemoved, with: { (snapshot) -> Void in + let index = self.indexOfMessage(snapshot) + self.comments.remove(at: index) + self.tableView.deleteRows( + at: [IndexPath(row: index, section: self.kSectionComments)], + with: UITableView.RowAnimation.automatic + ) + }) + // [END child_event_listener] + + // [START post_value_event_listener] + refHandle = postRef.observe(DataEventType.value, with: { snapshot in + // [START_EXCLUDE] + if let post = Post(snapshot: snapshot) { + self.post = post + } + self.tableView.reloadData() + self.navigationItem.title = self.post.title + // [END_EXCLUDE] + }) + // [END post_value_event_listener] + } + + func indexOfMessage(_ snapshot: DataSnapshot) -> Int { + var index = 0 + for comment in comments { + if snapshot.key == comment.key { + return index + } + index += 1 + } + return -1 + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if let refHandle = refHandle { + postRef.removeObserver(withHandle: refHandle) + } + commentsRef.removeAllObservers() + if let uid = Auth.auth().currentUser?.uid { + Database.database().reference().child("users").child(uid).removeAllObservers() + } + } + + // UITableViewDataSource protocol methods + override func numberOfSections(in tableView: UITableView) -> Int { + return 3 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case kSectionPost, kSectionSend: + return 1 + case kSectionComments: + return comments.count + default: + return 0 + } + } + + @IBAction func didTapSend(_ sender: UIButton) { + _ = textFieldShouldReturn(commentField!) + commentField?.isEnabled = false + sender.isEnabled = false + if let uid = Auth.auth().currentUser?.uid { + Database.database().reference().child("users").child(uid) + .observeSingleEvent(of: .value, with: { snapshot in + if let commentField = self.commentField, + let user = snapshot.value as? [String: AnyObject] { + let comment = [ + "uid": uid, + "author": user["username"] as? String ?? "", + "text": commentField.text!, + ] + self.commentsRef.childByAutoId().setValue(comment) + commentField.text = "" + commentField.isEnabled = true + sender.isEnabled = true + } + }) + } + } + + override func tableView(_ tableView: UITableView, + cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell: UITableViewCell + + switch indexPath.section { + case kSectionPost: + cell = tableView.dequeueReusableCell(withIdentifier: "post", for: indexPath) + if let uid = Auth.auth().currentUser?.uid { + guard let postcell = cell as? PostTableViewCell else { + break + } + let imageName = post.stars == nil || post.stars![uid] == nil ? "ic_star_border" : "ic_star" + postcell.authorLabel.text = post.author + postcell.postTitle.text = post.title + postcell.postBody.text = post.body + postcell.starButton.setImage(UIImage(named: imageName), for: .normal) + if let starCount = post.starCount { + postcell.numStarsLabel.text = "\(starCount)" + } + postcell.postKey = postKey + } + case kSectionComments: + cell = tableView.dequeueReusableCell(withIdentifier: "comment", for: indexPath) + let commentDict = comments[indexPath.row].value as? [String: AnyObject] + if let text = cell.textLabel, let detail = cell.detailTextLabel, + let author = commentDict?["author"], let commentText = commentDict?["text"] { + detail.text = String(describing: author) + text.text = String(describing: commentText) + } + default: // kSectionSend + cell = tableView.dequeueReusableCell(withIdentifier: "send", for: indexPath) + commentField = cell.viewWithTag(7) as? UITextField + } + return cell + } + + override func tableView(_ tableView: UITableView, + heightForRowAt indexPath: IndexPath) -> CGFloat { + if indexPath.section == kSectionPost { + return 160 + } + return 56 + } +} diff --git a/qs-snippets/DatabaseExampleSwift/PostListViewController.swift b/qs-snippets/DatabaseExampleSwift/PostListViewController.swift new file mode 100644 index 00000000..89815480 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/PostListViewController.swift @@ -0,0 +1,106 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase +import FirebaseDatabaseUI +import FirebaseAuth + +@objc(PostListViewController) +class PostListViewController: UIViewController, UITableViewDelegate { + // [START define_database_reference] + var ref: DatabaseReference! + // [END define_database_reference] + + var dataSource: FUITableViewDataSource? + + @IBOutlet var tableView: UITableView! + + override func viewDidLoad() { + super.viewDidLoad() + + // [START create_database_reference] + ref = Database.database().reference() + // [END create_database_reference] + + let identifier = "post" + let nib = UINib(nibName: "PostTableViewCell", bundle: nil) + tableView.register(nib, forCellReuseIdentifier: identifier) + + dataSource = + FUITableViewDataSource(query: getQuery()) { (tableView, indexPath, snap) -> UITableViewCell in + let cell = tableView.dequeueReusableCell( + withIdentifier: identifier, + for: indexPath + ) as! PostTableViewCell + + guard let post = Post(snapshot: snap) else { return cell } + cell.authorImage.image = UIImage(named: "ic_account_circle") + cell.authorLabel.text = post.author + var imageName = "ic_star_border" + if (post.stars?[self.getUid()]) != nil { + imageName = "ic_star" + } + cell.starButton.setImage(UIImage(named: imageName), for: .normal) + if let starCount = post.starCount { + cell.numStarsLabel.text = "\(starCount)" + } + cell.postTitle.text = post.title + cell.postBody.text = post.body + return cell + } + + dataSource?.bind(to: tableView) + tableView.delegate = self + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + tableView.reloadData() + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + performSegue(withIdentifier: "detail", sender: indexPath) + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 150 + } + + func getUid() -> String { + return (Auth.auth().currentUser?.uid)! + } + + func getQuery() -> DatabaseQuery { + return ref + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard let indexPath: IndexPath = sender as? IndexPath else { return } + guard let detail: PostDetailTableViewController = segue + .destination as? PostDetailTableViewController else { + return + } + if let dataSource = dataSource { + detail.postKey = dataSource.snapshot(at: indexPath.row).key + } + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + getQuery().removeAllObservers() + } +} diff --git a/qs-snippets/DatabaseExampleSwift/PostTableViewCell.swift b/qs-snippets/DatabaseExampleSwift/PostTableViewCell.swift new file mode 100644 index 00000000..998f617d --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/PostTableViewCell.swift @@ -0,0 +1,82 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase +import FirebaseAuth + +@objc(PostTableViewCell) +class PostTableViewCell: UITableViewCell { + @IBOutlet var authorImage: UIImageView! + @IBOutlet var authorLabel: UILabel! + @IBOutlet var starButton: UIButton! + @IBOutlet var numStarsLabel: UILabel! + @IBOutlet var postTitle: UILabel! + @IBOutlet var postBody: UITextView! + var postKey: String? + var postRef: DatabaseReference! + + @IBAction func didTapStarButton(_ sender: AnyObject) { + if let postKey = postKey { + postRef = Database.database().reference().child("posts").child(postKey) + incrementStars(forRef: postRef) + postRef.observeSingleEvent(of: .value, with: { snapshot in + let value = snapshot.value as? NSDictionary + if let uid = value?["uid"] as? String { + let userPostRef = Database.database().reference() + .child("user-posts") + .child(uid) + .child(postKey) + self.incrementStars(forRef: userPostRef) + } + }) + } + } + + func incrementStars(forRef ref: DatabaseReference) { + // [START post_stars_transaction] + ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in + if var post = currentData.value as? [String: AnyObject], + let uid = Auth.auth().currentUser?.uid { + var stars: [String: Bool] + stars = post["stars"] as? [String: Bool] ?? [:] + var starCount = post["starCount"] as? Int ?? 0 + if let _ = stars[uid] { + // Unstar the post and remove self from stars + starCount -= 1 + stars.removeValue(forKey: uid) + } else { + // Star the post and add self to stars + starCount += 1 + stars[uid] = true + } + post["starCount"] = starCount as AnyObject? + post["stars"] = stars as AnyObject? + + // Set value and report transaction success + currentData.value = post + + return TransactionResult.success(withValue: currentData) + } + return TransactionResult.success(withValue: currentData) + }) { error, committed, snapshot in + if let error = error { + print(error.localizedDescription) + } + } + // [END post_stars_transaction] + } +} diff --git a/qs-snippets/DatabaseExampleSwift/RecentPostsViewController.swift b/qs-snippets/DatabaseExampleSwift/RecentPostsViewController.swift new file mode 100644 index 00000000..68710f6b --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/RecentPostsViewController.swift @@ -0,0 +1,30 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase + +@objc(RecentPostsViewController) +class RecentPostsViewController: PostListViewController { + override func getQuery() -> DatabaseQuery { + // [START recent_posts_query] + // Last 100 posts, these are automatically the 100 most recent + // due to sorting by push() keys + let recentPostsQuery = (ref?.child("posts").queryLimited(toFirst: 100))! + // [END recent_posts_query] + return recentPostsQuery + } +} diff --git a/qs-snippets/DatabaseExampleSwift/SignInViewController.swift b/qs-snippets/DatabaseExampleSwift/SignInViewController.swift new file mode 100644 index 00000000..750b1994 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/SignInViewController.swift @@ -0,0 +1,163 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase +import FirebaseAuth + +@objc(SignInViewController) +class SignInViewController: UIViewController, UITextFieldDelegate { + @IBOutlet var emailField: UITextField! + @IBOutlet var passwordField: UITextField! + var ref: DatabaseReference! + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + view.endEditing(true) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if Auth.auth().currentUser != nil { + performSegue(withIdentifier: "signIn", sender: nil) + } + ref = Database.database().reference() + } + + // Saves user profile information to user database + func saveUserInfo(_ user: FirebaseAuth.User, withUsername username: String) { + // Create a change request + showSpinner {} + let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest() + changeRequest?.displayName = username + + // Commit profile changes to server + changeRequest?.commitChanges { error in + + self.hideSpinner {} + + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + + // [START basic_write] + self.ref.child("users").child(user.uid).setValue(["username": username]) + // [END basic_write] + self.performSegue(withIdentifier: "signIn", sender: nil) + } + } + + @IBAction func didTapEmailLogin(_ sender: AnyObject) { + guard let email = emailField.text, let password = passwordField.text else { + showMessagePrompt("email/password can't be empty") + return + } + + showSpinner {} + + // Sign user in + Auth.auth().signIn(withEmail: email, password: password, completion: { authResult, error in + + self.hideSpinner {} + + guard let user = authResult?.user, error == nil else { + self.showMessagePrompt(error!.localizedDescription) + return + } + + self.ref.child("users").child(user.uid).observeSingleEvent(of: .value, with: { snapshot in + + // Check if user already exists + guard !snapshot.exists() else { + self.performSegue(withIdentifier: "signIn", sender: nil) + return + } + + // Otherwise, create the new user account + self.showTextInputPrompt(withMessage: "Username:") { userPressedOK, username in + + guard let username = username else { + self.showMessagePrompt("Username can't be empty") + return + } + + self.saveUserInfo(user, withUsername: username) + } + }) // End of observeSingleEvent + }) // End of signIn + } + + @IBAction func didTapSignUp(_ sender: AnyObject) { + func getEmail(completion: @escaping (String) -> Void) { + showTextInputPrompt(withMessage: "Email:") { userPressedOK, email in + guard let email = email else { + self.showMessagePrompt("Email can't be empty.") + return + } + completion(email) + } + } + + func getUsername(completion: @escaping (String) -> Void) { + showTextInputPrompt(withMessage: "Username:") { userPressedOK, username in + guard let username = username else { + self.showMessagePrompt("Username can't be empty.") + return + } + completion(username) + } + } + + func getPassword(completion: @escaping (String) -> Void) { + showTextInputPrompt(withMessage: "Password:") { userPressedOK, password in + guard let password = password else { + self.showMessagePrompt("Password can't be empty.") + return + } + completion(password) + } + } + + // Get the credentials of the user + getEmail { email in + getUsername { username in + getPassword { password in + + // Create the user with the provided credentials + Auth.auth() + .createUser(withEmail: email, password: password, completion: { authResult, error in + + guard let user = authResult?.user, error == nil else { + self.showMessagePrompt(error!.localizedDescription) + return + } + + // Finally, save their profile + self.saveUserInfo(user, withUsername: username) + + }) + } + } + } + } + + // MARK: - UITextFieldDelegate protocol methods + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + didTapEmailLogin(textField) + return true + } +} diff --git a/qs-snippets/DatabaseExampleSwift/TabBarController.swift b/qs-snippets/DatabaseExampleSwift/TabBarController.swift new file mode 100644 index 00000000..7bdbd20f --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/TabBarController.swift @@ -0,0 +1,33 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import FirebaseDatabase +import FirebaseAuth + +@objc(TabBarController) +class TabBarController: UITabBarController { + override func didMove(toParent parent: UIViewController?) { + if parent == nil { + let firebaseAuth = Auth.auth() + do { + try firebaseAuth.signOut() + } catch let signOutError as NSError { + print("Error signing out: %@", signOutError) + } + } + } +} diff --git a/qs-snippets/DatabaseExampleSwift/User.swift b/qs-snippets/DatabaseExampleSwift/User.swift new file mode 100644 index 00000000..e0065b66 --- /dev/null +++ b/qs-snippets/DatabaseExampleSwift/User.swift @@ -0,0 +1,29 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class User: NSObject { + var username: String + + init(username: String) { + self.username = username + } + + override convenience init() { + self.init(username: "") + } +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/project.pbxproj b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..990ddc7a --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/project.pbxproj @@ -0,0 +1,951 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 47; + objects = { + +/* Begin PBXBuildFile section */ + 107346F52031590C004A66D1 /* AuthenticationExampleSwiftUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 107346F42031590C004A66D1 /* AuthenticationExampleSwiftUITests.swift */; }; + 1073480D20333B18004A66D1 /* AuthenticationExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1073480C20333B18004A66D1 /* AuthenticationExampleUITests.m */; }; + 10757BD22059CC9E007623FD /* PasswordlessViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10757BD02059CC9E007623FD /* PasswordlessViewController.swift */; }; + 10757BD52059CFC7007623FD /* PasswordlessViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 10757BD42059CFC7007623FD /* PasswordlessViewController.m */; }; + 10AF4D111DD3966C0096D192 /* AuthenticationExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 10AF4D101DD3966C0096D192 /* AuthenticationExampleTests.m */; }; + 10B053F91C6631CD0061077D /* UIViewController+Alerts.m in Sources */ = {isa = PBXBuildFile; fileRef = 10B053F81C6631CD0061077D /* UIViewController+Alerts.m */; }; + 10B054031C6A3C630061077D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 10B054011C6A3C630061077D /* Main.storyboard */; }; + 5F5A53521ADE670C00F81DF0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A53511ADE670C00F81DF0 /* main.m */; }; + 5F5A53551ADE670C00F81DF0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A53541ADE670C00F81DF0 /* AppDelegate.m */; }; + 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */; }; + 5F99610A1AE0CF4F0034F503 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961061AE0CF4F0034F503 /* Images.xcassets */; }; + 5F99610B1AE0CF4F0034F503 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961061AE0CF4F0034F503 /* Images.xcassets */; }; + 5F99610C1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */; }; + 5F99610D1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */; }; + DE31A564244E2B9300A7924A /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE31A55F244DFC9800A7924A /* UIViewController.swift */; }; + DEC82E4423AAEDF7000EA7B1 /* FIREGSignInHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC82E3F23AAEDEC000EA7B1 /* FIREGSignInHelper.m */; }; + DEC82E4523AAEDFD000EA7B1 /* FIREGHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DEC82E4023AAEDEC000EA7B1 /* FIREGHelper.m */; }; + EF4857041C752B7700649485 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 10B054011C6A3C630061077D /* Main.storyboard */; }; + EF6DB4CB1CDD60F700319C08 /* CustomTokenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4C81CDD60F700319C08 /* CustomTokenViewController.swift */; }; + EF6DB4CC1CDD60F700319C08 /* EmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4C91CDD60F700319C08 /* EmailViewController.swift */; }; + EF6DB4CD1CDD60F700319C08 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4CA1CDD60F700319C08 /* MainViewController.swift */; }; + EF6DB4D41CDD610200319C08 /* CustomTokenViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4CE1CDD610200319C08 /* CustomTokenViewController.m */; }; + EF6DB4D51CDD610200319C08 /* EmailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4CF1CDD610200319C08 /* EmailViewController.m */; }; + EF6DB4D61CDD610200319C08 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4D01CDD610200319C08 /* MainViewController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 107346F72031590C004A66D1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5F5A53441ADE670C00F81DF0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5F5A53781ADE67D500F81DF0; + remoteInfo = AuthenticationExampleSwift; + }; + 1073480F20333B18004A66D1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5F5A53441ADE670C00F81DF0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5F5A534B1ADE670C00F81DF0; + remoteInfo = AuthenticationExample; + }; + 10AF4D131DD3966C0096D192 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5F5A53441ADE670C00F81DF0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5F5A534B1ADE670C00F81DF0; + remoteInfo = AuthenticationExample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 107346F22031590C004A66D1 /* AuthenticationExampleSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AuthenticationExampleSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 107346F42031590C004A66D1 /* AuthenticationExampleSwiftUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationExampleSwiftUITests.swift; sourceTree = ""; }; + 107346F62031590C004A66D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1073480A20333B18004A66D1 /* AuthenticationExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AuthenticationExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1073480C20333B18004A66D1 /* AuthenticationExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AuthenticationExampleUITests.m; sourceTree = ""; }; + 1073480E20333B18004A66D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 10757BD02059CC9E007623FD /* PasswordlessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordlessViewController.swift; sourceTree = ""; }; + 10757BD32059CFC7007623FD /* PasswordlessViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PasswordlessViewController.h; sourceTree = ""; }; + 10757BD42059CFC7007623FD /* PasswordlessViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PasswordlessViewController.m; sourceTree = ""; }; + 10757BDD205AD4FC007623FD /* AuthenticationExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AuthenticationExample.entitlements; sourceTree = ""; }; + 10AF4D0E1DD3966C0096D192 /* AuthenticationExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AuthenticationExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 10AF4D101DD3966C0096D192 /* AuthenticationExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AuthenticationExampleTests.m; sourceTree = ""; }; + 10B053F71C6631CD0061077D /* UIViewController+Alerts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Alerts.h"; sourceTree = ""; }; + 10B053F81C6631CD0061077D /* UIViewController+Alerts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Alerts.m"; sourceTree = ""; }; + 10B054021C6A3C630061077D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 5F5A534C1ADE670C00F81DF0 /* AuthenticationExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AuthenticationExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5F5A53501ADE670C00F81DF0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5F5A53511ADE670C00F81DF0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 5F5A53531ADE670C00F81DF0 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 5F5A53541ADE670C00F81DF0 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 5F5A53791ADE67D500F81DF0 /* AuthenticationExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AuthenticationExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 5F9961061AE0CF4F0034F503 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; + DE31A55F244DFC9800A7924A /* UIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + DEC82E3E23AAEDEC000EA7B1 /* FIREGHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIREGHelper.h; sourceTree = ""; }; + DEC82E3F23AAEDEC000EA7B1 /* FIREGSignInHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREGSignInHelper.m; sourceTree = ""; }; + DEC82E4023AAEDEC000EA7B1 /* FIREGHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREGHelper.m; sourceTree = ""; }; + DEC82E4123AAEDEC000EA7B1 /* FIREGSignInHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIREGSignInHelper.h; sourceTree = ""; }; + DED65CF723E9DE6400461312 /* FIREGSignInInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FIREGSignInInfo.h; sourceTree = ""; }; + EF6DB4C81CDD60F700319C08 /* CustomTokenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTokenViewController.swift; sourceTree = ""; }; + EF6DB4C91CDD60F700319C08 /* EmailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailViewController.swift; sourceTree = ""; }; + EF6DB4CA1CDD60F700319C08 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; + EF6DB4CE1CDD610200319C08 /* CustomTokenViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomTokenViewController.m; sourceTree = ""; }; + EF6DB4CF1CDD610200319C08 /* EmailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EmailViewController.m; sourceTree = ""; }; + EF6DB4D01CDD610200319C08 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; + EF6DB4D11CDD610200319C08 /* CustomTokenViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTokenViewController.h; sourceTree = ""; }; + EF6DB4D21CDD610200319C08 /* EmailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmailViewController.h; sourceTree = ""; }; + EF6DB4D31CDD610200319C08 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 107346EF2031590C004A66D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1073480720333B18004A66D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 10AF4D0B1DD3966C0096D192 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53491ADE670C00F81DF0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53761ADE67D500F81DF0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 107346F32031590C004A66D1 /* AuthenticationExampleSwiftUITests */ = { + isa = PBXGroup; + children = ( + 107346F42031590C004A66D1 /* AuthenticationExampleSwiftUITests.swift */, + 107346F62031590C004A66D1 /* Info.plist */, + ); + path = AuthenticationExampleSwiftUITests; + sourceTree = ""; + }; + 1073480B20333B18004A66D1 /* AuthenticationExampleUITests */ = { + isa = PBXGroup; + children = ( + 1073480C20333B18004A66D1 /* AuthenticationExampleUITests.m */, + 1073480E20333B18004A66D1 /* Info.plist */, + ); + path = AuthenticationExampleUITests; + sourceTree = ""; + }; + 10AF4D0F1DD3966C0096D192 /* AuthenticationExampleTests */ = { + isa = PBXGroup; + children = ( + 10AF4D101DD3966C0096D192 /* AuthenticationExampleTests.m */, + ); + path = AuthenticationExampleTests; + sourceTree = ""; + }; + 5F5A53431ADE670C00F81DF0 = { + isa = PBXGroup; + children = ( + 5F5A534E1ADE670C00F81DF0 /* AuthenticationExample */, + 5F5A537A1ADE67D500F81DF0 /* AuthenticationExampleSwift */, + 10AF4D0F1DD3966C0096D192 /* AuthenticationExampleTests */, + 107346F32031590C004A66D1 /* AuthenticationExampleSwiftUITests */, + 1073480B20333B18004A66D1 /* AuthenticationExampleUITests */, + DEC82E3D23AAEDEC000EA7B1 /* TestUtils */, + 5F5A534D1ADE670C00F81DF0 /* Products */, + 5F9961041AE0CF4F0034F503 /* Shared */, + ); + sourceTree = ""; + wrapsLines = 0; + }; + 5F5A534D1ADE670C00F81DF0 /* Products */ = { + isa = PBXGroup; + children = ( + 5F5A534C1ADE670C00F81DF0 /* AuthenticationExample.app */, + 5F5A53791ADE67D500F81DF0 /* AuthenticationExample.app */, + 10AF4D0E1DD3966C0096D192 /* AuthenticationExampleTests.xctest */, + 107346F22031590C004A66D1 /* AuthenticationExampleSwiftUITests.xctest */, + 1073480A20333B18004A66D1 /* AuthenticationExampleUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 5F5A534E1ADE670C00F81DF0 /* AuthenticationExample */ = { + isa = PBXGroup; + children = ( + 10757BDD205AD4FC007623FD /* AuthenticationExample.entitlements */, + 10757BD32059CFC7007623FD /* PasswordlessViewController.h */, + 10757BD42059CFC7007623FD /* PasswordlessViewController.m */, + EF6DB4D11CDD610200319C08 /* CustomTokenViewController.h */, + EF6DB4CE1CDD610200319C08 /* CustomTokenViewController.m */, + EF6DB4D21CDD610200319C08 /* EmailViewController.h */, + EF6DB4CF1CDD610200319C08 /* EmailViewController.m */, + EF6DB4D31CDD610200319C08 /* MainViewController.h */, + EF6DB4D01CDD610200319C08 /* MainViewController.m */, + 5F5A53531ADE670C00F81DF0 /* AppDelegate.h */, + 5F5A53541ADE670C00F81DF0 /* AppDelegate.m */, + 5F5A534F1ADE670C00F81DF0 /* Supporting Files */, + ); + path = AuthenticationExample; + sourceTree = ""; + }; + 5F5A534F1ADE670C00F81DF0 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 10B053F71C6631CD0061077D /* UIViewController+Alerts.h */, + 10B053F81C6631CD0061077D /* UIViewController+Alerts.m */, + 10B054011C6A3C630061077D /* Main.storyboard */, + 5F5A53501ADE670C00F81DF0 /* Info.plist */, + 5F5A53511ADE670C00F81DF0 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 5F5A537A1ADE67D500F81DF0 /* AuthenticationExampleSwift */ = { + isa = PBXGroup; + children = ( + 10757BD02059CC9E007623FD /* PasswordlessViewController.swift */, + EF6DB4C81CDD60F700319C08 /* CustomTokenViewController.swift */, + EF6DB4C91CDD60F700319C08 /* EmailViewController.swift */, + EF6DB4CA1CDD60F700319C08 /* MainViewController.swift */, + 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */, + DE31A55F244DFC9800A7924A /* UIViewController.swift */, + ); + path = AuthenticationExampleSwift; + sourceTree = ""; + }; + 5F9961041AE0CF4F0034F503 /* Shared */ = { + isa = PBXGroup; + children = ( + 5F9961061AE0CF4F0034F503 /* Images.xcassets */, + 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */, + ); + name = Shared; + path = ../../shared; + sourceTree = ""; + }; + DEC82E3D23AAEDEC000EA7B1 /* TestUtils */ = { + isa = PBXGroup; + children = ( + DEC82E3E23AAEDEC000EA7B1 /* FIREGHelper.h */, + DEC82E3F23AAEDEC000EA7B1 /* FIREGSignInHelper.m */, + DEC82E4023AAEDEC000EA7B1 /* FIREGHelper.m */, + DEC82E4123AAEDEC000EA7B1 /* FIREGSignInHelper.h */, + DED65CF723E9DE6400461312 /* FIREGSignInInfo.h */, + ); + name = TestUtils; + path = ../TestUtils; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 107346F12031590C004A66D1 /* AuthenticationExampleSwiftUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 107346F92031590C004A66D1 /* Build configuration list for PBXNativeTarget "AuthenticationExampleSwiftUITests" */; + buildPhases = ( + 107346EE2031590C004A66D1 /* Sources */, + 107346EF2031590C004A66D1 /* Frameworks */, + 107346F02031590C004A66D1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 107346F82031590C004A66D1 /* PBXTargetDependency */, + ); + name = AuthenticationExampleSwiftUITests; + productName = AuthenticationExampleSwiftUITests; + productReference = 107346F22031590C004A66D1 /* AuthenticationExampleSwiftUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 1073480920333B18004A66D1 /* AuthenticationExampleUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1073481120333B18004A66D1 /* Build configuration list for PBXNativeTarget "AuthenticationExampleUITests" */; + buildPhases = ( + 1073480620333B18004A66D1 /* Sources */, + 1073480720333B18004A66D1 /* Frameworks */, + 1073480820333B18004A66D1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 1073481020333B18004A66D1 /* PBXTargetDependency */, + ); + name = AuthenticationExampleUITests; + productName = AuthenticationExampleUITests; + productReference = 1073480A20333B18004A66D1 /* AuthenticationExampleUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 10AF4D0D1DD3966C0096D192 /* AuthenticationExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 10AF4D151DD3966C0096D192 /* Build configuration list for PBXNativeTarget "AuthenticationExampleTests" */; + buildPhases = ( + 10AF4D0A1DD3966C0096D192 /* Sources */, + 10AF4D0B1DD3966C0096D192 /* Frameworks */, + 10AF4D0C1DD3966C0096D192 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 10AF4D141DD3966C0096D192 /* PBXTargetDependency */, + ); + name = AuthenticationExampleTests; + productName = AuthenticationExampleTests; + productReference = 10AF4D0E1DD3966C0096D192 /* AuthenticationExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 5F5A534B1ADE670C00F81DF0 /* AuthenticationExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5F5A536F1ADE670C00F81DF0 /* Build configuration list for PBXNativeTarget "AuthenticationExample" */; + buildPhases = ( + 5F5A53481ADE670C00F81DF0 /* Sources */, + 5F5A53491ADE670C00F81DF0 /* Frameworks */, + 5F5A534A1ADE670C00F81DF0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AuthenticationExample; + productName = UserManagementExample; + productReference = 5F5A534C1ADE670C00F81DF0 /* AuthenticationExample.app */; + productType = "com.apple.product-type.application"; + }; + 5F5A53781ADE67D500F81DF0 /* AuthenticationExampleSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "AuthenticationExampleSwift" */; + buildPhases = ( + 5F5A53751ADE67D500F81DF0 /* Sources */, + 5F5A53761ADE67D500F81DF0 /* Frameworks */, + 5F5A53771ADE67D500F81DF0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AuthenticationExampleSwift; + productName = UserManagementExampleSwift; + productReference = 5F5A53791ADE67D500F81DF0 /* AuthenticationExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5F5A53441ADE670C00F81DF0 /* Project object */ = { + isa = PBXProject; + attributes = { + KnownAssetTags = ( + New, + ); + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1110; + ORGANIZATIONNAME = "Google Inc."; + TargetAttributes = { + 107346F12031590C004A66D1 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1110; + ProvisioningStyle = Automatic; + TestTargetID = 5F5A53781ADE67D500F81DF0; + }; + 1073480920333B18004A66D1 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + TestTargetID = 5F5A534B1ADE670C00F81DF0; + }; + 10AF4D0D1DD3966C0096D192 = { + CreatedOnToolsVersion = 8.1; + ProvisioningStyle = Automatic; + TestTargetID = 5F5A534B1ADE670C00F81DF0; + }; + 5F5A534B1ADE670C00F81DF0 = { + CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 1140; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 1; + }; + com.apple.SafariKeychain = { + enabled = 1; + }; + }; + }; + 5F5A53781ADE67D500F81DF0 = { + CreatedOnToolsVersion = 6.3; + LastSwiftMigration = 1110; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 1; + }; + com.apple.SafariKeychain = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "AuthenticationExample" */; + compatibilityVersion = "Xcode 6.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5F5A53431ADE670C00F81DF0; + productRefGroup = 5F5A534D1ADE670C00F81DF0 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5F5A534B1ADE670C00F81DF0 /* AuthenticationExample */, + 5F5A53781ADE67D500F81DF0 /* AuthenticationExampleSwift */, + 10AF4D0D1DD3966C0096D192 /* AuthenticationExampleTests */, + 107346F12031590C004A66D1 /* AuthenticationExampleSwiftUITests */, + 1073480920333B18004A66D1 /* AuthenticationExampleUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 107346F02031590C004A66D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1073480820333B18004A66D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 10AF4D0C1DD3966C0096D192 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A534A1ADE670C00F81DF0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5F99610C1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */, + 10B054031C6A3C630061077D /* Main.storyboard in Resources */, + 5F99610A1AE0CF4F0034F503 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53771ADE67D500F81DF0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EF4857041C752B7700649485 /* Main.storyboard in Resources */, + 5F99610D1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */, + 5F99610B1AE0CF4F0034F503 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 107346EE2031590C004A66D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 107346F52031590C004A66D1 /* AuthenticationExampleSwiftUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1073480620333B18004A66D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1073480D20333B18004A66D1 /* AuthenticationExampleUITests.m in Sources */, + DEC82E4423AAEDF7000EA7B1 /* FIREGSignInHelper.m in Sources */, + DEC82E4523AAEDFD000EA7B1 /* FIREGHelper.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 10AF4D0A1DD3966C0096D192 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 10AF4D111DD3966C0096D192 /* AuthenticationExampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53481ADE670C00F81DF0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 10757BD52059CFC7007623FD /* PasswordlessViewController.m in Sources */, + EF6DB4D51CDD610200319C08 /* EmailViewController.m in Sources */, + EF6DB4D61CDD610200319C08 /* MainViewController.m in Sources */, + 10B053F91C6631CD0061077D /* UIViewController+Alerts.m in Sources */, + EF6DB4D41CDD610200319C08 /* CustomTokenViewController.m in Sources */, + 5F5A53551ADE670C00F81DF0 /* AppDelegate.m in Sources */, + 5F5A53521ADE670C00F81DF0 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53751ADE67D500F81DF0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DE31A564244E2B9300A7924A /* UIViewController.swift in Sources */, + 10757BD22059CC9E007623FD /* PasswordlessViewController.swift in Sources */, + 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */, + EF6DB4CB1CDD60F700319C08 /* CustomTokenViewController.swift in Sources */, + EF6DB4CC1CDD60F700319C08 /* EmailViewController.swift in Sources */, + EF6DB4CD1CDD60F700319C08 /* MainViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 107346F82031590C004A66D1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5F5A53781ADE67D500F81DF0 /* AuthenticationExampleSwift */; + targetProxy = 107346F72031590C004A66D1 /* PBXContainerItemProxy */; + }; + 1073481020333B18004A66D1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5F5A534B1ADE670C00F81DF0 /* AuthenticationExample */; + targetProxy = 1073480F20333B18004A66D1 /* PBXContainerItemProxy */; + }; + 10AF4D141DD3966C0096D192 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5F5A534B1ADE670C00F81DF0 /* AuthenticationExample */; + targetProxy = 10AF4D131DD3966C0096D192 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 10B054011C6A3C630061077D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 10B054021C6A3C630061077D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 107346FA2031590C004A66D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = AuthenticationExampleSwiftUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExampleSwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = AuthenticationExampleSwift; + }; + name = Debug; + }; + 107346FB2031590C004A66D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = AuthenticationExampleSwiftUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExampleSwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = AuthenticationExampleSwift; + }; + name = Release; + }; + 1073481220333B18004A66D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = AuthenticationExampleUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExampleUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = AuthenticationExample; + }; + name = Debug; + }; + 1073481320333B18004A66D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = AuthenticationExampleUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExampleUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = AuthenticationExample; + }; + name = Release; + }; + 10AF4D161DD3966C0096D192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = AuthenticationExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.samples.AuthenticationExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AuthenticationExample.app/AuthenticationExample"; + }; + name = Debug; + }; + 10AF4D171DD3966C0096D192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = AuthenticationExample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.samples.AuthenticationExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AuthenticationExample.app/AuthenticationExample"; + }; + name = Release; + }; + 5F5A536D1ADE670C00F81DF0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 5F5A536E1ADE670C00F81DF0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_BITCODE = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5F5A53701ADE670C00F81DF0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = "$(SRCROOT)/AuthenticationExample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExample; + PRODUCT_NAME = AuthenticationExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 5F5A53711ADE670C00F81DF0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = "$(SRCROOT)/AuthenticationExample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExample; + PRODUCT_NAME = AuthenticationExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + 5F5A53951ADE67D500F81DF0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = "$(SRCROOT)/AuthenticationExample/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExample; + PRODUCT_NAME = AuthenticationExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 5F5A53961ADE67D500F81DF0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_ENTITLEMENTS = AuthenticationExample/AuthenticationExample.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = "$(SRCROOT)/AuthenticationExample/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.AuthenticationExample; + PRODUCT_NAME = AuthenticationExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 107346F92031590C004A66D1 /* Build configuration list for PBXNativeTarget "AuthenticationExampleSwiftUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 107346FA2031590C004A66D1 /* Debug */, + 107346FB2031590C004A66D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1073481120333B18004A66D1 /* Build configuration list for PBXNativeTarget "AuthenticationExampleUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1073481220333B18004A66D1 /* Debug */, + 1073481320333B18004A66D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 10AF4D151DD3966C0096D192 /* Build configuration list for PBXNativeTarget "AuthenticationExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 10AF4D161DD3966C0096D192 /* Debug */, + 10AF4D171DD3966C0096D192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "AuthenticationExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5F5A536D1ADE670C00F81DF0 /* Debug */, + 5F5A536E1ADE670C00F81DF0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5F5A536F1ADE670C00F81DF0 /* Build configuration list for PBXNativeTarget "AuthenticationExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5F5A53701ADE670C00F81DF0 /* Debug */, + 5F5A53711ADE670C00F81DF0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "AuthenticationExampleSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5F5A53951ADE67D500F81DF0 /* Debug */, + 5F5A53961ADE67D500F81DF0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5F5A53441ADE670C00F81DF0 /* Project object */; +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/xcshareddata/xcschemes/AuthenticationExample.xcscheme b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/xcshareddata/xcschemes/AuthenticationExample.xcscheme new file mode 100644 index 00000000..c1acaeed --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/xcshareddata/xcschemes/AuthenticationExample.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/invites/InvitesExample.xcodeproj/xcshareddata/xcschemes/InvitesExampleSwift.xcscheme b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/xcshareddata/xcschemes/AuthenticationExampleSwift.xcscheme similarity index 65% rename from invites/InvitesExample.xcodeproj/xcshareddata/xcschemes/InvitesExampleSwift.xcscheme rename to qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/xcshareddata/xcschemes/AuthenticationExampleSwift.xcscheme index ca8c6c54..b592ace3 100644 --- a/invites/InvitesExample.xcodeproj/xcshareddata/xcschemes/InvitesExampleSwift.xcscheme +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample.xcodeproj/xcshareddata/xcschemes/AuthenticationExampleSwift.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "5F5A53781ADE67D500F81DF0" + BuildableName = "AuthenticationExample.app" + BlueprintName = "AuthenticationExampleSwift" + ReferencedContainer = "container:AuthenticationExample.xcodeproj"> @@ -27,29 +27,27 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + BlueprintIdentifier = "107346F12031590C004A66D1" + BuildableName = "AuthenticationExampleSwiftUITests.xctest" + BlueprintName = "AuthenticationExampleSwiftUITests" + ReferencedContainer = "container:AuthenticationExample.xcodeproj"> - - - - - - + BlueprintIdentifier = "5F5A53781ADE67D500F81DF0" + BuildableName = "AuthenticationExample.app" + BlueprintName = "AuthenticationExampleSwift" + ReferencedContainer = "container:AuthenticationExample.xcodeproj"> - - + BlueprintIdentifier = "5F5A53781ADE67D500F81DF0" + BuildableName = "AuthenticationExample.app" + BlueprintName = "AuthenticationExampleSwift" + ReferencedContainer = "container:AuthenticationExample.xcodeproj"> diff --git a/invites/InvitesExample/.clang-format b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/.clang-format similarity index 100% rename from invites/InvitesExample/.clang-format rename to qs-snippets/LegacyAuthQuickstart/AuthenticationExample/.clang-format diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AppDelegate.h b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AppDelegate.h new file mode 100644 index 00000000..00665f37 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AppDelegate.h @@ -0,0 +1,27 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import UIKit; + +@import GoogleSignIn; + +// [START signin_delegate] +@interface AppDelegate : UIResponder +// [END signin_delegate] + +@property(nonatomic, strong) UIWindow *window; + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AppDelegate.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AppDelegate.m new file mode 100644 index 00000000..599a79ed --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AppDelegate.m @@ -0,0 +1,97 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "AppDelegate.h" +#import "MainViewController.h" +#import "UIViewController+Alerts.h" +// [START auth_import] +@import FirebaseCore; +// [END auth_import] + +// [START google_import] +@import GoogleSignIn; +// [END google_import] +@import FBSDKCoreKit; + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // [START firebase_configure] + // Use Firebase library to configure APIs + [FIRApp configure]; + // [END firebase_configure] + + [[FBSDKApplicationDelegate sharedInstance] application:application + didFinishLaunchingWithOptions:launchOptions]; + return YES; +} + +// [START new_delegate] +- (BOOL)application:(nonnull UIApplication *)application + openURL:(nonnull NSURL *)url + options:(nonnull NSDictionary *)options { + // [END new_delegate] + return [self application:application + openURL:url + // [START new_options] + sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] + annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]; +} +// [END new_options] + +// [START old_delegate] +- (BOOL)application:(UIApplication *)application + openURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation { + // [END old_delegate] + if ([self handlePasswordlessSignInWithLink:url]) { + return YES; + } + if ([[GIDSignIn sharedInstance] handleURL:url]) { + return YES; + } + return [[FBSDKApplicationDelegate sharedInstance] application:application + openURL:url + // [START old_options] + sourceApplication:sourceApplication + annotation:annotation]; +} +// [END old_options] + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000 +- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> * _Nullable))restorationHandler { +#else +- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray * __nullable restorableObjects))restorationHandler { +#endif + return [self handlePasswordlessSignInWithLink:userActivity.webpageURL]; +} + +- (BOOL)handlePasswordlessSignInWithLink:(nonnull NSURL*)url { + NSString *link = url.absoluteString; + // [START is_signin_link] + if ([[FIRAuth auth] isSignInWithEmailLink:link]) { + // [END is_signin_link] + [NSUserDefaults.standardUserDefaults setObject:link forKey:@"Link"]; + [(UINavigationController*)_window.rootViewController popToRootViewControllerAnimated:NO]; + [_window.rootViewController.childViewControllers[0] performSegueWithIdentifier:@"passwordless" sender:nil]; + return YES; + } + return NO; +} + +@end diff --git a/invites/InvitesExample/InvitesExample.entitlements b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AuthenticationExample.entitlements similarity index 65% rename from invites/InvitesExample/InvitesExample.entitlements rename to qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AuthenticationExample.entitlements index bdcfc1a4..f2ab301e 100644 --- a/invites/InvitesExample/InvitesExample.entitlements +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/AuthenticationExample.entitlements @@ -2,9 +2,13 @@ + com.apple.developer.applesignin + + Default + com.apple.developer.associated-domains - applinks:n8r9f.app.goo.gl + applinks:sen9z.app.goo.gl diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/Base.lproj/Main.storyboard b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..df395447 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/Base.lproj/Main.storyboard @@ -0,0 +1,588 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Please enter your custom token here, which is signed using the private key from a service account downloaded from the Google Developer Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/CustomTokenViewController.h b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/CustomTokenViewController.h new file mode 100644 index 00000000..a787bbba --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/CustomTokenViewController.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import UIKit; + +@interface CustomTokenViewController : UIViewController +@end \ No newline at end of file diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/CustomTokenViewController.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/CustomTokenViewController.m new file mode 100644 index 00000000..268b7557 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/CustomTokenViewController.m @@ -0,0 +1,54 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "CustomTokenViewController.h" +#import "UIViewController+Alerts.h" + +// [START auth_view_import] +@import FirebaseAuth; +// [END auth_view_import] + +@interface CustomTokenViewController () +@property(weak, nonatomic) IBOutlet UITextView *tokenField; +@end +@implementation CustomTokenViewController + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self.view endEditing:YES]; +} + +- (IBAction)didTapCustomTokenLogin:(id)sender { + NSString *customToken = _tokenField.text; + [self showSpinner:^{ + // [START signinwithcustomtoken] + [[FIRAuth auth] signInWithCustomToken:customToken + completion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self.navigationController popViewControllerAnimated:YES]; + }]; + // [END_EXCLUDE] + }]; + // [END signinwithcustomtoken] + }]; +} + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/EmailViewController.h b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/EmailViewController.h new file mode 100644 index 00000000..da4c22d4 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/EmailViewController.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface EmailViewController : UIViewController + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/EmailViewController.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/EmailViewController.m new file mode 100644 index 00000000..4d700144 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/EmailViewController.m @@ -0,0 +1,191 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "EmailViewController.h" +#import "UIViewController+Alerts.h" + +@import FirebaseAuth; + +@interface EmailViewController () +@property(weak, nonatomic) IBOutlet UITextField *emailField; +@property(weak, nonatomic) IBOutlet UITextField *passwordField; +@end + +@implementation EmailViewController + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self.view endEditing:YES]; +} + +- (IBAction)didTapEmailLogin:(id)sender { + [self showSpinner:^{ + // [START headless_email_auth] + [[FIRAuth auth] signInWithEmail:self->_emailField.text + password:self->_passwordField.text + completion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error && error.code == FIRAuthErrorCodeSecondFactorRequired) { + FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey]; + NSMutableString *displayNameString = [NSMutableString string]; + for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { + [displayNameString appendString:tmpFactorInfo.displayName]; + [displayNameString appendString:@" "]; + } + [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString] + completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) { + FIRPhoneMultiFactorInfo* selectedHint; + for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { + if ([displayName isEqualToString:tmpFactorInfo.displayName]) { + selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo; + } + } + [FIRPhoneAuthProvider.provider + verifyPhoneNumberWithMultiFactorInfo:selectedHint + UIDelegate:nil + multiFactorSession:resolver.session + completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { + if (error) { + NSLog(@"Multi factor start sign in failed. Error: %@", error.description); + } else { + [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName] + completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) { + FIRPhoneAuthCredential *credential = + [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID + verificationCode:verificationCode]; + FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential]; + [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { + if (error) { + NSLog(@"Multi factor finanlize sign in failed. Error: %@", error.description); + } else { + [self.navigationController popViewControllerAnimated:YES]; + } + }]; + }]; + } + }]; + }]; + } else if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self.navigationController popViewControllerAnimated:YES]; + }]; + // [END_EXCLUDE] + }]; + // [END headless_email_auth] + }]; +} + +/** @fn requestPasswordReset + @brief Requests a "password reset" email be sent. + */ +- (IBAction)didRequestPasswordReset:(id)sender { + [self showTextInputPromptWithMessage:@"Email:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [START password_reset] + [[FIRAuth auth] sendPasswordResetWithEmail:userInput completion:^(NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + + [self showMessagePrompt:@"Sent"]; + }]; + // [END_EXCLUDE] + }]; + // [END password_reset] + }]; + }]; +} + +/** @fn getProvidersForEmail + @brief Prompts the user for an email address, calls @c FIRAuth.getProvidersForEmail:callback: + and displays the result. + */ +- (IBAction)didGetProvidersForEmail:(id)sender { + [self showTextInputPromptWithMessage:@"Email:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [START get_methods] + [[FIRAuth auth] fetchSignInMethodsForEmail:userInput + completion:^(NSArray *_Nullable providers, + NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + + [self showMessagePrompt:[providers componentsJoinedByString:@", "]]; + }]; + // [END_EXCLUDE] + }]; + // [END get_methods] + }]; + }]; +} + +- (IBAction)didCreateAccount:(id)sender { + [self showTextInputPromptWithMessage:@"Email:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) { + if (!userPressedOK || !email.length) { + return; + } + + [self showTextInputPromptWithMessage:@"Password:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) { + if (!userPressedOK || !password.length) { + return; + } + + [self showSpinner:^{ + // [START create_user] + [[FIRAuth auth] createUserWithEmail:email + password:password + completion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt: error.localizedDescription]; + return; + } + NSLog(@"%@ created", authResult.user.email); + [self.navigationController popViewControllerAnimated:YES]; + }]; + // [END_EXCLUDE] + }]; + // [END create_user] + }]; + }]; + }]; +} + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/Info.plist b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/Info.plist new file mode 100644 index 00000000..ac8f7590 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/Info.plist @@ -0,0 +1,107 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIcons + + CFBundleIcons~ipad + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + Client + CFBundleURLSchemes + + REVERSED_CLIENT_ID + + + + CFBundleVersion + 1 + FacebookAppID + Placeholder + FacebookDisplayName + Dev + LSApplicationQueriesSchemes + + fbapi + fbauth2 + + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSExceptionDomains + + akamaihd.net + + NSIncludesSubdomains + + NSThirdPartyExceptionRequiresForwardSecrecy + + + facebook.com + + NSIncludesSubdomains + + NSThirdPartyExceptionRequiresForwardSecrecy + + + fbcdn.net + + NSIncludesSubdomains + + NSThirdPartyExceptionRequiresForwardSecrecy + + + twimg.com + + NSIncludesSubdomains + + NSTemporaryExceptionAllowsInsecureHTTPLoads + + + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/MainViewController.h b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/MainViewController.h new file mode 100644 index 00000000..d9d82f4b --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/MainViewController.h @@ -0,0 +1,26 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@import GoogleSignIn; +@import FirebaseAuth; + +// [START signin_controller] +@interface MainViewController : UITableViewController +// [END signin_controller] +- (void)firebaseLoginWithCredential:(FIRAuthCredential *)credential; +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/MainViewController.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/MainViewController.m new file mode 100644 index 00000000..b3220651 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/MainViewController.m @@ -0,0 +1,1197 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MainViewController.h" +#import "UIViewController+Alerts.h" + +#import +#import + +@import AuthenticationServices; +@import FBSDKCoreKit; +@import FBSDKLoginKit; +@import FirebaseCore; +@import FirebaseAnalytics; + +static const int kSectionMultiFactor = 4; +static const int kSectionToken = 3; +static const int kSectionProviders = 2; +static const int kSectionUser = 1; +static const int kSectionSignIn = 0; + +typedef enum : NSUInteger { + AuthEmail, + AuthAnonymous, + AuthApple, + AuthFacebook, + AuthGoogle, + AuthTwitter, + AuthGitHub, + AuthCustom, + AuthPhone, + AuthPasswordless, + AuthGameCenter, + AuthMicrosoft, + AuthEmailMFA, +} AuthProvider; + +/*! @var kOKButtonText + @brief The text of the "OK" button for the Sign In result dialogs. + */ +static NSString *const kOKButtonText = @"OK"; + +/*! @var kTokenRefreshedAlertTitle + @brief The title of the "Token Refreshed" alert. + */ +static NSString *const kTokenRefreshedAlertTitle = @"Token"; + +/*! @var kTokenRefreshErrorAlertTitle + @brief The title of the "Token Refresh error" alert. + */ +static NSString *const kTokenRefreshErrorAlertTitle = @"Get Token Error"; + +/** @var kSetDisplayNameTitle + @brief The title of the "Set Display Name" error dialog. + */ +static NSString *const kSetDisplayNameTitle = @"Set Display Name"; + +/** @var kUnlinkTitle + @brief The text of the "Unlink from Provider" error Dialog. + */ +static NSString *const kUnlinkTitle = @"Unlink from Provider"; + +/** @var kChangeEmailText + @brief The title of the "Change Email" button. + */ +static NSString *const kChangeEmailText = @"Change Email"; + +/** @var kChangePasswordText + @brief The title of the "Change Password" button. + */ +static NSString *const kChangePasswordText = @"Change Password"; + +/** @var kUpdatePhoneNumberText + @brief The title of the "Update Phone Number" button. + */ +static NSString *const kUpdatePhoneNumberText = @"Update Phone Number"; + +static BOOL isMFAEnabled = NO; + +@interface MainViewController () +@property(strong, nonatomic) FIRAuthStateDidChangeListenerHandle handle; +@property(strong, nonatomic) FIROAuthProvider *microsoftProvider; +@property(strong, nonatomic) FIROAuthProvider *twitterProvider; +@property(strong, nonatomic) FIROAuthProvider *gitHubProvider; +@end + +@interface MainViewController (SignInWithApple) + +@property(nonatomic, readwrite, nullable) NSString *currentNonce; + +- (void)startSignInWithAppleFlow API_AVAILABLE(ios(13.0)); + +- (void)startSignInWithGoogleFlow; + +@end + +@implementation MainViewController { + NSString *_currentNonce; +} + +- (void)firebaseLoginWithCredential:(FIRAuthCredential *)credential { + [self showSpinner:^{ + if ([FIRAuth auth].currentUser) { + // [START link_credential] + [[FIRAuth auth].currentUser linkWithCredential:credential + completion:^(FIRAuthDataResult *result, NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self.tableView reloadData]; + }]; + // [END_EXCLUDE] + }]; + // [END link_credential] + } else { + // [START signin_credential] + [[FIRAuth auth] signInWithCredential:credential + completion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + // [START_EXCLUDE silent] + [self hideSpinner:^{ + // [END_EXCLUDE] + if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) { + FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey]; + NSMutableString *displayNameString = [NSMutableString string]; + for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { + [displayNameString appendString:tmpFactorInfo.displayName]; + [displayNameString appendString:@" "]; + } + [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString] + completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) { + FIRPhoneMultiFactorInfo* selectedHint; + for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) { + if ([displayName isEqualToString:tmpFactorInfo.displayName]) { + selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo; + } + } + [FIRPhoneAuthProvider.provider + verifyPhoneNumberWithMultiFactorInfo:selectedHint + UIDelegate:nil + multiFactorSession:resolver.session + completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { + if (error) { + [self showMessagePrompt:error.localizedDescription]; + } else { + [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName] + completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) { + FIRPhoneAuthCredential *credential = + [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID + verificationCode:verificationCode]; + FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential]; + [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { + if (error) { + [self showMessagePrompt:error.localizedDescription]; + } else { + NSLog(@"Multi factor finanlize sign in succeeded."); + } + }]; + }]; + } + }]; + }]; + } + else if (error) { + // [START_EXCLUDE] + [self showMessagePrompt:error.localizedDescription]; + // [END_EXCLUDE] + return; + } + // User successfully signed in. Get user data from the FIRUser object + if (authResult == nil) { return; } + FIRUser *user = authResult.user; + // [START_EXCLUDE] + }]; + // [END_EXCLUDE] + }]; + // [END signin_credential] + } + }]; +} + +- (void)showAuthPicker: (NSArray*) providers { + UIAlertController *picker = + [UIAlertController alertControllerWithTitle:@"Select Provider" + message:nil + preferredStyle:UIAlertControllerStyleAlert]; + + for (NSNumber *provider in providers) { + UIAlertAction *action; + switch (provider.unsignedIntegerValue) { + case AuthEmail: + { + action = [UIAlertAction actionWithTitle:@"Email" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self performSegueWithIdentifier:@"email" sender:nil]; + }]; + } + break; + case AuthEmailMFA: + { + action = [UIAlertAction actionWithTitle:@"Email with MFA" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + isMFAEnabled = YES; + [self performSegueWithIdentifier:@"email" sender:nil]; + }]; + } + break; + case AuthPasswordless: + { + action = [UIAlertAction actionWithTitle:@"Passwordless" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self performSegueWithIdentifier:@"passwordless" sender:nil]; + }]; + } + break; + case AuthCustom: + { + action = [UIAlertAction actionWithTitle:@"Custom" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self performSegueWithIdentifier:@"customToken" sender:nil]; + }]; + } + break; + case AuthApple: + { + if (@available(iOS 13, *)) { + action = [UIAlertAction actionWithTitle:@"Apple" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self startSignInWithAppleFlow]; + }]; + } else { + continue; + } + } + break; + case AuthTwitter: + { + action = [UIAlertAction actionWithTitle:@"Twitter" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + // [START firebase_auth_twitter] + [self.twitterProvider getCredentialWithUIDelegate:nil + completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { + [self showSpinner:^{ + if (error) { + [self hideSpinner:^{ + [self showMessagePrompt:error.localizedDescription]; + return; + }]; + } + if (credential) { + [[FIRAuth auth] signInWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + }]; + }]; + } + }]; + }]; + // [END firebase_auth_twitter] + }]; + } + break; + case AuthGitHub: + { + action = [UIAlertAction actionWithTitle:@"GitHub" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + // [START firebase_auth_github] + [self.gitHubProvider getCredentialWithUIDelegate:nil + completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { + [self showSpinner:^{ + if (error) { + [self hideSpinner:^{ + [self showMessagePrompt:error.localizedDescription]; + return; + }]; + } + if (credential) { + [[FIRAuth auth] signInWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + }]; + }]; + } + }]; + }]; + // [END firebase_auth_github] + }]; + } + break; + case AuthFacebook: { + action = [UIAlertAction actionWithTitle:@"Facebook" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init]; + [loginManager logInWithPermissions:@[ @"public_profile", @"email" ] + fromViewController:self + handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { + if (error) { + [self showMessagePrompt:error.localizedDescription]; + } else if (result.isCancelled) { + NSLog(@"FBLogin cancelled"); + } else { + // [START headless_facebook_auth] + FIRAuthCredential *credential = [FIRFacebookAuthProvider + credentialWithAccessToken:[FBSDKAccessToken currentAccessToken].tokenString]; + // [END headless_facebook_auth] + [self firebaseLoginWithCredential:credential]; + } + }]; + }]; + } + break; + case AuthGoogle: { + action = [UIAlertAction actionWithTitle:@"Google" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self startSignInWithGoogleFlow]; + }]; + } + break; + case AuthPhone: { + action = [UIAlertAction actionWithTitle:@"Phone" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self showTextInputPromptWithMessage:@"Phone Number:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [START phone_auth] + [[FIRPhoneAuthProvider provider] verifyPhoneNumber:userInput + UIDelegate:nil + completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) { + // [START_EXCLUDE silent] + [self hideSpinner:^{ + // [END_EXCLUDE] + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + // Sign in using the verificationID and the code sent to the user + // [START_EXCLUDE] + [self showTextInputPromptWithMessage:@"Verification Code:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + // [START get_phone_cred] + FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider] + credentialWithVerificationID:verificationID + verificationCode:userInput]; + // [END get_phone_cred] + [self firebaseLoginWithCredential:credential]; + }]; + }]; + // [END_EXCLUDE] + }]; + // [END phone_auth] + }]; + }]; + }]; + } + break; + case AuthAnonymous: { + action = [UIAlertAction actionWithTitle:@"Anonymous" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + [self showSpinner:^{ + // [START firebase_auth_anonymous] + [[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + }]; + // [END_EXCLUDE] + }]; + // [END firebase_auth_anonymous] + }]; + }]; + } + break; + case AuthGameCenter: { + action = [UIAlertAction actionWithTitle:@"Game Center" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + // [START firebase_auth_gamecenter] + [FIRGameCenterAuthProvider + getCredentialWithCompletion:^(FIRAuthCredential * _Nullable credential, + NSError * _Nullable error) { + [self showSpinner:^{ + if (error) { + [self hideSpinner:^{ + [self showMessagePrompt:error.localizedDescription]; + return; + }]; + } + if (credential) { + [[FIRAuth auth] signInWithCredential:credential + completion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + }]; + }]; + } + }]; + }]; + // [END firebase_auth_gamecenter] + }]; + }; + break; + case AuthMicrosoft: { + action = [UIAlertAction actionWithTitle:@"Microsoft" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * _Nonnull action) { + // [START firebase_auth_microsoft] + [self.microsoftProvider getCredentialWithUIDelegate:nil + completion:^(FIRAuthCredential *_Nullable credential, NSError *_Nullable error) { + [self showSpinner:^{ + if (error) { + [self hideSpinner:^{ + [self showMessagePrompt:error.localizedDescription]; + return; + }]; + } + if (credential) { + [[FIRAuth auth] signInWithCredential:credential + completion:^(FIRAuthDataResult *_Nullable authResult, + NSError *_Nullable error) { + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + }]; + }]; + } + }]; + }]; + // [END firebase_auth_microsoft] + }]; + } + break; + } + + [picker addAction:action]; + } + + + UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:nil]; + [picker addAction:cancel]; + + [self presentViewController:picker animated:YES completion:nil]; +} + +- (IBAction)didTapSignIn:(id)sender { + [self showAuthPicker:@[@(AuthEmail), + @(AuthEmailMFA), + @(AuthAnonymous), + @(AuthApple), + @(AuthGoogle), + @(AuthFacebook), + @(AuthTwitter), + @(AuthGitHub), + @(AuthPhone), + @(AuthCustom), + @(AuthPasswordless), + @(AuthGameCenter), + @(AuthMicrosoft)]]; +} + +- (IBAction)didTapLink:(id)sender { + NSMutableArray *providers = [@[@(AuthGoogle), + @(AuthFacebook), + @(AuthTwitter), + @(AuthPhone)] mutableCopy]; + + // Remove any existing providers. Note that this is not a complete list of + // providers, so always check the documentation for a complete reference: + // https://firebase.google.com/docs/auth + for (id userInfo in [FIRAuth auth].currentUser.providerData) { + if ([userInfo.providerID isEqualToString:FIRFacebookAuthProviderID]) { + [providers removeObject:@(AuthFacebook)]; + } else if ([userInfo.providerID isEqualToString:FIRGoogleAuthProviderID]) { + [providers removeObject:@(AuthGoogle)]; + } else if ([userInfo.providerID isEqualToString:FIRTwitterAuthProviderID]) { + [providers removeObject:@(AuthTwitter)]; + } else if ([userInfo.providerID isEqualToString:FIRPhoneAuthProviderID]) { + [providers removeObject:@(AuthPhone)]; + } + } + [self showAuthPicker:providers]; +} + +- (IBAction)didTapSignOut:(id)sender { + // [START signout] + NSError *signOutError; + BOOL status = [[FIRAuth auth] signOut:&signOutError]; + if (!status) { + NSLog(@"Error signing out: %@", signOutError); + return; + } + // [END signout] +} + +- (void)authenticateGameCenterLocalPlayer { + __weak GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; + localPlayer.authenticateHandler = ^(UIViewController *gcAuthViewController, + NSError *error) { + if (gcAuthViewController != nil) { + // Pause any activities that require user interaction, then present the + // gcAuthViewController to the player. + [self presentViewController:gcAuthViewController animated:YES completion:nil]; + } else if (localPlayer.isAuthenticated) { + // Local player is logged in to Game Center. + } else { + // Error + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + } + }; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + // [START auth_listener] + self.handle = [[FIRAuth auth] + addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth, FIRUser *_Nullable user) { + // [START_EXCLUDE] + [self setTitleDisplay:user]; + [self.tableView reloadData]; + // [END_EXCLUDE] + }]; + // [END auth_listener] + + self.microsoftProvider = [FIROAuthProvider providerWithProviderID:@"microsoft.com"]; + self.twitterProvider = [FIROAuthProvider providerWithProviderID:@"twitter.com"]; + self.gitHubProvider = [FIROAuthProvider providerWithProviderID:@"github.com"]; + + // Authenticate Game Center Local Player + // Uncomment to sign in with Game Center + // [self authenticateGameCenterLocalPlayer]; +} + +- (void)setTitleDisplay: (FIRUser *)user { + if (user.displayName) { + self.navigationItem.title = [NSString stringWithFormat:@"Welcome %@", user.displayName]; + } else { + self.navigationItem.title = @"Authentication Example"; + } +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + // [START remove_auth_listener] + [[FIRAuth auth] removeAuthStateDidChangeListener:_handle]; + // [END remove_auth_listener] +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + if (section == kSectionSignIn) { + return 1; + } else if (section == kSectionUser || section == kSectionToken || section == kSectionMultiFactor) { + if ([FIRAuth auth].currentUser) { + return 1; + } else { + return 0; + } + } else if (section == kSectionProviders) { + return [[FIRAuth auth].currentUser.providerData count]; + } + NSAssert(NO, @"Unexpected section"); + return 0; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView + cellForRowAtIndexPath:(NSIndexPath *)indexPath { + UITableViewCell *cell; + if (indexPath.section == kSectionSignIn) { + // [START current_user] + if ([FIRAuth auth].currentUser) { + // User is signed in. + // [START_EXCLUDE] + cell = [tableView dequeueReusableCellWithIdentifier:@"SignOut"]; + // [END_EXCLUDE] + } else { + // No user is signed in. + // [START_EXCLUDE] + cell = [tableView dequeueReusableCellWithIdentifier:@"SignIn"]; + // [END_EXCLUDE] + } + // [END current_user] + } else if (indexPath.section == kSectionUser) { + cell = [tableView dequeueReusableCellWithIdentifier:@"Profile"]; + // [START get_user_profile] + FIRUser *user = [FIRAuth auth].currentUser; + // [END get_user_profile] + // [START user_profile] + if (user) { + // The user's ID, unique to the Firebase project. + // Do NOT use this value to authenticate with your backend server, + // if you have one. Use getTokenWithCompletion:completion: instead. + NSString *email = user.email; + NSString *uid = user.uid; + NSMutableString *multiFactorString = [NSMutableString stringWithFormat:@"MultiFactor: "]; + for (FIRMultiFactorInfo *info in user.multiFactor.enrolledFactors) { + [multiFactorString appendString:info.displayName]; + [multiFactorString appendString:@" "]; + } + NSURL *photoURL = user.photoURL; + // [START_EXCLUDE] + UILabel *emailLabel = [(UILabel *)cell viewWithTag:1]; + UILabel *userIDLabel = [(UILabel *)cell viewWithTag:2]; + UIImageView *profileImageView = [(UIImageView *)cell viewWithTag:3]; + UILabel *multiFactorLabel = [(UILabel *)cell viewWithTag:4]; + emailLabel.text = email; + userIDLabel.text = uid; + multiFactorLabel.text = multiFactorString; + if (isMFAEnabled) { + multiFactorLabel.hidden = NO; + } else { + multiFactorLabel.hidden = YES; + } + + static NSURL *lastPhotoURL = nil; + lastPhotoURL = photoURL; // to prevent earlier image overwrites later one. + if (photoURL) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^() { + UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:photoURL]]; + dispatch_async(dispatch_get_main_queue(), ^() { + if (photoURL == lastPhotoURL) { + profileImageView.image = image; + } + }); + }); + } else { + profileImageView.image = [UIImage imageNamed:@"ic_account_circle"]; + } + // [END_EXCLUDE] + } + // [END user_profile] + } else if (indexPath.section == kSectionProviders) { + cell = [tableView dequeueReusableCellWithIdentifier:@"Provider"]; + // [START provider_data] + id userInfo = [FIRAuth auth].currentUser.providerData[indexPath.row]; + cell.textLabel.text = [userInfo providerID]; + // Provider-specific UID + cell.detailTextLabel.text = [userInfo uid]; + // [END provider_data] + } else if (indexPath.section == kSectionToken) { + cell = [tableView dequeueReusableCellWithIdentifier:@"Token"]; + UIButton *requestEmailButton = [(UIButton *)cell viewWithTag:4]; + requestEmailButton.enabled = [FIRAuth auth].currentUser.email ? YES : NO; + } else if (indexPath.section == kSectionMultiFactor) { + cell = [tableView dequeueReusableCellWithIdentifier:@"MultiFactor"]; + } else { + [NSException raise:NSInternalInconsistencyException format:@"Unexpected state"]; + } + return cell; +} + +- (NSString *)tableView:(UITableView *)tableView + titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { + return @"Unlink"; +} + +- (UITableViewCellEditingStyle)tableView:(UITableView *)aTableView + editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == kSectionProviders) { + return UITableViewCellEditingStyleDelete; + } + return UITableViewCellEditingStyleNone; +} + +// Swipe to delete. +- (void)tableView:(UITableView *)tableView + commitEditingStyle:(UITableViewCellEditingStyle)editingStyle + forRowAtIndexPath:(NSIndexPath *)indexPath { + if (editingStyle == UITableViewCellEditingStyleDelete) { + NSString *providerID = [[FIRAuth auth].currentUser.providerData[indexPath.row] providerID]; + [self showSpinner:^{ + // [START unlink_provider] + [[FIRAuth auth].currentUser unlinkFromProvider:providerID + completion:^(FIRUser *_Nullable user, NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self.tableView reloadData]; + }]; + // [END_EXCLUDE] + }]; + // [END unlink_provider] + }]; + } +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == kSectionUser) { + return 200; + } + return 44; +} +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + if (isMFAEnabled) { + return 5; + } else { + return 4; + } +} + +- (IBAction)didMultiFactorEnroll:(id)sender { + FIRUser *user = FIRAuth.auth.currentUser; + if (!user) { + NSLog(@"Please sign in first."); + } else { + [self showTextInputPromptWithMessage:@"Phone Number" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable phoneNumber) { + [user.multiFactor + getSessionWithCompletion:^(FIRMultiFactorSession *_Nullable session, NSError *_Nullable error) { + [FIRPhoneAuthProvider.provider verifyPhoneNumber:phoneNumber + UIDelegate:nil + multiFactorSession:session + completion:^(NSString * _Nullable verificationID, + NSError * _Nullable error) { + if (error) { + [self showMessagePrompt:error.localizedDescription]; + } else { + [self showTextInputPromptWithMessage:@"Verification code" + completionBlock:^(BOOL userPressedOK, + NSString *_Nullable verificationCode) { + FIRPhoneAuthCredential *credential = + [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID + verificationCode:verificationCode]; + FIRMultiFactorAssertion *assertion = + [FIRPhoneMultiFactorGenerator assertionWithCredential:credential]; + [self showTextInputPromptWithMessage:@"Display name" + completionBlock:^(BOOL userPressedOK, + NSString *_Nullable displayName) { + [user.multiFactor enrollWithAssertion:assertion + displayName:displayName + completion:^(NSError *_Nullable error) { + if (error) { + [self showMessagePrompt:error.localizedDescription]; + } else { + NSLog(@"Multi factor finanlize enroll succeeded."); + [self showTypicalUIForUserUpdateResultsWithTitle:@"Multi Factor" error:error]; + } + }]; + }]; + }]; + } + }]; + }]; + }]; + } +} + +- (IBAction)didMultiFactorUnenroll:(id)sender { + NSMutableString *displayNameString = [NSMutableString string]; + for (FIRMultiFactorInfo *tmpFactorInfo in FIRAuth.auth.currentUser.multiFactor.enrolledFactors) { + [displayNameString appendString:tmpFactorInfo.displayName]; + [displayNameString appendString:@" "]; + } + [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Multifactor Unenroll\n%@", displayNameString] + completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) { + FIRMultiFactorInfo *factorInfo; + for (FIRMultiFactorInfo *tmpFactorInfo in FIRAuth.auth.currentUser.multiFactor.enrolledFactors) { + if ([displayName isEqualToString:tmpFactorInfo.displayName]) { + factorInfo = tmpFactorInfo; + } + } + [FIRAuth.auth.currentUser.multiFactor unenrollWithInfo:factorInfo + completion:^(NSError * _Nullable error) { + if (error) { + [self showMessagePrompt:error.localizedDescription]; + } else { + NSLog(@"Multi factor finanlize unenroll succeeded."); + [self showTypicalUIForUserUpdateResultsWithTitle:@"Multi Factor" error:error]; + } + }]; + }]; +} + +- (IBAction)didTokenRefresh:(id)sender { + FIRAuthTokenCallback action = ^(NSString *_Nullable token, NSError *_Nullable error) { + UIAlertAction *okAction = [UIAlertAction actionWithTitle:kOKButtonText + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + NSLog(kOKButtonText); + }]; + if (error) { + UIAlertController *alertController = + [UIAlertController alertControllerWithTitle:kTokenRefreshErrorAlertTitle + message:error.localizedDescription + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction:okAction]; + [self presentViewController:alertController animated:YES completion:nil]; + return; + } + + // Log token refresh event to Analytics. + [FIRAnalytics logEventWithName:@"tokenrefresh" parameters:nil]; + + UIAlertController *alertController = + [UIAlertController alertControllerWithTitle:kTokenRefreshedAlertTitle + message:token + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction:okAction]; + [self presentViewController:alertController animated:YES completion:nil]; + }; + // [START token_refresh] + [[FIRAuth auth].currentUser getIDTokenForcingRefresh:YES completion:action]; + // [END token_refresh] +} + + +/** @fn setDisplayName + @brief Changes the display name of the current user. + */ +- (IBAction)didSetDisplayName:(id)sender { + [self showTextInputPromptWithMessage:@"Display Name:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [START profile_change] + FIRUserProfileChangeRequest *changeRequest = [[FIRAuth auth].currentUser profileChangeRequest]; + changeRequest.displayName = userInput; + [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + [self showTypicalUIForUserUpdateResultsWithTitle:kSetDisplayNameTitle error:error]; + [self setTitleDisplay:[FIRAuth auth].currentUser]; + }]; + // [END_EXCLUDE] + }]; + // [END profile_change] + }]; + }]; +} + +/** @fn requestVerifyEmail + @brief Requests a "verify email" email be sent. + */ +- (IBAction)didRequestVerifyEmail:(id)sender { + [self showSpinner:^{ + // [START send_verification_email] + [[FIRAuth auth].currentUser sendEmailVerificationWithCompletion:^(NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + + [self showMessagePrompt:@"Sent"]; + }]; + // [END_EXCLUDE] + }]; + // [END send_verification_email] + }]; +} + +/** @fn changeEmail + @brief Changes the email address of the current user. + */ +- (IBAction)didChangeEmail:(id)sender { + [self showTextInputPromptWithMessage:@"Email Address:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [START change_email] + [[FIRAuth auth].currentUser updateEmail:userInput completion:^(NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + [self showTypicalUIForUserUpdateResultsWithTitle:kChangeEmailText error:error]; + }]; + // [END_EXCLUDE] + }]; + // [END change_email] + }]; + }]; +} + +/** @fn changePassword + @brief Changes the password of the current user. + */ +- (IBAction)didChangePassword:(id)sender { + [self showTextInputPromptWithMessage:@"New Password:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [START change_password] + [[FIRAuth auth].currentUser updatePassword:userInput completion:^(NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + [self showTypicalUIForUserUpdateResultsWithTitle:kChangePasswordText error:error]; + }]; + // [END_EXCLUDE] + }]; + // [END change_password] + }]; + }]; +} + +/** @fn updatePhoneNumber + @brief Updates the phone number of the current user. + */ +- (IBAction)didUpdatePhoneNumber:(id)sender { + [self showTextInputPromptWithMessage:@"New Phone Number:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [START update_phone] + [[FIRPhoneAuthProvider provider] verifyPhoneNumber:userInput + UIDelegate:nil + completion:^(NSString * _Nullable verificationID, + NSError * _Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self showTextInputPromptWithMessage:@"Verification Code:" + completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) { + if (!userPressedOK || !userInput.length) { + return; + } + + [self showSpinner:^{ + // [END_EXCLUDE] + FIRPhoneAuthCredential *credential = [[FIRPhoneAuthProvider provider] + credentialWithVerificationID:verificationID + verificationCode:userInput]; + [[FIRAuth auth].currentUser updatePhoneNumberCredential:credential + completion:^(NSError * _Nullable error) { + // [END update_phone] + [self hideSpinner:^{ + [self showTypicalUIForUserUpdateResultsWithTitle:kUpdatePhoneNumberText + error:error]; + }]; + }]; + }]; + }]; + }]; + }]; + }]; + }]; +} + +/** @fn showTypicalUIForUserUpdateResultsWithTitle:error: + @brief Shows a @c UIAlertView if error is non-nil with the localized description of the error. + @param resultsTitle The title of the @c UIAlertView + @param error The error details to display if non-nil. + */ +- (void)showTypicalUIForUserUpdateResultsWithTitle:(NSString *)resultsTitle error:(NSError *)error { + if (error) { + NSString *message = [NSString stringWithFormat:@"%@ (%ld)\n%@", error.domain, (long)error.code, + error.localizedDescription]; + if ([UIAlertController class]) { + UIAlertAction *okAction = [UIAlertAction actionWithTitle:kOKButtonText + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + NSLog(@"OK"); + }]; + + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:resultsTitle + message:message + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addAction:okAction]; + [self presentViewController:alertController + animated:YES + completion:nil]; + } else { + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:resultsTitle + message:message + delegate:nil + cancelButtonTitle:nil + otherButtonTitles:kOKButtonText, nil]; + [alert show]; + return; + } + } + [self.tableView reloadData]; +} + +@end + +#pragma mark - Sign in with Apple + +@implementation MainViewController (SignInWithApple) + +- (void)startSignInWithAppleFlow { + NSString *nonce = [self randomNonce:32]; + self.currentNonce = nonce; + ASAuthorizationAppleIDProvider *appleIDProvider = [[ASAuthorizationAppleIDProvider alloc] init]; + ASAuthorizationAppleIDRequest *request = [appleIDProvider createRequest]; + request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail]; + request.nonce = [self stringBySha256HashingString:nonce]; + + ASAuthorizationController *authorizationController = + [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]]; + authorizationController.delegate = self; + authorizationController.presentationContextProvider = self; + [authorizationController performRequests]; +} + +// [START random_nonce] +// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce +- (NSString *)randomNonce:(NSInteger)length { + NSAssert(length > 0, @"Expected nonce to have positive length"); + NSString *characterSet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._"; + NSMutableString *result = [NSMutableString string]; + NSInteger remainingLength = length; + + while (remainingLength > 0) { + NSMutableArray *randoms = [NSMutableArray arrayWithCapacity:16]; + for (NSInteger i = 0; i < 16; i++) { + uint8_t random = 0; + int errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random); + NSAssert(errorCode == errSecSuccess, @"Unable to generate nonce: OSStatus %i", errorCode); + + [randoms addObject:@(random)]; + } + + for (NSNumber *random in randoms) { + if (remainingLength == 0) { + break; + } + + if (random.unsignedIntValue < characterSet.length) { + unichar character = [characterSet characterAtIndex:random.unsignedIntValue]; + [result appendFormat:@"%C", character]; + remainingLength--; + } + } + } + + return [result copy]; +} +// [END random_nonce] + +// [START sha_256] +- (NSString *)stringBySha256HashingString:(NSString *)input { + const char *string = [input UTF8String]; + unsigned char result[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(string, (CC_LONG)strlen(string), result); + + NSMutableString *hashed = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hashed appendFormat:@"%02x", result[i]]; + } + return hashed; +} +// [END sha_256] + +- (void)authorizationController:(ASAuthorizationController *)controller + didCompleteWithAuthorization:(ASAuthorization *)authorization API_AVAILABLE(ios(13.0)) { + if ([authorization.credential isKindOfClass:[ASAuthorizationAppleIDCredential class]]) { + ASAuthorizationAppleIDCredential *appleIDCredential = authorization.credential; + NSString *rawNonce = self.currentNonce; + NSAssert(rawNonce != nil, @"Invalid state: A login callback was received, but no login request was sent."); + + if (appleIDCredential.identityToken == nil) { + NSLog(@"Unable to fetch identity token."); + return; + } + + NSString *idToken = [[NSString alloc] initWithData:appleIDCredential.identityToken + encoding:NSUTF8StringEncoding]; + if (idToken == nil) { + NSLog(@"Unable to serialize id token from data: %@", appleIDCredential.identityToken); + } + + // Initialize a Firebase credential. + FIROAuthCredential *credential = [FIROAuthProvider credentialWithProviderID:@"apple.com" + IDToken:idToken + rawNonce:rawNonce]; + + // Sign in with Firebase. + [self firebaseLoginWithCredential:credential]; + } +} + +- (void)authorizationController:(ASAuthorizationController *)controller + didCompleteWithError:(NSError *)error API_AVAILABLE(ios(13.0)) { + NSLog(@"Sign in with Apple errored: %@", error); +} + +- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller API_AVAILABLE(ios(13.0)) { + return self.view.window; +} + +- (void)setCurrentNonce:(NSString *)currentNonce { + _currentNonce = [currentNonce copy]; +} + +- (NSString *)currentNonce { + return [_currentNonce copy]; +} + +#pragma mark - Sign in with Google + +- (void)startSignInWithGoogleFlow { + // [START headless_google_auth] + GIDConfiguration *config = [[GIDConfiguration alloc] initWithClientID:[FIRApp defaultApp].options.clientID]; + [GIDSignIn.sharedInstance setConfiguration:config]; + + __weak __auto_type weakSelf = self; + [GIDSignIn.sharedInstance signInWithPresentingViewController:self + completion:^(GIDSignInResult * _Nullable result, NSError * _Nullable error) { + __auto_type strongSelf = weakSelf; + if (strongSelf == nil) { return; } + + if (error == nil) { + // [START google_credential] + FIRAuthCredential *credential = + [FIRGoogleAuthProvider credentialWithIDToken:result.user.idToken.tokenString + accessToken:result.user.accessToken.tokenString]; + // [END google_credential] + // [START_EXCLUDE] + [strongSelf firebaseLoginWithCredential:credential]; + // [END_EXCLUDE] + } else { + // [START_EXCLUDE] + [strongSelf showMessagePrompt:error.localizedDescription]; + // [END_EXCLUDE] + } + }]; + + // [END headless_google_auth] +} + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/PasswordlessViewController.h b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/PasswordlessViewController.h new file mode 100644 index 00000000..8a42259a --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/PasswordlessViewController.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import UIKit; + +@interface PasswordlessViewController : UIViewController +@end + diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/PasswordlessViewController.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/PasswordlessViewController.m new file mode 100644 index 00000000..dbe167c6 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/PasswordlessViewController.m @@ -0,0 +1,102 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "PasswordlessViewController.h" +#import "UIViewController+Alerts.h" + +@import FirebaseAuth; + +@interface PasswordlessViewController () +@property (weak, nonatomic) IBOutlet UITextField *emailField; +@property (weak, nonatomic) IBOutlet UIButton *signInButton; +@property(strong, nonatomic) NSString *link; +@end + +@implementation PasswordlessViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + _emailField.text = [NSUserDefaults.standardUserDefaults valueForKey:@"Email"]; + self.link = [NSUserDefaults.standardUserDefaults valueForKey:@"Link"]; + if (_link) { + _signInButton.enabled = YES; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self.view endEditing:YES]; +} + +- (IBAction)didTapSignInWithEmailLink:(id)sender { + NSString *email = _emailField.text; + NSString *link = _link; + [self showSpinner:^{ + // [START signin_emaillink] + [[FIRAuth auth] signInWithEmail:email + link:link + completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + [self.navigationController popViewControllerAnimated:YES]; + }]; + // [END_EXCLUDE] + }]; + // [END signin_emaillink] + }]; +} + +- (IBAction)didTapSendSignInLink:(id)sender { + NSString *email = _emailField.text; + [self showSpinner:^{ + // [START action_code_settings] + FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init]; + [actionCodeSettings setURL:[NSURL URLWithString:@"https://www.example.com"]]; + // The sign-in operation has to always be completed in the app. + actionCodeSettings.handleCodeInApp = YES; + [actionCodeSettings setIOSBundleID:[[NSBundle mainBundle] bundleIdentifier]]; + [actionCodeSettings setAndroidPackageName:@"com.example.android" + installIfNotAvailable:NO + minimumVersion:@"12"]; + // [END action_code_settings] + // [START send_signin_link] + [[FIRAuth auth] sendSignInLinkToEmail:email + actionCodeSettings:actionCodeSettings + completion:^(NSError *_Nullable error) { + // [START_EXCLUDE] + [self hideSpinner:^{ + // [END_EXCLUDE] + if (error) { + [self showMessagePrompt:error.localizedDescription]; + return; + } + // The link was successfully sent. Inform the user. + // Save the email locally so you don't need to ask the user for it again + // if they open the link on the same device. + [NSUserDefaults.standardUserDefaults setObject:email forKey:@"Email"]; + [self showMessagePrompt:@"Check your email for link"]; + // [START_EXCLUDE] + }]; + // [END_EXCLUDE] + }]; + // [END send_signin_link] + }]; +} + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/UIViewController+Alerts.h b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/UIViewController+Alerts.h new file mode 100644 index 00000000..c610cddd --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/UIViewController+Alerts.h @@ -0,0 +1,60 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/*! @typedef AlertPromptCompletionBlock + @brief The type of callback used to report text input prompt results. + */ +typedef void (^AlertPromptCompletionBlock)(BOOL userPressedOK, NSString *_Nullable userInput); + +/*! @class Alerts + @brief Wrapper for @c UIAlertController and @c UIAlertView for backwards compatability with + iOS 6+. + */ +@interface UIViewController (Alerts) + +/*! @fn showMessagePrompt: + @brief Displays an alert with an 'OK' button and a message. + @param message The message to display. + */ +- (void)showMessagePrompt:(NSString *)message; + +/*! @fn showTextInputPromptWithMessage:completionBlock: + @brief Shows a prompt with a text field and 'OK'/'Cancel' buttons. + @param message The message to display. + @param completion A block to call when the user taps 'OK' or 'Cancel'. + */ +- (void)showTextInputPromptWithMessage:(NSString *)message + completionBlock:(AlertPromptCompletionBlock)completion; + +/*! @fn showSpinner + @brief Shows the please wait spinner. + @param completion Called after the spinner has been shown. + */ +- (void)showSpinner:(nullable void (^)(void))completion; + +/*! @fn hideSpinner + @brief Hides the please wait spinner. + @param completion Called after the spinner has been hidden. + */ +- (void)hideSpinner:(nullable void (^)(void))completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/UIViewController+Alerts.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/UIViewController+Alerts.m new file mode 100644 index 00000000..c7e297f5 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/UIViewController+Alerts.m @@ -0,0 +1,159 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "UIViewController+Alerts.h" + +#import + +/*! @var kPleaseWaitAssociatedObjectKey + @brief Key used to identify the "please wait" spinner associated object. + */ +static NSString *const kPleaseWaitAssociatedObjectKey = + @"_UIViewControllerAlertCategory_PleaseWaitScreenAssociatedObject"; + +/*! @var kOK + @brief Text for an 'OK' button. + */ +static NSString *const kOK = @"OK"; + +/*! @var kCancel + @brief Text for an 'Cancel' button. + */ +static NSString *const kCancel = @"Cancel"; + +/*! @class SimpleTextPromptDelegate + @brief A @c UIAlertViewDelegate which allows @c UIAlertView to be used with blocks more easily. + */ +@interface SimpleTextPromptDelegate : NSObject + +/*! @fn init + @brief Please use initWithCompletionHandler. + */ +- (nullable instancetype)init NS_UNAVAILABLE; + +/*! @fn initWithCompletionHandler: + @brief Designated initializer. + @param completionHandler The block to call when the alert view is dismissed. + */ +- (nullable instancetype)initWithCompletionHandler:(AlertPromptCompletionBlock)completionHandler + NS_DESIGNATED_INITIALIZER; + +@end + +@implementation UIViewController (Alerts) + +- (void)showMessagePrompt:(NSString *)message { + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:nil + message:message + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction *okAction = + [UIAlertAction actionWithTitle:kOK style:UIAlertActionStyleDefault handler:nil]; + [alert addAction:okAction]; + [self presentViewController:alert animated:YES completion:nil]; +} + +- (void)showTextInputPromptWithMessage:(NSString *)message + completionBlock:(AlertPromptCompletionBlock)completion { + UIAlertController *prompt = + [UIAlertController alertControllerWithTitle:nil + message:message + preferredStyle:UIAlertControllerStyleAlert]; + __weak UIAlertController *weakPrompt = prompt; + UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:kCancel + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + completion(NO, nil); + }]; + UIAlertAction *okAction = + [UIAlertAction actionWithTitle:kOK + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + UIAlertController *strongPrompt = weakPrompt; + completion(YES, strongPrompt.textFields[0].text); + }]; + [prompt addTextFieldWithConfigurationHandler:nil]; + [prompt addAction:cancelAction]; + [prompt addAction:okAction]; + [self presentViewController:prompt animated:YES completion:nil]; +} + +- (void)showSpinner:(nullable void (^)(void))completion { + UIAlertController *pleaseWaitAlert = + objc_getAssociatedObject(self, (__bridge const void *)(kPleaseWaitAssociatedObjectKey)); + if (pleaseWaitAlert) { + if (completion) { + completion(); + } + return; + } + pleaseWaitAlert = [UIAlertController alertControllerWithTitle:nil + message:@"Please Wait...\n\n\n\n" + preferredStyle:UIAlertControllerStyleAlert]; + + UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] + initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + spinner.color = [UIColor blackColor]; + spinner.center = CGPointMake(pleaseWaitAlert.view.bounds.size.width / 2, + pleaseWaitAlert.view.bounds.size.height / 2); + spinner.autoresizingMask = + UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin | + UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + [spinner startAnimating]; + [pleaseWaitAlert.view addSubview:spinner]; + + objc_setAssociatedObject(self, (__bridge const void *)(kPleaseWaitAssociatedObjectKey), + pleaseWaitAlert, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + [self presentViewController:pleaseWaitAlert animated:YES completion:completion]; +} + +- (void)hideSpinner:(nullable void (^)(void))completion { + UIAlertController *pleaseWaitAlert = + objc_getAssociatedObject(self, (__bridge const void *)(kPleaseWaitAssociatedObjectKey)); + + [pleaseWaitAlert dismissViewControllerAnimated:YES completion:completion]; + + objc_setAssociatedObject(self, (__bridge const void *)(kPleaseWaitAssociatedObjectKey), nil, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +@implementation SimpleTextPromptDelegate { + AlertPromptCompletionBlock _completionHandler; + SimpleTextPromptDelegate *_retainedSelf; +} + +- (instancetype)initWithCompletionHandler:(AlertPromptCompletionBlock)completionHandler { + self = [super init]; + if (self) { + _completionHandler = completionHandler; + _retainedSelf = self; + } + return self; +} + +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { + if (buttonIndex == alertView.firstOtherButtonIndex) { + _completionHandler(YES, [alertView textFieldAtIndex:0].text); + } else { + _completionHandler(NO, nil); + } + _completionHandler = nil; + _retainedSelf = nil; +} + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/main.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/main.m new file mode 100644 index 00000000..d4878306 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExample/main.m @@ -0,0 +1,24 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/AppDelegate.swift b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/AppDelegate.swift new file mode 100644 index 00000000..ec762c4b --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/AppDelegate.swift @@ -0,0 +1,104 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// [START auth_import] +import FirebaseCore +// [END auth_import] + +// [START google_import] +import GoogleSignIn +// [END google_import] +import FBSDKCoreKit + +@UIApplicationMain +// [START signin_delegate] +class AppDelegate: UIResponder, UIApplicationDelegate { + // [END signin_delegate] + + var window: UIWindow? + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [ + UIApplication.LaunchOptionsKey: Any + ]?) -> Bool { + // [START firebase_configure] + // Use Firebase library to configure APIs + FirebaseApp.configure() + // [END firebase_configure] + + ApplicationDelegate.shared.application(application, + didFinishLaunchingWithOptions: launchOptions) + + return true + } + + // [START new_delegate] + @available(iOS 9.0, *) + func application(_ application: UIApplication, open url: URL, + options: [UIApplication.OpenURLOptionsKey: Any]) + -> Bool { + // [END new_delegate] + return self.application(application, + open: url, + // [START new_options] + sourceApplication: options[UIApplication.OpenURLOptionsKey + .sourceApplication] as? String, + annotation: [:]) + } + + // [END new_options] + + // [START old_delegate] + func application(_ application: UIApplication, open url: URL, sourceApplication: String?, + annotation: Any) -> Bool { + // [END old_delegate] + if handlePasswordlessSignIn(withURL: url) { + return true + } + if GIDSignIn.sharedInstance.handle(url) { + return true + } + return ApplicationDelegate.shared.application(application, + open: url, + // [START old_options] + sourceApplication: sourceApplication, + annotation: annotation) + } + + // [END old_options] + + func application(_ application: UIApplication, continue userActivity: NSUserActivity, + restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { + return userActivity.webpageURL.flatMap(handlePasswordlessSignIn)! + } + + func handlePasswordlessSignIn(withURL url: URL) -> Bool { + let link = url.absoluteString + // [START is_signin_link] + if Auth.auth().isSignIn(withEmailLink: link) { + // [END is_signin_link] + UserDefaults.standard.set(link, forKey: "Link") + (window?.rootViewController as? UINavigationController)? + .popToRootViewController(animated: false) + window?.rootViewController?.children[0] + .performSegue(withIdentifier: "passwordless", sender: nil) + return true + } + return false + } +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/AuthenticationExample.entitlements b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/AuthenticationExample.entitlements new file mode 100644 index 00000000..a8aefa8f --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/AuthenticationExample.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.google.firebase.quickstart.AuthenticationExample + + + diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/CustomTokenViewController.swift b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/CustomTokenViewController.swift new file mode 100644 index 00000000..fc7fccfc --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/CustomTokenViewController.swift @@ -0,0 +1,49 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +// [START auth_view_import] +import FirebaseAuth +// [END auth_view_import] + +@objc(CustomTokenViewController) +class CustomTokenViewController: UIViewController { + @IBOutlet var tokenField: UITextView! + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + view.endEditing(true) + } + + @IBAction func didTapCustomTokenLogin(_ sender: AnyObject) { + let customToken = tokenField.text + showSpinner { + // [START signinwithcustomtoken] + Auth.auth().signIn(withCustomToken: customToken ?? "") { user, error in + // [START_EXCLUDE] + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + self.navigationController!.popViewController(animated: true) + } + // [END_EXCLUDE] + } + // [END signinwithcustomtoken] + } + } +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/EmailViewController.swift b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/EmailViewController.swift new file mode 100644 index 00000000..bdb55d25 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/EmailViewController.swift @@ -0,0 +1,190 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +import FirebaseAuth + +@objc(EmailViewController) +class EmailViewController: UIViewController { + @IBOutlet var emailField: UITextField! + @IBOutlet var passwordField: UITextField! + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + view.endEditing(true) + } + + @IBAction func didTapEmailLogin(_ sender: AnyObject) { + guard let email = emailField.text, let password = passwordField.text else { + showMessagePrompt("email/password can't be empty") + return + } + showSpinner { + // [START headless_email_auth] + Auth.auth().signIn(withEmail: email, password: password) { [weak self] authResult, error in + guard let strongSelf = self else { return } + // [START_EXCLUDE] + strongSelf.hideSpinner { + if let error = error { + let authError = error as NSError + if authError.code == AuthErrorCode.secondFactorRequired.rawValue { + // The user is a multi-factor user. Second factor challenge is required. + let resolver = authError + .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver + var displayNameString = "" + for tmpFactorInfo in resolver.hints { + displayNameString += tmpFactorInfo.displayName ?? "" + displayNameString += " " + } + self!.showTextInputPrompt( + withMessage: "Select factor to sign in\n\(displayNameString)", + completionBlock: { userPressedOK, displayName in + var selectedHint: PhoneMultiFactorInfo? + for tmpFactorInfo in resolver.hints { + if displayName == tmpFactorInfo.displayName { + selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo + } + } + PhoneAuthProvider.provider() + .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil, + multiFactorSession: resolver + .session) { verificationID, error in + if error != nil { + print("Multi factor start sign in failed. Error: \(error.debugDescription)") + } else { + self!.showTextInputPrompt( + withMessage: "Verification code for \(selectedHint?.displayName ?? "")", + completionBlock: { userPressedOK, verificationCode in + let credential: PhoneAuthCredential? = PhoneAuthProvider.provider() + .credential(withVerificationID: verificationID!, + verificationCode: verificationCode!) + let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator + .assertion(with: credential!) + resolver.resolveSignIn(with: assertion!) { authResult, error in + if error != nil { + print( + "Multi factor finanlize sign in failed. Error: \(error.debugDescription)" + ) + } else { + strongSelf.navigationController?.popViewController(animated: true) + } + } + } + ) + } + } + } + ) + } else { + strongSelf.showMessagePrompt(error.localizedDescription) + return + } + } + strongSelf.navigationController?.popViewController(animated: true) + } + // [END_EXCLUDE] + } + // [END headless_email_auth] + } + } + + /** @fn requestPasswordReset + @brief Requests a "password reset" email be sent. + */ + @IBAction func didRequestPasswordReset(_ sender: AnyObject) { + showTextInputPrompt(withMessage: "Email:") { [weak self] userPressedOK, email in + guard let strongSelf = self, let email = email else { + return + } + strongSelf.showSpinner { + // [START password_reset] + Auth.auth().sendPasswordReset(withEmail: email) { error in + // [START_EXCLUDE] + strongSelf.hideSpinner { + if let error = error { + strongSelf.showMessagePrompt(error.localizedDescription) + return + } + strongSelf.showMessagePrompt("Sent") + } + // [END_EXCLUDE] + } + // [END password_reset] + } + } + } + + /** @fn getProvidersForEmail + @brief Prompts the user for an email address, calls @c FIRAuth.getProvidersForEmail:callback: + and displays the result. + */ + @IBAction func didGetProvidersForEmail(_ sender: AnyObject) { + showTextInputPrompt(withMessage: "Email:") { [weak self] userPressedOK, email in + guard let strongSelf = self else { return } + guard let email = email else { + strongSelf.showMessagePrompt("email can't be empty") + return + } + strongSelf.showSpinner { + // [START get_methods] + Auth.auth().fetchSignInMethods(forEmail: email) { methods, error in + // [START_EXCLUDE] + strongSelf.hideSpinner { + if let error = error { + strongSelf.showMessagePrompt(error.localizedDescription) + return + } + strongSelf.showMessagePrompt(methods!.joined(separator: ", ")) + } + // [END_EXCLUDE] + } + // [END get_methods] + } + } + } + + @IBAction func didCreateAccount(_ sender: AnyObject) { + showTextInputPrompt(withMessage: "Email:") { [weak self] userPressedOK, email in + guard let strongSelf = self else { return } + guard let email = email else { + strongSelf.showMessagePrompt("email can't be empty") + return + } + strongSelf.showTextInputPrompt(withMessage: "Password:") { userPressedOK, password in + guard let password = password else { + strongSelf.showMessagePrompt("password can't be empty") + return + } + strongSelf.showSpinner { + // [START create_user] + Auth.auth().createUser(withEmail: email, password: password) { authResult, error in + // [START_EXCLUDE] + strongSelf.hideSpinner { + guard let user = authResult?.user, error == nil else { + strongSelf.showMessagePrompt(error!.localizedDescription) + return + } + print("\(user.email!) created") + strongSelf.navigationController?.popViewController(animated: true) + } + // [END_EXCLUDE] + } + // [END create_user] + } + } + } + } +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/MainViewController.swift b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/MainViewController.swift new file mode 100644 index 00000000..71ab339e --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/MainViewController.swift @@ -0,0 +1,1121 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import AuthenticationServices +import CryptoKit +import GameKit +import UIKit + +import FirebaseCore +import Security + +// [START usermanagement_view_import] +import FirebaseAuth +// [END usermanagement_view_import] +import GoogleSignIn +import FBSDKCoreKit +import FBSDKLoginKit + +private var isMFAEnabled = false + +@objc(MainViewController) +// [START signin_controller] +class MainViewController: UITableViewController { + // [END signin_controller] + + let kSectionMultiFactor = 4 + let kSectionToken = 3 + let kSectionProviders = 2 + let kSectionUser = 1 + let kSectionSignIn = 0 + + enum AuthProvider { + case authEmail + case authEmailMFA + case authAnonymous + case authApple + case authFacebook + case authGoogle + case authTwitter + case authGitHub + case authPhone + case authCustom + case authPasswordless + case authGameCenter + case authMicrosoft + } + + /*! @var kOKButtonText + @brief The text of the "OK" button for the Sign In result dialogs. + */ + let kOKButtonText = "OK" + + /*! @var kTokenRefreshedAlertTitle + @brief The title of the "Token Refreshed" alert. + */ + let kTokenRefreshedAlertTitle = "Token" + + /*! @var kTokenRefreshErrorAlertTitle + @brief The title of the "Token Refresh error" alert. + */ + let kTokenRefreshErrorAlertTitle = "Get Token Error" + + /** @var kSetDisplayNameTitle + @brief The title of the "Set Display Name" error dialog. + */ + let kSetDisplayNameTitle = "Set Display Name" + + /** @var kUnlinkTitle + @brief The text of the "Unlink from Provider" error Dialog. + */ + let kUnlinkTitle = "Unlink from Provider" + + /** @var kChangeEmailText + @brief The title of the "Change Email" button. + */ + let kChangeEmailText = "Change Email" + + /** @var kChangePasswordText + @brief The title of the "Change Password" button. + */ + let kChangePasswordText = "Change Password" + + /** @var kUpdatePhoneNumberText + @brief The title of the "Update Phone Number" button. + */ + let kUpdatePhoneNumberText = "Update Phone Number" + + /** @var handle + @brief The handler for the auth state listener, to allow cancelling later. + */ + var handle: AuthStateDidChangeListenerHandle? + + /** @var microsoftProvider + @brief The OAuth provider instance for Microsoft. + */ + var microsoftProvider: OAuthProvider? + + /** @var twitterProvider + @brief The OAuth provider instance for Twitter. + */ + var twitterProvider: OAuthProvider? + + /** @var gitHubProvider + @brief The OAuth provider instance for GitHub. + */ + var gitHubProvider: OAuthProvider? + + func showAuthPicker(_ providers: [AuthProvider]) { + let picker = UIAlertController(title: "Select Provider", + message: nil, + preferredStyle: .alert) + for provider in providers { + var action: UIAlertAction + switch provider { + case .authEmail: + action = UIAlertAction(title: "Email", style: .default) { UIAlertAction in + self.performSegue(withIdentifier: "email", sender: nil) + } + case .authEmailMFA: + action = UIAlertAction(title: "Email with MFA", style: .default) { UIAlertAction in + isMFAEnabled = true + self.performSegue(withIdentifier: "email", sender: nil) + } + case .authPasswordless: + action = UIAlertAction(title: "Passwordless", style: .default) { UIAlertAction in + self.performSegue(withIdentifier: "passwordless", sender: nil) + } + case .authCustom: + action = UIAlertAction(title: "Custom", style: .default) { UIAlertAction in + self.performSegue(withIdentifier: "customToken", sender: nil) + } + case .authAnonymous: + action = UIAlertAction(title: "Anonymous", style: .default) { UIAlertAction in + self.showSpinner { + // [START firebase_auth_anonymous] + Auth.auth().signInAnonymously { authResult, error in + // [START_EXCLUDE] + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + } + // [END_EXCLUDE] + } + // [END firebase_auth_anonymous] + } + } + case .authApple: + if #available(iOS 13, *) { + action = UIAlertAction(title: "Apple", style: .default) { UIAlertAction in + self.startSignInWithAppleFlow() + } + } else { + continue + } + case .authFacebook: + action = UIAlertAction(title: "Facebook", style: .default) { UIAlertAction in + let loginManager = LoginManager() + loginManager.logIn(permissions: ["email"], from: self, handler: { result, error in + if let error = error { + self.showMessagePrompt(error.localizedDescription) + } else if result!.isCancelled { + print("FBLogin cancelled") + } else { + // [START headless_facebook_auth] + let credential = FacebookAuthProvider + .credential(withAccessToken: AccessToken.current!.tokenString) + // [END headless_facebook_auth] + self.firebaseLogin(credential) + } + }) + } + case .authGoogle: + action = UIAlertAction(title: "Google", style: .default) { UIAlertAction in + self.startSignInWithGoogleFlow() + } + case .authTwitter: + action = UIAlertAction(title: "Twitter", style: .default) { UIAlertAction in + // [START firebase_auth_twitter] + self.twitterProvider?.getCredentialWith(_: nil) { credential, error in + self.showSpinner { + if let error = error { + self.hideSpinner { + self.showMessagePrompt(error.localizedDescription) + return + } + } + if let credential = credential { + Auth.auth().signIn(with: credential) { result, error in + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + } + } + } + } + } + // [END firebase_auth_twitter] + } + case .authGitHub: + action = UIAlertAction(title: "GitHub", style: .default) { UIAlertAction in + // [START firebase_auth_github] + self.gitHubProvider?.getCredentialWith(_: nil) { credential, error in + self.showSpinner { + if let error = error { + self.hideSpinner { + self.showMessagePrompt(error.localizedDescription) + return + } + } + if let credential = credential { + Auth.auth().signIn(with: credential) { result, error in + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + } + } + } + } + } + // [END firebase_auth_github] + } + case .authPhone: + action = UIAlertAction(title: "Phone", style: .default) { UIAlertAction in + self.showTextInputPrompt(withMessage: "Phone Number:") { userPressedOK, userInput in + if let phoneNumber = userInput { + self.showSpinner { + // [START phone_auth] + PhoneAuthProvider.provider() + .verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in + // [START_EXCLUDE silent] + self.hideSpinner { + // [END_EXCLUDE] + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + // Sign in using the verificationID and the code sent to the user + // [START_EXCLUDE] + guard let verificationID = verificationID else { return } + self + .showTextInputPrompt(withMessage: "Verification Code:") { userPressedOK, verificationCode in + if let verificationCode = verificationCode { + // [START get_phone_cred] + let credential = PhoneAuthProvider.provider().credential( + withVerificationID: verificationID, + verificationCode: verificationCode + ) + // [END get_phone_cred] + self.firebaseLogin(credential) + } else { + self.showMessagePrompt("verification code can't be empty") + } + } + } + // [END_EXCLUDE] + } + // [END phone_auth] + } + } else { + self.showMessagePrompt("phone number can't be empty") + } + } + } + case .authGameCenter: + action = UIAlertAction(title: "Game Center", style: .default) { UIAlertAction in + // [START firebase_auth_gamecenter] + GameCenterAuthProvider.getCredential { credential, error in + self.showSpinner { + if let error = error { + self.hideSpinner { + self.showMessagePrompt(error.localizedDescription) + return + } + } + if let credential = credential { + Auth.auth().signIn(with: credential) { result, error in + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + } + } + } + } + } + // [END firebase_auth_gamecenter] + } + case .authMicrosoft: + action = UIAlertAction(title: "Microsoft", style: .default) { UIAlertAction in + // [START firebase_auth_microsoft] + self.microsoftProvider?.getCredentialWith(_: nil) { credential, error in + self.showSpinner { + if let error = error { + self.hideSpinner { + self.showMessagePrompt(error.localizedDescription) + return + } + } + if let credential = credential { + Auth.auth().signIn(with: credential) { result, error in + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + } + } + } + } + } + // [END firebase_auth_microsoft] + } + } + picker.addAction(action) + } + + picker.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + present(picker, animated: true, completion: nil) + } + + @IBAction func didTapSignIn(_ sender: AnyObject) { + showAuthPicker([ + AuthProvider.authEmail, + AuthProvider.authAnonymous, + AuthProvider.authApple, + AuthProvider.authGoogle, + AuthProvider.authFacebook, + AuthProvider.authTwitter, + AuthProvider.authGitHub, + AuthProvider.authPhone, + AuthProvider.authCustom, + AuthProvider.authPasswordless, + AuthProvider.authGameCenter, + AuthProvider.authMicrosoft, + ]) + } + + @IBAction func didTapLink(_ sender: AnyObject) { + var providers = Set([ + AuthProvider.authGoogle, + AuthProvider.authFacebook, + AuthProvider.authTwitter, + AuthProvider.authPhone, + ]) + // Remove any existing providers. Note that this is not a complete list of + // providers, so always check the documentation for a complete reference: + // https://firebase.google.com/docs/auth + let user = Auth.auth().currentUser + for info in (user?.providerData)! { + switch info.providerID { + case TwitterAuthProviderID: + providers.remove(AuthProvider.authTwitter) + case FacebookAuthProviderID: + providers.remove(AuthProvider.authFacebook) + case GoogleAuthProviderID: + providers.remove(AuthProvider.authGoogle) + case PhoneAuthProviderID: + providers.remove(AuthProvider.authPhone) + default: + break + } + } + showAuthPicker(Array(providers)) + } + + func setTitleDisplay(_ user: User?) { + if let name = user?.displayName { + navigationItem.title = "Welcome \(name)" + } else { + navigationItem.title = "Authentication Example" + } + } + + func firebaseLogin(_ credential: AuthCredential) { + showSpinner { + if let user = Auth.auth().currentUser { + // [START link_credential] + user.link(with: credential) { authResult, error in + // [START_EXCLUDE] + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + self.tableView.reloadData() + } + // [END_EXCLUDE] + } + // [END link_credential] + } else { + // [START signin_credential] + Auth.auth().signIn(with: credential) { authResult, error in + // [START_EXCLUDE silent] + self.hideSpinner { + // [END_EXCLUDE] + if let error = error { + let authError = error as NSError + if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue { + // The user is a multi-factor user. Second factor challenge is required. + let resolver = authError + .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver + var displayNameString = "" + for tmpFactorInfo in resolver.hints { + displayNameString += tmpFactorInfo.displayName ?? "" + displayNameString += " " + } + self.showTextInputPrompt( + withMessage: "Select factor to sign in\n\(displayNameString)", + completionBlock: { userPressedOK, displayName in + var selectedHint: PhoneMultiFactorInfo? + for tmpFactorInfo in resolver.hints { + if displayName == tmpFactorInfo.displayName { + selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo + } + } + PhoneAuthProvider.provider() + .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil, + multiFactorSession: resolver + .session) { verificationID, error in + if error != nil { + print( + "Multi factor start sign in failed. Error: \(error.debugDescription)" + ) + } else { + self.showTextInputPrompt( + withMessage: "Verification code for \(selectedHint?.displayName ?? "")", + completionBlock: { userPressedOK, verificationCode in + let credential: PhoneAuthCredential? = PhoneAuthProvider.provider() + .credential(withVerificationID: verificationID!, + verificationCode: verificationCode!) + let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator + .assertion(with: credential!) + resolver.resolveSignIn(with: assertion!) { authResult, error in + if error != nil { + print( + "Multi factor finanlize sign in failed. Error: \(error.debugDescription)" + ) + } else { + self.navigationController?.popViewController(animated: true) + } + } + } + ) + } + } + } + ) + } else { + self.showMessagePrompt(error.localizedDescription) + return + } + // [START_EXCLUDE] + self.showMessagePrompt(error.localizedDescription) + // [END_EXCLUDE] + return + } + // User is signed in + // [START_EXCLUDE] + // Merge prevUser and currentUser accounts and data + // ... + // [END_EXCLUDE] + // [START_EXCLUDE silent] + } + // [END_EXCLUDE] + } + // [END signin_credential] + } + } + } + + @IBAction func didTapSignOut(_ sender: AnyObject) { + // [START signout] + let firebaseAuth = Auth.auth() + do { + try firebaseAuth.signOut() + } catch let signOutError as NSError { + print("Error signing out: %@", signOutError) + } + // [END signout] + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + // [START auth_listener] + handle = Auth.auth().addStateDidChangeListener { auth, user in + // [START_EXCLUDE] + self.setTitleDisplay(user) + self.tableView.reloadData() + // [END_EXCLUDE] + } + // [END auth_listener] + microsoftProvider = OAuthProvider(providerID: "microsoft.com") + twitterProvider = OAuthProvider(providerID: "twitter.com") + gitHubProvider = OAuthProvider(providerID: "github.com") + // Authenticate Game Center Local Player + // Uncomment to sign in with Game Center + // self.authenticateGameCenterLocalPlayer() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + // [START remove_auth_listener] + Auth.auth().removeStateDidChangeListener(handle!) + // [END remove_auth_listener] + } + + func authenticateGameCenterLocalPlayer() { + let localPlayer = GKLocalPlayer.local + localPlayer.authenticateHandler = { gcAuthViewController, error in + if let gcAuthViewController = gcAuthViewController { + // Pause any activities that require user interaction, then present the + // gcAuthViewController to the player. + self.present(gcAuthViewController, animated: true, completion: nil) + } else if localPlayer.isAuthenticated { + // Local player is logged in to Game Center. + } else { + // Error + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + } + } + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case kSectionSignIn: + return 1 + case kSectionUser, kSectionToken, kSectionMultiFactor: + if Auth.auth().currentUser != nil { + return 1 + } else { + return 0 + } + case kSectionProviders: + if let user = Auth.auth().currentUser { + return user.providerData.count + } + return 0 + default: + return 0 + } + } + + override func tableView(_ tableView: UITableView, + cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell: UITableViewCell? + switch indexPath.section { + case kSectionSignIn: + // [START current_user] + if Auth.auth().currentUser != nil { + // User is signed in. + // [START_EXCLUDE] + cell = tableView.dequeueReusableCell(withIdentifier: "SignOut") + // [END_EXCLUDE] + } else { + // No user is signed in. + // [START_EXCLUDE] + cell = tableView.dequeueReusableCell(withIdentifier: "SignIn") + // [END_EXCLUDE] + } + // [END current_user] + case kSectionUser: + cell = tableView.dequeueReusableCell(withIdentifier: "Profile") + // [START get_user_profile] + let user = Auth.auth().currentUser + // [END get_user_profile] + // [START user_profile] + if let user = user { + // The user's ID, unique to the Firebase project. + // Do NOT use this value to authenticate with your backend server, + // if you have one. Use getTokenWithCompletion:completion: instead. + let uid = user.uid + let email = user.email + let photoURL = user.photoURL + var multiFactorString = "MultiFactor: " + for info in user.multiFactor.enrolledFactors { + multiFactorString += info.displayName ?? "[DispayName]" + multiFactorString += " " + } + // [START_EXCLUDE] + let emailLabel = cell?.viewWithTag(1) as? UILabel + let userIDLabel = cell?.viewWithTag(2) as? UILabel + let profileImageView = cell?.viewWithTag(3) as? UIImageView + let multiFactorLabel = cell?.viewWithTag(4) as? UILabel + emailLabel?.text = email + userIDLabel?.text = uid + multiFactorLabel?.text = multiFactorString + if isMFAEnabled { + multiFactorLabel?.isHidden = false + } else { + multiFactorLabel?.isHidden = true + } + + struct last { + static var photoURL: URL? = nil + } + last.photoURL = photoURL // to prevent earlier image overwrites later one. + if let photoURL = photoURL { + DispatchQueue.global(qos: .default).async { + let data = try? Data(contentsOf: photoURL) + if let data = data { + let image = UIImage(data: data) + DispatchQueue.main.async { + if photoURL == last.photoURL { + profileImageView?.image = image + } + } + } + } + } else { + profileImageView?.image = UIImage(named: "ic_account_circle") + } + // [END_EXCLUDE] + } + // [END user_profile] + case kSectionProviders: + cell = tableView.dequeueReusableCell(withIdentifier: "Provider") + // [START provider_data] + let userInfo = Auth.auth().currentUser?.providerData[indexPath.row] + cell?.textLabel?.text = userInfo?.providerID + // Provider-specific UID + cell?.detailTextLabel?.text = userInfo?.uid + // [END provider_data] + case kSectionToken: + cell = tableView.dequeueReusableCell(withIdentifier: "Token") + let requestEmailButton = cell?.viewWithTag(4) as? UIButton + requestEmailButton?.isEnabled = (Auth.auth().currentUser?.email != nil) ? true : false + case kSectionMultiFactor: + cell = tableView.dequeueReusableCell(withIdentifier: "MultiFactor") + + default: + fatalError("Unknown section in UITableView") + } + return cell! + } + + override func tableView(_ tableView: UITableView, + titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) + -> String? { + return "Unlink" + } + + override func tableView(_ tableView: UITableView, + editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell + .EditingStyle { + if indexPath.section == kSectionProviders { + return .delete + } + return .none + } + + // Swipe to delete + override func tableView(_ tableView: UITableView, + commit editingStyle: UITableViewCell.EditingStyle, + forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + let providerID = Auth.auth().currentUser?.providerData[indexPath.row].providerID + showSpinner { + // [START unlink_provider] + Auth.auth().currentUser?.unlink(fromProvider: providerID!) { user, error in + // [START_EXCLUDE] + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + tableView.reloadData() + } + // [END_EXCLUDE] + } + // [END unlink_provider] + } + } + } + + override func tableView(_ tableView: UITableView, + heightForRowAt indexPath: IndexPath) -> CGFloat { + if indexPath.section == kSectionUser { + return 200 + } + return 44 + } + + override func numberOfSections(in tableView: UITableView) -> Int { + if isMFAEnabled { + return 5 + } else { + return 4 + } + } + + @IBAction func didMultiFactorEnroll(_ sender: Any) { + let user = Auth.auth().currentUser + if user == nil { + print("Please sign in first.") + } else { + showTextInputPrompt(withMessage: "Phone Number") { userPressedOK, phoneNumber in + user?.multiFactor.getSessionWithCompletion { session, error in + PhoneAuthProvider.provider().verifyPhoneNumber( + phoneNumber!, + uiDelegate: nil, + multiFactorSession: session, + completion: { verificationID, error in + if let error = error { + self.showMessagePrompt(error.localizedDescription) + } else { + self.showTextInputPrompt( + withMessage: "Verification code", + completionBlock: { userPressedOK, verificationCode in + let credential = PhoneAuthProvider.provider() + .credential(withVerificationID: verificationID!, + verificationCode: verificationCode!) + let assertion = PhoneMultiFactorGenerator.assertion(with: credential) + self.showTextInputPrompt( + withMessage: "Display name", + completionBlock: { userPressedOK, displayName in + user?.multiFactor.enroll( + with: assertion, + displayName: displayName, + completion: { error in + if let error = error { + self.showMessagePrompt(error.localizedDescription) + } else { + print("Multi factor finanlize enroll succeeded.") + self.showTypicalUIForUserUpdateResults( + withTitle: "Multi Factor", + error: error + ) + } + } + ) + } + ) + } + ) + } + } + ) + } + } + } + } + + @IBAction func didMultiFactorUnenroll(_ sender: Any) { + var displayNameString = "" + for tmpFactorInfo in Auth.auth().currentUser!.multiFactor.enrolledFactors { + displayNameString += tmpFactorInfo.displayName ?? " " + displayNameString += " " + } + showTextInputPrompt(withMessage: "Multifactor Unenroll \(displayNameString)") { userPressedOK, displayName in + var factorInfo: MultiFactorInfo? + for tmpFactorInfo: MultiFactorInfo in Auth.auth().currentUser!.multiFactor.enrolledFactors { + if displayName == tmpFactorInfo.displayName { + factorInfo = tmpFactorInfo + } + } + Auth.auth().currentUser?.multiFactor.unenroll(with: factorInfo!, completion: { error in + if let error = error { + self.showMessagePrompt(error.localizedDescription) + } else { + print("Multi factor finanlize unenroll succeeded.") + self.showTypicalUIForUserUpdateResults(withTitle: "Multi Factor", error: error) + } + }) + } + } + + @IBAction func didTokenRefresh(_ sender: AnyObject) { + let action: AuthTokenCallback = { token, error in + let okAction = UIAlertAction(title: self.kOKButtonText, style: .default) { + action in print(self.kOKButtonText) + } + if let error = error { + let alertController = UIAlertController(title: self.kTokenRefreshErrorAlertTitle, + message: error.localizedDescription, + preferredStyle: .alert) + alertController.addAction(okAction) + self.present(alertController, animated: true, completion: nil) + return + } + + // Log token refresh event to Scion. + Analytics.logEvent("tokenrefresh", parameters: nil) + + let alertController = UIAlertController(title: self.kTokenRefreshedAlertTitle, + message: token, preferredStyle: .alert) + alertController.addAction(okAction) + self.present(alertController, animated: true, completion: nil) + } + // [START token_refresh] + Auth.auth().currentUser?.getIDTokenForcingRefresh(true, completion: action) + // [END token_refresh] + } + + /** @fn setDisplayName + @brief Changes the display name of the current user. + */ + @IBAction func didSetDisplayName(_ sender: AnyObject) { + showTextInputPrompt(withMessage: "Display Name:") { userPressedOK, userInput in + if let displayName = userInput { + self.showSpinner { + // [START profile_change] + let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest() + changeRequest?.displayName = displayName + changeRequest?.commitChanges { error in + // [START_EXCLUDE] + self.hideSpinner { + self.showTypicalUIForUserUpdateResults( + withTitle: self.kSetDisplayNameTitle, + error: error as NSError? + ) + self.setTitleDisplay(Auth.auth().currentUser) + } + // [END_EXCLUDE] + } + // [END profile_change] + } + } else { + self.showMessagePrompt("displayname can't be empty") + } + } + } + + /** @fn requestVerifyEmail + @brief Requests a "verify email" email be sent. + */ + @IBAction func didRequestVerifyEmail(_ sender: AnyObject) { + showSpinner { + // [START send_verification_email] + Auth.auth().currentUser?.sendEmailVerification { error in + // [START_EXCLUDE] + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + self.showMessagePrompt("Sent") + } + // [END_EXCLUDE] + } + // [END send_verification_email] + } + } + + /** @fn changeEmail + @brief Changes the email address of the current user. + */ + @IBAction func didChangeEmail(_ sender: AnyObject) { + showTextInputPrompt(withMessage: "Email Address:") { userPressedOK, userInput in + if let email = userInput { + self.showSpinner { + // [START change_email] + Auth.auth().currentUser?.updateEmail(to: email) { error in + // [START_EXCLUDE] + self.hideSpinner { + self.showTypicalUIForUserUpdateResults(withTitle: self.kChangeEmailText, error: error) + } + // [END_EXCLUDE] + } + // [END change_email] + } + } else { + self.showMessagePrompt("email can't be empty") + } + } + } + + /** @fn changePassword + @brief Changes the password of the current user. + */ + @IBAction func didChangePassword(_ sender: AnyObject) { + showTextInputPrompt(withMessage: "New Password:") { userPressedOK, userInput in + if let password = userInput { + self.showSpinner { + // [START change_password] + Auth.auth().currentUser?.updatePassword(to: password) { error in + // [START_EXCLUDE] + self.hideSpinner { + self.showTypicalUIForUserUpdateResults( + withTitle: self.kChangePasswordText, + error: error + ) + } + // [END_EXCLUDE] + } + // [END change_password] + } + } else { + self.showMessagePrompt("password can't be empty") + } + } + } + + /** @fn updatePhoneNumber + @brief Updates the phone number of the current user. + */ + @IBAction func didUpdatePhoneNumber(_ sender: AnyObject) { + showTextInputPrompt(withMessage: "New Phone Number:") { userPressedOK, userInput in + if let phoneNumber = userInput { + self.showSpinner { + // [START update_phone] + PhoneAuthProvider.provider() + .verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in + // [START_EXCLUDE] + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + guard let verificationID = verificationID else { return } + self + .showTextInputPrompt(withMessage: "Verification Code:") { userPressedOK, userInput in + if let verificationCode = userInput { + self.showSpinner { + // [END_EXCLUDE] + let credential = PhoneAuthProvider.provider() + .credential(withVerificationID: verificationID, + verificationCode: verificationCode) + Auth.auth().currentUser?.updatePhoneNumber(credential) { error in + // [END update_phone] + self.hideSpinner { + self.showTypicalUIForUserUpdateResults( + withTitle: self.kUpdatePhoneNumberText, + error: error + ) + } + } + } + } else { + self.showMessagePrompt("verification code can't be empty") + } + } + } + } + } + } else { + self.showMessagePrompt("phone number can't be empty") + } + } + } + + // MARK: - Helpers + + /** @fn showTypicalUIForUserUpdateResultsWithTitle:error: + @brief Shows a @c UIAlertView if error is non-nil with the localized description of the error. + @param resultsTitle The title of the @c UIAlertView + @param error The error details to display if non-nil. + */ + func showTypicalUIForUserUpdateResults(withTitle resultsTitle: String, error: Error?) { + if let error = error { + let message = "\(error.localizedDescription)" + let okAction = UIAlertAction(title: kOKButtonText, style: .default) { + action in print(self.kOKButtonText) + } + let alertController = UIAlertController(title: resultsTitle, + message: message, preferredStyle: .alert) + alertController.addAction(okAction) + present(alertController, animated: true, completion: nil) + return + } + tableView.reloadData() + } + + // MARK: - Sign in with Google + + func startSignInWithGoogleFlow() { + // [START headless_google_auth] + guard let clientID = FirebaseApp.app()?.options.clientID else { return } + + // Create Google Sign In configuration object. + let config = GIDConfiguration(clientID: clientID) + + // Start the sign in flow! + GIDSignIn.sharedInstance.signIn(with: config, presenting: self) { [unowned self] user, error in + + if let error = error { + // [START_EXCLUDE] + self.showMessagePrompt(error.localizedDescription) + // [END_EXCLUDE] + return + } + + // [START google_credential] + guard + let authentication = user?.authentication, + let idToken = authentication.idToken + else { + return + } + + let credential = GoogleAuthProvider.credential(withIDToken: idToken, + accessToken: authentication.accessToken) + // [END google_credential] + + // [START_EXCLUDE] + self.firebaseLogin(credential) + // [END_EXCLUDE] + } + // [END headless_google_auth] + } + + // MARK: - Sign in with Apple + + // Unhashed nonce. + fileprivate var currentNonce: String? + + @available(iOS 13, *) + func startSignInWithAppleFlow() { + let nonce = randomNonceString() + currentNonce = nonce + let appleIDProvider = ASAuthorizationAppleIDProvider() + let request = appleIDProvider.createRequest() + request.requestedScopes = [.fullName, .email] + request.nonce = sha256(nonce) + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + authorizationController.delegate = self + authorizationController.presentationContextProvider = self + authorizationController.performRequests() + } + + // [START random_nonce] + private func randomNonceString(length: Int = 32) -> String { + precondition(length > 0) + var randomBytes = [UInt8](repeating: 0, count: length) + let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) + if errorCode != errSecSuccess { + fatalError( + "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)" + ) + } + + let charset: [Character] = + Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") + + let nonce = randomBytes.map { byte in + // Pick a random character from the set, wrapping around if needed. + charset[Int(byte) % charset.count] + } + + return String(nonce) + } + + // [END random_nonce] + + // [START sha_256] + @available(iOS 13, *) + private func sha256(_ input: String) -> String { + let inputData = Data(input.utf8) + let hashedData = SHA256.hash(data: inputData) + let hashString = hashedData.compactMap { + String(format: "%02x", $0) + }.joined() + + return hashString + } + + // [END sha_256] +} + +@available(iOS 13.0, *) +extension MainViewController: ASAuthorizationControllerDelegate { + func authorizationController(controller: ASAuthorizationController, + didCompleteWithAuthorization authorization: ASAuthorization) { + if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential { + guard let nonce = currentNonce else { + fatalError("Invalid state: A login callback was received, but no login request was sent.") + } + + guard let appleIDToken = appleIDCredential.identityToken else { + print("Unable to fetch identity token") + return + } + guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else { + print("Unable to serialize token string from data: \(appleIDToken.debugDescription)") + return + } + // Initialize a Firebase credential. + let credential = OAuthProvider.credential(withProviderID: "apple.com", + idToken: idTokenString, + rawNonce: nonce) + // Sign in with Firebase. + firebaseLogin(credential) + } + } + + func authorizationController(controller: ASAuthorizationController, + didCompleteWithError error: Error) { + // Handle error. + print("Sign in with Apple errored: \(error)") + } +} + +@available(iOS 13.0, *) +extension MainViewController: ASAuthorizationControllerPresentationContextProviding { + func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + return view.window! + } +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/PasswordlessViewController.swift b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/PasswordlessViewController.swift new file mode 100644 index 00000000..b20fa588 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/PasswordlessViewController.swift @@ -0,0 +1,98 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseAuth +import UIKit + +@objc(PasswordlessViewController) +class PasswordlessViewController: UIViewController { + @IBOutlet var emailField: UITextField! + @IBOutlet var signInButton: UIButton! + var link: String! + + override func viewDidLoad() { + super.viewDidLoad() + emailField.text = UserDefaults.standard.value(forKey: "Email") as? String + if let link = UserDefaults.standard.value(forKey: "Link") as? String { + self.link = link + signInButton.isEnabled = true + } + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + view.endEditing(true) + } + + @IBAction func didTapSignInWithEmailLink(_ sender: AnyObject) { + if let email = emailField.text { + showSpinner { + // [START signin_emaillink] + Auth.auth().signIn(withEmail: email, link: self.link) { user, error in + // [START_EXCLUDE] + self.hideSpinner { + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + self.navigationController!.popViewController(animated: true) + } + // [END_EXCLUDE] + } + // [END signin_emaillink] + } + } else { + showMessagePrompt("Email can't be empty") + } + } + + @IBAction func didTapSendSignInLink(_ sender: AnyObject) { + if let email = emailField.text { + showSpinner { + // [START action_code_settings] + let actionCodeSettings = ActionCodeSettings() + actionCodeSettings.url = URL(string: "https://www.example.com") + // The sign-in operation has to always be completed in the app. + actionCodeSettings.handleCodeInApp = true + actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!) + actionCodeSettings.setAndroidPackageName("com.example.android", + installIfNotAvailable: false, minimumVersion: "12") + // [END action_code_settings] + // [START send_signin_link] + Auth.auth().sendSignInLink(toEmail: email, + actionCodeSettings: actionCodeSettings) { error in + // [START_EXCLUDE] + self.hideSpinner { + // [END_EXCLUDE] + if let error = error { + self.showMessagePrompt(error.localizedDescription) + return + } + // The link was successfully sent. Inform the user. + // Save the email locally so you don't need to ask the user for it again + // if they open the link on the same device. + UserDefaults.standard.set(email, forKey: "Email") + self.showMessagePrompt("Check your email for link") + // [START_EXCLUDE] + } + // [END_EXCLUDE] + } + // [END send_signin_link] + } + } else { + showMessagePrompt("Email can't be empty") + } + } +} diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/UIViewController.swift b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/UIViewController.swift new file mode 100644 index 00000000..fff37786 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwift/UIViewController.swift @@ -0,0 +1,98 @@ +// +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +private class SaveAlertHandle { + static var alertHandle: UIAlertController? + + class func set(_ handle: UIAlertController) { + alertHandle = handle + } + + class func clear() { + alertHandle = nil + } + + class func get() -> UIAlertController? { + return alertHandle + } +} + +extension UIViewController { + /*! @fn showMessagePrompt + @brief Displays an alert with an 'OK' button and a message. + @param message The message to display. + */ + func showMessagePrompt(_ message: String) { + let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) + let okAction = UIAlertAction(title: "OK", style: .default, handler: nil) + alert.addAction(okAction) + present(alert, animated: false, completion: nil) + } + + /*! @fn showTextInputPromptWithMessage + @brief Shows a prompt with a text field and 'OK'/'Cancel' buttons. + @param message The message to display. + @param completion A block to call when the user taps 'OK' or 'Cancel'. + */ + func showTextInputPrompt(withMessage message: String, + completionBlock: @escaping ((Bool, String?) -> Void)) { + let prompt = UIAlertController(title: nil, message: message, preferredStyle: .alert) + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in + completionBlock(false, nil) + } + weak var weakPrompt = prompt + let okAction = UIAlertAction(title: "OK", style: .default) { _ in + guard let text = weakPrompt?.textFields?.first?.text else { return } + completionBlock(true, text) + } + prompt.addTextField(configurationHandler: nil) + prompt.addAction(cancelAction) + prompt.addAction(okAction) + present(prompt, animated: true, completion: nil) + } + + /*! @fn showSpinner + @brief Shows the please wait spinner. + @param completion Called after the spinner has been hidden. + */ + func showSpinner(_ completion: (() -> Void)?) { + let alertController = UIAlertController(title: nil, message: "Please Wait...\n\n\n\n", + preferredStyle: .alert) + SaveAlertHandle.set(alertController) + let spinner = UIActivityIndicatorView(style: .whiteLarge) + spinner.color = UIColor(ciColor: .black) + spinner.center = CGPoint(x: alertController.view.frame.midX, + y: alertController.view.frame.midY) + spinner.autoresizingMask = [.flexibleBottomMargin, .flexibleTopMargin, + .flexibleLeftMargin, .flexibleRightMargin] + spinner.startAnimating() + alertController.view.addSubview(spinner) + present(alertController, animated: true, completion: completion) + } + + /*! @fn hideSpinner + @brief Hides the please wait spinner. + @param completion Called after the spinner has been hidden. + */ + func hideSpinner(_ completion: (() -> Void)?) { + if let controller = SaveAlertHandle.get() { + SaveAlertHandle.clear() + controller.dismiss(animated: true, completion: completion) + } + } +} diff --git a/invites/InvitesExampleUITests/InvitesExampleUITests.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwiftUITests/AuthenticationExampleSwiftUITests.swift similarity index 66% rename from invites/InvitesExampleUITests/InvitesExampleUITests.m rename to qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwiftUITests/AuthenticationExampleSwiftUITests.swift index 709c4c3d..040d4c31 100644 --- a/invites/InvitesExampleUITests/InvitesExampleUITests.m +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwiftUITests/AuthenticationExampleSwiftUITests.swift @@ -1,40 +1,34 @@ // -// InvitesExampleUITests.m -// InvitesExampleUITests +// AuthenticationExampleSwiftUITests.swift +// AuthenticationExampleSwiftUITests // -// Created by Ibrahim Ulukaya on 2/13/18. +// Created by Ibrahim Ulukaya on 2/12/18. // Copyright © 2018 Google Inc. All rights reserved. // -#import +import XCTest -@interface InvitesExampleUITests : XCTestCase +class AuthenticationExampleSwiftUITests: XCTestCase { + override func setUp() { + super.setUp() -@end - -@implementation InvitesExampleUITests - -- (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. - + // In UI tests it is usually best to stop immediately when a failure occurs. - self.continueAfterFailure = NO; + continueAfterFailure = false // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. - [[[XCUIApplication alloc] init] launch]; - + XCUIApplication().launch() + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. -} + } -- (void)tearDown { + override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} + super.tearDown() + } -- (void)testExample { + func testExample() { // Use recording to get started writing UI tests. // Use XCTAssert and related functions to verify your tests produce the correct results. + } } - -@end diff --git a/invites/InvitesExampleSwiftUITests/Info.plist b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwiftUITests/Info.plist similarity index 100% rename from invites/InvitesExampleSwiftUITests/Info.plist rename to qs-snippets/LegacyAuthQuickstart/AuthenticationExampleSwiftUITests/Info.plist diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleTests/AuthenticationExampleTests.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleTests/AuthenticationExampleTests.m new file mode 100644 index 00000000..fb1abd02 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleTests/AuthenticationExampleTests.m @@ -0,0 +1,47 @@ +// +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface AuthenticationExampleTests : XCTestCase + +@end + +@implementation AuthenticationExampleTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleUITests/AuthenticationExampleUITests.m b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleUITests/AuthenticationExampleUITests.m new file mode 100644 index 00000000..8cf83239 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleUITests/AuthenticationExampleUITests.m @@ -0,0 +1,321 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "FIREGHelper.h" +#import "FIREGSignInHelper.h" + +typedef BOOL (^SystemAlertHandler)(XCUIElement *); +static SystemAlertHandler const alertHandler = ^(XCUIElement *element) { + if (element.buttons[@"Continue"].exists) { + [element.buttons[@"Continue"] tap]; + } + + if (element.buttons[@"WLAN Only"].exists) { + [element.buttons[@"WLAN Only"] tap]; + } + return YES; +}; + +static NSString *const header = @"Authentication Example"; +static NSString *const signInButton = @"Sign In"; +static NSString *const createButton = @"Create"; +static NSString *const signOutButton = @"Sign Out"; +static NSString *const linkButton = @"Link"; +static NSString *const optionsHeader = @"Select Provider"; +static NSString *const emailPlaceholder = @"Email"; +static NSString *const passwordPlaceholder = @"Password"; +static NSString *const okButton = @"OK"; +static NSString *const backButton = @"Back"; +static NSString *const googleProvider = @"Google"; +static NSString *const tokenRefreshButton = @"Token Refresh"; +static NSString *const requestVerifyEmail = @"Request Verify Email"; +static NSString *const cancelButton = @"Cancel"; +static NSString *const errorMessage = + @"Wrong password. Try again or click Forgot password to reset it."; +static NSString *const alertMessage = @"The user canceled the sign-in flow."; + +// Test account credentials. +static NSString *const testEmail = @"test@test.com"; +static NSString *const testPassword = @"test12"; + +@interface AuthenticationExampleUITests : XCTestCase +@property NSArray *signInOptions; +@end + +@implementation AuthenticationExampleUITests { + XCUIApplication *_app; + id signInPermissionMonitor; +} + +- (void)setUp { + [super setUp]; + self.continueAfterFailure = NO; + _app = [[XCUIApplication alloc] init]; + signInPermissionMonitor = + [self addUIInterruptionMonitorWithDescription:@"Allow Google Sign-In" handler:alertHandler]; + _signInOptions = @[ @"Email", @"Anonymous", @"Google", @"Facebook" ]; + [_app launch]; + + if ([self signedIn]) { + [self signOut]; + } + FIRWaitForVisible(_app.buttons[signInButton]); +} + +- (void)tearDown { + [self removeUIInterruptionMonitor:signInPermissionMonitor]; + [super tearDown]; +} + +- (void)testAuth { + // Verify that Auth Example app launched successfully and its title is visible. + XCTAssertTrue(_app.navigationBars[header].exists); +} + +- (void)testAuthOptions { + [_app.buttons[signInButton] tap]; + FIRWaitForVisible(_app.alerts[optionsHeader]); + + // Make sure various authentication options are present. + for (NSString *option in _signInOptions) { + XCTAssertTrue(_app.buttons[option].exists); + } +} + +- (void)testAuthAnonymously { + // Verify that user is able to authenticate anonymously. + [self signInWith:@"Anonymous"]; + + FIRWaitForVisible(_app.buttons[signOutButton]); + XCTAssertTrue(_app.buttons[linkButton].exists); + [self signOut]; +} + +- (void)testAuthExistingAccount { + [self signInWith:@"Email"]; + FIRWaitForVisible(_app.buttons[createButton]); + + XCUIElement *inputText = [[_app textFields] elementBoundByIndex:0]; + FIRWaitForVisible(inputText); + [inputText tap]; + [inputText typeText:testEmail]; + + XCUIElement *password = [[_app secureTextFields] elementBoundByIndex:0]; + FIRWaitForVisible(password); + [password tap]; + [password typeText:testPassword]; + + [[_app buttons][signInButton] tap]; + FIRWaitForVisible(_app.buttons[signOutButton]); + XCTAssertTrue([self signedIn], @"User should be able to sign-in with existing credentials"); + [self signOut]; +} + +- (void)testAuthExistingAccountWrongPassword { + [self signInWith:@"Email"]; + FIRWaitForVisible(_app.buttons[createButton]); + + XCUIElement *inputText = [[_app textFields] elementBoundByIndex:0]; + FIRWaitForVisible(inputText); + [inputText tap]; + [inputText typeText:testEmail]; + + XCUIElement *password = [[_app secureTextFields] elementBoundByIndex:0]; + FIRWaitForVisible(password); + [password tap]; + [password typeText:@"wrong password"]; + + [[_app buttons][signInButton] tap]; + XCUIElement *error = + _app.alerts.staticTexts[@"The password is invalid or the user does not have a password."]; + FIRWaitForVisible(error); + XCTAssertTrue(error.exists); + + [self dismissAlertIfOpen]; + [self goBack]; + + XCTAssertFalse([self signedIn], @"User shouldn't be able to sign-in with wrong credentials"); +} + +- (void)testCreateAccountBadPassword { + [self signInWith:@"Email"]; + FIRWaitForVisible(_app.buttons[createButton]); + + // 5 characters is not long enough, user should see an error. + [self createLogin:testEmail withPassword:randomString(5)]; + + XCUIElement *error = _app.alerts.staticTexts[@"The password must be 6 characters long or more."]; + FIRWaitForVisible(error); + XCTAssertTrue(error.exists); + + [self dismissAlertIfOpen]; + [self goBack]; + XCTAssertFalse([self signedIn], @"User shouldn't be signed in with a weak password"); +} + +- (void)testCreateAlreadyExistingAccount { + [self signInWith:@"Email"]; + FIRWaitForVisible(_app.buttons[createButton]); + + // This account is already created. + [self createLogin:testEmail withPassword:testPassword]; + + XCUIElement *error = + _app.alerts.staticTexts[@"The email address is already in use by another account."]; + FIRWaitForVisible(error); + XCTAssertTrue(error.exists); + + [self dismissAlertIfOpen]; + [self goBack]; + XCTAssertFalse([self signedIn], @"User shouldn't be able to create an already existing account"); +} + +- (void)testCreateAccountCorrectPassword { + [self signInWith:@"Email"]; + FIRWaitForVisible(_app.buttons[createButton]); + + // These are valid credentials. + NSString *newEmail = [timestamp() stringByAppendingString:@"_test@test.com"]; + NSString *newPassword = randomString(10); + [self createLogin:newEmail withPassword:newPassword]; + + FIRWaitForVisible(_app.buttons[signOutButton]); + XCTAssertTrue([self signedIn], @"User should be signed in with newly created credentials"); + [self signOut]; +} + +// TODO(b/140411106): Fix the GoogleSignIn breakage. +- (void)FAILING_testGoogleSignInAndLinkAccount { + [self signInWith:@"Google"]; + + // User can be signed in right away, without following Google Sign-In flow. + if (![self signedIn]) { + doGoogleSignIn(_app, YES, YES); + // Wait till all alerts and spinners are gone. + FIRWaitForVisible(_app.buttons[signOutButton]); + } + // Make sure user is signed in. + XCTAssertTrue([self signedIn], @"User should be able to sign-in with existing credentials"); + + // Make sure all the required UI elements are present. + XCTAssertTrue(_app.buttons[tokenRefreshButton].exists); + XCTAssertTrue(_app.buttons[requestVerifyEmail].exists); + XCTAssertTrue(_app.buttons[linkButton].exists); + + // Sign out from this Google account. + [self signOut]; + XCTAssertFalse([self signedIn], @"User should be signed out"); + + // Sign in anonymously. + [self signInWith:@"Anonymous"]; + XCTAssertTrue([self signedIn], @"User should be able to sign-in anonymously"); + + // Try to link anonymous accout with exisiting Google account. + FIRWaitForVisible(_app.buttons[linkButton]); + [_app.buttons[linkButton] tap]; + FIRWaitForVisible(_app.buttons[@"Google"]); + [_app.buttons[@"Google"] tap]; + + XCUIElement *error = + _app.alerts + .staticTexts[@"This credential is already associated with a different user account."]; + FIRWaitForVisible(error); + XCTAssertTrue(error.exists); + + // Dismiss the alert. + [self dismissAlertIfOpen]; + + [self signOut]; +} + +// TODO(b/140411106): Fix the GoogleSignIn breakage. +- (void)FAILING_testGoogleSignInWrongPasswordAndCancelFlow { + [self signInWith:@"Google"]; + if (![self signedIn]) { + // Try to sign in with invalid credentials. + doGoogleSignIn(_app, NO, YES); + + XCUIElement *error = _app.staticTexts[errorMessage]; + XCTAssertTrue([error exists], @"Error message should be present."); + + // Close Safari ViewController. + [_app.buttons[cancelButton] tap]; + + // Make sure the correct alert message appears. + FIRWaitForVisible(_app.alerts.staticTexts[alertMessage]); + + // Dismiss the alert. + [self dismissAlertIfOpen]; + } +} + +#pragma mark - Helpers + +- (void)signInWith:(NSString *)provider { + FIRWaitForVisible(_app.buttons[signInButton]); + [_app.buttons[signInButton] tap]; + FIRWaitForVisible(_app.buttons[provider]); + [_app.buttons[provider] tap]; +} + +- (void)signOut { + FIRWaitForVisible(_app.buttons[signOutButton]); + [_app.buttons[signOutButton] tap]; + XCTAssertTrue(_app.buttons[signInButton].exists); +} + +- (BOOL)signedIn { + FIRWaitForVisible(_app.buttons[signOutButton]); + return _app.buttons[signOutButton].exists; +} + +// Navigate back using Navigation Controller (back button could different on different OS, +// so both options are checked). +- (void)goBack { + FIRWaitForVisible(_app.buttons[backButton]); + if (_app.buttons[backButton].exists) { + [_app.buttons[backButton] tap]; + } else { + [[_app.navigationBars.buttons firstMatch] tap]; + } +} + +// Dismiss alert by tapping "OK" button. +- (void)dismissAlertIfOpen { + XCUIElement *button = _app.buttons[okButton]; + FIRWaitForVisible(button); + if (button.exists) { + [button tap]; + } +} + +- (void)createLogin:(NSString *)login withPassword:(NSString *)password { + FIRWaitForVisible(_app.buttons[createButton]); + [_app.buttons[createButton] tap]; + + XCUIElement *inputText = [[[_app alerts] textFields] elementBoundByIndex:0]; + FIRWaitForVisible(inputText); + [inputText typeText:login]; + [[_app buttons][okButton] tap]; + + XCUIElement *passwordField = [[[_app alerts] textFields] elementBoundByIndex:0]; + FIRWaitForVisible(passwordField); + [passwordField typeText:password]; + [[_app buttons][okButton] tap]; +} + +@end diff --git a/invites/InvitesExampleUITests/Info.plist b/qs-snippets/LegacyAuthQuickstart/AuthenticationExampleUITests/Info.plist similarity index 100% rename from invites/InvitesExampleUITests/Info.plist rename to qs-snippets/LegacyAuthQuickstart/AuthenticationExampleUITests/Info.plist diff --git a/qs-snippets/LegacyAuthQuickstart/Podfile b/qs-snippets/LegacyAuthQuickstart/Podfile new file mode 100644 index 00000000..be0e8d96 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/Podfile @@ -0,0 +1,17 @@ +# AuthenticationExample + +use_frameworks! +platform :ios, '15.0' +pod 'FirebaseAnalytics' +pod 'FirebaseAuth' + +# These are pods used for the auth providers. +pod 'FBSDKLoginKit' +pod 'GoogleSignIn' + +target 'AuthenticationExample' do +end +target 'AuthenticationExampleSwift' do +end +target 'AuthenticationExampleTests' do +end diff --git a/qs-snippets/LegacyAuthQuickstart/Podfile.lock b/qs-snippets/LegacyAuthQuickstart/Podfile.lock new file mode 100644 index 00000000..4a3fb217 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/Podfile.lock @@ -0,0 +1,192 @@ +PODS: + - AppAuth (2.0.0): + - AppAuth/Core (= 2.0.0) + - AppAuth/ExternalUserAgent (= 2.0.0) + - AppAuth/Core (2.0.0) + - AppAuth/ExternalUserAgent (2.0.0): + - AppAuth/Core + - AppCheckCore (11.2.0): + - GoogleUtilities/Environment (~> 8.0) + - GoogleUtilities/UserDefaults (~> 8.0) + - PromisesObjC (~> 2.4) + - FBAEMKit (18.0.1): + - FBSDKCoreKit_Basics (= 18.0.1) + - FBSDKCoreKit (18.0.1): + - FBAEMKit (= 18.0.1) + - FBSDKCoreKit_Basics (= 18.0.1) + - FBSDKCoreKit_Basics (18.0.1) + - FBSDKLoginKit (18.0.1): + - FBSDKCoreKit (= 18.0.1) + - FirebaseAnalytics (12.6.0): + - FirebaseAnalytics/Default (= 12.6.0) + - FirebaseCore (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseAnalytics/Default (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - GoogleAppMeasurement/Default (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseAppCheckInterop (12.6.0) + - FirebaseAuth (12.6.0): + - FirebaseAppCheckInterop (~> 12.6.0) + - FirebaseAuthInterop (~> 12.6.0) + - FirebaseCore (~> 12.6.0) + - FirebaseCoreExtension (~> 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GTMSessionFetcher/Core (< 6.0, >= 3.4) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (12.6.0) + - FirebaseCore (12.6.0): + - FirebaseCoreInternal (~> 12.6.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreExtension (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseCoreInternal (12.6.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseInstallations (12.6.0): + - FirebaseCore (~> 12.6.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - GoogleAdsOnDeviceConversion (3.2.0): + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Core (12.6.0): + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Default (12.6.0): + - GoogleAdsOnDeviceConversion (~> 3.2.0) + - GoogleAppMeasurement/Core (= 12.6.0) + - GoogleAppMeasurement/IdentitySupport (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/IdentitySupport (12.6.0): + - GoogleAppMeasurement/Core (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleSignIn (9.0.0): + - AppAuth (~> 2.0) + - AppCheckCore (~> 11.0) + - GTMAppAuth (~> 5.0) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleUtilities/AppDelegateSwizzler (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.1.0): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.1.0)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GTMAppAuth (5.0.0): + - AppAuth/Core (~> 2.0) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher/Core (3.5.0) + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - PromisesObjC (2.4.0) + - RecaptchaInterop (101.0.0) + +DEPENDENCIES: + - FBSDKLoginKit + - FirebaseAnalytics + - FirebaseAuth + - GoogleSignIn + +SPEC REPOS: + trunk: + - AppAuth + - AppCheckCore + - FBAEMKit + - FBSDKCoreKit + - FBSDKCoreKit_Basics + - FBSDKLoginKit + - FirebaseAnalytics + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseInstallations + - GoogleAdsOnDeviceConversion + - GoogleAppMeasurement + - GoogleSignIn + - GoogleUtilities + - GTMAppAuth + - GTMSessionFetcher + - nanopb + - PromisesObjC + - RecaptchaInterop + +SPEC CHECKSUMS: + AppAuth: 1c1a8afa7e12f2ec3a294d9882dfa5ab7d3cb063 + AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f + FBAEMKit: b2ed182002dbcb65d5a60059c9693d322186cd00 + FBSDKCoreKit: 7f96852f2539bdb88ba8d47e5ab4ae70a6f8d691 + FBSDKCoreKit_Basics: 22d4c1a509ded4e45116f3bb167a14907550f62e + FBSDKLoginKit: f48c06446995cd209332831f692cea28b26e51da + FirebaseAnalytics: d0a97a0db6425e5a5d966340b87f92ca7b13a557 + FirebaseAppCheckInterop: e2178171b4145013c7c1a3cc464d1d446d3a1896 + FirebaseAuth: 613c463cb43545a7fd2cd99ade09b78ac472c544 + FirebaseAuthInterop: db06756ef028006d034b6004dc0c37c24f7828d4 + FirebaseCore: 0e38ad5d62d980a47a64b8e9301ffa311457be04 + FirebaseCoreExtension: 032fd6f8509e591fda8cb76f6651f20d926b121f + FirebaseCoreInternal: 69bf1306a05b8ac43004f6cc1f804bb7b05b229e + FirebaseInstallations: 631b38da2e11a83daa4bfb482f79d286a5dfa7ad + GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f + GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee + GoogleSignIn: c7f09cfbc85a1abf69187be091997c317cc33b77 + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 + GTMAppAuth: 217a876b249c3c585a54fd6f73e6b58c4f5c4238 + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba + +PODFILE CHECKSUM: 49d8a9dbfae5766ae10c73fa8acdf33de4335530 + +COCOAPODS: 1.16.2 diff --git a/qs-snippets/LegacyAuthQuickstart/README.md b/qs-snippets/LegacyAuthQuickstart/README.md new file mode 100644 index 00000000..a3c2a22c --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/README.md @@ -0,0 +1,130 @@ +Firebase Authentication Quickstart +============================= + +Introduction +------------ + +- [Read more about Firebase Authentication](https://firebase.google.com/docs/auth/) + +Getting Started +--------------- + +- [Add Firebase to your iOS Project](https://firebase.google.com/docs/ios/setup). + + +### Google Sign In Setup +- Go to the [Firebase Console](https://console.firebase.google.com) and navigate to your project: + - Select the **Auth** panel and then click the **Sign In Method** tab. + - Click **Google** and turn on the **Enable** switch, then click **Save**. +- In Xcode, [add a custom URL scheme for your reversed client ID](https://developers.google.com/identity/sign-in/ios/start-integrating). + - You can find this in the `GoogleService-Info.plist` +- Run the app on your device or simulator. + - Select **Sign In** and select Google to begin. + +### Sign in with Apple Setup +- Go to the [Firebase Console](https://console.firebase.google.com) and navigate to your project: + - Select the **Auth** panel and then click the **Sign In Method** tab. + - Click **Apple** and turn on the **Enable** switch, then click **Save**. +- Run the app on your device or simulator. + - Select **Sign In** and select Apple to begin. +- See the [Getting Started guide](https://firebase.google.com/docs/auth/ios/apple) for more details. + +### Microsoft Sign In Setup +- Go to the [Firebase Console](https://console.firebase.google.com) and navigate to your project: + - Select the **Auth** panel and then click the **Sign In Method** tab. + - Click **Microsoft** and turn on the **Enable** switch, then click **Save**. +- In Xcode, [add a custom URL scheme for your reversed client ID](https://developers.google.com/identity/sign-in/ios/start-integrating). + - You can find this in the `GoogleService-Info.plist` +- Run the app on your device or simulator. + - Select **Sign In** and select Microsoft to begin. + +### Facebook Login Setup +- Go to the [Facebook Developers Site](https://developers.facebook.com) and follow all + instructions to set up a new iOS app. When asked for a bundle ID, use + `com.google.firebase.quickstart.AuthenticationExample`. +- Go to the [Firebase Console](https://console.firebase.google.com) and navigate to your project: + - Select the **Auth** panel and then click the **Sign In Method** tab. + - Click **Facebook** and turn on the **Enable** switch, then click **Save**. + - Enter your Facebook **App Id** and **App Secret** and click **Save**. +- Open your regular `Info.plist` and replace the value of the `FacebookAppID` with the ID of the + Facebook app you just created, e.g 124567. Save that file. +- In the *Info* tab of your target settings add a *URL Type* with a *URL Scheme* of 'fb' + the ID + of your Facebook app, e.g. fb1234567. +- Run the app on your device or simulator. + - Select **Sign In** and select Facebook to begin. + +### Email/Password Setup +- Go to the [Firebase Console](https://console.firebase.google.com) and navigate to your project: + - Select the **Auth** panel and then click the **Sign In Method** tab. + - Click **Email/Password** and turn on the **Enable** switch, then click **Save**. +- Run the app on your device or simulator. + - Select **Sign In** and select Email to begin. + +### Multi Factor Authentication +**Note**: Multi Factor authentication only works for apps using [Google Cloud Identity Platform](https://cloud.google.com/identity-platform/docs/ios/mfa), +a paid service. If you are only using Firebase Authentication this sample will not work for you. + +- Run the app on your device + - Select **Email (with MFA)** from the main screen. + - Sign in (if necessary). + - Verify your email (if necessary). + - Hit **Enroll MFA** to begin enrolling an SMS second factor. + +### Twitter Login Setup +- [Register your app](https://apps.twitter.com) as a developer application on Twitter and get your + app's OAuth API key and API secret. +- Go to the [Firebase Console](https://console.firebase.google.com) and navigate to your project: + - Select the **Auth** panel and then click the **Sign In Method** tab. + - Click **Twitter** and turn on the **Enable** switch, then click **Save**. + - Enter your Twitter **API Key** and **App Secret** and click **Save**. + - Make sure your Firebase OAuth redirect URI (e.g. my-app-12345.firebaseapp.com/__/auth/handler) is set as your + Authorization callback URL in your app's settings page on your [Twitter app's config](https://apps.twitter.com). +- Run the app on your device or simulator. + - Select **Sign In** and select Twitter to begin. + +### Custom Authentication Setup +- In the [Firebase Console](https://console.firebase.google.com/), navigate to **Project settings**: + - Navigate to the **Service accounts** tab. + - Locate the section **All service account**, and click on the `X service accounts` link. This will take you to the Google Cloud Console. +- In the [Google Cloud Console](https://console.cloud.google.com): + - Make sure the right Firebase project is selected. + - From the left "hamburger" menu navigate to the **API Manager** tab. + - Click on the **Credentials** item in the left column. + - Click **New credentials** and select **Service account key**. Select **New service account**, + pick any name, and select **JSON** as the key type. Then click **Create**. + - You should now have a new JSON file for your service account in your Downloads directory. +- In the `quickstart-ios/authentication/LegacyAuthQuickstart/web` directory: + - Open the file `auth.html` in your computer's web browser. + - Click **Choose File** and upload the JSON file you just downloaded. + - Enter any User ID and click **Generate**. + - Copy the token link displayed. +- Run the app on the simulator. + - Select **Sign In** and select Custom to begin. + - Paste in the token you generated earlier. + - When you return to the main screen, you should see the User ID you entered when generating the + token. + +Support +------- + +- [Firebase Support](https://firebase.google.com/support/) + +License +------- + +Copyright 2016 Google, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/qs-snippets/LegacyAuthQuickstart/web/auth.html b/qs-snippets/LegacyAuthQuickstart/web/auth.html new file mode 100644 index 00000000..7328fbc6 --- /dev/null +++ b/qs-snippets/LegacyAuthQuickstart/web/auth.html @@ -0,0 +1,127 @@ + + + + + + Custom Token Generator Example + + + + + + + +
+
+

Token Generator

+
+
+
+
+
+

In order to generate a token you'll need to use a service account JSON file provided + by the Google Developers Console view + of your Firebase project. We recommend that once you're done testing with this tool you + delete the service account you made, and create a fresh one for the integration with your + actual authentication system. +

+

+ To get started, select your JSON file below. +

+ +
+
+
+
+
+ +
+
+
+
+ + + diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/project.pbxproj b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..723b40b0 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/project.pbxproj @@ -0,0 +1,986 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 47; + objects = { + +/* Begin PBXBuildFile section */ + 0B38EF10647EB2DA5A5D56D1 /* GoogleService-Info.plist in Sources */ = {isa = PBXBuildFile; fileRef = 43ED466C2F109A2272796CB6 /* GoogleService-Info.plist */; }; + 1036DA002087CA10003253C9 /* FunctionsExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1036D9FF2087CA10003253C9 /* FunctionsExampleTests.m */; }; + 1036DA0E2087CA25003253C9 /* FunctionsExampleUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1036DA0D2087CA25003253C9 /* FunctionsExampleUITests.m */; }; + 1036DA1C2087CA66003253C9 /* FunctionsExampleSwiftUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1036DA1B2087CA66003253C9 /* FunctionsExampleSwiftUITests.swift */; }; + 10757BD52059CFC7007623FD /* CloudAddCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 10757BD42059CFC7007623FD /* CloudAddCell.m */; }; + 10A181852060458C00133918 /* FAuthPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A181842060458C00133918 /* FAuthPickerViewController.m */; }; + 10B054031C6A3C630061077D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 10B054011C6A3C630061077D /* Main.storyboard */; }; + 10B1CCB7205C4CA70067EDB4 /* FAuthPickerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 10B1CCB6205C4CA70067EDB4 /* FAuthPickerViewController.xib */; }; + 10B1CCB8205C4CA70067EDB4 /* FAuthPickerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 10B1CCB6205C4CA70067EDB4 /* FAuthPickerViewController.xib */; }; + 10B1CCBB205C4D520067EDB4 /* FAuthPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B1CCB9205C4D520067EDB4 /* FAuthPickerViewController.swift */; }; + 10B1CCBF205C51F90067EDB4 /* SignInViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B1CCBE205C51F90067EDB4 /* SignInViewController.swift */; }; + 10B1CCC1205C60890067EDB4 /* CloudAddCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B1CCC0205C60890067EDB4 /* CloudAddCell.swift */; }; + 10B1CCC3205C63370067EDB4 /* CommentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10B1CCC2205C63370067EDB4 /* CommentCell.swift */; }; + 46D64964EDAD308E5F394D61 /* GoogleService-Info.plist in Sources */ = {isa = PBXBuildFile; fileRef = 43ED466C2F109A2272796CB6 /* GoogleService-Info.plist */; }; + 5F5A53521ADE670C00F81DF0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A53511ADE670C00F81DF0 /* main.m */; }; + 5F5A53551ADE670C00F81DF0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A53541ADE670C00F81DF0 /* AppDelegate.m */; }; + 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */; }; + 5F99610A1AE0CF4F0034F503 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961061AE0CF4F0034F503 /* Images.xcassets */; }; + 5F99610B1AE0CF4F0034F503 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961061AE0CF4F0034F503 /* Images.xcassets */; }; + 5F99610C1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */; }; + 5F99610D1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */; }; + 945A2E9F85C854B9E5D77ADF /* GoogleService-Info.plist in Sources */ = {isa = PBXBuildFile; fileRef = 43ED466C2F109A2272796CB6 /* GoogleService-Info.plist */; }; + C8CC5DB90E433A1628474CAE /* GoogleService-Info.plist in Sources */ = {isa = PBXBuildFile; fileRef = 43ED466C2F109A2272796CB6 /* GoogleService-Info.plist */; }; + D295393A72DB35CE1616AA79 /* GoogleService-Info.plist in Sources */ = {isa = PBXBuildFile; fileRef = 43ED466C2F109A2272796CB6 /* GoogleService-Info.plist */; }; + DE43B5D023AEC6980056B15F /* FIREGSignInHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DE43B5CD23AEC6980056B15F /* FIREGSignInHelper.m */; }; + DE43B5D123AEC6980056B15F /* FIREGHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DE43B5CE23AEC6980056B15F /* FIREGHelper.m */; }; + EF4857041C752B7700649485 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 10B054011C6A3C630061077D /* Main.storyboard */; }; + EF6DB4CD1CDD60F700319C08 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4CA1CDD60F700319C08 /* MainViewController.swift */; }; + EF6DB4D41CDD610200319C08 /* CommentCell.m in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4CE1CDD610200319C08 /* CommentCell.m */; }; + EF6DB4D51CDD610200319C08 /* SignInViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4CF1CDD610200319C08 /* SignInViewController.m */; }; + EF6DB4D61CDD610200319C08 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = EF6DB4D01CDD610200319C08 /* MainViewController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 1036DA022087CA10003253C9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5F5A53441ADE670C00F81DF0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5F5A534B1ADE670C00F81DF0; + remoteInfo = FunctionsExample; + }; + 1036DA102087CA25003253C9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5F5A53441ADE670C00F81DF0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5F5A534B1ADE670C00F81DF0; + remoteInfo = FunctionsExample; + }; + 1036DA1E2087CA66003253C9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5F5A53441ADE670C00F81DF0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5F5A53781ADE67D500F81DF0; + remoteInfo = FunctionsExampleSwift; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 1036D9FD2087CA10003253C9 /* FunctionsExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FunctionsExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1036D9FF2087CA10003253C9 /* FunctionsExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FunctionsExampleTests.m; sourceTree = ""; }; + 1036DA012087CA10003253C9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1036DA0B2087CA25003253C9 /* FunctionsExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FunctionsExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1036DA0D2087CA25003253C9 /* FunctionsExampleUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FunctionsExampleUITests.m; sourceTree = ""; }; + 1036DA0F2087CA25003253C9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1036DA192087CA66003253C9 /* FunctionsExampleSwiftUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FunctionsExampleSwiftUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1036DA1B2087CA66003253C9 /* FunctionsExampleSwiftUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FunctionsExampleSwiftUITests.swift; sourceTree = ""; }; + 1036DA1D2087CA66003253C9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 10757BD32059CFC7007623FD /* CloudAddCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CloudAddCell.h; sourceTree = ""; }; + 10757BD42059CFC7007623FD /* CloudAddCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CloudAddCell.m; sourceTree = ""; }; + 10A181842060458C00133918 /* FAuthPickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FAuthPickerViewController.m; sourceTree = ""; }; + 10B054021C6A3C630061077D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 10B1CCB6205C4CA70067EDB4 /* FAuthPickerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FAuthPickerViewController.xib; sourceTree = ""; }; + 10B1CCB9205C4D520067EDB4 /* FAuthPickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FAuthPickerViewController.swift; sourceTree = ""; }; + 10B1CCBC205C4D9B0067EDB4 /* FAuthPickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FAuthPickerViewController.h; sourceTree = ""; }; + 10B1CCBE205C51F90067EDB4 /* SignInViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignInViewController.swift; sourceTree = ""; }; + 10B1CCC0205C60890067EDB4 /* CloudAddCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudAddCell.swift; sourceTree = ""; }; + 10B1CCC2205C63370067EDB4 /* CommentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentCell.swift; sourceTree = ""; }; + 43ED466C2F109A2272796CB6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../GoogleService-Info.plist"; sourceTree = ""; }; + 5F5A534C1ADE670C00F81DF0 /* FunctionsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FunctionsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5F5A53501ADE670C00F81DF0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5F5A53511ADE670C00F81DF0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 5F5A53531ADE670C00F81DF0 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 5F5A53541ADE670C00F81DF0 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 5F5A53791ADE67D500F81DF0 /* FunctionsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FunctionsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 5F9961061AE0CF4F0034F503 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; + DE43B5CC23AEC6980056B15F /* FIREGHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIREGHelper.h; sourceTree = ""; }; + DE43B5CD23AEC6980056B15F /* FIREGSignInHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREGSignInHelper.m; sourceTree = ""; }; + DE43B5CE23AEC6980056B15F /* FIREGHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIREGHelper.m; sourceTree = ""; }; + DE43B5CF23AEC6980056B15F /* FIREGSignInHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIREGSignInHelper.h; sourceTree = ""; }; + DED65CF723E9DE6400461312 /* FIREGSignInInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FIREGSignInInfo.h; sourceTree = ""; }; + EF6DB4CA1CDD60F700319C08 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; + EF6DB4CE1CDD610200319C08 /* CommentCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommentCell.m; sourceTree = ""; }; + EF6DB4CF1CDD610200319C08 /* SignInViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignInViewController.m; sourceTree = ""; }; + EF6DB4D01CDD610200319C08 /* MainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainViewController.m; sourceTree = ""; }; + EF6DB4D11CDD610200319C08 /* CommentCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommentCell.h; sourceTree = ""; }; + EF6DB4D21CDD610200319C08 /* SignInViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignInViewController.h; sourceTree = ""; }; + EF6DB4D31CDD610200319C08 /* MainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainViewController.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1036D9FA2087CA10003253C9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1036DA082087CA25003253C9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1036DA162087CA66003253C9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53491ADE670C00F81DF0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53761ADE67D500F81DF0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1036D9FE2087CA10003253C9 /* FunctionsExampleTests */ = { + isa = PBXGroup; + children = ( + 1036D9FF2087CA10003253C9 /* FunctionsExampleTests.m */, + 1036DA012087CA10003253C9 /* Info.plist */, + ); + path = FunctionsExampleTests; + sourceTree = ""; + }; + 1036DA0C2087CA25003253C9 /* FunctionsExampleUITests */ = { + isa = PBXGroup; + children = ( + 1036DA0D2087CA25003253C9 /* FunctionsExampleUITests.m */, + 1036DA0F2087CA25003253C9 /* Info.plist */, + ); + path = FunctionsExampleUITests; + sourceTree = ""; + }; + 1036DA1A2087CA66003253C9 /* FunctionsExampleSwiftUITests */ = { + isa = PBXGroup; + children = ( + 1036DA1B2087CA66003253C9 /* FunctionsExampleSwiftUITests.swift */, + 1036DA1D2087CA66003253C9 /* Info.plist */, + ); + path = FunctionsExampleSwiftUITests; + sourceTree = ""; + }; + 5F5A53431ADE670C00F81DF0 = { + isa = PBXGroup; + children = ( + 5F5A534E1ADE670C00F81DF0 /* FunctionsExample */, + 5F5A537A1ADE67D500F81DF0 /* FunctionsExampleSwift */, + 1036D9FE2087CA10003253C9 /* FunctionsExampleTests */, + 1036DA0C2087CA25003253C9 /* FunctionsExampleUITests */, + 1036DA1A2087CA66003253C9 /* FunctionsExampleSwiftUITests */, + DE43B5CB23AEC6980056B15F /* TestUtils */, + 5F5A534D1ADE670C00F81DF0 /* Products */, + 5F9961041AE0CF4F0034F503 /* Shared */, + 43ED466C2F109A2272796CB6 /* GoogleService-Info.plist */, + ); + sourceTree = ""; + wrapsLines = 0; + }; + 5F5A534D1ADE670C00F81DF0 /* Products */ = { + isa = PBXGroup; + children = ( + 5F5A534C1ADE670C00F81DF0 /* FunctionsExample.app */, + 5F5A53791ADE67D500F81DF0 /* FunctionsExample.app */, + 1036D9FD2087CA10003253C9 /* FunctionsExampleTests.xctest */, + 1036DA0B2087CA25003253C9 /* FunctionsExampleUITests.xctest */, + 1036DA192087CA66003253C9 /* FunctionsExampleSwiftUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 5F5A534E1ADE670C00F81DF0 /* FunctionsExample */ = { + isa = PBXGroup; + children = ( + 10757BD32059CFC7007623FD /* CloudAddCell.h */, + 10757BD42059CFC7007623FD /* CloudAddCell.m */, + EF6DB4D11CDD610200319C08 /* CommentCell.h */, + EF6DB4CE1CDD610200319C08 /* CommentCell.m */, + EF6DB4D21CDD610200319C08 /* SignInViewController.h */, + EF6DB4CF1CDD610200319C08 /* SignInViewController.m */, + EF6DB4D31CDD610200319C08 /* MainViewController.h */, + EF6DB4D01CDD610200319C08 /* MainViewController.m */, + 5F5A53531ADE670C00F81DF0 /* AppDelegate.h */, + 5F5A53541ADE670C00F81DF0 /* AppDelegate.m */, + 10B1CCBC205C4D9B0067EDB4 /* FAuthPickerViewController.h */, + 10A181842060458C00133918 /* FAuthPickerViewController.m */, + 5F5A534F1ADE670C00F81DF0 /* Supporting Files */, + ); + path = FunctionsExample; + sourceTree = ""; + }; + 5F5A534F1ADE670C00F81DF0 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 10B054011C6A3C630061077D /* Main.storyboard */, + 5F5A53501ADE670C00F81DF0 /* Info.plist */, + 5F5A53511ADE670C00F81DF0 /* main.m */, + 10B1CCB6205C4CA70067EDB4 /* FAuthPickerViewController.xib */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 5F5A537A1ADE67D500F81DF0 /* FunctionsExampleSwift */ = { + isa = PBXGroup; + children = ( + 10B1CCBE205C51F90067EDB4 /* SignInViewController.swift */, + 10B1CCB9205C4D520067EDB4 /* FAuthPickerViewController.swift */, + EF6DB4CA1CDD60F700319C08 /* MainViewController.swift */, + 5F5A537D1ADE67D500F81DF0 /* AppDelegate.swift */, + 10B1CCC0205C60890067EDB4 /* CloudAddCell.swift */, + 10B1CCC2205C63370067EDB4 /* CommentCell.swift */, + ); + path = FunctionsExampleSwift; + sourceTree = ""; + }; + 5F9961041AE0CF4F0034F503 /* Shared */ = { + isa = PBXGroup; + children = ( + 5F9961061AE0CF4F0034F503 /* Images.xcassets */, + 5F9961071AE0CF4F0034F503 /* LaunchScreen.xib */, + ); + name = Shared; + path = ../../shared; + sourceTree = ""; + }; + DE43B5CB23AEC6980056B15F /* TestUtils */ = { + isa = PBXGroup; + children = ( + DE43B5CC23AEC6980056B15F /* FIREGHelper.h */, + DE43B5CD23AEC6980056B15F /* FIREGSignInHelper.m */, + DE43B5CE23AEC6980056B15F /* FIREGHelper.m */, + DE43B5CF23AEC6980056B15F /* FIREGSignInHelper.h */, + DED65CF723E9DE6400461312 /* FIREGSignInInfo.h */, + ); + name = TestUtils; + path = ../../TestUtils; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1036D9FC2087CA10003253C9 /* FunctionsExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1036DA062087CA10003253C9 /* Build configuration list for PBXNativeTarget "FunctionsExampleTests" */; + buildPhases = ( + 1036D9F92087CA10003253C9 /* Sources */, + 1036D9FA2087CA10003253C9 /* Frameworks */, + 1036D9FB2087CA10003253C9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 1036DA032087CA10003253C9 /* PBXTargetDependency */, + ); + name = FunctionsExampleTests; + productName = FunctionsExampleTests; + productReference = 1036D9FD2087CA10003253C9 /* FunctionsExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 1036DA0A2087CA25003253C9 /* FunctionsExampleUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1036DA122087CA25003253C9 /* Build configuration list for PBXNativeTarget "FunctionsExampleUITests" */; + buildPhases = ( + 1036DA072087CA25003253C9 /* Sources */, + 1036DA082087CA25003253C9 /* Frameworks */, + 1036DA092087CA25003253C9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 1036DA112087CA25003253C9 /* PBXTargetDependency */, + ); + name = FunctionsExampleUITests; + productName = FunctionsExampleUITests; + productReference = 1036DA0B2087CA25003253C9 /* FunctionsExampleUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 1036DA182087CA66003253C9 /* FunctionsExampleSwiftUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1036DA202087CA66003253C9 /* Build configuration list for PBXNativeTarget "FunctionsExampleSwiftUITests" */; + buildPhases = ( + 1036DA152087CA66003253C9 /* Sources */, + 1036DA162087CA66003253C9 /* Frameworks */, + 1036DA172087CA66003253C9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 1036DA1F2087CA66003253C9 /* PBXTargetDependency */, + ); + name = FunctionsExampleSwiftUITests; + productName = FunctionsExampleSwiftUITests; + productReference = 1036DA192087CA66003253C9 /* FunctionsExampleSwiftUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 5F5A534B1ADE670C00F81DF0 /* FunctionsExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5F5A536F1ADE670C00F81DF0 /* Build configuration list for PBXNativeTarget "FunctionsExample" */; + buildPhases = ( + 5F5A53481ADE670C00F81DF0 /* Sources */, + 5F5A53491ADE670C00F81DF0 /* Frameworks */, + 5F5A534A1ADE670C00F81DF0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FunctionsExample; + productName = UserManagementExample; + productReference = 5F5A534C1ADE670C00F81DF0 /* FunctionsExample.app */; + productType = "com.apple.product-type.application"; + }; + 5F5A53781ADE67D500F81DF0 /* FunctionsExampleSwift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "FunctionsExampleSwift" */; + buildPhases = ( + 5F5A53751ADE67D500F81DF0 /* Sources */, + 5F5A53761ADE67D500F81DF0 /* Frameworks */, + 5F5A53771ADE67D500F81DF0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FunctionsExampleSwift; + productName = UserManagementExampleSwift; + productReference = 5F5A53791ADE67D500F81DF0 /* FunctionsExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5F5A53441ADE670C00F81DF0 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0930; + LastUpgradeCheck = 1110; + ORGANIZATIONNAME = "Google Inc."; + TargetAttributes = { + 1036D9FC2087CA10003253C9 = { + CreatedOnToolsVersion = 9.3; + ProvisioningStyle = Automatic; + TestTargetID = 5F5A534B1ADE670C00F81DF0; + }; + 1036DA0A2087CA25003253C9 = { + CreatedOnToolsVersion = 9.3; + ProvisioningStyle = Automatic; + TestTargetID = 5F5A534B1ADE670C00F81DF0; + }; + 1036DA182087CA66003253C9 = { + CreatedOnToolsVersion = 9.3; + LastSwiftMigration = 1110; + ProvisioningStyle = Automatic; + TestTargetID = 5F5A53781ADE67D500F81DF0; + }; + 5F5A534B1ADE670C00F81DF0 = { + CreatedOnToolsVersion = 6.3; + DevelopmentTeam = 4G4F7RJDV8; + LastSwiftMigration = 0920; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 1; + }; + com.apple.SafariKeychain = { + enabled = 0; + }; + }; + }; + 5F5A53781ADE67D500F81DF0 = { + CreatedOnToolsVersion = 6.3; + DevelopmentTeam = 4G4F7RJDV8; + LastSwiftMigration = 1110; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 0; + }; + com.apple.SafariKeychain = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "FunctionsExample" */; + compatibilityVersion = "Xcode 6.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5F5A53431ADE670C00F81DF0; + productRefGroup = 5F5A534D1ADE670C00F81DF0 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5F5A534B1ADE670C00F81DF0 /* FunctionsExample */, + 5F5A53781ADE67D500F81DF0 /* FunctionsExampleSwift */, + 1036D9FC2087CA10003253C9 /* FunctionsExampleTests */, + 1036DA0A2087CA25003253C9 /* FunctionsExampleUITests */, + 1036DA182087CA66003253C9 /* FunctionsExampleSwiftUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1036D9FB2087CA10003253C9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1036DA092087CA25003253C9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1036DA172087CA66003253C9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A534A1ADE670C00F81DF0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 10B1CCB7205C4CA70067EDB4 /* FAuthPickerViewController.xib in Resources */, + 5F99610C1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */, + 10B054031C6A3C630061077D /* Main.storyboard in Resources */, + 5F99610A1AE0CF4F0034F503 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53771ADE67D500F81DF0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 10B1CCB8205C4CA70067EDB4 /* FAuthPickerViewController.xib in Resources */, + EF4857041C752B7700649485 /* Main.storyboard in Resources */, + 5F99610D1AE0CF4F0034F503 /* LaunchScreen.xib in Resources */, + 5F99610B1AE0CF4F0034F503 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1036D9F92087CA10003253C9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1036DA002087CA10003253C9 /* FunctionsExampleTests.m in Sources */, + C8CC5DB90E433A1628474CAE /* GoogleService-Info.plist in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1036DA072087CA25003253C9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1036DA0E2087CA25003253C9 /* FunctionsExampleUITests.m in Sources */, + DE43B5D023AEC6980056B15F /* FIREGSignInHelper.m in Sources */, + DE43B5D123AEC6980056B15F /* FIREGHelper.m in Sources */, + 945A2E9F85C854B9E5D77ADF /* GoogleService-Info.plist in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1036DA152087CA66003253C9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1036DA1C2087CA66003253C9 /* FunctionsExampleSwiftUITests.swift in Sources */, + 46D64964EDAD308E5F394D61 /* GoogleService-Info.plist in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53481ADE670C00F81DF0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 10A181852060458C00133918 /* FAuthPickerViewController.m in Sources */, + 10757BD52059CFC7007623FD /* CloudAddCell.m in Sources */, + EF6DB4D51CDD610200319C08 /* SignInViewController.m in Sources */, + EF6DB4D61CDD610200319C08 /* MainViewController.m in Sources */, + EF6DB4D41CDD610200319C08 /* CommentCell.m in Sources */, + 5F5A53551ADE670C00F81DF0 /* AppDelegate.m in Sources */, + 5F5A53521ADE670C00F81DF0 /* main.m in Sources */, + D295393A72DB35CE1616AA79 /* GoogleService-Info.plist in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5F5A53751ADE67D500F81DF0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5F5A537E1ADE67D500F81DF0 /* AppDelegate.swift in Sources */, + 10B1CCBF205C51F90067EDB4 /* SignInViewController.swift in Sources */, + 10B1CCC3205C63370067EDB4 /* CommentCell.swift in Sources */, + 10B1CCBB205C4D520067EDB4 /* FAuthPickerViewController.swift in Sources */, + 10B1CCC1205C60890067EDB4 /* CloudAddCell.swift in Sources */, + EF6DB4CD1CDD60F700319C08 /* MainViewController.swift in Sources */, + 0B38EF10647EB2DA5A5D56D1 /* GoogleService-Info.plist in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1036DA032087CA10003253C9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5F5A534B1ADE670C00F81DF0 /* FunctionsExample */; + targetProxy = 1036DA022087CA10003253C9 /* PBXContainerItemProxy */; + }; + 1036DA112087CA25003253C9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5F5A534B1ADE670C00F81DF0 /* FunctionsExample */; + targetProxy = 1036DA102087CA25003253C9 /* PBXContainerItemProxy */; + }; + 1036DA1F2087CA66003253C9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5F5A53781ADE67D500F81DF0 /* FunctionsExampleSwift */; + targetProxy = 1036DA1E2087CA66003253C9 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 10B054011C6A3C630061077D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 10B054021C6A3C630061077D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1036DA042087CA10003253C9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FunctionsExampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.friendlypix.FunctionsExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FunctionsExample.app/FunctionsExample"; + }; + name = Debug; + }; + 1036DA052087CA10003253C9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FunctionsExampleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.friendlypix.FunctionsExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FunctionsExample.app/FunctionsExample"; + }; + name = Release; + }; + 1036DA132087CA25003253C9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FunctionsExampleUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.friendlypix.FunctionsExampleUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FunctionsExample; + }; + name = Debug; + }; + 1036DA142087CA25003253C9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FunctionsExampleUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.friendlypix.FunctionsExampleUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FunctionsExample; + }; + name = Release; + }; + 1036DA212087CA66003253C9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FunctionsExampleSwiftUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.friendlypix.FunctionsExampleSwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FunctionsExampleSwift; + }; + name = Debug; + }; + 1036DA222087CA66003253C9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + INFOPLIST_FILE = FunctionsExampleSwiftUITests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.friendlypix.FunctionsExampleSwiftUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FunctionsExampleSwift; + }; + name = Release; + }; + 5F5A536D1ADE670C00F81DF0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_BITCODE = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 5F5A536E1ADE670C00F81DF0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = 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_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_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_BITCODE = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5F5A53701ADE670C00F81DF0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4G4F7RJDV8; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = "$(SRCROOT)/FunctionsExample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.FunctionsExample; + PRODUCT_NAME = FunctionsExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 5F5A53711ADE670C00F81DF0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4G4F7RJDV8; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = "$(SRCROOT)/FunctionsExample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.FunctionsExample; + PRODUCT_NAME = FunctionsExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + 5F5A53951ADE67D500F81DF0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4G4F7RJDV8; + INFOPLIST_FILE = "$(SRCROOT)/FunctionsExample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.FunctionsExample; + PRODUCT_NAME = FunctionsExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 5F5A53961ADE67D500F81DF0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 4G4F7RJDV8; + INFOPLIST_FILE = "$(SRCROOT)/FunctionsExample/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.quickstart.FunctionsExample; + PRODUCT_NAME = FunctionsExample; + PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_SWIFT3_OBJC_INFERENCE = Off; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1036DA062087CA10003253C9 /* Build configuration list for PBXNativeTarget "FunctionsExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1036DA042087CA10003253C9 /* Debug */, + 1036DA052087CA10003253C9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1036DA122087CA25003253C9 /* Build configuration list for PBXNativeTarget "FunctionsExampleUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1036DA132087CA25003253C9 /* Debug */, + 1036DA142087CA25003253C9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1036DA202087CA66003253C9 /* Build configuration list for PBXNativeTarget "FunctionsExampleSwiftUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1036DA212087CA66003253C9 /* Debug */, + 1036DA222087CA66003253C9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5F5A53471ADE670C00F81DF0 /* Build configuration list for PBXProject "FunctionsExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5F5A536D1ADE670C00F81DF0 /* Debug */, + 5F5A536E1ADE670C00F81DF0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5F5A536F1ADE670C00F81DF0 /* Build configuration list for PBXNativeTarget "FunctionsExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5F5A53701ADE670C00F81DF0 /* Debug */, + 5F5A53711ADE670C00F81DF0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5F5A53991ADE67D500F81DF0 /* Build configuration list for PBXNativeTarget "FunctionsExampleSwift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5F5A53951ADE67D500F81DF0 /* Debug */, + 5F5A53961ADE67D500F81DF0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5F5A53441ADE670C00F81DF0 /* Project object */; +} diff --git a/invites/InvitesExample.xcodeproj/xcshareddata/xcschemes/InvitesExample.xcscheme b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/xcshareddata/xcschemes/FunctionsExample.xcscheme similarity index 63% rename from invites/InvitesExample.xcodeproj/xcshareddata/xcschemes/InvitesExample.xcscheme rename to qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/xcshareddata/xcschemes/FunctionsExample.xcscheme index 4a4a0eb8..6e1b7692 100644 --- a/invites/InvitesExample.xcodeproj/xcshareddata/xcschemes/InvitesExample.xcscheme +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/xcshareddata/xcschemes/FunctionsExample.xcscheme @@ -1,6 +1,6 @@ + BlueprintIdentifier = "5F5A534B1ADE670C00F81DF0" + BuildableName = "FunctionsExample.app" + BlueprintName = "FunctionsExample" + ReferencedContainer = "container:FunctionsExample.xcodeproj"> @@ -27,39 +27,37 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + BlueprintIdentifier = "1036D9FC2087CA10003253C9" + BuildableName = "FunctionsExampleTests.xctest" + BlueprintName = "FunctionsExampleTests" + ReferencedContainer = "container:FunctionsExample.xcodeproj"> + BlueprintIdentifier = "1036DA0A2087CA25003253C9" + BuildableName = "FunctionsExampleUITests.xctest" + BlueprintName = "FunctionsExampleUITests" + ReferencedContainer = "container:FunctionsExample.xcodeproj"> - - - - - - + BlueprintIdentifier = "5F5A534B1ADE670C00F81DF0" + BuildableName = "FunctionsExample.app" + BlueprintName = "FunctionsExample" + ReferencedContainer = "container:FunctionsExample.xcodeproj"> - - + BlueprintIdentifier = "5F5A534B1ADE670C00F81DF0" + BuildableName = "FunctionsExample.app" + BlueprintName = "FunctionsExample" + ReferencedContainer = "container:FunctionsExample.xcodeproj"> diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/xcshareddata/xcschemes/FunctionsExampleSwift.xcscheme b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/xcshareddata/xcschemes/FunctionsExampleSwift.xcscheme new file mode 100644 index 00000000..9f978cac --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample.xcodeproj/xcshareddata/xcschemes/FunctionsExampleSwift.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/.clang-format b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/.clang-format new file mode 100644 index 00000000..1f09ce0f --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/.clang-format @@ -0,0 +1,4 @@ +BasedOnStyle: Google +ColumnLimit: 100 +BinPackParameters: false +AllowAllParametersOfDeclarationOnNextLine: true diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/AppDelegate.h b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/AppDelegate.h new file mode 100644 index 00000000..f284483f --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/AppDelegate.h @@ -0,0 +1,23 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import UIKit; + +@interface AppDelegate : UIResponder + +@property(nonatomic, strong) UIWindow *window; + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/AppDelegate.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/AppDelegate.m new file mode 100644 index 00000000..ff008268 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/AppDelegate.m @@ -0,0 +1,43 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "AppDelegate.h" +#import "SignInViewController.h" + +@import FirebaseCore; +@import FirebaseAuth; +@import FirebaseAuthUI; + + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // [START firebase_configure] + // Use Firebase library to configure APIs + [FIRApp configure]; + // [END firebase_configure] + if ([FIRAuth auth].currentUser == nil) { + _window.rootViewController = [[SignInViewController alloc] init]; + } + return YES; +} + +- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { + return [[FUIAuth defaultAuthUI] handleOpenURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]]; +} + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/Base.lproj/Main.storyboard b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/Base.lproj/Main.storyboard new file mode 100644 index 00000000..2cb287d6 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/Base.lproj/Main.storyboard @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CloudAddCell.h b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CloudAddCell.h new file mode 100644 index 00000000..6ce2548b --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CloudAddCell.h @@ -0,0 +1,24 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import MaterialComponents; + +@interface CloudAddCell : MDCCollectionViewCell +@property (weak, nonatomic) IBOutlet MDCTextField *number1Field; +@property (weak, nonatomic) IBOutlet MDCTextField *number2Field; +@property (weak, nonatomic) IBOutlet MDCButton *button; +@end + diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CloudAddCell.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CloudAddCell.m new file mode 100644 index 00000000..1a58c6d8 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CloudAddCell.m @@ -0,0 +1,69 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "CloudAddCell.h" + +@import FirebaseFunctions; + +@interface CloudAddCell () +@property (weak, nonatomic) IBOutlet UITextField *resultField; +// [START define_functions_instance] +@property(strong, nonatomic) FIRFunctions *functions; +// [END define_functions_instance] +@end + +@implementation CloudAddCell + +- (instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + if (self) { + // [START initialize_functions_instance] + self.functions = [FIRFunctions functions]; + // [END initialize_functions_instance] + } + return self; +} + +- (IBAction)didTapAdd:(id)sender { + // [START function_add_numbers] + NSDictionary *data = @{@"firstNumber": [NSNumber numberWithInt:_number1Field.text.intValue], + @"secondNumber": [NSNumber numberWithInt:_number2Field.text.intValue]}; + [[_functions HTTPSCallableWithName:@"addNumbers"] callWithObject:data + completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) { + + // [START function_error] + if (error) { + if ([error.domain isEqual:@"com.firebase.functions"]) { + FIRFunctionsErrorCode code = error.code; + NSString *message = error.localizedDescription; + NSObject *details = error.userInfo[@"details"]; + } + // [START_EXCLUDE] + NSLog(@"%@", error); + return; + // [END_EXCLUDE] + } + // [END function_error] + + + NSNumber *operationResult = result.data[@"operationResult"]; + self->_resultField.text = operationResult.stringValue; + }]; + // [END function_add_numbers] +} + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CommentCell.h b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CommentCell.h new file mode 100644 index 00000000..c8cffaa4 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CommentCell.h @@ -0,0 +1,22 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import MaterialComponents; + +@interface CommentCell : MDCCollectionViewCell +@property(weak, nonatomic) IBOutlet MDCTextField *inputField; +@property(weak, nonatomic) IBOutlet MDCButton *button; +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CommentCell.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CommentCell.m new file mode 100644 index 00000000..3bfe77a8 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/CommentCell.m @@ -0,0 +1,63 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "CommentCell.h" + +@import FirebaseFunctions; + +@interface CommentCell () +@property(weak, nonatomic) IBOutlet MDCTextField *resultField; +// [START define_functions_instance] +@property(strong, nonatomic) FIRFunctions *functions; +// [END define_functions_instance] +@end + +@implementation CommentCell + +- (instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + if (self) { + // [START initialize_functions_instance] + self.functions = [FIRFunctions functions]; + // [END initialize_functions_instance] + } + return self; +} + +- (IBAction)didTapAddMessage:(id)sender { + // [START function_add_message] + [[_functions HTTPSCallableWithName:@"addMessage"] callWithObject:@{@"text": _inputField.text} + completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) { + // [START function_error] + if (error) { + if ([error.domain isEqual:@"com.firebase.functions"]) { + FIRFunctionsErrorCode code = error.code; + NSString *message = error.localizedDescription; + NSObject *details = error.userInfo[@"details"]; + } + // [START_EXCLUDE] + NSLog(@"%@", error); + return; + // [END_EXCLUDE] + } + // [END function_error] + self->_resultField.text = result.data[@"text"]; + }]; + // [END function_add_message] +} + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.h b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.h new file mode 100644 index 00000000..3aa95509 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseAuthUI; + +@interface FAuthPickerViewController : FUIAuthPickerViewController +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.m new file mode 100644 index 00000000..15c84d88 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.m @@ -0,0 +1,20 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "FAuthPickerViewController.h" + +@implementation FAuthPickerViewController +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.xib b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.xib new file mode 100644 index 00000000..0f346148 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/FAuthPickerViewController.xib @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/invites/InvitesExample/Info.plist b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/Info.plist similarity index 90% rename from invites/InvitesExample/Info.plist rename to qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/Info.plist index 69cf445e..095fbf94 100644 --- a/invites/InvitesExample/Info.plist +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/Info.plist @@ -2,12 +2,14 @@ - NSContactsUsageDescription - Used for Firebase Invites CFBundleDevelopmentRegion en CFBundleExecutable $(EXECUTABLE_NAME) + CFBundleIcons + + CFBundleIcons~ipad + CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion @@ -17,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.1 + 1.0 CFBundleSignature ???? CFBundleURLTypes @@ -26,10 +28,10 @@ CFBundleTypeRole Editor CFBundleURLName - google + Client CFBundleURLSchemes - YOUR_REVERSED_CLIENT_ID + REVERSED_CLIENT_ID diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/MainViewController.h b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/MainViewController.h new file mode 100644 index 00000000..24df7867 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/MainViewController.h @@ -0,0 +1,22 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import MaterialComponents; + +// [START signin_controller] +@interface MainViewController : MDCCollectionViewController +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/MainViewController.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/MainViewController.m new file mode 100644 index 00000000..a8cd2d7c --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/MainViewController.m @@ -0,0 +1,73 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MainViewController.h" +#import "CloudAddCell.h" +#import "CommentCell.h" +@import MaterialComponents; + +@interface MainViewController () +@property(strong, nonatomic) MDCTextInputControllerUnderline *controller1; +@property(strong, nonatomic) MDCTextInputControllerUnderline *controller2; +@property(strong, nonatomic) MDCTextInputControllerUnderline *controller3; +@end + +@implementation MainViewController +- (void)viewDidLoad { + [super viewDidLoad]; + self.styler.cellStyle = MDCCollectionViewCellStyleCard; + self.styler.cellLayoutType = MDCCollectionViewCellLayoutTypeList; +} + +- (CGFloat)collectionView:(UICollectionView *)collectionView cellHeightAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0) { + return 181; + } + return 230; +} + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { + return 2; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { + return 1; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section == 0) { + CloudAddCell *addCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"add" forIndexPath:indexPath]; + addCell.number1Field.delegate = self; + _controller1 = [[MDCTextInputControllerUnderline alloc] initWithTextInput:addCell.number1Field]; + addCell.number2Field.delegate = self; + _controller2 = [[MDCTextInputControllerUnderline alloc] initWithTextInput:addCell.number2Field]; + + + [addCell.button setElevation:MDCShadowElevationRaisedButtonResting forState:UIControlStateNormal]; + [addCell.button setElevation:MDCShadowElevationRaisedButtonPressed forState:UIControlStateHighlighted]; + return addCell; + } else { + CommentCell *commentCell = [collectionView dequeueReusableCellWithReuseIdentifier:@"message" forIndexPath:indexPath]; + commentCell.inputField.delegate = self; + _controller3 = [[MDCTextInputControllerUnderline alloc] initWithTextInput:commentCell.inputField]; + + [commentCell.button setElevation:MDCShadowElevationRaisedButtonResting forState:UIControlStateNormal]; + [commentCell.button setElevation:MDCShadowElevationRaisedButtonPressed forState:UIControlStateHighlighted]; + return commentCell; + } +} + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/SignInViewController.h b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/SignInViewController.h new file mode 100644 index 00000000..7608eb88 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/SignInViewController.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +@import FirebaseAuthUI; + +@interface SignInViewController : UIViewController +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/SignInViewController.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/SignInViewController.m new file mode 100644 index 00000000..7412a999 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/SignInViewController.m @@ -0,0 +1,78 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SignInViewController.h" +#import "AppDelegate.h" +#import "FAuthPickerViewController.h" + +@import FirebaseAuth; +@import FirebaseAuthUI; +@import FirebaseGoogleAuthUI; + +@interface SignInViewController () + +@end + +@implementation SignInViewController + +- (void)viewDidAppear:(BOOL)animated { + [super viewDidAppear:animated]; + if ([FIRAuth auth].currentUser) { + AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; + appDelegate.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" + bundle:[NSBundle mainBundle]] instantiateInitialViewController]; + [self dismissViewControllerAnimated:YES completion:nil]; + return; + } + FUIAuth *authUI = [FUIAuth defaultAuthUI]; + authUI.delegate = self; + authUI.TOSURL = [NSURL URLWithString:@"https://firebase.google.com/terms/"]; + authUI.providers = @[[[FUIGoogleAuth alloc] init]]; + UINavigationController *authViewController = authUI.authViewController; + authViewController.navigationBar.hidden = true; + [self presentViewController:authViewController animated:true completion:nil]; +} + +- (void)authUI:(FUIAuth *)authUI didSignInWithAuthDataResult:(FIRAuthDataResult *)authDataResult error:(NSError *)error { + if (error) { + if (error.code == FUIAuthErrorCodeUserCancelledSignIn) { + NSLog(@"User cancelled sign-in"); + } else { + NSError *detailedError = error.userInfo[NSUnderlyingErrorKey]; + if (!detailedError) { + detailedError = error; + } + NSLog(@"Login error: %@", detailedError.localizedDescription); + } + return; + } + [self signedIn:authDataResult.user]; +} + +- (FUIAuthPickerViewController *)authPickerViewControllerForAuthUI:(FUIAuth *)authUI { + return [[FAuthPickerViewController alloc] initWithNibName:@"FAuthPickerViewController" + bundle:[NSBundle mainBundle] + authUI:authUI]; +} + +- (void)signedIn:(FIRUser *)user { + AppDelegate *appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; + appDelegate.window.rootViewController = [[UIStoryboard storyboardWithName:@"Main" + bundle:[NSBundle mainBundle]] instantiateInitialViewController]; + [self dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/main.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/main.m new file mode 100644 index 00000000..d4878306 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExample/main.m @@ -0,0 +1,24 @@ +// +// Copyright (c) 2015 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/AppDelegate.swift b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/AppDelegate.swift new file mode 100644 index 00000000..7aaa95c4 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/AppDelegate.swift @@ -0,0 +1,63 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +import FirebaseCore +import FirebaseAuth +import FirebaseAuthUI + +@UIApplicationMain +// [START signin_delegate] +class AppDelegate: UIResponder, UIApplicationDelegate { + // [END signin_delegate] + + var window: UIWindow? + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [ + UIApplication.LaunchOptionsKey: Any + ]?) -> Bool { + // [START firebase_configure] + // Use Firebase library to configure APIs + FirebaseApp.configure() + // [END firebase_configure] + if Auth.auth().currentUser == nil { + window?.rootViewController = SignInViewController() + } + return true + } + + @available(iOS 9.0, *) + func application(_ app: UIApplication, open url: URL, + options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool { + guard let sourceApplication = + options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String else { + return false + } + return handleOpenUrl(url, sourceApplication: sourceApplication) + } + + @available(iOS 8.0, *) + func application(_ application: UIApplication, open url: URL, sourceApplication: String?, + annotation: Any) -> Bool { + return handleOpenUrl(url, sourceApplication: sourceApplication) + } + + func handleOpenUrl(_ url: URL, sourceApplication: String?) -> Bool { + return FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication) ?? false + } +} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/CloudAddCell.swift b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/CloudAddCell.swift new file mode 100644 index 00000000..98363921 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/CloudAddCell.swift @@ -0,0 +1,54 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import MaterialComponents +import FirebaseFunctions + +@objc(CloudAddCell) +class CloudAddCell: MDCCollectionViewCell { + @IBOutlet var number1Field: MDCTextField! + @IBOutlet var number2Field: MDCTextField! + @IBOutlet var button: MDCButton! + @IBOutlet private var resultField: UITextField! + // [START functions_instance] + lazy var functions = Functions.functions() + // [END functions_instance] + + @IBAction func didTapAdd(_ sender: Any) { + // [START function_add_numbers] + let data = ["firstNumber": Int(number1Field.text!), + "secondNumber": Int(number2Field.text!)] + functions.httpsCallable("addNumbers").call(data) { result, error in + // [START function_error] + if let error = error as NSError? { + if error.domain == FunctionsErrorDomain { + let code = FunctionsErrorCode(rawValue: error.code) + let message = error.localizedDescription + let details = error.userInfo[FunctionsErrorDetailsKey] + } + // [START_EXCLUDE] + print(error) + return + // [END_EXCLUDE] + } + // [END function_error] + if let operationResult = (result?.data as? [String: Any])?["operationResult"] as? Int { + self.resultField.text = "\(operationResult)" + } + } + // [END function_add_numbers] + } +} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/CommentCell.swift b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/CommentCell.swift new file mode 100644 index 00000000..40d1ab44 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/CommentCell.swift @@ -0,0 +1,51 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import MaterialComponents +import FirebaseFunctions + +@objc(CommentCell) +class CommentCell: MDCCollectionViewCell { + @IBOutlet var inputField: MDCTextField! + @IBOutlet var resultField: UITextField! + @IBOutlet var button: MDCButton! + // [START functions_instance] + lazy var functions = Functions.functions() + // [END functions_instance] + + @IBAction func didTapAddMessage(_ sender: Any) { + // [START function_add_message] + functions.httpsCallable("addMessage").call(["text": inputField.text]) { result, error in + // [START function_error] + if let error = error as NSError? { + if error.domain == FunctionsErrorDomain { + let code = FunctionsErrorCode(rawValue: error.code) + let message = error.localizedDescription + let details = error.userInfo[FunctionsErrorDetailsKey] + } + // [START_EXCLUDE] + print(error) + return + // [END_EXCLUDE] + } + // [END function_error] + if let data = result?.data as? [String: Any], let text = data["text"] as? String { + self.resultField.text = text + } + } + // [END function_add_message] + } +} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/FAuthPickerViewController.swift b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/FAuthPickerViewController.swift new file mode 100644 index 00000000..7e1ff10a --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/FAuthPickerViewController.swift @@ -0,0 +1,20 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseAuthUI + +@objc(FAuthPickerViewController) +class FAuthPickerViewController: FUIAuthPickerViewController {} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/MainViewController.swift b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/MainViewController.swift new file mode 100644 index 00000000..3af29011 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/MainViewController.swift @@ -0,0 +1,76 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import MaterialComponents + +@objc(MainViewController) +class MainViewController: MDCCollectionViewController, UITextFieldDelegate { + var controller1: MDCTextInputControllerUnderline! + var controller2: MDCTextInputControllerUnderline! + var controller3: MDCTextInputControllerUnderline! + + override func viewDidLoad() { + super.viewDidLoad() + styler.cellStyle = .card + styler.cellLayoutType = .list + } + + override func collectionView(_ collectionView: UICollectionView, + cellHeightAt indexPath: IndexPath) -> CGFloat { + if indexPath.section == 0 { + return 181 + } + return 230 + } + + override func numberOfSections(in collectionView: UICollectionView) -> Int { + return 2 + } + + override func collectionView(_ collectionView: UICollectionView, + numberOfItemsInSection section: Int) -> Int { + return 1 + } + + override func collectionView(_ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + if indexPath.section == 0 { + let addCell = collectionView.dequeueReusableCell( + withReuseIdentifier: "add", + for: indexPath + ) as! CloudAddCell + addCell.number1Field.delegate = self + controller1 = MDCTextInputControllerUnderline(textInput: addCell.number1Field) + addCell.number2Field.delegate = self + controller2 = MDCTextInputControllerUnderline(textInput: addCell.number2Field) + + addCell.button.setElevation(ShadowElevation.raisedButtonResting, for: .normal) + addCell.button.setElevation(ShadowElevation.raisedButtonPressed, for: .highlighted) + return addCell + } else { + let commentCell = collectionView.dequeueReusableCell( + withReuseIdentifier: "message", + for: indexPath + ) as! CommentCell + commentCell.inputField.delegate = self + controller3 = MDCTextInputControllerUnderline(textInput: commentCell.inputField) + + commentCell.button.setElevation(ShadowElevation.raisedButtonResting, for: .normal) + commentCell.button.setElevation(ShadowElevation.raisedButtonPressed, for: .highlighted) + return commentCell + } + } +} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/SignInViewController.swift b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/SignInViewController.swift new file mode 100644 index 00000000..af2b48e5 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwift/SignInViewController.swift @@ -0,0 +1,73 @@ +// +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseAuth +import FirebaseAuthUI +import FirebaseGoogleAuthUI + +private let kFirebaseTermsOfService = URL(string: "https://firebase.google.com/terms/")! + +@objc(SignInViewController) +class SignInViewController: UIViewController, FUIAuthDelegate { + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if Auth.auth().currentUser != nil { + let appDelegate = UIApplication.shared.delegate as? AppDelegate + appDelegate?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main) + .instantiateInitialViewController() + dismiss(animated: true, completion: nil) + return + } + let authUI = FUIAuth.defaultAuthUI() + authUI?.delegate = self + authUI?.tosurl = kFirebaseTermsOfService + authUI?.providers = [FUIGoogleAuth()] + let authViewController: UINavigationController? = authUI?.authViewController() + authViewController?.navigationBar.isHidden = true + present(authViewController!, animated: true, completion: nil) + } + + func authUI(_ authUI: FUIAuth, didSignInWith authDataResult: AuthDataResult?, error: Error?) { + switch error { + case let .some(error as NSError) + where UInt(error.code) == FUIAuthErrorCode.userCancelledSignIn.rawValue: + print("User cancelled sign-in") + case let .some(error as NSError) where error.userInfo[NSUnderlyingErrorKey] != nil: + print("Login error: \(error.userInfo[NSUnderlyingErrorKey]!)") + case let .some(error): + print("Login error: \(error.localizedDescription)") + case .none: + if let user = authDataResult?.user { + signed(in: user) + } + } + } + + func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController { + return FAuthPickerViewController( + nibName: "FAuthPickerViewController", + bundle: Bundle.main, + authUI: authUI + ) + } + + func signed(in user: User) { + let appDelegate = UIApplication.shared.delegate as? AppDelegate + appDelegate?.window?.rootViewController = UIStoryboard(name: "Main", bundle: Bundle.main) + .instantiateInitialViewController() + dismiss(animated: true, completion: nil) + } +} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwiftUITests/FunctionsExampleSwiftUITests.swift b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwiftUITests/FunctionsExampleSwiftUITests.swift new file mode 100644 index 00000000..0eb8bb77 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwiftUITests/FunctionsExampleSwiftUITests.swift @@ -0,0 +1,34 @@ +// +// FunctionsExampleSwiftUITests.swift +// FunctionsExampleSwiftUITests +// +// Created by Ibrahim Ulukaya on 4/18/18. +// Copyright © 2018 Google Inc. All rights reserved. +// + +import XCTest + +class FunctionsExampleSwiftUITests: XCTestCase { + override func setUp() { + super.setUp() + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } +} diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwiftUITests/Info.plist b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwiftUITests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleSwiftUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleTests/FunctionsExampleTests.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleTests/FunctionsExampleTests.m new file mode 100644 index 00000000..4be49cbe --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleTests/FunctionsExampleTests.m @@ -0,0 +1,47 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +@interface FunctionsExampleTests : XCTestCase + +@end + +@implementation FunctionsExampleTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleTests/Info.plist b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleTests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleUITests/FunctionsExampleUITests.m b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleUITests/FunctionsExampleUITests.m new file mode 100644 index 00000000..cd168a0b --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleUITests/FunctionsExampleUITests.m @@ -0,0 +1,104 @@ +// +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +#import "FIREGHelper.h" +#import "FIREGSignInHelper.h" + +typedef BOOL (^SystemAlertHandler)(XCUIElement*); +static SystemAlertHandler const alertHandler = ^(XCUIElement* element) { + if (element.buttons[@"Continue"].exists) { + [element.buttons[@"Continue"] tap]; + } + return YES; +}; + +static NSString *const signInButton = @"Sign in with Google"; +static NSString *const welcomeMessage = @"HTTPS Callable functions Quickstart"; + +@interface FunctionsExampleUITests : XCTestCase +@end + +@implementation FunctionsExampleUITests { + XCUIApplication *_app; + id signInPermissionMonitor; +} + +- (void)setUp { + [super setUp]; + _app = [[XCUIApplication alloc] init]; + signInPermissionMonitor = + [self addUIInterruptionMonitorWithDescription:@"Allow Google Sign-In" handler:alertHandler]; + [_app launch]; + if (![self signedIn]) { + [[_app buttons][signInButton] tap]; + doGoogleSignIn(_app, YES, YES); + // Make sure main app screen is loaded. + FIRWaitForVisible([[_app collectionViews] firstMatch]); + } +} + +- (void)tearDown { + [self removeUIInterruptionMonitor:signInPermissionMonitor]; + [super tearDown]; +} + +- (void)SKIPtestVerifyAppLaunched { + // Check that main UI elements are present on the screen. + XCTAssertTrue([[_app staticTexts][@"Add two numbers"] exists]); + XCTAssertTrue([[_app staticTexts][@"Sanitize a message"] exists]); +} + +- (void)SKIPtestAddTwoNumbers { + XCUIElement* number1 = [_app textFields][@"Num 1"]; + FIRWaitForVisible(number1); + [number1 tap]; + [number1 typeText:@"14"]; + + XCUIElement* number2 = [_app textFields][@"Num 2"]; + FIRWaitForVisible(number2); + [number2 doubleTap]; + [number2 typeText:@"51"]; + + [[_app buttons][@"Calculate"] tap]; + XCUIElement* result = [_app textFields][@"65"]; + + // Wait till network call is completed. + FIRWaitForVisible(result); + XCTAssert(result.exists); +} + +- (void)SKIPtestChangeMessage { + NSString* testText = @"hello from cloud functions!"; + XCUIElement* input = [_app textFields][@"Add your message"]; + [input tap]; + [input typeText:testText]; + + [[_app buttons][@"Add message"] tap]; + XCUIElement* result = [_app textFields][[testText uppercaseString]]; + + // Wait till network call is completed. + FIRWaitForVisible(result); + XCTAssert(result.exists); +} + +- (BOOL)signedIn { + FIRWaitForVisible(_app.buttons[signInButton]); + return !_app.buttons[signInButton].exists; +} + +@end diff --git a/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleUITests/Info.plist b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleUITests/Info.plist new file mode 100644 index 00000000..6c40a6cd --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/FunctionsExampleUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/qs-snippets/LegacyFunctionsQuickstart/Podfile b/qs-snippets/LegacyFunctionsQuickstart/Podfile new file mode 100644 index 00000000..7d9497ef --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/Podfile @@ -0,0 +1,21 @@ +# FunctionsExample + +use_frameworks! +platform :ios, '15.0' + +pod 'FirebaseAnalytics' +pod 'FirebaseAuth' +pod 'FirebaseUI/Auth', '~> 15.0' +pod 'FirebaseUI/Google', '~> 15.0' +# [START functions_pod] +pod 'FirebaseFunctions' +# [END functions_pod] + +pod 'MaterialComponents/Buttons' +pod 'MaterialComponents/Collections' +pod 'MaterialComponents/TextFields' + +target 'FunctionsExample' do +end +target 'FunctionsExampleSwift' do +end diff --git a/qs-snippets/LegacyFunctionsQuickstart/Podfile.lock b/qs-snippets/LegacyFunctionsQuickstart/Podfile.lock new file mode 100644 index 00000000..abb4a5c3 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/Podfile.lock @@ -0,0 +1,317 @@ +PODS: + - AppAuth (1.7.6): + - AppAuth/Core (= 1.7.6) + - AppAuth/ExternalUserAgent (= 1.7.6) + - AppAuth/Core (1.7.6) + - AppAuth/ExternalUserAgent (1.7.6): + - AppAuth/Core + - AppCheckCore (11.2.0): + - GoogleUtilities/Environment (~> 8.0) + - GoogleUtilities/UserDefaults (~> 8.0) + - PromisesObjC (~> 2.4) + - FirebaseAnalytics (12.6.0): + - FirebaseAnalytics/Default (= 12.6.0) + - FirebaseCore (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseAnalytics/Default (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseInstallations (~> 12.6.0) + - GoogleAppMeasurement/Default (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseAppCheckInterop (12.6.0) + - FirebaseAuth (12.6.0): + - FirebaseAppCheckInterop (~> 12.6.0) + - FirebaseAuthInterop (~> 12.6.0) + - FirebaseCore (~> 12.6.0) + - FirebaseCoreExtension (~> 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GTMSessionFetcher/Core (< 6.0, >= 3.4) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (12.6.0) + - FirebaseAuthUI (15.1.0): + - FirebaseAuth (< 13.0, >= 11.0) + - FirebaseCore + - FirebaseCore (12.6.0): + - FirebaseCoreInternal (~> 12.6.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreExtension (12.6.0): + - FirebaseCore (~> 12.6.0) + - FirebaseCoreInternal (12.6.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseFunctions (12.6.0): + - FirebaseAppCheckInterop (~> 12.6.0) + - FirebaseAuthInterop (~> 12.6.0) + - FirebaseCore (~> 12.6.0) + - FirebaseCoreExtension (~> 12.6.0) + - FirebaseMessagingInterop (~> 12.6.0) + - FirebaseSharedSwift (~> 12.6.0) + - GTMSessionFetcher/Core (< 6.0, >= 3.4) + - FirebaseGoogleAuthUI (15.1.0): + - FirebaseAuth + - FirebaseAuthUI (~> 15.0) + - FirebaseCore + - GoogleSignIn (~> 8.0) + - FirebaseInstallations (12.6.0): + - FirebaseCore (~> 12.6.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - FirebaseMessagingInterop (12.6.0) + - FirebaseSharedSwift (12.6.0) + - FirebaseUI/Auth (15.1.0): + - FirebaseAuthUI (~> 15.0) + - FirebaseUI/Google (15.1.0): + - FirebaseGoogleAuthUI (~> 15.0) + - GoogleAdsOnDeviceConversion (3.2.0): + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Core (12.6.0): + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Default (12.6.0): + - GoogleAdsOnDeviceConversion (~> 3.2.0) + - GoogleAppMeasurement/Core (= 12.6.0) + - GoogleAppMeasurement/IdentitySupport (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/IdentitySupport (12.6.0): + - GoogleAppMeasurement/Core (= 12.6.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleSignIn (8.0.0): + - AppAuth (< 2.0, >= 1.7.3) + - AppCheckCore (~> 11.0) + - GTMAppAuth (< 5.0, >= 4.1.1) + - GTMSessionFetcher/Core (~> 3.3) + - GoogleUtilities/AppDelegateSwizzler (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.1.0): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.1.0)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - GTMAppAuth (4.1.1): + - AppAuth/Core (~> 1.7) + - GTMSessionFetcher/Core (< 4.0, >= 3.3) + - GTMSessionFetcher/Core (3.5.0) + - MaterialComponents/AnimationTiming (124.2.0) + - MaterialComponents/Availability (124.2.0) + - MaterialComponents/Buttons (124.2.0): + - MaterialComponents/Elevation + - MaterialComponents/Ink + - MaterialComponents/private/Math + - MaterialComponents/Ripple + - MaterialComponents/Shadow + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer + - MaterialComponents/ShapeLibrary + - MaterialComponents/Shapes + - MaterialComponents/Typography + - MDFInternationalization + - MDFTextAccessibility + - MaterialComponents/CollectionCells (124.2.0): + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/Palettes + - MaterialComponents/private/Icons/ic_check + - MaterialComponents/private/Icons/ic_check_circle + - MaterialComponents/private/Icons/ic_chevron_right + - MaterialComponents/private/Icons/ic_info + - MaterialComponents/private/Icons/ic_radio_button_unchecked + - MaterialComponents/private/Icons/ic_reorder + - MaterialComponents/private/Math + - MaterialComponents/Ripple + - MaterialComponents/Typography + - MDFInternationalization + - MaterialComponents/CollectionLayoutAttributes (124.2.0) + - MaterialComponents/Collections (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/CollectionCells + - MaterialComponents/CollectionLayoutAttributes + - MaterialComponents/Ink + - MaterialComponents/Palettes + - MaterialComponents/Ripple + - MaterialComponents/ShadowElevations + - MaterialComponents/ShadowLayer + - MaterialComponents/Typography + - MaterialComponents/Elevation (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/Ink (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/Palettes (124.2.0) + - MaterialComponents/private/Application (124.2.0) + - MaterialComponents/private/Color (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/private/Icons/Base (124.2.0) + - MaterialComponents/private/Icons/ic_check (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_check_circle (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_chevron_right (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_info (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_radio_button_unchecked (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Icons/ic_reorder (124.2.0): + - MaterialComponents/private/Icons/Base + - MaterialComponents/private/Math (124.2.0) + - MaterialComponents/Ripple (124.2.0): + - MaterialComponents/AnimationTiming + - MaterialComponents/Availability + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/Shadow (124.2.0): + - MaterialComponents/Availability + - MaterialComponents/ShadowElevations (124.2.0) + - MaterialComponents/ShadowLayer (124.2.0): + - MaterialComponents/ShadowElevations + - MaterialComponents/ShapeLibrary (124.2.0): + - MaterialComponents/private/Math + - MaterialComponents/Shapes + - MaterialComponents/Shapes (124.2.0): + - MaterialComponents/private/Color + - MaterialComponents/private/Math + - MaterialComponents/ShadowLayer + - MaterialComponents/TextFields (124.2.0): + - MaterialComponents/AnimationTiming + - MaterialComponents/Buttons + - MaterialComponents/Elevation + - MaterialComponents/Palettes + - MaterialComponents/private/Math + - MaterialComponents/Typography + - MDFInternationalization + - MaterialComponents/Typography (124.2.0): + - MaterialComponents/private/Application + - MaterialComponents/private/Math + - MDFTextAccessibility + - MDFInternationalization (3.0.0) + - MDFTextAccessibility (2.0.1) + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - PromisesObjC (2.4.0) + - RecaptchaInterop (101.0.0) + +DEPENDENCIES: + - FirebaseAnalytics + - FirebaseAuth + - FirebaseFunctions + - FirebaseUI/Auth (~> 15.0) + - FirebaseUI/Google (~> 15.0) + - MaterialComponents/Buttons + - MaterialComponents/Collections + - MaterialComponents/TextFields + +SPEC REPOS: + trunk: + - AppAuth + - AppCheckCore + - FirebaseAnalytics + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseAuthUI + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseFunctions + - FirebaseGoogleAuthUI + - FirebaseInstallations + - FirebaseMessagingInterop + - FirebaseSharedSwift + - FirebaseUI + - GoogleAdsOnDeviceConversion + - GoogleAppMeasurement + - GoogleSignIn + - GoogleUtilities + - GTMAppAuth + - GTMSessionFetcher + - MaterialComponents + - MDFInternationalization + - MDFTextAccessibility + - nanopb + - PromisesObjC + - RecaptchaInterop + +SPEC CHECKSUMS: + AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 + AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f + FirebaseAnalytics: d0a97a0db6425e5a5d966340b87f92ca7b13a557 + FirebaseAppCheckInterop: e2178171b4145013c7c1a3cc464d1d446d3a1896 + FirebaseAuth: 613c463cb43545a7fd2cd99ade09b78ac472c544 + FirebaseAuthInterop: db06756ef028006d034b6004dc0c37c24f7828d4 + FirebaseAuthUI: c574e8904bd14503ff47e55ba2fc9ec64aacaed6 + FirebaseCore: 0e38ad5d62d980a47a64b8e9301ffa311457be04 + FirebaseCoreExtension: 032fd6f8509e591fda8cb76f6651f20d926b121f + FirebaseCoreInternal: 69bf1306a05b8ac43004f6cc1f804bb7b05b229e + FirebaseFunctions: c049fe05d03045fdd2b1ecd014c1345a841fc601 + FirebaseGoogleAuthUI: 740a15519a36b4d15802c4ef1db4aaeb7f7bad0b + FirebaseInstallations: 631b38da2e11a83daa4bfb482f79d286a5dfa7ad + FirebaseMessagingInterop: b18f87820d64e2fffe8e00a9d5f5c2efd430b554 + FirebaseSharedSwift: 79f27fff0addd15c3de19b87fba426f3cc2c964f + FirebaseUI: 1dfcf45d4bb4073380091394c9c555d697ecca11 + GoogleAdsOnDeviceConversion: d68c69dd9581a0f5da02617b6f377e5be483970f + GoogleAppMeasurement: 3bf40aff49a601af5da1c3345702fcb4991d35ee + GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4 + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 + GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de + GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 + MaterialComponents: 1a9b2d9d45b1601ae544de85089adc4c464306d4 + MDFInternationalization: d697c55307816222a55685c4ccb1044ffb030c12 + MDFTextAccessibility: f4bb4cc2194286651b59a215fdeaa0e05dc90ba5 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba + +PODFILE CHECKSUM: 56b139ff3f9ff3b9dfe39b30951b6aad99eb689e + +COCOAPODS: 1.16.2 diff --git a/qs-snippets/LegacyFunctionsQuickstart/README.md b/qs-snippets/LegacyFunctionsQuickstart/README.md new file mode 100644 index 00000000..b2f33f89 --- /dev/null +++ b/qs-snippets/LegacyFunctionsQuickstart/README.md @@ -0,0 +1,45 @@ +Firebase Functions Quickstart +============================= + +Introduction +------------ + +This quickstart demonstrates **Callable Functions** which are HTTPS Cloud Functions +that can be invoked directly from your mobile application. + +- [Read more about callable functions](https://firebase.google.com/docs/functions/callable) + +Getting Started +--------------- + +- [Add Firebase to your iOS Project](https://firebase.google.com/docs/ios/setup). + +### Google Sign In Setup +- In Xcode, [add a custom URL scheme for your reversed client ID](https://developers.google.com/identity/sign-in/ios/start-integrating). +- You can find this in the `GoogleService-Info.plist` + +Support +------- + +- [Stack Overflow](https://stackoverflow.com/questions/tagged/google-cloud-functions) +- [Firebase Support](https://firebase.google.com/support/) + +License +------- + +Copyright 2018 Google, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/qs-snippets/QSSnippets.xcodeproj/project.pbxproj b/qs-snippets/QSSnippets.xcodeproj/project.pbxproj new file mode 100644 index 00000000..91fc7c5b --- /dev/null +++ b/qs-snippets/QSSnippets.xcodeproj/project.pbxproj @@ -0,0 +1,715 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 8D7499372ECBD910008D3C7A /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7499362ECBD910008D3C7A /* FirebaseAnalytics */; }; + 8D7499392ECBD910008D3C7A /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7499382ECBD910008D3C7A /* FirebaseAuth */; }; + 8D74993B2ECBD910008D3C7A /* FirebaseCore in Frameworks */ = {isa = PBXBuildFile; productRef = 8D74993A2ECBD910008D3C7A /* FirebaseCore */; }; + 8D7499412ECBDFB2008D3C7A /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7499402ECBDFB2008D3C7A /* GoogleSignIn */; }; + 8D7499432ECBDFB2008D3C7A /* GoogleSignInSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7499422ECBDFB2008D3C7A /* GoogleSignInSwift */; }; + 8D7499462ECBE4FF008D3C7A /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7499452ECBE4FF008D3C7A /* FacebookLogin */; }; + 8D74994C2ECBED88008D3C7A /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 8D74994B2ECBED88008D3C7A /* FirebaseRemoteConfig */; }; + 8D74994E2ECD1BF0008D3C7A /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 8D74994D2ECD1BF0008D3C7A /* FirebaseCrashlytics */; }; + 8D7499502ECD1C54008D3C7A /* FirebaseDatabase in Frameworks */ = {isa = PBXBuildFile; productRef = 8D74994F2ECD1C54008D3C7A /* FirebaseDatabase */; }; + 8D7499522ECD280E008D3C7A /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7499512ECD280E008D3C7A /* FirebaseMessaging */; }; + 8D7499542ECD31CF008D3C7A /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7499532ECD31CF008D3C7A /* FirebaseStorage */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8D7499092ECBD7F4008D3C7A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D7498F32ECBD7F3008D3C7A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D7498FA2ECBD7F3008D3C7A; + remoteInfo = QSSnippets; + }; + 8D7499132ECBD7F4008D3C7A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D7498F32ECBD7F3008D3C7A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D7498FA2ECBD7F3008D3C7A; + remoteInfo = QSSnippets; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 8D7498FB2ECBD7F3008D3C7A /* QSSnippets.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QSSnippets.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D7499082ECBD7F4008D3C7A /* QSSnippetsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QSSnippetsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D7499122ECBD7F4008D3C7A /* QSSnippetsUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = QSSnippetsUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 8D7498FD2ECBD7F3008D3C7A /* QSSnippets */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = QSSnippets; + sourceTree = ""; + }; + 8D74990B2ECBD7F4008D3C7A /* QSSnippetsTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = QSSnippetsTests; + sourceTree = ""; + }; + 8D7499152ECBD7F4008D3C7A /* QSSnippetsUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = QSSnippetsUITests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D7498F82ECBD7F3008D3C7A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7499432ECBDFB2008D3C7A /* GoogleSignInSwift in Frameworks */, + 8D7499372ECBD910008D3C7A /* FirebaseAnalytics in Frameworks */, + 8D7499522ECD280E008D3C7A /* FirebaseMessaging in Frameworks */, + 8D7499462ECBE4FF008D3C7A /* FacebookLogin in Frameworks */, + 8D74994C2ECBED88008D3C7A /* FirebaseRemoteConfig in Frameworks */, + 8D7499412ECBDFB2008D3C7A /* GoogleSignIn in Frameworks */, + 8D74994E2ECD1BF0008D3C7A /* FirebaseCrashlytics in Frameworks */, + 8D7499502ECD1C54008D3C7A /* FirebaseDatabase in Frameworks */, + 8D7499542ECD31CF008D3C7A /* FirebaseStorage in Frameworks */, + 8D74993B2ECBD910008D3C7A /* FirebaseCore in Frameworks */, + 8D7499392ECBD910008D3C7A /* FirebaseAuth in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D7499052ECBD7F4008D3C7A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D74990F2ECBD7F4008D3C7A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D7498F22ECBD7F3008D3C7A = { + isa = PBXGroup; + children = ( + 8D7498FD2ECBD7F3008D3C7A /* QSSnippets */, + 8D74990B2ECBD7F4008D3C7A /* QSSnippetsTests */, + 8D7499152ECBD7F4008D3C7A /* QSSnippetsUITests */, + 8D74994A2ECBED88008D3C7A /* Frameworks */, + 8D7498FC2ECBD7F3008D3C7A /* Products */, + ); + sourceTree = ""; + }; + 8D7498FC2ECBD7F3008D3C7A /* Products */ = { + isa = PBXGroup; + children = ( + 8D7498FB2ECBD7F3008D3C7A /* QSSnippets.app */, + 8D7499082ECBD7F4008D3C7A /* QSSnippetsTests.xctest */, + 8D7499122ECBD7F4008D3C7A /* QSSnippetsUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 8D74994A2ECBED88008D3C7A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D7498FA2ECBD7F3008D3C7A /* QSSnippets */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D74991C2ECBD7F4008D3C7A /* Build configuration list for PBXNativeTarget "QSSnippets" */; + buildPhases = ( + 8D7498F72ECBD7F3008D3C7A /* Sources */, + 8D7498F82ECBD7F3008D3C7A /* Frameworks */, + 8D7498F92ECBD7F3008D3C7A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 8D7498FD2ECBD7F3008D3C7A /* QSSnippets */, + ); + name = QSSnippets; + packageProductDependencies = ( + 8D7499362ECBD910008D3C7A /* FirebaseAnalytics */, + 8D7499382ECBD910008D3C7A /* FirebaseAuth */, + 8D74993A2ECBD910008D3C7A /* FirebaseCore */, + 8D7499402ECBDFB2008D3C7A /* GoogleSignIn */, + 8D7499422ECBDFB2008D3C7A /* GoogleSignInSwift */, + 8D7499452ECBE4FF008D3C7A /* FacebookLogin */, + 8D74994B2ECBED88008D3C7A /* FirebaseRemoteConfig */, + 8D74994D2ECD1BF0008D3C7A /* FirebaseCrashlytics */, + 8D74994F2ECD1C54008D3C7A /* FirebaseDatabase */, + 8D7499512ECD280E008D3C7A /* FirebaseMessaging */, + 8D7499532ECD31CF008D3C7A /* FirebaseStorage */, + ); + productName = QSSnippets; + productReference = 8D7498FB2ECBD7F3008D3C7A /* QSSnippets.app */; + productType = "com.apple.product-type.application"; + }; + 8D7499072ECBD7F4008D3C7A /* QSSnippetsTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D74991F2ECBD7F4008D3C7A /* Build configuration list for PBXNativeTarget "QSSnippetsTests" */; + buildPhases = ( + 8D7499042ECBD7F4008D3C7A /* Sources */, + 8D7499052ECBD7F4008D3C7A /* Frameworks */, + 8D7499062ECBD7F4008D3C7A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D74990A2ECBD7F4008D3C7A /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 8D74990B2ECBD7F4008D3C7A /* QSSnippetsTests */, + ); + name = QSSnippetsTests; + packageProductDependencies = ( + ); + productName = QSSnippetsTests; + productReference = 8D7499082ECBD7F4008D3C7A /* QSSnippetsTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 8D7499112ECBD7F4008D3C7A /* QSSnippetsUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D7499222ECBD7F4008D3C7A /* Build configuration list for PBXNativeTarget "QSSnippetsUITests" */; + buildPhases = ( + 8D74990E2ECBD7F4008D3C7A /* Sources */, + 8D74990F2ECBD7F4008D3C7A /* Frameworks */, + 8D7499102ECBD7F4008D3C7A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 8D7499142ECBD7F4008D3C7A /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 8D7499152ECBD7F4008D3C7A /* QSSnippetsUITests */, + ); + name = QSSnippetsUITests; + packageProductDependencies = ( + ); + productName = QSSnippetsUITests; + productReference = 8D7499122ECBD7F4008D3C7A /* QSSnippetsUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8D7498F32ECBD7F3008D3C7A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2610; + LastUpgradeCheck = 2610; + TargetAttributes = { + 8D7498FA2ECBD7F3008D3C7A = { + CreatedOnToolsVersion = 26.1; + LastSwiftMigration = 2610; + }; + 8D7499072ECBD7F4008D3C7A = { + CreatedOnToolsVersion = 26.1; + TestTargetID = 8D7498FA2ECBD7F3008D3C7A; + }; + 8D7499112ECBD7F4008D3C7A = { + CreatedOnToolsVersion = 26.1; + TestTargetID = 8D7498FA2ECBD7F3008D3C7A; + }; + }; + }; + buildConfigurationList = 8D7498F62ECBD7F3008D3C7A /* Build configuration list for PBXProject "QSSnippets" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8D7498F22ECBD7F3008D3C7A; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 8D74993F2ECBDFB2008D3C7A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, + 8D7499442ECBE4FF008D3C7A /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 8D7498FC2ECBD7F3008D3C7A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D7498FA2ECBD7F3008D3C7A /* QSSnippets */, + 8D7499072ECBD7F4008D3C7A /* QSSnippetsTests */, + 8D7499112ECBD7F4008D3C7A /* QSSnippetsUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D7498F92ECBD7F3008D3C7A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D7499062ECBD7F4008D3C7A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D7499102ECBD7F4008D3C7A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D7498F72ECBD7F3008D3C7A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D7499042ECBD7F4008D3C7A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8D74990E2ECBD7F4008D3C7A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 8D74990A2ECBD7F4008D3C7A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D7498FA2ECBD7F3008D3C7A /* QSSnippets */; + targetProxy = 8D7499092ECBD7F4008D3C7A /* PBXContainerItemProxy */; + }; + 8D7499142ECBD7F4008D3C7A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D7498FA2ECBD7F3008D3C7A /* QSSnippets */; + targetProxy = 8D7499132ECBD7F4008D3C7A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 8D74991A2ECBD7F4008D3C7A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8D74991B2ECBD7F4008D3C7A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8D74991D2ECBD7F4008D3C7A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.QSSnippets; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "QSSnippets/QSSnippets-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8D74991E2ECBD7F4008D3C7A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.QSSnippets; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OBJC_BRIDGING_HEADER = "QSSnippets/QSSnippets-Bridging-Header.h"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 8D7499202ECBD7F4008D3C7A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.QSSnippetsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/QSSnippets.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/QSSnippets"; + }; + name = Debug; + }; + 8D7499212ECBD7F4008D3C7A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.1; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.QSSnippetsTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/QSSnippets.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/QSSnippets"; + }; + name = Release; + }; + 8D7499232ECBD7F4008D3C7A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.QSSnippetsUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = QSSnippets; + }; + name = Debug; + }; + 8D7499242ECBD7F4008D3C7A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.QSSnippetsUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = QSSnippets; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8D7498F62ECBD7F3008D3C7A /* Build configuration list for PBXProject "QSSnippets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D74991A2ECBD7F4008D3C7A /* Debug */, + 8D74991B2ECBD7F4008D3C7A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D74991C2ECBD7F4008D3C7A /* Build configuration list for PBXNativeTarget "QSSnippets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D74991D2ECBD7F4008D3C7A /* Debug */, + 8D74991E2ECBD7F4008D3C7A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D74991F2ECBD7F4008D3C7A /* Build configuration list for PBXNativeTarget "QSSnippetsTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D7499202ECBD7F4008D3C7A /* Debug */, + 8D7499212ECBD7F4008D3C7A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D7499222ECBD7F4008D3C7A /* Build configuration list for PBXNativeTarget "QSSnippetsUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D7499232ECBD7F4008D3C7A /* Debug */, + 8D7499242ECBD7F4008D3C7A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 12.6.0; + }; + }; + 8D74993F2ECBDFB2008D3C7A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/google/GoogleSignIn-iOS.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 9.0.0; + }; + }; + 8D7499442ECBE4FF008D3C7A /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/facebook/facebook-ios-sdk.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 14.1.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7499362ECBD910008D3C7A /* FirebaseAnalytics */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAnalytics; + }; + 8D7499382ECBD910008D3C7A /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 8D74993A2ECBD910008D3C7A /* FirebaseCore */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCore; + }; + 8D7499402ECBDFB2008D3C7A /* GoogleSignIn */ = { + isa = XCSwiftPackageProductDependency; + package = 8D74993F2ECBDFB2008D3C7A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; + productName = GoogleSignIn; + }; + 8D7499422ECBDFB2008D3C7A /* GoogleSignInSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 8D74993F2ECBDFB2008D3C7A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; + productName = GoogleSignInSwift; + }; + 8D7499452ECBE4FF008D3C7A /* FacebookLogin */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499442ECBE4FF008D3C7A /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */; + productName = FacebookLogin; + }; + 8D74994B2ECBED88008D3C7A /* FirebaseRemoteConfig */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseRemoteConfig; + }; + 8D74994D2ECD1BF0008D3C7A /* FirebaseCrashlytics */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseCrashlytics; + }; + 8D74994F2ECD1C54008D3C7A /* FirebaseDatabase */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseDatabase; + }; + 8D7499512ECD280E008D3C7A /* FirebaseMessaging */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseMessaging; + }; + 8D7499532ECD31CF008D3C7A /* FirebaseStorage */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7499352ECBD910008D3C7A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseStorage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 8D7498F32ECBD7F3008D3C7A /* Project object */; +} diff --git a/qs-snippets/QSSnippets/Assets.xcassets/AccentColor.colorset/Contents.json b/qs-snippets/QSSnippets/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/qs-snippets/QSSnippets/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/qs-snippets/QSSnippets/Assets.xcassets/AppIcon.appiconset/Contents.json b/qs-snippets/QSSnippets/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/qs-snippets/QSSnippets/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/qs-snippets/QSSnippets/Assets.xcassets/Contents.json b/qs-snippets/QSSnippets/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/qs-snippets/QSSnippets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/qs-snippets/QSSnippets/ContentView.swift b/qs-snippets/QSSnippets/ContentView.swift new file mode 100644 index 00000000..0c8174d7 --- /dev/null +++ b/qs-snippets/QSSnippets/ContentView.swift @@ -0,0 +1,33 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, world!") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/qs-snippets/QSSnippets/MigratedSnippets.swift b/qs-snippets/QSSnippets/MigratedSnippets.swift new file mode 100644 index 00000000..e280c9a2 --- /dev/null +++ b/qs-snippets/QSSnippets/MigratedSnippets.swift @@ -0,0 +1,462 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import FirebaseAnalytics +import FirebaseCore +import FirebaseCrashlytics +import FirebaseRemoteConfig +import FirebaseMessaging +import FirebaseStorage + +import AuthenticationServices +import CryptoKit + +// [START auth_import] +import FirebaseAuth +// [END auth_import] + +// [START google_import] +import GoogleSignIn +// [END google_import] + +class DummySceneDelegate: NSObject, UISceneDelegate { + // [START application_open] + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + // ... + } + // [END application_open] +} + +class MigratedSnippets { + + func configureFirebase() { + // [START firebase_configure] + FirebaseApp.configure() + // [END firebase_configure] + } + + func setUserProperty() { + let food = "" + // [START user_property] + Analytics.setUserProperty(food, forName: "favorite_food") + // [END user_property] + } + + func logCustomEvent() { + let name = "" + let text = "" + // [START custom_event_swift] + Analytics.logEvent("share_image", parameters: [ + "name": name, + "full_text": text, + ]) + // [END custom_event_swift] + } + + func logSelectContentEvent() { + let title = "" + // formerly custom_event_swift + // [START log_event_swift] + Analytics.logEvent(AnalyticsEventSelectContent, parameters: [ + AnalyticsParameterItemID: "id-\(title)", + AnalyticsParameterItemName: title, + AnalyticsParameterContentType: "cont", + ]) + // [END log_event_swift] + } + + func recordScreenView() { + let screenName = "" + let screenClass = "" + + // [START set_current_screen] + Analytics.logEvent(AnalyticsEventScreenView, + parameters: [AnalyticsParameterScreenName: screenName, + AnalyticsParameterScreenClass: screenClass]) + // [END set_current_screen] + } + + private func performGoogleSignInFlow() { + let viewController = UIViewController() + + // [START headless_google_auth] + guard let clientID = FirebaseApp.app()?.options.clientID else { return } + + // Create Google Sign In configuration object. + // [START_EXCLUDE silent] + // TODO: Move configuration to Info.plist + // [END_EXCLUDE] + let config = GIDConfiguration(clientID: clientID) + GIDSignIn.sharedInstance.configuration = config + + // Start the sign in flow! + GIDSignIn.sharedInstance.signIn(withPresenting: viewController) { result, error in + guard error == nil else { + // ... + return + } + + guard let user = result?.user, + let idToken = user.idToken?.tokenString + else { + // ... + return + } + + let credential = GoogleAuthProvider.credential(withIDToken: idToken, + accessToken: user.accessToken.tokenString) + self.signIn(with: credential) + } + // [END headless_google_auth] + } + + func signIn(with credential: AuthCredential) { + // [START signin_google_credential] + Auth.auth().signIn(with: credential) { result, error in + guard error == nil else { + // ... + return + } + + // At this point, our user is signed in + } + // [END signin_google_credential] + } + + func addConfigUpdateListener() { + // [START add_config_update_listener] + RemoteConfig.remoteConfig().addOnConfigUpdateListener { configUpdate, error in + guard error == nil else { return } + print("Updated keys: \(configUpdate!.updatedKeys)") + + RemoteConfig.remoteConfig().activate { changed, error in + guard error == nil else { return } + // ... + } + } + // [END add_config_update_listener] + } + + + +} + +class SnippetsViewController: UIViewController, + ASAuthorizationControllerDelegate, + ASAuthorizationControllerPresentationContextProviding { + + func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + return self.view.window! + } + + // [START token_revocation_deleteuser] + private var currentNonce: String? + + private func deleteCurrentUser() { + do { + let nonce = try CryptoUtils.randomNonceString() + currentNonce = nonce + let appleIDProvider = ASAuthorizationAppleIDProvider() + let request = appleIDProvider.createRequest() + request.requestedScopes = [.fullName, .email] + request.nonce = CryptoUtils.sha256(nonce) + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + authorizationController.delegate = self + authorizationController.presentationContextProvider = self + authorizationController.performRequests() + } catch { + // In the unlikely case that nonce generation fails, show error view. + displayError(error) + } + } + // [END token_revocation_deleteuser] + + // [START token_revocation] + private var user: User? + + func authorizationController(controller: ASAuthorizationController, + didCompleteWithAuthorization authorization: ASAuthorization) { + guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential + else { + print("Unable to retrieve AppleIDCredential") + return + } + + guard let _ = currentNonce else { + fatalError("Invalid state: A login callback was received, but no login request was sent.") + } + + guard let appleAuthCode = appleIDCredential.authorizationCode else { + print("Unable to fetch authorization code") + return + } + + guard let authCodeString = String(data: appleAuthCode, encoding: .utf8) else { + print("Unable to serialize auth code string from data: \(appleAuthCode.debugDescription)") + return + } + + Task { + do { + try await Auth.auth().revokeToken(withAuthorizationCode: authCodeString) + try await user?.delete() + self.updateUI() + } catch { + self.displayError(error) + } + } + } + // [END token_revocation] + + func displayError(_ error: any Error) { + // do nothing + } + + func updateUI() {} + + func getRemoteConfig() { + // [START get_remote_config_instance] + let remoteConfig = RemoteConfig.remoteConfig() + // [END get_remote_config_instance] + print(remoteConfig) + } + + func enableDeveloperMode() { + // [START enable_dev_mode] + let settings = RemoteConfigSettings() + settings.minimumFetchInterval = 0 + RemoteConfig.remoteConfig().configSettings = settings + // [END enable_dev_mode] + } + + func setDefaultValues() { + // [START set_default_values] + RemoteConfig.remoteConfig().setDefaults(fromPlist: "RemoteConfigDefaults") + // [END set_default_values] + } + + func fetchConfigWithCallback() { + let remoteConfig = RemoteConfig.remoteConfig() + // [START fetch_config_with_callback] + remoteConfig.fetch { (status, error) -> Void in + if status == .success { + print("Config fetched!") + remoteConfig.activate { changed, error in + // ... + } + } else { + print("Config not fetched") + print("Error: \(error?.localizedDescription ?? "No error available.")") + } + } + // [END fetch_config_with_callback] + } + + func getConfigValue() { + let remoteConfig = RemoteConfig.remoteConfig() + // [START get_config_value] + let welcomeMessage = remoteConfig["welcome_message"].stringValue + // [END get_config_value] + print(welcomeMessage) + } + + func logAndCrash() { + // [START log_and_crash_swift] + Crashlytics.crashlytics().log("Cause Crash button clicked") + fatalError() + // [END log_and_crash_swift] + } + + func downloadFile() { + let storageRef = Storage.storage().reference() + + let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) + let documentsDirectory = paths[0] + let filePath = "file:\(documentsDirectory)/myimage.jpg" + guard let fileURL = URL(string: filePath) else { return } + guard let storagePath = UserDefaults.standard.object(forKey: "storagePath") as? String else { + return + } + // [START downloadimage] + storageRef.child(storagePath).write(toFile: fileURL) { result in + switch result { + case let .success(url): + print(UIImage(contentsOfFile: url.path) ?? "(invalid image)") + case let .failure(error): + print("Error downloading:\(error)") + } + } + // [END downloadimage] + } + + func configureStorage() { + // [START configurestorage] + let storage = Storage.storage() + // [END configurestorage] + print(storage) + } + + func storageAuth() { + // [START storageauth] + // Using Cloud Storage for Firebase requires the user be authenticated. Here we are using + // anonymous authentication. + if Auth.auth().currentUser == nil { + Auth.auth().signInAnonymously(completion: { authResult, error in + if let error = error { + print("Error signing in: \(error)") + } + }) + } + // [END storageauth] + } + + func uploadFile() { + let filePath = "example" + guard let imagePath = Bundle.main.url(forResource: "sample", withExtension: "jpg") else { + return + } + // [START uploadimage] + let storageRef = Storage.storage().reference(withPath: filePath) + storageRef.putFile(from: imagePath) { result in + switch result { + case .success: + print("Upload succeeded") + case let .failure(error): + print("Error uploading: \(error)") + } + } + // [END uploadimage] + } + +} + +class MessagingAppDelegate: NSObject, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate { + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication + .LaunchOptionsKey: Any]?) -> Bool { + FirebaseApp.configure() + + // [START set_messaging_delegate] + Messaging.messaging().delegate = self + // [END set_messaging_delegate] + + // Register for remote notifications. This shows a permission dialog on first run, to + // show the dialog at a more appropriate time move this registration accordingly. + // [START register_for_notifications] + + UNUserNotificationCenter.current().delegate = self + + let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] + UNUserNotificationCenter.current().requestAuthorization( + options: authOptions, + completionHandler: { _, _ in } + ) + + application.registerForRemoteNotifications() + + // [END register_for_notifications] + + return true + } + + // [START receive_message] + @MainActor + func application(_ application: UIApplication, + didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async + -> UIBackgroundFetchResult { + // If you are receiving a notification message while your app is in the background, + // this callback will not be fired till the user taps on the notification launching the application. + // TODO: Handle data of notification + + // With swizzling disabled you must let Messaging know about the message, for Analytics + // Messaging.messaging().appDidReceiveMessage(userInfo) + + // ... + + // Print full message. + print(userInfo) + print("Call exportDeliveryMetricsToBigQuery() from AppDelegate") + Messaging.serviceExtension().exportDeliveryMetricsToBigQuery(withMessageInfo: userInfo) + return UIBackgroundFetchResult.newData + } + // [END receive_message] + + // [START ios_10_message_handling] + // Receive displayed notifications for iOS 10+ devices. + func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification) async + -> UNNotificationPresentationOptions { + let userInfo = notification.request.content.userInfo + + // With swizzling disabled you must let Messaging know about the message, for Analytics + // Messaging.messaging().appDidReceiveMessage(userInfo) + + // ... + + // Print full message. + print(userInfo) + + // Change this to your preferred presentation option + // Note: UNNotificationPresentationOptions.alert has been deprecated. + return [.list, .banner, .sound] + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse) async { + let userInfo = response.notification.request.content.userInfo + + // ... + + // Print full message. + print(userInfo) + } + // [END ios_10_message_handling] + + // [START refresh_token] + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + print("Firebase registration token: \(String(describing: fcmToken))") + // TODO: If necessary send token to application server. + // Note: This callback is fired at each app startup and whenever a new token is generated. + } + // [END refresh_token] + + func logFCMToken() { + // [START log_fcm_reg_token] + let token = Messaging.messaging().fcmToken + print("FCM token: \(token ?? "")") + // [END log_fcm_reg_token] + + // [START log_iid_reg_token] + Messaging.messaging().token { token, error in + if let error = error { + print("Error fetching remote FCM registration token: \(error)") + } else if let token = token { + print("Remote instance ID token: \(token)") + } + } + // [END log_iid_reg_token] + } + + func subscribeToTopic() { + // [START subscribe_topic] + Messaging.messaging().subscribe(toTopic: "weather") { error in + print("Subscribed to weather topic") + } + // [END subscribe_topic] + } +} diff --git a/qs-snippets/QSSnippets/ObjCMigratedSnippets.m b/qs-snippets/QSSnippets/ObjCMigratedSnippets.m new file mode 100644 index 00000000..157a55ee --- /dev/null +++ b/qs-snippets/QSSnippets/ObjCMigratedSnippets.m @@ -0,0 +1,356 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import + +#import +#import + +@import FirebaseRemoteConfig; +@import FirebaseCrashlytics; +@import FirebaseDatabase; +@import FirebaseMessaging; +@import FirebaseStorage; + +@import UserNotifications; + +// Redundant snippets: auth_view_import (unused) +// [START auth_import] +@import FirebaseAuth; +// [END auth_import] + +// [START google_import] +@import GoogleSignIn; +// [END google_import] + +@import FBSDKLoginKit; + +@interface ObjcSnippets : NSObject + +@end + +@implementation ObjcSnippets + +- (void)configureFirebase { + // Redundant snippets: + // - firebase_configure (AppDelegate_m_firebase_configure) + // - configure_firebase + // [START firebase_configure] + [FIRApp configure]; + // [END firebase_configure] +} + +- (void)setUserProperty { + NSString *food = @""; + // [START user_property] + [FIRAnalytics setUserPropertyString:food forName:@"favorite_food"]; + // [END user_property] +} + +- (void)logCustomEvent { + NSString *name = @""; + NSString *text = @""; + // [START custom_event_objc] + [FIRAnalytics logEventWithName:@"share_image" + parameters:@{ + @"name": name, + @"full_text": text + }]; + // [END custom_event_objc] +} + +- (void)logPredefinedEvent { + NSString *title = @""; + // formerly custom_event_objc + // [START log_event_objc] + [FIRAnalytics logEventWithName:kFIREventSelectContent + parameters:@{ + kFIRParameterItemID: [NSString stringWithFormat:@"id-%@", title], + kFIRParameterItemName: title, + kFIRParameterContentType: @"image" + }]; + // [END log_event_objc] +} + +- (void)setCurrentScreen { + // These strings must be <= 36 characters long in order for setScreenName:screenClass: to succeed. + NSString *screenName = @""; + NSString *screenClass = NSStringFromClass([self class]); + + // [START set_current_screen] + [FIRAnalytics logEventWithName:kFIREventScreenView + parameters:@{kFIRParameterScreenClass: screenClass, + kFIRParameterScreenName: screenName}]; + // [END set_current_screen] +} + +- (void)getRemoteConfig { + // [START get_remote_config_instance] + FIRRemoteConfig *remoteConfig = [FIRRemoteConfig remoteConfig]; + // [END get_remote_config_instance] +} + +- (void)enableDevMode { + FIRRemoteConfig *remoteConfig = [FIRRemoteConfig remoteConfig]; + // [START enable_dev_mode] + FIRRemoteConfigSettings *remoteConfigSettings = [[FIRRemoteConfigSettings alloc] init]; + remoteConfigSettings.minimumFetchInterval = 0; + remoteConfig.configSettings = remoteConfigSettings; + // [END enable_dev_mode] +} + +- (void)setDefaultValues { + FIRRemoteConfig *remoteConfig = [FIRRemoteConfig remoteConfig]; + // [START set_default_values] + [remoteConfig setDefaultsFromPlistFileName:@"RemoteConfigDefaults"]; + // [END set_default_values] +} + +- (void)addConfigUpdateListener { + // [START add_config_update_listener] + __weak FIRRemoteConfig *config = [FIRRemoteConfig remoteConfig]; + [config addOnConfigUpdateListener:^(FIRRemoteConfigUpdate * _Nonnull configUpdate, NSError * _Nullable error) { + if (error != nil) { + NSLog(@"Error listening for config updates %@", error.localizedDescription); + } else { + NSLog(@"Updated keys: %@", configUpdate.updatedKeys); + [config activateWithCompletion:^(BOOL changed, NSError * _Nullable error) { + if (error != nil) { + NSLog(@"Activate error %@", error.localizedDescription); + } + dispatch_async(dispatch_get_main_queue(), ^{ + // update UI + }); + }]; + } + }]; + // [END add_config_update_listener] +} + +- (void)fetchConfigWithFallback { + FIRRemoteConfig *remoteConfig = [FIRRemoteConfig remoteConfig]; + // [START fetch_config_with_callback] + [remoteConfig fetchWithCompletionHandler:^(FIRRemoteConfigFetchStatus status, NSError *error) { + if (status == FIRRemoteConfigFetchStatusSuccess) { + NSLog(@"Config fetched!"); + [remoteConfig activateWithCompletion:^(BOOL changed, NSError * _Nullable error) { + if (error != nil) { + NSLog(@"Activate error: %@", error.localizedDescription); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + // update UI + }); + } + }]; + } else { + NSLog(@"Config not fetched"); + NSLog(@"Error %@", error.localizedDescription); + } + }]; + // [END fetch_config_with_callback] +} + +- (void)getConfigValue { + FIRRemoteConfig *remoteConfig = [FIRRemoteConfig remoteConfig]; + // [START get_config_value] + NSString *welcomeMessage = remoteConfig[@"welcome_message"].stringValue; + // [END get_config_value] + NSLog(@"%@", welcomeMessage); +} + +- (void)logAndCrash { + // [START log_and_crash] + [[FIRCrashlytics crashlytics] log:@"Cause Crash button clicked"]; + // crash + int *x = NULL; + *x = 10; + // [END log_and_crash] +} + +- (void)downloadImage { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = paths[0]; + NSString *filePath = [NSString stringWithFormat:@"file:%@/myimage.jpg", documentsDirectory]; + NSURL *fileURL = [NSURL URLWithString:filePath]; + + FIRStorageReference *reference = [[FIRStorage storage] reference]; + // [START downloadimage] + [[reference child:@"storagePath"] + writeToFile:fileURL + completion:^(NSURL * _Nullable URL, NSError * _Nullable error) { + if (error) { + NSLog(@"Error downloading: %@", error); + return; + } else if (URL) { + UIImage *image = [UIImage imageWithContentsOfFile:URL.path]; + NSLog(@"Download succeeded: %@", image); + } + }]; + // [END downloadimage] +} + +- (void)storageReference { + // [START configurestorage] + FIRStorage *storage = [FIRStorage storage]; + // [END configurestorage] +} + +- (void)storageAuth { + // [START storageauth] + // Using Cloud Storage for Firebase requires the user be authenticated. Here we are using + // anonymous authentication. + if (![FIRAuth auth].currentUser) { + [[FIRAuth auth] signInAnonymouslyWithCompletion:^(FIRAuthDataResult * _Nullable authResult, + NSError * _Nullable error) { + if (error) { + NSLog(@"%@", error.description); + } else { + // Sign in successful + } + }]; + } + // [END storageauth] +} + +- (void)uploadFile { + // [START uploadimage] + NSURL *imageFile = [[NSBundle mainBundle] URLForResource:@"image" withExtension:@"png"]; + FIRStorageReference *reference = [[FIRStorage storage] reference]; + [reference putFile:imageFile + metadata:nil + completion:^(FIRStorageMetadata *metadata, NSError *error) { + if (error) { + NSLog(@"Error uploading: %@", error); + return; + } + }]; + // [END uploadimage] +} + +@end + +@interface MessagingAppDelegate: NSObject + +@end + +@implementation MessagingAppDelegate + +- (void)setMessagingDelegate { + // [START set_messaging_delegate] + [FIRMessaging messaging].delegate = self; + // [END set_messaging_delegate] +} + +- (void)registerForNotifications { + UIApplication *application = [UIApplication sharedApplication]; + // [START register_for_notifications] + + [UNUserNotificationCenter currentNotificationCenter].delegate = self; + UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | + UNAuthorizationOptionSound | UNAuthorizationOptionBadge; + [[UNUserNotificationCenter currentNotificationCenter] + requestAuthorizationWithOptions:authOptions + completionHandler:^(BOOL granted, NSError * _Nullable error) { + // ... + }]; + + [application registerForRemoteNotifications]; + // [END register_for_notifications] +} + +// [START receive_message] +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo + fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + // If you are receiving a notification message while your app is in the background, + // this callback will not be fired until the user taps on the notification launching the application. + // TODO: Handle data of notification + + // With swizzling disabled you must let Messaging know about the message, for Analytics + // [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; + + // Print full message. + NSLog(@"%@", userInfo); + + completionHandler(UIBackgroundFetchResultNewData); +} +// [END receive_message] + +// [START ios_10_message_handling] +// Receive displayed notifications for iOS 10+ devices. +// Handle incoming notification messages while app is in the foreground. +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + willPresentNotification:(UNNotification *)notification + withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { + NSDictionary *userInfo = notification.request.content.userInfo; + + // With swizzling disabled you must let Messaging know about the message, for Analytics + // [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; + + // Print full message. + NSLog(@"%@", userInfo); + + // Change this to your preferred presentation option + completionHandler(UNNotificationPresentationOptionList | + UNNotificationPresentationOptionBanner | + UNNotificationPresentationOptionSound); +} + +// [START refresh_token] +- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken { + NSLog(@"FCM registration token: %@", fcmToken); + // Notify about received token. + NSDictionary *dataDict = [NSDictionary dictionaryWithObject:fcmToken forKey:@"token"]; + [[NSNotificationCenter defaultCenter] postNotificationName: + @"FCMToken" object:nil userInfo:dataDict]; + // TODO: If necessary send token to application server. + // Note: This callback is fired at each app startup and whenever a new token is generated. +} +// [END refresh_token] + +- (void)logFCMToken { + // [START log_fcm_reg_token] + NSString *fcmToken = [FIRMessaging messaging].FCMToken; + NSLog(@"Local FCM registration token: %@", fcmToken); + // [END log_fcm_reg_token] + + NSString* displayToken = [NSString stringWithFormat:@"Logged FCM token: %@", fcmToken]; + + // [START log_iid_reg_token] + [[FIRMessaging messaging] tokenWithCompletion:^(NSString * _Nullable token, NSError * _Nullable error) { + if (error != nil) { + NSLog(@"Error fetching the remote FCM registration token: %@", error); + } else { + NSLog(@"Remote FCM registration token: %@", token); + NSString* message = + [NSString stringWithFormat:@"FCM registration token: %@", token]; + // display message + NSLog(@"%@", message); + } + }]; + // [END log_iid_reg_token] + NSLog(@"%@", displayToken); +} + +- (void)subsribeToTopic { + // [START subscribe_topic] + [[FIRMessaging messaging] subscribeToTopic:@"weather" + completion:^(NSError * _Nullable error) { + NSLog(@"Subscribed to weather topic"); + }]; + // [END subscribe_topic] +} + +@end diff --git a/qs-snippets/QSSnippets/QSSnippets-Bridging-Header.h b/qs-snippets/QSSnippets/QSSnippets-Bridging-Header.h new file mode 100644 index 00000000..28a1d906 --- /dev/null +++ b/qs-snippets/QSSnippets/QSSnippets-Bridging-Header.h @@ -0,0 +1,16 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + diff --git a/qs-snippets/QSSnippets/QSSnippetsApp.swift b/qs-snippets/QSSnippets/QSSnippetsApp.swift new file mode 100644 index 00000000..dff94a87 --- /dev/null +++ b/qs-snippets/QSSnippets/QSSnippetsApp.swift @@ -0,0 +1,63 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import CryptoKit + +@main +struct QSSnippetsApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} + +/// Set of utility APIs for generating cryptographical artifacts. +enum CryptoUtils { + enum NonceGenerationError: Error { + case generationFailure(status: OSStatus) + } + + static func randomNonceString(length: Int = 32) throws -> String { + precondition(length > 0) + var randomBytes = [UInt8](repeating: 0, count: length) + let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) + if errorCode != errSecSuccess { + throw NonceGenerationError.generationFailure(status: errorCode) + } + + let charset: [Character] = + Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") + + let nonce = randomBytes.map { byte in + // Pick a random character from the set, wrapping around if needed. + charset[Int(byte) % charset.count] + } + + return String(nonce) + } + + static func sha256(_ input: String) -> String { + let inputData = Data(input.utf8) + let hashedData = SHA256.hash(data: inputData) + let hashString = hashedData.compactMap { + String(format: "%02x", $0) + }.joined() + + return hashString + } +} diff --git a/qs-snippets/QSSnippetsTests/QSSnippetsTests.swift b/qs-snippets/QSSnippetsTests/QSSnippetsTests.swift new file mode 100644 index 00000000..392881ab --- /dev/null +++ b/qs-snippets/QSSnippetsTests/QSSnippetsTests.swift @@ -0,0 +1,26 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Testing +@testable import QSSnippets + +struct QSSnippetsTests { + + @Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + } + +} diff --git a/qs-snippets/QSSnippetsUITests/QSSnippetsUITests.swift b/qs-snippets/QSSnippetsUITests/QSSnippetsUITests.swift new file mode 100644 index 00000000..4b0381c2 --- /dev/null +++ b/qs-snippets/QSSnippetsUITests/QSSnippetsUITests.swift @@ -0,0 +1,50 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +final class QSSnippetsUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + @MainActor + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + @MainActor + func testLaunchPerformance() throws { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } +} diff --git a/qs-snippets/QSSnippetsUITests/QSSnippetsUITestsLaunchTests.swift b/qs-snippets/QSSnippetsUITests/QSSnippetsUITestsLaunchTests.swift new file mode 100644 index 00000000..242f5b47 --- /dev/null +++ b/qs-snippets/QSSnippetsUITests/QSSnippetsUITestsLaunchTests.swift @@ -0,0 +1,42 @@ +// +// Copyright (c) 2025 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +final class QSSnippetsUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + @MainActor + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/storage/Podfile b/storage/Podfile index b208f24a..c54ab2e5 100644 --- a/storage/Podfile +++ b/storage/Podfile @@ -1,11 +1,12 @@ -platform :ios, '8.0' +# Podfile left around for legacy snippets. The project has since been migrated to SPM. +platform :ios, '12.0' use_frameworks! # [START pod_firebase_storage] -pod 'Firebase/Storage' +pod 'FirebaseStorage', '~> 9.0' # [END pod_firebase_storage] # [START pod_firebaseui_storage] -pod 'FirebaseUI/Storage' +pod 'FirebaseStorageUI' # [END pod_firebaseui_storage] target 'StorageReference' do diff --git a/storage/Podfile.lock b/storage/Podfile.lock deleted file mode 100644 index 9a6e3abb..00000000 --- a/storage/Podfile.lock +++ /dev/null @@ -1,80 +0,0 @@ -PODS: - - Firebase/CoreOnly (6.8.0): - - FirebaseCore (= 6.2.2) - - Firebase/Storage (6.8.0): - - Firebase/CoreOnly - - FirebaseStorage (~> 3.4.0) - - FirebaseAuthInterop (1.0.0) - - FirebaseCore (6.2.2): - - FirebaseCoreDiagnostics (~> 1.0) - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnostics (1.0.1): - - FirebaseCoreDiagnosticsInterop (~> 1.0) - - GoogleDataTransportCCTSupport (~> 1.0) - - GoogleUtilities/Environment (~> 6.2) - - GoogleUtilities/Logger (~> 6.2) - - FirebaseCoreDiagnosticsInterop (1.0.0) - - FirebaseStorage (3.4.0): - - FirebaseAuthInterop (~> 1.0) - - FirebaseCore (~> 6.0) - - GTMSessionFetcher/Core (~> 1.1) - - FirebaseUI/Storage (8.0.4): - - Firebase/Storage (~> 6.0) - - SDWebImage (~> 5.0) - - GoogleDataTransport (1.2.0) - - GoogleDataTransportCCTSupport (1.0.3): - - GoogleDataTransport (~> 1.2) - - nanopb - - GoogleUtilities/Environment (6.2.5) - - GoogleUtilities/Logger (6.2.5): - - GoogleUtilities/Environment - - GTMSessionFetcher/Core (1.2.2) - - nanopb (0.3.901): - - nanopb/decode (= 0.3.901) - - nanopb/encode (= 0.3.901) - - nanopb/decode (0.3.901) - - nanopb/encode (0.3.901) - - SDWebImage (5.1.1): - - SDWebImage/Core (= 5.1.1) - - SDWebImage/Core (5.1.1) - -DEPENDENCIES: - - Firebase/Storage - - FirebaseUI/Storage - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - Firebase - - FirebaseAuthInterop - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCoreDiagnosticsInterop - - FirebaseStorage - - FirebaseUI - - GoogleDataTransport - - GoogleDataTransportCCTSupport - - GoogleUtilities - - GTMSessionFetcher - - nanopb - - SDWebImage - -SPEC CHECKSUMS: - Firebase: c35912a5c160193dc423f260dac3f167a1a795ab - FirebaseAuthInterop: 0ffa57668be100582bb7643d4fcb7615496c41fc - FirebaseCore: 12422a2a2b79ed161b06ccad1edfe650de7a4b34 - FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13 - FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb - FirebaseStorage: 618dd8fd886b30adf6e5d1915b1f5c18cb5c3944 - FirebaseUI: d92c980c1c7e553df3c5e7b0725baeb13f80f6d2 - GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc - GoogleDataTransportCCTSupport: 51134d81fca795c60cc247d1cb6af63c3d67b8d8 - GoogleUtilities: e7dc37039b19df7fe543479d3e4a02ac8d11bb69 - GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23 - nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 - SDWebImage: 96d7f03415ccb28d299d765f93557ff8a617abd8 - -PODFILE CHECKSUM: f0cd9d079793b24a37b713cd64dc7900bb8601af - -COCOAPODS: 1.7.5 diff --git a/storage/StorageReference.xcodeproj/project.pbxproj b/storage/StorageReference.xcodeproj/project.pbxproj index 312025d3..999509a5 100644 --- a/storage/StorageReference.xcodeproj/project.pbxproj +++ b/storage/StorageReference.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -15,6 +15,10 @@ 8D64553B1DFF56CE00972DCE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D6455391DFF56CE00972DCE /* Main.storyboard */; }; 8D64553D1DFF56CE00972DCE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D64553C1DFF56CE00972DCE /* Assets.xcassets */; }; 8D6455401DFF56CE00972DCE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8D64553E1DFF56CE00972DCE /* LaunchScreen.storyboard */; }; + 8D7951D02D2C8C71000FD694 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951CF2D2C8C71000FD694 /* FirebaseStorage */; }; + 8D7951D22D2C8C77000FD694 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951D12D2C8C77000FD694 /* FirebaseStorage */; }; + 8D7951D52D2C8D2A000FD694 /* FirebaseStorageUI in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951D42D2C8D2A000FD694 /* FirebaseStorageUI */; }; + 8D7951D72D2C8D33000FD694 /* FirebaseStorageUI in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7951D62D2C8D33000FD694 /* FirebaseStorageUI */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -38,6 +42,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D7951D22D2C8C77000FD694 /* FirebaseStorage in Frameworks */, + 8D7951D52D2C8D2A000FD694 /* FirebaseStorageUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -45,19 +51,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8D7951D02D2C8C71000FD694 /* FirebaseStorage in Frameworks */, + 8D7951D72D2C8D33000FD694 /* FirebaseStorageUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0B3979B2C8769728EE24D424 /* Pods */ = { - isa = PBXGroup; - children = ( - ); - path = Pods; - sourceTree = ""; - }; 1090B0671EBCE02000A8F759 /* StorageReferenceSwift */ = { isa = PBXGroup; children = ( @@ -72,8 +73,8 @@ children = ( 8D64552F1DFF56CE00972DCE /* StorageReference */, 1090B0671EBCE02000A8F759 /* StorageReferenceSwift */, + 8D7951CE2D2C8C71000FD694 /* Frameworks */, 8D64552E1DFF56CE00972DCE /* Products */, - 0B3979B2C8769728EE24D424 /* Pods */, ); sourceTree = ""; }; @@ -110,6 +111,13 @@ name = "Supporting Files"; sourceTree = ""; }; + 8D7951CE2D2C8C71000FD694 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -153,8 +161,9 @@ 8D6455251DFF56CD00972DCE /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1610; ORGANIZATIONNAME = "Google Inc."; TargetAttributes = { 1090B0651EBCE02000A8F759 = { @@ -170,14 +179,17 @@ }; buildConfigurationList = 8D6455281DFF56CD00972DCE /* Build configuration list for PBXProject "StorageReference" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, Base, ); mainGroup = 8D6455241DFF56CD00972DCE; + packageReferences = ( + 8D7951CD2D2C8C62000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 8D7951D32D2C8D15000FD694 /* XCRemoteSwiftPackageReference "FirebaseUI-iOS" */, + ); productRefGroup = 8D64552E1DFF56CE00972DCE /* Products */; projectDirPath = ""; projectRoot = ""; @@ -258,13 +270,17 @@ CLANG_ENABLE_MODULES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; INFOPLIST_FILE = "$(SRCROOT)/StorageReference/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_SWIFT_FLAGS = "-Xcc -fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/FirebaseStorage.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.referencecode.FirebaseStorageReference; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 6.0; }; name = Debug; }; @@ -276,12 +292,17 @@ CLANG_ENABLE_MODULES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; INFOPLIST_FILE = "$(SRCROOT)/StorageReference/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_SWIFT_FLAGS = "-Xcc -fmodule-map-file=$(GENERATED_MODULEMAP_DIR)/FirebaseStorage.modulemap"; PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.referencecode.FirebaseStorageReference; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 5.0; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 6.0; }; name = Release; }; @@ -289,20 +310,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -312,6 +343,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -326,10 +358,11 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.1; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -338,20 +371,30 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -361,6 +404,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -369,10 +413,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.1; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -383,7 +429,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/StorageReference/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.referencecode.StorageReference; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -394,7 +444,11 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; INFOPLIST_FILE = "$(SRCROOT)/StorageReference/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.google.firebase.referencecode.StorageReference; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -431,6 +485,48 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D7951CD2D2C8C62000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 11.6.0; + }; + }; + 8D7951D32D2C8D15000FD694 /* XCRemoteSwiftPackageReference "FirebaseUI-iOS" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/FirebaseUI-iOS"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 15.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D7951CF2D2C8C71000FD694 /* FirebaseStorage */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951CD2D2C8C62000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseStorage; + }; + 8D7951D12D2C8C77000FD694 /* FirebaseStorage */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951CD2D2C8C62000FD694 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseStorage; + }; + 8D7951D42D2C8D2A000FD694 /* FirebaseStorageUI */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951D32D2C8D15000FD694 /* XCRemoteSwiftPackageReference "FirebaseUI-iOS" */; + productName = FirebaseStorageUI; + }; + 8D7951D62D2C8D33000FD694 /* FirebaseStorageUI */ = { + isa = XCSwiftPackageProductDependency; + package = 8D7951D32D2C8D15000FD694 /* XCRemoteSwiftPackageReference "FirebaseUI-iOS" */; + productName = FirebaseStorageUI; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 8D6455251DFF56CD00972DCE /* Project object */; } diff --git a/storage/StorageReference/AppDelegate.m b/storage/StorageReference/AppDelegate.m index 46d54a96..182441e6 100644 --- a/storage/StorageReference/AppDelegate.m +++ b/storage/StorageReference/AppDelegate.m @@ -17,7 +17,11 @@ #import "AppDelegate.h" // [START import_firebase] -@import Firebase; +@import FirebaseCore; +// Import other Firebase products as necessary. +// @import FirebaseAnalytics; +// @import FirebaseCrashlytics; +// ... // [END import_firebase] @interface AppDelegate () diff --git a/storage/StorageReference/ViewController.m b/storage/StorageReference/ViewController.m index 01ba1763..30aef157 100644 --- a/storage/StorageReference/ViewController.m +++ b/storage/StorageReference/ViewController.m @@ -16,9 +16,8 @@ #import "ViewController.h" -@import Firebase; - -@import FirebaseUI; +@import FirebaseStorage; +@import FirebaseStorageUI; @interface ViewController () @@ -245,10 +244,10 @@ - (void)storagePauseExample { // [START firstorage_progress] // Add a progress observer to an upload task - FIRStorageHandle observer = [uploadTask observeStatus:FIRStorageTaskStatusProgress - handler:^(FIRStorageTaskSnapshot *snapshot) { - // A progress event occurred - }]; + NSString *observer = [uploadTask observeStatus:FIRStorageTaskStatusProgress + handler:^(FIRStorageTaskSnapshot *snapshot) { + // A progress event occurred + }]; // [END firstorage_progress] } @@ -260,10 +259,10 @@ - (void)storageTaskExample { // [START firstorage_task] // Create a task listener handle - FIRStorageHandle observer = [uploadTask observeStatus:FIRStorageTaskStatusProgress - handler:^(FIRStorageTaskSnapshot *snapshot) { - // A progress event occurred - }]; + NSString *observer = [uploadTask observeStatus:FIRStorageTaskStatusProgress + handler:^(FIRStorageTaskSnapshot *snapshot) { + // A progress event occurred + }]; // Remove an individual observer [uploadTask removeObserverWithHandle:observer]; @@ -447,10 +446,10 @@ - (void)storageDownloadPauseExample { // [START firstorage_download_observe] // Add a progress observer to a download task - FIRStorageHandle observer = [downloadTask observeStatus:FIRStorageTaskStatusProgress - handler:^(FIRStorageTaskSnapshot *snapshot) { - // A progress event occurred - }]; + NSString *observer = [downloadTask observeStatus:FIRStorageTaskStatusProgress + handler:^(FIRStorageTaskSnapshot *snapshot) { + // A progress event occurred + }]; // [END firstorage_download_observe] } @@ -461,10 +460,10 @@ - (void)storageHandleObserverExample { // [START firstorage_handle_observer] // Create a task listener handle - FIRStorageHandle observer = [downloadTask observeStatus:FIRStorageTaskStatusProgress - handler:^(FIRStorageTaskSnapshot *snapshot) { - // A progress event occurred - }]; + NSString *observer = [downloadTask observeStatus:FIRStorageTaskStatusProgress + handler:^(FIRStorageTaskSnapshot *snapshot) { + // A progress event occurred + }]; // Remove an individual observer [downloadTask removeObserverWithHandle:observer]; @@ -580,7 +579,7 @@ - (void)storageDeleteMetadataExample { // [START firstorage_delete_metadata] FIRStorageMetadata *newMetadata = [[FIRStorageMetadata alloc] init]; - newMetadata.contentType = @""; + newMetadata.contentType = nil; // Delete the metadata property [forestRef updateMetadata:newMetadata completion:^(FIRStorageMetadata *metadata, NSError *error){ diff --git a/storage/StorageReferenceSwift/AppDelegate.swift b/storage/StorageReferenceSwift/AppDelegate.swift index e6f9400b..7cf52e57 100644 --- a/storage/StorageReferenceSwift/AppDelegate.swift +++ b/storage/StorageReferenceSwift/AppDelegate.swift @@ -17,10 +17,14 @@ import UIKit // [START import_firebase] -import Firebase +import FirebaseCore +// Import other Firebase products as necessary. +// import FirebaseAnalytics +// import FirebaseCrashlytics +// ... // [END import_firebase] -@UIApplicationMain +@main class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? diff --git a/storage/StorageReferenceSwift/ViewController.swift b/storage/StorageReferenceSwift/ViewController.swift index edb48bed..5cf1ac24 100644 --- a/storage/StorageReferenceSwift/ViewController.swift +++ b/storage/StorageReferenceSwift/ViewController.swift @@ -16,8 +16,12 @@ import UIKit -import Firebase -import FirebaseUI +import FirebaseCore +import FirebaseStorage +import FirebaseStorageUI + +extension StorageMetadata: @unchecked @retroactive Sendable {} +extension StorageListResult: @unchecked @retroactive Sendable {} class ViewController: UIViewController { @@ -108,10 +112,10 @@ class ViewController: UIViewController { let spaceRef = imagesRef.child(fileName) // File path is "images/space.jpg" - let path = spaceRef.fullPath; + let path = spaceRef.fullPath // File name is "space.jpg" - let name = spaceRef.name; + let name = spaceRef.name // Points to "images" let images = spaceRef.parent() @@ -132,12 +136,12 @@ class ViewController: UIViewController { let mountainImagesRef = storageRef.child("images/mountains.jpg") // While the file names are the same, the references point to different files - mountainsRef.name == mountainImagesRef.name; // true - mountainsRef.fullPath == mountainImagesRef.fullPath; // false + mountainsRef.name == mountainImagesRef.name // true + mountainsRef.fullPath == mountainImagesRef.fullPath // false // [END firstorage_upload] } - func storageInMemoryExample() { + func storageInMemoryExample() async { let storageRef = Storage.storage().reference() // [START firstorage_memory] @@ -185,7 +189,7 @@ class ViewController: UIViewController { // Metadata contains file metadata such as size, content-type. let size = metadata.size // You can also access to download URL after upload. - storageRef.downloadURL { (url, error) in + riversRef.downloadURL { (url, error) in guard let downloadURL = url else { // Uh-oh, an error occurred! return @@ -529,7 +533,7 @@ class ViewController: UIViewController { // [END firstorage_download_combined] } - func storageGetMetadataExample() { + func storageGetMetadataExample() async { let storageRef = Storage.storage().reference() // [START firstorage_get_metadata] @@ -537,17 +541,15 @@ class ViewController: UIViewController { let forestRef = storageRef.child("images/forest.jpg") // Get metadata properties - forestRef.getMetadata { metadata, error in - if let error = error { - // Uh-oh, an error occurred! - } else { - // Metadata now contains the metadata for 'images/forest.jpg' - } + do { + let metadata = try await forestRef.getMetadata() + } catch { + // ... } // [END firstorage_get_metadata] } - func storageChangeMetadataExample() { + func storageChangeMetadataExample() async { let storageRef = Storage.storage().reference() // [START firstorage_change_metadata] @@ -556,35 +558,31 @@ class ViewController: UIViewController { // Create file metadata to update let newMetadata = StorageMetadata() - newMetadata.cacheControl = "public,max-age=300"; - newMetadata.contentType = "image/jpeg"; + newMetadata.cacheControl = "public,max-age=300" + newMetadata.contentType = "image/jpeg" // Update metadata properties - forestRef.updateMetadata(newMetadata) { metadata, error in - if let error = error { - // Uh-oh, an error occurred! - } else { - // Updated metadata for 'images/forest.jpg' is returned - } + do { + let updatedMetadata = try await forestRef.updateMetadata(newMetadata) + } catch { + // ... } // [END firstorage_change_metadata] } - func storageDeleteMetadataExample() { + func storageDeleteMetadataExample() async { let storageRef = Storage.storage().reference() let forestRef = storageRef.child("images/forest.jpg") // [START firstorage_delete_metadata] let newMetadata = StorageMetadata() - newMetadata.contentType = ""; + newMetadata.contentType = nil - // Delete the metadata property - forestRef.updateMetadata(newMetadata) { metadata, error in - if let error = error { - // Uh-oh, an error occurred! - } else { - // metadata.contentType should be nil - } + do { + // Delete the metadata property + let updatedMetadata = try await forestRef.updateMetadata(newMetadata) + } catch { + // ... } // [END firstorage_delete_metadata] } @@ -600,32 +598,28 @@ class ViewController: UIViewController { // [END firstorage_custom_metadata] } - func storageDeleteFileExample() { + func storageDeleteFileExample() async { let storageRef = Storage.storage().reference() // [START firstorage_delete] // Create a reference to the file to delete let desertRef = storageRef.child("desert.jpg") - // Delete the file - desertRef.delete { error in - if let error = error { - // Uh-oh, an error occurred! - } else { - // File deleted successfully - } + do { + // Delete the file + try await desertRef.delete() + } catch { + // ... } // [END firstorage_delete] } - func listAllFiles() { + func listAllFiles() async { let storage = Storage.storage() // [START storage_list_all] let storageReference = storage.reference().child("files/uid") - storageReference.listAll { (result, error) in - if let error = error { - // ... - } + do { + let result = try await storageReference.listAll() for prefix in result.prefixes { // The prefixes under storageReference. // You may call listAll(completion:) recursively on them. @@ -633,35 +627,38 @@ class ViewController: UIViewController { for item in result.items { // The items under storageReference. } + } catch { + // ... } // [END storage_list_all] } // [START storage_list_paginated] - func listAllPaginated(pageToken: String? = nil) { + func listAllPaginated(pageToken: String? = nil) async throws { let storage = Storage.storage() let storageReference = storage.reference().child("files/uid") - let pageHandler: (StorageListResult, Error?) -> Void = { (result, error) in - if let error = error { - // ... - } - let prefixes = result.prefixes - let items = result.items - - // ... - - // Process next page - if let token = result.pageToken { - self.listAllPaginated(pageToken: token) - } - } - + let listResult: StorageListResult if let pageToken = pageToken { - storageReference.list(withMaxResults: 100, pageToken: pageToken, completion: pageHandler) + listResult = try await storageReference.list(maxResults: 100, pageToken: pageToken) } else { - storageReference.list(withMaxResults: 100, completion: pageHandler) + listResult = try await storageReference.list(maxResults: 100) + } + let prefixes = listResult.prefixes + let items = listResult.items + // Handle list result + // ... + + // Process next page + if let token = listResult.pageToken { + try await listAllPaginated(pageToken: token) } } // [END storage_list_paginated] + + func emulatorSettings() { + // [START storage_emulator_connect] + Storage.storage().useEmulator(withHost: "127.0.0.1", port: 9199) + // [END storage_emulator_connect] + } } diff --git a/vertexai/VertexAISnippets.xcodeproj/project.pbxproj b/vertexai/VertexAISnippets.xcodeproj/project.pbxproj new file mode 100644 index 00000000..70883f14 --- /dev/null +++ b/vertexai/VertexAISnippets.xcodeproj/project.pbxproj @@ -0,0 +1,419 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 8D40F40E2BD1CDC30020872A /* VertexAISnippetsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D40F40D2BD1CDC30020872A /* VertexAISnippetsApp.swift */; }; + 8D40F4102BD1CDC30020872A /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D40F40F2BD1CDC30020872A /* ContentView.swift */; }; + 8D40F4122BD1CDC40020872A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D40F4112BD1CDC40020872A /* Assets.xcassets */; }; + 8D40F4162BD1CDC40020872A /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8D40F4152BD1CDC40020872A /* Preview Assets.xcassets */; }; + 8D40F4262BD1CE1A0020872A /* FirebaseAppCheck in Frameworks */ = {isa = PBXBuildFile; productRef = 8D40F4252BD1CE1A0020872A /* FirebaseAppCheck */; }; + 8D40F43D2BD1CE910020872A /* VertexAISnippets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D40F43C2BD1CE910020872A /* VertexAISnippets.swift */; }; + 8D7B83012CD4127C0024A604 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7B83002CD4127C0024A604 /* FirebaseAuth */; }; + 8D7B83032CD4127C0024A604 /* FirebaseVertexAI in Frameworks */ = {isa = PBXBuildFile; productRef = 8D7B83022CD4127C0024A604 /* FirebaseVertexAI */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 8D40F40A2BD1CDC30020872A /* VertexAISnippets.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VertexAISnippets.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D40F40D2BD1CDC30020872A /* VertexAISnippetsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VertexAISnippetsApp.swift; sourceTree = ""; }; + 8D40F40F2BD1CDC30020872A /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 8D40F4112BD1CDC40020872A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8D40F4132BD1CDC40020872A /* VertexAISnippets.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VertexAISnippets.entitlements; sourceTree = ""; }; + 8D40F4152BD1CDC40020872A /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 8D40F43C2BD1CE910020872A /* VertexAISnippets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VertexAISnippets.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D40F4072BD1CDC30020872A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D7B83032CD4127C0024A604 /* FirebaseVertexAI in Frameworks */, + 8D7B83012CD4127C0024A604 /* FirebaseAuth in Frameworks */, + 8D40F4262BD1CE1A0020872A /* FirebaseAppCheck in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 8D40F4012BD1CDC30020872A = { + isa = PBXGroup; + children = ( + 8D40F40C2BD1CDC30020872A /* VertexAISnippets */, + 8D40F40B2BD1CDC30020872A /* Products */, + 8D40F4392BD1CE3E0020872A /* Frameworks */, + ); + sourceTree = ""; + }; + 8D40F40B2BD1CDC30020872A /* Products */ = { + isa = PBXGroup; + children = ( + 8D40F40A2BD1CDC30020872A /* VertexAISnippets.app */, + ); + name = Products; + sourceTree = ""; + }; + 8D40F40C2BD1CDC30020872A /* VertexAISnippets */ = { + isa = PBXGroup; + children = ( + 8D40F40D2BD1CDC30020872A /* VertexAISnippetsApp.swift */, + 8D40F43C2BD1CE910020872A /* VertexAISnippets.swift */, + 8D40F40F2BD1CDC30020872A /* ContentView.swift */, + 8D40F4112BD1CDC40020872A /* Assets.xcassets */, + 8D40F4132BD1CDC40020872A /* VertexAISnippets.entitlements */, + 8D40F4142BD1CDC40020872A /* Preview Content */, + ); + path = VertexAISnippets; + sourceTree = ""; + }; + 8D40F4142BD1CDC40020872A /* Preview Content */ = { + isa = PBXGroup; + children = ( + 8D40F4152BD1CDC40020872A /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 8D40F4392BD1CE3E0020872A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D40F4092BD1CDC30020872A /* VertexAISnippets */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8D40F4192BD1CDC40020872A /* Build configuration list for PBXNativeTarget "VertexAISnippets" */; + buildPhases = ( + 8D40F4062BD1CDC30020872A /* Sources */, + 8D40F4072BD1CDC30020872A /* Frameworks */, + 8D40F4082BD1CDC30020872A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VertexAISnippets; + packageProductDependencies = ( + 8D40F4252BD1CE1A0020872A /* FirebaseAppCheck */, + 8D7B83002CD4127C0024A604 /* FirebaseAuth */, + 8D7B83022CD4127C0024A604 /* FirebaseVertexAI */, + ); + productName = VertexAISnippets; + productReference = 8D40F40A2BD1CDC30020872A /* VertexAISnippets.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8D40F4022BD1CDC30020872A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1520; + LastUpgradeCheck = 1530; + TargetAttributes = { + 8D40F4092BD1CDC30020872A = { + CreatedOnToolsVersion = 15.2; + }; + }; + }; + buildConfigurationList = 8D40F4052BD1CDC30020872A /* Build configuration list for PBXProject "VertexAISnippets" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8D40F4012BD1CDC30020872A; + packageReferences = ( + 8D40F41C2BD1CE1A0020872A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); + productRefGroup = 8D40F40B2BD1CDC30020872A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D40F4092BD1CDC30020872A /* VertexAISnippets */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D40F4082BD1CDC30020872A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D40F4162BD1CDC40020872A /* Preview Assets.xcassets in Resources */, + 8D40F4122BD1CDC40020872A /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D40F4062BD1CDC30020872A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D40F4102BD1CDC30020872A /* ContentView.swift in Sources */, + 8D40F43D2BD1CE910020872A /* VertexAISnippets.swift in Sources */, + 8D40F40E2BD1CDC30020872A /* VertexAISnippetsApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 8D40F4172BD1CDC40020872A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 8D40F4182BD1CDC40020872A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + 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; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 8D40F41A2BD1CDC40020872A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = VertexAISnippets/VertexAISnippets.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_ASSET_PATHS = "\"VertexAISnippets/Preview Content\""; + 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 = 17.2; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 14.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.VertexAISnippets; + 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; + }; + 8D40F41B2BD1CDC40020872A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = VertexAISnippets/VertexAISnippets.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_ASSET_PATHS = "\"VertexAISnippets/Preview Content\""; + 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 = 17.2; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 14.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.firebase.VertexAISnippets; + 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 */ + 8D40F4052BD1CDC30020872A /* Build configuration list for PBXProject "VertexAISnippets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D40F4172BD1CDC40020872A /* Debug */, + 8D40F4182BD1CDC40020872A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8D40F4192BD1CDC40020872A /* Build configuration list for PBXNativeTarget "VertexAISnippets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8D40F41A2BD1CDC40020872A /* Debug */, + 8D40F41B2BD1CDC40020872A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 8D40F41C2BD1CE1A0020872A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; + requirement = { + branch = main; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8D40F4252BD1CE1A0020872A /* FirebaseAppCheck */ = { + isa = XCSwiftPackageProductDependency; + package = 8D40F41C2BD1CE1A0020872A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAppCheck; + }; + 8D7B83002CD4127C0024A604 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 8D40F41C2BD1CE1A0020872A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 8D7B83022CD4127C0024A604 /* FirebaseVertexAI */ = { + isa = XCSwiftPackageProductDependency; + package = 8D40F41C2BD1CE1A0020872A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseVertexAI; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 8D40F4022BD1CDC30020872A /* Project object */; +} diff --git a/vertexai/VertexAISnippets/Assets.xcassets/AccentColor.colorset/Contents.json b/vertexai/VertexAISnippets/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/vertexai/VertexAISnippets/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/vertexai/VertexAISnippets/Assets.xcassets/AppIcon.appiconset/Contents.json b/vertexai/VertexAISnippets/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..532cd729 --- /dev/null +++ b/vertexai/VertexAISnippets/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/vertexai/VertexAISnippets/Assets.xcassets/Contents.json b/vertexai/VertexAISnippets/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/vertexai/VertexAISnippets/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/vertexai/VertexAISnippets/ContentView.swift b/vertexai/VertexAISnippets/ContentView.swift new file mode 100644 index 00000000..0c5e96f7 --- /dev/null +++ b/vertexai/VertexAISnippets/ContentView.swift @@ -0,0 +1,31 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text("Hello, world!") + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/vertexai/VertexAISnippets/Preview Content/Preview Assets.xcassets/Contents.json b/vertexai/VertexAISnippets/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/vertexai/VertexAISnippets/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/vertexai/VertexAISnippets/VertexAISnippets.entitlements b/vertexai/VertexAISnippets/VertexAISnippets.entitlements new file mode 100644 index 00000000..f2ef3ae0 --- /dev/null +++ b/vertexai/VertexAISnippets/VertexAISnippets.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/vertexai/VertexAISnippets/VertexAISnippets.swift b/vertexai/VertexAISnippets/VertexAISnippets.swift new file mode 100644 index 00000000..9218dbe9 --- /dev/null +++ b/vertexai/VertexAISnippets/VertexAISnippets.swift @@ -0,0 +1,468 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if canImport(UIKit) +import UIKit +#else +import AppKit +#endif + +// [START import_vertexai] +import FirebaseVertexAI +import FirebaseCore +// [END import_vertexai] + +class Snippets { + + var model: GenerativeModel! + + func initializeModel() { + // [START initialize_model] + // Initialize the Vertex AI service + let vertex = VertexAI.vertexAI() + + // Initialize the generative model with a model that supports your use case + // Gemini 1.5 models are versatile and can be used with all API capabilities + let model = vertex.generativeModel(modelName: "gemini-1.5-flash") + // [END initialize_model] + + self.model = model + } + + func configureModel() { + let vertex = VertexAI.vertexAI() + + // [START configure_model] + let config = GenerationConfig( + temperature: 0.9, + topP: 0.1, + topK: 16, + maxOutputTokens: 200, + stopSequences: ["red"] + ) + + let model = vertex.generativeModel( + modelName: "gemini-1.5-flash", + generationConfig: config + ) + // [END configure_model] + } + + func safetySettings() { + let vertex = VertexAI.vertexAI() + + // [START safety_settings] + let model = vertex.generativeModel( + modelName: "gemini-1.5-flash", + safetySettings: [ + SafetySetting(harmCategory: .harassment, threshold: .blockOnlyHigh) + ] + ) + // [END safety_settings] + } + + func multiSafetySettings() { + let vertex = VertexAI.vertexAI() + + // [START multi_safety_settings] + let harassmentSafety = SafetySetting(harmCategory: .harassment, threshold: .blockOnlyHigh) + let hateSpeechSafety = SafetySetting(harmCategory: .hateSpeech, threshold: .blockMediumAndAbove) + + let model = vertex.generativeModel( + modelName: "gemini-1.5-flash", + safetySettings: [harassmentSafety, hateSpeechSafety] + ) + // [END multi_safety_settings] + } + + func sendTextOnlyPromptStreaming() async throws { + // [START text_gen_text_only_prompt_streaming] + // Provide a prompt that contains text + let prompt = "Write a story about a magic backpack." + + // To stream generated text output, call generateContentStream with the text input + let contentStream = try model.generateContentStream(prompt) + for try await chunk in contentStream { + if let text = chunk.text { + print(text) + } + } + // [END text_gen_text_only_prompt_streaming] + } + + func sendTextOnlyPromt() async throws { + // [START text_gen_text_only_prompt] + // Provide a prompt that contains text + let prompt = "Write a story about a magic backpack." + + // To generate text output, call generateContent with the text input + let response = try await model.generateContent(prompt) + if let text = response.text { + print(text) + } + // [END text_gen_text_only_prompt] + } + + func sendMultimodalPromptStreaming() async throws { + // [START text_gen_multimodal_one_image_prompt_streaming] + #if canImport(UIKit) + guard let image = UIImage(named: "image") else { fatalError() } + #else + guard let image = NSImage(named: "image") else { fatalError() } + #endif + + // Provide a text prompt to include with the image + let prompt = "What's in this picture?" + + // To stream generated text output, call generateContentStream and pass in the prompt + let contentStream = try model.generateContentStream(image, prompt) + for try await chunk in contentStream { + if let text = chunk.text { + print(text) + } + } + // [END text_gen_multimodal_one_image_prompt_streaming] + } + + func sendMultimodalPrompt() async throws { + // [START text_gen_multimodal_one_image_prompt] + // Provide a text prompt to include with the image + #if canImport(UIKit) + guard let image = UIImage(named: "image") else { fatalError() } + #else + guard let image = NSImage(named: "image") else { fatalError() } + #endif + + let prompt = "What's in this picture?" + + // To generate text output, call generateContent and pass in the prompt + let response = try await model.generateContent(image, prompt) + if let text = response.text { + print(text) + } + // [END text_gen_multimodal_one_image_prompt] + } + + func multiImagePromptStreaming() async throws { + // [START text_gen_multimodal_multi_image_prompt_streaming] + #if canImport(UIKit) + guard let image1 = UIImage(named: "image1") else { fatalError() } + guard let image2 = UIImage(named: "image2") else { fatalError() } + #else + guard let image1 = NSImage(named: "image1") else { fatalError() } + guard let image2 = NSImage(named: "image2") else { fatalError() } + #endif + + // Provide a text prompt to include with the images + let prompt = "What's different between these pictures?" + + // To stream generated text output, call generateContentStream and pass in the prompt + let contentStream = try model.generateContentStream(image1, image2, prompt) + for try await chunk in contentStream { + if let text = chunk.text { + print(text) + } + } + // [END text_gen_multimodal_multi_image_prompt_streaming] + } + + func multiImagePrompt() async throws { + // [START text_gen_multimodal_multi_image_prompt] + #if canImport(UIKit) + guard let image1 = UIImage(named: "image1") else { fatalError() } + guard let image2 = UIImage(named: "image2") else { fatalError() } + #else + guard let image1 = NSImage(named: "image1") else { fatalError() } + guard let image2 = NSImage(named: "image2") else { fatalError() } + #endif + + // Provide a text prompt to include with the images + let prompt = "What's different between these pictures?" + + // To generate text output, call generateContent and pass in the prompt + let response = try await model.generateContent(image1, image2, prompt) + if let text = response.text { + print(text) + } + // [END text_gen_multimodal_multi_image_prompt] + } + + func textAndVideoPrompt() async throws { + // [START text_gen_multimodal_video_prompt] + guard let fileURL = Bundle.main.url(forResource: "sample", + withExtension: "mp4") else { fatalError() } + let video = try Data(contentsOf: fileURL) + let prompt = "What's in this video?" + let videoContent = InlineDataPart(data: video, mimeType: "video/mp4") + + // To generate text output, call generateContent and pass in the prompt + let response = try await model.generateContent(videoContent, prompt) + if let text = response.text { + print(text) + } + // [END text_gen_multimodal_video_prompt] + } + + func textAndVideoPromptStreaming() async throws { + // [START text_gen_multimodal_video_prompt_streaming] + guard let fileURL = Bundle.main.url(forResource: "sample", + withExtension: "mp4") else { fatalError() } + let video = try Data(contentsOf: fileURL) + let prompt = "What's in this video?" + let videoContent = InlineDataPart(data: video, mimeType: "video/mp4") + + // To stream generated text output, call generateContentStream and pass in the prompt + let contentStream = try model.generateContentStream(videoContent, prompt) + for try await chunk in contentStream { + if let text = chunk.text { + print(text) + } + } + // [END text_gen_multimodal_video_prompt_streaming] + } + + func chatStreaming() async throws { + // [START chat_streaming] + // Optionally specify existing chat history + let history = [ + ModelContent(role: "user", parts: "Hello, I have 2 dogs in my house."), + ModelContent(role: "model", parts: "Great to meet you. What would you like to know?"), + ] + + // Initialize the chat with optional chat history + let chat = model.startChat(history: history) + + // To stream generated text output, call sendMessageStream and pass in the message + let contentStream = try chat.sendMessageStream("How many paws are in my house?") + for try await chunk in contentStream { + if let text = chunk.text { + print(text) + } + } + // [END chat_streaming] + } + + func chat() async throws { + // [START chat] + // Optionally specify existing chat history + let history = [ + ModelContent(role: "user", parts: "Hello, I have 2 dogs in my house."), + ModelContent(role: "model", parts: "Great to meet you. What would you like to know?"), + ] + + // Initialize the chat with optional chat history + let chat = model.startChat(history: history) + + // To generate text output, call sendMessage and pass in the message + let response = try await chat.sendMessage("How many paws are in my house?") + if let text = response.text { + print(text) + } + // [END chat] + } + + func countTokensText() async throws { + // [START count_tokens_text] + let response = try await model.countTokens("Why is the sky blue?") + print("Total Tokens: \(response.totalTokens)") + print("Total Billable Characters: \(response.totalBillableCharacters ?? 0)") + // [END count_tokens_text] + } + + func countTokensTextAndImage() async throws { +#if canImport(UIKit) + guard let image = UIImage(named: "image") else { fatalError() } +#else + guard let image = NSImage(named: "image") else { fatalError() } +#endif + // [START count_tokens_text_image] + let response = try await model.countTokens(image, "What's in this picture?") + print("Total Tokens: \(response.totalTokens)") + print("Total Billable Characters: \(response.totalBillableCharacters ?? 0)") + // [END count_tokens_text_image] + } + + func countTokensMultiImage() async throws { +#if canImport(UIKit) + guard let image1 = UIImage(named: "image1") else { fatalError() } + guard let image2 = UIImage(named: "image2") else { fatalError() } +#else + guard let image1 = NSImage(named: "image1") else { fatalError() } + guard let image2 = NSImage(named: "image2") else { fatalError() } +#endif + // [START count_tokens_multi_image] + let response = try await model.countTokens(image1, image2, "What's in this picture?") + print("Total Tokens: \(response.totalTokens)") + print("Total Billable Characters: \(response.totalBillableCharacters ?? 0)") + // [END count_tokens_multi_image] + } + + func countTokensChat() async throws { + // [START count_tokens_chat] + let chat = model.startChat() + let history = chat.history + let message = ModelContent(role: "user", parts: "Why is the sky blue?") + let contents = history + [message] + let response = try await model.countTokens(contents) + print("Total Tokens: \(response.totalTokens)") + print("Total Billable Characters: \(response.totalBillableCharacters ?? 0)") + // [END count_tokens_chat] + } + + func setSafetySetting() { + // [START set_one_safety_setting] + let model = VertexAI.vertexAI().generativeModel( + modelName: "gemini-1.5-flash", + safetySettings: [ + SafetySetting(harmCategory: .harassment, threshold: .blockOnlyHigh) + ] + ) + // [END set_one_safety_setting] + } + + func setMultipleSafetySettings() { + // [START set_multi_safety_settings] + let harassmentSafety = SafetySetting(harmCategory: .harassment, threshold: .blockOnlyHigh) + let hateSpeechSafety = SafetySetting(harmCategory: .hateSpeech, threshold: .blockMediumAndAbove) + + let model = VertexAI.vertexAI().generativeModel( + modelName: "gemini-1.5-flash", + safetySettings: [harassmentSafety, hateSpeechSafety] + ) + // [END set_multi_safety_settings] + } + + func setSystemInstructions() { + // [START system_instructions_text] + // Initialize the Vertex AI service + let vertex = VertexAI.vertexAI() + + // Initialize the generative model + // Specify a model that supports system instructions, like a Gemini 1.5 model + let model = vertex.generativeModel( + modelName: "gemini-1.5-flash", + systemInstruction: ModelContent(role: "system", parts: "You are a cat. Your name is Neko.") + ) + // [END system_instructions_text] + } + + // MARK: - Function Calling + + func functionCalling() async throws { + // [START create_function] + func makeAPIRequest(currencyFrom: String, currencyTo: String) -> JSONObject { + // This hypothetical API returns a JSON such as: + // {"base":"USD","rates":{"SEK": 10.99}} + return [ + "base": .string(currencyFrom), + "rates": .object([currencyTo: .number(10.99)]), + ] + } + // [END create_function] + + // [START create_function_metadata] + let getExchangeRate = FunctionDeclaration( + name: "getExchangeRate", + description: "Get the exchange rate for currencies between countries", + parameters: [ + "currencyFrom": Schema.string( + description: "The currency to convert from." + ), + "currencyTo": Schema.string( + description: "The currency to convert to." + ), + ] + ) + // [END create_function_metadata] + + // [START initialize_model_function] + // Initialize the Vertex AI service + let vertex = VertexAI.vertexAI() + + // Initialize the generative model + // Use a model that supports function calling, like a Gemini 1.5 model. + let model = vertex.generativeModel( + modelName: "gemini-1.5-flash", + // Specify the function declaration. + tools: [Tool.functionDeclarations([getExchangeRate])] + ) + // [END initialize_model_function] + + // [START generate_function_call] + let chat = model.startChat() + + let prompt = "How much is 50 US dollars worth in Swedish krona?" + + // Send the message to the generative model + let response1 = try await chat.sendMessage(prompt) + + // Check if the model responded with a function call + guard let functionCall = response1.functionCalls.first else { + fatalError("Model did not respond with a function call.") + } + // Print an error if the returned function was not declared + guard functionCall.name == "getExchangeRate" else { + fatalError("Unexpected function called: \(functionCall.name)") + } + // Verify that the names and types of the parameters match the declaration + guard case let .string(currencyFrom) = functionCall.args["currencyFrom"] else { + fatalError("Missing argument: currencyFrom") + } + guard case let .string(currencyTo) = functionCall.args["currencyTo"] else { + fatalError("Missing argument: currencyTo") + } + + // Call the hypothetical API + let apiResponse = makeAPIRequest(currencyFrom: currencyFrom, currencyTo: currencyTo) + + // Send the API response back to the model so it can generate a text response that can be + // displayed to the user. + let response = try await chat.sendMessage([ModelContent( + role: "function", + parts: [FunctionResponsePart(name: functionCall.name, response: apiResponse)] + )]) + + // Log the text response. + guard let modelResponse = response.text else { + fatalError("Model did not respond with text.") + } + print(modelResponse) + // [END generate_function_call] + } + + func functionCallingModes() { + let getExchangeRate = FunctionDeclaration( + name: "getExchangeRate", + description: "Get the exchange rate for currencies between countries", + parameters: [:] + ) + + // [START function_modes] + let model = VertexAI.vertexAI().generativeModel( + // Setting a function calling mode is only available in Gemini 1.5 Pro + modelName: "gemini-1.5-pro", + // Pass the function declaration + tools: [Tool.functionDeclarations([getExchangeRate])], + toolConfig: ToolConfig( + // Only call functions (model won't generate text) + functionCallingConfig: FunctionCallingConfig.any( + // This should only be set when the Mode is .any. + allowedFunctionNames: ["getExchangeRate"] + ) + ) + ) + // [END function_modes] + } + +} diff --git a/vertexai/VertexAISnippets/VertexAISnippetsApp.swift b/vertexai/VertexAISnippets/VertexAISnippetsApp.swift new file mode 100644 index 00000000..cc626444 --- /dev/null +++ b/vertexai/VertexAISnippets/VertexAISnippetsApp.swift @@ -0,0 +1,24 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftUI + +@main +struct VertexAISnippetsApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +}