aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Paul Beusterien <paulbeusterien@google.com>2018-03-20 11:59:03 -0700
committerGravatar GitHub <noreply@github.com>2018-03-20 11:59:03 -0700
commitb7f35a0b76bb2afd682b806d2b25568611612557 (patch)
treedeb4577d3e54c3fafa2a065605faef228a186c1b
parent7e65885762757209e0e14ec28e99ec91380e9c2f (diff)
Initial Firebase Functions (#948)
-rw-r--r--FirebaseFunctions.podspec33
-rw-r--r--Functions/.clang-format7
-rw-r--r--Functions/Backend/index.js95
-rw-r--r--Functions/Backend/package.json12
-rwxr-xr-xFunctions/Backend/start.sh41
-rw-r--r--Functions/CHANGELOG.md2
-rw-r--r--Functions/Example/FirebaseFunctions.xcodeproj/project.pbxproj848
-rw-r--r--Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions-Example.xcscheme113
-rw-r--r--Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_IntegrationTests.xcscheme56
-rw-r--r--Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_Tests.xcscheme56
-rw-r--r--Functions/Example/FirebaseFunctions/Base.lproj/LaunchScreen.storyboard27
-rw-r--r--Functions/Example/FirebaseFunctions/Base.lproj/Main.storyboard27
-rw-r--r--Functions/Example/FirebaseFunctions/FIRAppDelegate.h21
-rw-r--r--Functions/Example/FirebaseFunctions/FIRAppDelegate.m52
-rw-r--r--Functions/Example/FirebaseFunctions/FIRViewController.h19
-rw-r--r--Functions/Example/FirebaseFunctions/FIRViewController.m35
-rw-r--r--Functions/Example/FirebaseFunctions/FirebaseFunctions-Info.plist54
-rw-r--r--Functions/Example/FirebaseFunctions/Images.xcassets/AppIcon.appiconset/Contents.json93
-rw-r--r--Functions/Example/FirebaseFunctions/en.lproj/InfoPlist.strings2
-rw-r--r--Functions/Example/FirebaseFunctions/main.m23
-rw-r--r--Functions/Example/IntegrationTests/FIRIntegrationTests.m190
-rw-r--r--Functions/Example/IntegrationTests/IntegrationTests-Info.plist22
-rw-r--r--Functions/Example/Podfile16
-rw-r--r--Functions/Example/TestUtils/FUNFakeApp.h39
-rw-r--r--Functions/Example/TestUtils/FUNFakeApp.m71
-rw-r--r--Functions/Example/TestUtils/FUNFakeInstanceID.h34
-rw-r--r--Functions/Example/TestUtils/FUNFakeInstanceID.m33
-rw-r--r--Functions/Example/Tests/FIRFunctionsTests.m50
-rw-r--r--Functions/Example/Tests/FUNSerializerTests.m240
-rw-r--r--Functions/Example/Tests/Tests-Info.plist22
-rw-r--r--Functions/Example/Tests/en.lproj/InfoPlist.strings2
-rw-r--r--Functions/FirebaseFunctions/FIRFunctions+Internal.h50
-rw-r--r--Functions/FirebaseFunctions/FIRFunctions.m247
-rw-r--r--Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h46
-rw-r--r--Functions/FirebaseFunctions/FIRHTTPSCallable.m71
-rw-r--r--Functions/FirebaseFunctions/FUNContext.h41
-rw-r--r--Functions/FirebaseFunctions/FUNContext.m84
-rw-r--r--Functions/FirebaseFunctions/FUNError.h31
-rw-r--r--Functions/FirebaseFunctions/FUNError.m186
-rw-r--r--Functions/FirebaseFunctions/FUNInstanceIDProxy.h29
-rw-r--r--Functions/FirebaseFunctions/FUNInstanceIDProxy.m57
-rw-r--r--Functions/FirebaseFunctions/FUNSerializer.h33
-rw-r--r--Functions/FirebaseFunctions/FUNSerializer.m231
-rw-r--r--Functions/FirebaseFunctions/FUNUsageValidation.h38
-rw-r--r--Functions/FirebaseFunctions/FUNUsageValidation.m28
-rw-r--r--Functions/FirebaseFunctions/Public/FIRError.h90
-rw-r--r--Functions/FirebaseFunctions/Public/FIRFunctions.h66
-rw-r--r--Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h94
-rw-r--r--Functions/README.md21
-rw-r--r--README.md14
50 files changed, 3786 insertions, 6 deletions
diff --git a/FirebaseFunctions.podspec b/FirebaseFunctions.podspec
new file mode 100644
index 0000000..caf3023
--- /dev/null
+++ b/FirebaseFunctions.podspec
@@ -0,0 +1,33 @@
+#
+# Be sure to run `pod lib lint FirebaseFunctions.podspec' to ensure this is a
+# valid spec before submitting.
+#
+# Any lines starting with a # are optional, but their use is encouraged
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
+#
+
+Pod::Spec.new do |s|
+ s.name = 'FirebaseFunctions'
+ s.version = '1.0.0'
+ s.summary = 'Cloud Functions for Firebase iOS SDK.'
+
+ s.description = <<-DESC
+iOS SDK for Cloud Functions for Firebase.
+ DESC
+
+ s.homepage = 'https://developers.google.com/'
+ s.authors = 'Google, Inc.'
+ s.source = { :git => 'https://github.com/TBD/FirebaseFunctions.git', :tag => s.version.to_s }
+
+ s.ios.deployment_target = '8.0'
+
+ s.cocoapods_version = '>= 1.4.0'
+ s.static_framework = true
+ s.prefix_header_file = false
+
+ s.source_files = 'Functions/FirebaseFunctions/**/*'
+ s.public_header_files = 'Functions/FirebaseFunctions/Public/*.h'
+
+ s.dependency 'FirebaseCore', '~> 4.0'
+ s.dependency 'GTMSessionFetcher/Core', '~> 1.1'
+end
diff --git a/Functions/.clang-format b/Functions/.clang-format
new file mode 100644
index 0000000..fb06210
--- /dev/null
+++ b/Functions/.clang-format
@@ -0,0 +1,7 @@
+BasedOnStyle: Google
+ColumnLimit: 100
+BinPackParameters: false
+AllowAllParametersOfDeclarationOnNextLine: true
+ObjCSpaceBeforeProtocolList: false
+SpacesInContainerLiterals: true
+PointerAlignment: Right
diff --git a/Functions/Backend/index.js b/Functions/Backend/index.js
new file mode 100644
index 0000000..0245f1b
--- /dev/null
+++ b/Functions/Backend/index.js
@@ -0,0 +1,95 @@
+const assert = require('assert');
+const functions = require('firebase-functions');
+
+exports.dataTest = functions.https.onRequest((request, response) => {
+ assert.deepEqual(request.body, {
+ data: {
+ bool: true,
+ int: 2,
+ long: {
+ value: '3',
+ '@type': 'type.googleapis.com/google.protobuf.Int64Value',
+ },
+ string: 'four',
+ array: [5, 6],
+ 'null': null,
+ }
+ });
+ response.send({
+ data: {
+ message: 'stub response',
+ code: 42,
+ long: {
+ value: '420',
+ '@type': 'type.googleapis.com/google.protobuf.Int64Value',
+ },
+ }
+ });
+});
+
+exports.scalarTest = functions.https.onRequest((request, response) => {
+ assert.deepEqual(request.body, { data: 17 });
+ response.send({ data: 76 });
+});
+
+exports.tokenTest = functions.https.onRequest((request, response) => {
+ assert.equal('Bearer token', request.get('Authorization'));
+ assert.deepEqual(request.body, { data: {} });
+ response.send({ data: {} });
+});
+
+exports.instanceIdTest = functions.https.onRequest((request, response) => {
+ assert.equal(request.get('Firebase-Instance-ID-Token'), 'iid');
+ assert.deepEqual(request.body, { data: {} });
+ response.send({ data: {} });
+});
+
+exports.nullTest = functions.https.onRequest((request, response) => {
+ assert.deepEqual(request.body, { data: null });
+ response.send({ data: null });
+});
+
+exports.missingResultTest = functions.https.onRequest((request, response) => {
+ assert.deepEqual(request.body, { data: null });
+ response.send({});
+});
+
+exports.unhandledErrorTest = functions.https.onRequest((request, response) => {
+ // Fail in a way that the client shouldn't see.
+ throw 'nope';
+});
+
+exports.unknownErrorTest = functions.https.onRequest((request, response) => {
+ // Send an http error with a body with an explicit code.
+ response.status(400).send({
+ error: {
+ status: 'THIS_IS_NOT_VALID',
+ message: 'this should be ignored',
+ },
+ });
+});
+
+exports.explicitErrorTest = functions.https.onRequest((request, response) => {
+ // Send an http error with a body with an explicit code.
+ // Note that eventually the SDK will have a helper to automatically return
+ // the appropriate http status code for an error.
+ response.status(400).send({
+ error: {
+ status: 'OUT_OF_RANGE',
+ message: 'explicit nope',
+ details: {
+ start: 10,
+ end: 20,
+ long: {
+ value: '30',
+ '@type': 'type.googleapis.com/google.protobuf.Int64Value',
+ },
+ },
+ },
+ });
+});
+
+exports.httpErrorTest = functions.https.onRequest((request, response) => {
+ // Send an http error with no body.
+ response.status(400).send();
+});
diff --git a/Functions/Backend/package.json b/Functions/Backend/package.json
new file mode 100644
index 0000000..c227bb1
--- /dev/null
+++ b/Functions/Backend/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "functions",
+ "description": "Cloud Functions for Firebase",
+ "dependencies": {
+ "firebase-admin": "~4.2.1",
+ "firebase-functions": "^0.5.7"
+ },
+ "private": true,
+ "devDependencies": {
+ "@google-cloud/functions-emulator": "^1.0.0-alpha.21"
+ }
+}
diff --git a/Functions/Backend/start.sh b/Functions/Backend/start.sh
new file mode 100755
index 0000000..aaa2b73
--- /dev/null
+++ b/Functions/Backend/start.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Sets up a project with the functions CLI and starts a backend to run
+# integration tests against.
+
+set -e
+
+# Get the absolute path to the directory containing this script.
+SCRIPT_DIR="$(cd $(dirname ${BASH_SOURCE[0]}) && pwd)"
+TEMP_DIR="$(mktemp -d -t firebase-functions)"
+echo "Creating functions in ${TEMP_DIR}"
+
+# Set up the functions directory.
+cp "${SCRIPT_DIR}/index.js" "${TEMP_DIR}/"
+cp "${SCRIPT_DIR}/package.json" "${TEMP_DIR}/"
+cd "${TEMP_DIR}"
+npm install
+
+# Start the server.
+FUNCTIONS_BIN="./node_modules/.bin/functions"
+"${FUNCTIONS_BIN}" config set projectId functions-integration-test
+"${FUNCTIONS_BIN}" config set supervisorPort 5005
+"${FUNCTIONS_BIN}" config set region us-central1
+"${FUNCTIONS_BIN}" config set verbose true
+"${FUNCTIONS_BIN}" restart
+"${FUNCTIONS_BIN}" deploy dataTest --trigger-http
+"${FUNCTIONS_BIN}" deploy scalarTest --trigger-http
+"${FUNCTIONS_BIN}" deploy tokenTest --trigger-http
+"${FUNCTIONS_BIN}" deploy instanceIdTest --trigger-http
+"${FUNCTIONS_BIN}" deploy nullTest --trigger-http
+"${FUNCTIONS_BIN}" deploy missingResultTest --trigger-http
+"${FUNCTIONS_BIN}" deploy unhandledErrorTest --trigger-http
+"${FUNCTIONS_BIN}" deploy unknownErrorTest --trigger-http
+"${FUNCTIONS_BIN}" deploy explicitErrorTest --trigger-http
+"${FUNCTIONS_BIN}" deploy httpErrorTest --trigger-http
+
+# Wait for the user to tell us to stop the server.
+echo "Functions emulator now running in ${TEMP_DIR}."
+read -n 1 -p "*** Press any key to stop the server. ***"
+echo "\nStopping the emulator..."
+"${FUNCTIONS_BIN}" stop
diff --git a/Functions/CHANGELOG.md b/Functions/CHANGELOG.md
new file mode 100644
index 0000000..d2ea864
--- /dev/null
+++ b/Functions/CHANGELOG.md
@@ -0,0 +1,2 @@
+# v1.0.0
+- Initial public release \ No newline at end of file
diff --git a/Functions/Example/FirebaseFunctions.xcodeproj/project.pbxproj b/Functions/Example/FirebaseFunctions.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..d70f4b5
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions.xcodeproj/project.pbxproj
@@ -0,0 +1,848 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
+ 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; };
+ 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
+ 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; };
+ 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; };
+ 6003F59E195388D20070C39A /* FIRAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* FIRAppDelegate.m */; };
+ 6003F5A7195388D20070C39A /* FIRViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* FIRViewController.m */; };
+ 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; };
+ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
+ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
+ 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
+ 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; };
+ 6003F5BC195388D20070C39A /* FIRFunctionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* FIRFunctionsTests.m */; };
+ 633D50991C0FDF68085030B5 /* Pods_FirebaseFunctions_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B954281766FFD7C7C9970E9B /* Pods_FirebaseFunctions_Example.framework */; };
+ 6358342A9F1AF395C6C52471 /* Pods_FirebaseFunctions_IntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A4C1781DC97515D32B33DBD /* Pods_FirebaseFunctions_IntegrationTests.framework */; };
+ 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; };
+ 78F883E44C88CC18414F3DAF /* Pods_FirebaseFunctions_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A19679D6C21E3206B4A13697 /* Pods_FirebaseFunctions_Tests.framework */; };
+ 7C58B03A1F1441F0005ED954 /* FUNSerializerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C58B0391F1441F0005ED954 /* FUNSerializerTests.m */; };
+ 7CBFAA82205702AB00A65866 /* FIRIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7C9BFD3F1F10A12F001A19ED /* FIRIntegrationTests.m */; };
+ 7CBFAA86205702AB00A65866 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
+ 7CBFAA87205702AB00A65866 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
+ 7CBFAA88205702AB00A65866 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
+ 7CBFAA89205702AB00A65866 /* Pods_FirebaseFunctions_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A19679D6C21E3206B4A13697 /* Pods_FirebaseFunctions_Tests.framework */; };
+ 7CBFAA8B205702AB00A65866 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; };
+ 7CF3BEC21F97EE2F00B16B6E /* FUNFakeInstanceID.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CF3BEC01F97EE2F00B16B6E /* FUNFakeInstanceID.m */; };
+ 7CF563091F1FE70600FEE1F4 /* FUNFakeApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 7CF563081F1FE70600FEE1F4 /* FUNFakeApp.m */; };
+ 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 6003F582195388D10070C39A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 6003F589195388D20070C39A;
+ remoteInfo = FirebaseFunctions;
+ };
+ 7CBFAA7F205702AB00A65866 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 6003F582195388D10070C39A /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 6003F589195388D20070C39A;
+ remoteInfo = FirebaseFunctions;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 0A4C1781DC97515D32B33DBD /* Pods_FirebaseFunctions_IntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FirebaseFunctions_IntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 501CFEC7487BD4C12FCEAB03 /* Pods-FirebaseFunctions_IntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseFunctions_IntegrationTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FirebaseFunctions_IntegrationTests/Pods-FirebaseFunctions_IntegrationTests.debug.xcconfig"; sourceTree = "<group>"; };
+ 59519EC771AF4770F85B6534 /* Pods-FirebaseFunctions_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseFunctions_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FirebaseFunctions_Example/Pods-FirebaseFunctions_Example.debug.xcconfig"; sourceTree = "<group>"; };
+ 5B43C813C653DE3455952E9B /* Pods-FirebaseFunctions_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseFunctions_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-FirebaseFunctions_Tests/Pods-FirebaseFunctions_Tests.release.xcconfig"; sourceTree = "<group>"; };
+ 6003F58A195388D20070C39A /* FirebaseFunctions_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FirebaseFunctions_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ 6003F595195388D20070C39A /* FirebaseFunctions-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "FirebaseFunctions-Info.plist"; sourceTree = "<group>"; };
+ 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ 6003F59C195388D20070C39A /* FIRAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FIRAppDelegate.h; sourceTree = "<group>"; };
+ 6003F59D195388D20070C39A /* FIRAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRAppDelegate.m; sourceTree = "<group>"; };
+ 6003F5A5195388D20070C39A /* FIRViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FIRViewController.h; sourceTree = "<group>"; };
+ 6003F5A6195388D20070C39A /* FIRViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRViewController.m; sourceTree = "<group>"; };
+ 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+ 6003F5AE195388D20070C39A /* FirebaseFunctions_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FirebaseFunctions_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
+ 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; };
+ 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 6003F5BB195388D20070C39A /* FIRFunctionsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRFunctionsTests.m; sourceTree = "<group>"; };
+ 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+ 7BEA793625C8DE7C8EC60006 /* FirebaseFunctions.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = FirebaseFunctions.podspec; path = ../FirebaseFunctions.podspec; sourceTree = "<group>"; };
+ 7C58B0391F1441F0005ED954 /* FUNSerializerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FUNSerializerTests.m; sourceTree = "<group>"; };
+ 7C9BFD3F1F10A12F001A19ED /* FIRIntegrationTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIRIntegrationTests.m; sourceTree = "<group>"; };
+ 7CBFAA91205702AB00A65866 /* FirebaseFunctions_IntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FirebaseFunctions_IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7CBFAA92205702AC00A65866 /* FirebaseFunctions_Tests copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "FirebaseFunctions_Tests copy-Info.plist"; path = "/Users/klimt/src/firebase-ios-sdk/Functions/Example/FirebaseFunctions_Tests copy-Info.plist"; sourceTree = "<absolute>"; };
+ 7CF3BEC01F97EE2F00B16B6E /* FUNFakeInstanceID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FUNFakeInstanceID.m; sourceTree = "<group>"; };
+ 7CF3BEC11F97EE2F00B16B6E /* FUNFakeInstanceID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FUNFakeInstanceID.h; sourceTree = "<group>"; };
+ 7CF563081F1FE70600FEE1F4 /* FUNFakeApp.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FUNFakeApp.m; sourceTree = "<group>"; };
+ 7CF5630A1F1FE76700FEE1F4 /* FUNFakeApp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FUNFakeApp.h; sourceTree = "<group>"; };
+ 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ A19679D6C21E3206B4A13697 /* Pods_FirebaseFunctions_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FirebaseFunctions_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ B954281766FFD7C7C9970E9B /* Pods_FirebaseFunctions_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FirebaseFunctions_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ E0A8D570636E99E7C3396DF8 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
+ E2E6249788F1618471335BE7 /* Pods-FirebaseFunctions_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseFunctions_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-FirebaseFunctions_Example/Pods-FirebaseFunctions_Example.release.xcconfig"; sourceTree = "<group>"; };
+ F1F2A7C03C10A3A03F9502B8 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
+ F840F9EDCB950E7509527AB7 /* Pods-FirebaseFunctions_IntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseFunctions_IntegrationTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-FirebaseFunctions_IntegrationTests/Pods-FirebaseFunctions_IntegrationTests.release.xcconfig"; sourceTree = "<group>"; };
+ FFE894EDD87F1EF24F0ED8DD /* Pods-FirebaseFunctions_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseFunctions_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-FirebaseFunctions_Tests/Pods-FirebaseFunctions_Tests.debug.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 6003F587195388D20070C39A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */,
+ 6003F592195388D20070C39A /* UIKit.framework in Frameworks */,
+ 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */,
+ 633D50991C0FDF68085030B5 /* Pods_FirebaseFunctions_Example.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6003F5AB195388D20070C39A /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */,
+ 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */,
+ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */,
+ 78F883E44C88CC18414F3DAF /* Pods_FirebaseFunctions_Tests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7CBFAA85205702AB00A65866 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7CBFAA86205702AB00A65866 /* XCTest.framework in Frameworks */,
+ 7CBFAA87205702AB00A65866 /* UIKit.framework in Frameworks */,
+ 7CBFAA88205702AB00A65866 /* Foundation.framework in Frameworks */,
+ 7CBFAA89205702AB00A65866 /* Pods_FirebaseFunctions_Tests.framework in Frameworks */,
+ 6358342A9F1AF395C6C52471 /* Pods_FirebaseFunctions_IntegrationTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 004DC60D1A62294B06E83453 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 59519EC771AF4770F85B6534 /* Pods-FirebaseFunctions_Example.debug.xcconfig */,
+ E2E6249788F1618471335BE7 /* Pods-FirebaseFunctions_Example.release.xcconfig */,
+ FFE894EDD87F1EF24F0ED8DD /* Pods-FirebaseFunctions_Tests.debug.xcconfig */,
+ 5B43C813C653DE3455952E9B /* Pods-FirebaseFunctions_Tests.release.xcconfig */,
+ 501CFEC7487BD4C12FCEAB03 /* Pods-FirebaseFunctions_IntegrationTests.debug.xcconfig */,
+ F840F9EDCB950E7509527AB7 /* Pods-FirebaseFunctions_IntegrationTests.release.xcconfig */,
+ );
+ name = Pods;
+ sourceTree = "<group>";
+ };
+ 6003F581195388D10070C39A = {
+ isa = PBXGroup;
+ children = (
+ 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */,
+ 6003F593195388D20070C39A /* Example for FirebaseFunctions */,
+ 7C70065B20572C1B007A5573 /* TestUtils */,
+ 7CFC6F8720570365005630C2 /* IntegrationTests */,
+ 6003F5B5195388D20070C39A /* Tests */,
+ 6003F58C195388D20070C39A /* Frameworks */,
+ 6003F58B195388D20070C39A /* Products */,
+ 004DC60D1A62294B06E83453 /* Pods */,
+ 7CBFAA92205702AC00A65866 /* FirebaseFunctions_Tests copy-Info.plist */,
+ );
+ sourceTree = "<group>";
+ };
+ 6003F58B195388D20070C39A /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 6003F58A195388D20070C39A /* FirebaseFunctions_Example.app */,
+ 6003F5AE195388D20070C39A /* FirebaseFunctions_Tests.xctest */,
+ 7CBFAA91205702AB00A65866 /* FirebaseFunctions_IntegrationTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 6003F58C195388D20070C39A /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 6003F58D195388D20070C39A /* Foundation.framework */,
+ 6003F58F195388D20070C39A /* CoreGraphics.framework */,
+ 6003F591195388D20070C39A /* UIKit.framework */,
+ 6003F5AF195388D20070C39A /* XCTest.framework */,
+ B954281766FFD7C7C9970E9B /* Pods_FirebaseFunctions_Example.framework */,
+ A19679D6C21E3206B4A13697 /* Pods_FirebaseFunctions_Tests.framework */,
+ 0A4C1781DC97515D32B33DBD /* Pods_FirebaseFunctions_IntegrationTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ 6003F593195388D20070C39A /* Example for FirebaseFunctions */ = {
+ isa = PBXGroup;
+ children = (
+ 6003F59C195388D20070C39A /* FIRAppDelegate.h */,
+ 6003F59D195388D20070C39A /* FIRAppDelegate.m */,
+ 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */,
+ 6003F5A5195388D20070C39A /* FIRViewController.h */,
+ 6003F5A6195388D20070C39A /* FIRViewController.m */,
+ 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */,
+ 6003F5A8195388D20070C39A /* Images.xcassets */,
+ 6003F594195388D20070C39A /* Supporting Files */,
+ );
+ name = "Example for FirebaseFunctions";
+ path = FirebaseFunctions;
+ sourceTree = "<group>";
+ };
+ 6003F594195388D20070C39A /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 6003F595195388D20070C39A /* FirebaseFunctions-Info.plist */,
+ 6003F596195388D20070C39A /* InfoPlist.strings */,
+ 6003F599195388D20070C39A /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 6003F5B5195388D20070C39A /* Tests */ = {
+ isa = PBXGroup;
+ children = (
+ 6003F5BB195388D20070C39A /* FIRFunctionsTests.m */,
+ 7C58B0391F1441F0005ED954 /* FUNSerializerTests.m */,
+ 6003F5B6195388D20070C39A /* Supporting Files */,
+ );
+ path = Tests;
+ sourceTree = "<group>";
+ };
+ 6003F5B6195388D20070C39A /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 6003F5B7195388D20070C39A /* Tests-Info.plist */,
+ 6003F5B8195388D20070C39A /* InfoPlist.strings */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = {
+ isa = PBXGroup;
+ children = (
+ 7BEA793625C8DE7C8EC60006 /* FirebaseFunctions.podspec */,
+ E0A8D570636E99E7C3396DF8 /* README.md */,
+ F1F2A7C03C10A3A03F9502B8 /* LICENSE */,
+ );
+ name = "Podspec Metadata";
+ sourceTree = "<group>";
+ };
+ 7C70065B20572C1B007A5573 /* TestUtils */ = {
+ isa = PBXGroup;
+ children = (
+ 7CF5630A1F1FE76700FEE1F4 /* FUNFakeApp.h */,
+ 7CF563081F1FE70600FEE1F4 /* FUNFakeApp.m */,
+ 7CF3BEC11F97EE2F00B16B6E /* FUNFakeInstanceID.h */,
+ 7CF3BEC01F97EE2F00B16B6E /* FUNFakeInstanceID.m */,
+ );
+ path = TestUtils;
+ sourceTree = "<group>";
+ };
+ 7CFC6F8720570365005630C2 /* IntegrationTests */ = {
+ isa = PBXGroup;
+ children = (
+ 7C9BFD3F1F10A12F001A19ED /* FIRIntegrationTests.m */,
+ );
+ path = IntegrationTests;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 6003F589195388D20070C39A /* FirebaseFunctions_Example */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "FirebaseFunctions_Example" */;
+ buildPhases = (
+ C1B1E870DA1D732EC2F1F946 /* [CP] Check Pods Manifest.lock */,
+ 6003F586195388D20070C39A /* Sources */,
+ 6003F587195388D20070C39A /* Frameworks */,
+ 6003F588195388D20070C39A /* Resources */,
+ BB7A7FC40453827CC7541BB7 /* [CP] Embed Pods Frameworks */,
+ 0409BC62B2C369D14416E480 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = FirebaseFunctions_Example;
+ productName = FirebaseFunctions;
+ productReference = 6003F58A195388D20070C39A /* FirebaseFunctions_Example.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 6003F5AD195388D20070C39A /* FirebaseFunctions_Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "FirebaseFunctions_Tests" */;
+ buildPhases = (
+ D9E332DA9E27ECA02C2B45D2 /* [CP] Check Pods Manifest.lock */,
+ 6003F5AA195388D20070C39A /* Sources */,
+ 6003F5AB195388D20070C39A /* Frameworks */,
+ 6003F5AC195388D20070C39A /* Resources */,
+ 3B7914C40161367B84BC9E52 /* [CP] Embed Pods Frameworks */,
+ F0A35EE9B43049BA133AD88F /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 6003F5B4195388D20070C39A /* PBXTargetDependency */,
+ );
+ name = FirebaseFunctions_Tests;
+ productName = FirebaseFunctionsTests;
+ productReference = 6003F5AE195388D20070C39A /* FirebaseFunctions_Tests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 7CBFAA7D205702AB00A65866 /* FirebaseFunctions_IntegrationTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7CBFAA8E205702AB00A65866 /* Build configuration list for PBXNativeTarget "FirebaseFunctions_IntegrationTests" */;
+ buildPhases = (
+ 7CBFAA80205702AB00A65866 /* [CP] Check Pods Manifest.lock */,
+ 7CBFAA81205702AB00A65866 /* Sources */,
+ 7CBFAA85205702AB00A65866 /* Frameworks */,
+ 7CBFAA8A205702AB00A65866 /* Resources */,
+ 7CBFAA8C205702AB00A65866 /* [CP] Embed Pods Frameworks */,
+ 7CBFAA8D205702AB00A65866 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 7CBFAA7E205702AB00A65866 /* PBXTargetDependency */,
+ );
+ name = FirebaseFunctions_IntegrationTests;
+ productName = FirebaseFunctionsTests;
+ productReference = 7CBFAA91205702AB00A65866 /* FirebaseFunctions_IntegrationTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 6003F582195388D10070C39A /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ CLASSPREFIX = FIR;
+ LastUpgradeCheck = 0720;
+ ORGANIZATIONNAME = "Google, Inc.";
+ TargetAttributes = {
+ 6003F5AD195388D20070C39A = {
+ TestTargetID = 6003F589195388D20070C39A;
+ };
+ };
+ };
+ buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "FirebaseFunctions" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 6003F581195388D10070C39A;
+ productRefGroup = 6003F58B195388D20070C39A /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 6003F589195388D20070C39A /* FirebaseFunctions_Example */,
+ 6003F5AD195388D20070C39A /* FirebaseFunctions_Tests */,
+ 7CBFAA7D205702AB00A65866 /* FirebaseFunctions_IntegrationTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 6003F588195388D20070C39A /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */,
+ 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */,
+ 6003F5A9195388D20070C39A /* Images.xcassets in Resources */,
+ 6003F598195388D20070C39A /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6003F5AC195388D20070C39A /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7CBFAA8A205702AB00A65866 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7CBFAA8B205702AB00A65866 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 0409BC62B2C369D14416E480 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FirebaseFunctions_Example/Pods-FirebaseFunctions_Example-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3B7914C40161367B84BC9E52 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FirebaseFunctions_Tests/Pods-FirebaseFunctions_Tests-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 7CBFAA80205702AB00A65866 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-FirebaseFunctions_IntegrationTests-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;
+ };
+ 7CBFAA8C205702AB00A65866 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FirebaseFunctions_IntegrationTests/Pods-FirebaseFunctions_IntegrationTests-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 7CBFAA8D205702AB00A65866 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FirebaseFunctions_IntegrationTests/Pods-FirebaseFunctions_IntegrationTests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ BB7A7FC40453827CC7541BB7 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${SRCROOT}/Pods/Target Support Files/Pods-FirebaseFunctions_Example/Pods-FirebaseFunctions_Example-frameworks.sh",
+ "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework",
+ "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework",
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FirebaseFunctions_Example/Pods-FirebaseFunctions_Example-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ C1B1E870DA1D732EC2F1F946 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-FirebaseFunctions_Example-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;
+ };
+ D9E332DA9E27ECA02C2B45D2 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-FirebaseFunctions_Tests-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;
+ };
+ F0A35EE9B43049BA133AD88F /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-FirebaseFunctions_Tests/Pods-FirebaseFunctions_Tests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 6003F586195388D20070C39A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7CF563091F1FE70600FEE1F4 /* FUNFakeApp.m in Sources */,
+ 6003F59E195388D20070C39A /* FIRAppDelegate.m in Sources */,
+ 6003F5A7195388D20070C39A /* FIRViewController.m in Sources */,
+ 7CF3BEC21F97EE2F00B16B6E /* FUNFakeInstanceID.m in Sources */,
+ 6003F59A195388D20070C39A /* main.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 6003F5AA195388D20070C39A /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7C58B03A1F1441F0005ED954 /* FUNSerializerTests.m in Sources */,
+ 6003F5BC195388D20070C39A /* FIRFunctionsTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 7CBFAA81205702AB00A65866 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7CBFAA82205702AB00A65866 /* FIRIntegrationTests.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 6003F5B4195388D20070C39A /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 6003F589195388D20070C39A /* FirebaseFunctions_Example */;
+ targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */;
+ };
+ 7CBFAA7E205702AB00A65866 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 6003F589195388D20070C39A /* FirebaseFunctions_Example */;
+ targetProxy = 7CBFAA7F205702AB00A65866 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 6003F596195388D20070C39A /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 6003F597195388D20070C39A /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 6003F5B8195388D20070C39A /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 6003F5B9195388D20070C39A /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 71719F9E1E33DC2100824A3D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 6003F5BD195388D20070C39A /* 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_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ 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.3;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 6003F5BE195388D20070C39A /* 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_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ 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.3;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 6003F5C0195388D20070C39A /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 59519EC771AF4770F85B6534 /* Pods-FirebaseFunctions_Example.debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ INFOPLIST_FILE = "FirebaseFunctions/FirebaseFunctions-Info.plist";
+ MODULE_NAME = ExampleApp;
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ 6003F5C1195388D20070C39A /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = E2E6249788F1618471335BE7 /* Pods-FirebaseFunctions_Example.release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ INFOPLIST_FILE = "FirebaseFunctions/FirebaseFunctions-Info.plist";
+ MODULE_NAME = ExampleApp;
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ 6003F5C3195388D20070C39A /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = FFE894EDD87F1EF24F0ED8DD /* Pods-FirebaseFunctions_Tests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/../FirebaseFunctions";
+ INFOPLIST_FILE = "Tests/Tests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseFunctions_Example.app/FirebaseFunctions_Example";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Debug;
+ };
+ 6003F5C4195388D20070C39A /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 5B43C813C653DE3455952E9B /* Pods-FirebaseFunctions_Tests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/../FirebaseFunctions";
+ INFOPLIST_FILE = "Tests/Tests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseFunctions_Example.app/FirebaseFunctions_Example";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Release;
+ };
+ 7CBFAA8F205702AB00A65866 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 501CFEC7487BD4C12FCEAB03 /* Pods-FirebaseFunctions_IntegrationTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/../FirebaseFunctions";
+ INFOPLIST_FILE = "IntegrationTests/IntegrationTests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseFunctions_Example.app/FirebaseFunctions_Example";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Debug;
+ };
+ 7CBFAA90205702AB00A65866 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = F840F9EDCB950E7509527AB7 /* Pods-FirebaseFunctions_IntegrationTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(inherited)",
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = NO;
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/../FirebaseFunctions";
+ INFOPLIST_FILE = "IntegrationTests/IntegrationTests-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseFunctions_Example.app/FirebaseFunctions_Example";
+ WRAPPER_EXTENSION = xctest;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 6003F585195388D10070C39A /* Build configuration list for PBXProject "FirebaseFunctions" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6003F5BD195388D20070C39A /* Debug */,
+ 6003F5BE195388D20070C39A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "FirebaseFunctions_Example" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6003F5C0195388D20070C39A /* Debug */,
+ 6003F5C1195388D20070C39A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "FirebaseFunctions_Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 6003F5C3195388D20070C39A /* Debug */,
+ 6003F5C4195388D20070C39A /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 7CBFAA8E205702AB00A65866 /* Build configuration list for PBXNativeTarget "FirebaseFunctions_IntegrationTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7CBFAA8F205702AB00A65866 /* Debug */,
+ 7CBFAA90205702AB00A65866 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 6003F582195388D10070C39A /* Project object */;
+}
diff --git a/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions-Example.xcscheme b/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions-Example.xcscheme
new file mode 100644
index 0000000..b9983e8
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions-Example.xcscheme
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0720"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6003F589195388D20070C39A"
+ BuildableName = "FirebaseFunctions_Example.app"
+ BlueprintName = "FirebaseFunctions_Example"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6003F5AD195388D20070C39A"
+ BuildableName = "FirebaseFunctions_Tests.xctest"
+ BlueprintName = "FirebaseFunctions_Tests"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "7CBFAA7D205702AB00A65866"
+ BuildableName = "FirebaseFunctions_IntegrationTests.xctest"
+ BlueprintName = "FirebaseFunctions_IntegrationTests"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6003F589195388D20070C39A"
+ BuildableName = "FirebaseFunctions_Example.app"
+ BlueprintName = "FirebaseFunctions_Example"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ language = ""
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6003F589195388D20070C39A"
+ BuildableName = "FirebaseFunctions_Example.app"
+ BlueprintName = "FirebaseFunctions_Example"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6003F589195388D20070C39A"
+ BuildableName = "FirebaseFunctions_Example.app"
+ BlueprintName = "FirebaseFunctions_Example"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_IntegrationTests.xcscheme b/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_IntegrationTests.xcscheme
new file mode 100644
index 0000000..46914fb
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_IntegrationTests.xcscheme
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0930"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "7CBFAA7D205702AB00A65866"
+ BuildableName = "FirebaseFunctions_IntegrationTests.xctest"
+ BlueprintName = "FirebaseFunctions_IntegrationTests"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_Tests.xcscheme b/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_Tests.xcscheme
new file mode 100644
index 0000000..26063f9
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions.xcodeproj/xcshareddata/xcschemes/FirebaseFunctions_Tests.xcscheme
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "0930"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <Testables>
+ <TestableReference
+ skipped = "NO">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "6003F5AD195388D20070C39A"
+ BuildableName = "FirebaseFunctions_Tests.xctest"
+ BlueprintName = "FirebaseFunctions_Tests"
+ ReferencedContainer = "container:FirebaseFunctions.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ allowLocationSimulation = "YES">
+ <AdditionalOptions>
+ </AdditionalOptions>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Release"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/Functions/Example/FirebaseFunctions/Base.lproj/LaunchScreen.storyboard b/Functions/Example/FirebaseFunctions/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..66a7681
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
+ <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/Functions/Example/FirebaseFunctions/Base.lproj/Main.storyboard b/Functions/Example/FirebaseFunctions/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..d164a23
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/Base.lproj/Main.storyboard
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="whP-gf-Uak">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="wQg-tq-qST">
+ <objects>
+ <viewController id="whP-gf-Uak" customClass="FIRViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="uEw-UM-LJ8"/>
+ <viewControllerLayoutGuide type="bottom" id="Mvr-aV-6Um"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="TpU-gO-2f1">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="tc2-Qw-aMS" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="305" y="433"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/Functions/Example/FirebaseFunctions/FIRAppDelegate.h b/Functions/Example/FirebaseFunctions/FIRAppDelegate.h
new file mode 100644
index 0000000..500ee08
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/FIRAppDelegate.h
@@ -0,0 +1,21 @@
+// Copyright 2017 Google
+//
+// 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 FIRAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/Functions/Example/FirebaseFunctions/FIRAppDelegate.m b/Functions/Example/FirebaseFunctions/FIRAppDelegate.m
new file mode 100644
index 0000000..0ecfdea
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/FIRAppDelegate.m
@@ -0,0 +1,52 @@
+// Copyright 2017 Google
+//
+// 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 "FIRAppDelegate.h"
+
+@implementation FIRAppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ // Override point for customization after application launch.
+ return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application
+{
+ // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+ // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application
+{
+ // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+ // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application
+{
+ // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application
+{
+ // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application
+{
+ // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
diff --git a/Functions/Example/FirebaseFunctions/FIRViewController.h b/Functions/Example/FirebaseFunctions/FIRViewController.h
new file mode 100644
index 0000000..5c4dff5
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/FIRViewController.h
@@ -0,0 +1,19 @@
+// Copyright 2017 Google
+//
+// 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 FIRViewController : UIViewController
+
+@end
diff --git a/Functions/Example/FirebaseFunctions/FIRViewController.m b/Functions/Example/FirebaseFunctions/FIRViewController.m
new file mode 100644
index 0000000..901accf
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/FIRViewController.m
@@ -0,0 +1,35 @@
+// Copyright 2017 Google
+//
+// 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 "FIRViewController.h"
+
+@interface FIRViewController ()
+
+@end
+
+@implementation FIRViewController
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning
+{
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+@end
diff --git a/Functions/Example/FirebaseFunctions/FirebaseFunctions-Info.plist b/Functions/Example/FirebaseFunctions/FirebaseFunctions-Info.plist
new file mode 100644
index 0000000..fc26896
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/FirebaseFunctions-Info.plist
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>NSAppTransportSecurity</key>
+ <dict>
+ <key>NSAllowsArbitraryLoads</key>
+ <true/>
+ </dict>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
diff --git a/Functions/Example/FirebaseFunctions/Images.xcassets/AppIcon.appiconset/Contents.json b/Functions/Example/FirebaseFunctions/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d7070bc
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,93 @@
+{
+ "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"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/Functions/Example/FirebaseFunctions/en.lproj/InfoPlist.strings b/Functions/Example/FirebaseFunctions/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/Functions/Example/FirebaseFunctions/main.m b/Functions/Example/FirebaseFunctions/main.m
new file mode 100644
index 0000000..03b5c12
--- /dev/null
+++ b/Functions/Example/FirebaseFunctions/main.m
@@ -0,0 +1,23 @@
+// Copyright 2017 Google
+//
+// 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 "FIRAppDelegate.h"
+
+int main(int argc, char * argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([FIRAppDelegate class]));
+ }
+}
diff --git a/Functions/Example/IntegrationTests/FIRIntegrationTests.m b/Functions/Example/IntegrationTests/FIRIntegrationTests.m
new file mode 100644
index 0000000..15a5af5
--- /dev/null
+++ b/Functions/Example/IntegrationTests/FIRIntegrationTests.m
@@ -0,0 +1,190 @@
+// Copyright 2017 Google
+//
+// 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;
+
+#import "FIRError.h"
+#import "FIRFunctions+Internal.h"
+#import "FIRFunctions.h"
+#import "FIRHTTPSCallable.h"
+#import "FUNFakeApp.h"
+#import "FUNFakeInstanceID.h"
+
+@interface FIRIntegrationTests : XCTestCase {
+ FIRFunctions *_functions;
+}
+@end
+
+@implementation FIRIntegrationTests
+
+- (void)setUp {
+ [super setUp];
+ id app = [[FUNFakeApp alloc] initWithProjectID:@"functions-integration-test"];
+ _functions = [FIRFunctions functionsForApp:app];
+ [_functions useLocalhost];
+}
+
+- (void)tearDown {
+ [super tearDown];
+}
+
+- (void)testData {
+ NSDictionary *data = @{
+ @"bool" : @YES,
+ @"int" : @2,
+ @"long" : @3L,
+ @"string" : @"four",
+ @"array" : @[ @5, @6 ],
+ @"null" : [NSNull null],
+ };
+
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"dataTest"];
+ [function callWithObject:data
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNil(error);
+ XCTAssertEqualObjects(@"stub response", result.data[@"message"]);
+ XCTAssertEqualObjects(@42, result.data[@"code"]);
+ XCTAssertEqualObjects(@420L, result.data[@"long"]);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testScalar {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"scalarTest"];
+ [function callWithObject:@17
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNil(error);
+ XCTAssertEqualObjects(@76, result.data);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testToken {
+ // Recreate _functions with a token.
+ id app = [[FUNFakeApp alloc] initWithProjectID:@"functions-integration-test" token:@"token"];
+ FIRFunctions *functions = [FIRFunctions functionsForApp:app];
+ [functions useLocalhost];
+
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [functions HTTPSCallableWithName:@"tokenTest"];
+ [function callWithObject:@{}
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNil(error);
+ XCTAssertEqualObjects(@{}, result.data);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testInstanceID {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"instanceIdTest"];
+ [function callWithObject:@{}
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNil(error);
+ XCTAssertEqualObjects(@{}, result.data);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testNull {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"nullTest"];
+ [function callWithObject:[NSNull null]
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertEqualObjects([NSNull null], result.data);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+
+ // Test the version with no arguments.
+ [function
+ callWithCompletion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertEqualObjects([NSNull null], result.data);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testMissingResult {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"missingResultTest"];
+ [function
+ callWithCompletion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNotNil(error);
+ XCTAssertEqual(FIRFunctionsErrorCodeInternal, error.code);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testUnhandledError {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"unhandledErrorTest"];
+ [function callWithObject:@{}
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNotNil(error);
+ XCTAssertEqual(FIRFunctionsErrorCodeInternal, error.code);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testUnknownError {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"unknownErrorTest"];
+ [function callWithObject:@{}
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNotNil(error);
+ XCTAssertEqual(FIRFunctionsErrorCodeInternal, error.code);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testExplicitError {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"explicitErrorTest"];
+ [function callWithObject:@{}
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNotNil(error);
+ XCTAssertEqual(FIRFunctionsErrorCodeOutOfRange, error.code);
+ XCTAssertEqualObjects(@"explicit nope", error.userInfo[NSLocalizedDescriptionKey]);
+ NSDictionary *expectedDetails = @{ @"start" : @10, @"end" : @20, @"long" : @30L };
+ XCTAssertEqualObjects(expectedDetails, error.userInfo[FIRFunctionsErrorDetailsKey]);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+- (void)testHttpError {
+ XCTestExpectation *expectation = [[XCTestExpectation alloc] init];
+ FIRHTTPSCallable *function = [_functions HTTPSCallableWithName:@"httpErrorTest"];
+ [function callWithObject:@{}
+ completion:^(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error) {
+ XCTAssertNotNil(error);
+ XCTAssertEqual(FIRFunctionsErrorCodeInvalidArgument, error.code);
+ [expectation fulfill];
+ }];
+ [self waitForExpectations:@[ expectation ] timeout:10];
+}
+
+@end
diff --git a/Functions/Example/IntegrationTests/IntegrationTests-Info.plist b/Functions/Example/IntegrationTests/IntegrationTests-Info.plist
new file mode 100644
index 0000000..169b6f7
--- /dev/null
+++ b/Functions/Example/IntegrationTests/IntegrationTests-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/Functions/Example/Podfile b/Functions/Example/Podfile
new file mode 100644
index 0000000..ea07e11
--- /dev/null
+++ b/Functions/Example/Podfile
@@ -0,0 +1,16 @@
+use_frameworks!
+
+target 'FirebaseFunctions_Example' do
+ platform :ios, '8.0'
+
+ pod 'FirebaseCore', :path => '../../'
+ pod 'FirebaseFunctions', :path => '../../'
+
+ target 'FirebaseFunctions_Tests' do
+ inherit! :search_paths
+ end
+
+ target 'FirebaseFunctions_IntegrationTests' do
+ inherit! :search_paths
+ end
+end
diff --git a/Functions/Example/TestUtils/FUNFakeApp.h b/Functions/Example/TestUtils/FUNFakeApp.h
new file mode 100644
index 0000000..d07262b
--- /dev/null
+++ b/Functions/Example/TestUtils/FUNFakeApp.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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 <Foundation/Foundation.h>
+
+@class FUNFakeOptions;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * FUNFakeApp is a mock app to use for tests.
+ */
+@interface FUNFakeApp : NSObject
+
+- (id)init NS_UNAVAILABLE;
+
+- (instancetype)initWithProjectID:(NSString *)projectID;
+
+- (instancetype)initWithProjectID:(NSString *)projectID
+ token:(NSString *_Nullable)token NS_DESIGNATED_INITIALIZER;
+
+@property(nonatomic, strong, readonly) FUNFakeOptions *options;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/Example/TestUtils/FUNFakeApp.m b/Functions/Example/TestUtils/FUNFakeApp.m
new file mode 100644
index 0000000..5b31d01
--- /dev/null
+++ b/Functions/Example/TestUtils/FUNFakeApp.m
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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 "FUNFakeApp.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FUNFakeOptions : NSObject
+
+@property(nonatomic, readonly, copy) NSString *projectID;
+
+- (id)init NS_UNAVAILABLE;
+
+- (instancetype)initWithProjectID:(NSString *)projectID NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation FUNFakeOptions
+
+- (instancetype)initWithProjectID:(NSString *)projectID {
+ self = [super init];
+ if (self) {
+ self->_projectID = [projectID copy];
+ }
+ return self;
+}
+
+@end
+
+@interface FUNFakeApp () {
+ NSString *_token;
+}
+@end
+
+@implementation FUNFakeApp
+
+- (instancetype)initWithProjectID:(NSString *)projectID {
+ return [self initWithProjectID:projectID token:nil];
+}
+
+- (instancetype)initWithProjectID:(NSString *)projectID token:(NSString *_Nullable)token {
+ self = [super init];
+ if (self) {
+ _options = [[FUNFakeOptions alloc] initWithProjectID:projectID];
+ _token = [token copy];
+ }
+ return self;
+}
+
+- (void)getTokenForcingRefresh:(BOOL)forceRefresh
+ withCallback:
+ (void (^)(NSString *_Nullable token, NSError *_Nullable error))callback {
+ callback(_token, nil);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/Example/TestUtils/FUNFakeInstanceID.h b/Functions/Example/TestUtils/FUNFakeInstanceID.h
new file mode 100644
index 0000000..9925410
--- /dev/null
+++ b/Functions/Example/TestUtils/FUNFakeInstanceID.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * This FIRInstanceID is a mock instance ID provider to use for tests.
+ * Since FirebaseFunctions loads FIRInstanceID as a weak dependency by reflection, we just have to
+ * make a class with the same name.
+ */
+@interface FIRInstanceID : NSObject
+
++ (instancetype)instanceID;
+
+- (NSString *)token;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/Example/TestUtils/FUNFakeInstanceID.m b/Functions/Example/TestUtils/FUNFakeInstanceID.m
new file mode 100644
index 0000000..a81e67a
--- /dev/null
+++ b/Functions/Example/TestUtils/FUNFakeInstanceID.m
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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 "FUNFakeInstanceID.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation FIRInstanceID
+
++ (instancetype)instanceID {
+ return [[FIRInstanceID alloc] init];
+}
+
+- (NSString *)token {
+ return @"iid";
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/Example/Tests/FIRFunctionsTests.m b/Functions/Example/Tests/FIRFunctionsTests.m
new file mode 100644
index 0000000..bec96e6
--- /dev/null
+++ b/Functions/Example/Tests/FIRFunctionsTests.m
@@ -0,0 +1,50 @@
+// Copyright 2017 Google
+//
+// 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;
+
+#import "FIRFunctions+Internal.h"
+#import "FIRFunctions.h"
+
+#import "FUNFakeApp.h"
+
+@interface FIRFunctionsTests : XCTestCase
+@end
+
+@implementation FIRFunctionsTests
+
+- (void)setUp {
+ [super setUp];
+}
+
+- (void)tearDown {
+ [super tearDown];
+}
+
+- (void)testURLWithName {
+ // TODO(klimt): Add this test back when we add the constructor back.
+ /*
+ id app = [[FUNFakeApp alloc] initWithProjectID:@"my-project"];
+ FIRFunctions *functions = [FIRFunctions functionsForApp:app region:@"my-region"];
+ NSString *url = [functions URLWithName:@"my-endpoint"];
+ XCTAssertEqualObjects(@"https://my-region-my-project.cloudfunctions.net/my-endpoint", url);
+ */
+
+ id app = [[FUNFakeApp alloc] initWithProjectID:@"my-project"];
+ FIRFunctions *functions = [FIRFunctions functionsForApp:app];
+ NSString *url = [functions URLWithName:@"my-endpoint"];
+ XCTAssertEqualObjects(@"https://us-central1-my-project.cloudfunctions.net/my-endpoint", url);
+}
+
+@end
diff --git a/Functions/Example/Tests/FUNSerializerTests.m b/Functions/Example/Tests/FUNSerializerTests.m
new file mode 100644
index 0000000..707c3e7
--- /dev/null
+++ b/Functions/Example/Tests/FUNSerializerTests.m
@@ -0,0 +1,240 @@
+// Copyright 2017 Google
+//
+// 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;
+
+#import "FIRError.h"
+#import "FUNSerializer.h"
+
+@interface FUNSerializerTests : XCTestCase
+@end
+
+@implementation FUNSerializerTests
+
+- (void)setUp {
+ [super setUp];
+}
+
+- (void)tearDown {
+ [super tearDown];
+}
+
+- (void)testEncodeNull {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ XCTAssertEqualObjects([NSNull null], [serializer encode:[NSNull null]]);
+}
+
+- (void)testDecodeNull {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects([NSNull null], [serializer decode:[NSNull null] error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testEncodeInt {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ XCTAssertEqualObjects(@1, [serializer encode:@1]);
+}
+
+- (void)testDecodeInt {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects(@1, [serializer decode:@1 error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testEncodeLong {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSDictionary *expected = @{
+ @"@type" : @"type.googleapis.com/google.protobuf.Int64Value",
+ @"value" : @"-9223372036854775800",
+ };
+ XCTAssertEqualObjects(expected, [serializer encode:@-9223372036854775800L]);
+}
+
+- (void)testDecodeLong {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSDictionary *input = @{
+ @"@type" : @"type.googleapis.com/google.protobuf.Int64Value",
+ @"value" : @"-9223372036854775800",
+ };
+ NSError *error = nil;
+ NSNumber *actual = [serializer decode:input error:&error];
+ XCTAssertEqualObjects(@-9223372036854775800L, actual);
+ // A naive implementation might convert a number to a double and think that's close enough.
+ // We need to make sure it's a long long for accuracy.
+ XCTAssertEqual('q', actual.objCType[0]);
+ XCTAssertNil(error);
+}
+
+- (void)testDecodeInvalidLong {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSDictionary *input = @{
+ @"@type" : @"type.googleapis.com/google.protobuf.Int64Value",
+ @"value" : @"-9223372036854775800 and some other junk",
+ };
+ NSError *error = nil;
+ NSNumber *actual = [serializer decode:input error:&error];
+ XCTAssertNil(actual);
+ XCTAssertNotNil(error);
+ XCTAssertEqualObjects(FIRFunctionsErrorDomain, error.domain);
+ XCTAssertEqual(FIRFunctionsErrorCodeInternal, error.code);
+}
+
+- (void)testEncodeUnsignedLong {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSDictionary *expected = @{
+ @"@type" : @"type.googleapis.com/google.protobuf.UInt64Value",
+ @"value" : @"18446744073709551600",
+ };
+ XCTAssertEqualObjects(expected, [serializer encode:@18446744073709551600UL]);
+}
+
+- (void)testDecodeUnsignedLong {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSDictionary *input = @{
+ @"@type" : @"type.googleapis.com/google.protobuf.UInt64Value",
+ @"value" : @"17446744073709551688",
+ };
+ NSError *error = nil;
+ NSNumber *actual = [serializer decode:input error:&error];
+ XCTAssertEqualObjects(@17446744073709551688UL, actual);
+ // A naive NSNumberFormatter implementation will convert the number to a double and think
+ // that's close enough. We need to make sure it's an unsigned long long for accuracy.
+ XCTAssertEqual('Q', actual.objCType[0]);
+ XCTAssertNil(error);
+}
+
+- (void)testEncodeDouble {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ XCTAssertEqualObjects(@1.2, [serializer encode:@1.2]);
+}
+
+- (void)testDecodeDouble {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects(@1.2, [serializer decode:@1.2 error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testEncodeBool {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ XCTAssertEqualObjects(@YES, [serializer encode:@YES]);
+}
+
+- (void)testDecodeBool {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects(@NO, [serializer decode:@NO error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testEncodeString {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ XCTAssertEqualObjects(@"hello", [serializer encode:@"hello"]);
+}
+
+- (void)testDecodeString {
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects(@"hello", [serializer decode:@"hello" error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testEncodeArray {
+ NSArray *input = @[ @1, @"two", @[ @3, @4L ] ];
+ NSArray *expected = @[
+ @1, @"two",
+ @[
+ @3, @{
+ @"@type" : @"type.googleapis.com/google.protobuf.Int64Value",
+ @"value" : @"4",
+ }
+ ]
+ ];
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ XCTAssertEqualObjects(expected, [serializer encode:input]);
+}
+
+- (void)testDecodeArray {
+ NSArray *input = @[
+ @1, @"two",
+ @[
+ @3, @{
+ @"@type" : @"type.googleapis.com/google.protobuf.Int64Value",
+ @"value" : @"4",
+ }
+ ]
+ ];
+ NSArray *expected = @[ @1, @"two", @[ @3, @4L ] ];
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+
+ XCTAssertEqualObjects(expected, [serializer decode:input error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testEncodeMap {
+ NSDictionary *input = @{ @"foo" : @1, @"bar" : @"hello", @"baz" : @[ @3, @4L ] };
+ NSDictionary *expected = @{
+ @"foo" : @1,
+ @"bar" : @"hello",
+ @"baz" : @[
+ @3, @{
+ @"@type" : @"type.googleapis.com/google.protobuf.Int64Value",
+ @"value" : @"4",
+ }
+ ]
+ };
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ XCTAssertEqualObjects(expected, [serializer encode:input]);
+}
+
+- (void)testDecodeMap {
+ NSDictionary *input = @{
+ @"foo" : @1,
+ @"bar" : @"hello",
+ @"baz" : @[
+ @3, @{
+ @"@type" : @"type.googleapis.com/google.protobuf.Int64Value",
+ @"value" : @"4",
+ }
+ ]
+ };
+ NSDictionary *expected = @{ @"foo" : @1, @"bar" : @"hello", @"baz" : @[ @3, @4L ] };
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects(expected, [serializer decode:input error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testDecodeUnknownType {
+ NSDictionary *input = @{@"@type" : @"unknown", @"value" : @"whatever"};
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects(input, [serializer decode:input error:&error]);
+ XCTAssertNil(error);
+}
+
+- (void)testDecodeUnknownTypeWithoutValue {
+ NSDictionary *input = @{
+ @"@type" : @"unknown",
+ };
+ FUNSerializer *serializer = [[FUNSerializer alloc] init];
+ NSError *error = nil;
+ XCTAssertEqualObjects(input, [serializer decode:input error:&error]);
+ XCTAssertNil(error);
+}
+
+@end
diff --git a/Functions/Example/Tests/Tests-Info.plist b/Functions/Example/Tests/Tests-Info.plist
new file mode 100644
index 0000000..169b6f7
--- /dev/null
+++ b/Functions/Example/Tests/Tests-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
diff --git a/Functions/Example/Tests/en.lproj/InfoPlist.strings b/Functions/Example/Tests/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/Functions/Example/Tests/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
diff --git a/Functions/FirebaseFunctions/FIRFunctions+Internal.h b/Functions/FirebaseFunctions/FIRFunctions+Internal.h
new file mode 100644
index 0000000..6c555f0
--- /dev/null
+++ b/Functions/FirebaseFunctions/FIRFunctions+Internal.h
@@ -0,0 +1,50 @@
+// Copyright 2017 Google
+//
+// 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 <Foundation/Foundation.h>
+
+#import "FIRFunctions.h"
+
+@class FIRHTTPSCallableResult;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRFunctions (Internal)
+
+/**
+ * Calls an http trigger endpoint.
+ * @param name The name of the http trigger.
+ * @param data Parameters to pass to the function. Can be anything encodable as JSON.
+ * @param completion The block to call when the request is complete.
+ */
+- (void)callFunction:(NSString *)name
+ withObject:(nullable id)data
+ completion:(void (^)(FIRHTTPSCallableResult *_Nullable result,
+ NSError *_Nullable error))completion;
+
+/**
+ * Constructs the url for an http trigger. This is exposed only for testing.
+ * @param name The name of the endpoint.
+ */
+- (NSString *)URLWithName:(NSString *)name;
+
+/**
+ * Sets the functions client to send requests to localhost instead of Firebase.
+ * For testing only.
+ */
+- (void)useLocalhost;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FIRFunctions.m b/Functions/FirebaseFunctions/FIRFunctions.m
new file mode 100644
index 0000000..274d058
--- /dev/null
+++ b/Functions/FirebaseFunctions/FIRFunctions.m
@@ -0,0 +1,247 @@
+// Copyright 2017 Google
+//
+// 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 "FIRFunctions.h"
+#import "FIRFunctions+Internal.h"
+
+#import "FIRError.h"
+#import "FIRHTTPSCallable+Internal.h"
+#import "FIRHTTPSCallable.h"
+#import "FUNContext.h"
+#import "FUNError.h"
+#import "FUNSerializer.h"
+#import "FUNUsageValidation.h"
+
+#import "FIRApp.h"
+#import "FIRAppInternal.h"
+#import "FIROptions.h"
+#import "GTMSessionFetcherService.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+NSString *const kFUNInstanceIDTokenHeader = @"Firebase-Instance-ID-Token";
+
+@interface FIRFunctions () {
+ // The network client to use for http requests.
+ GTMSessionFetcherService *_fetcherService;
+ // The projectID to use for all function references.
+ FIRApp *_app;
+ // The region to use for all function references.
+ NSString *_region;
+ // A serializer to encode/decode data and return values.
+ FUNSerializer *_serializer;
+ // A factory for getting the metadata to include with function calls.
+ FUNContextProvider *_contextProvider;
+ // For testing only. If this is set, functions will be called against localhost instead of
+ // Firebase.
+ BOOL _useLocalhost;
+}
+
+/**
+ * Initialize the Cloud Functions client with the given app and region.
+ * @param app The app for the Firebase project.
+ * @param region The region for the http trigger, such as "us-central1".
+ */
+- (id)initWithApp:(FIRApp *)app region:(NSString *)region NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation FIRFunctions
+
++ (instancetype)functions {
+ return [[self alloc] initWithApp:[FIRApp defaultApp] region:@"us-central1"];
+}
+
++ (instancetype)functionsForApp:(FIRApp *)app {
+ return [[self alloc] initWithApp:app region:@"us-central1"];
+}
+
++ (instancetype)functionsForRegion:(NSString *)region {
+ return [[self alloc] initWithApp:[FIRApp defaultApp] region:region];
+}
+
++ (instancetype)functionsForApp:(FIRApp *)app region:(NSString *)region {
+ return [[self alloc] initWithApp:app region:region];
+}
+
+- (instancetype)initWithApp:(FIRApp *)app region:(NSString *)region {
+ self = [super init];
+ if (self) {
+ if (!region) {
+ FUNThrowInvalidArgument(@"FIRFunctions region cannot be nil.");
+ }
+ _fetcherService = [[GTMSessionFetcherService alloc] init];
+ _app = app;
+ _region = [region copy];
+ _serializer = [[FUNSerializer alloc] init];
+ _contextProvider = [[FUNContextProvider alloc] initWithApp:app];
+ _useLocalhost = NO;
+ }
+ return self;
+}
+
+- (void)useLocalhost {
+ _useLocalhost = YES;
+}
+
+- (NSString *)URLWithName:(NSString *)name {
+ if (!name) {
+ FUNThrowInvalidArgument(@"FIRFunctions function name cannot be nil.");
+ }
+ NSString *projectID = _app.options.projectID;
+ if (!projectID) {
+ FUNThrowInvalidArgument(@"FIRFunctions app projectID cannot be nil.");
+ }
+ if (_useLocalhost) {
+ return [NSString stringWithFormat:@"http://localhost:5005/%@/%@/%@", projectID, _region, name];
+ }
+ return
+ [NSString stringWithFormat:@"https://%@-%@.cloudfunctions.net/%@", _region, projectID, name];
+}
+
+- (void)callFunction:(NSString *)name
+ withObject:(nullable id)data
+ completion:(void (^)(FIRHTTPSCallableResult *_Nullable result,
+ NSError *_Nullable error))completion {
+ [_contextProvider getContext:^(FUNContext *_Nullable context, NSError *_Nullable error) {
+ if (error) {
+ if (completion) {
+ completion(nil, error);
+ }
+ return;
+ }
+ return [self callFunction:name withObject:data context:context completion:completion];
+ }];
+}
+
+- (void)callFunction:(NSString *)name
+ withObject:(nullable id)data
+ context:(FUNContext *)context
+ completion:(void (^)(FIRHTTPSCallableResult *_Nullable result,
+ NSError *_Nullable error))completion {
+ GTMSessionFetcher *fetcher = [_fetcherService fetcherWithURLString:[self URLWithName:name]];
+
+ NSMutableDictionary *body = [NSMutableDictionary dictionary];
+ // Encode the data in the body.
+ if (!data) {
+ data = [NSNull null];
+ }
+ id encoded = [_serializer encode:data];
+ if (!encoded) {
+ FUNThrowInvalidArgument(@"FIRFunctions data encoded as nil. This should not happen.");
+ }
+ body[@"data"] = encoded;
+
+ NSError *error = nil;
+ NSData *payload = [NSJSONSerialization dataWithJSONObject:body options:0 error:&error];
+ if (error) {
+ if (completion) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completion(nil, error);
+ });
+ }
+ return;
+ }
+ fetcher.bodyData = payload;
+
+ // Set the headers.
+ [fetcher setRequestValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
+ if (context.authToken) {
+ NSString *value = [NSString stringWithFormat:@"Bearer %@", context.authToken];
+ [fetcher setRequestValue:value forHTTPHeaderField:@"Authorization"];
+ }
+ if (context.instanceIDToken) {
+ [fetcher setRequestValue:context.instanceIDToken forHTTPHeaderField:kFUNInstanceIDTokenHeader];
+ }
+
+ // Override normal security rules if this is a local test.
+ if (_useLocalhost) {
+ fetcher.allowLocalhostRequest = YES;
+ fetcher.allowedInsecureSchemes = @[ @"http" ];
+ }
+
+ FUNSerializer *serializer = _serializer;
+ [fetcher beginFetchWithCompletionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
+ // If there was an HTTP error, convert it to our own error domain.
+ if (error) {
+ if ([error.domain isEqualToString:kGTMSessionFetcherStatusDomain]) {
+ error = FUNErrorForResponse(error.code, data, serializer);
+ }
+ } else {
+ // If there wasn't an HTTP error, see if there was an error in the body.
+ error = FUNErrorForResponse(200, data, serializer);
+ }
+ // If there was an error, report it to the user and stop.
+ if (error) {
+ if (completion) {
+ completion(nil, error);
+ }
+ return;
+ }
+
+ id responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
+ if (error) {
+ if (completion) {
+ completion(nil, error);
+ }
+ return;
+ }
+ if (![responseJSON isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *userInfo = @{NSLocalizedDescriptionKey : @"Response was not a dictionary."};
+ error = [NSError errorWithDomain:FIRFunctionsErrorDomain
+ code:FIRFunctionsErrorCodeInternal
+ userInfo:userInfo];
+ if (completion) {
+ completion(nil, error);
+ }
+ return;
+ }
+ id dataJSON = responseJSON[@"data"];
+ // TODO(klimt): Allow "result" instead of "data" for now, for backwards compatibility.
+ if (!dataJSON) {
+ dataJSON = responseJSON[@"result"];
+ }
+ if (!dataJSON) {
+ NSDictionary *userInfo =
+ @{NSLocalizedDescriptionKey : @"Response did not include data field."};
+ error = [NSError errorWithDomain:FIRFunctionsErrorDomain
+ code:FIRFunctionsErrorCodeInternal
+ userInfo:userInfo];
+ if (completion) {
+ completion(nil, error);
+ }
+ return;
+ }
+ id resultData = [serializer decode:dataJSON error:&error];
+ if (error) {
+ if (completion) {
+ completion(nil, error);
+ }
+ return;
+ }
+ id result = [[FIRHTTPSCallableResult alloc] initWithData:resultData];
+ if (completion) {
+ // If there's no result field, this will return nil, which is fine.
+ completion(result, nil);
+ }
+ }];
+}
+
+- (FIRHTTPSCallable *)HTTPSCallableWithName:(NSString *)name {
+ return [[FIRHTTPSCallable alloc] initWithFunctions:self name:name];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h b/Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h
new file mode 100644
index 0000000..0a8dae1
--- /dev/null
+++ b/Functions/FirebaseFunctions/FIRHTTPSCallable+Internal.h
@@ -0,0 +1,46 @@
+// Copyright 2017 Google
+//
+// 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 <Foundation/Foundation.h>
+
+#import "FIRHTTPSCallable.h"
+
+@class FIRFunctions;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRHTTPSCallableResult (Internal)
+
+/**
+ * Initializes a callable result.
+ *
+ * @param result The data to wrap.
+ */
+- (instancetype)initWithData:(id)result;
+
+@end
+
+@interface FIRHTTPSCallable (Internal)
+
+/**
+ * Initializes a reference to the given http trigger.
+ *
+ * @param functionsClient The functions client to use for making network requests.
+ * @param name The name of the http trigger.
+ */
+- (instancetype)initWithFunctions:(FIRFunctions *)functionsClient name:(NSString *)name;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FIRHTTPSCallable.m b/Functions/FirebaseFunctions/FIRHTTPSCallable.m
new file mode 100644
index 0000000..2979ca5
--- /dev/null
+++ b/Functions/FirebaseFunctions/FIRHTTPSCallable.m
@@ -0,0 +1,71 @@
+// Copyright 2017 Google
+//
+// 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 "FIRHTTPSCallable.h"
+#import "FIRHTTPSCallable+Internal.h"
+
+#import "FIRFunctions+Internal.h"
+#import "FUNUsageValidation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation FIRHTTPSCallableResult
+
+- (instancetype)initWithData:(id)data {
+ self = [super init];
+ if (self) {
+ _data = data;
+ }
+ return self;
+}
+
+@end
+
+@interface FIRHTTPSCallable () {
+ // The functions client to use for making calls.
+ FIRFunctions *_functions;
+ // The name of the http endpoint this reference refers to.
+ NSString *_name;
+}
+
+@end
+
+@implementation FIRHTTPSCallable
+
+- (instancetype)initWithFunctions:(FIRFunctions *)functions name:(NSString *)name {
+ self = [super init];
+ if (self) {
+ if (!name) {
+ FUNThrowInvalidArgument(@"FIRHTTPSCallable name cannot be nil.");
+ }
+ _name = [name copy];
+ _functions = functions;
+ }
+ return self;
+}
+
+- (void)callWithCompletion:(void (^)(FIRHTTPSCallableResult *_Nullable result,
+ NSError *_Nullable error))completion {
+ [self callWithObject:nil completion:completion];
+}
+
+- (void)callWithObject:(nullable id)data
+ completion:(void (^)(FIRHTTPSCallableResult *_Nullable result,
+ NSError *_Nullable error))completion {
+ [_functions callFunction:_name withObject:data completion:completion];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNContext.h b/Functions/FirebaseFunctions/FUNContext.h
new file mode 100644
index 0000000..0979b67
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNContext.h
@@ -0,0 +1,41 @@
+// 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 <Foundation/Foundation.h>
+
+@class FIRApp;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * FUNContext is a helper class for gathering metadata for a function call.
+ */
+@interface FUNContext : NSObject
+- (id)init NS_UNAVAILABLE;
+@property(nonatomic, copy, nullable, readonly) NSString *authToken;
+@property(nonatomic, copy, nullable, readonly) NSString *instanceIDToken;
+@end
+
+/**
+ * A FUNContextProvider gathers metadata and creats a FUNContext.
+ */
+@interface FUNContextProvider : NSObject
+
+- (id)init NS_UNAVAILABLE;
+
+- (instancetype)initWithApp:(FIRApp *)app NS_DESIGNATED_INITIALIZER;
+
+- (void)getContext:(void (^)(FUNContext *_Nullable context, NSError *_Nullable error))completion;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNContext.m b/Functions/FirebaseFunctions/FUNContext.m
new file mode 100644
index 0000000..0ce4c2b
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNContext.m
@@ -0,0 +1,84 @@
+// 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 "FUNContext.h"
+
+#import "FIRApp.h"
+#import "FIRAppInternal.h"
+#import "FUNInstanceIDProxy.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FUNContext ()
+
+- (instancetype)initWithAuthToken:(NSString *_Nullable)authToken
+ instanceIDToken:(NSString *_Nullable)instanceIDToken NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation FUNContext
+
+- (instancetype)initWithAuthToken:(NSString *_Nullable)authToken
+ instanceIDToken:(NSString *_Nullable)instanceIDToken {
+ self = [super init];
+ if (self) {
+ _authToken = [authToken copy];
+ _instanceIDToken = [instanceIDToken copy];
+ }
+ return self;
+}
+
+@end
+
+@interface FUNContextProvider () {
+ FIRApp *_app;
+ FUNInstanceIDProxy *_instanceIDProxy;
+}
+@end
+
+@implementation FUNContextProvider
+
+- (instancetype)initWithApp:(FIRApp *)app {
+ self = [super init];
+ if (self) {
+ _app = app;
+ _instanceIDProxy = [[FUNInstanceIDProxy alloc] init];
+ }
+ return self;
+}
+
+// This is broken out so it can be mocked for tests.
+- (NSString *)instanceIDToken {
+ return [_instanceIDProxy token];
+}
+
+- (void)getContext:(void (^)(FUNContext *_Nullable context, NSError *_Nullable error))completion {
+ // Get the auth token.
+ [_app getTokenForcingRefresh:NO
+ withCallback:^(NSString *_Nullable token, NSError *_Nullable error) {
+ if (error) {
+ completion(nil, error);
+ return;
+ }
+
+ // Get the instance id token.
+ NSString *_Nullable instanceIDToken = [self instanceIDToken];
+
+ FUNContext *context = [[FUNContext alloc] initWithAuthToken:token
+ instanceIDToken:instanceIDToken];
+ completion(context, nil);
+ }];
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNError.h b/Functions/FirebaseFunctions/FUNError.h
new file mode 100644
index 0000000..04a9257
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNError.h
@@ -0,0 +1,31 @@
+// 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 <Foundation/Foundation.h>
+
+@class FUNSerializer;
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Takes an HTTP status code and optional body and returns a corresponding NSError.
+ * If an explicit error is encoded in the JSON body, it will be used.
+ * Otherwise, uses the standard HTTP status code -> error mapping defined in:
+ * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
+ * @param status An HTTP status code.
+ * @param body Optional body of the HTTP response.
+ * @param serializer A serializer to use to decode the details in the error response.
+ * @return The corresponding error.
+ */
+NSError *FUNErrorForResponse(NSInteger status, NSData *_Nullable body, FUNSerializer *serializer);
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNError.m b/Functions/FirebaseFunctions/FUNError.m
new file mode 100644
index 0000000..d2fac6d
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNError.m
@@ -0,0 +1,186 @@
+// 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 "FUNError.h"
+#import "FIRError.h"
+
+#import "FUNSerializer.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+NSString *const FIRFunctionsErrorDomain = @"com.firebase.functions";
+NSString *const FIRFunctionsErrorDetailsKey = @"details";
+
+/**
+ * Takes an HTTP status code and returns the corresponding FIRFunctionsErrorCode error code.
+ * This is the standard HTTP status code -> error mapping defined in:
+ * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
+ * @param status An HTTP status code.
+ * @return The corresponding error code, or FIRFunctionsErrorCodeUnknown if none.
+ */
+FIRFunctionsErrorCode FIRFunctionsErrorCodeForHTTPStatus(NSInteger status) {
+ switch (status) {
+ case 200:
+ return FIRFunctionsErrorCodeOK;
+ case 400:
+ return FIRFunctionsErrorCodeInvalidArgument;
+ case 401:
+ return FIRFunctionsErrorCodeUnauthenticated;
+ case 403:
+ return FIRFunctionsErrorCodePermissionDenied;
+ case 404:
+ return FIRFunctionsErrorCodeNotFound;
+ case 409:
+ return FIRFunctionsErrorCodeAborted;
+ case 429:
+ return FIRFunctionsErrorCodeResourceExhausted;
+ case 499:
+ return FIRFunctionsErrorCodeCancelled;
+ case 500:
+ return FIRFunctionsErrorCodeInternal;
+ case 501:
+ return FIRFunctionsErrorCodeUnimplemented;
+ case 503:
+ return FIRFunctionsErrorCodeUnavailable;
+ case 504:
+ return FIRFunctionsErrorCodeDeadlineExceeded;
+ }
+ return FIRFunctionsErrorCodeInternal;
+}
+
+/**
+ * Takes the name of an error code and returns the enum value for it.
+ * @param name An error name.
+ * @return The error code with this name, or FIRFunctionsErrorCodeUnknown if none.
+ */
+FIRFunctionsErrorCode FIRFunctionsErrorCodeForName(NSString *name) {
+ static NSDictionary<NSString *, NSNumber *> *errors;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ errors = @{
+ @"OK" : @(FIRFunctionsErrorCodeOK),
+ @"CANCELLED" : @(FIRFunctionsErrorCodeCancelled),
+ @"UNKNOWN" : @(FIRFunctionsErrorCodeUnknown),
+ @"INVALID_ARGUMENT" : @(FIRFunctionsErrorCodeInvalidArgument),
+ @"DEADLINE_EXCEEDED" : @(FIRFunctionsErrorCodeDeadlineExceeded),
+ @"NOT_FOUND" : @(FIRFunctionsErrorCodeNotFound),
+ @"ALREADY_EXISTS" : @(FIRFunctionsErrorCodeAlreadyExists),
+ @"PERMISSION_DENIED" : @(FIRFunctionsErrorCodePermissionDenied),
+ @"RESOURCE_EXHAUSTED" : @(FIRFunctionsErrorCodeResourceExhausted),
+ @"FAILED_PRECONDITION" : @(FIRFunctionsErrorCodeFailedPrecondition),
+ @"ABORTED" : @(FIRFunctionsErrorCodeAborted),
+ @"OUT_OF_RANGE" : @(FIRFunctionsErrorCodeOutOfRange),
+ @"UNIMPLEMENTED" : @(FIRFunctionsErrorCodeUnimplemented),
+ @"INTERNAL" : @(FIRFunctionsErrorCodeInternal),
+ @"UNAVAILABLE" : @(FIRFunctionsErrorCodeUnavailable),
+ @"DATA_LOSS" : @(FIRFunctionsErrorCodeDataLoss),
+ @"UNAUTHENTICATED" : @(FIRFunctionsErrorCodeUnauthenticated),
+ };
+ });
+ NSNumber *code = errors[name];
+ if (code) {
+ return code.intValue;
+ }
+ return FIRFunctionsErrorCodeInternal;
+}
+
+/**
+ * Takes a FIRFunctionsErrorCode and returns an English description of it.
+ * @param code An error code.
+ * @return A description of the code, or "UNKNOWN" if none.
+ */
+NSString *FUNDescriptionForErrorCode(FIRFunctionsErrorCode code) {
+ switch (code) {
+ case FIRFunctionsErrorCodeOK:
+ return @"OK";
+ case FIRFunctionsErrorCodeCancelled:
+ return @"CANCELLED";
+ case FIRFunctionsErrorCodeUnknown:
+ return @"UNKNOWN";
+ case FIRFunctionsErrorCodeInvalidArgument:
+ return @"INVALID ARGUMENT";
+ case FIRFunctionsErrorCodeDeadlineExceeded:
+ return @"DEADLINE EXCEEDED";
+ case FIRFunctionsErrorCodeNotFound:
+ return @"NOT FOUND";
+ case FIRFunctionsErrorCodeAlreadyExists:
+ return @"ALREADY EXISTS";
+ case FIRFunctionsErrorCodePermissionDenied:
+ return @"PERMISSION DENIED";
+ case FIRFunctionsErrorCodeResourceExhausted:
+ return @"RESOURCE EXHAUSTED";
+ case FIRFunctionsErrorCodeFailedPrecondition:
+ return @"FAILED PRECONDITION";
+ case FIRFunctionsErrorCodeAborted:
+ return @"ABORTED";
+ case FIRFunctionsErrorCodeOutOfRange:
+ return @"OUT OF RANGE";
+ case FIRFunctionsErrorCodeUnimplemented:
+ return @"UNIMPLEMENTED";
+ case FIRFunctionsErrorCodeInternal:
+ return @"INTERNAL";
+ case FIRFunctionsErrorCodeUnavailable:
+ return @"UNAVAILABLE";
+ case FIRFunctionsErrorCodeDataLoss:
+ return @"DATA LOSS";
+ case FIRFunctionsErrorCodeUnauthenticated:
+ return @"UNAUTHENTICATED";
+ }
+ return @"UNKNOWN";
+}
+
+NSError *FUNErrorForResponse(NSInteger status, NSData *_Nullable body, FUNSerializer *serializer) {
+ // Start with reasonable defaults from the status code.
+ FIRFunctionsErrorCode code = FIRFunctionsErrorCodeForHTTPStatus(status);
+ NSString *description = FUNDescriptionForErrorCode(code);
+ id details = nil;
+
+ // Then look through the body for explicit details.
+ if (body) {
+ NSError *parseError = nil;
+ id json = [NSJSONSerialization JSONObjectWithData:body options:0 error:&parseError];
+ if (!parseError && [json isKindOfClass:[NSDictionary class]]) {
+ id errorDetails = json[@"error"];
+ if ([errorDetails isKindOfClass:[NSDictionary class]]) {
+ if ([errorDetails[@"status"] isKindOfClass:[NSString class]]) {
+ code = FIRFunctionsErrorCodeForName(errorDetails[@"status"]);
+ // The default description needs to be updated for the new code.
+ description = FUNDescriptionForErrorCode(code);
+ }
+ if ([errorDetails[@"message"] isKindOfClass:[NSString class]]) {
+ description = (NSString *)errorDetails[@"message"];
+ }
+ details = errorDetails[@"details"];
+ if (details) {
+ NSError *decodeError = nil;
+ details = [serializer decode:details error:&decodeError];
+ // Just ignore the details if there an error decoding them.
+ }
+ }
+ }
+ }
+
+ if (code == FIRFunctionsErrorCodeOK) {
+ // Technically, there's an edge case where a developer could explicitly return an error code of
+ // OK, and we will treat it as success, but that seems reasonable.
+ return nil;
+ }
+
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+ userInfo[NSLocalizedDescriptionKey] = description;
+ if (details) {
+ userInfo[FIRFunctionsErrorDetailsKey] = details;
+ }
+ return [NSError errorWithDomain:FIRFunctionsErrorDomain code:code userInfo:userInfo];
+}
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNInstanceIDProxy.h b/Functions/FirebaseFunctions/FUNInstanceIDProxy.h
new file mode 100644
index 0000000..17ec9ef
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNInstanceIDProxy.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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.
+ */
+
+// Note: This file is forked from FIRMessagingInstanceIDProxy.h
+
+#import <Foundation/Foundation.h>
+
+/**
+ * FirebaseFunctions cannot always depend on FIRInstanceID directly, due to how it is
+ * packaged. To make it easier to make calls to FIRInstanceID, this proxy class, will provide
+ * method names duplicated from FIRInstanceID, while using reflection-based called to proxy
+ * the requests.
+ */
+@interface FUNInstanceIDProxy : NSObject
+- (nullable NSString *)token;
+@end
diff --git a/Functions/FirebaseFunctions/FUNInstanceIDProxy.m b/Functions/FirebaseFunctions/FUNInstanceIDProxy.m
new file mode 100644
index 0000000..f89ec98
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNInstanceIDProxy.m
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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.
+ */
+
+// Note: This is forked from FIRMessagingInstanceIDProxy.m
+
+#import "FUNInstanceIDProxy.h"
+
+@implementation FUNInstanceIDProxy
+
++ (nonnull instancetype)instanceIDProxy {
+ static id proxyInstanceID = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ Class instanceIDClass = NSClassFromString(@"FIRInstanceID");
+ if (!instanceIDClass) {
+ proxyInstanceID = nil;
+ return;
+ }
+ SEL instanceIDSelector = NSSelectorFromString(@"instanceID");
+ if (![instanceIDClass respondsToSelector:instanceIDSelector]) {
+ proxyInstanceID = nil;
+ return;
+ }
+ IMP instanceIDImp = [instanceIDClass methodForSelector:instanceIDSelector];
+ id (*instanceIDFunc)(id, SEL) = (void *)instanceIDImp;
+ proxyInstanceID = instanceIDFunc(instanceIDClass, instanceIDSelector);
+ });
+ return (FUNInstanceIDProxy *)proxyInstanceID;
+}
+
+#pragma mark - Tokens
+
+- (nullable NSString *)token {
+ id proxy = [[self class] instanceIDProxy];
+ SEL getTokenSelector = NSSelectorFromString(@"token");
+ if (![proxy respondsToSelector:getTokenSelector]) {
+ return nil;
+ }
+ IMP getTokenIMP = [proxy methodForSelector:getTokenSelector];
+ NSString *(*getToken)(id, SEL) = (void *)getTokenIMP;
+ return getToken(proxy, getTokenSelector);
+}
+
+@end
diff --git a/Functions/FirebaseFunctions/FUNSerializer.h b/Functions/FirebaseFunctions/FUNSerializer.h
new file mode 100644
index 0000000..598a2a1
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNSerializer.h
@@ -0,0 +1,33 @@
+// Copyright 2017 Google
+//
+// 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FUNSerializer : NSObject
+
+/**
+ * Converts raw Objective-C types into JSON objects.
+ */
+- (id)encode:(id)object;
+
+/**
+ * Converts objects in JSON to Objective-C types.
+ */
+- (id)decode:(id)object error:(NSError **)error;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNSerializer.m b/Functions/FirebaseFunctions/FUNSerializer.m
new file mode 100644
index 0000000..682f981
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNSerializer.m
@@ -0,0 +1,231 @@
+// Copyright 2017 Google
+//
+// 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 "FUNSerializer.h"
+
+#import "FIRError.h"
+#import "FUNUsageValidation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+static NSString *const kLongType = @"type.googleapis.com/google.protobuf.Int64Value";
+static NSString *const kUnsignedLongType = @"type.googleapis.com/google.protobuf.UInt64Value";
+static NSString *const kDateType = @"type.googleapis.com/google.protobuf.Timestamp";
+
+@interface FUNSerializer () {
+ NSDateFormatter *_dateFormatter;
+}
+@end
+
+@implementation FUNSerializer
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ _dateFormatter = [[NSDateFormatter alloc] init];
+ _dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+ _dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
+ }
+ return self;
+}
+
+- (id)encodeNumber:(NSNumber *)number {
+ // Recover the underlying type of the number, using the method described here:
+ // http://stackoverflow.com/questions/2518761/get-type-of-nsnumber
+ const char *cType = [number objCType];
+
+ // Type Encoding values taken from
+ // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/
+ // Articles/ocrtTypeEncodings.html
+ switch (cType[0]) {
+ case 'q':
+ // "long long" might be larger than JS supports, so make it a string.
+ return @{
+ @"@type" : kLongType,
+ @"value" : [NSString stringWithFormat:@"%@", number],
+ };
+ case 'Q':
+ // "unsigned long long" might be larger than JS supports, so make it a string.
+ return @{
+ @"@type" : kUnsignedLongType,
+ @"value" : [NSString stringWithFormat:@"%@", number],
+ };
+
+ case 'i':
+ case 's':
+ case 'l':
+ case 'I':
+ case 'S':
+ // If it's an integer that isn't too long, so just use the number.
+ return number;
+
+ case 'f':
+ case 'd':
+ // It's a float/double that's not too large.
+ return number;
+
+ case 'B':
+ case 'c':
+ case 'C':
+ // Boolean values are weird.
+ //
+ // On arm64, objCType of a BOOL-valued NSNumber will be "c", even though @encode(BOOL)
+ // returns "B". "c" is the same as @encode(signed char). Unfortunately this means that
+ // legitimate usage of signed chars is impossible, but this should be rare.
+ //
+ // Just return Boolean values as-is.
+ return number;
+
+ default:
+ // All documented codes should be handled above, so this shouldn't happen.
+ FUNThrowInvalidArgument(@"Unknown NSNumber objCType %s on %@", cType, number);
+ }
+}
+
+- (id)encode:(id)object {
+ if ([object isEqual:[NSNull null]]) {
+ return object;
+ }
+ if ([object isKindOfClass:[NSNumber class]]) {
+ return [self encodeNumber:object];
+ }
+ if ([object isKindOfClass:[NSString class]]) {
+ return object;
+ }
+ if ([object isKindOfClass:[NSDictionary class]]) {
+ NSMutableDictionary *encoded = [NSMutableDictionary dictionary];
+ [object
+ enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
+ encoded[key] = [self encode:obj];
+ }];
+ return encoded;
+ }
+ if ([object isKindOfClass:[NSArray class]]) {
+ NSMutableArray *encoded = [NSMutableArray arrayWithCapacity:[object count]];
+ for (id obj in object) {
+ [encoded addObject:[self encode:obj]];
+ }
+ return encoded;
+ }
+ // TODO(klimt): Add this back when we support NSDate.
+ /*
+ if ([object isKindOfClass:[NSDate class]]) {
+ NSString *iso8601 = [_dateFormatter stringFromDate:object];
+ return @{
+ @"@type" : kDateType,
+ @"value" : iso8601,
+ };
+ }
+ */
+ FUNThrowInvalidArgument(@"Unsupported type: %@ for value %@", NSStringFromClass([object class]),
+ object);
+}
+
+NSError *FUNInvalidNumberError(id value, id wrapped) {
+ NSString *description = [NSString stringWithFormat:@"Invalid number: %@ for %@", value, wrapped];
+ NSDictionary *userInfo = @{
+ NSLocalizedDescriptionKey : description,
+ };
+ return [NSError errorWithDomain:FIRFunctionsErrorDomain
+ code:FIRFunctionsErrorCodeInternal
+ userInfo:userInfo];
+}
+
+- (id)decodeWrappedType:(NSDictionary *)wrapped error:(NSError **)error {
+ NSAssert(error, @"error must not be nil");
+ NSString *type = wrapped[@"@type"];
+ NSString *value = wrapped[@"value"];
+ if (!value) {
+ return nil;
+ }
+ if ([type isEqualToString:kLongType]) {
+ NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
+ NSNumber *n = [formatter numberFromString:value];
+ if (!n) {
+ *error = FUNInvalidNumberError(value, wrapped);
+ return nil;
+ }
+ return n;
+ } else if ([type isEqualToString:kUnsignedLongType]) {
+ // NSNumber formatter doesn't handle unsigned long long, so we have to parse it.
+ const char *str = value.UTF8String;
+ char *end = NULL;
+ unsigned long long n = strtoull(str, &end, 10);
+ if (errno == ERANGE) {
+ // This number was actually too big for an unsigned long long.
+ *error = FUNInvalidNumberError(value, wrapped);
+ return nil;
+ }
+ if (*end) {
+ // The whole string wasn't parsed.
+ *error = FUNInvalidNumberError(value, wrapped);
+ return nil;
+ }
+ return @(n);
+ }
+ return nil;
+}
+
+- (id)decode:(id)object error:(NSError **)error {
+ NSAssert(error, @"error must not be nil");
+ if ([object isKindOfClass:[NSDictionary class]]) {
+ if (object[@"@type"]) {
+ id result = [self decodeWrappedType:object error:error];
+ if (*error) {
+ return nil;
+ }
+ if (result) {
+ return result;
+ }
+ // Treat unknown types as dictionaries, so we don't crash old clients when we add types.
+ }
+ NSMutableDictionary *decoded = [NSMutableDictionary dictionary];
+ __block NSError *decodeError = nil;
+ [object
+ enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
+ id decodedItem = [self decode:obj error:&decodeError];
+ if (decodeError) {
+ *stop = YES;
+ return;
+ }
+ decoded[key] = decodedItem;
+ }];
+ if (decodeError) {
+ *error = decodeError;
+ return nil;
+ }
+ return decoded;
+ }
+ if ([object isKindOfClass:[NSArray class]]) {
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:[object count]];
+ for (id obj in object) {
+ id decoded = [self decode:obj error:error];
+ if (*error) {
+ return nil;
+ }
+ [result addObject:decoded];
+ }
+ return result;
+ }
+ if ([object isKindOfClass:[NSNumber class]] || [object isKindOfClass:[NSString class]] ||
+ [object isEqual:[NSNull null]]) {
+ return object;
+ }
+ FUNThrowInvalidArgument(@"Unsupported type: %@ for value %@", NSStringFromClass([object class]),
+ object);
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNUsageValidation.h b/Functions/FirebaseFunctions/FUNUsageValidation.h
new file mode 100644
index 0000000..17b1c4f
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNUsageValidation.h
@@ -0,0 +1,38 @@
+// Copyright 2017 Google
+//
+// 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Helper for creating a general exception for invalid usage of an API. */
+NSException *FUNInvalidUsage(NSString *exceptionName, NSString *format, ...);
+
+/**
+ * Macro to throw exceptions in response to API usage errors. Avoids the lint warning you usually
+ * get when using @throw and (unlike a function) doesn't trigger warnings about not all codepaths
+ * returning a value.
+ *
+ * Exceptions should only be used for programmer errors made by consumers of the SDK, e.g.
+ * invalid method arguments.
+ *
+ * For recoverable runtime errors, use NSError**.
+ * For internal programming errors, use FSTFail().
+ */
+#define FUNThrowInvalidArgument(format, ...) \
+ do { \
+ @throw FUNInvalidUsage(@"FIRInvalidArgumentException", format, ##__VA_ARGS__); \
+ } while (0)
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/FUNUsageValidation.m b/Functions/FirebaseFunctions/FUNUsageValidation.m
new file mode 100644
index 0000000..a50f525
--- /dev/null
+++ b/Functions/FirebaseFunctions/FUNUsageValidation.m
@@ -0,0 +1,28 @@
+// Copyright 2017 Google
+//
+// 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 "FUNUsageValidation.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+NSException *FUNInvalidUsage(NSString *exceptionName, NSString *format, ...) {
+ va_list arg_list;
+ va_start(arg_list, format);
+ NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
+ va_end(arg_list);
+
+ return [[NSException alloc] initWithName:exceptionName reason:formattedString userInfo:nil];
+}
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/Public/FIRError.h b/Functions/FirebaseFunctions/Public/FIRError.h
new file mode 100644
index 0000000..a275633
--- /dev/null
+++ b/Functions/FirebaseFunctions/Public/FIRError.h
@@ -0,0 +1,90 @@
+// Copyright 2017 Google
+//
+// 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+// The error domain for codes in the FIRFunctionsErrorCode enum.
+FOUNDATION_EXPORT NSString *const FIRFunctionsErrorDomain NS_SWIFT_NAME(FunctionsErrorDomain);
+
+// The key for finding error details in the NSError userInfo.
+FOUNDATION_EXPORT NSString *const FIRFunctionsErrorDetailsKey NS_SWIFT_NAME(FunctionsErrorDetailsKey);
+
+/**
+ * The set of error status codes that can be returned from a Callable HTTPS tigger. These are the
+ * canonical error codes for Google APIs, as documented here:
+ * https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto#L26
+ */
+typedef NS_ENUM(NSInteger, FIRFunctionsErrorCode) {
+ /** The operation completed successfully. */
+ FIRFunctionsErrorCodeOK = 0,
+ /** The operation was cancelled (typically by the caller). */
+ FIRFunctionsErrorCodeCancelled = 1,
+ /** Unknown error or an error from a different error domain. */
+ FIRFunctionsErrorCodeUnknown = 2,
+ /**
+ * Client specified an invalid argument. Note that this differs from `FailedPrecondition`.
+ * `InvalidArgument` indicates arguments that are problematic regardless of the state of the
+ * system (e.g., an invalid field name).
+ */
+ FIRFunctionsErrorCodeInvalidArgument = 3,
+ /**
+ * Deadline expired before operation could complete. For operations that change the state of the
+ * system, this error may be returned even if the operation has completed successfully. For
+ * example, a successful response from a server could have been delayed long enough for the
+ * deadline to expire.
+ */
+ FIRFunctionsErrorCodeDeadlineExceeded = 4,
+ /** Some requested document was not found. */
+ FIRFunctionsErrorCodeNotFound = 5,
+ /** Some document that we attempted to create already exists. */
+ FIRFunctionsErrorCodeAlreadyExists = 6,
+ /** The caller does not have permission to execute the specified operation. */
+ FIRFunctionsErrorCodePermissionDenied = 7,
+ /**
+ * Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system
+ * is out of space.
+ */
+ FIRFunctionsErrorCodeResourceExhausted = 8,
+ /**
+ * Operation was rejected because the system is not in a state required for the operation's
+ * execution.
+ */
+ FIRFunctionsErrorCodeFailedPrecondition = 9,
+ /**
+ * The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.
+ */
+ FIRFunctionsErrorCodeAborted = 10,
+ /** Operation was attempted past the valid range. */
+ FIRFunctionsErrorCodeOutOfRange = 11,
+ /** Operation is not implemented or not supported/enabled. */
+ FIRFunctionsErrorCodeUnimplemented = 12,
+ /**
+ * Internal errors. Means some invariant expected by underlying system has been broken. If you
+ * see one of these errors, something is very broken.
+ */
+ FIRFunctionsErrorCodeInternal = 13,
+ /**
+ * The service is currently unavailable. This is a most likely a transient condition and may be
+ * corrected by retrying with a backoff.
+ */
+ FIRFunctionsErrorCodeUnavailable = 14,
+ /** Unrecoverable data loss or corruption. */
+ FIRFunctionsErrorCodeDataLoss = 15,
+ /** The request does not have valid authentication credentials for the operation. */
+ FIRFunctionsErrorCodeUnauthenticated = 16,
+};
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/Public/FIRFunctions.h b/Functions/FirebaseFunctions/Public/FIRFunctions.h
new file mode 100644
index 0000000..40ec634
--- /dev/null
+++ b/Functions/FirebaseFunctions/Public/FIRFunctions.h
@@ -0,0 +1,66 @@
+// Copyright 2017 Google
+//
+// 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class FIRApp;
+@class FIRHTTPSCallable;
+
+/**
+ * `FIRFunctions` is the client for Cloud Functions for a Firebase project.
+ */
+NS_SWIFT_NAME(Functions)
+@interface FIRFunctions : NSObject
+
+- (id)init NS_UNAVAILABLE;
+
+/**
+ * Creates a Cloud Functions client with the default app.
+ */
++ (instancetype)functions NS_SWIFT_NAME(functions());
+
+/**
+ * Creates a Cloud Functions client with the given app.
+ * @param app The app for the Firebase project.
+ */
++ (instancetype)functionsForApp:(FIRApp *)app NS_SWIFT_NAME(functions(app:));
+
+/**
+ * Creates a Cloud Functions client with the default app and given region.
+ * @param region The region for the http trigger, such as "us-central1".
+ */
+// + (instancetype)functionsForRegion:(NSString *)region NS_SWIFT_NAME(functions(region:));
+
+/**
+ * Creates a Cloud Functions client with the given app and region.
+ * @param app The app for the Firebase project.
+ * @param region The region for the http trigger, such as "us-central1".
+ */
+// clang-format off
+// because it incorrectly breaks this NS_SWIFT_NAME.
+// + (instancetype)functionsForApp:(FIRApp *)app
+// region:(NSString *)region NS_SWIFT_NAME(functions(app:region:));
+// clang-format on
+
+/**
+ * Creates a reference to the Callable HTTPS trigger with the given name.
+ * @param name The name of the Callable HTTPS trigger.
+ */
+- (FIRHTTPSCallable *)HTTPSCallableWithName:(NSString *)name NS_SWIFT_NAME(httpsCallable(_:));
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h b/Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h
new file mode 100644
index 0000000..948696d
--- /dev/null
+++ b/Functions/FirebaseFunctions/Public/FIRHTTPSCallable.h
@@ -0,0 +1,94 @@
+// Copyright 2017 Google
+//
+// 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 <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * A `FIRHTTPSCallableResult` contains the result of calling a `FIRHTTPSCallable`.
+ */
+NS_SWIFT_NAME(HTTPSCallableResult)
+@interface FIRHTTPSCallableResult : NSObject
+
+- (id)init NS_UNAVAILABLE;
+
+
+/**
+ * The data that was returned from the Callable HTTPS trigger.
+ *
+ * The data is in the form of native objects. For example, if your trigger returned an
+ * array, this object would be an NSArray. If your trigger returned a JavaScript object with
+ * keys and values, this object would be an NSDictionary.
+ */
+@property(nonatomic, strong, readonly) id data;
+
+@end
+
+/**
+ * A `FIRHTTPSCallable` is reference to a particular Callable HTTPS trigger in Cloud Functions.
+ */
+NS_SWIFT_NAME(HTTPSCallable)
+@interface FIRHTTPSCallable : NSObject
+
+- (id)init NS_UNAVAILABLE;
+
+/**
+ * Executes this Callable HTTPS trigger asynchronously without any parameters.
+ *
+ * The request to the Cloud Functions backend made by this method automatically includes a
+ * Firebase Instance ID token to identify the app instance. If a user is logged in with Firebase
+ * Auth, an auth ID token for the user is also automatically included.
+ *
+ * Firebase Instance ID sends data to the Firebase backend periodically to collect information
+ * regarding the app instance. To stop this, see `[FIRInstanceID deleteIDWithHandler:]`. It
+ * resumes with a new Instance ID the next time you call this method.
+ *
+ * @param completion The block to call when the HTTPS request has completed.
+ */
+- (void)callWithCompletion:
+ (void (^)(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error))completion
+ NS_SWIFT_NAME(call(completion:));
+
+/**
+ * Executes this Callable HTTPS trigger asynchronously.
+ *
+ * The data passed into the trigger can be any of the following types:
+ * * NSNull
+ * * NSString
+ * * NSNumber
+ * * NSArray<id>, where the contained objects are also one of these types.
+ * * NSDictionary<NSString, id>, where the values are also one of these types.
+ *
+ * The request to the Cloud Functions backend made by this method automatically includes a
+ * Firebase Instance ID token to identify the app instance. If a user is logged in with Firebase
+ * Auth, an auth ID token for the user is also automatically included.
+ *
+ * Firebase Instance ID sends data to the Firebase backend periodically to collect information
+ * regarding the app instance. To stop this, see `[FIRInstanceID deleteIDWithHandler:]`. It
+ * resumes with a new Instance ID the next time you call this method.
+ *
+ * @param data Parameters to pass to the trigger.
+ * @param completion The block to call when the HTTPS request has completed.
+ */
+// clang-format off
+// because it incorrectly breaks this NS_SWIFT_NAME.
+- (void)callWithObject:(nullable id)data
+ completion:(void (^)(FIRHTTPSCallableResult *_Nullable result, NSError *_Nullable error))completion
+ NS_SWIFT_NAME(call(_:completion:));
+// clang-format on
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Functions/README.md b/Functions/README.md
new file mode 100644
index 0000000..f0b8d62
--- /dev/null
+++ b/Functions/README.md
@@ -0,0 +1,21 @@
+# Cloud Functions for Firebase iOS SDK
+
+## To run unit tests
+
+Choose the FirebaseFunctions_Tests scheme and press Command-u.
+
+## To run integration tests
+
+Before running the integration tests, you'll need to start a backend emulator
+for them to talk to.
+
+1. Make sure you have `npm` installed.
+2. Run the backend startup script: `Backend/start.sh`
+ It will use `npm install` to automatically download the libraries it needs
+ to run the [Cloud Functions Local Emulator](https://cloud.google.com/functions/docs/emulator).
+ The first time you run it, it will ask for a projectId.
+ You can put anything you like. It will be ignored.
+3. Create the workspace in Functions/Example with `pod install`.
+4. `open FirebaseFunctions.xcworkspace`
+5. Choose the FirebaseFunctions_IntegrationTests scheme and press Command-u.
+6. When you are finished, you can press any key to stop the backend.
diff --git a/README.md b/README.md
index 2d44c9a..c54cdec 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
-FirebaseMessaging and FirebaseStorage.
+FirebaseFunctions, FirebaseMessaging and FirebaseStorage.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
@@ -15,9 +15,10 @@ the Firebase iOS SDK. If you're interested in using the Firebase iOS SDK, start
## Context
This repo contains a fully functional development environment for FirebaseCore,
-FirebaseAuth, FirebaseDatabase, FirebaseFirestore, FirebaseMessaging, and
-FirebaseStorage. By following the usage instructions below, they can be
-developed and debugged with unit tests, integration tests, and reference samples.
+FirebaseAuth, FirebaseDatabase, FirebaseFirestore, FirebaseFunctions,
+FirebaseMessaging, and FirebaseStorage. By following the usage instructions
+below, they can be developed and debugged with unit tests, integration tests,
+and reference samples.
## Source pod integration
@@ -70,8 +71,9 @@ $ pod update
$ open Firebase.xcworkspace
```
-Firestore has a self contained Xcode project. See
-[Firestore/README.md](Firestore/README.md).
+Firestore and Functions have self contained Xcode projects. See
+[Firestore/README.md](Firestore/README.md) and
+[Functions/README.md](Functions/README.md).
### Running Unit Tests