diff --git a/Readme.md b/Readme.md index 4406160ab..981cae6d0 100644 --- a/Readme.md +++ b/Readme.md @@ -104,6 +104,7 @@ Nullability updates are generally compile-time metadata changes, but they can su ## Quick links +- Documentation index: [docs/README.md](docs/README.md) - Build locally: [docs/BUILDING.md](docs/BUILDING.md) - Publish to GitHub Packages: [docs/PUBLISHING_GITHUB_PACKAGES.md](docs/PUBLISHING_GITHUB_PACKAGES.md) - Contributing guidelines: [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 09af85e2d..4745838e3 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -1,50 +1,47 @@ -# Building locally (macOS) +# Building Locally -This repo builds .NET iOS/macOS bindings and NuGet packages using Cake. +This repository builds .NET iOS and Mac Catalyst bindings plus NuGet packages using Cake. ## Prerequisites -- .NET SDK (see `global.json`) -- Xcode (matching the installed iOS workload requirements) -- CocoaPods (`pod`) +- .NET SDK from `global.json`. +- Xcode compatible with the installed .NET Apple workloads. +- CocoaPods (`pod`). -Restore the local Cake tool from the checked-in .NET tool manifest: +Restore the checked-in Cake tool manifest before building: ```sh dotnet tool restore ``` -## Configure GitHub Packages feed (for fork contributors) +## GitHub Packages Feed for Forks -If you are working on a fork of this repository and want to resolve NuGet packages published from your fork, run: +If you are working from a fork and need to restore packages published from that fork, configure a GitHub Packages source: ```sh -# Using GitHub CLI (recommended) ./scripts/configure-github-feed.sh --gh +``` + +You can also use a local personal access token with `read:packages` scope: -# Or using a personal access token +```sh export GITHUB_PACKAGES_PAT="your_github_pat_here" ./scripts/configure-github-feed.sh ``` -This script: -- Auto-detects your fork owner from the git remote URL -- Configures a GitHub Packages feed (`github-`) -- Allows `dotnet restore` to resolve packages published from your fork +Do not commit tokens or generated local NuGet credentials. -**Note**: Your GitHub Personal Access Token must have the `read:packages` scope. See [GitHub docs](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic) for token creation. +The script detects the fork owner from the git remote, adds a source named like `github-`, and allows `dotnet restore` to resolve fork-published packages. -## Build + pack a component +## Build and Pack One Component -Build and produce `.nupkg` files into `./output`: +Build and produce `.nupkg` files under `./output`: ```sh dotnet tool run dotnet-cake -- --target=nuget --names=Google.SignIn ``` -## Clean-up - -To clean generated folders: +## Clean Generated Output ```sh dotnet tool run dotnet-cake -- --target=clean @@ -54,34 +51,30 @@ dotnet tool run dotnet-cake -- --target=clean ### MSB4057: The target "source/..." does not exist -This error can occur when using MSBuild solution-level targets with certain .NET SDK versions. The `build.cake` script avoids this by building each `.csproj` directly in dependency order, which is more explicit and reliable. +This can happen when using MSBuild solution-level targets with some .NET SDK versions. The Cake script builds `.csproj` files directly in dependency order to avoid that failure mode. ### NU1101: Unable to find package AdamE.Google.iOS.AppCheckCore -Some packages (like `SignIn`) depend on `AppCheckCore` which is built from this repo. The Cake script handles this automatically by building dependencies first. If you still encounter this: +Some packages depend on other packages built from this repository. The Cake script handles dependency-first packing. If restore still fails, run the component build through Cake instead of building the project directly: -1. Ensure you're using the latest `build.cake` (it should iterate `ARTIFACTS_TO_BUILD`) -2. Run the full build: `dotnet tool run dotnet-cake -- --target=nuget --names=Google.SignIn` +```sh +dotnet tool run dotnet-cake -- --target=nuget --names=Google.SignIn +``` ### Code signing errors during xcframework build -The Cake scripts disable code signing by default (`CODE_SIGNING_ALLOWED=NO`) for CI compatibility. If you need signed frameworks, override the build settings in `common.cake`. +The Cake scripts disable code signing by default (`CODE_SIGNING_ALLOWED=NO`) for CI compatibility. If you need signed frameworks, override the build settings locally instead of committing signing material. ### NuGet feed issues -If you see errors like: -``` -NU1101: Unable to find package AdamE.Firebase.iOS.AppCheck [...] -``` +If restore cannot find packages from a fork, verify the configured sources: -This may occur if your GitHub Packages feed is not configured. See "[Configure GitHub Packages feed](#configure-github-packages-feed-for-fork-contributors)" above. - -Verify your feed configuration: ```sh dotnet nuget list source ``` -Clear the NuGet cache if needed: +Then clear local NuGet caches if needed: + ```sh dotnet nuget locals all --clear dotnet restore diff --git a/docs/Firebase/Analytics/Details.md b/docs/Firebase/Analytics/Details.md index 181e24706..835751ca7 100755 --- a/docs/Firebase/Analytics/Details.md +++ b/docs/Firebase/Analytics/Details.md @@ -1,20 +1,18 @@ -Firebase Analytics is a free app measurement solution that provides insight on app usage and user engagement. +# Firebase Analytics -At the heart of Firebase is Firebase Analytics, a free and unlimited analytics solution. Analytics integrates across Firebase features and provides you with unlimited reporting for up to 500 distinct events that you can define using the Firebase SDK. Analytics reports help you understand clearly how your users behave, which enables you to make informed decisions regarding app marketing and performance optimizations. +This path is retained for existing links, but this repository does not maintain a copied Firebase Analytics walkthrough. -## Key capabilities +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -| | | -|-:|-| -| **Unlimited Reporting:** | Firebase Analytics provides unlimited reporting on up to 500 distinct events. | -| **Audience Segmentation:** | Custom audiences can be defined in the Firebase console based on device data, custom events, or user properties. These audiences can be used with other Firebase features when targeting new features or notifications messages. | +## Native Documentation -## How does it work? +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Analytics documentation: https://firebase.google.com/docs/analytics/get-started -Firebase Analytics helps you understand how people use your iOS app. The SDK automatically captures a number of events and user properties and also allows you to define your own custom events to measure the things that uniquely matter to your business. Once the data is captured, it's available in a dashboard through the Firebase console. This dashboard provides detailed insights about your data — from summary data such as active users and demographics, to more detailed data such as identifying your most purchased items. +## Binding Package -Analytics also integrates with a number of other Firebase features. For example, it automatically logs events that correspond to notification messages sent via the Notifications composer and provides reporting on the impact of each campaign. +- Package README: [../NuGet/Analytics.md](../NuGet/Analytics.md) +- Package ID: `AdamE.Firebase.iOS.Analytics` +- Managed namespace: `Firebase.Analytics` -Firebase Analytics helps you understand how your users behave, so you can make informed decisions about how to market your app. See the performance of your campaigns across organic and paid channels to understand which methods are most effective at driving high-value users. If you need to perform custom analysis or join your data with other sources you can link your Analytics data to BigQuery, which allows for more complex analysis like querying large data sets and joining multiple data sources. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/analytics/) to see original Firebase documentation._ +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Analytics/GettingStarted.md b/docs/Firebase/Analytics/GettingStarted.md index 92da4304e..835751ca7 100755 --- a/docs/Firebase/Analytics/GettingStarted.md +++ b/docs/Firebase/Analytics/GettingStarted.md @@ -1,308 +1,18 @@ -# Get Started with Firebase Analytics for iOS +# Firebase Analytics -Firebase Analytics collects usage and behavior data for your app. The SDK logs two primary types of information: +This path is retained for existing links, but this repository does not maintain a copied Firebase Analytics walkthrough. -* **Events:** What is happening in your app, such as user actions, system events, or errors. -* **User properties:** Attributes you define to describe segments of your userbase, such as language preference or geographic location. +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## Table of Content +## Native Documentation -- [Get Started with Firebase Analytics for iOS](#get-started-with-firebase-analytics-for-ios) - - [Table of Content](#table-of-content) - - [Add Firebase to your app](#add-firebase-to-your-app) - - [Configure Analytics in your app](#configure-analytics-in-your-app) -- [Log events](#log-events) - - [View events in the dashboard](#view-events-in-the-dashboard) -- [Set User Properties](#set-user-properties) -- [Use Analytics in a WebView on iOS](#use-analytics-in-a-webview-on-ios) - - [Implement Javascript handler](#implement-javascript-handler) - - [Implement native interface](#implement-native-interface) -- [Debugging Events](#debugging-events) - - [Enabling debug mode](#enabling-debug-mode) -- [Track Screenviews](#track-screenviews) - - [Automatically track screens](#automatically-track-screens) - - [Manually track screens](#manually-track-screens) -- [Extend Google Analytics for Firebase with Cloud Functions](#extend-google-analytics-for-firebase-with-cloud-functions) +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Analytics documentation: https://firebase.google.com/docs/analytics/get-started -## Add Firebase to your app +## Binding Package -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. +- Package README: [../NuGet/Analytics.md](../NuGet/Analytics.md) +- Package ID: `AdamE.Firebase.iOS.Analytics` +- Managed namespace: `Firebase.Analytics` -## Configure Analytics in your app - -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Open `GoogleService-Info.plist` file and change `IS_ANALYTICS_ENABLED` value to `Yes`. -4. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -App.Configure (); -``` - ---- - -# Log events - -Events provide insight on what is happening in your app, such as user actions, system events, or errors. - -Analytics automatically logs some [events][3] for you; you don't need to add any code to receive them. If your app needs to collect additional data, you can log up to 500 different Analytics Event types in your app. There is no limit on the total volume of events your app logs. - -After you have configured Analytics in your app, you can begin to log events with the `Analytics.LogEvent` method. You can find some constants names ready to be used with your log: - -* Suggested events: see the `EventNamesConstants` class. -* Prescribed parameters: see the `ParameterNamesConstants` class. - -It is very easy to log an event, the following example demonstrates how to log an event with constants values (don't forget to import `Firebase.Analytics` namespace): - -```csharp -NSString [] keys = { ParameterNamesConstants.ContentType, ParameterNamesConstants.ItemId }; -NSObject [] values = { new NSString ("cont"), new NSString ("1") }; -var parameters = NSDictionary.FromObjectsAndKeys (values, keys, keys.Length); -Analytics.LogEvent (EventNamesConstants.SelectContent, parameters); - -// Or - -var parameters = new Dictionary { - { ParameterNamesConstants.ContentType, "cont" }, - { ParameterNamesConstants.ItemId, "1" } -}; -Analytics.LogEvent (EventNamesConstants.SelectContent, parameters); -``` - -In addition to the prescribed parameters, you can add the following parameters to any event: - -* **Custom parameters**: Custom parameters can be [registered][15] for reporting in your Analytics reports. They can also be used as filters in [audience][4] definitions that can be applied to every report. Custom parameters are also included in data [exported to BigQuery][5] if your app is linked to a BigQuery project. -* **`ParameterNamesConstants.Value` parameter**: `ParameterNamesConstants.Value` is a general purpose parameter that is useful for accumulating a key metric that pertains to an event. Examples include revenue, distance, time, and points. - -If your application has specific needs not covered by a suggested event type, you can log your own custom events as shown in this example: - -```csharp -NSString [] keys = { new NSString ("Name") }; -NSObject [] values = { new NSString ("Image name") }; -var parameters = NSDictionary.FromObjectsAndKeys (values, keys, keys.Length); -Analytics.LogEvent ("share_image", parameters); - -// Or - -var parameters = new Dictionary { { "Name", "Image Name" } }; -Analytics.LogEvent ("share_image", parameters); -``` - -> ![note_icon] **_Note: Data logged to Analytics can take hours to be refreshed on reports._** - -### View events in the dashboard - -You can view aggregrated statistics about your Analytics events in the Firebase console dashboards. These dashboards update periodically throughout the day. - -You can access this data in the Firebase console as follows: - -1. In the [Firebase console][1], open your project. -2. Select **Analytics** from the menu to view the Analytics reporting dashboard. - -The **Events** tab shows the [event reports][10] that are automatically created for each distinct type of Analytics event logged by your app. Read more about the [Analytics reporting dashboard][11] in the Firebase Help Center. - ---- - -# Set User Properties - -User properties are attributes you define to describe segments of your userbase, such as language preference or geographic location. - -Analytics automatically logs some [user properties][6]; you don't need to add any code to enable them. If your app needs to collect additional data, you can set up to 25 different Analytics User Properties in your app. - -> ![note_icon] **_Note: The Age, Gender, and Interests properties are automatically collected only if your app links to the Ad Support framework. Linking to this framework also automatically collects the Advertising Identifier (IDFA)._** - -You can set Analytics user properties to describe the users of your app. You can analyze behaviors of various user segments by applying these properties as filters to your reports. - -To set a user property you need to: - -1. [Register][7] the property in the **Analytics** page of the [Firebase console][1]. -2. Add code to set an Analytics user property with the `Analytics.SetUserProperty` method. You can use the name and value of your choosing for each property (don't forget to import `Firebase.Analytics` namespace): - -```csharp -// Pass null as value if you want to remove a registered user property -Analytics.SetUserProperty ("your value", "your property name"); -``` - -> ![note_icon] ***Note:*** *Once the property is registered, it can take several hours for data collected with the property to be included in reports. When the new data is available, the user property can be used as a report filter or audience definition.* - -You can access this data in the Firebase console as follows: - -1. In the [Firebase console][1], open your project. -2. Select **Analytics** from the menu to view the Analytics reporting dashboard. - -The **User Properties** tab shows a list of user properties that you have defined for your app. You can use these properties as a filter on many of the reports available in Firebase Analytics. Read more about the [Analytics reporting dashboard][11] in the Firebase Help Center. - -> ![note_icon] _**Note:**_ _Data in the Analytics reporting dashboard refreshes periodically throughout the day._ - ---- - -# Use Analytics in a WebView on iOS - -Calls to log events or set user properties fired from within a WebView must be forwarded to native code before they can be sent to Firebase Analytics. - -## Implement Javascript handler - -The first step in using Google Analytics for Firebase in a WebView is to create JavaScript functions to forward events and user properties to native code. The following example shows how to do this in a way that is compatible with both Android and iOS native code: - -```javascript -function logEvent(name, params) { - if (!name) { - return; - } - - if (window.AnalyticsWebInterface) { - // Call Android interface - window.AnalyticsWebInterface.logEvent(name, JSON.stringify(params)); - } else if (window.webkit - && window.webkit.messageHandlers - && window.webkit.messageHandlers.firebase) { - // Call iOS interface - var message = { - command: 'logEvent', - name: name, - parameters: params - }; - window.webkit.messageHandlers.firebase.postMessage(message); - } else { - // No Android or iOS interface found - console.log("No native APIs found."); - } -} - -function setUserProperty(name, value) { - if (!name || !value) { - return; - } - - if (window.AnalyticsWebInterface) { - // Call Android interface - window.AnalyticsWebInterface.setUserProperty(name, value); - } else if (window.webkit - && window.webkit.messageHandlers - && window.webkit.messageHandlers.firebase) { - // Call iOS interface - var message = { - command: 'setUserProperty', - name: name, - value: value - }; - window.webkit.messageHandlers.firebase.postMessage(message); - } else { - // No Android or iOS interface found - console.log("No native APIs found."); - } -} -``` - -## Implement native interface - -To invoke native iOS code from JavaScript, create a message handler class conforming to the `IWKScriptMessageHandler` interface. You can make Firebase Analytics calls inside of the `DidReceiveScriptMessage` callback: - -```csharp -public void DidReceiveScriptMessage (WKUserContentController userContentController, WKScriptMessage message) -{ - var messageBody = message.Body as NSDictionary; - - switch (messageBody ["command"].ToString ()) { - case "setUserProperty": - Analytics.SetUserProperty (messageBody ["value"].ToString (), messageBody ["name"].ToString ()); - break; - case "logEvent": - Analytics.LogEvent (messageBody ["name"].ToString (), messageBody ["parameters"] as NSDictionary); - break; - } -} -``` - -Finally, add the message handler to the webview's user content controller: - -```csharp -webView.Configuration.UserContentController.AddScriptMessageHandler (this, "firebase"); -``` - ---- - -# Debugging Events - -DebugView enables you to see the raw event data logged by your app on development devices in near real-time. This is very useful for validation purposes during the instrumentation phase of development and can help you discover errors and mistakes in your analytics implementation and confirm that all events and user properties are being logged correctly. - -## Enabling debug mode - -Generally, events logged by your app are batched together over the period of approximately one hour and uploaded together. This approach conserves the battery on end users’ devices and reduces network data usage. However, for the purposes of validating your analytics implementation (and, in order to view your analytics in the DebugView report), you can enable Debug mode on your development device to upload events with a minimal delay. - -To enable Analytics Debug mode on your development device, follow these steps: - -* Visual Studio for Mac - * Open your project settings - * Go to Run/Configuration and select your desired configuration - * In **Extra mlaunch Arguments**, type the command line showed below. - -* Visual Studio for Windows - * Open your project settings - * Go **iOS Run Options** - * In **Extra mlaunch Arguments**, type the command line showed below. - -``` ---argument=-FIRDebugEnabled -``` - -This behavior persists until you explicitly disable Debug mode by specifying the following command line argument: - -``` ---argument=-FIRDebugDisabled -``` - -To learn more about this, please, read the following [documentation][12]. - ---- - -# Track Screenviews - -Google Analytics for Firebase tracks screen transitions and attaches information about the current screen to events, enabling you to track metrics such as user engagement or user behavior per screen. Much of this data collection happens automatically, but you can also manually track screen names. Manually tracking screens is useful if your app does not use a separate `UIViewController` for each screen you may wish to track, such as in a game. - -### Automatically track screens - -Analytics automatically tracks some information about screens in your application, such as the class name of the `UIViewController` that is currently in focus. When a screen transition occurs, Analytics logs a `screen_view` event that identifies the new screen. Events that occur on these screens are automatically tagged with the parameter `firebase_screen_class` (for example, `MenuViewController`) and a generated `firebase_screen_id`. If your app uses a distinct `UIViewController` for each screen, Analytics can automatically track every screen transition and generate a report of user engagement broken down by screen. If your app doesn't, you can still get these reports by manually setting the screen name with the API. - -### Manually track screens - -You can manually set the screen name and optionally override the class name when screen transitions occur. After setting the screen name, events that occur on these screens are additionally tagged with the parameter `firebase_screen`. For example, you could name a screen "Main Menu" or "Friends List". The following example shows how to manually set the screen name: - -```csharp -Firebase.Analytics.Analytics.SetScreenNameAndClass (screenName:, screenClass); -``` - -The screen name and screen class stay the same until the `UIViewController` changes or you make a new call to `SetScreenNameAndClass`. - ---- - -# Extend Google Analytics for Firebase with Cloud Functions - -Google Analytics for Firebase provides event reports that help you understand how users interact with your app. With Cloud Functions, you can access conversion events you have logged and trigger functions based on those events. - -> ![note_icon] _Only events marked as conversion events are currently supported by Cloud Functions. You can specify which events are conversion events in the [Events][13] tab of the Firebase console **Analytics** pane._ - -To learn more about this, please, read the following [documentation][14]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/analytics/ios/start) to see original Firebase documentation._ - -[1]: https://firebase.google.com/console/ -[2]: http://support.google.com/firebase/answer/7015592 -[3]: https://support.google.com/firebase/answer/6317485 -[4]: https://support.google.com/firebase/answer/6317509?hl=en&ref_topic=6317489 -[5]: https://support.google.com/firebase/answer/6318765 -[6]: https://support.google.com/firebase/answer/6317486 -[7]: https://support.google.com/firebase/answer/6317519?hl=en&ref_topic=6317489#create-property -[10]: https://support.google.com/firebase/answer/6317522?hl=en&ref_topic=6317489 -[11]: https://support.google.com/firebase/answer/6317517?hl=en&ref_topic=6317489 -[12]: https://firebase.google.com/docs/analytics/debugview -[13]: https://console.firebase.google.com/project/_/analytics/app/_/events -[14]: https://firebase.google.com/docs/analytics/extend-with-functions -[15]: https://support.google.com/firebase/answer/7397304?hl=en&ref_topic=6317489 -[note_icon]: https://cdn3.iconfinder.com/data/icons/UltimateGnome/22x22/apps/gnome-app-install-star.png -[warning_icon]: https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-20.png +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Auth/Details.md b/docs/Firebase/Auth/Details.md index b6297f844..454baffb5 100755 --- a/docs/Firebase/Auth/Details.md +++ b/docs/Firebase/Auth/Details.md @@ -1,26 +1,18 @@ -Most apps need to know the identity of a user. Knowing a user's identity allows an app to securely save user data in the cloud and provide the same personalized experience across all of the user's devices. +# Firebase Auth -Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app. It supports authentication using passwords, popular federated identity providers like Google, Facebook and Twitter, and more. +This path is retained for existing links, but this repository does not maintain a copied Firebase Auth walkthrough. -Firebase Authentication integrates tightly with other Firebase services, and it leverages industry standards like OAuth 2.0 and OpenID Connect, so it can be easily integrated with your custom backend. +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## Key capabilities +## Native Documentation -| | | -|-:|-| -| **Email and password based authentication** | Authenticate users with their email addresses and passwords. The Firebase Authentication SDK provides methods to create and manage users that use their email addresses and passwords to sign in. Firebase Authentication also handles sending password reset emails. | -| **Federated identity provider integration** | Authenticate users by integrating with federated identity providers. The Firebase Authentication SDK provides methods that allow users to sign in with their Google, Facebook, Twitter, and GitHub accounts. | -| **Phone number authentication** | Authenticate users by sending SMS messages to their phones. | -| **Custom auth system integration** | Connect your app's existing sign-in system to the Firebase Authentication SDK and gain access to Firebase Realtime Database and other Firebase services. | -| **Anonymous auth** | Use Firebase features that require authentication without requiring users to sign in first by creating temporary anonymous accounts. If the user later chooses to sign up, you can upgrade the anonymous account to a regular account, so the user can continue where they left off. | +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Auth documentation: https://firebase.google.com/docs/auth/ios/start +## Binding Package -## How does it work? +- Package README: [../NuGet/Auth.md](../NuGet/Auth.md) +- Package ID: `AdamE.Firebase.iOS.Auth` +- Managed namespace: `Firebase.Auth` -![FirebaseAuth_HowItWorks](https://firebase.google.com/docs/auth/images/auth-providers.png) - -To sign a user into your app, you first get authentication credentials from the user. These credentials can be the user's email address and password, or an OAuth token from a federated identity provider. Then, you pass these credentials to the Firebase Authentication SDK. Our backend services will then verify those credentials and return a response to the client. - -After a successful sign in, you can access the user's basic profile information, and you can control the user's access to data stored in other Firebase products. You can also use the provided authentication token to verify the identity of users in your own backend services. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/auth/) to see original Firebase documentation._ \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Auth/GettingStarted.md b/docs/Firebase/Auth/GettingStarted.md index 79e30c93e..454baffb5 100755 --- a/docs/Firebase/Auth/GettingStarted.md +++ b/docs/Firebase/Auth/GettingStarted.md @@ -1,1982 +1,18 @@ -# Firebase Auth on iOS +# Firebase Auth -You can use Firebase Authentication to allow users to sign in to your app using one or more sign-in methods, including email address and password sign-in, and federated identity providers such as Google Sign-in and Facebook Login. This tutorial gets you started with Firebase Authentication by showing you how to add email address and password sign-in to your app. +This path is retained for existing links, but this repository does not maintain a copied Firebase Auth walkthrough. -## Table of content +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -- [Firebase Auth on iOS](#firebase-auth-on-ios) - - [Table of content](#table-of-content) - - [Add Firebase to your app](#add-firebase-to-your-app) - - [Configure Auth in your app](#configure-auth-in-your-app) - - [Listen for authentication state](#listen-for-authentication-state) - - [Sign up new users](#sign-up-new-users) - - [Sign in existing users](#sign-in-existing-users) - - [Get user information](#get-user-information) -- [Manage Users in Firebase](#manage-users-in-firebase) - - [Create a user](#create-a-user) - - [Get the currently signed-in user](#get-the-currently-signed-in-user) - - [Sign out a user](#sign-out-a-user) - - [Get a user's profile](#get-a-users-profile) - - [Get a user's provider-specific profile information](#get-a-users-provider-specific-profile-information) - - [Update a user's profile](#update-a-users-profile) - - [Set a user's email address](#set-a-users-email-address) - - [Send a user a verification email](#send-a-user-a-verification-email) - - [Set a user's password](#set-a-users-password) - - [Send a password reset email](#send-a-password-reset-email) - - [Delete a user](#delete-a-user) - - [Re-authenticate a user](#re-authenticate-a-user) - - [Import user accounts](#import-user-accounts) - - [Authenticate with Firebase using Password-Based Accounts](#authenticate-with-firebase-using-password-based-accounts) - - [Create an account](#create-an-account) - - [Sign in a user with an email address and password](#sign-in-a-user-with-an-email-address-and-password) -- [Authenticate Using Google Sign-In](#authenticate-using-google-sign-in) -- [Authenticate Using Facebook Login](#authenticate-using-facebook-login) -- [Authenticate Using Twitter Login](#authenticate-using-twitter-login) -- [Authenticate Using GitHub](#authenticate-using-github) -- [Authenticate with Firebase on iOS using a Phone Number](#authenticate-with-firebase-on-ios-using-a-phone-number) - - [Security concerns](#security-concerns) - - [1. Enable Phone Number sign-in for your Firebase project](#1-enable-phone-number-sign-in-for-your-firebase-project) - - [2. Enable app verification](#2-enable-app-verification) - - [Start receiving silent notifications](#start-receiving-silent-notifications) - - [Set up reCAPTCHA verification](#set-up-recaptcha-verification) - - [3. Send a verification code to the user's phone](#3-send-a-verification-code-to-the-users-phone) - - [4. Sign in the user with the verification code](#4-sign-in-the-user-with-the-verification-code) - - [Appendix: Using phone sign-in without swizzling](#appendix--using-phone-sign-in-without-swizzling) -- [Authenticate with Firebase Using a Custom Authentication System](#authenticate-with-firebase-using-a-custom-authentication-system) -- [Authenticate with Firebase Anonymously](#authenticate-with-firebase-anonymously) -- [Passing State in Email Actions](#passing-state-in-email-actions) - - [Passing state/continue URL in email actions](#passing-state/continue-url-in-email-actions) - - [Configuring Firebase Dynamic Links](#configuring-firebase-dynamic-links) - - [Handling email actions in a web application](#handling-email-actions-in-a-web-application) - - [Handling email actions in a mobile application](#handling-email-actions-in-a-mobile-application) - - [Convert an anonymous account to a permanent account](#convert-an-anonymous-account-to-a-permanent-account) -- [Link Multiple Auth Providers to an Account](#link-multiple-auth-providers-to-an-account) - - [Link auth provider credentials to a user account](#link-auth-provider-credentials-to-a-user-account) - - [Unlink an auth provider from a user account](#unlink-an-auth-provider-from-a-user-account) -- [Create custom email action handlers](#create-custom-email-action-handlers) -- [Extend Firebase Authentication with Cloud Functions](#extend-firebase-authentication-with-cloud-functions) +## Native Documentation -## Add Firebase to your app +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Auth documentation: https://firebase.google.com/docs/auth/ios/start -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. +## Binding Package -## Configure Auth in your app +- Package README: [../NuGet/Auth.md](../NuGet/Auth.md) +- Package ID: `AdamE.Firebase.iOS.Auth` +- Managed namespace: `Firebase.Auth` -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -App.Configure (); -``` - -## Listen for authentication state - -For each of your app's views that need information about the signed-in user, attach a listener to the `Auth` object. This listener gets called whenever the user's sign-in state changes. - -Attach the listener in the view controller's `ViewWillAppear` method: - -```csharp -var handle = Auth.DefaultInstance.AddAuthStateDidChangeListener (HandleAuthStateDidChangeListener); - -void HandleAuthStateDidChangeListener (Auth auth, User user) -{ - // ... -} -``` - -And detach the listener in the view controller's `ViewWillDisappear` method: - -```csharp -Auth.DefaultInstance.RemoveAuthStateDidChangeListener (handle); -``` - -## Sign up new users - -Create a form that allows new users to register with your app using their email address and a password. When a user completes the form, validate the email address and password provided by the user, then pass them to the `CreateUser` method: - -```csharp -Auth.DefaultInstance.CreateUser (email, password, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - // ... -} - -// async/await version - -try { - User user = await Auth.DefaultInstance.CreateUserAsync (email, password); -} catch (NSErrorException ex) { - // ... -} -``` - -## Sign in existing users - -Create a form that allows existing users to sign in using their email address and password. When a user completes the form, call the `SignIn` method: - -```csharp -Auth.DefaultInstance.SignIn (email, password, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - // ... -} - -// async/await version - -try { - User user = await Auth.DefaultInstance.SignInAsync (email, password); -} catch (NSErrorException ex) { - // ... -} -``` - -## Get user information - -After a user signs in successfully, you can get information about the user. For example, in your authentication state listener: - -```csharp -if (user != null) { - // The user's ID, unique to the Firebase project. - // Do NOT use this value to authenticate with your backend server, - // if you have one. Use GetToken method instead. - var uid = user.Uid; - var email = user.Email; - var photoUrl = user.PhotoUrl; -} -``` - ---- - -# Manage Users in Firebase - -## Create a user - -You create a new user in your Firebase project by calling the `CreateUser` method or by signing in a user for the first time using a federated identity provider, such as [Google Sign-In](#authenticate-using-google-sign-in) or [Facebook Login](#authenticate-using-facebook-login). - -You can also create new password-authenticated users from the Authentication section of the [Firebase console][1], on the Users page. - -## Get the currently signed-in user - -The recommended way to get the current user is by setting a listener on the Auth object: - -```csharp -var handle = Auth.DefaultInstance.AddAuthStateDidChangeListener (HandleAuthStateDidChangeListener); - -void HandleAuthStateDidChangeListener (Auth auth, User user) -{ - // ... -} -``` - -To remove the listener, pass the `handle` to `RemoveAuthStateDidChangeListener`: - -```csharp -Auth.DefaultInstance.RemoveAuthStateDidChangeListener (handle); -``` - -By using a listener, you ensure that the Auth object isn't in an intermediate state—such as initialization—when you get the current user. - -You can also get the currently signed-in user by using the `CurrentUser` property. If a user isn't signed in, `CurrentUser` is `null`: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -if (user != null) { - // User is signed in. -} else { - // No user is signed in. -} -``` - -> ![note_icon] **_Note_**: _`CurrentUser` might also be `null` because the auth object has not finished initializing. If you use a listener to keep track of the user's sign-in status, you don't need to handle this case._ - -## Sign out a user - -To sign out a user, just call `SignOut` method: - -```csharp -NSError error; -var signedOut = Auth.DefaultInstance.SignOut (out error); - -if (!signedOut) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignOut method with credentials could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.KeychainError: - default: - // Print error - break; - } -} - -// Do your magic to handle successful signout -``` - -## Get a user's profile - -To get a user's profile information, use the properties of an instance of `User`. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -string providerId = user.ProviderId; -string name = user.DisplayName; -string email = user.Email; -NSUrl photoUrl = user.PhotoUrl; - -// The user's ID, unique to the Firebase project. Do NOT use this value to -// authenticate with your backend server, if you have one. Use -// GetToken method instead. -string uid = user.UID; -``` - -## Get a user's provider-specific profile information - -To get the profile information retrieved from the sign-in providers linked to a user, use the `ProviderData` property. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -foreach (var profile in user.ProviderData) { - string providerId = profile.ProviderId; - string name = profile.DisplayName; - string email = profile.Email; - NSUrl photoUrl = profile.PhotoUrl; -} -``` - -## Update a user's profile - -You can update a user's basic profile information—the user's display name and profile photo Url—with the `UserProfileChangeRequest` class. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -var changeRequest = user.ProfileChangeRequest (); -changeRequest.DisplayName = name; -changeRequest.PhotoUrl = new NSUrl (photoUrl); - -changeRequest.CommitChanges (HandleUserProfileChange); - -void HandleUserProfileChange (NSError error) -{ - if (error != null) { - // An error happened. - return; - } - - // Profile updated. -} - -// async/await version - -try { - await changeRequest.CommitChangesAsync (); - // Profile updated. -} catch (NSErrorException ex) { - // An error happened. -} - -``` - -### Set a user's email address - -You can set a user's email address with the `UpdateEmail` method. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -user.UpdateEmail (email, HandleUserProfileChange); - -void HandleUserProfileChange (NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that UpdateEmail method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.RequiresRecentLogin: - default: - // Print error - break; - } - - return; - } - - // Email updated. -} - -// async/await version - -try { - await user.UpdateEmailAsync (email); - // Email updated. -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that UpdateEmail method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.RequiresRecentLogin: - default: - // Print error - break; - } -} - -``` - -> ![note_icon] **_Important_**: _To set a user's password, the user must have signed in recently. See [Re-authenticate a user](#re-authenticate-a-user) section._ - -## Send a user a verification email - -You can send an address verification email to a user with the `SendEmailVerification` method. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -user.SendEmailVerification (HandleSendEmailVerification); - -void HandleSendEmailVerification (NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SendEmailVerification method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.UserNotFound: - default: - // Print error - break; - } - - return; - } - - // Email sent. -} - -// async/await version -try { - await user.SendEmailVerificationAsync (); - // Email sent. -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that SendEmailVerification method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.UserNotFound: - default: - // Print error - break; - } -} -``` - -You can customize the email template that is used in Authentication section of the [Firebase console][1], on the Email Templates page. See [Email Templates][12] in Firebase Help Center. - -It is also possible to pass state via a [continue URL](#passing-state-in-email-actions) to redirect back to the app when sending a verification email. - -Additionally you can localize the verification email by updating the language code on the Auth instance before sending the email. For example: - -```csharp -Auth.DefaultInstance.LanguageCode = "es"; - -// To apply the default app language instead of explicitly setting it. -Auth.DefaultInstance.UseAppLanguage (); -``` - -## Set a user's password - -You can set a user's password with the `UpdatePassword` method. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -user.UpdatePassword (password, HandleUserProfileChange); - -void HandleUserProfileChange (NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that UpdatePassword method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.RequiresRecentLogin: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } - - return; - } - - // Password updated. -} - -//async/await version - -try { - await user.UpdatePasswordAsync (password); - // Password updated. -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that UpdatePassword method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.RequiresRecentLogin: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } -} -``` - -> ![note_icon] **_Important_**: _To set a user's password, the user must have signed in recently. See [Re-authenticate a user](#re-authenticate-a-user) section._ - -## Send a password reset email - -You can send a password reset email to a user with the `SendPasswordReset` method. For example: - -```csharp -Auth.DefaultInstance.SendPasswordReset (email, HandleSendPasswordReset); - -void HandleSendPasswordReset (NSError error) -{ - if (error != null) { - // An error happened. - } else { - // Password reset email sent. - } -} - -// async/await version - -try { - await Auth.DefaultInstance.SendPasswordReset (email); - // Password reset email sent. -} catch (NSErrorException ex) { - // An error happened. -} -``` - -You can customize the email template that is used in Authentication section of the [Firebase console][1], on the Email Templates page. See [Email Templates][12] in Firebase Help Center. - -It is also possible to pass state via a [continue URL](#passing-state-in-email-actions) to redirect back to the app when sending a verification email. - -Additionally you can localize the verification email by updating the language code on the Auth instance before sending the email. For example: - -```csharp -Auth.DefaultInstance.LanguageCode = "es"; - -// To apply the default app language instead of explicitly setting it. -Auth.DefaultInstance.UseAppLanguage (); -``` - -You can also send password reset emails from the Firebase console. - -## Delete a user - -You can delete a user account with the `Delete` method. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -user.Delete (HandleUserProfileChange); - -void HandleUserProfileChange (NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that Delete method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.RequiresRecentLogin: - default: - // Print error - break; - } - - return; - } - - // Account deleted. -} - -// async/await version - -try { - await user.DeleteAsync (); -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that Delete method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.RequiresRecentLogin: - default: - // Print error - break; - } -} -``` - -You can also delete users from the Authentication section of the [Firebase console][1], on the Users page. - -> ![note_icon] **_Important_**: _To set a user's password, the user must have signed in recently. See [Re-authenticate a user](#re-authenticate-a-user) section._ - -### Re-authenticate a user - -Some security-sensitive actions (such as **deleting an account**, **setting a primary email address**, and **changing a password**) require that the user has recently signed in. If you perform one of these actions, and the user signed in too long ago, the action fails with the `AuthErrorCode.RequiresRecentLogin` error. When this happens, re-authenticate the user by getting new sign-in credentials from the user and passing the credentials to `Reauthenticate` method. For example: - -```csharp -var user = Auth.DefaultInstance.CurrentUser; -AuthCredential credential; - -// Prompt the user to re-provide their sign-in credentials - -user.Reauthenticate (credential, HandleUserProfileChange); - -void HandleUserProfileChange (NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that Reauthenticate method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.InvalidCredential: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.WrongPassword: - case AuthErrorCode.UserMismatch: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.UserDisabled: - default: - // Print error - break; - } - - return; - } - - // User re-authenticated. -} - -// async/await version - -try { - await user.ReauthenticateAsync (credential); - // User re-authenticated. -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that Reauthenticate method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.InvalidCredential: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.WrongPassword: - case AuthErrorCode.UserMismatch: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.UserDisabled: - default: - // Print error - break; - } -} -``` - -## Import user accounts - -You can import user accounts from a file into your Firebase project by using the Firebase CLI's `auth:import` command. For example: - -``` -firebase auth:import users.json --hash-algo=scrypt --rounds=8 --mem-cost=14 -``` - ---- - -# Authenticate with Firebase using Password-Based Accounts - -You can use Firebase Authentication to let your users authenticate with Firebase using their email addresses and passwords, and to manage your app's password-based accounts. - -## Create a password-based account - -When a new user signs up using your app's sign-up form, complete any new account validation steps that your app requires, such as verifying that the new account's password was correctly typed and meets your complexity requirements. - -Create a new account by passing the new user's email address and password to `Auth.CreateUser` instance method (don't forget to import `Firebase.Auth` namespace): - -```csharp -Auth.DefaultInstance.CreateUser (email, password, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that CreateUser method could throw - switch (errorCode) { - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } - - return; - } - // Do your magic to handle authentication result -} - -// async/await version - -try { - User user = await Auth.DefaultInstance.CreateUserAsync (email, password); - // Do your magic to handle authentication result -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that CreateUser method could throw - switch (errorCode) { - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } -} -``` - -If the new account was successfully created, the user is signed in, and you can get the user's account data from the user object that's passed to the callback method. - -> ![note_icon] _**Note:**_ _To protect your project from abuse, Firebase limits the number of new email/password and anonymous sign-ups that your application can have from the same IP address in a short period of time. You can request and schedule temporary changes to this quota from the [Firebase console][1]._ - -## Sign in a user with an email address and password - -When a user signs in to your app, pass the user's email address and password to `Auth.SignIn` instance method (don't forget to import `Firebase.Auth` namespace): - -```csharp -Auth.DefaultInstance.SignIn (email, password, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result -} - -// async/await version - -try { - User user = await Auth.DefaultInstance.SignInAsync (email, password); - // Do your magic to handle authentication result -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } -} -``` - -If the user successfully signs in, you can get the user's account data from the user object that's passed to the callback method. - ---- - -# Authenticate with Firebase Using Email Link in iOS - -You can use Firebase Authentication to sign in a user by sending them an email containing a link, which they can click to sign in. In the process, the user's email address is also verified. - -There are numerous benefits to signing in by email: - -* Low friction sign-up and sign-in. -* Lower risk of password reuse across applications, which can undermine security of even well-selected passwords. -* The ability to authenticate a user while also verifying that the user is the legitimate owner of an email address. -* A user only needs an accessible email account to sign in. No ownership of a phone number or social media account is required. -* A user can sign in securely without the need to provide (or remember) a password, which can be cumbersome on a mobile device. -* An existing user who previously signed in with an email identifier (password or federated) can be upgraded to sign in with just the email. For example, a user who has forgotten their password can still sign in without needing to reset their password. - -## Enable Email Link sign-in for your Firebase project - -To sign in users by email link, you must first enable the Email provider and Email link sign-in method for your Firebase project: - -* In the [Firebase console][1], open the **Auth** section. -* On the **Sign in method** tab, enable the **Email/Password** provider. Note that email/password sign-in must be enabled to use email link sign-in. -* In the same section, enable **Email link (passwordless sign-in)** sign-in method. -* Click **Save**. - -## Send an authentication link to the user's email address - -To initiate the authentication flow, present the user with an interface that prompts the user to provide their email address and then call `SendSignInLink` method to request that Firebase send the authentication link to the user's email. - -1. Construct the `ActionCodeSettings` object, which provides Firebase with instructions on how to construct the email link. Set the following fields: - * Url The deep link to embed and any additional state to be passed along. The link's domain has to be whitelisted in the Firebase Console list of authorized domains. - * IOSBundleId and AndroidPackageName : The apps to use when the sign-in link is opened on an Android or iOS device. Learn more on how to [configure Firebase Dynamic Links](#configuring-firebase-dynamic-links) to open email action links via mobile apps. - * HandleCodeInApp: Set to `true`. The sign-in operation has to always be completed in the app unlike other out of band email actions (password reset and email verifications). This is because, at the end of the flow, the user is expected to be signed in and their Auth state persisted within the app. - - ```csharp - var actionCodeSettings = new ActionCodeSettings { - Url = new NSUrl ("https://www.example.com"), - IOSBundleId = NSBundle.MainBundle.BundleIdentifier, - // The sign-in operation has to always be completed in the app. - HandleCodeInApp = true - }; - ``` - - To learn more on `ActionCodeSettings`, refer to the [Passing State in Email Actions](#passing-state/continue-url-in-email-actions) section. -2. Ask the user for their email. -3. Send the authentication link to the user's email, and save the user's email in case the user completes the email sign-in on the same device. - ```csharp - Auth.DefaultInstance.SendSignInLink (email, actionCodeSettings, HandleSendSignInLinkToEmail); - - void HandleSendSignInLinkToEmail (NSError error) - { - if (error != null) { - Console.WriteLine (error.LocalizedDescription); - return; - } - - // The link was successfully sent. Inform the user. - // Save the email locally so you don't need to ask the user for it again - // if they open the link on the same device. - NSUserDefaults.StandardUserDefaults.SetString (email, "Email"); - Console.WriteLine ("Check your email for link."); - } - - // Async/await - - try { - await Auth.DefaultInstance.SendSignInLinkAsync (email, actionCodeSettings); - - // The link was successfully sent. Inform the user. - // Save the email locally so you don't need to ask the user for it again - // if they open the link on the same device. - NSUserDefaults.StandardUserDefaults.SetString (email, "Email"); - Console.WriteLine ("Check your email for link."); - } catch (NSErrorException ex) { - Console.WriteLine (error.LocalizedDescription); - } - ``` - -## Complete sign in with the email link - -### Security concerns - -To prevent a sign-in link from being used to sign in as an unintended user or on an unintended device, Firebase Auth requires the user's email address to be provided when completing the sign-in flow. For sign-in to succeed, this email address must match the address to which the sign-in link was originally sent. - -You can streamline this flow for users who open the sign-in link on the same device they request the link, by storing their email address locally when you send the sign-in email. Then, use this address to complete the flow. - -After sign-in completion, any previous unverified mechanism of sign-in will be removed from the user and any existing sessions will be invalidated. For example, if someone previously created an unverified account with the same email and password, the user's password will be removed to prevent the impersonator who claimed ownership and created that unverified account from signing in again with the same account. - -### Completing sign-in in an iOS mobile app - -Firebase Authentication uses Firebase Dynamic Links to send the email link to a mobile device. For sign-in completion via mobile application, the application has to be configured to detect the incoming application link, parse the underlying deep link and then complete the sign-in. - -### Configuring Firebase Dynamic Links - -Firebase Auth uses [Firebase Dynamic Links][21] when sending a link that is meant to be opened in a mobile application. In order to use this feature, Dynamic Links need to be configured in the Firebase Console. - -1. Enable Firebase Dynamic Links: - 1. In the Firebase console, open the Dynamic Links section. - 2. If you have not yet accepted the Dynamic Links terms and created a Dynamic Links domain, do so now. - - If you already created a Dynamic Links domain, take note of it. A Dynamic Links domain typically looks like the following example: - - ```csharp - example.page.link - ``` - - You will need this value when you configure your iOS or Android app to intercept the incoming link. -2. Configuring iOS applications: - 1. If you plan on handling these links from your iOS appliction, the iOS bundle ID needs to be specified in the Firebase Console project settings. In addition, the App Store ID and the Apple Developer Team ID also need to be specified. - 2. You will also need to configure the FDL universal link domain as an Associated Domain in your application capabilities. - 3. If you plan to distribute your application to iOS versions 8 and under, you will need to set your iOS bundle ID as a custom scheme for incoming URLs. - 4. For more on this, refer to [Receiving iOS Dynamic Links][21] instructions. - -### Verify link and sign in - -After you receive the link as described above, verify that it is meant for email link authentication and complete the sign in. - -```csharp -if (Auth.DefaultInstance.IsSignIn (link)) { - Auth.DefaultInstance.SignInWithLink (email, link, HandleAuthDataResult); -} - -void HandleAuthDataResult (AuthDataResult authResult, NSError error) -{ - // ... -} - -// Async/await - -if (Auth.DefaultInstance.IsSignIn (link)) { - try { - var authResult = await Auth.DefaultInstance.SignInWithLinkAsync (email, link); - - // ... - } catch (NSErrorException ex) { - } -} -``` - ---- - -# Authenticate Using Google Sign-In - -You can let your users authenticate with Firebase using their Google Accounts by integrating Google Sign-In into your app. To be able to use this authentication, please, first integrate [Google Sign-In for iOS][3] to your app. - -Once you have integrated Sign-In into your app, follow these steps to complete your integration with Firebase: - -* If you haven't yet connected your app to your Firebase project, do so from the [Firebase console][1]. -* Enable Google Sign-In in the Firebase console: - * In the [Firebase console][1], open the **Auth** section. - * On the **Sign in method** tab, enable the **Google** sign-in method and click **Save**. -* In your code, in `ISignInDelegate.DidSignIn` method, get a Google ID token and Google access token from the `Authentication` object and exchange them for a Firebase credential and, finally, authenticate with Firebase using the credential: - -```csharp -public void DidSignIn (SignIn signIn, GoogleUser user, NSError error) -{ - if (error == null && user != null) { - // Get Google ID token and Google access token and exchange them for a Firebase credential - var authentication = user.Authentication; - var credential = GoogleAuthProvider.GetCredential (authentication.IdToken, authentication.AccessToken); - - // Authenticate with Firebase using the credential - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - Auth.DefaultInstance.SignInAndRetrieveDataWithCredential (authCredential, HandleAuthDataResult); - } else { - // Print error - } - - void HandleAuthDataResult (AuthDataResult authResult, NSError error) - { - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result - } -} -``` - ---- - -# Authenticate Using Facebook Login - -You can let your users authenticate with Firebase using their Facebook accounts by integrating Facebook Login into your app. To be able to use this authentication, please, first integrate [Facebook iOS SDK][4] to your app. - -Once you have integrated Facebook into your app, follow these steps to complete your integration with Firebase: - -* If you haven't yet connected your app to your Firebase project, do so from the [Firebase console][1]. -* On the [Facebook for Developers][5] site, get the **App ID** and an **App Secret** for your app. -* Enable Facebook Login: - * In the [Firebase console][1], open the Auth section. - * On the **Sign in method** tab, enable the **Facebook** sign-in method and specify the **App ID** and **App Secret** you got from Facebook. - * Then, make sure your **OAuth redirect URI** (e.g. `my-app-12345.firebaseapp.com/__/auth/handler`) is listed as one of your **OAuth redirect URIs** in your Facebook app's settings page on the [Facebook for Developers][5] site in the **Products** > **Facebook Login** > **Settings**. -* In your code, after a user successfully signs in, in your `Completed` event or in your `DidComplete` method, get an access token for the signed-in user and exchange it for a Firebase credential and, finally, authenticate with Firebase using the credential: - -```csharp -BtnLogin.Completed += (object sender, LoginButtonCompletedEventArgs e) => { - if (e.Error != null) { - // Handle if there was an error - } - - if (e.Result.IsCancelled) { - // Handle if the user cancelled the login request - } - - // Get access token for the signed-in user and exchange it for a Firebase credential - var credential = FacebookAuthProvider.GetCredential (AccessToken.CurrentAccessToken.TokenString); - - // Authenticate with Firebase using the credential - Auth.DefaultInstance.SignInAndRetrieveDataWithCredential (authCredential, HandleAuthDataResult); - - void HandleAuthDataResult (AuthDataResult authResult, NSError error) - { - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result - } -}; -``` - ---- - -# Authenticate Using Twitter Login - -You can let your users authenticate with Firebase using their Twitter accounts by integrating Twitter Login into your app. To be able to use this authentication, please, add [Xamarin.Auth NuGet][7] to your project and follow Xamarin.Auth [Getting Started][8] guide to integrate it into your app. - -Once you have integrated Xamarin.Auth into your app, follow these steps to complete your integration with Firebase: - -* [Register your app][9] as a developer application on Twitter and get your app's **API Key** and **API Secret**. -* Enable Twitter Login in the Firebase console: - * In the [Firebase console][1], open the **Auth** section. - * On the **Sign in method** tab, enable the **Twitter** sign-in method and specify the **API Key** and **API Secret** you got from Twitter. - * Then, make sure your Firebase **OAuth redirect URI** (e.g. `my-app-12345.firebaseapp.com/__/auth/handler`) is set as your **Callback URL** in your app's settings page on your [Twitter app's config][9]. - - Be sure to select **Enable Callback Locking** when you set your redirect URI in the Twitter console. - - > ![warning_icon] _Callback locking, which is disabled by default, is necessary to prevent Firebase security tokens from being intercepted by unauthorized parties._ -* After a user successfully signs in, in your implementation of `Completed`, exchange the Twitter auth token and Twitter auth token secret for a Firebase credential: - -```csharp -// Exchange the Twitter auth token and Twitter auth token secret for a Firebase credential -var credential = TwitterAuthProvider.GetCredential (token, secret); - -// Authenticate with Firebase using the credential -Auth.DefaultInstance.SignIn (credential, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result -} -``` - -***Optional:*** Add an email address to the user's profile. When users sign in to your app with Twitter, their email addresses aren't accessible to Firebase. If you want to add email addresses to the profiles of users that sign in with Twitter, prompt users to provide their email addresses, and then call `UpdateEmail` method as in the following example: - -```csharp -user.UpdateEmail (email, HandleUserProfileChange); - -void HandleUserProfileChange (NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that UpdateEmail method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.RequiresRecentLogin: - default: - // Print error - break; - } - - return; - } - - // Email updated. -} -``` - ---- - -# Authenticate Using GitHub - -You can let your users authenticate with Firebase using their GitHub accounts by integrating GitHub authentication into your app. To be able to use this authentication, please, add [Xamarin.Auth NuGet][7] to your project and follow Xamarin.Auth [Getting Started][8] guide to integrate it into your app. - -Once you have integrated Xamarin.Auth into your app, follow these steps to complete your integration with Firebase: - -* [Register your app][10] as a developer application on GitHub and get your app's OAuth 2.0 **Client ID** and **Client Secret**. -* Enable GitHub authentication in the Firebase console: - * In the [Firebase console][1], open the **Auth** section. - * On the **Sign in method** tab, enable the **GitHub** sign-in method and specify the OAuth 2.0 **Client ID** and **Client Secret** you got from GitHub. - * Then, make sure your Firebase **OAuth redirect URI** (e.g. `my-app-12345.firebaseapp.com/__/auth/handler`) is set as your **Authorization callback URL** in your app's settings page on your [GitHub app's config][11]. -* After a user successfully signs in with GitHub, in your implementation of `Completed`, exchange the OAuth 2.0 access token from GitHub for a Firebase credential: - -```csharp -// Exchange the OAuth 2.0 access token from GitHub for a Firebase credential -var credential = GitHubAuthProvider.GetCredential (token); - -// Authenticate with Firebase using the credential -Auth.DefaultInstance.SignIn (credential, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result -} -``` - ---- - -# Authenticate with Firebase on iOS using a Phone Number - -You can use Firebase Authentication to sign in a user by sending an SMS message to the user's phone. The user signs in using a one-time code contained in the SMS message. - -Phone number sign-in requires a physical device and won't work on a simulator. - -This document describes how to implement a phone number sign-in flow using the Firebase SDK. - -> ![note_icon] _**Note:**_ _Phone numbers that end users provide for authentication will be sent and stored by Google to improve our spam and abuse prevention across Google services, including but not limited to Firebase. Developers should ensure they have appropriate end-user consent prior to using the Firebase Authentication phone number sign-in service._ - -## Security concerns - -Authentication using only a phone number, while convenient, is less secure than the other available methods, because possession of a phone number can be easily transferred between users. Also, on devices with multiple user profiles, any user that can receive SMS messages can sign in to an account using the device's phone number. - -If you use phone number based sign-in in your app, you should offer it alongside more secure sign-in methods, and inform users of the security tradeoffs of using phone number sign-in. - -## 1. Enable Phone Number sign-in for your Firebase project - -To sign in users by SMS, you must first enable the Phone Number sign-in method for your Firebase project: - -1. In the [Firebase console][1], open the **Authentication** section. -2. On the **Sign-in Method** page, enable the **Phone Number** sign-in method. - -Firebase's phone number sign-in request quota is high enough that most apps won't be affected. However, if you need to sign in a very high volume of users with phone authentication, you might need to upgrade your pricing plan. See the [pricing][14] page. - -## 2. Enable app verification - -To use phone number authentication, Firebase must be able to verify that phone number sign-in requests are coming from your app. There are two ways Firebase Authentication accomplishes this: - -* **Silent APNs notifications:** When you sign in a user with their phone number for the first time on a device, Firebase Authentication sends a token to the device using a silent push notification. If your app successfully receives the notification from Firebase, phone number sign-in can proceed. - - For iOS 8.0 and newer, silent notifications do not require explicit user consent and is therefore unaffected by a user declining to receive APNs notifications in the app. Thus, the app does not need to request user permission to receive push notifications when implementing Firebase phone number auth. - -* **reCAPTCHA verification**: In the event that sending or receiving a silent push notification is not possible, such as when the user has disabled background refresh for your app, or when testing your app on an iOS simulator, Firebase Authentication uses reCAPTCHA verification to complete the phone sign-in flow. The reCAPTCHA challenge can often be completed without the user having to solve anything. - -When silent push notifications are properly configured, only a very small percentage of users will experience the reCAPTCHA flow. Nonetheless, you should ensure that phone number sign-in functions correctly whether or not silent push notifications are available. - -> ![note_icon] To ensure that both scenarios are working correctly, test your app on a physical iOS device with background app refresh both enabled and disabled. When background app refresh is disabled, you should be able to successfully sign in after completing the reCAPTCHA challenge. You can also test the reCAPTCHA flow by running your app on an iOS simulator, which always uses the reCAPTCHA flow. - -### Start receiving silent notifications - -To enable APNs notifications for use with Firebase Authentication: - -1. In Visual Studio, enable push notifications for your project by opening **Entitlements.plist** and enabling **Push Notifications**. -2. Upload your APNs authentication key to Firebase. If you don't already have an APNs authentication key, see [Configuring APNs with FCM][15]. - 1. Inside your project in the Firebase console, select the gear icon, select **Project Settings**, and then select the **Cloud Messaging** tab. - 2. In **APNs authentication key** under **iOS app configuration**, click the **Upload** button. - 3. Browse to the location where you saved your key, select it, and click **Open**. Add the key ID for the key (available in **Certificates**, **Identifiers** & **Profiles** in the [Apple Developer Member Center][16]) and click **Upload**. - -If you already have an APNs certificate, you can upload the certificate instead. - -### Set up reCAPTCHA verification - -To enable the Firebase SDK to use reCAPTCHA verification: - -1. Add custom URL schemes to your project: - - 1. Open your **Info.plist**: Select the **Advance** tab, and expand the URL Types section. - 2. Click the **Add URL Type** button, and add a **URL scheme** for your **reversed client ID**. To find this value, open the **GoogleService-Info.plist** configuration file, and look for the **REVERSED_CLIENT_ID** key. Copy the value of that key, and paste it into the **URL Schemes** box on the configuration page. Leave the other fields blank. - -2. **Optional:** If you want to customize the way your app presents the `SFSafariViewController` or `UIWebView` when displaying the reCAPTCHA to the user, create a custom class that conforms to the IAuthUIDelegate interface, and pass it to `VerifyPhoneNumber (string, IAuthUIDelegate, VerificationResultHandler)` overload method. - -## 3. Send a verification code to the user's phone - -To initiate phone number sign-in, present the user an interface that prompts them to provide their phone number, and then call `VerifyPhoneNumber` method to request that Firebase send an authentication code to the user's phone by SMS: - -1. Get the user's phone number. - - Legal requirements vary, but as a best practice and to set expectations for your users, you should inform them that if they use phone sign-in, they might receive an SMS message for verification and standard rates apply. - -2. Call `VerifyPhoneNumber`, passing to it the user's phone number: - - ```csharp - PhoneAuthProvider.DefaultInstance.VerifyPhoneNumber (phoneNumber, HandleVerificationResult); - - void HandleVerificationResult (string verificationId, NSError error) - { - if (error != null) { - Console.WriteLine (error.LocalizedDescription); - return; - } - - // Sign in using the verificationID and the code sent to the user - // ... - } - - // async/await version - - try { - string verificationId = await PhoneAuthProvider.DefaultInstance.VerifyPhoneNumberAsync (phoneNumber); - - // Sign in using the verificationID and the code sent to the user - // ... - } catch (NSErrorException ex) { - Console.WriteLine (ex.Error.LocalizedDescription); - } - ``` - - When you call `VerifyPhoneNumber`, Firebase sends a silent push notification to your app or issues a reCAPTCHA challenge to the user. After your app receives the notification or the user completes the reCAPTCHA challenge, Firebase sends an SMS message containing an authentication code to the specified phone number and passes a verification ID to your completion function. You will need both the verification code and the verification ID to sign in the user. - - The SMS message sent by Firebase can also be localized by specifying the auth language via the languageCode property on your Auth instance: - - ```csharp - Auth.DefaultInstance.LanguageCode = "es"; - ``` - -3. Save the verification ID and restore it when your app loads. By doing so, you can ensure that you still have a valid verification ID if your app is terminated before the user completes the sign-in flow (for example, while switching to the SMS app). - - You can persist the verification ID any way you want. A simple way is to save the verification ID with the `NSUserDefaults` object: - - ```csharp - NSUserDefaults.StandardUserDefaults.SetString (verificationId, "authVerificationID"); - ``` - - Then, you can restore the saved value: - - ```csharp - var verificationId = NSUserDefaults.StandardUserDefaults.StringForKey ("AuthVerificationID"); - ``` - -If the call to `VerifyPhoneNumber` succeeds, you can prompt the user to type the verification code when they receive it in the SMS message. - -> ![note_icon] _**Note:**_ _To prevent abuse, Firebase enforces a limit on the number of SMS messages that can be sent to a single phone number within a period of time. If you exceed this limit, phone number verification requests might be throttled. If you encounter this issue during development, use a different phone number for testing, or try the request again later._ - -## 4. Sign in the user with the verification code - -After the user provides your app with the verification code from the SMS message, sign the user in by creating a `PhoneAuthCredential` object from the verification code and verification ID and passing that object to `SignIn (AuthCredential, AuthResultHandler)` method. - -1. Get the verification code from the user. -2. Create a `PhoneAuthCredential` object from the verification code and verification ID: - - ```csharp - var credential = PhoneAuthProvider.DefaultInstance.GetCredential (verificationId, verificationCode); - ``` - -3. Sign in the user with the `PhoneAuthCredential` object: - - ```csharp - Auth.DefaultInstance.SignIn (credential, HandleAuthResult); - - void HandleAuthResult (User user, NSError error) - { - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result - } - ``` - -### Test with whitelisted phone numbers - -You can whitelist phone numbers for development via the Firebase console. Whitelisting phone numbers provides these benefits: - -* Test phone number authentication without consuming your usage quota. -* Test phone number authentication without sending an actual SMS message. -* Run consecutive tests with the same phone number without getting throttled. This minimizes the risk of rejection during App store review process if the reviewer happens to use the same phone number for testing. -* Test readily in development environments without any additional effort, such as the ability to develop in an iOS simulator or an Android emulator without Google Play Services. -* Write integration tests without being blocked by security checks normally applied on real phone numbers in a production environment. - -Phone numbers to whitelist must meet these requirements: - -* Make sure you use fictional numbers that do not already exist. Firebase Authentication does not allow you to whitelist existing phone numbers used by real users. One option is to use 555 prefixed numbers as US test phone numbers, for example: +1 650-555-3434 -* Phone numbers have to be correctly formatted for length and other constraints. They will still go through the same validation as a real user's phone number. -* You can add up to 10 phone numbers for development. -* Use test phone numbers/codes that are hard to guess and change those frequently. - -#### Whitelist phone numbers and verification codes - -* In the [Firebase console][1], open the **Authentication section**. -* In the **Sign in method** tab, enable the Phone provider if you haven't already. -* Open the **Phone numbers for testing** accordion menu. -* Provide the phone number you want to test, for example: _+1 650-555-3434_. -* Provide the 6-digit verification code for that specific number, for example: _654321_. -* **Add** the number. If there's a need, you can delete the phone number and its code by hovering over the corresponding row and clicking the trash icon. - -#### Manual testing - -You can directly start using a whitelisted phone number in your application. This allows you to perform manual testing during development stages without running into quota issues or throttling. You can also test directly from an iOS simulator or Android emulator without Google Play Services installed. - -When you provide the whitelisted phone number and send the verification code, no actual SMS is sent. Instead, you need to provide the previously configured verification code to complete the sign in. - -On sign-in completion, a Firebase user is created with that phone number. The user has the same behavior and properties as a real phone number user, and can access Realtime Database/Cloud Firestore and other services the same way. The ID token minted during this process has the same signature as a real phone number user. - -> ![warning_icon] _Because the ID token for the whitelisted phone number has the same signature as a real phone number user, it is important to store these numbers securely and to continuously recycle them._ - -Another option is to [set a test role via custom claims][22] on these users to differentiate them as fake users if you want to further restrict access. - -#### Integration testing - -In addition to manual testing, Firebase Authentication provides APIs to help write integration tests for phone auth testing. These APIs disable app verification by disabling the reCAPTCHA requirement in web and silent push notifications in iOS. This makes automation testing possible in these flows and easier to implement. In addition, they help provide the ability to test instant verification flows on Android. - -> ![note_icon] _Make sure app verification is not disabled for production apps and that no whitelisted phone numbers are hardcoded in your production app._ - -On iOS, the `AppVerificationDisabledForTesting` setting has to be set to `true` before calling `VerifyPhoneNumber`. This is processed without requiring any APNs token or sending silent push notifications in the background, making it easier to test in a simulator. This also disables the reCAPTCHA fallback flow. - -Note that when app verification is disabled, using a non-whitelisted phone number will fail to complete sign in. Only whitelisted phone numbers can be used with this API. - -```csharp -var phoneNumber = "+16505554567"; - -// This test verification code is specified for the given test phone number in the developer console. -var testVerificationCode = "123456"; - -Auth.DefaultInstance.Settings.AppVerificationDisabledForTesting = true; -PhoneAuthProvider.DefaultInstance.VerifyPhoneNumber (phoneNumber, null, HandleVerificationResult); - -void HandleVerificationResult (string verificationId, NSError error) -{ - if (error != null) { - // Handle error - Console.WriteLine (error.LocalizedDescription); - return; - } - - var authCredential = PhoneAuthProvider.DefaultInstance.GetCredential (verificationId ?? "", testVerificationCode); - - Auth.DefaultInstance.SignInAndRetrieveDataWithCredential (authCredential, HandleAuthDataResult); -} - -void HandleAuthDataResult (AuthDataResult authResult, NSError error) -{ - if (error != null) { - // Handle error - Console.WriteLine (error.LocalizedDescription); - return; - } - - // Success -} - -// Async/await - -try { - var verificationId = await PhoneAuthProvider.DefaultInstance.VerifyPhoneNumberAsync (phoneNumber, null); - var authCredential = PhoneAuthProvider.DefaultInstance.GetCredential (verificationId ?? "", testVerificationCode); - var authResult = await Auth.DefaultInstance.SignInAndRetrieveDataWithCredentialAsync (authCredential); - - // Success -} catch (NSErrorException ex) { - // Handle error - Console.WriteLine (error.LocalizedDescription); -} -``` - -## Appendix: Using phone sign-in without swizzling - -Firebase Authentication uses method swizzling to automatically obtain your app's APNs token and to handle the silent push notifications that Firebase sends to your app, and to automatically intercept the custom scheme redirect from the reCAPTCHA verification page during verification. - -If you prefer not to use swizzling, you can disable it by adding the flag **FirebaseAppDelegateProxyEnabled** to your app's Info.plist file and setting it to **No**. Note that setting this flag to **No** also disables swizzling for other Firebase products, including Firebase Cloud Messaging. - -If you disable swizzling, you must explicitly pass the APNs device token and push notifications to Firebase Authentication. - -To obtain the APNs device token, implement the `AppDelegate`'s `RegisteredForRemoteNotifications` method, and in it, pass the device token to `Auth`'s `SetApnsToken` method: - -```csharp -public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken) -{ - // Pass device token to auth - Auth.DefaultInstance.SetApnsToken (deviceToken, AuthApnsTokenType.Production); // Production if you are ready to release your app, otherwise, use Sandbox. - - // Further handling of the device token if needed by the app - // ... -} -``` - -To handle push notifications, in the `AppDelegate`'s `DidReceiveRemoteNotification` method, check for Firebase auth related notifications by calling `Auth`'s `CanHandleNotification` method: - -```csharp -public override void DidReceiveRemoteNotification (UIApplication application, NSDictionary userInfo, Action completionHandler) -{ - // Pass notification to auth and check if they can handle it. - if (Auth.DefaultInstance.CanHandleNotification (userInfo)) { - completionHandler (UIBackgroundFetchResult.NoData); - return; - } - - // This notification is not auth related, developer should handle it. -} -``` - -To handle the custom scheme redirect URL, implement the `OpenUrl (UIApplication, NSUrl, string, NSObject)` overload method for devices running iOS 8 and older, and the `OpenUrl (UIApplication, NSUrl, NSDictionary)` overload method for devices running iOS 9 and newer, and in them, pass the URL to `Auth`'s `CanHandleURrl` method: - -```csharp -// Support for iOS 9 or later -public override bool OpenUrl (UIApplication app, NSUrl url, NSDictionary options) -{ - var openUrlOptions = new UIApplicationOpenUrlOptions (options); - return OpenUrl (app, url, openUrlOptions.SourceApplication, openUrlOptions.Annotation); -} - -// Support for iOS 8 or before -public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation) -{ - if (Auth.DefaultInstance.CanHandleUrl (url)) { - return true; - } - - // URL not auth related, developer should handle it. -} -``` - ---- - -# Authenticate with Firebase Using a Custom Authentication System - -You can integrate Firebase Authentication with a custom authentication system by modifying your authentication server to produce custom signed tokens when a user successfully signs in. Your app receives this token and uses it to authenticate with Firebase. - -Follow these steps to complete your integration with Firebase: - -* Get your project's server keys: - * Go to the [Service Accounts][6] page in your project's settings. - * Click _Generate New Private Key_ at the bottom of the _Firebase Admin SDK section of the Service Accounts_ page - * The new service account's public/private key pair is automatically saved on your computer. Copy this file to your authentication server. -* When users sign in to your app, send their sign-in credentials (for example, their username and password) to your authentication server. Your server checks the credentials and returns a [custom token][17] if they are valid. -* After you receive the custom token from your authentication server, pass it to `SignIn` method to sign in the user: - -```csharp -Auth.DefaultInstance.SignIn (credential, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignIn method with email and password could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.UserDisabled: - case AuthErrorCode.WrongPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result -} -``` - ---- - -# Authenticate with Firebase Anonymously - -You can use Firebase Authentication to create and use temporary anonymous accounts to authenticate with Firebase. These temporary anonymous accounts can be used to allow users who haven't yet signed up to your app to to work with data protected by security rules. If an anonymous user decides to sign up to your app, you can [link their sign-in credentials to the anonymous account](#link-multiple-auth-providers-to-an-account) so that they can continue to work with their protected data in future sessions. - -Follow these steps to complete your integration with Firebase: - -* If you haven't yet connected your app to your Firebase project, do so from the [Firebase console][1]. -* Enable anonymous auth in the Firebase console: - * In the [Firebase console][1], open the **Auth** section. - * On the **Sign in method** tab, enable the **Anonymous** sign-in method and click **Save**. -* When a signed-out user uses an app feature that requires authentication with Firebase, sign in the user anonymously by using `Auth.SignInAnonymously` instance method: - -```csharp -Auth.DefaultInstance.SignInAnonymously (HandleAuthResultHandler); - -void HandleAuthResultHandler (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that SignInAnonymously method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle authentication result -} - -// async/await version - -try { - User user = await Auth.DefaultInstance.SignInAnonymouslyAsync (); - // Do your magic to handle authentication result -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that SignInAnonymously method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.OperationNotAllowed: - default: - // Print error - break; - } -} -``` - -After a successful login, you can get the anonymous user's account data from the `User` object: - -```csharp -bool isAnonymous = user.IsAnonymous; -string uid = user.Uid; -``` - ---- - -# Passing State in Email Actions - -You can pass state via a continue URL when sending email actions for password resets or verifying a user's email. This provides the user the ability to be returned to the app after the action is completed. In addition, you can specify whether to handle the email action link directly from a mobile application when it is installed instead of a web page. - -This can be extremely useful in the following common scenarios: - -* A user, not currently logged in, may be trying to access content that requires the user to be signed in. However, the user might have forgotten their password and therefore trigger the reset password flow. At the end of the flow, the user expects to go back to the section of the app they were trying to access. -* An application may only offer access to verified accounts. For example, a newsletter app may require the user to verify their email before subscribing. The user would go through the email verification flow and expect to be returned to the app to complete their subscription. -* In general, when a user begins a password reset or email verification flow on an iOS app they expect to complete the flow within the app; the ability to pass state via continue URL makes this possible. - -Having the ability to pass state via a continue URL is a powerful feature that Firebase Auth provides and which can significantly enhance the user experience. - -## Passing state/continue URL in email actions - -In order to securely pass a continue URL, the domain for the URL will need to be whitelisted in the [Firebase console][1]. This is done in the **Authentication** section by adding this domain to the list of **OAuth redirect domains** if it is not already there. - -A `ActionCodeSettings` instance needs to be provided when sending a password reset email or a verification email. This interface takes the following parameters: - -| Parameter | Type | Description | -|----------------------------------|--------|-------------| -| **Url** | String | Sets the link (state/continue URL) which has different meanings in different contexts:
  • When the link is handled in the web action widgets, this is the deep link in the **continueUrl** query parameter.

  • When the link is handled in the app directly, this is the **continueUrl** query parameter in the deep link of the Dynamic Link.
| -| **IOSBundleID** | String | Sets the iOS bundle ID. This will try to open the link in an iOS app if it is installed. The iOS app needs to be registered in the Console. If no Bundle ID is provided, the value of this field is set to the bundle ID of the App's main bundle. | -| **AndroidPackageName** | String | Sets the Android package name. This will try to open the link in an android app if it is installed. | -| **AndroidInstallIfNotAvailable** | Bool | Specifies whether to install the Android app if the device supports it and the app is not already installed. If this field is provided without a packageName, an error is thrown explaining that the |packageName must be provided in conjunction with this field. | -| **AndroidMinimumVersion** | String | The minimum version of the app that is supported in this flow. If minimumVersion is specified, and an older version of the app is installed, the user is taken to the Play Store to upgrade the app. The Android app needs to be registered in the Console. | -| **HandleCodeInApp** | Bool | Whether the email action link will be opened in a mobile app or a web link first. The default is false. When set to true, the action code link will be be sent as a Universal Link or Android App Link and will be opened by the app if installed. In the false case, the code will be sent to the web widget first and then on continue will redirect to the app if installed. | - -The following example illustrates how to send an email verification link that will open in a mobile app first as a Firebase Dynamic Link (iOS app `com.example.ios` or Android app `com.example.android` where the app will install if not already installed and the minimum version is `12`). The deep link will contain the continue URL payload `https://www.example.com/?email=user@example.com`. - -```csharp -var user = Auth.DefaultInstance.CurrentUser; - -var actionCodeSettings = new ActionCodeSettings { - HandleCodeInApp = true, - Url = new NSUrl ($"https://www.example.com/?email={user.Email}"), - IOSBundleId = NSBundle.MainBundle.BundleIdentifier -}; -actionCodeSettings.SetAndroidPackageName ("com.example.android", true, "12"); - -user.SendEmailVerification (actionCodeSettings,HandleSendEmailVerification); - -void HandleSendEmailVerification (NSError error) -{ - if (error != null) { - // Error occurred. Inspect error.code and handle error. - return; - } - - // Email verification sent. -} - -// async/await version - -try { - await user.SendEmailVerificationAsync (actionCodeSettings); - // Email verification sent. -} catch (NSErrorException ex) { - // Error occurred. Inspect error.code and handle error. -} -``` - -## Configuring Firebase Dynamic Links - -Firebase Auth uses [Firebase Dynamic Links][18] when sending a link that is meant to be opened in a mobile application. In order to use this feature, Dynamic Links need to be configured in the Firebase Console. - -1. Enable Firebase Dynamic Links: - 1. In the [Firebase console][1], open the **Dynamic Links** section. - 2. If you have not yet accepted the Dynamic Links terms, select **Get Started**. Go back to the main Dynamic Links dashboard. - 3. Take note of your **Dynamic Link Domain**. It should look as follows: **abc123.app.goo.gl**. This will needed when configuring the Android or iOS app to intercept the incoming link. -2. Configuring iOS applications: - 1. If you plan on handling these links from your iOS appliction, the iOS bundle ID needs to be specified in the Firebase Console project settings. In addition, the App Store ID and the Apple Developer Team ID also need to be specified. - 2. You will also need to configure the FDL universal link domain as an Associated Domain in your application capabilities. - 3. If you plan to distribute your application to iOS versions 8 and under, you will need to set your iOS bundle ID as a custom scheme for incoming URLs. - -## Handling email actions in a web application - -You can specify whether you want to handle the action code link from a web application first and then redirect to another web page or mobile application after successful completion, provided the mobile application is available. This is done by setting `HandleCodeInApp` to `false` in the `ActionCodeSettings` object. While an iOS bundle ID or Android package name are not required, providing them will allow the user to redirect back to the specified app on email action code completion. - -The web URL used here, is the one configured in the email action templates section. A default one is provisioned for all projects. Refer to [customizing email handlers](#create–custom-email-action-handlers) to learn more on how to customize the email action handler. - -In this case, the link within the `continueURL` query parameter will be an FDL link whose payload is the `URL` specified in the ActionCodeSettings object. While you can intercept and handle the incoming link from your app without any additional dependency, we recommend using the FDL client library to parse the deep link for you. - -## Handling email actions in a mobile application - -You can specify whether you want to handle the action code link within your mobile application first, provided it is installed. With Android applications, you also have the ability to specify via the `AndroidInstallIfNotAvailable` that the app is to be installed if the device supports it and it is not already installed. If the link is clicked from a device that does not support the mobile application, it is opened from a web page instead. This is done by setting handleCodeInApp to true in the FIRActionCodeSettings (Obj-C) or ActionCodeSettings (Swift) object. The mobile application's Android package name or iOS bundle ID will also need to be specified.The fallback web URL used here, when no mobile app is available, is the one configured in the email action templates section. A default one is provisioned for all projects. Refer to [customizing email handlers](#create–custom-email-action-handlers) to learn more on how to customize the email action handler. - -In this case, the mobile app link sent to the user will be an FDL link whose payload is the action code URL, configured in the Console, with the query parameters `oobCode`, `mode`, `apiKey` and `continueUrl`. The latter will be the original URL specified in the `ActionCodeSettings` object. While you can intercept and handle the incoming link from your app without any additional dependency, we recommend using the FDL client library to parse the deep link for you. The action code can be applied directly from a mobile application similar to how it is handled from the web flow described in the [customizing email handlers](#create–custom-email-action-handlers) section. - ---- - -# Convert an anonymous account to a permanent account - -When an anonymous user signs up to your app, you might want to allow them to continue their work with their new account—for example, you might want to make the items the user added to their shopping cart before they signed up available in their new account's shopping cart. To do so, complete the following steps: - -1. When the user signs up, complete the sign-in flow for the user's authentication provider up to, but not including, calling one of the `Auth.SignIn` methods. -2. Get an `AuthCredential` for the new authentication provider -3. Pass the `AuthCredential` object to the sign-in user's `Link` method: - -```csharp -user.Link (credential, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that Link method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.ProviderAlreadyLinked: - case AuthErrorCode.CredentialAlreadyInUse: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.RequiresRecentLogin: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle link result -} - -// async/await version - -try { - User user = user.Link (credential, HandleAuthResult); - // Do your magic to handle link result -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that Link method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.ProviderAlreadyLinked: - case AuthErrorCode.CredentialAlreadyInUse: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.RequiresRecentLogin: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } -} -``` - -If the call to `Link` succeeds, the user's new account can access the anonymous account's Firebase data. - -> ![note_icon] This technique can also be used to [link any two accounts](#link-multiple-auth-providers-to-an-account). - ---- - -# Link Multiple Auth Providers to an Account - -You can allow users to sign in to your app using multiple authentication providers by linking auth provider credentials to an existing user account. Users are identifiable by the same Firebase user ID regardless of the authentication provider they used to sign in. For example, a user who signed in with a password can link a Google account and sign in with either method in the future. Or, an anonymous user can link a Facebook account and then, later, sign in with Facebook to continue using your app. - -## Link auth provider credentials to a user account - -To link auth provider credentials to an existing user account: - -1. Sign in the user using any authentication provider or method. -2. Complete the sign-in flow for the new authentication provider up to, but not including, calling one of the `SignIn` methods. -3. Get a `AuthCredential` for the new authentication provider. -4. Pass the `AuthCredential` object to the signed-in user's `Link` method: - -```csharp -var currentUser = Auth.DefaultInstance.CurrentUser; -currentUser.Link (credential, HandleAuthResultHandler); - -void HandleAuthResultHandler (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that Link method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.ProviderAlreadyLinked: - case AuthErrorCode.CredentialAlreadyInUse: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.RequiresRecentLogin: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } - - return; - } - - // Do your magic to handle link result -} - -// async/await version - -var currentUser = Auth.DefaultInstance.CurrentUser; - -try { - User user = currentUser.LinkAsync (credential); - // Do your magic to handle link result -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that Link method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.ProviderAlreadyLinked: - case AuthErrorCode.CredentialAlreadyInUse: - case AuthErrorCode.OperationNotAllowed: - case AuthErrorCode.EmailAlreadyInUse: - case AuthErrorCode.InvalidEmail: - case AuthErrorCode.RequiresRecentLogin: - case AuthErrorCode.WeakPassword: - default: - // Print error - break; - } -} -``` - -The call to `Link` will fail if the credentials are already linked to another user account. In this situation, you must handle merging the accounts and associated data as appropriate for your app: - -```csharp -var previousUser = Auth.DefaultInstance.CurrentUser; -Auth.DefaultInstance.SignIn (credential, (user, error) => { - // ... -}); -var currentUser = Auth.DefaultInstance.CurrentUser; - -// Merge previousUser and currentUser accounts and data -// ... -``` - -If the call to `Link` succeeds, the user can now sign in using any linked authentication provider and access the same Firebase data. - -## Unlink an auth provider from a user account - -You can unlink an auth provider from an account, so that the user can no longer sign in with that provider. - -To unlink an auth provider from a user account, pass the provider ID to the `Unlink` method. You can get the provider IDs of the auth providers linked to a user from the `ProviderData` property. - -```csharp -var currentUser = Auth.DefaultInstance.CurrentUser; -currentUser .Unlink (providerId, HandleAuthResult); - -void HandleAuthResult (User user, NSError error) -{ - if (error != null) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)error.Code); - - // Posible error codes that Unlink method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.NoSuchProvider: - case AuthErrorCode.RequiresRecentLogin: - default: - // Print error - break; - } - } - - // Provider unlinked from account -} - -// async/await version - -try { - User user = currentUser.UnlinkAsync (providerId); - // Provider unlinked from account -} catch (NSErrorException ex) { - AuthErrorCode errorCode; - if (IntPtr.Size == 8) // 64 bits devices - errorCode = (AuthErrorCode)((long)ex.Error.Code); - else // 32 bits devices - errorCode = (AuthErrorCode)((int)ex.Error.Code); - - // Posible error codes that Unlink method could throw - // Visit https://firebase.google.com/docs/auth/ios/errors for more information - switch (errorCode) { - case AuthErrorCode.NoSuchProvider: - case AuthErrorCode.RequiresRecentLogin: - default: - // Print error - break; - } -} - -``` - ---- - -# Create custom email action handlers - -Some user management actions, such as updating a user's email address and resetting a user's password, result in emails being sent to the user. These emails contain links that recipients can open to complete or cancel the user management action. By default, user management emails link to the default action handler, which is a web page hosted at a URL in your project's Firebase Hosting domain. - -To learn more about this, please, read the following [documentation][19]. - ---- - -# Extend Firebase Authentication with Cloud Functions - -You can trigger a function in response to the creation and deletion of user accounts via Firebase Authentication. For example, you could send a welcome email to a user who has just created an account in your app. - -To learn more about this, please, read the following [documentation][20]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/auth/ios/manage-users) to see original Firebase documentation._ - -[1]: https://firebase.google.com/console/ -[2]: http://support.google.com/firebase/answer/7015592 -[3]: https://components.xamarin.com/gettingstarted/googleiossignin -[4]: https://components.xamarin.com/gettingstarted/facebookios -[5]: https://developers.facebook.com/ -[6]: https://console.firebase.google.com/u/0/project/_/settings/serviceaccounts/adminsdk -[7]: https://www.nuget.org/packages/xamarin.auth -[8]: https://components.xamarin.com/gettingstarted/xamarin.auth -[9]: https://apps.twitter.com/ -[10]: https://github.com/settings/applications/new -[11]: https://github.com/settings/developers -[12]: https://support.google.com/firebase/answer/7000714 -[14]: https://firebase.google.com/pricing/ -[15]: https://firebase.google.com/docs/cloud-messaging/ios/certs -[16]: https://developer.apple.com/membercenter/index.action -[17]: https://firebase.google.com/docs/auth/admin/create-custom-tokens -[18]: https://components.xamarin.com/view/firebaseiosdynamiclinks -[19]: https://firebase.google.com/docs/auth/custom-email-handler -[20]: https://firebase.google.com/docs/auth/ios/account-linking -[21]: https://github.com/xamarin/GoogleApisForiOSComponents/blob/master/Firebase.DynamicLinks/component/GettingStarted.md -[22]: https://firebase.google.com/docs/auth/admin/custom-claims -[note_icon]: https://cdn3.iconfinder.com/data/icons/UltimateGnome/22x22/apps/gnome-app-install-star.png -[warning_icon]: https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-20.png +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/CloudFirestore/Details.md b/docs/Firebase/CloudFirestore/Details.md index 659b8b9c7..91ccaa6c6 100755 --- a/docs/Firebase/CloudFirestore/Details.md +++ b/docs/Firebase/CloudFirestore/Details.md @@ -1,27 +1,18 @@ -Use Firebase flexible, scalable NoSQL cloud database to store and sync data for client- and server-side development. +# Firebase Cloud Firestore -Firebase Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud Platform. Like Firebase Database, it keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity. Firebase Cloud Firestore also offers seamless integration with other Firebase and Google Cloud Platform products, including Cloud Functions. +This path is retained for existing links, but this repository does not maintain a copied Firebase Cloud Firestore walkthrough. -_**Note:**_ _Firebase Cloud Firestore is currently in beta release. Feature availability and support for product integrations and platforms will continue to improve as the product matures. For more information on existing limitations in Cloud Firestore, see the [limits and quotas documentation](https://firebase.google.com/docs/firestore/quotas)._ +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## Key capabilities +## Native Documentation -| | | -|--:|---| -| Flexibility | The Firebase Cloud Firestore data model supports flexible, hierarchical data structures. Store your data in documents, organized into collections. Documents can contain complex nested objects in addition to subcollections. | -| Expressive querying | In Firebase Cloud Firestore, you can use queries to retrieve individual, specific documents or to retrieve all the documents in a collection that match your query parameters. Your queries can include multiple, chained filters and combine filtering and sorting. They're also indexed by default, so query performance is proportional to the size of your result set, not your data set. | -| Realtime updates | Like Firebase Database, Firebase Cloud Firestore uses data synchronization to update data on any connected device. However, it's also designed to make simple, one-time fetch queries efficiently. | -| Offline support | Firebase Cloud Firestore caches data that your app is actively using, so the app can write, read, listen to, and query data even if the device is offline. When the device comes back online, Firebase Cloud Firestore synchronizes any local changes back to Firebase Cloud Firestore. | -| Designed to scale | Firebase Cloud Firestore brings you the best of Google Cloud Platform's powerful infrastructure: automatic multi-region data replication, strong consistency guarantees, atomic batch operations, and real transaction support. We've designed Firebase Cloud Firestore to handle the toughest database workloads from the world's biggest apps. | +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Cloud Firestore documentation: https://firebase.google.com/docs/firestore -## How does it work? +## Binding Package -Firebase Cloud Firestore is a cloud-hosted, NoSQL database that your iOS, Android, and web apps can access directly via native SDKs. +- Package README: [../NuGet/CloudFirestore.md](../NuGet/CloudFirestore.md) +- Package ID: `AdamE.Firebase.iOS.CloudFirestore` +- Managed namespace: `Firebase.CloudFirestore` -Following Firebase Cloud Firestore's NoSQL data model, you store data in documents that contain fields mapping to values. These documents are stored in collections, which are containers for your documents that you can use to organize your data and build queries. Documents support many different [data types](https://firebase.google.com/docs/firestore/manage-data/data-types), from simple strings and numbers, to complex, nested objects. You can also create subcollections within documents and build hierarchical data structures that scale as your database grows. The Firebase Cloud Firestore data model supports whatever data structure works best for your app. - -Additionally, querying in Firebase Cloud Firestore is expressive, efficient, and flexible. Create shallow queries to retrieve data at the document level without needing to retrieve the entire collection, or any nested subcollections. Add sorting, filtering, and limits to your queries or cursors to paginate your results. To keep data in your apps current, without retrieving your entire database each time an update happens, add realtime listeners. Adding realtime listeners to your app notifies you with a data snapshot whenever the data your client apps are listening to changes, retrieving only the new changes. - -Protect access to your data in Firebase Cloud Firestore with Firebase Authentication and Firebase Cloud Firestore Security Rules for Android, iOS, and JavaScript, or Identity and Access Management (IAM) for server-side languages. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/firestore/) to see original Firebase documentation._ \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/CloudFirestore/GettingStarted.md b/docs/Firebase/CloudFirestore/GettingStarted.md index 0f15d2c91..91ccaa6c6 100755 --- a/docs/Firebase/CloudFirestore/GettingStarted.md +++ b/docs/Firebase/CloudFirestore/GettingStarted.md @@ -1,1560 +1,18 @@ -# Firebase Cloud Firestore on iOS +# Firebase Cloud Firestore -## Table of content +This path is retained for existing links, but this repository does not maintain a copied Firebase Cloud Firestore walkthrough. -- [Firebase Cloud Firestore on iOS](#firebase-cloud-firestore-on-ios) - - [Table of content](#table-of-content) - - [Create a Cloud Firestore project](#create-a-cloud-firestore-project) - - [Add Firebase to your app](#add-firebase-to-your-app) - - [Configure Remote Config in your app](#configure-remote-config-in-your-app) -- [Add and Manage Data](#add-and-manage-data) - - [Cloud Firestore Data Model](#cloud-firestore-data-model) - - [Documents](#documents) - - [Collections](#collections) - - [References](#references) - - [Hierarchical Data](#hierarchical-data) - - [Subcollections](#subcollections) - - [Choose a Data Structure](#choose-a-data-structure) - - [Supported Data Types](#supported-data-types) - - [Add Data to Cloud Firestore](#add-data-to-cloud-firestore) - - [Set a document](#set-a-document) - - [Data types](#data-types) - - [Add a document](#add-a-document) - - [Update a document](#update-a-document) - - [Update fields in nested objects](#update-fields-in-nested-objects) - - [Transactions and Batched Writes](#transactions-and-batched-writes) - - [Updating data with transactions](#updating-data-with-transactions) - - [Passing information out of transactions](#passing-information-out-of-transactions) - - [Transaction failure](#transaction-failure) - - [Batched writes](#batched-writes) - - [Delete Data from Cloud Firestore](#delete-data-from-cloud-firestore) - - [Delete fields](#delete-fields) - - [Delete collections](#delete-collections) - - [Delete data with the Firebase CLI](#delete-data-with-the-firebase-cli) - - [Manage Cloud Firestore with the Firebase Console](#manage-cloud-firestore-with-the-firebase-console) -- [Query Data](#query-data) - - [Get Data with Cloud Firestore](#get-data-with-cloud-firestore) - - [Example data](#example-data) - - [Get a document](#get-a-document) - - [Get multiple documents from a collection](#get-multiple-documents-from-a-collection) - - [Get all documents in a collection](#get-all-documents-in-a-collection) - - [Get Realtime Updates with Cloud Firestore](#get-realtime-updates-with-cloud-firestore) - - [Events for local changes](#events-for-local-changes) - - [Listen to multiple documents in a collection](#listen-to-multiple-documents-in-a-collection) - - [View changes between snapshots](#view-changes-between-snapshots) - - [Detach a listener](#detach-a-listener) - - [Handle listen errors](#handle-listen-errors) - - [Perform Simple and Compound Queries in Cloud Firestore](#perform-simple-and-compound-queries-in-cloud-firestore) - - [Example data](#example-data) - - [Simple queries](#simple-queries) - - [Compound queries](#compound-queries) - - [Order and Limit Data with Cloud Firestore](#order-and-limit-data-with-cloud-firestore) - - [Order and limit data](#order-and-limit-data) - - [Paginate Data with Query Cursors](#paginate-data-with-query-cursors) - - [Add a simple cursor to a query](#add-a-simple-cursor-to-a-query) - - [Use a document snapshot to define the query cursor](#use-a-document-snapshot-to-define-the-query-cursor) - - [Paginate a query](#paginate-a-query) - - [Set multiple cursor conditions](#set-multiple-cursor-conditions) - - [Cities](#cities) - - [Manage Indexes in Cloud Firestore](#manage-indexes-in-cloud-firestore) -- [Secure Data](#secure-data) - - [Secure Data in Cloud Firestore](#secure-data-in-cloud-firestore) -- [Enable Offline Data](#enable-offline-data) - - [Configure offline persistence](#configure-offline-persistence) - - [Listen to offline data](#listen-to-offline-data) - - [Get offline data](#get-offline-data) - - [Query offline data](#query-offline-data) +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## Create a Cloud Firestore project +## Native Documentation -* Open the [Firebase Console][1] and create a new project. -* In the *Database* section, click on **Realtime Database** combobox and then **Try Firestore Beta**. -* Click Enable. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Cloud Firestore documentation: https://firebase.google.com/docs/firestore -> ![note_icon] _**Note:**_ _Cloud Firestore and App Engine: You can't use both Cloud Firestore and Cloud Datastore in the same project, which might affect apps using App Engine. Try using Cloud Firestore with a different project._ +## Binding Package -When you create a Cloud Firestore project, it also enables the API in the [Cloud API Manager][]. +- Package README: [../NuGet/CloudFirestore.md](../NuGet/CloudFirestore.md) +- Package ID: `AdamE.Firebase.iOS.CloudFirestore` +- Managed namespace: `Firebase.CloudFirestore` -## Add Firebase to your app - -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. - -## Configure Remote Config in your app - -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -App.Configure (); -``` - ---- - -# Add and Manage Data - -## Cloud Firestore Data Model - -Cloud Firestore is a NoSQL, document-oriented database. Unlike a SQL database, there are no tables or rows. Instead, you store data in documents, which are organized into collections. - -Each document contains a set of key-value pairs. Cloud Firestore is optimized for storing large collections of small documents. - -All documents must be stored in collections. Documents can contain subcollections and nested objects, both of which can include primitive fields like strings or complex objects like lists. - -Collections and documents are created implicitly in Cloud Firestore. Simply assign data to a document within a collection. If either the collection or document does not exist, Cloud Firestore creates it. - -### Documents - -In Cloud Firestore, the unit of storage is the document. A document is a lightweight record that contains fields, which map to values. Each document is identified by a name. - -A document representing a user `alovelace` might look like this: - -![document_icon] **alovelace** -* **first** : "Ada" -* **last** : Lovelace -* **born** : 1815 - -> ![note_icon] _**Note:**_ _Cloud Firestore supports a variety of data types for values: boolean, number, string, geo point, binary blob, and timestamp. You can also use arrays or nested objects, called maps, to structure data within a document._ - -Complex, nested objects in a document are called maps. For example, you could structure the user's name from the example above with a map, like this: - -![document_icon] **alovelace** -* **name**: - * **first** : "Ada" - * **last** : Lovelace -* **born** : 1815 - -You may notice that documents look a lot like JSON. In fact, they basically are. There are some differences (for example, documents support extra data types and are limited in size to 1 MB), but in general, you can treat documents as lightweight JSON records. - -### Collections - -Documents live in collections, which are simply containers for documents. For example, you could have a users collection to contain your various users, each represented by a document: - -![collections_icon] **users** -* ![document_icon] **alovelace** - * **first** : "Ada" - * **last** : "Lovelace" - * **born** : 1815 -* ![document_icon] **aturing** - * **first** : "Alan" - * **last** : "Turing" - * **born** : 1912 - -Cloud Firestore is schemaless, so you have complete freedom over what fields you put in each document and what data types you store in those fields. Documents within the same collection can all contain different fields or store different types of data in those fields. However, it's a good idea to use the same fields and data types across multiple documents, so that you can query the documents more easily. - -A collection contains documents and nothing else. It can't directly contain raw fields with values, and it can't contain other collections. (See [Hierarchical Data](#hierarchical-data) for an explanation of how to structure more complex data in Cloud Firestore.) - -The names of documents within a collection are unique. You can provide your own keys, such as user IDs, or you can let Cloud Firestore create random IDs for you automatically (for example, by calling `Add` method). - -You do not need to "create" or "delete" collections. After you create the first document in a collection, the collection exists. If you delete all of the documents in a collection, it no longer exists. - -### References - -Every document in Cloud Firestore is uniquely identified by its location within the database. The previous example showed a document `alovelace` within the collection `users`. To refer to this location in your code, you can create a _reference_ to it: - -```csharp -var alovelaceDocument = db.GetCollection ("users").GetDocument ("alovelace"); -``` - -A reference is a lightweight object that just points to a location in your database. You can create a reference whether or not data exists there, and creating a reference does not perform any network operations. - -You can also create references to collections: - -```csharp -var userCollection = db.GetCollection ("users"); -``` - -> ![note_icon] _**Note:**_ _Collection references and document references are two distinct types of references and let you perform different operations. For example, you could use a collection reference for querying the documents in the collection, and you could use a document reference to read or write an individual document._ - -For convenience, you can also create references by specifying the path to a document or collection as a string, with path components separated by a forward slash (/). For example, to create a reference to the `alovelace` document: - -```csharp -var alovelaceDocument = db.GetDocument ("users/alovelace"); -``` - -### Hierarchical Data - -To understand how hierarchical data structures work in Cloud Firestore, consider an example chat app with messages and chat rooms. - -You can create a collection called `rooms` to store different chat rooms: - -![collections_icon] **rooms** -* ![document_icon] **roomA** - * **name** : "my chat room" -* ![document_icon] **roomB** - * ... - -Now that you have chat rooms, decide how to store your messages. You might not want to store them in the chat room's document. Documents in Cloud Firestore should be lightweight, and a chat room could contain a large number of messages. However, you can create additional collections within your chat room's document, as subcollections. - -#### Subcollections - -The best way to store messages in this scenario is by using subcollections. A subcollection is a collection associated with a specific document. - -> ![note_icon] _**Note:**_ _Querying across subcollections is not currently supported in Cloud Firestore. If you need to query data across collections, use root-level collections._ - -You can create a subcollection called `messages` for every room document in your `rooms` collection: - -![collections_icon] **rooms** -* ![document_icon] **roomA** - * **name** : "my chat room" - * ![collections_icon] **messages** - * ![document_icon] **message1** - * **from** : "alex" - * **msg** : "Hello World!" - * ![document_icon] **message2** - * ... -* ![document_icon] **roomB** - * ... - -In this example, you would create a reference to a message in the subcollection with the following code: - -```csharp -var message = db.GetCollection ("rooms").GetDocument ("roomA") - .GetCollection ("messages").GetDocument ("message1"); -``` - -Notice the alternating pattern of collections and documents. Your collections and documents must always follow this pattern. You cannot reference a collection in a collection or a document in a document. - -Subcollections allow you to structure data hierarchically, making data easier to access. To get all messages in `roomA`, you can simply access the `db.GetCollection ("rooms").GetDocument ("roomA").GetCollection ("messages");` collection. - -Documents in subcollections can contain subcollections as well, allowing you to further nest data. You can nest data up to 100 levels deep. - -> ![warning_icon] _**Warning:**_ _Deleting a document does not delete its subcollections!_ -> -> _When you delete a document that has associated subcollections, the subcollections are not deleted. They are still accessible by reference. For example, there may be a document referenced by `db.GetCollection ("coll").GetDocument ("doc").GetCollection ("subcoll").GetDocument ("subdoc")` even though the document referenced by `db.GetCollection ("coll").GetDocument ("doc")` no longer exists. If you want to delete documents in subcollections when deleting a document, you must do so manually, as shown in [Delete Collections](#delete_collections)._ - -## Choose a Data Structure - -Remember, when you structure your data in Cloud Firestore, you have a few different options: documents, multiple collections, and subcollections within documents. Read this [documentation][3] to learn more about this. Consider the advantages of each option as they relate to your use case. - -## Supported Data Types - -This [page][4] describes the data types that Cloud Firestore supports. - -## Add Data to Cloud Firestore - -There are several ways to write data to Cloud Firestore: - -* Set the data of a document within a collection, explicitly specifying a document identifier. -* Add a new document to a collection. In this case, Cloud Firestore automatically generates the document identifier. -* Create an empty document with an automatically generated identifier, and assign data to it later. - -This guide explains how to use the set, add, or update individual documents in Cloud Firestore. If you want to write data in bulk, see [Transactions and Batched Writes](#transactions-and-batched-writes). - -### Set a document - -To create or overwrite a single document, use the `SetData` method: - -```csharp -var docData = new Dictionary { - { "name", "Los Angeles" }, - { "state", "CA" }, - { "country", "USA" } -}; - -// Add a new document in collection "cities" -db.GetCollection ("cities").GetDocument ("LA").SetData (docData, HandleDocumentActionCompletion); - -void HandleDocumentActionCompletion (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error writing document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Document successfully written!"); -} -``` - -Also, you can use `SetDataAsync` method for `await/async` lovers: - -```csharp -var docData = new Dictionary { - { "name", "Los Angeles" }, - { "state", "CA" }, - { "country", "USA" } -}; - -// Add a new document in collection "cities" -try { - await db.GetCollection ("cities").GetDocument ("LA").SetDataAsync (docData); - System.Console.WriteLine ("Document successfully written!"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error writing document: {ex.Error.LocalizedDescription}"); -} -``` - -If the document does not exist, it will be created. If the document does exist, its contents will be overwritten with the newly provided data, unless you specify that the data should be merged into the existing document, as follows: - -```csharp -var docData = new Dictionary { - { "name", "Los Angeles" }, - { "state", "CA" }, - { "country", "USA" } -}; - -// Add a new document in collection "cities" -db.GetCollection ("cities").GetDocument ("LA").SetData (docData, SetOptions.CreateMerge ()); -``` - -If you're not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents. - -#### Data types - -Cloud Firestore lets you write a variety of data types inside a document, including strings, booleans, numbers, dates, null, and nested arrays and objects. Cloud Firestore always stores numbers as doubles, regardless of what type of number you use in your code: - -```csharp -var docData = new Dictionary { - { "stringExample", "Hello world!" }, - { "booleanExample", true }, - { "numberExample", 3.14 }, - { "dateExample", new NSDate () }, - { "arrayExample", NSArray.FromObjects (true, 3, "hello") }, - { "nullExample", null }, - { "objectExample", NSDictionary.FromObjectsAndKeys ( - keys: new object [] { "a", "b" }, - objects: new object [] { 5, NSDictionary.FromObjectsAndKeys ( - keys: new object [] { "nested" }, - objects: new object [] { "foo" } - ) } - ) } -}; - -db.GetCollection ("data").GetDocument ("one").SetData (docData, HandleDocumentActionCompletion); - -void HandleDocumentActionCompletion (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error writing document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Document successfully written!"); -} - -// Async/await way -try { - await db.GetCollection ("data").GetDocument ("one").SetDataAsync (docData); - System.Console.WriteLine ("Document successfully written!"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error writing document: {ex.Error.LocalizedDescription}"); -} -``` - -> ![note_icon] _**Important:**_ _To create nested objects, you cannot use nested Dictionaries, doing so will throw an exception. To create nested object, you need to use NSDictionaries as shown above._ - -### Add a document - -When you use `SetData` method to create a document, you must specify an ID for the document to create. For example: - -```csharp -db.GetCollection ("cities").GetDocument ("new_city_id").SetData (data); -``` - -But sometimes there isn't a meaningful ID for the document, and it's more convenient to let Cloud Firestore auto-generate an ID for you. You can do this by calling `AddDocument` method: - -```csharp -var docData = new Dictionary { - { "name", "Tokyo" }, - { "country", "Japan" } -}; - -DocumentReference newDocument = null; -newDocument = db.GetCollection ("cities").AddDocument (docData, HandleAddDocumentCompletion); - -void HandleAddDocumentCompletion (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error adding document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ($"Document added with ID: {newDocument.Id}"); -} -``` - -> ![note_icon] **_Important:_** _Unlike "push IDs" in the Firebase Realtime Database, Cloud Firestore auto-generated IDs do not provide any automatic ordering. If you want to be able to order your documents by creation date, you should store a timestamp as a field in the documents._ - -In some cases, it can be useful to create a document reference with an auto-generated ID, then use the reference later. For this use case, you can call `CreateDocument` method: - -```csharp -var newCityDocument = db.GetCollection ("cities").CreateDocument (); -// later -newCityDocument.SetData ... -``` - -Behind the scenes, `AddDocument (...)` and `.CreateDocument ().SetData (...)` are completely equivalent, so you can use whichever is more convenient. - -### Update a document - -To update some fields of a document without overwriting the entire document, use the `UpdateData` method: - -```csharp -var docData = new Dictionary { { "capital", true } }; -var washingtonDocument = db.GetCollection ("cities").GetDocument ("DC"); - -// Set the "capital" field of the city 'DC' -washingtonDocument.UpdateData (docData, HandleDocumentActionCompletion); - -void HandleDocumentActionCompletion (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error updating document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Document successfully updated"); -} -``` - -An `async/await` version of this: - -```csharp -var docData = new Dictionary { { "capital", true } }; -var washingtonDocument = db.GetCollection ("cities").GetDocument ("DC"); - -// Set the "capital" field of the city 'DC' -try { - await washingtonDocument.UpdateDataAsync (docData); - System.Console.WriteLine ("Document successfully updated"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error updating document: {ex.Error.LocalizedDescription}"); -} -``` - -#### Update fields in nested objects - -If your document contains nested objects, you can use "dot notation" to reference nested fields within the document when you call `UpdateData` method: - -```csharp -var frankData = new Dictionary { - { "name", "Frank" }, - { "favorites", NSDictionary.FromObjectsAndKeys ( - keys: new object [] { "food", "color", "subject" }, - objects: new object [] { "Pizza", "Blue", "recess" } - ) }, - { "age", 12 } -}; -var frankDocument = db.GetCollection ("users").GetDocument ("frank"); -frankDocument.SetData (frankData); - -// To update age and favorite color: -var updateData = new Dictionary { - { "age", 13 }, - { "favorites.color", "Red" } -}; -db.GetCollection ("users").GetDocument ("frank").UpdateData (updateData, HandleDocumentActionCompletion); - -void HandleDocumentActionCompletion (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error updating document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Document successfully updated"); -} - -// Async/await way -try { - await db.GetCollection ("users").GetDocument ("frank").UpdateDataAsync (updateData); - System.Console.WriteLine ("Document successfully updated"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error updating document: {ex.Error.LocalizedDescription}"); -} -``` - -You can also add server timestamps to specific fields in your documents, to track when an update was received by the server: - -```csharp -var updateData = new Dictionary { { "lastUpdated", FieldValue.ServerTimestamp } }; -db.GetCollection ("objects").GetDocument ("some-id").UpdateData (updateData, HandleDocumentActionCompletion); - -void HandleDocumentActionCompletion (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error updating document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Document successfully updated"); -} - -// Async/await way -try { - await db.GetCollection ("objects").GetDocument ("some-id").UpdateDataAsync (updateData); - System.Console.WriteLine ("Document successfully updated"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error updating document: {ex.Error.LocalizedDescription}"); -} -``` - -## Transactions and Batched Writes - -Firebase Cloud Firestore supports atomic operations for reading and writing data. In a set of atomic operations, either all of the operations succeed, or none of them are applied. There are two types of atomic operations in Firebase Cloud Firestore: - -* **Transactions:** a transaction is a set of read and write operations on one or more documents. -* **Batched Writes:** a batched write is a set of up to 500 write operations on one or more documents. - -### Updating data with transactions - -Using the Firebase Cloud Firestore client libraries, you can group multiple operations into a single transaction. Transactions are useful when you want to update a field's value based on its current value, or the value of some other field. You could increment a counter by creating a transaction that reads the current value of the counter, increments it, and writes the new value to Cloud Firestore. - -A transaction consists of any number of `GetDocument` operations followed by any number of write operations such as `SetData`, `UpdateData`, or `DeleteData`. In the case of a concurrent edit, Firebase Cloud Firestore runs the entire transaction again. For example, if a transaction reads documents and another client modifies any of those documents, Cloud Firestore retries the transaction. This feature ensures that the transaction runs on up-to-date and consistent data. - -Transactions never partially apply writes. All writes execute at the end of a successful transaction. - -When using transactions, note that: - -* Read operations must come before write operations. -* A function calling a transaction (transaction function) might run more than once if a concurrent edit affects a document that the transaction reads. -* Transaction functions should not directly modify application state. -* Transactions will fail when the client is offline. - -The following example shows how to create and run a transaction: - -```csharp -var sf = db.GetCollection ("cities").GetDocument ("SF"); -db.RunTransaction (HandleTransactionUpdate, HandleTransactionCompletion); - -NSObject HandleTransactionUpdate (Transaction transaction, out NSError error) -{ - DocumentSnapshot sfDocument = transaction.GetDocument (sf, out error); - - if (error != null) - return null; - - if (!(sfDocument.Data? ["population"] is NSNumber)) { - error = new NSError (new NSString ("AppErrorDomain"), - -1, - NSDictionary.FromObjectAndKey (new NSString ("Unable to retrieve population from snapshot"), NSError.LocalizedDescriptionKey)); - return null; - } - - var oldPopulation = (sfDocument.Data ["population"] as NSNumber).Int32Value; - - var updateData = new Dictionary { { "population", oldPopulation + 1 } }; - transaction.UpdateData (updateData, sf); - - return null; -} - -void HandleTransactionCompletion (NSObject result, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Transaction failed: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Transaction successfully committed!"); -} -``` - -An `async/await` version: - -```csharp -var sf = db.GetCollection ("cities").GetDocument ("SF"); - -try { - await db.RunTransactionAsync (HandleTransactionUpdate); - System.Console.WriteLine ("Transaction successfully committed!"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error updating document: {ex.Error.LocalizedDescription}"); -} - -NSObject HandleTransactionUpdate (Transaction transaction, out NSError error) -{ - DocumentSnapshot sfDocument = transaction.GetDocument (sf, out error); - - if (error != null) - return null; - - if (!(sfDocument.Data? ["population"] is NSNumber)) { - error = new NSError (new NSString ("AppErrorDomain"), - -1, - NSDictionary.FromObjectAndKey (new NSString ("Unable to retrieve population from snapshot"), NSError.LocalizedDescriptionKey)); - return null; - } - - var oldPopulation = (sfDocument.Data ["population"] as NSNumber).Int32Value; - - var updateData = new Dictionary { { "population", oldPopulation + 1 } }; - transaction.UpdateData (updateData, sf); - - return null; -} -``` - -#### Passing information out of transactions - -Do not modify application state inside of your transaction functions. Doing so will introduce concurrency issues, because transaction functions can run multiple times and are not guaranteed to run on the UI thread. Instead, pass information you need out of your transaction functions. The following example builds on the previous example to show how to pass information out of a transaction: - -```csharp -var sf = db.GetCollection ("cities").GetDocument ("SF"); -db.RunTransaction (HandleTransactionUpdate, HandleTransactionCompletion); - -NSObject HandleTransactionUpdate (Transaction transaction, out NSError error) -{ - DocumentSnapshot sfDocument = transaction.GetDocument (sf, out error); - - if (error != null) - return null; - - if (!(sfDocument.Data? ["population"] is NSNumber)) { - error = new NSError (new NSString ("AppErrorDomain"), - -1, - NSDictionary.FromObjectAndKey (new NSString ("Unable to retrieve population from snapshot"), NSError.LocalizedDescriptionKey)); - return null; - } - - var oldPopulation = (sfDocument.Data ["population"] as NSNumber).Int32Value; - var newPopulation = oldPopulation + 1; - - if (newPopulation > 1000000) { - error = new NSError (new NSString ("AppErrorDomain"), - -2, - NSDictionary.FromObjectAndKey (new NSString ("Population too big"), NSError.LocalizedDescriptionKey)); - return null; - } - - var updateData = new Dictionary { { "population", newPopulation } }; - transaction.UpdateData (updateData, sf); - - return NSNumber.FromInt32 (newPopulation); -} - -void HandleTransactionCompletion (NSObject result, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error updating population: {error.LocalizedDescription}"); - return; - } - - var population = (result as NSNumber).Int32Value; - System.Console.WriteLine ($"Population increased to {population}"); -} -``` - -An `async/await` version: - -```csharp -var sf = db.GetCollection ("cities").GetDocument ("SF"); - -try { - var result = await db.RunTransactionAsync (HandleTransactionUpdate); - var population = (result as NSNumber).Int32Value; - System.Console.WriteLine ($"Population increased to {population}"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error updating population: {error.LocalizedDescription}"); -} - -NSObject HandleTransactionUpdate (Transaction transaction, out NSError error) -{ - DocumentSnapshot sfDocument = transaction.GetDocument (sf, out error); - - if (error != null) - return null; - - if (!(sfDocument.Data? ["population"] is NSNumber)) { - error = new NSError (new NSString ("AppErrorDomain"), - -1, - NSDictionary.FromObjectAndKey (new NSString ("Unable to retrieve population from snapshot"), NSError.LocalizedDescriptionKey)); - return null; - } - - var oldPopulation = (sfDocument.Data ["population"] as NSNumber).Int32Value; - var newPopulation = oldPopulation + 1; - - if (newPopulation > 1000000) { - error = new NSError (new NSString ("AppErrorDomain"), - -2, - NSDictionary.FromObjectAndKey (new NSString ("Population too big"), NSError.LocalizedDescriptionKey)); - return null; - } - - var updateData = new Dictionary { { "population", newPopulation } }; - transaction.UpdateData (updateData, sf); - - return NSNumber.FromInt32 (newPopulation); -} -``` - -#### Transaction failure - -A transaction can fail for the following reasons: - -* The transaction contains read operations after write operations. Read operations must always come before any write operations. -* The transaction read a document that was modified outside of the transaction. In this case, the transaction automatically runs again. The transaction is retried a finite number of times. - -A failed transaction returns an error and does not write anything to the database. You do not need to roll back the transaction; Cloud Firestore does this automatically. - -### Batched writes - -If you do not need to read any documents in your operation set, you can execute multiple write operations as a single batch that contains any combination of `SetData`, `UpdateData`, or `DeleteData` operations. A batch of writes completes atomically and can write to multiple documents. - -Batched writes are also useful for migrating large data sets to Cloud Firestore. A batched write can contain up to 500 operations and batching operations togethezr reduces connection overhead resulting in faster data migration. - -Batched writes have fewer failure cases than transactions and use simpler code. They are not affected by contention issues, because they don't depend on consistently reading any documents. Batched writes execute even when the user's device is offline. The following example shows how to build and commit a batch of writes: - -```csharp -// Get new write batch -var batch = db.CreateBatch (); - -// Set the value of 'NYC' -var nyc = db.GetCollection ("cities").GetDocument ("NYC"); -batch.SetData (new NSDictionary (), nyc); - -// Update the population of 'SF' -var sf = db.GetCollection ("cities").GetDocument ("SF"); -batch.UpdateData (new Dictionary { { "population", 1000000 } }, sf); - -// Delete the city 'LA' -var la = db.GetCollection ("cities").GetDocument ("LA"); -batch.DeleteDocument (la); - -// Commit the batch -batch.Commit (HandleCommitCompletion); - -void HandleCommitCompletion (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error writing batch: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Batch write succeeded."); -} - -// Async/await way: -try { - // Commit the batch - await batch.CommitAsync (); - System.Console.WriteLine ("Batch write succeeded."); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error writing batch: {error.LocalizedDescription}"); -} -``` - -## Delete Data from Cloud Firestore - -To delete a document, use the `DeleteData` method: - -```csharp -db.GetCollection ("cities").GetDocument ("DC").DeleteDocument (HandleDocumentActionCompletionHandler); - -void HandleDocumentActionCompletionHandler (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error removing document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Document successfully removed!"); -} - -// Async/await way: -try { - await db.GetCollection ("cities").GetDocument ("DC").DeleteDocumentAsync (); - System.Console.WriteLine ("Document successfully removed!"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error removing document: {ex.Error.LocalizedDescription}"); -} -``` - -### Delete fields - -To delete specific fields from a document, use the `FieldValue.Delete` property when you update a document: - -```csharp -var deleteData = new Dictionary { { "capital", FieldValue.Delete } }; -db.GetCollection ("cities").GetDocument ("BJ").UpdateData (deleteData, HandleDocumentActionCompletionHandler); - -void HandleDocumentActionCompletionHandler (NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error updating document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ("Document successfully updated!"); -} - -// Async/await way: -try { - await db.GetCollection ("cities").GetDocument ("BJ").UpdateDataAsync (deleteData); - System.Console.WriteLine ("Document successfully updated!"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error updating document: {error.LocalizedDescription}"); -} -``` - -### Delete collections - -To delete an entire collection or subcollection in Cloud Firestore, retrieve all the documents within the collection or subcollection and delete them. If you have larger collections, you may want to delete the documents in smaller batches to avoid out-of-memory errors. Repeat the process until you've deleted the entire collection or subcollection: - -```csharp -void DeleteCollection (CollectionReference collection, int batchSize, Action completion) -{ - // Limit query to avoid out-of-memory errors on large collections. - // When deleting a collection guaranteed to fit in memory, batching can be avoided entirely. - collection.LimitedTo (batchSize).GetDocuments (HandleQuerySnapshot); - - void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) - { - // An error occurred. - if (error != null) { - completion (error); - return; - } - - // There's nothing to delete. - if (snapshot.Count == 0) { - completion (null); - return; - } - - var batch = collection.Firestore.CreateBatch (); - - foreach (var document in snapshot.Documents) - batch.DeleteDocument (document.Reference); - - batch.Commit (HandleCommitCompletion); - } - - void HandleCommitCompletion (NSError error) - { - // Stop the deletion process and handle the error. Some elements - // may have been deleted. - if (error != null) { - completion (error); - return; - } - - DeleteCollection (collection, batchSize, completion); - } -} -``` - -An `async/await` version: - -```csharp -async Task DeleteCollectionAsync (CollectionReference collection, int batchSize) -{ - // Limit query to avoid out-of-memory errors on large collections. - // When deleting a collection guaranteed to fit in memory, batching can be avoided entirely. - try { - var snapshot = await collection.LimitedTo (batchSize).GetDocumentsAsync (); - - // There's nothing to delete. - if (snapshot.Count == 0) - return; - - var batch = collection.Firestore.CreateBatch (); - - foreach (var document in snapshot.Documents) - batch.DeleteDocument (document.Reference); - - await batch.CommitAsync (); - } catch (NSErrorException ex) { - // Stop the deletion process and handle the error. Some elements - // may have been deleted. - } - - await DeleteCollectionAsync (collection, batchSize); -} -``` - -### Delete data with the Firebase CLI - -You can also use the [Firebase CLI][5] to delete documents and collections. Use the following command to delete data: - -``` -firebase firestore:delete [options] <> -``` - -## Manage Cloud Firestore with the Firebase Console - -You can manage Cloud Firestore through the following actions in the [Firebase console][6]: - -* Add, edit, and delete data. -* Create and update Cloud Firestore Security Rules. -* Manage indexes. - -> ![note_icon] _**Note:**_ _During the Beta period, you can only manage Cloud Firestore from the Firebase console, not the Cloud Platform Console._ - -To learn more about this, please, read the following [documentation][7]. - ---- - -# Query Data - -## Get Data with Cloud Firestore - -There are two ways to retrieve data stored in Cloud Firestore. Either of these methods can be used with documents, collections of documents, or the results of queries: - -* Call a method to get the data. -* Set a listener to receive data-change events. - -When you set a listener, Cloud Firestore sends your listener an initial snapshot of the data, and then another snapshot each time the document changes. - -### Example data - -To get started, write some data about cities so we can look at different ways to read it back: - -```csharp -var cities = db.GetCollection ("cities"); - -cities.GetDocument ("SF").SetData (new Dictionary { - { "name", "San Francisco" }, - { "state", "CA" }, - { "country", "USA" }, - { "capital", false }, - { "population", 860000 } -}); -cities.GetDocument ("LA").SetData (new Dictionary { - { "name", "Los Angeles" }, - { "state", "CA" }, - { "country", "USA" }, - { "capital", false }, - { "population", 3900000 } -}); -cities.GetDocument ("DC").SetData (new Dictionary { - { "name", "Washington D.C." }, - { "country", "USA" }, - { "capital", true }, - { "population", 680000 } -}); -cities.GetDocument ("TOK").SetData (new Dictionary { - { "name", "Tokyo" }, - { "country", "Japan" }, - { "capital", true }, - { "population", 9000000 } -}); -cities.GetDocument ("BJ").SetData (new Dictionary { - { "name", "Beijing" }, - { "country", "China" }, - { "capital", true }, - { "population", 21500000 } -}); -``` - -### Get a document - -The following example shows how to retrieve the contents of a single document using `GetDocument`: - -```csharp -var document = db.GetCollection ("cities").GetDocument ("SF"); -document.GetDocument (HandleDocumentSnapshot); - -void HandleDocumentSnapshot (DocumentSnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error getting the document: {error.LocalizedDescription}"); - return; - } - - if (snapshot != null) { - System.Console.WriteLine ($"Document data: {snapshot.Data}"); - } else { - System.Console.WriteLine ("Document does not exist."); - } -} -``` - -An `async/await` version: - -```csharp -var document = db.GetCollection ("cities").GetDocument ("SF"); - -try { - var snapshot = await document.GetDocumentAsync (); - - if (snapshot != null) - System.Console.WriteLine ($"Document data: {snapshot.Data}"); - else - System.Console.WriteLine ("Document does not exist."); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error getting the document: {ex.Error.LocalizedDescription}"); -} -``` - -> ![note_icon] _**Note:**_ _If there is no document at the location referenced by *document*, the resulting document will be `null`._ - -### Get multiple documents from a collection - -You can also retrieve multiple documents with one request by querying documents in a collection. For example, you can use `WhereEqualsTo` methods to query for all of the documents that meet a certain condition, then use `GetDocuments` method to retrieve the results: - -```csharp -db.GetCollection ("cities").WhereEqualsTo ("capital", true).GetDocuments (HandleQuerySnapshot); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error getting documents: {error.LocalizedDescription}"); - return; - } - - foreach (var document in snapshot?.Documents) - System.Console.WriteLine ($"{document.Id} => {document.Data}"); -} -``` - -An `async/await` version: - -```csharp -try { - var snapshot = await db.GetCollection ("cities").WhereEqualsTo ("capital", true).GetDocumentsAsync (); - - foreach (var document in snapshot?.Documents) - System.Console.WriteLine ($"{document.Id} => {document.Data}"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error getting documents: {ex.Error.LocalizedDescription}"); -} -``` - -#### Get all documents in a collection - -In addition, you can retrieve all documents in a collection by omitting the `WhereEqualsTo` method filter entirely: - -```csharp -db.GetCollection ("cities").GetDocuments (HandleQuerySnapshot); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error getting documents: {error.LocalizedDescription}"); - return; - } - - foreach (var document in snapshot?.Documents) - System.Console.WriteLine ($"{document.Id} => {document.Data}"); -} -``` - -An `async/await` version: - -```csharp -try { - var snapshot = await db.GetCollection ("cities").GetDocumentsAsync (); - - foreach (var document in snapshot?.Documents) - System.Console.WriteLine ($"{document.Id} => {document.Data}"); -} catch (NSErrorException ex) { - System.Console.WriteLine ($"Error getting documents: {ex.Error.LocalizedDescription}"); -} -``` - -## Get Realtime Updates with Cloud Firestore - -You can listen to a document with the `AddSnapshotListener` method. An initial call using the callback you provide creates a document snapshot immediately with the current contents of the single document. Then, each time the contents change, another call updates the document snapshot: - -```csharp -db.GetCollection ("cities").GetDocument ("SF").AddSnapshotListener (HandleDocumentSnapshot); - -void HandleDocumentSnapshot (DocumentSnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error fetching document: {error.LocalizedDescription}"); - return; - } - - System.Console.WriteLine ($"Current data: {snapshot.Data}"); -} -``` - -#### Events for local changes - -In the previous example, the callback was called twice after setting San Francisco's population to 999999. This is because of an important feature called "latency compensation." When you perform a write, your listeners will be notified with the new data immediately, before the data is even sent to the backend database. - -As shown above, your listener will then be notified again once the data is actually written to the backend. This could take any amount of time from a few milliseconds to a few hours, depending on the state of your network connection. - -Retrieved documents have a `metadata.HasPendingWrites` property that indicates whether the document has local changes that haven't been written to the backend yet. You can use this property to repeat the previous example but show the difference between the two events: - -```csharp -db.GetCollection ("cities").GetDocument ("SF").AddSnapshotListener (HandleDocumentSnapshot); - -void HandleDocumentSnapshot (DocumentSnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error fetching document: {error.LocalizedDescription}"); - return; - } - - var source = snapshot.Metadata.HasPendingWrites ? "Local" : "Server"; - System.Console.WriteLine ($"{source} data: {snapshot.Data}"); -} -``` - -> ![note_icon] _**Note:**_ _If you just want to know when your write has completed, you can listen to the completion callback of your write function rather than using `HasPendingWrites` property. - -### Listen to multiple documents in a collection - -As with documents, you can use `AddSnapshotListener` method instead of `GetDocument` method to listen to the results of a query. This creates a query snapshot. For example, to listen to the documents with state CA: - -```csharp -db.GetCollection ("cities").WhereEqualsTo ("state", "CA").AddSnapshotListener (HandleQuerySnapshot); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error getting documents: {error.LocalizedDescription}"); - return; - } - - var cities = new string [snapshot?.Documents.Length ?? 0]; - - for (int i = 0; i < cities.Length; i++) - cities [i] = snapshot.Documents [i].Data ["name"].ToString (); - - var citiesString = string.Join (", ", cities); - - System.Console.WriteLine ($"Current cities in CA: {citiesString}"); -} -``` - -The snapshot handler will receive a new query snapshot every time the query results change (that is, when a document is added, removed, or modified). - -> ![note_icon] _**Important:**_ _As explained above under [Events for local changes](#events-for-local-changes), you will receive events immediately for your local writes. Your listener can use the `metadata.HasPendingWrites` field on each document to determine whether the document has local changes that have not yet been written to the backend._ - -#### View changes between snapshots - -It is often useful to see the actual changes to query results between query snapshots, instead of simply using the entire query snapshot. For example, you may want to maintain a cache as individual documents are added, removed, and modified: - -```csharp -db.GetCollection ("cities").WhereEqualsTo ("state", "CA").AddSnapshotListener (HandleQuerySnapshot); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error getting documents: {error.LocalizedDescription}"); - return; - } - - foreach (var documentChange in snapshot?.DocumentChanges) { - switch (documentChange.Type) { - case DocumentChangeType.Added: - System.Console.WriteLine ($"New city: {documentChange.Document.Data}"); - break; - case DocumentChangeType.Modified: - System.Console.WriteLine ($"Modified city: {documentChange.Document.Data}"); - break; - case DocumentChangeType.Removed: - System.Console.WriteLine ($"Removed city: {documentChange.Document.Data}"); - break; - } - } -} -``` - -> ![note_icon] _**Important:**_ _The first query snapshot contains **added** events for all existing documents that match the query. This is because you're getting a set of changes that bring your query snapshot current with the initial state of the query. This allows you, for instance, to directly populate your UI from the changes you receive in the first query snapshot, without needing to add special logic for handling the initial state._ - -The initial state can come from the server directly, or from a local cache. If there is state available in a local cache, the query snapshot will be initially populated with the cached data, then updated with the server's data when the client has caught up with the server's state. - -#### Detach a listener - -When you are no longer interested in listening to your data, you must detach your listener so that your event callbacks stop getting called. This allows the client to stop using bandwidth to receive updates. You can use the unsubscribe function on `AddSnapshotListener` method to stop listening to updates: - -```csharp -var listener = db.GetCollection ("cities").AddSnapshotListener (HandleQuerySnapshot); - -// ... - -// Stop listening to changes -listener.Remove (); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - // ... -} -``` - -#### Handle listen errors - -A listen may occasionally fail — for example, due to security permissions, or if you tried to listen on an invalid query. (Learn more about [valid and invalid queries](#compound_queries).) To handle these failures, you can provide an error callback when you attach your snapshot listener. After an error, the listener will not receive any more events, and there is no need to detach your listener. - -```csharp -db.GetCollection ("cities").AddSnapshotListener (HandleQuerySnapshot); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error retreiving collection: {error.LocalizedDescription}"); - return; - } -} -``` - -## Perform Simple and Compound Queries in Cloud Firestore - -Cloud Firestore provides powerful query functionality for specifying which documents you want to retrieve from a collection. These queries can also be used with either `GetDocument` or `AddSnapshotListener` methods, as described in [Get Data](#get-data-with-cloud-firestore) and [Get Realtime Updates](#get-realtime-updates-with-cloud-firestore). - -### Example data - -To get started, write some data about cities so we can look at different ways to read it back: - -```csharp -var cities = db.GetCollection ("cities"); - -cities.GetDocument ("SF").SetData (new Dictionary { - { "name", "San Francisco" }, - { "state", "CA" }, - { "country", "USA" }, - { "capital", false }, - { "population", 860000 } -}); -cities.GetDocument ("LA").SetData (new Dictionary { - { "name", "Los Angeles" }, - { "state", "CA" }, - { "country", "USA" }, - { "capital", false }, - { "population", 3900000 } -}); -cities.GetDocument ("DC").SetData (new Dictionary { - { "name", "Washington D.C." }, - { "country", "USA" }, - { "capital", true }, - { "population", 680000 } -}); -cities.GetDocument ("TOK").SetData (new Dictionary { - { "name", "Tokyo" }, - { "country", "Japan" }, - { "capital", true }, - { "population", 9000000 } -}); -cities.GetDocument ("BJ").SetData (new Dictionary { - { "name", "Beijing" }, - { "country", "China" }, - { "capital", true }, - { "population", 21500000 } -}); -``` - -### Simple queries - -The following query returns all cities with state **CA**: - -```csharp -// Create a reference to the cities collection -var cities = db.GetCollection ("cities"); - -// Create a query against the collection. -var query = cities.WhereEqualsTo ("state", "CA"). -``` - -The following query returns all the capital cities: - -```csharp -var capitalCities = db.GetCollection ("cities").WhereEqualsTo ("state", true); -``` - -There are some `WhereFoo` methods to help in different comparison scenarios: - -```csharp -cities.WhereEqualsTo ("state", "CA"); -cities.WhereLessThan ("population", 100000); -cities.WhereGreaterThanOrEqualsTo ("name", "San Francisco"); -``` - -### Compound queries - -You can also chain multiple `WhereFoo` methods to create more specific queries (logical `AND`). However, to combine the equality operator (`==`) with a range comparison (`<`, `<=`, `>`, or `>=`), make sure to create a [custom index][8]. - -```csharp -cities.WhereEqualsTo ("state", "CO").WhereEqualsTo ("name", "Denver"); -cities.WhereEqualsTo ("state", "CA").WhereLessThan ("population", 1000000); -``` - -Additionally, you can only perform range comparisons (`<`, `<=`, `>`, `>=`) on a single field: - -![correct_icon] **Valid:** Range filters on only one field: - -```csharp -cities.WhereGreaterThanOrEqualsTo ("state", "CA") - .WhereLessThanOrEqualsTo ("state", "IN"); - -cities.WhereEqualsTo ("state", "CA") - .WhereGreaterThan ("population", 1000000); -``` - -![wrong_icon] **Invalid:** Range filters on different fields: - -```csharp -cities.WhereGreaterThanOrEqualsTo ("state", "CA") - .WhereGreaterThan ("population", 1000000); -``` - -## Order and Limit Data with Cloud Firestore - -Cloud Firestore provides powerful query functionality for specifying which documents you want to retrieve from a collection. These queries can also be used with either `GetDocuments` or `AddSnapshotListener` methods, as described in [Get Data](#get-data-with-cloud-firestore). - -### Order and limit data - -Cloud Firestore also lets you specify the sort order for your data and specify a limit to how many documents you want to retrieve using `OrderedBy` and `LimitedTo` methods. For example, you could query for the first 3 cities alphabetically with: - -```csharp -cities.OrderedBy ("name").LimitedTo (3); -``` - -You could also sort in descending order to get the last 3 cities: - -```csharp -cities.OrderedBy ("name", true).LimitedTo (3); -``` - -You can also order by multiple fields. For example, if you wanted to order by state, and within each state order by population in descending order: - -```csharp -cities.OrderedBy ("state") - .OrderedBy ("population", true); -``` - -You can combine `WhereFoo` filters with `OrderedBy` and `LimitedTo` methods. In the following example, the queries define a population threshold, sort by population in ascending order, and return only the first few results that exceed the threshold: - -```csharp -cities.WhereGreaterThan ("population", 100000) - .OrderedBy ("population") - .LimitedTo (2); -``` - -However, if you have a filter with a range comparison (<, <=, >, >=), your first ordering must be on the same field: - -![correct_icon] **Valid:** Range filter and `OrderedBy` on the same field: - -```csharp -cities.WhereGreaterThan ("population", 100000) - .OrderedBy ("population"); -``` - -![wrong_icon] **Invalid:** Range filter and first `OrderedBy` on different fields: - -```csharp -cities.WhereGreaterThan ("population", 100000) - .OrderedBy ("population"); -``` - -## Paginate Data with Query Cursors - -With query cursors in Cloud Firestore, you can split data returned by a query into batches according to the parameters you define in your query. - -Query cursors define the start and end points for a query, allowing you to: - -* Return a subset of the data. -* Paginate query results. - -However, to define a specific range for a query, you should use the `WhereFoo` methods described in [Simple Queries](#simple_queries). - -### Add a simple cursor to a query - -Use the `StartingAt` or `StartingAfter` methods to define the start point for a query. The `StartingAt` method includes the start point, while the `StartingAfter` method excludes it. - -For example, if you use `StartingAt (A)` in a query, it returns the entire alphabet. If you use `StartingAfter (A)` instead, it returns B-Z: - -```csharp -// Get all cities with population over one million, ordered by population. -cities.OrderedBy ("population") - .StartingAt (new object [] { 1000000 }); -``` - -Similarly, use the `EndingAt` or `EndingBefore` methods to define an end point for your query results: - -```csharp -// Get all cities with population less than one million, ordered by population. -cities.OrderedBy ("population") - .EndingAt (new object [] { 1000000 }); -``` - -### Use a document snapshot to define the query cursor - -You can also pass a document snapshot to the cursor clause as the start or end point of the query cursor. The values in the document snapshot serve as the values in the query cursor. - -For example, take a snapshot of a "San Francisco" document in your data set of cities and populations. Then, use that document snapshot as the start point for your population query cursor. Your query will return all the cities with a population larger than or equal to San Francisco's, as defined in the document snapshot: - -```csharp -db.GetCollection ("cities").GetDocument ("SF").AddSnapshotListener (HandleDocumentSnapshot); - -void HandleDocumentSnapshot (DocumentSnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error fetching document: {error.LocalizedDescription}"); - return; - } - - // Get all cities with a population greater than or equal to San Francisco. - var sfSizeOrBigger = db.GetCollection ("cities") - .OrderedBy ("population") - .StartingAt (snapshot); -} -``` - -### Paginate a query - -Paginate queries by combining query cursors with the `LimitedTo` method. For example, use the last document in a batch as the start of a cursor for the next batch: - -```csharp -// Construct query for first 25 cities, ordered by population -var first = db.GetCollection ("cities") - .OrderedBy ("population") - .LimitedTo (25); - -first.AddSnapshotListener (HandleQuerySnapshot); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error retreving cities: {error.LocalizedDescription}"); - return; - } - - if (snapshot.Documents.Length == 0) { - // The collection is empty. - return; - } - - // Construct a new query starting after this document, - // retrieving the next 25 cities. - var lastSnapshot = snapshot.Documents.Last (); - var next = db.GetCollection ("cities") - .OrderedBy ("population") - .StartingAfter (lastSnapshot); - - // Use the query for pagination. - // ... -} -``` - -### Set multiple cursor conditions - -To add more granularity to your cursor's start or end point, you can specify multiple conditions in the cursor clause. This is particularly useful if your data set includes fields where the first condition in a cursor clause would return multiple results. Use multiple conditions to further specify the start or end point and reduce ambiguity. - -For example, in a data set containing all the cities named "Springfield" in the United States, there would be multiple start points for a query set to start at "Springfield": - -##### Cities - -| Name | State | -|-------------|---------------| -| Springfield | Massachusetts | -| Springfield | Missouri | -| Springfield | Wisconsin | - -To start at a specific Springfield, you could add the state as a secondary condition in your cursor clause: - -```csharp -// Will return all Springfields -db.GetCollection ("cities") - .OrderedBy ("name") - .OrderedBy ("state") - .StartingAt (new object [] { "Springfield" }); - -// Will return "Springfield, Missouri" and "Springfield, Wisconsin" -db.GetCollection ("cities") - .OrderedBy ("name") - .OrderedBy ("state") - .StartingAt (new object [] { "Springfield", "Missouri" }); -``` - -## Manage Indexes in Cloud Firestore - -Cloud Firestore requires an index for every query, to ensure the best performance. All document fields are automatically indexed, so queries that only use equality clauses don't need additional indexes. If you attempt a compound query with a range clause that doesn't map to an existing index, you receive an error. The error message includes a direct link to create the missing index in the Firebase console. - -Follow the generated link to the Firebase console, review the automatically populated info, and click Create. - -To learn more about this, please, read the following [documentation][8]. - ---- - -# Secure Data - -## Secure Data in Cloud Firestore - -Cloud Firestore offers robust access management and authentication through two different methods, depending on the client libraries you use. - -* For mobile and web client libraries, use Firebase Authentication and Cloud Firestore Security Rules to handle serverless authentication, authorization, and data validation. Learn how to secure your data for the Android, iOS, and Web client libraries with [Cloud Firestore Security Rules][9]. -* For server client libraries, use Cloud Identity and Access Management (IAM) to manage access to your database. Learn how to secure your data for the Java, Python, Node.js, and Go client libraries with [IAM][10]. - ---- - -# Enable Offline Data - -Cloud Firestore supports offline data persistence. This feature caches a copy of the Cloud Firestore data that your app is actively using, so your app can access the data when the device is offline. You can write, read, listen to, and query the cached data. When the device comes back online, Cloud Firestore synchronizes any local changes made by your app to the data stored remotely in Cloud Firestore. - -To use offline persistence, you don't need to make any changes to the code that you use to access Cloud Firestore data. With offline persistence enabled, the Cloud Firestore client library automatically manages online and offline data access and synchronizes local data when the device is back online. - -### Configure offline persistence - -When you initialize Cloud Firestore, you can enable or disable offline persistence. For Android and iOS, offline persistence is enabled by default. To disable persistence, set the `PersistenceEnabled` option to `false`: - -```csharp -var settings = new FirestoreSettings { PersistenceEnabled = false }; - -// Any additional options -// ... - -// Disable offline data persistence -var db = Firestore.SharedInstance; -db.Settings = settings; -``` - -### Listen to offline data - -While the device is offline, if you have enabled offline persistence, your listeners will receive listen events when the locally cached data changes. You can listen to documents, collections, and queries. - -To check whether you're receiving data from the server or the cache, use the fromCache property on the SnapshotMetadata in your snapshot event. If fromCache is true, the data came from the cache and might be stale or incomplete. If fromCache is false, the data is complete and current with the latest updates on the server. - -By default, no event is raised if only the SnapshotMetadata changed. If you rely on the fromCache values, specify the includeMetadataChanges listen option when you attach your listen handler: - -```csharp -// Listen to metadata updates to receive a server snapshot even if -// the data is the same as the cached data. -var options = new QueryListenOptions (); -options.SetIncludeQueryMetadataChanges (true); - -db.GetCollection ("cities").WhereEqualsTo ("state", "CA").AddSnapshotListener (options, HandleQuerySnapshot); - -void HandleQuerySnapshot (QuerySnapshot snapshot, NSError error) -{ - if (error != null) { - System.Console.WriteLine ($"Error retreving snapshot: {error.LocalizedDescription}"); - return; - } - - foreach (var documentChange in snapshot?.DocumentChanges) - if (documentChange.Type == DocumentChangeType.Added) - System.Console.WriteLine ($"New city: {documentChange.Document.Data}"); - - var source = snapshot.Metadata.IsFromCache ? "local cache" : "server"; - System.Console.WriteLine ($"Metadata: Data fetched from {source}"); -} -``` - -### Get offline data - -If you get a document while the device is offline, Cloud Firestore returns data from the cache. If the cache does not contain data for that document, or the document does not exist, the get call returns an error. - -### Query offline data - -Querying works with offline persistence. You can retrieve the results of queries with either a direct get or by listening, as described in the preceding sections. You can also create new queries on locally persisted data while the device is offline, but the queries will initially run only against the cached documents. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/firestore/) to see original Firebase documentation._ - -[1]: https://firebase.google.com/console/ -[2]: https://console.cloud.google.com/projectselector/apis/api/firestore.googleapis.com/overview?pli=1 -[3]: https://firebase.google.com/docs/firestore/manage-data/structure-data -[4]: https://firebase.google.com/docs/firestore/manage-data/data-types -[5]: https://firebase.google.com/docs/cli -[6]: https://console.firebase.google.com/project/_/database/firestore/data -[7]: https://firebase.google.com/docs/firestore/using-console -[8]: https://firebase.google.com/docs/firestore/query-data/indexing -[9]: https://firebase.google.com/docs/firestore/security/get-started -[10]: https://firebase.google.com/docs/firestore/security/iam -[11]: https://bugzilla.xamarin.com/show_bug.cgi?id=43689 -[document_icon]: https://cdn1.iconfinder.com/data/icons/hawcons/32/698661-icon-54-document-20.png -[collections_icon]: https://cdn1.iconfinder.com/data/icons/hawcons/32/698680-icon-72-documents-20.png -[note_icon]: https://cdn3.iconfinder.com/data/icons/UltimateGnome/22x22/apps/gnome-app-install-star.png -[warning_icon]: https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-20.png -[correct_icon]: https://cdn4.iconfinder.com/data/icons/icocentre-free-icons/137/f-check_256-20.png -[wrong_icon]: https://cdn4.iconfinder.com/data/icons/icocentre-free-icons/114/f-cross_256-20.png +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/CloudMessaging/Details.md b/docs/Firebase/CloudMessaging/Details.md index b30042a9c..a0c417a43 100755 --- a/docs/Firebase/CloudMessaging/Details.md +++ b/docs/Firebase/CloudMessaging/Details.md @@ -1,17 +1,18 @@ -Using Firebase Cloud Messaging, you can notify a client app that new email or other data is available to sync. You can send notification messages to drive user reengagement and retention. For use cases such as instant messaging, a message can transfer a payload of up to 4KB to a client app. +# Firebase Cloud Messaging -## Key capabilities +This path is retained for existing links, but this repository does not maintain a copied Firebase Cloud Messaging walkthrough. -| | | -|-:|--| -| **Send notification messages or data messages** | Send notification messages that are displayed to your user. Or send data messages and determine completely what happens in your application code. See Message types. | -| **Versatile message targeting** | Distribute messages to your client app in any of three ways — to single devices, to groups of devices, or to devices subscribed to topics. | -| **Send messages from client apps** | Send acknowledgments, chats, and other messages from devices back to your server over FCM’s reliable and battery-efficient connection channel. | +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## How does it work? +## Native Documentation -![FirebaseCloudMessaging_HowItWorks](https://firebase.google.com/docs/cloud-messaging/images/messaging-overview.png) +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Cloud Messaging documentation: https://firebase.google.com/docs/cloud-messaging/ios/client -A Firebase Cloud Messaging implementation includes an app server that interacts with FCM via HTTP or XMPP protocol, and a client app. +## Binding Package -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/cloud-messaging/) to see original Firebase documentation._ \ No newline at end of file +- Package README: [../NuGet/CloudMessaging.md](../NuGet/CloudMessaging.md) +- Package ID: `AdamE.Firebase.iOS.CloudMessaging` +- Managed namespace: `Firebase.CloudMessaging` + +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/CloudMessaging/GettingStarted.md b/docs/Firebase/CloudMessaging/GettingStarted.md index 6e228626a..a0c417a43 100755 --- a/docs/Firebase/CloudMessaging/GettingStarted.md +++ b/docs/Firebase/CloudMessaging/GettingStarted.md @@ -1,597 +1,18 @@ -# Firebase Cloud Messaging on iOS +# Firebase Cloud Messaging -## Table of content +This path is retained for existing links, but this repository does not maintain a copied Firebase Cloud Messaging walkthrough. -- [Firebase Cloud Messaging on iOS](#firebase-cloud-messaging-on-ios) - - [Table of content](#table-of-content) - - [About Firebase Cloud Messaging](#about-firebase-cloud-messaging) - - [Method swizzling in Firebase Cloud Messaging](#method-swizzling-in-firebase-cloud-messaging) -- [Setting Up a Firebase Cloud Messaging Client App on iOS](#setting-up-a-firebase-cloud-messaging-client-app-on-ios) - - [Prerequisites](#prerequisites) - - [Add Firebase to your app](#add-firebase-to-your-app) - - [Configure Cloud Messaging in your app](#configure-cloud-messaging-in-your-app) - - [Register for remote notifications](#register-for-remote-notifications) - - [Access the registration token](#access-the-registration-token) - - [Set the Messaging Delegate property](#set-the-messaging-delegate-property) - - [Receive the current registration token](#receive-the-current-registration-token) - - [Monitor token generation](#monitor-token-generation) - - [Swizzling disabled: mapping your APNs token and registration token](#swizzling-disabled--mapping-your-apns-token-and-registration-token) - - [Import existing user APNs tokens](#import-existing-user-apns-tokens) - - [Prevent auto initialization](#prevent-auto-initialization) -- [Send your First Message to a Backgrounded App](#send-your-first-message-to-a-backgrounded-app) - - [Send a notification message](#send-a-notification-message) -- [Send Messages to Multiple Devices](#send-messages-to-multiple-devices) - - [Subscribe a client app to a topic](#subscribe-a-client-app-to-a-topic) - - [Receive and handle topic messages](#receive-and-handle-topic-messages) - - [Build send requests](#build-send-requests) -- [Receive Messages](#receive-messages) - - [Handle messages received through the FCM APNs interface](#handle-messages-received-through-the-fcm-apns-interface) - - [Interpreting notification message payload](#interpreting-notification-message-payload) - - [Handle data messages in foregrounded apps](#handle-data-messages-in-foregrounded-apps) - - [Handle messages with method swizzling disabled](#handle-messages-with-method-swizzling-disabled) - - [Interpreting data message payload](#interpreting-data-message-payload) - - [Handle queued and deleted messages](#handle-queued-and-deleted-messages) -- [Topic Messaging](#topic-messaging) - - [Subscribe the client app to a topic](#subscribe-the-client-app-to-a-topic) - - [Manage topic subscriptions on the server](#manage-topic-subscriptions-on-the-server) - - [Receive and handle topic messages](#receive-and-handle-topic-messages) - - [Build send requests](#build-send-requests) -- [Device Group Messaging](#device-group-messaging) - - [Managing device groups](#managing-device-groups) - - [Sending downstream messages to device groups](#sending-downstream-messages-to-device-groups) - - [Sending upstream messages to device groups](#sending-upstream-messages-to-device-groups) -- [Sending Upstream Messages](#sending-upstream-messages) - - [Send an upstream message](#send-an-upstream-message) - - [Handle upstream message callbacks](#handle-upstream-message-callbacks) - - [Receive XMPP messages on the app server](#receive-xmpp-messages-on-the-app-server) -- [Send Messages with the Firebase Console](#send-messages-with-the-firebase-console) +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## About Firebase Cloud Messaging +## Native Documentation -Firebase Cloud Messaging offers a broad range of messaging options and capabilities. I invite you to read the following [documentation][1] to have a better understanding about notification messages and data messages and what you can do with them using FCM's options. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Cloud Messaging documentation: https://firebase.google.com/docs/cloud-messaging/ios/client -## Method swizzling in Firebase Cloud Messaging +## Binding Package -One important thing you should know before start using FCM is that FCM API performs method swizzling in two key areas: mapping your APNs token to the FCM registration token and capturing analytics data during downstream message callback handling. Developers who prefer not to use swizzling can disable it by adding the flag `FirebaseAppDelegateProxyEnabled` in the app’s Info.plist file and setting it to `No` (boolean value). If you decided to disable swizzling, the docs provide example for both scenarios, with and without method swizzling enabled. +- Package README: [../NuGet/CloudMessaging.md](../NuGet/CloudMessaging.md) +- Package ID: `AdamE.Firebase.iOS.CloudMessaging` +- Managed namespace: `Firebase.CloudMessaging` ---- - -# Setting Up a Firebase Cloud Messaging Client App on iOS - -You can implement Firebase Cloud Messaging in two complementary ways: - -* Receive basic push messages up to 2KB over the Firebase Cloud Messaging APNs interface. -* Send messages upstream and/or receive downstream payloads up to 4KB. - -## Prerequisites - -* A physical iOS device -* A project targeting iOS 8 or above -* If you want to enable Notifications specifically, you'll need to create an [Apple Push Notification Authentication Key][2], an **App Id** and a **Provisioning Profile**, then [upload the key to Firebase][3] and finally enable the app for remote notifications. - * Open Entitlements.plist and check **Enable Push Notifications** - -> ![note_icon] _**Support for iOS 7 deprecated:**_ _As of v4.5.0 of the Firebase SDK for iOS, support for iOS 7 is deprecated. Upgrade your apps to target iOS 8 or above. To see the breakdown of worldwide iOS versions, go to [Apple’s App Store support page][4]._ - -## Add Firebase to your app - -1. Create a Firebase project in the [Firebase console][5], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][6]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][6] again at any time. - -> ![note_icon] **_Note:_** _If you have multiple build variants with different bundle IDs defined, each app must be added to your project in Firebase console._ - -## Configure Cloud Messaging in your app - -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Open `GoogleService-Info.plist` file and change `IS_GCM_ENABLED` value to `Yes`. -4. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -Firebase.Core.App.Configure(); -``` - -### Register for remote notifications - -Either at startup, or at the desired point in your application flow, register your app for remote notifications. Call `RegisterForRemoteNotifications` method as shown: - -```csharp -// Register your app for remote notifications. -if (UIDevice.CurrentDevice.CheckSystemVersion (10, 0)) { - - // For iOS 10 display notification (sent via APNS) - UNUserNotificationCenter.Current.Delegate = this; - - var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound; - UNUserNotificationCenter.Current.RequestAuthorization (authOptions, (granted, error) => { - Console.WriteLine (granted); - }); -} else { - // iOS 9 or before - var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound; - var settings = UIUserNotificationSettings.GetSettingsForTypes (allNotificationTypes, null); - UIApplication.SharedApplication.RegisterUserNotificationSettings (settings); -} - -UIApplication.SharedApplication.RegisterForRemoteNotifications (); -``` - -> ![advice_icon] For devices running iOS 10 and above, you must assign your delegate object to the `UNUserNotificationCenter` object to receive display notifications, and the `Messaging` object to receive data messages, before your app finishes launching. For example, in an iOS app, you must assign it in the `WillFinishLaunching` or `FinishedLaunching` method. - -## Access the registration token - -By default, the FCM SDK generates a registration token for the client app instance on initial startup of your app. Similar to the APNs device token, this token allows you to target notification messages to this particular instance of the app. - -In the same way that iOS typically delivers an APNs device token on app start, FCM provides a registration token via the `DidReceiveRegistrationToken` method of the `Messaging` delegate on each app startup. During the first app start, and in all situations where the registration token is changed, the FCM SDK retrieves the token. In both cases, the FCM SDK calls `DidReceiveRegistrationToken` found in the `IMessageDelegate` interface. - -The registration token may change when: - -* The app is restored on a new device -* The user uninstalls/reinstall the app -* The user clears app data. - -### Set the Messaging Delegate property - -To receive registration tokens on app start, implement the `IMessagingDelegate` interface in a class and provide it to `Messaging`’s `Delegate` property after calling `App.Configure ()` method. For example, if your application delegate conforms to the `IMessagingDelegate` interface, you can set the delegate on `FinishedLaunching` to itself: - -```csharp -Messaging.SharedInstance.Delegate = this; -``` - -### Receive the current registration token - -Registration tokens are delivered via the `IMessagingDelegate` method `DidReceiveRegistrationToken`. This method is called generally once per app start with an FCM token. When this method is called, it is the ideal time to: - -* If the registration token is new, send it to your application server (it's recommended to implement server logic to determine whether the token is new). -* Subscribe the registration token to topics. This is required only for new subscriptions or for situations where the user has re-installed the app. - -```csharp -var token = Messaging.SharedInstance.FcmToken ?? ""; -Console.WriteLine ($"FCM token: {token}"); -``` - -After this delegate method is called, the registration token is available via the `FcmToken` property of `Messaging` class. Prior to this delegate method call, the property may be `null`. - -### Monitor token generation - -To be notified whenever the token is updated, supply a class conforming to the `IMessagingDelegate` interface and set it to `Messaging` `Delegate` property. The following example registers the delegate and adds the proper interface method: - -```csharp -[Export ("messaging:didReceiveRegistrationToken:")] -public void DidReceiveRegistrationToken (Messaging messaging, string fcmToken) -{ - Console.WriteLine ($"Firebase registration token: {fcmToken}"); - - // TODO: If necessary send token to application server. - // Note: This callback is fired at each app startup and whenever a new token is generated. -} -``` - -Alternatively, you can listen for an `NSNotification` named `RegistrationTokenRefreshedNotification` rather than supplying a interface method. The `Messaging.SharedInstance.FcmToken` property always has the current token value. - -#### Swizzling disabled: mapping your APNs token and registration token - -If you have disabled method swizzling, you'll need to explicitly map your APNs token to the FCM registration token. Override the `AppDelegate`'s method `RegisteredForRemoteNotifications` to retrieve the APNs token, and then use the `ApnsToken` property. - -Provide your APNs token using the `ApnsToken` property: - -```csharp -public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken) -{ - Messaging.SharedInstance.ApnsToken = deviceToken; -} -``` - -After the FCM registration token is generated, you can access it and listen for refresh events using the same methods as with swizzling enabled. - -### Import existing user APNs tokens - -If you have an existing user base that you want to onboard to an FCM client app, use the [batchImport][7] API provided by Instance ID. With this API, you can bulk import existing iOS APNs tokens into FCM, mapping them to new, valid registration tokens. - -## Prevent auto initialization - -FCM generates an Instance ID, which is used as a registration token within FCM. When an Instance ID is generated the library will upload the identifier and configuration data to Firebase. If you want to get an explicit opt-in before using Instance ID, you can prevent generation at configure time by disabling FCM. To do this, add a metadata value to your `Info.plist` (not your `GoogleService-Info.plist`): - -`FirebaseMessagingAutoInitEnabled = NO` - -To re-enable FCM, you can make a runtime call: - -```csharp -Messaging.SharedInstance.AutoInitEnabled = true; -``` - -This value persists across app restarts once set. - ---- - -# Send your First Message to a Backgrounded App - -To get started with FCM, build out the simplest use case: sending a notification message from the [Notifications composer][8] to a specific user's device when the app is in the background on the device. - -## Send a notification message - -1. Install and run the app on the target device. You'll need to accept the request for permission to receive remote notifications. -2. Make sure the app is in the background on the device. -3. Open the [Notifications composer][8] and select **New Message**. -4. Enter the message text. -5. Select **Single Device** for the message target. -6. In the field labeled FCM Registration Token, enter the registration token you obtained in a previous section of this guide. - -After you click **Send Message**, targeted client devices that have the app in the background receive the notification in the notification center. - ---- - -# Send Messages to Multiple Devices - -Firebase Cloud Messaging provides two ways to target a message to multiple devices: - -* [Topic messaging](#topic-messaging), which allows you to send a message to multiple devices that have opted in to a particular topic. -* [Device group messaging](#device-group-messaging), which allows you to send a single message to multiple instances of an app running on devices belonging to a group. - -This part focuses on sending topic messages from your app server using the HTTP or XMPP protocols for FCM, and receiving and handling them in an iOS app. - -## Subscribe a client app to a topic - -Client apps can subscribe to any existing topic, or they can create a new topic. To learn more about this, go to [Topic Messaging](#subscribe-the-client–app-to-a-topic) section below. - -## Receive and handle topic messages - -FCM delivers topic messages in the same way as other downstream messages. To learn how to receive and handle messages, go to [Receive Messages](#receive-messages) section below. - -## Build send requests - -Sending messages to a Firebase Cloud Messaging topic is very similar to sending messages to an individual device or to a user group. To learn more about this, please, read the following [documentation][9]. - ---- - -# Receive Messages - -Once your client app is installed on a device, it can receive messages through the FCM APNs interface. You can immediately start sending notifications to user segments with the Notifications composer, or your application server can send messages with a notification payload through the APNs interface. - -Handling messages received through the FCM APNs interface is likely to cover most typical use cases. You can also [send upstream messages](#sending-upstream-messages), or [receive data messages in foregrounded apps](#handle-data-messages-in-foregrounded-apps). - -## Handle messages received through the FCM APNs interface - -When your app is in the background, iOS directs messages with the `notification` key to the system tray. A tap on a notification opens the app, and the content of the notification is passed to the `DidReceiveRemoteNotification` method in the `AppDelegate`. - -Implement AppDelegate `DidReceiveRemoteNotification` as shown: - -```csharp -public override void ReceivedRemoteNotification (UIApplication application, NSDictionary userInfo) -{ - // If you are receiving a notification message while your app is in the background, - // this callback will not be fired till the user taps on the notification launching the application. - // TODO: Handle data of notification - - // With swizzling disabled you must let Messaging know about the message, for Analytics - //Messaging.SharedInstance.AppDidReceiveMessage (userInfo); - - // Print full message. - Console.WriteLine (userInfo); -} - -public override void DidReceiveRemoteNotification (UIApplication application, NSDictionary userInfo, Action completionHandler) -{ - // If you are receiving a notification message while your app is in the background, - // this callback will not be fired till the user taps on the notification launching the application. - // TODO: Handle data of notification - - // With swizzling disabled you must let Messaging know about the message, for Analytics - //Messaging.SharedInstance.AppDidReceiveMessage (userInfo); - - // Print full message. - Console.WriteLine (userInfo); - - completionHandler (UIBackgroundFetchResult.NewData); -} -``` - -If you want to open your app and perform a specific action, set `click_action` in the [notification payload][10]. Use the value that you would use for the `category` key in the APNs payload. - -When overriding `DidReceiveRemoteNotification` method, you need to add "**remote-notification**" to the list of your **Required background modes** in your Info.plist - -### Interpreting notification message payload - -The payload of notification messages is a dictionary of keys and values. Notification messages sent through APNs follow the APNs payload format as below: - -```json -{ - "aps" : { - "alert" : { - "body" : "great match!", - "title" : "Portugal vs. Denmark", - }, - "badge" : 1, - }, - "customKey" : "customValue" -} -``` - -## Handle data messages in foregrounded apps - -To receive data-only messages directly from FCM (as opposed to via APNs) when the app is in the foreground, you'll need to connect to the FCM service and handle messages with `IMessagingDelegate` `DidReceiveMessage` interface method. - -To connect, set the `ShouldEstablishDirectChannel` property to `true` in the `AppDelegate`. FCM manages the connection, closing it when your app goes into the background and reopening it whenever the app is foregrounded. - -To receive data messages when your app is in the foreground, implement `DidReceiveMessage`. Your app can still receive data messages when it is in the background without this callback, but for foreground cases you'll need to handle it. - -### Handle messages with method swizzling disabled - -If you disable method swizzling, you'll need to call method `Messaging` `AppDidReceiveMessage` method. This lets FCM track message delivery and analytics, which is performed automatically with method swizzling enabled: - -```csharp -// With "FirebaseAppDelegateProxyEnabled": NO -public override void DidReceiveRemoteNotification (UIApplication application, NSDictionary userInfo, Action completionHandler) -{ - // Let FCM know about the message for analytics etc. - Messaging.SharedInstance.AppDidReceiveMessage (userInfo); - - // Do your magic to handle the notification data -} -``` - -Since iOS 10, you can set `UNUserNotificationCenter` `Delegate` to receive display notifications from Apple and `Messaging`s `Delegate` to receive data messages from FCM. If you do not set these two delegates with `AppDelegate`, method swizzling for message handling is disabled. You'll need to call method `AppDidReceiveMessage` to track message delivery and analytics. - -```csharp -// Receive displayed notifications for iOS 10 devices. -// Handle incoming notification messages while app is in the foreground. -[Export ("userNotificationCenter:willPresentNotification:withCompletionHandler:")] -public void WillPresentNotification (UNUserNotificationCenter center, UNNotification notification, Action completionHandler) -{ - var userInfo = notification.Request.Content.UserInfo; - - // With swizzling disabled you must let Messaging know about the message, for Analytics - //Messaging.SharedInstance.AppDidReceiveMessage (userInfo); - - // Print full message. - Console.WriteLine (userInfo); - - // Change this to your preferred presentation option - completionHandler (UNNotificationPresentationOptions.None); -} - -// Handle notification messages after display notification is tapped by the user. -[Export ("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")] -public void DidReceiveNotificationResponse (UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler) -{ - var userInfo = response.Notification.Request.Content.UserInfo; - - // Print full message. - Console.WriteLine (userInfo); - - completionHandler (); -} -``` - -### Interpreting data message payload - -The payload of data messages is a dictionary of keys and values. Data messages sent to the devices directly by FCM server are expressed in the format of a dictionary as below: - -```json -{ - "body" : "great match!", - "title" : "Portugal vs. Denmark", - "icon" : "myicon" -} -``` - -### Handle queued and deleted messages - -Apps that connect to FCM to receive data messages should handle `Messaging.MessagesDeletedNotification` with `NSNotificationCenter` or with `Messaging.Notifications.ObserveMessagesDeleted`. You may receive this callback when there are too many messages (>100) pending for your app on a particular device at the time it connects, or if the device hasn't connected to FCM for more than one month. When the app instance receives this callback, it should perform a full sync with your app server. - ---- - -# Topic Messaging - -Based on the publish/subscribe model, FCM topic messaging allows you to send a message to multiple devices that have opted in to a particular topic. You compose topic messages as needed, and FCM handles routing and delivering the message reliably to the right devices. - -For example, users of a local weather forecasting app could opt in to a "severe weather alerts" topic and receive notifications of storms threatening specified areas. Users of a sports app could subscribe to automatic updates in live game scores for their favorite teams. - -Some things to keep in mind about topics: - -* Topic messaging supports unlimited topics and subscriptions for each app. -* Topic messaging is best suited for content such as news, weather, or other publicly available information. -* Topic messages are optimized for throughput rather than latency. For fast, secure delivery to single devices or small groups of devices, [target messages to registration tokens][11], not topics. -* If you need to send messages to multiple devices per user, consider [device group messaging][12] for those use cases. - -## Subscribe the client app to a topic - -Client apps can subscribe to any existing topic, or they can create a new topic. When a client app subscribes to a new topic name (one that does not already exist for your Firebase project), a new topic of that name is created in FCM and any client can subsequently subscribe to it. - -The `Messaging` class handles topic messaging functionality. To subscribe to a topic, call `Subscribe` method from your application's main thread (FCM is not thread-safe): - -```csharp -Messaging.SharedInstance.Subscribe ("topic", (error) => { - // ... -}); -Console.WriteLine ("Subscribed to topic"); - -// async/away Version - -try { - await Messaging.SharedInstance.SubscribeAsync ("topic"); - Console.WriteLine ("Subscribed to topic"); -} catch (NSErrorException ex) { - // ... -} -``` - -This makes an asynchronous request to the FCM backend and subscribes the client to the given topic. Before calling `Subscribe` method, make sure that the client app instance has already received a registration token via the callback `IMessagingDelegate` `DidReceiveRegistrationToken` method. - -If the subscription request fails initially, FCM retries until it can subscribe to the topic successfully. Each time the app starts, FCM makes sure that all requested topics have been subscribed. - -To unsubscribe, call `Unsubscribe` method, and FCM unsubscribes from the topic in the background. - -```csharp -Messaging.SharedInstance.Unsubscribe ("topic", (error) => { - // ... -}); -Console.WriteLine ("Unsubscribed to topic"); - -// async/away Version - -try { - await Messaging.SharedInstance.UnsubscribeAsync ("topic"); - Console.WriteLine ("Unsubscribed to topic"); -} catch (NSErrorException ex) { - // ... -} -``` - -## Manage topic subscriptions on the server - -You can take advantage of [Instance ID APIs][13] to perform basic topic management tasks from the server side. Given the registration token(s) of client app instances, you can do the following: - -* Find out details about a client app instance's subscriptions, including each topic name and subscribe date. See [Get information about app instances][14]. -* Subscribe or unsubscribe an app instance to a topic. See [Create a relationship mapping for an app instance][15]. -* Subscribe or unsubscribe multiple app instances to a topic. See [Manage relationship maps for multiple app instances][16]. - -## Receive and handle topic messages - -FCM delivers topic messages in the same way as other downstream messages. To learn how to receive and handle messages, go to [Receive Messages](#receive-messages) section above. - -## Build send requests - -Sending messages to a Firebase Cloud Messaging topic is very similar to sending messages to an individual device or to a user group. To learn more about this, please, read the following [documentation][9]. - ---- - -# Device Group Messaging - -With device group messaging, you can send a single message to multiple instances of an app running on devices belonging to a group. Typically, "group" refers a set of different devices that belong to a single user. All devices in a group share a common notification key, which is the token that FCM uses to fan out messages to all devices in the group. - -You can use device group messaging with the [Admin SDKs][17], or by implementing the [XMPP][18] or [HTTP][19] protocols on your app server. The maximum number of members allowed for a notification key is 20. - -## Managing device groups - -To learn about this, please, read the following [documentation][20]. - -## Sending downstream messages to device groups - -To learn about this, please, read the following [documentation][21]. - -## Sending upstream messages to device groups - -To send upstream messages to device groups on iOS, the iOS client app needs to call `Messaging.SendMessage` method. - ---- - -# Sending Upstream Messages - -If your app server implements the [XMPP Connection Server][18] protocol, it can receive upstream messages from a user's device to the cloud. To initiate an upstream message, the client app sends a request containing the following: - -* The address of the receiving app server in the format **SENDER_ID@gcm.googleapis.com**. -* A message ID that should be unique for each [sender ID][22]. -* The message data comprising the key-value pairs of the message's payload. - -When it receives this data, FCM builds an XMPP stanza to send to the app server, adding some additional information about the sending device and app. - -## Send an upstream message - -To send messages upstream to the server, an iOS client app composes a message, connects to FCM, and calls `SendMessage` method. - -To connect, set the `ShouldEstablishDirectChannel` property to `true` in the `AppDelegate`. FCM manages the connection, closing it when your app goes into the background and reopening it whenever the app is foregrounded. - -Configure the call to SendMessage method as shown: - -```csharp -Messaging.SharedInstance.SendMessage (message, to, messageId, ttl); -``` - -where: - -* `message` is a `Dictionary` or `NSDictionary` of keys and values as strings. Any key-value pair that is not a string is ignored. -* `to` is the address of the receiving app server in the format **SENDER_ID@gcm.googleapis.com**. -* `messageId` is a unique message identifier. All the message receiver callbacks are identified on the basis of this message ID. -* `ttl` is the time to live. If FCM can't deliver the message before this expiration is reached, it sends a notification back to the client. - -The FCM client library caches the message on the client app and sends it when the client has an active server connection. On receiving the message, the FCM connection server sends it to the app server. - -### Handle upstream message callbacks - -Handle upstream callbacks by adding observers that listen for `SendErrorNotification` and `SendSuccessNotification`, and then retrieve error information from the methods. In this example, `HandleSendMessageError` is the method used to handle the callback: - -```csharp -var sendSuccessToken = Messaging.Notifications.ObserveSendSuccess (HandleSendMessageSuccess); -var sendErrorToken = Messaging.Notifications.ObserveSendError (HandleSendMessageError); - -void HandleSendMessageSuccess(object sender, NSNotificationEventArgs e) -{ - var messageId = e.Notification.Object.ToString (); -} - -void HandleSendMessageError (object sender, NSNotificationEventArgs e) -{ - var messageId = e.Notification.Object.ToString (); - var userInfo = e.Notification.UserInfo; // contains error info etc -} - -// Call this lines to stop receiving notifications -sendSuccessToken.Dispose (); -sendErrorToken.Dispose (); - -// Or - -var sendSuccessToken = NSNotificationCenter.DefaultCenter.AddObserver (Messaging.SendSuccessNotification, HandleSendMessageSuccess); -var sendErrorToken = NSNotificationCenter.DefaultCenter.AddObserver (Messaging.SendSuccessNotification, HandleSendMessageError); - -void HandleSendMessageSuccess (NSNotification notification) -{ - var messageId = notification.Object.ToString (); -} - -void HandleSendMessageError (NSNotification notification) -{ - var messageId = notification.Object.ToString (); - var userInfo = notification.UserInfo; // contains error info etc -} - -// Call this lines to stop receiving notifications -NSNotificationCenter.DefaultCenter.RemoveObserver (sendSuccessToken); -NSNotificationCenter.DefaultCenter.RemoveObserver (sendErrorToken); -``` - -To optimize network usage, FCM batches responses to `HandleSendMessageError` and `HandleSendMessageSuccess`, so the acknowledgement may not be immediate for each message. - -## Receive XMPP messages on the app server - -To learn about this, please, read the following [documentation][23]. - ---- - -# Send Messages with the Firebase Console - -You can send notification messages to iOS and Android devices using the Notifications composer in the Firebase console. To learn more about this, please, read the following [documentation][24]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/cloud-messaging/ios/client) to see original Firebase documentation._ - -[1]: https://firebase.google.com/docs/cloud-messaging/concept-options -[2]: https://firebase.google.com/docs/cloud-messaging/ios/certs -[3]: https://firebase.google.com/docs/cloud-messaging/ios/client#upload_your_apns_authentication_key -[4]: https://developer.apple.com/support/app-store/ -[5]: https://firebase.google.com/console/ -[6]: http://support.google.com/firebase/answer/7015592 -[7]: https://developers.google.com/instance-id/reference/server#create_registration_tokens_for_apns_tokens -[8]: https://console.firebase.google.com/project/_/notification -[9]: https://firebase.google.com/docs/cloud-messaging/send-message#send_messages_to_topics_1 -[10]: https://firebase.google.com/docs/cloud-messaging/http-server-ref#notification-payload-support -[11]: https://firebase.google.com/docs/cloud-messaging/send-message#send_messages_to_specific_devices -[12]: https://firebase.google.com/docs/cloud-messaging/send-message#send_messages_to_device_groups -[13]: https://developers.google.com/instance-id/ -[14]: https://developers.google.com/instance-id/reference/server#get_information_about_app_instances -[15]: https://developers.google.com/instance-id/reference/server#create_a_relation_mapping_for_an_app_instance -[16]: https://developers.google.com/instance-id/reference/server#manage_relationship_maps_for_multiple_app_instances -[17]: https://firebase.google.com/docs/cloud-messaging/admin/ -[18]: https://firebase.google.com/docs/cloud-messaging/server#implementing-the-xmpp-server-protocol -[19]: https://firebase.google.com/docs/cloud-messaging/server#implementing-the-http-server-protocol -[20]: https://firebase.google.com/docs/cloud-messaging/ios/device-group#managing_device_groups -[21]: https://firebase.google.com/docs/cloud-messaging/ios/device-group#sending_downstream_messages_to_device_groups -[22]: https://firebase.google.com/docs/cloud-messaging/concept-options#senderid -[23]: https://firebase.google.com/docs/cloud-messaging/ios/upstream#receive_xmpp_messages_on_the_app_server -[24]: https://firebase.google.com/docs/cloud-messaging/send-with-console -[note_icon]: https://cdn3.iconfinder.com/data/icons/UltimateGnome/22x22/apps/gnome-app-install-star.png -[advice_icon]: https://cdn1.iconfinder.com/data/icons/nuove/22x22/actions/messagebox_warning.png -[warning_icon]: https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-20.png +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Crashlytics/Details.md b/docs/Firebase/Crashlytics/Details.md index 219d23ff3..ffbe5e52c 100755 --- a/docs/Firebase/Crashlytics/Details.md +++ b/docs/Firebase/Crashlytics/Details.md @@ -1,16 +1,18 @@ -Get clear, actionable insight into app issues with this powerful crash reporting solution. +# Firebase Crashlytics -Firebase Crashlytics is a lightweight, realtime crash reporter that helps you track, prioritize, and fix stability issues that erode your app quality. Crashlytics saves you troubleshooting time by intelligently grouping crashes and highlighting the circumstances that lead up to them. +This path is retained for existing links, but this repository does not maintain a copied Firebase Crashlytics walkthrough. -Find out if a particular crash is impacting a lot of users. Get alerts when an issue suddenly increases in severity. Figure out which lines of code are causing crashes. +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## Key capabilities +## Native Documentation -| | | -|-:|-| -| **Curated crash reports** | Crashlytics synthesizes an avalanche of crashes into a manageable list of issues, provides contextual information, and highlights the severity and prevalence of crashes so you can pinpoint the root cause faster. | -| **Cures for the common crash** | Crashlytics offers Crash Insights, helpful tips that highlight common stability problems and provide resources that make them easier to troubleshoot, triage, and resolve. | -| **Integrated with Analytics** | Crashlytics can capture your app's errors as **app_exception** events in Analytics. The events simplify debugging by giving you access a list of other events leading up to each crash, and provide audience insights by letting you pull Analytics reports for users with crashes. | -| **Realtime alerts** | Get realtime alerts for new issues, regressed issues, and growing issues that might require immediate attention. | +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Crashlytics documentation: https://firebase.google.com/docs/crashlytics/get-started -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/crashlytics/) to see original Firebase documentation._ +## Binding Package + +- Package README: [../NuGet/Crashlytics.md](../NuGet/Crashlytics.md) +- Package ID: `AdamE.Firebase.iOS.Crashlytics` +- Managed namespace: `Firebase.Crashlytics` + +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Crashlytics/GettingStarted.md b/docs/Firebase/Crashlytics/GettingStarted.md index f9e7676a9..ffbe5e52c 100755 --- a/docs/Firebase/Crashlytics/GettingStarted.md +++ b/docs/Firebase/Crashlytics/GettingStarted.md @@ -1,356 +1,18 @@ -# Get Started with Firebase Crashlytics for iOS +# Firebase Crashlytics -## Table of Content +This path is retained for existing links, but this repository does not maintain a copied Firebase Crashlytics walkthrough. -- [Get Started with Firebase Crashlytics for iOS](#get-started-with-firebase-crashlytics-for-ios) - - [Table of Content](#table-of-content) - - [Before you begin](#before-you-begin) - - [Add Firebase to your app](#add-firebase-to-your-app) - - [Configure Firebase in your app](#configure-firebase-in-your-app) -- [Upgrade to Firebase Crashlytics from Firebase Crash Reporting](#upgrade-to-firebase-crashlytics-from-firebase-crash-reporting) - - [Update project dependencies](#update-project-dependencies) - - [Migrate logs](#migrate-logs) - - [Set up manual initialization](#set-up-manual-initialization) -- [Test your Firebase Crashlytics implementation](#test-your-firebase-crashlytics-implementation) - - [Force a crash to test your implementation](#force-a-crash-to-test-your-implementation) - - [Adjust your project's debug settings](#adjust-your-projects-debug-settings) - - [Test it out](#test-it-out) - - [Enable Crashlytics debug mode](#enable-crashlytics-debug-mode) -- [Customize your Firebase Crashlytics crash reports](#customize-your-firebase-crashlytics-crash-reports) - - [Enable opt-in reporting](#enable-opt-in-reporting) - - [Add custom logs](#add-custom-logs) - - [Add custom keys](#add-custom-keys) - - [Set user IDs](#set-user-ids) - - [Log non-fatal exceptions](#log-non-fatal-exceptions) - - [Logs and custom keys](#logs-and-custom-keys) - - [Performance considerations](#performance-considerations) - - [What about NSExceptions?](#what-about-nsexceptions) - - [Manage Crash Insights data](#manage-crash-insights-data) -- [Extend Firebase Crashlytics with Cloud Functions](#extend-firebase-crashlytics-with-cloud-functions) +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## Before you begin +## Native Documentation -* Enable Crashlytics: Click **Set up Crashlytics** in the [Firebase console][1]. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Crashlytics documentation: https://firebase.google.com/docs/crashlytics/get-started -## Add Firebase to your app +## Binding Package -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. +- Package README: [../NuGet/Crashlytics.md](../NuGet/Crashlytics.md) +- Package ID: `AdamE.Firebase.iOS.Crashlytics` +- Managed namespace: `Firebase.Crashlytics` -## Configure Firebase in your app - -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Visual Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behavior to `Bundle Resource` by Right clicking/Build Action. -3. Add the `Xamarin.Firebase.iOS.Core` NuGet to your project. -4. Add the following lines of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` and `Firebase.Crashlytics` namespace): - -```csharp -App.Configure (); -Crashlytics.Configure (); -``` - ---- - -# Upgrade to Firebase Crashlytics from Firebase Crash Reporting - -Crashlytics is the new, primary crash reporter for Firebase. If your app uses Firebase Crash Reporting, there's good news: Crashlytics offers enhanced crash reporting with nearly the same setup process you're used to, so upgrading is straightforward: - -1. Update your project's dependencies. -2. Migrate any log calls, if you have them. -3. Set up manual initialization, if you used it. - -## Update project dependencies - -To update your app's depencencies for Firebase Crashlytics, swap the Xamarin.Firebase.iOS.CrashReporting NuGet with the Xamarin.Firebase.iOS.Crashlytics NuGet and remove the Firebase Crash Reporting custom command: - -1. In Visual Studio, do a double click on **Packages** folder and add the Xamarin.Firebase.iOS.Crashlytics NuGet. -2. Remove Xamarin.Firebase.iOS.CrashReporting NuGet by right clicking/Remove. -3. Open your app **Project Options**, go to **Build** > **Custom Command** and delete the Crash Reporting command: - - ``` - sh ${ProjectDir}/scripts/FirebaseCrashReporting/xamarin_upload_symbols.sh -n ${ProjectName} -b ${TargetDir} -i ${ProjectDir}/Info.plist -p ${ProjectDir}/GoogleService-Info.plist -s ${ProjectDir}/service-account.json - ``` - -## Migrate logs - -If you used Firebase Crash Reporting custom logs, you have to update those for Firebase Crashlytics too: - -| Firebase Crash Reporting | Firebase Crashlytics | -|--------------------------|--------------------------------------------------------------------------------------------------| -| CrashReporting.Log | Logging.Log / Logging.LogCallerInformation
Logging.NSLog / Logging.NSLogCallerInformation | - -> ![warning_icon] _**Note:**_ _The string given to these methods must be an escaped string due it will be passed to a C function and it expects an escaped string. For example, if you want to print a %, you must type %%. Passing an unescaped string may cause the termination of your app._ - -## Set up manual initialization - -Like Firebase Crash Reporting, the Firebase Crashlytics SDK automatically initializes Crashlytics as soon as you add it to your app. If instead you initialize reporting manually, Crashlytics has a way to do that as well: - -1. Turn off automatic collection with a new key to your Info.plist file: - * Key: `firebase_crashlytics_collection_enabled` - * Value: `false` -2. Replace the Crash Reporting initialization call with one for Crashlytics: - ```csharp - // Delete Crash Reporting - // CrashReporting.SharedInstance.CrashCollectionEnabled = true; - - // Add Crashlytics - Fabric.Fabric.With (typeof (Crashlytics)); - ``` - ---- - -# Test your Firebase Crashlytics implementation - -## Force a crash to test your implementation - -You don't have to wait for a crash to know that Crashlytics is working. You can use the SDK to force a crash by adding the following code to your app: - -```csharp -using Firebase.Crashlytics; - -... - -public override void ViewDidLoad () -{ - base.ViewDidLoad (); - - var button = new UIButton (UIButtonType.RoundedRect) { - Frame = new CGRect (0, 0, 100, 30), - TranslatesAutoresizingMaskIntoConstraints = false - }; - button.SetTitle ("Crash", UIControlState.Normal); - button.TouchUpInside += Button_TouchUpInside; - View.AddSubview (button); - - button.AddConstraint (NSLayoutConstraint.Create (button, NSLayoutAttribute.CenterX, NSLayoutRelation.Equal, - View, NSLayoutAttribute.CenterX, - 1, 0)); - button.AddConstraint (NSLayoutConstraint.Create (button, NSLayoutAttribute.CenterY, NSLayoutRelation.Equal, - View, NSLayoutAttribute.CenterY, - 1, 0)); -} - -void Button_TouchUpInside (object sender, EventArgs e) -{ - Crashlytics.SharedInstance.Crash (); -} -``` - -## Adjust your project's debug settings - -Crashlytics can’t capture crashes if your build attaches a debugger at launch. Adjust your build settings to change the project's debug information format: - -1. In Visual Studio, open your app project settings. - * _Mac:_ Go to **Build** > **iOS Build** - * _Windows:_ Go to **iOS Build** -2. Select an **iPhone** platform. -2. Make sure that **Strip native debugging symbols** is unchecked. - -## Test it out - -The code snippet above adds a button that crashes your app when pressed. For it to work, run the app without a debugger, the debugger interferes with Crashlytics: - -1. Select a physical device and Debug configuration. -2. Run the app without debugging: - * _Mac:_ Open **Run** menu > **Start Without Debugging** - * _Windows:_ Open **Build** menu > **Start Without Debugging** - - The build process will upload the `dSYM` file of the app to Firebase in a background process. -3. Touch Crash button to crash the app. -4. Open your app once more to let the Crashlytics API report the crash. Your crash should show up in the Firebase console within 5 minutes. - -## Enable Crashlytics debug mode - -If your forced crash didn't crash, crashed before you wanted it to, or you're experiencing some other issue with Crashlytics, you can enable Crashlytics debug mode to track down the problem: - -```csharp -public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions) -{ - ... - Fabric.Fabric.SharedSdk.Debug = true; - ... - - return true; -} -``` - ---- - -# Customize your Firebase Crashlytics crash reports - -Firebase Crashlytics can work with very little setup on your part—as soon as you add the SDK, Crashlytics gets to work sending crash reports to the Firebase console. - -For more fine-grained control of your crash reports, customize your Crashlytics SDK configuration. For example, you can enable opt-in reporting for privacy-minded users, add logs to track down pesky bugs, and more. - -## Enable opt-in reporting - -By default, Firebase Crashlytics automatically collects crash reports for all your app's users. To give users more control over the data they send, you can enable opt-in reporting instead. - -To do that, you have to disable automatic collection and initialize Crashlytics only for opt-in users. - -1. Turn off automatic collection with a new key to your **Info.plist** file: - * Key: `firebase_crashlytics_collection_enabled` - * Value: `false` -2. Enable collection for selected users by initializing Crashlytics at runtime: - ```csharp - Fabric.Fabric.With (typeof (Crashlytics)); - ``` - -## Add custom logs - -To give yourself more context for the events leading up to a crash, you can add custom Crashlytics logs to your app. Crashlytics associates the logs with your crash data and makes them visible in the Firebase console. - -Use `LogCallerInformation` and `NSLogCallerInformation` methods to help pinpoint issues. It automatically includes information about the C# filename, method, and line number associated with the log. You can optionally pass the class name to have a better log: - -* **Debug builds:** `NSLogCallerInformation` method passes through to `NSLog` method so you can see the output in Visual Studio and on the device. -* **Release builds:** To improve performance, `LogCallerInformation` method silences all other output and will be only shown on Firebase Crashlytics dashboard when a crash occurs. - -```csharp -void Button_TouchUpInside (object sender, EventArgs e) -{ - var data = new Dictionary { - { "A keyblade", "for a heartless" }, - { "A heart", "for a nobody" } - }; - var nsData = NSDictionary.FromObjectsAndKeys (data.Values.ToArray (), data.Keys.ToArray (), data.Keys.Count); - - Logging.LogCallerInformation ($"Hi! Maybe, I'm about to crash! Here's some data: {nsData}", nameof (ViewController)); - - // or - - Logging.NSLogCallerInformation ($"Hi! Maybe, I'm about to crash! Here's some data: {nsData}", nameof (ViewController)); - - Crashlytics.SharedInstance.Crash (); -} -``` - -Assuming that you are coding in a file named `MyViewController.cs`, the output will be: - -``` -MyViewController.cs: ViewController.Button_TouchUpInside line 47 $ Hi! Maybe I'm about to crash! Here's some data: { - "A keyblade" = "for a heartless"; - "A heart" = "for a nobody"; -} -``` - -If you omit the second parameter of `NSLogCallerInformation` or `LogCallerInformation` method, the output will be: - -``` -MyViewController.cs: Button_TouchUpInside line 47 $ Hi! Maybe I'm about to crash! Here's some data: { - "A keyblade" = "for a heartless"; - "A heart" = "for a nobody"; -} -``` - -> ![note_icon] _To avoid slowing down your app, Crashlytics limits logs to 64kB. Crashlytics deletes older log entries if a session's logs go over that limit._ - -> ![warning_icon] _**Note:**_ _The string given to these methods must be an escaped string due it will be passed to a C function and it expects an escaped string. For example, if you want to print a %, you must type %%. Passing an unescaped string may cause the termination of your app._ - -## Add custom keys - -Custom keys help you get the specific state of your app leading up to a crash. You can associate arbitrary key/value pairs with your crash reports, and see them in the Firebase console: - -```csharp -void SetObjectValue (NSObject value, string key); -void SetStringValue (string value, string key); -void SetIntValue (int value, string key); -void SetBoolValue (bool value, string key); -void SetFloatValue (float value, string key); -``` - -Sometimes you need to change the existing key value. Call the same key, but replace the value, for example: - -```csharp -Crashlytics.SharedInstance.SetIntValue (3, "current_level"); -Crashlytics.SharedInstance.SetStringValue ("logged_in", "last_UI_action"); -``` - -> ![note_icon] _Crashlytics supports a maximum of 64 key/value pairs. Once you reach this threshold, additional values are not saved. Each key/value pair can be up to 1 kB in size_ - -## Set user IDs - -To diagnose an issue, it’s often helpful to know which of your users experienced a given crash. Crashlytics includes a way to anonymously identify users in your crash reports. - -To add user IDs to your reports, assign each user a unique identifier in the form of an ID number, token, or hashed value: - -```csharp -Crashlytics.SharedInstance.SetUserIdentifier ("123456789"); -``` - -If you ever need to clear a user identifier after you set it, reset the value to a blank string. - -## Log non-fatal exceptions - -In addition to automatically reporting your app’s crashes, Crashlytics lets you log non-fatal exceptions. - -On iOS, you do that by recording `NSError` objects, which Crashlytics reports and groups much like crashes: - -```csharp -Crashlytics.SharedInstance.RecordError (error); -``` - -When using the `RecordError` method, it's important to understand the `NSError` structure and how Crashlytics uses the data to group crashes. Incorrect usage of the `RecordError` method can cause unpredicatable behavior and may require Crashlytics to limit reporting of logged errors for your app. - -An `NSError` object has three arguments: `NSString Domain`, `nint Code`, and `NSDictionary UserInfo`. - -Unlike fatal crashes, which are grouped via stack trace analysis, logged errors are grouped by the NSError `Domain` and `Code`. This is an important distinction between fatal crashes and logged errors. For example, logging an error such as: - -```csharp -var userInfo = new Dictionary { - { NSError.LocalizedDescriptionKey, NSBundle.MainBundle.LocalizedString ("The request failed.", null) }, - { NSError.LocalizedFailureReasonErrorKey, NSBundle.MainBundle.LocalizedString ("The response returned a 404.", null) }, - { NSError.LocalizedRecoverySuggestionErrorKey, NSBundle.MainBundle.LocalizedString ("Does this page exist?", null) }, - { "ProductId", "123456" }, - { "UserId", "Jane Smith" } -}; - -var error = new NSError (new NSString ("SomeErrorDomain"), - -1001, - NSDictionary.FromObjectsAndKeys (userInfo.Values.ToArray (), userInfo.Keys.ToArray (), userInfo.Keys.Count)); -``` - -Creates a new issue that is grouped by `SomeErrorDomain` and `-1001`. Additional logged errors that use the same domain and code values will be grouped under this issue. - -> ![warning_icon] _Avoid using unique values, such as user ID, product ID, and timestamps in the domain and code fields. Using unique values in these fields causes a high cardinality of issues and may result in Crashlytics needing to limit the reporting of logged errors in your app. Unique values should instead be added to the `UserInfo` Dictionary object._ - -Data contained within the `UserInfo` object are converted to key-value pairs and displayed in the keys/logs section within an individual issue. - -![note_icon] _Crashlytics only stores the most recent 8 exceptions in a given app session. If your app throws more than 8 exceptions in a session, older exceptions are lost._ - -### Logs and custom keys - -Just like crash reports, you can embed logs and custom keys to add context to the NSError. However, there is a difference in what logs are attached to crashes versus logged errors. When a crash occurs and the app is relaunched, the logs Crashlytics retrieves from disk are those that were written right up to the time of the crash. When you log an NSError, the app does not immediately terminate. Because Crashlytics only sends the logged error report on the next app launch, and because Crashlytics must limit the amount of space allocated for logs on disk, it is possible to log enough after an NSError is recorded so that all relevant logs are rotated out by the time Crashlytics sends the report from the device. Keep this balance in mind when logging NSErrors and using `Log` and custom keys in your app. - -### Performance considerations - -Keep in mind that logging an NSError can be fairly expensive. At the time you make the call, Crashlytics captures the current thread’s call stack using a process called stack unwinding. This process can be CPU and I/O intensive, particularly on architectures that support DWARF unwinding (arm64 and x86). After the unwind is complete, the information is written to disk synchronously. This prevents data loss if the next line were to crash. - -While it is safe to call this API on a background thread, remember that dispatching this call to another queue will lose the context of the current stack trace. - -### What about NSExceptions? - -Crashlytics doesn’t offer a facility for logging/recording NSException instances directly. Generally speaking, the Cocoa and Cocoa Touch APIs are not exception-safe. That means the use of `@catch` in your Objective-C library code or in a third-party Objective-C code can have very serious unintended side-effects in your process, even when used with extreme care. You should never use `@catch` statements in your Objective-C code. Please refer to Apple’s [documentation][2] on the topic. - -## Manage Crash Insights data - -Crash Insights helps you resolve issues by comparing your anonymized stack traces to traces from other Firebase apps and letting you know if your issue is part of a larger trend. For many issues, Crash Insights even provides resources to help you debug the crash. - -Crash Insights uses aggregated crash data to identify common stability trends. If you’d prefer not to share your app's data, you can opt-out of Crash Insights from the Crash Insights menu at the top of your Crashlytics issue list in the [Firebase console][1]. - ---- - -# Extend Firebase Crashlytics with Cloud Functions - -You can trigger a function in response to Crashlytics issue events including new issues, regressed issues, and velocity alerts. To learn more about this, please, read the following [documentation][3]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/crashlytics/get-started) to see original Firebase documentation._ - -[1]: https://console.firebase.google.com -[2]: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Exceptions/Articles/ExceptionsAndCocoaFrameworks.html -[3]: https://firebase.google.com/docs/crashlytics/extend-with-cloud-functions -[note_icon]: https://cdn3.iconfinder.com/data/icons/UltimateGnome/22x22/apps/gnome-app-install-star.png -[warning_icon]: https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-20.png +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Database/Details.md b/docs/Firebase/Database/Details.md index 619419a7c..0ce8a2361 100755 --- a/docs/Firebase/Database/Details.md +++ b/docs/Firebase/Database/Details.md @@ -1,23 +1,18 @@ -Store and sync data with Firebase NoSQL cloud database. Data is synced across all clients in realtime, and remains available when your app goes offline. +# Firebase Realtime Database -The Firebase Realtime Database is a cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected client. When you build cross-platform apps with our iOS, Android, and JavaScript SDKs, all of your clients share one Realtime Database instance and automatically receive updates with the newest data. +This path is retained for existing links, but this repository does not maintain a copied Firebase Realtime Database walkthrough. -## Key capabilities +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -| | | -|-:|--| -| **Realtime** | Instead of typical HTTP requests, the Firebase Realtime Database uses data synchronization—every time data changes, any connected device receives that update within milliseconds. Provide collaborative and immersive experiences without thinking about networking code. | -| **Offline** | Firebase apps remain responsive even when offline because the Firebase Realtime Database SDK persists your data to disk. Once connectivity is reestablished, the client device receives any changes it missed, synchronizing it with the current server state. | -| **Accessible from Client Devices** | The Firebase Realtime Database can be accessed directly from a mobile device or web browser; there’s no need for an application server. Security and data validation are available through the Firebase Realtime Database Security Rules, expression-based rules that are executed when data is read or written. | +## Native Documentation -## How does it work? +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Realtime Database documentation: https://firebase.google.com/docs/database/ios/start -The Firebase Realtime Database lets you build rich, collaborative applications by allowing secure access to the database directly from client-side code. Data is persisted locally, and even while offline, realtime events continue to fire, giving the end user a responsive experience. When the device regains connection, the Realtime Database synchronizes the local data changes with the remote updates that occurred while the client was offline, merging any conflicts automatically. +## Binding Package -The Realtime Database provides a flexible, expression-based rules language, called Firebase Realtime Database Security Rules, to define how your data should be structured and when data can be read from or written to. When integrated with Firebase Authentication, developers can define who has access to what data, and how they can access it. +- Package README: [../NuGet/Database.md](../NuGet/Database.md) +- Package ID: `AdamE.Firebase.iOS.Database` +- Managed namespace: `Firebase.Database` -The Realtime Database is a NoSQL database and as such has different optimizations and functionality compared to a relational database. The Realtime Database API is designed to only allow operations that can be executed quickly. This enables you to build a great realtime experience that can serve millions of users without compromising on responsiveness. Because of this, it is important to think about how users need to access your data and then [structure it accordingly][1]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/database/) to see original Firebase documentation._ - -[1]: https://firebase.google.com/docs/database/web/structure-data \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Database/GettingStarted.md b/docs/Firebase/Database/GettingStarted.md index a8a4031dc..0ce8a2361 100755 --- a/docs/Firebase/Database/GettingStarted.md +++ b/docs/Firebase/Database/GettingStarted.md @@ -1,734 +1,18 @@ -# Firebase Database on iOS +# Firebase Realtime Database -The Firebase Database is a cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected client. When you build cross-platform apps with our Android, iOS, and JavaScript SDKs, all of your clients share one Realtime Database instance and automatically receive updates with the newest data. +This path is retained for existing links, but this repository does not maintain a copied Firebase Realtime Database walkthrough. -## Table of content +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -- [Add Firebase to your app](#add-firebase-to-your-app) -- [Configure Database in your app](#configure-database-in-your-app) -- [Structure Your Database](#structure-your-database) -- [Recommended documentation to get a better understanding of the Security & Rules of Firebase Database](#recommended-documentation-to-get-a-better-understanding-of-the-security-rules-of-firebase-database) -- [Configure Firebase Database Rules](#configure-firebase-database-rules) -- [Read and Write Data on iOS](#read-and-write-data-on-ios) - - [Basic write operations](#basic-write-operations) - - [Listen for value events](#listen-for-value-events) - - [Read data once](#read-data-once) - - [Update specific fields](#update-specific-fields) - - [Delete data](#delete-data) -- [Detach listeners](#detach-listeners) -- [Save data as transactions](#save-data-as-transactions) -- [Write data offline](#write-data-offline) -- [Work with Lists of Data on iOS](#work-with-lists-of-data-on-ios) - - [Append to a list of data](#append-to-a-list-of-data) - - [Listen for child events](#listen-for-child-events) - - [Listen for value events](#listen-for-value-events) -- [Sorting and filtering data](#sorting-and-filtering-data) - - [Sort data](#sort-data) - - [Filtering data](#filtering-data) - - [Limit the number of results](#limit-the-number-of-results) - - [Filter by key or value](#filter-by-key-or-value) -- [How query data is ordered](#how-query-data-is-ordered) - - [GetQueryOrderedByKey method](#getqueryorderedbykey-method) - - [GetQueryOrderedByValue method](#getqueryorderedbyvalue-method) - - [GetQueryOrderedByChild method](#getqueryorderedbychild-method) -- [Offline Capabilities on iOS](#offline-capabilities-on-ios) - - [Disk Persistence](#disk-persistence) - - [Persistence Behavior](#persistence-behavior) - - [Keeping Data Fresh](#keeping-data-fresh) - - [Querying Data Offline](#querying-data-offline) - - [Handling Transactions Offline](#handling-transactions-offline) -- [Managing Presence](#managing-presence) - - [How OnDisconnect methods works](#how-ondisconnect-methods-works) -- [Detecting Connection State](#detecting-connection-state) -- [Handling Latency](#handling-latency) - - [Server Timestamps](#server-timestamps) - - [Clock Skew](#clock-skew) -- [Automated Backups](#automated-backups) -- [Best practices](#best-practices) +## Native Documentation -## Add Firebase to your app +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Realtime Database documentation: https://firebase.google.com/docs/database/ios/start -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. +## Binding Package -## Configure Database in your app +- Package README: [../NuGet/Database.md](../NuGet/Database.md) +- Package ID: `AdamE.Firebase.iOS.Database` +- Managed namespace: `Firebase.Database` -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -App.Configure (); -``` - -## Structure Your Database - -Before you continue, please, read this [documentation][10] to know how Database is structured and best practices for data structure. - -## Recommended documentation to get a better understanding of the Security & Rules of Firebase Database - -Before you continue, I invite you to read these following docs to make your coding easier: - -* [Understand Rules][13] -* [Get Started][14] -* [Secure Data][15] -* [Secure User Data][16] -* [Index Data][17] -* [Manage Rules via REST][18] - -## Configure Firebase Database Rules - -The Realtime Database provides a declarative rules language that allows you to define how your data should be structured, how it should be indexed, and when your data can be read from and written to. By default, read and write access to your database is restricted so only authenticated users can read or write data. To get started without setting up [Authentication][3], you can [configure your rules for public access][4]. This does make your database open to anyone, even people not using your app, so be sure to restrict your database again when you set up authentication. - -## Read and Write Data on iOS - -This document covers the basics of reading and writing Firebase data. - -Firebase data is written to a `Database` reference and retrieved by attaching an asynchronous listener to the reference. The listener is triggered once for the initial state of the data and again anytime the data changes. - -To read or write data from the database, you need an instance of `DatabaseReference`: - -```csharp -DatabaseReference rootNode = Database.DefaultInstance.GetRootReference (); -``` - -Doing this, you will have a variable called `rootNode` that points to **https://yourFirebaseDatabase/** - -### Basic write operations - -For basic write operations, you can use `SetValue (T value)`, `SetValues (T [] values)` or `SetValues (NSObject [] values)` to save data to a specified reference, replacing any existing data at that path. You can use this method to pass types that correspond to the available JSON types as follows: - -* NSString -* NSNumber -* NSDictionary -* NSArray - -> ![warning_icon] ***Note:*** *If you pass an object different from these 4 types, an exception will be thrown. Types inherited from these 4 types will be accepted.* - -For instance, you can add a user with `SetValue (T value)`. First create a reference that points to the user reference: - -```csharp -DatabaseReference userNode = rootNode.GetChild ("users").GetChild (user.Uid); -``` - -Doing this, now you have a variable that points to **https://yourFirebaseDatabase/users/$userUid** where **$userUid** is the key of the reference. The reference is created even if it doesn't exist but only will be saved into Firebase Database until you assign some value to the reference: - -```csharp -object [] keys = { "username" }; -object [] values = { username }; -var data = NSDictionary.FromObjectsAndKeys (values, keys, keys.Length); - -userNode.SetValue (data); -``` - -You can avoid creating variables for each reference and assign the value directly: - -```csharp -rootNode.GetChild ("users").GetChild (user.Uid).SetValue (data); -``` - -Using `SetValue` in these ways overwrites data at the specified location, including any child nodes. However, you can still update a child without rewriting the entire object. If you want to allow users to update their profiles you could update the username as follows: - -```csharp -rootNode.GetChild ("users").GetChild (user.Uid).GetChild ("username").SetValue (username); -``` - -### Listen for value events - -To read data at a path and listen for changes, use the `ObserveEvent` or `ObserveSingleEvent` methods of `DatabaseReference` to observe `DataEventType.Value` events. - -| Event type | Typical usage | -|:-----------------------:|---------------------------------------------------------------| -| **DataEventType.Value** | Read and listen for changes to the entire contents of a path. | - -You can use the `DataEventType.Value` event to read the data at a given path, as it exists at the time of the event. This method is triggered once when the listener is attached and again every time the data, including any children, changes. The event callback is passed a `snapshot` containing all data at that location, including child data. If there is no data, the `value` of the `snapshot` returned is `null`. - -> ![note_icon] ***Important:*** *The `DataEventType.Value` event is fired every time data is changed at the specified database reference, including changes to children. To limit the size of your snapshots, attach only at the highest level needed for watching changes. For example, attaching a listener to the root of your database is not recommended.* - -The following example demonstrates a notes application retrieving the user's folders from the database: - -```csharp -DatabaseReference foldersNode = rootNode.GetChild ("folders").GetChild (user.Uid); -nuint handleReference = foldersNode.ObserveEvent (DataEventType.Value, (snapshot) => { - var folderData = snapshot.GetValue (); - // Do magic with the folder data -}); -``` - -The listener receives a `DataSnapshot` that contains the data at the specified location in the database at the time of the event in its `GetValue`, `GetValue`, `GetValues` or `GetValues` methods. You can assign the values to the appropriate native type, such as `NSDictionary`. If no data exists at the location, the value is `null`. - -### Read data once - -In some cases you may want a callback to be called once and then immediately removed, such as when initializing a UI element that you don't expect to change. You can use the `ObserveSingleEvent` method to simplify this scenario: the event callback added triggers once and then does not trigger again. - -This is useful for data that only needs to be loaded once and isn't expected to change frequently or require active listening. For instance, we can use this method to know the total of notes that a folder has: - -```csharp -nuint notesCount; -DatabaseReference notesCountNode = rootNode.GetChild ("folders").GetChild (user.Uid).GetChild (folderUid).GetChild ("notesCount"); -notesCountNode.ObserveSingleEvent (DataEventType.Value, (snapshot) => { - notesCount = snapshot.GetValue ().NUIntValue; -}, (error) => { - Console.WriteLine (error.LocalizedDescription); -}); -``` - -### Update specific fields - -To simultaneously write to specific children of a node without overwriting other child nodes, use the `UpdateChildValues` method. - -When calling updateChildValues, you can update lower-level child values by specifying a path for the key. If data is stored in multiple locations to scale better, you can update all instances of that data using [data fan-out][5]. For example, in notes app, the user wants to save a new note and simultaneously update the total of notes that the folder has. To do this, the code could be like this: - -```csharp -var noteUid = rootNode.GetChild ("notes").GetChild (user.Uid).GetChild (folderUid).GetChildByAutoId ().Key; - -object [] noteKeys = { "content", "created", "lastModified", "title" }; -object [] noteValues = { content, created, lastModified, title }; -var noteData = NSDictionary.FromObjectsAndKeys (noteValues, noteKeys, noteKeys.Length); - -object [] nodes = { $"notes/{user.Uid}/{folderUid}/{noteUid}", $"folders/{user.Uid}/{folderUid}/notesCount" }; -object [] nodesValues = { noteData, NSNumber.FromNUInt (++notesCount) }; -var childUpdates = NSDictionary.FromObjectsAndKeys (nodesValues, nodes, nodes.Length); - -rootNode.UpdateChildValues (childUpdates); -``` - -Using these paths, you can perform simultaneous updates to multiple locations in the JSON tree with a single call to `UpdateChildValues` method. Simultaneous updates made this way are atomic: either all updates succeed or all updates fail. - -### Delete data - -The simplest way to delete data is to call `RemoveValue` method on a reference to the location of that data. - -You can also delete by specifying `null` as the value for another write operation such as `SetValue` or `UpdateChildValues`. You can use this technique with `UpdateChildValues` to delete multiple children in a single API call: - -```csharp -DatabaseReference noteNode = rootNode.GetChild ("notes").GetChild (user.Uid).GetChild (folderUid).GetChild (noteUid); - -// Same functionality -noteNode.RemoveValue (); -noteNode.SetValue (null); - -object [] nodes = { $"notes/{user.Uid}/{folderUid}/{noteUid}" }; -object [] nodesValues = { null }; -var childUpdates = NSDictionary.FromObjectsAndKeys (nodesValues, nodes, nodes.Length); -rootNode.UpdateChildValues (childUpdates); -``` - -## Detach listeners - -Observers don't automatically stop syncing data when you leave a ViewController. If an observer isn't properly removed, it continues to sync data to local memory. When an observer is no longer needed, remove it by passing the associated **DatabaseHandle** to the `RemoveObserver` method: - -```csharp -DatabaseReference foldersNode = rootNode.GetChild ("folders").GetChild (user.Uid); -nuint handleReference = foldersNode.ObserveEvent (DataEventType.Value, (snapshot) => { - var folderData = snapshot.GetValue (); - // Do magic with the folder data -}); - -// Some other code - -// When you don't want to keep listening for changes in that node, do the following at some point of your app -foldersNode.RemoveObserver (handleReference); -``` - -When you add a callback block to a reference, a `nuint` is returned. These handles can be used to remove the callback block. - -If multiple listeners have been added to a database reference, each listener is called when an event is raised. In order to stop syncing data at that location, you must remove all observers at a location by calling the `RemoveAllObservers` method. - -Calling `RemoveObserver` or `RemoveAllObservers` on a listener does not automatically remove listeners registered on its child nodes; you must also be keep track of those references or handles to remove them. - -## Save data as transactions - -When working with data that could be corrupted by concurrent modifications, such as incremental counters, you can use a [transaction operation][6]. You give this operation two arguments: an update function and an optional completion callback. The update function takes the current state of the data as an argument and returns the new desired state you would like to write. - -For instance, imagine that you have a collaborative notes app, and some users are working in the same note, you could allow users to update the note as follows: - -```csharp -noteNode.RunTransaction ((currentData) => { - var noteData = currentData.GetValue (); - - if (noteData == null) - return TransactionResult.Success (currentData); - - var mutableNoteData = noteData.MutableCopy () as NSMutableDictionary; - mutableNoteData ["content"] = content; - mutableNoteData ["title"] = title; - - currentData.SetValue (mutableNoteData); - - return TransactionResult.Success (currentData); -}, (error, commited, snapshot) => { - if (error != null) { - Console.WriteLine (error.LocalizedDescription); - } -}); -``` - -Using a transaction prevents note content from being incorrect if multiple users edit the same note at the same time or the client had stale data. The value contained in the `MutableData` class is initially the client's last known value for the path, or `null` if there is none. The server compares the initial value against it's current value and accepts the transaction if the values match, or rejects it. If the transaction is rejected, the server returns the current value to the client, which runs the transaction again with the updated value. This repeats until the transaction is accepted or too many attempts have been made. - -> ![note_icon] ***Note:*** *Because `RunTransaction` method is called multiple times, it must be able to handle `null` data. Even if there is existing data in your remote database, it may not be locally cached when the transaction function is run, resulting in `null` for the initial value.* - -## Write data offline - -If a client loses its network connection, your app will continue functioning correctly. - -Every client connected to a Firebase database maintains its own internal version of any active data. When data is written, it's written to this local version first. The Firebase client then synchronizes that data with the remote database servers and with other clients on a "best-effort" basis. - -As a result, all writes to the database trigger local events immediately, before any data is written to the server. This means your app remains responsive regardless of network latency or connectivity. - -Once connectivity is reestablished, your app receives the appropriate set of events so that the client syncs with the current server state, without having to write any custom code. - -## Work with Lists of Data on iOS - -### Append to a list of data - -Use the `GetChildByAutoId` method to append data to a list in multiuser applications. The `GetChildByAutoId` method generates a unique key every time a new child is added to the specified Firebase reference. By using these auto-generated keys for each new element in the list, several clients can add children to the same location at the same time without write conflicts. The unique key generated by `GetChildByAutoId` is based on a timestamp, so list items are automatically ordered chronologically. - -You can use the reference to the new data returned by the `GetChildByAutoId` method to get the value of the child's auto-generated key or set data for the child. Calling `Key` property on a `GetChildByAutoId` reference returns the auto-generated key. - -You can use these auto-generated keys to simplify flattening your data structure. For more information, see the data fan-out [example][5]. - -### Listen for child events - -Child events are triggered in response to specific operations that happen to the children of a node from an operation such as a new child added through the `GetChildByAutoId` method or a child being updated through the `UpdateChildValues` method. - -| Event type | Typical usage | -|:------------------------------:|---------------| -| **DataEventType.ChildAdded** | Retrieve lists of items or listen for additions to a list of items. This event is triggered once for each existing child and then again every time a new child is added to the specified path. The listener is passed a snapshot containing the new child's data. Also, It is used with data that is ordered by `GetQueryOrderedByChild` or `GetQueryOrderedByValue` methods. | -| **DataEventType.ChildChanged** | Listen for changes to the items in a list. This event is triggered any time a child node is modified. This includes any modifications to descendants of the child node. The snapshot passed to the event listener contains the updated data for the child. | -| **DataEventType.ChildRemoved** | Listen for items being removed from a list. This event is triggered when an immediate child is removed.The snapshot passed to the callback block contains the data for the removed child. | -| **DataEventType.ChildMoved** | Listen for changes to the order of items in an ordered list. This event is triggered whenever an update causes reordering of the child. | - -Each of these together can be useful for listening to changes to a specific node in a database. For example, the notes app could use these methods together to monitor activity when a folder is created or deleted, as shown below: - -```csharp -// Listen for new folders in the Firebase database -foldersNode.ObserveEvent (DataEventType.ChildAdded, (snapshot, prevKey) => { - var data = snapshot.GetValue (); - var created = data ["created"].ToString (); - var lastModified = data ["lastModified"].ToString (); - var name = data ["name"].ToString (); - var notesCount = (data ["notesCount"] as NSNumber).UInt32Value; - - var folder = new Folder { - Name = name, - Created = created, - LastModified = AppDelegate.ConvertUnformattedUtcDateToCurrentDate (lastModified), - NotesCount = notesCount - }; - - folders.Add (folder); - - TableView.ReloadData (); -}); - -// Listen for deleted comments in the Firebase database -foldersNode.ObserveEvent (DataEventType.ChildRemoved, (snapshot, prevKey) => { - var data = snapshot.GetValue (); - var created = data ["created"].ToString (); - var lastModified = data ["lastModified"].ToString (); - var name = data ["name"].ToString (); - var notesCount = (data ["notesCount"] as NSNumber).UInt32Value; - - var folder = new Folder { - Name = name, - Created = created, - LastModified = AppDelegate.ConvertUnformattedUtcDateToCurrentDate (lastModified), - NotesCount = notesCount - }; - - folders.Remove (folder); - - TableView.ReloadData (); -}); -``` - -### Listen for value events - -While listening for child events is the recommended way to read lists of data, there are situations listening for value events on a list reference is useful. - -Attaching a `DataEventType.Value` observer to a list of data will return the entire list of data as a single DataSnapshot, which you can then loop over to access individual children. - -Even when there is only a single match for the query, the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result: - -```csharp -foldersNode.ObserveEvent (DataEventType.Value, (snapshot) => { - // Loop over the children - NSEnumerator children = snapshot.Children; - var child = children.NextObject () as DataSnapshot; - - while (child != null) { - // Work with data... - - child = children.NextObject () as DataSnapshot; - } -}); -``` - -This pattern can be useful when you want to fetch all children of a list in a single operation, rather than listening for additional child added events. - -## Sorting and filtering data - -You can use the Realtime Database `DatabaseQuery` class to retrieve data sorted by key, by value, or by the value of a child. You can also filter the sorted result to a specific number of results or a range of keys or values. - -> ![note_icon] ***Note:*** *Filtering and sorting can be expensive, especially when done on the client. If your app uses queries, define the .indexOn rule to index those keys on the server and improve query performance as described in [Indexing Your Data][7].* - -### Sort data - -To retrieve sorted data, start by specifying one of the order-by methods to determine how results are ordered: - -| Method | Usage | -|:--------------------------:|------------------------------------------------------| -| **GetQueryOrderedByKey** | Order results by child keys. | -| **GetQueryOrderedByValue** | Order results by child values. | -| **GetQueryOrderedByChild** | Order results by the value of a specified child key. | - -You can only use **one** order-by method at a time. Calling an order-by method multiple times in the same query **throws an error**. - -The following example demonstrates how you could retrieve a list of a notes sorted by their last time modified: - -```csharp -DatabaseReference notesNode = rootNode.GetChild ("notes").GetChild (user.Uid).GetChild (folderUid); - -// Get all notes sorted by their last time modified in ascending order -DatabaseQuery notesByDate = notesNode.GetQueryOrderedByChild ("lastModified"); -notesByDate.ObserveEvent (DataEventType.ChildAdded, (snapshot) => { - var data = snapshot.GetValue (); - var content = data ["content"].ToString (); - var created = data ["created"].ToString (); - var lastModified = data ["lastModified"].ToString (); - var title = data ["title"].ToString (); - - var note = new Note { - Content = content, - Created = created, - LastModified = lastModified, - Title = title - } - - notes.Add (note); - - TableView.ReloadData (); -}); -``` - -This query retrieves the user's notes from the path in the database based on their user Id and the folder Id, ordered by the last time modified. This technique of using IDs as index keys is called data fan out, you can read more about it in [Structure Your Database][5]. - -The call to the `GetQueryOrderedByChild` method specifies the child key to order the results by. In this case, notes are sorted by the value of the **lastModified** child in each post. For more information on how other data types are ordered, see [How query data is ordered][8]. - -### Filtering data - -To filter data, you can combine any of the limit or range methods with an order-by method when constructing a query. - -| Method | Usage | -|:---------------------------:|------------------------------------------------------------------------------------------------------------| -| **GetQueryLimitedToFirst** | Sets the maximum number of items to return from the beginning of the ordered list of results. | -| **GetQueryLimitedToLast** | Sets the maximum number of items to return from the end of the ordered list of results. | -| **GetQueryStartingAtValue** | Return items greater than or equal to the specified key or value, depending on the order-by method chosen. | -| **GetQueryEndingAtValue** | Return items less than or equal to the specified key or value, depending on the order-by method chosen. | -| **GetQueryEqualToValue** | Return items equal to the specified key or value, depending on the order-by method chosen. | - -Unlike the order-by methods, you can combine multiple limit or range functions. For example, you can combine the `GetQueryStartingAtValue` and `GetQueryEndingAtValue` methods to limit the results to a specified range of values. - -### Limit the number of results - -You can use the `GetQueryLimitedToFirst` and `GetQueryLimitedToLast` methods to set a maximum number of children to be synced for a given callback. For example, if you use `GetQueryLimitedToFirst` to set a limit of 100, you initially only receive up to 100 `DataEventType.ChildAdded` callbacks. If you have fewer than 100 items stored in your Firebase database, an `DataEventType.ChildAdded` callback fires for each item. - -As items change, you receive `DataEventType.ChildAdded` callbacks for items that enter the query and `DataEventType.ChildRemoved` callbacks for items that drop out of it so that the total number stays at 100. - -The following example demonstrates how example notes app retrieves a list of the 100 most recent modified notes by user: - -```csharp -DatabaseReference notesNode = rootNode.GetChild ("notes").GetChild (user.Uid).GetChild (folderUid); - -// First 100 notes sorted by their last time modified in ascending order -DatabaseQuery notesByDate = notesNode.GetQueryOrderedByChild ("lastModified").GetQueryLimitedToFirst (100); -notesByDate.ObserveEvent (DataEventType.ChildAdded, (snapshot) => { - var data = snapshot.GetValue (); - var content = data ["content"].ToString (); - var created = data ["created"].ToString (); - var lastModified = data ["lastModified"].ToString (); - var title = data ["title"].ToString (); - - var note = new Note { - Content = content, - Created = created, - LastModified = lastModified, - Title = title - } - - notes.Add (note); - - TableView.ReloadData (); -}); -``` - -### Filter by key or value - -You can use `GetQueryStartingAtValue`, `GetQueryEndingAtValue`, and `GetQueryEqualToValue` to choose arbitrary starting, ending, and equivalence points for queries. This can be useful for paginating data or finding items with children that have a specific value. - -## How query data is ordered - -This section explains how data is sorted by each of the order-by methods in the `DatabaseQuery` class. - -### GetQueryOrderedByKey method - -When using `GetQueryOrderedByKey` to sort your data, data is returned in ascending order by key. - -1. Children with a key that can be parsed as a 32-bit integer come first, sorted in ascending order. -2. Children with a string value as their key come next, sorted [lexicographically][9] in ascending order. - -### GetQueryOrderedByValue method - -When using `GetQueryOrderedByValue`, children are ordered by their value. The ordering criteria are the same as in `GetQueryOrderedByChild`, except the value of the node is used instead of the value of a specified child key. - -### GetQueryOrderedByChild method - -When using `GetQueryOrderedByChild`, data that contains the specified child key is ordered as follows: - -1. Children with a `null` value for the specified child key come first. -2. Children with a value of `false` for the specified child key come next. If multiple children have a value of false, they are sorted [lexicographically][9] by key. -3. Children with a value of `true` for the specified child key come next. If multiple children have a value of true, they are sorted lexicographically by key. -4. Children with a numeric value come next, sorted in ascending order. If multiple children have the same numerical value for the specified child node, they are sorted by key. -5. Strings come after numbers and are sorted lexicographically in ascending order. If multiple children have the same value for the specified child node, they are ordered lexicographically by key. -6. Objects come last and are sorted lexicographically by key in ascending order. - -## Offline Capabilities on iOS - -Firebase apps work great offline and we have several features to make the experience even better. Enabling disk persistence allows your app to keep all of its state even after an app restart. We provide several tools for monitoring presence and connectivity state. - -### Disk Persistence - -Firebase apps automatically handle temporary network interruptions for you. Cached data will still be available while offline and your writes will be resent when network connectivity is recovered. Enabling disk persistence allows our app to also keep all of its state even after an app restart. We can enable disk persistence with just one line of code: - -```csharp -Database.DefaultInstance.PersistenceEnabled = true; -``` - -With disk persistence enabled, our synced data and writes will be persisted to disk across app restarts and our app should work seamlessly in offline situations. - -### Persistence Behavior - -By enabling persistence, any data that we sync while online will be persisted to disk and available offline, even when we restart the app. This means our app will work as it would online using the local data stored in the cache. Listener callbacks will continue to fire for local updates. - -The Firebase Database client automatically keeps a queue of all write operations that are performed while our application is offline. When persistence is enabled, this queue will also be persisted to disk so all of our writes will be available when we restart the app. When the app regains connectivity, all of the operations will be sent to the server. - -If our app uses [Firebase Authentication][3], the client will persist the user's authentication token across restarts. If the auth token expires while our app is offline, the client will pause our write operations until we re-authenticate, else our writes might fail due to security rules. - -### Keeping Data Fresh - -The Firebase Database synchronizes and stores a local copy of the data for active listeners. In addition, you can keep specific locations in sync: - -```csharp -DatabaseReference foldersNode = rootNode.GetChild ("folders").GetChild (AppDelegate.UserUid); -foldersNode.KeepSynced (true); -``` - -The client will automatically download the data at these locations and keep it in sync even if the reference has no active listeners. You can turn synchronization back off with the following line of code: - -```csharp -foldersNode.KeepSynced (false); -``` - -By default, 10MB of previously synced data will be cached. This should be enough for most applications. If the cache outgrows its configured size, the Firebase Database will purge data that has been used least recently. Data that is kept in sync, will not be purged from the cache. - -### Querying Data Offline - -The Firebase Database stores data returned from a query for use when offline. For queries constructed while offline, the Firebase Database continues to work for previously loaded data. If the requested data hasn't loaded, the Firebase Database loads data from the local cache. When we come back online our data will load and reflect the query. - -For example, here we have a piece of code that queries for the last four items in our Firebase Database of notes: - -```csharp -DatabaseReference notesNode = rootNode.GetChild ("notes").GetChild (user.Uid).GetChild (folderUid); - -// Last 4 notes sorted by their last time modified -DatabaseQuery notesByDate = notesNode.GetQueryOrderedByChild ("lastModified").GetQueryLimitedToLast (4); -notesByDate.ObserveEvent (DataEventType.ChildAdded, (snapshot) => { - var data = snapshot.GetValue (); - var content = data ["content"].ToString (); - var created = data ["created"].ToString (); - var lastModified = data ["lastModified"].ToString (); - var title = data ["title"].ToString (); - - var note = new Note { - Content = content, - Created = created, - LastModified = lastModified, - Title = title - } - - notes.Add (note); - - TableView.ReloadData (); -}); -``` - -Now let's assume that the user loses connection, goes offline, and restarts the app. While still offline, we query for the last two items from the same location. This query will successfully return the last two items because we had loaded all four in the query above: - -```csharp -DatabaseReference notesNode = rootNode.GetChild ("notes").GetChild (user.Uid).GetChild (folderUid); - -// Last 2 notes sorted by their last time modified -DatabaseQuery notesByDate = notesNode.GetQueryOrderedByChild ("lastModified").GetQueryLimitedToLast (2); -notesByDate.ObserveEvent (DataEventType.ChildAdded, (snapshot) => { - var data = snapshot.GetValue (); - var content = data ["content"].ToString (); - var created = data ["created"].ToString (); - var lastModified = data ["lastModified"].ToString (); - var title = data ["title"].ToString (); - - var note = new Note { - Content = content, - Created = created, - LastModified = lastModified, - Title = title - } - - notes.Add (note); - - TableView.ReloadData (); -}); -``` - -In the above example, the Firebase Database client raises `DataEventType.ChildAdded` events for the latest modified notes, via our persisted cache. But it will not raise a `DataEventType.Value` event, since we've never done that query while online. - -If we were to request the last six items while offline, we'd get `DataEventType.ChildAdded` events for the four cached items straight away. When we come back online, the Firebase Realtime Database client will synchronize with the server and we'll get the final two `DataEventType.ChildAdded` and the `DataEventType.Value` events. - -### Handling Transactions Offline - -Any transactions that are performed while our app is offline, will be queued. Once the app regains network connectivity, the transactions will be sent to the server. - -It's important to know that **Transactions are not persisted across app restarts**. Even with persistence enabled, transactions are not persisted across app restarts. So you cannot rely on transactions done offline being committed to your Firebase Database. To provide the best user experience, your app should show that a transaction has not been saved into your Firebase Database yet, or make sure your app remembers them manually and executes them again after an app restart. - -## Managing Presence - -In realtime applications it is often useful to detect when clients connect and disconnect. For example, we may want to mark a user as 'offline' when their client disconnects. - -Firebase Database clients provide simple primitives that allow data to be written to the database when a client disconnects from the Firebase Database servers. These updates will occur whether the client disconnects cleanly or not, so we can rely on them to clean up data even if a connection is dropped or a client crashes. All write operations, including setting, updating, and removing, can be performed upon a disconnection. - -Here is a simple example of writing data upon disconnection by using the **OnDisconnect** primitive: - -```csharp -DatabaseReference disconnectedNode = rootNode.GetChild ("disconnected"); -// Write a string when this client loses connection -disconnectedNode.SetValueOnDisconnect (new NSString ("I disconnected!")); -``` - -### How OnDisconnect methods works - -When an **OnDisconnect** operation is established, it lives on the Firebase Database server. The server checks security to make sure the user can perform the write event requested, and informs the client if it is invalid. The server then monitors the connection. If at any point it times out, or is actively closed by the client, the server checks security a second time (to make sure the operation is still valid) and then invokes the event. - -The client can use the callback on the write operation to ensure the **OnDisconnect** was correctly attached: - -```csharp -rootNode.RemoveValueOnDisconnect ((error, reference) => { - if (error != null) { - Console.WriteLine ($"Could not establish onDisconnect event: {error.LocalizedDescription}"); - } -}); -``` - -An **OnDisconnect** event can also be canceled by calling `CancelDisconnectOperations` method: - -```csharp -rootNode.CancelDisconnectOperations (); -``` - -## Detecting Connection State - -For many presence-related features, it is useful for a client to know when it is online or offline. Firebase Database clients provide a special location at **/.info/connected** which is updated every time the client's connection state changes. Here is an example: - -```csharp -DatabaseReference connectedNode = Database.DefaultInstance.GetReferenceFromPath (".info/connected"); -connectedNode.ObserveEvent (DataEventType.Value, (snapshot) => { - var connected = snapshot.GetValue ().BoolValue; - - if (connected) - Console.WriteLine ("Connected"); - else - Console.WriteLine ("Not connected"); -}); -``` - -**/.info/connected** is a boolean value which is not synchronized between clients because the value is dependent on the state of the client. In other words, if one client reads **/.info/connected** as `false`, this is no guarantee that a separate client will also read `false`. - -## Handling Latency - -### Server Timestamps - -The Firebase Database servers provide a mechanism to insert timestamps generated on the server as data. This feature, combined with **OnDisconnect**, provides an easy way to reliably make note of the time at which a client disconnected: - -```csharp -DatabaseReference userLastOnlineNode = Database.DefaultInstance.GetReferenceFromPath ($"users/{user.Uid}/lastOnline"); -userLastOnlineNode.SetValueOnDisconnect (ServerValue.Timestamp); -``` - -### Clock Skew - -While `ServerValue.Timestamp` is much more accurate, and preferable for most read/write ops, it can occasionally be useful to estimate the clients clock skew with respect to the Firebase Database's servers. We can attach a callback to the location **/.info/serverTimeOffset** to obtain the value, in milliseconds, that Firebase Database clients will add to the local reported time (epoch time in milliseconds) to estimate the server time. Note that this offset's accuracy can be affected by networking latency, and so is useful primarily for discovering large (> 1 second) discrepancies in clock time: - -```csharp -DatabaseReference offsetNode = Database.DefaultInstance.GetReferenceFromPath (".info/serverTimeOffset"); -offsetNode.ObserveSingleEvent (DataEventType.Value, (snapshot) => { - var offset = snapshot.GetValue ().DoubleValue; - - var ticksFrom1970 = new System.DateTime (1970, 1, 1, 0, 0, 0, 0).Ticks; - var ticksUtc = System.DateTime.UtcNow.Ticks; - var epoch = (ticksUtc - ticksFrom1970) / System.TimeSpan.TicksPerMillisecond; - - var estimatedServerTimeMs = epoch + offset; -}); -``` - -## Automated Backups - -Please, read this [Firebase documentation][19] to learn more about Automated Backups. - -## Best practices - -To learn about Firebase Database best practices, please visit this [Firebase post][11]. - -## Automated Backups - -[Blaze][20] plan users can set up their Firebase Realtime Database for automatic backups, a self-service feature that enables daily backups of your Database application data and [rules][13] in JSON format to a [Google Cloud Storage][21] bucket. - -To learn more about this, please, read the following [documentation][22]. - -## Extend Realtime Database with Cloud Functions - -With Cloud Functions, you can handle events in the Firebase Realtime Database with no need to update client code. Cloud Functions lets you run database operations with full administrative privileges, and ensures that each change to the database is processed individually. You can make Firebase Database changes via the [DeltaSnapshot][23] or via the [Admin SDK][24]. - -To learn more about this, please, read the following [documentation][25]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/database/ios/start) to see original Firebase documentation._ - -[1]: https://firebase.google.com/console/ -[2]: http://support.google.com/firebase/answer/7015592 -[3]: https://components.xamarin.com/view/firebaseiosanalytics -[4]: https://firebase.google.com/docs/database/security/quickstart#sample-rules -[5]: https://firebase.google.com/docs/database/ios/structure-data#fanout -[6]: https://firebase.google.com/docs/reference/ios/firebasedatabase/interface_f_i_r_database_reference.html#a796bff455159479a44b225eeaa2ba9d6 -[7]: https://firebase.google.com/docs/database/security/indexing-data -[8]: https://firebase.google.com/docs/database/ios/lists-of-data#data_order -[9]: http://en.wikipedia.org/wiki/Lexicographical_order -[10]: https://firebase.google.com/docs/database/ios/structure-data -[11]: https://firebase.googleblog.com/2015/10/best-practices-for-ios-uiviewcontroller_6.html -[13]: https://firebase.google.com/docs/database/security/ -[14]: https://firebase.google.com/docs/database/security/quickstart -[15]: https://firebase.google.com/docs/database/security/securing-data -[16]: https://firebase.google.com/docs/database/security/user-security -[17]: https://firebase.google.com/docs/database/security/indexing-data -[18]: https://firebase.google.com/docs/database/rest/app-management -[19]: https://firebase.google.com/docs/database/ios/backups -[20]: https://firebase.google.com/pricing/ -[21]: https://cloud.google.com/storage/docs/ -[22]: https://firebase.google.com/docs/database/backups -[23]: https://firebase.google.com/docs/reference/functions/functions.database.DeltaSnapshot -[24]: https://firebase.google.com/docs/database/admin/start -[25]: https://firebase.google.com/docs/database/extend-with-functions -[note_icon]: https://cdn3.iconfinder.com/data/icons/UltimateGnome/22x22/apps/gnome-app-install-star.png -[warning_icon]: https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-20.png +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/NuGet/ABTesting.md b/docs/Firebase/NuGet/ABTesting.md index 5b96a72cc..99d5d0578 100644 --- a/docs/Firebase/NuGet/ABTesting.md +++ b/docs/Firebase/NuGet/ABTesting.md @@ -1,52 +1,35 @@ # AdamE.Firebase.iOS.ABTesting -.NET bindings for Firebase A/B Testing on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase A/B Testing on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase A/B Testing Apple SDK surface exposed in the `Firebase.ABTesting` namespace. It provides access to experiment payload coordination APIs such as `ExperimentController`, `LifecycleEvents`, and default experiment lifecycle event names. +A/B Testing experiment payload and experiment-controller APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- Firebase A/B Testing experiment payload handling from C# -- access to `ExperimentController.SharedInstance` -- experiment lifecycle event names for activation, timeout, expiration, and clearing +## Native Documentation -Most app code uses this package indirectly through Firebase feature packages such as Remote Config, In-App Messaging, or Performance Monitoring. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase A/B Testing documentation: https://firebase.google.com/docs/ab-testing -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.ABTesting` +- Managed namespace: `Firebase.ABTesting` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup -- Firebase A/B Testing documentation: https://firebase.google.com/docs/ab-testing/ - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -56,96 +39,21 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.ABTesting ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. Direct use of A/B Testing payload APIs is uncommon in app code, but the binding exposes the native controller surface. - -```csharp -using System; -using Firebase.ABTesting; -using Firebase.Core; -using Foundation; - -App.Configure(); - -var controller = ExperimentController.SharedInstance; -var events = new LifecycleEvents -{ - SetExperimentEventName = DefaultLifecycleEventNames.SetExperiment, - ActivateExperimentEventName = DefaultLifecycleEventNames.ActivateExperiment, - ClearExperimentEventName = DefaultLifecycleEventNames.ClearExperiment, - TimeoutExperimentEventName = DefaultLifecycleEventNames.TimeoutExperiment, - ExpireExperimentEventName = DefaultLifecycleEventNames.ExpireExperiment, -}; - -controller.UpdateExperiments( - "remote-config", - events, - ExperimentPayloadExperimentOverflowPolicy.DiscardOldest, - 0, - Array.Empty(), - error => - { - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - } - }); -``` +## Binding Notes -## Common companion packages +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.RemoteConfig` - commonly uses A/B Testing experiment payloads. -- `AdamE.Firebase.iOS.InAppMessaging` - commonly uses A/B Testing experiment payloads. -- `AdamE.Firebase.iOS.PerformanceMonitoring` - package metadata references A/B Testing as a companion dependency. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -## Firebase app configuration +Most app code uses A/B Testing indirectly through Remote Config, In-App Messaging, or Performance Monitoring. Direct use still requires Firebase to be configured first. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +## Version Alignment -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -If the official Firebase docs for this feature require additional setup, follow those docs first. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -## Package versioning rules (important) - -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. - -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.ABTesting`. API names closely mirror the native Firebase Apple SDK surface and expose Apple-native concepts such as `NSError`, `NSData`, and callback-based completion handlers. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/Analytics.md b/docs/Firebase/NuGet/Analytics.md index c7a239234..a088ef35c 100644 --- a/docs/Firebase/NuGet/Analytics.md +++ b/docs/Firebase/NuGet/Analytics.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.Analytics -.NET bindings for Firebase Analytics on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Analytics on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Analytics Apple SDK surface exposed in the `Firebase.Analytics` namespace. It provides static `Analytics` APIs and event, parameter, user property, consent, and session constants for reporting app analytics data to Firebase. +Analytics event, user property, consent, session, and conversion APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- analytics event logging with `Analytics.LogEvent` -- user property and user ID reporting with `Analytics.SetUserProperty` and `Analytics.SetUserId` -- analytics collection, consent, and session controls -- access to the Firebase Analytics app instance ID +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Analytics documentation: https://firebase.google.com/docs/analytics/get-started -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.Analytics` +- Managed namespace: `Firebase.Analytics` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup -- Firebase Analytics documentation: https://firebase.google.com/docs/analytics/ios/start - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,78 +39,19 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.Analytics ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System.Collections.Generic; -using Firebase.Analytics; -using Firebase.Core; - -App.Configure(); - -Analytics.LogEvent(EventNamesConstants.SelectContent, new Dictionary -{ - { ParameterNamesConstants.ContentType, "screen" }, - { ParameterNamesConstants.ItemId, "home" }, -}); - -Analytics.SetUserProperty("paid", "account_type"); -Analytics.SetUserId("user-123"); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Installations` - package metadata references Firebase Installations for underlying Firebase identity support. - -## Firebase app configuration - -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +## Binding Notes -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -If the official Firebase docs for this feature require additional setup, follow those docs first. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -## Package versioning rules (important) +## Version Alignment -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.Analytics`. API names closely mirror the native Firebase Analytics SDK surface, including static APIs on `Analytics` and Apple-native data shapes such as `NSDictionary` for parameter dictionaries. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/AppCheck.md b/docs/Firebase/NuGet/AppCheck.md index 7c9949f6a..00d758837 100644 --- a/docs/Firebase/NuGet/AppCheck.md +++ b/docs/Firebase/NuGet/AppCheck.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.AppCheck -.NET bindings for Firebase App Check on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase App Check on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase App Check Apple SDK surface exposed in the `Firebase.AppCheck` namespace. It provides access to App Check token APIs, provider factories, debug provider support, DeviceCheck support, and App Attest provider types exposed by the native SDK. +App Check provider, token, debug-provider, and limited-use token APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- App Check provider configuration before Firebase app initialization -- App Check token retrieval through `AppCheck.SharedInstance` -- debug, DeviceCheck, or App Attest provider binding access -- App Check token auto-refresh controls +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase App Check documentation: https://firebase.google.com/docs/app-check -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.AppCheck` +- Managed namespace: `Firebase.AppCheck` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup -- Firebase App Check documentation: https://firebase.google.com/docs/app-check/ios/overview - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,82 +39,21 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.AppCheck ``` -## Basic usage - -Set the App Check provider factory before calling `Firebase.Core.App.Configure()`. - -```csharp -using System; -using Firebase.AppCheck; -using Firebase.Core; - -AppCheck.SetAppCheckProviderFactory(new AppCheckDebugProviderFactory()); -App.Configure(); - -AppCheck.SharedInstance.TokenForcingRefresh(true, (token, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(token?.ExpirationDate); -}); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. - -## Firebase app configuration - -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +## Binding Notes -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -If the official Firebase docs for this feature require additional setup, follow those docs first. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -## Package versioning rules (important) +If you use a debug or custom provider factory, call `Firebase.AppCheck.AppCheck.SetAppCheckProviderFactory(...)` before `Firebase.Core.App.Configure()`. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +## Version Alignment -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.AppCheck`. API names closely mirror the native Firebase App Check SDK surface and expose Apple-native concepts such as `NSError`, `NSDate`, and provider factory protocols. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -App Check support in this binding was added through a special effort by GitHub user Kapusch. - -If this App Check package is valuable in your app or organization, sponsorship helps support that work and continued maintenance. - -- GitHub Sponsors: https://github.com/sponsors/Kapusch diff --git a/docs/Firebase/NuGet/AppDistribution.md b/docs/Firebase/NuGet/AppDistribution.md index dca000bac..ab5c12313 100644 --- a/docs/Firebase/NuGet/AppDistribution.md +++ b/docs/Firebase/NuGet/AppDistribution.md @@ -1,52 +1,35 @@ # AdamE.Firebase.iOS.AppDistribution -.NET bindings for Firebase App Distribution on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase App Distribution on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase App Distribution Apple SDK surface exposed in the `Firebase.AppDistribution` namespace. It provides access to tester sign-in, tester sign-out, update checking, and release metadata APIs exposed by the native SDK. +Tester sign-in, sign-out, release lookup, and update-check APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- in-app tester sign-in for Firebase App Distribution builds -- update checking for distributed tester builds -- release metadata such as display version, build version, release notes, and expiration state +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Firebase App Distribution documentation: https://firebase.google.com/docs/app-distribution -## Supported target frameworks +## Package -This package is intended for Apple platform TFMs such as: +- Package ID: `AdamE.Firebase.iOS.AppDistribution` +- Managed namespace: `Firebase.AppDistribution` + +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -56,86 +39,21 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.AppDistribution ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using Firebase.Core; - -App.Configure(); - -var appDistribution = Firebase.AppDistribution.AppDistribution.SharedInstance; - -if (!appDistribution.IsTesterSignedIn) -{ - appDistribution.SignInTester(error => - { - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(appDistribution.IsTesterSignedIn); - }); -} -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Installations` - package metadata references Firebase Installations for underlying Firebase identity support. +## Binding Notes -This package is part of the Firebase `12.8.0` package line. Firebase's `12.8.0` aggregate CocoaPods spec exposes App Distribution through `Firebase/AppDistribution`, which resolves the native `FirebaseAppDistribution` pod at `12.8.0-beta`. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -## Firebase app configuration +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +The native Firebase App Distribution Apple SDK can use beta native framework versions behind the aggregate Firebase package line. Keep the NuGet package line aligned with the rest of Firebase in the application. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +## Version Alignment -If the official Firebase docs for this feature require additional setup, follow those docs first. +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -## Package versioning rules (important) +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. - -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.AppDistribution`. API names closely mirror the native Firebase App Distribution SDK surface. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/Auth.md b/docs/Firebase/NuGet/Auth.md index 76f6951c9..6632ea30a 100644 --- a/docs/Firebase/NuGet/Auth.md +++ b/docs/Firebase/NuGet/Auth.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.Auth -.NET bindings for Firebase Authentication on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Auth on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Authentication Apple SDK surface exposed in the `Firebase.Auth` namespace. It provides access to `Auth`, `User`, credentials, auth state listeners, action code APIs, password and email-link flows, provider sign-in APIs, and emulator configuration methods. +Authentication, provider credential, user, action-code, tenant, reCAPTCHA, and token APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- sign-in, sign-up, and sign-out flows from C# -- access to the current Firebase Auth user through `Auth.CurrentUser` -- auth state or ID token change listeners -- email/password, anonymous, custom token, credential, or provider-based sign-in APIs +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Auth documentation: https://firebase.google.com/docs/auth/ios/start -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.Auth` +- Managed namespace: `Firebase.Auth` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup -- Firebase Authentication documentation: https://firebase.google.com/docs/auth/ios/manage-users - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,88 +39,21 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.Auth ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using Firebase.Auth; -using Firebase.Core; - -App.Configure(); - -var auth = Auth.DefaultInstance; -auth.SignInAnonymously((result, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(result?.User?.Uid); -}); - -var listener = auth.AddAuthStateDidChangeListener((_, user) => -{ - Console.WriteLine(user?.Email); -}); - -// Later, when the listener is no longer needed: -auth.RemoveAuthStateDidChangeListener(listener); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. +## Binding Notes -## Firebase app configuration +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +App delegate callback helpers are exposed on `Firebase.Auth.Auth.DefaultInstance`, including `CanHandleUrl(url)`, `CanHandleNotification(userInfo)`, and `SetApnsToken(...)`. For phone-auth test flows, set `Auth.DefaultInstance.Settings.AppVerificationDisabledForTesting` before verification calls. -If the official Firebase docs for this feature require additional setup, follow those docs first. +## Version Alignment -## Package versioning rules (important) +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.Auth`. API names closely mirror the native Firebase Authentication SDK surface and expose Apple-native concepts such as `NSError`, `NSUrl`, `NSDictionary`, and callback-based completion handlers. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/CloudFirestore.md b/docs/Firebase/NuGet/CloudFirestore.md index 2452f963e..97ed55cf2 100644 --- a/docs/Firebase/NuGet/CloudFirestore.md +++ b/docs/Firebase/NuGet/CloudFirestore.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.CloudFirestore -.NET bindings for Cloud Firestore on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Cloud Firestore on Apple platforms. -## What this package provides +## Scope -This package binds the Cloud Firestore Apple SDK surface exposed in the `Firebase.CloudFirestore` namespace. It provides access to `Firestore`, collections, documents, queries, listeners, field values, batch writes, transactions, aggregate queries, and Firestore settings. +Firestore app, collection, document, query, listener, transaction, cache, aggregate, vector, and index APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- document and collection access from C# -- Firestore queries and snapshot listeners -- document writes, updates, deletes, and batch writes -- Firestore field values such as server timestamps and array operations +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Cloud Firestore documentation: https://firebase.google.com/docs/firestore -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.CloudFirestore` +- Managed namespace: `Firebase.CloudFirestore` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup -- Cloud Firestore documentation: https://firebase.google.com/docs/firestore/ - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,90 +39,19 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.CloudFirestore ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using System.Collections.Generic; -using Firebase.CloudFirestore; -using Firebase.Core; - -App.Configure(); - -var firestore = Firestore.SharedInstance; -var document = firestore.GetCollection("notes").GetDocument("welcome"); - -document.SetData(new Dictionary -{ - { "title", "Hello from Firestore" }, - { "updatedAt", FieldValue.ServerTimestamp }, -}); - -document.GetDocument((snapshot, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(snapshot?.Exists); -}); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Auth` - commonly used when Firestore security rules depend on Firebase Authentication. - -## Firebase app configuration +## Binding Notes -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -If the official Firebase docs for this feature require additional setup, follow those docs first. +## Version Alignment -## Package versioning rules (important) +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.CloudFirestore`. API names closely mirror the native Cloud Firestore SDK surface and expose Apple-native concepts such as `NSDictionary`, `NSError`, listeners, and callback-based completion handlers. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/CloudFunctions.md b/docs/Firebase/NuGet/CloudFunctions.md index 397134679..ad22fe4af 100644 --- a/docs/Firebase/NuGet/CloudFunctions.md +++ b/docs/Firebase/NuGet/CloudFunctions.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.CloudFunctions -.NET bindings for Cloud Functions for Firebase on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Cloud Functions on Apple platforms. -## What this package provides +## Scope -This package binds the Cloud Functions for Firebase Apple SDK surface exposed in the `Firebase.CloudFunctions` namespace. It provides access to `CloudFunctions`, callable HTTPS functions, callable results, region and custom domain selection, and emulator configuration. +HTTPS callable function, callable options, emulator, result, and error APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- callable Cloud Functions access from C# -- callable function invocation with `HttpsCallable.Call` -- region-specific or custom-domain function clients -- local emulator configuration for callable functions +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Cloud Functions documentation: https://firebase.google.com/docs/functions -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.CloudFunctions` +- Managed namespace: `Firebase.CloudFunctions` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup -- Cloud Functions for Firebase documentation: https://firebase.google.com/docs/functions - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,84 +39,19 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.CloudFunctions ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using Firebase.CloudFunctions; -using Firebase.Core; - -App.Configure(); - -var functions = CloudFunctions.DefaultInstance; -var callable = functions.HttpsCallable("helloWorld"); -callable.TimeoutInterval = 30; - -callable.Call((result, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(result?.Data); -}); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Auth` - commonly used when callable functions depend on Firebase Authentication context. - -## Firebase app configuration +## Binding Notes -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -If the official Firebase docs for this feature require additional setup, follow those docs first. +## Version Alignment -## Package versioning rules (important) +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.CloudFunctions`. API names closely mirror the native Cloud Functions for Firebase SDK surface and expose Apple-native concepts such as `NSObject`, `NSError`, and callback-based completion handlers. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/CloudMessaging.md b/docs/Firebase/NuGet/CloudMessaging.md index 4f920b988..5b2c7fc3e 100644 --- a/docs/Firebase/NuGet/CloudMessaging.md +++ b/docs/Firebase/NuGet/CloudMessaging.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.CloudMessaging -.NET bindings for Firebase Cloud Messaging on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Cloud Messaging on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Cloud Messaging Apple SDK surface exposed in the `Firebase.CloudMessaging` namespace. It provides access to `Messaging`, FCM token APIs, APNs token assignment, topic subscription APIs, message delivery metadata, and notification service extension helper APIs. +FCM token, APNs token, topic subscription, message delivery, and notification service extension APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- FCM registration token retrieval from C# -- APNs token registration through `Messaging.SetApnsToken` -- topic subscribe and unsubscribe operations -- notification extension helper access for rich notification handling +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Firebase Cloud Messaging documentation: https://firebase.google.com/docs/cloud-messaging/ios/client -## Supported target frameworks +## Package -This package is intended for Apple platform TFMs such as: +- Package ID: `AdamE.Firebase.iOS.CloudMessaging` +- Managed namespace: `Firebase.CloudMessaging` + +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,86 +39,23 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.CloudMessaging ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. Follow the official Firebase and Apple documentation for APNs registration and notification permissions. - -```csharp -using System; -using Firebase.CloudMessaging; -using Firebase.Core; - -App.Configure(); - -var messaging = Messaging.SharedInstance; -messaging.AutoInitEnabled = true; - -messaging.FetchToken((fcmToken, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(fcmToken); -}); - -messaging.Subscribe("news"); -``` - -## Common companion packages +## Binding Notes -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Installations` - package metadata references Firebase Installations for underlying Firebase identity support. -- `AdamE.Firebase.iOS.Analytics` - commonly used when Cloud Messaging analytics and notification engagement reporting are needed. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -## Firebase app configuration +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Follow the official Firebase and Apple documentation for APNs registration and notification permissions; this package only exposes the managed binding surface. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +The managed entry point is `Firebase.CloudMessaging.Messaging.SharedInstance`. Set `Delegate` to an `IMessagingDelegate`, pass APNs tokens through `ApnsToken` or `SetApnsToken(...)`, and read or fetch FCM tokens through `FcmToken` or `FetchToken(...)`. If you disable swizzling with `FirebaseAppDelegateProxyEnabled`, route AppDelegate notification callbacks manually as described by the native docs. -If the official Firebase docs for this feature require additional setup, follow those docs first. +## Version Alignment -## Package versioning rules (important) +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.CloudMessaging`. API names closely mirror the native Firebase Cloud Messaging SDK surface and expose Apple-native concepts such as `NSData`, `NSDictionary`, `NSError`, and notification extension types. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/Core.md b/docs/Firebase/NuGet/Core.md index ca2aeb1c1..7e6efc743 100644 --- a/docs/Firebase/NuGet/Core.md +++ b/docs/Firebase/NuGet/Core.md @@ -1,52 +1,35 @@ # AdamE.Firebase.iOS.Core -.NET bindings for Firebase Core on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Core on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Core Apple SDK surface exposed in the `Firebase.Core` namespace. It provides Firebase app initialization, app lookup, options, logger configuration, data collection defaults, and timestamp types used by other Firebase packages. +Firebase app initialization, options, app lookup, logger configuration, data collection defaults, and shared Firebase types. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- Firebase app initialization with `App.Configure()` -- access to `App.DefaultInstance` or named Firebase app instances -- custom Firebase options with `Options` -- shared Firebase types such as `Timestamp` +## Native Documentation -This package is foundational and is commonly used alongside feature-specific Firebase packages. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Core documentation: https://firebase.google.com/docs/ios/setup -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.Core` +- Managed namespace: `Firebase.Core` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -56,79 +39,19 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.Core ``` -## Basic usage - -For the default Firebase app, include your app's `GoogleService-Info.plist` in the application project and call `App.Configure()`. - -```csharp -using System; -using Firebase.Core; - -App.Configure(); - -var app = App.DefaultInstance; -if (app is not null) -{ - app.DataCollectionDefaultEnabled = true; - Console.WriteLine(app.Name); -} - -Configuration.SharedInstance.SetLoggerLevel(LoggerLevel.Info); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Analytics` - analytics event reporting. -- `AdamE.Firebase.iOS.Auth` - authentication and user identity flows. -- `AdamE.Firebase.iOS.CloudFirestore` - Cloud Firestore document and collection access. -- `AdamE.Firebase.iOS.Storage` - Cloud Storage for Firebase file upload and download APIs. - -## Firebase app configuration - -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +## Binding Notes -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -If the official Firebase docs for this feature require additional setup, follow those docs first. +Call `Firebase.Core.App.Configure()` once from application startup after bundling the app-specific Firebase configuration. This is the .NET binding for native `FirebaseApp.configure()`. -## Package versioning rules (important) +## Version Alignment -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.Core`. API names closely mirror the native Firebase Core SDK surface and expose Apple-native concepts such as `NSDictionary`, `NSDate`, app options, and callback-based completion handlers. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/Crashlytics.md b/docs/Firebase/NuGet/Crashlytics.md index 7cef54d8d..463d7ca8f 100644 --- a/docs/Firebase/NuGet/Crashlytics.md +++ b/docs/Firebase/NuGet/Crashlytics.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.Crashlytics -.NET bindings for Firebase Crashlytics on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Crashlytics on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Crashlytics Apple SDK surface exposed in the `Firebase.Crashlytics` namespace. It provides access to `Crashlytics`, custom logs and keys, user IDs, non-fatal error recording, exception models, unsent report management, and crash state inspection. +Native crash reporting, custom key, log, user identifier, and non-fatal error APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- Crashlytics logging from C# with `Crashlytics.Log` -- user IDs and custom keys for crash reports -- non-fatal `NSError` or `ExceptionModel` recording -- unsent report checks, sending, or deletion +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Firebase Crashlytics documentation: https://firebase.google.com/docs/crashlytics/get-started -## Supported target frameworks +## Package + +- Package ID: `AdamE.Firebase.iOS.Crashlytics` +- Managed namespace: `Firebase.Crashlytics` -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,83 +39,45 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.Crashlytics ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using Firebase.Core; -using Firebase.Crashlytics; -using Foundation; - -App.Configure(); - -var crashlytics = Crashlytics.SharedInstance; -crashlytics.Log("Opened checkout"); -crashlytics.SetUserId("user-123"); -crashlytics.SetCustomValue(new NSString("checkout"), "screen"); - -var exception = new ExceptionModel("HandledError", "Checkout validation failed") -{ - StackTrace = new[] - { - StackFrame.Create("ValidateCart", "CheckoutService.cs", 42), - }, -}; -crashlytics.RecordExceptionModel(exception); -``` - -## Common companion packages +## Binding Notes -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Installations` - package metadata references Firebase Installations for underlying Firebase identity support. +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -## Firebase app configuration +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Crashlytics is native crash reporting, not a complete managed .NET exception-reporting replacement. On .NET 8 iOS projects, the top-level README includes an `_ExportSymbolsExplicitly` workaround for missing symbol/export issues. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +There is no separate `Crashlytics.Configure()` call in the current binding surface. After Firebase is configured, use `Firebase.Crashlytics.Crashlytics.SharedInstance` for Crashlytics APIs such as `Log(...)`, `SetCustomValue(...)`, `SetUserId(...)`, `RecordError(...)`, and `RecordExceptionModel(...)`. -If the official Firebase docs for this feature require additional setup, follow those docs first. +Runtime collection controls are exposed through `Crashlytics.SharedInstance.SetCrashlyticsCollectionEnabled(...)`, `IsCrashlyticsCollectionEnabled`, `CheckForUnsentReports(...)`, `SendUnsentReports()`, and `DeleteUnsentReports()`. Use the native Crashlytics docs as the source of truth for the matching plist keys and consent behavior. -## Package versioning rules (important) +The NuGet package also ships MSBuild targets for Firebase dSYM symbol upload. Release app builds enable symbol upload by default. Set these properties in the consuming app project file, or in `Directory.Build.targets` for a shared repo policy. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +To disable symbol upload: -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. +```xml + + false + +``` -Example: +To keep upload enabled but fail the build if upload fails: ```xml - - - - - + + false + ``` -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts +For one-off builds, pass the same values as command-line MSBuild properties, such as `dotnet build -c Release /p:FirebaseCrashlyticsUploadSymbolsEnabled=false`. Prefer the app `.csproj`, `Directory.Build.targets`, or command-line properties over `Directory.Build.props` so the value is applied after NuGet package props set their defaults. The upload target expects the app bundle to contain `GoogleService-Info.plist`. -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. +## Version Alignment -## API surface notes +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -The public namespace is `Firebase.Crashlytics`. API names closely mirror the native Firebase Crashlytics SDK surface and expose Apple-native concepts such as `NSError`, `NSDictionary`, `NSDate`, and native exception model types. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/Database.md b/docs/Firebase/NuGet/Database.md index 195a85f16..7db00febc 100644 --- a/docs/Firebase/NuGet/Database.md +++ b/docs/Firebase/NuGet/Database.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.Database -.NET bindings for Firebase Realtime Database on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Realtime Database on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Realtime Database Apple SDK surface exposed in the `Firebase.Database` namespace. It provides access to `Database`, database references, queries, snapshots, server values, transactions, offline persistence controls, and emulator configuration. +Realtime Database app, reference, query, snapshot, server value, transaction, persistence, and emulator APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- Realtime Database references and child paths from C# -- value listeners and single-event reads -- writes, updates, deletes, and transactions -- offline persistence and cache controls +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Firebase Realtime Database documentation: https://firebase.google.com/docs/database/ios/start -## Supported target frameworks +## Package -This package is intended for Apple platform TFMs such as: +- Package ID: `AdamE.Firebase.iOS.Database` +- Managed namespace: `Firebase.Database` + +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,81 +39,19 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.Database ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using Firebase.Core; -using Firebase.Database; -using Foundation; - -App.Configure(); - -var database = Database.DefaultInstance; -database.PersistenceEnabled = true; - -var statusRef = database.GetRootReference().GetChild("status"); -statusRef.SetValue(new NSString("online")); - -statusRef.ObserveSingleEvent(DataEventType.Value, snapshot => -{ - Console.WriteLine(snapshot.Exists); -}); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Auth` - commonly used when Realtime Database security rules depend on Firebase Authentication. +## Binding Notes -## Firebase app configuration +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +## Version Alignment -If the official Firebase docs for this feature require additional setup, follow those docs first. +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -## Package versioning rules (important) +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. - -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.Database`. API names closely mirror the native Firebase Realtime Database SDK surface and expose Apple-native concepts such as `NSObject`, `NSDictionary`, `NSError`, and callback-based listeners. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/InAppMessaging.md b/docs/Firebase/NuGet/InAppMessaging.md index c2015d7d5..4e2f480d8 100644 --- a/docs/Firebase/NuGet/InAppMessaging.md +++ b/docs/Firebase/NuGet/InAppMessaging.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.InAppMessaging -.NET bindings for Firebase In-App Messaging on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase In-App Messaging on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase In-App Messaging Apple SDK surface exposed in the `Firebase.InAppMessaging` namespace. It provides access to message display suppression, automatic data collection, custom trigger events, display delegates, display component protocols, and message model types for banner, card, modal, and image-only messages. +In-App Messaging display, trigger, and message APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- Firebase In-App Messaging campaign display control from C# -- custom trigger events with `InAppMessaging.TriggerEvent` -- display delegate callbacks for impressions, clicks, dismissals, and display errors -- custom message display component integration +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Firebase In-App Messaging documentation: https://firebase.google.com/docs/in-app-messaging -## Supported target frameworks +## Package + +- Package ID: `AdamE.Firebase.iOS.InAppMessaging` +- Managed namespace: `Firebase.InAppMessaging` -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,76 +39,21 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.InAppMessaging ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using Firebase.Core; - -App.Configure(); - -var inAppMessaging = Firebase.InAppMessaging.InAppMessaging.SharedInstance; -inAppMessaging.AutomaticDataCollectionEnabled = true; -inAppMessaging.MessageDisplaySuppressed = false; -inAppMessaging.TriggerEvent("purchase_complete"); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Installations` - package metadata references Firebase Installations for underlying Firebase identity support. -- `AdamE.Firebase.iOS.ABTesting` - package metadata references Firebase A/B Testing for campaign experiment handling. -- `AdamE.Firebase.iOS.Analytics` - commonly used with In-App Messaging campaign triggers and measurement. - -This package is part of the Firebase `12.8.0` package line in this repository. Firebase's aggregate `Firebase/InAppMessaging` `12.8.0` CocoaPods subspec resolves the native `FirebaseInAppMessaging` `12.8.0-beta` pod. +## Binding Notes -## Firebase app configuration +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +The native Firebase In-App Messaging Apple SDK can use beta native framework versions behind the aggregate Firebase package line. Keep the NuGet package line aligned with the rest of Firebase in the application. -If the official Firebase docs for this feature require additional setup, follow those docs first. +## Version Alignment -## Package versioning rules (important) +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.InAppMessaging`. API names closely mirror the native Firebase In-App Messaging SDK surface and expose Apple-native concepts such as `NSObject`, `NSDictionary`, `NSError`, `NSUrl`, and UIKit display model types. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/Installations.md b/docs/Firebase/NuGet/Installations.md index 698bd29a4..217e9cf29 100644 --- a/docs/Firebase/NuGet/Installations.md +++ b/docs/Firebase/NuGet/Installations.md @@ -1,52 +1,35 @@ # AdamE.Firebase.iOS.Installations -.NET bindings for Firebase Installations on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Installations on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Installations Apple SDK surface exposed in the `Firebase.Installations` namespace. It provides access to Firebase installation IDs, installation auth tokens, installation deletion, per-app installations, and the installation ID change notification metadata exposed by the native SDK. +Firebase installation identifier, auth token, deletion, and notification APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- Firebase installation ID retrieval from C# -- Firebase Installations auth token retrieval -- installation deletion for reset or privacy workflows -- direct access to the Firebase Installations native API surface +## Native Documentation -Many Firebase feature packages reference Installations as an underlying dependency. Most app code only uses this package directly for installation identity or token workflows. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Installations documentation: https://firebase.google.com/docs/projects/manage-installations -## Official Firebase documentation comes first +## Package -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. +- Package ID: `AdamE.Firebase.iOS.Installations` +- Managed namespace: `Firebase.Installations` -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup - -## Supported target frameworks - -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -56,87 +39,21 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.Installations ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using Firebase.Core; -using Firebase.Installations; - -App.Configure(); - -var installations = Installations.DefaultInstance; - -installations.GetInstallationId((identifier, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(identifier); -}); - -installations.GetAuthToken(false, (tokenResult, error) => -{ - Console.WriteLine(tokenResult?.ExpirationDate); -}); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Analytics`, `AdamE.Firebase.iOS.CloudMessaging`, `AdamE.Firebase.iOS.Crashlytics`, and other feature packages - package metadata commonly references Firebase Installations for underlying Firebase identity support. +## Binding Notes -## Firebase app configuration +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Some package combinations work more reliably when `AdamE.Firebase.iOS.Installations` is referenced explicitly, even if it appears to be transitive. -If the official Firebase docs for this feature require additional setup, follow those docs first. +## Version Alignment -## Package versioning rules (important) +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.Installations`. API names closely mirror the native Firebase Installations SDK surface and expose Apple-native concepts such as `NSError`, `NSDate`, and notification metadata. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/PerformanceMonitoring.md b/docs/Firebase/NuGet/PerformanceMonitoring.md index 48bf8c1b3..4faf07dcb 100644 --- a/docs/Firebase/NuGet/PerformanceMonitoring.md +++ b/docs/Firebase/NuGet/PerformanceMonitoring.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.PerformanceMonitoring -.NET bindings for Firebase Performance Monitoring on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Performance Monitoring on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Performance Monitoring Apple SDK surface exposed in the `Firebase.PerformanceMonitoring` namespace. It provides access to `Performance`, custom traces, trace metrics, HTTP metrics, attributes, data collection controls, and instrumentation controls. +Performance collection, trace, metric, HTTP metric, attribute, and instrumentation APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- custom performance traces from C# -- trace metrics with `Trace.IncrementMetric` or `Trace.SetIntValue` -- HTTP request metrics with `HttpMetric` -- Performance Monitoring data collection and instrumentation toggles +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Firebase Performance Monitoring documentation: https://firebase.google.com/docs/perf-mon/get-started-ios -## Supported target frameworks +## Package + +- Package ID: `AdamE.Firebase.iOS.PerformanceMonitoring` +- Managed namespace: `Firebase.PerformanceMonitoring` -This package is intended for Apple platform TFMs such as: +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,83 +39,21 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.PerformanceMonitoring ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using Firebase.Core; -using Firebase.PerformanceMonitoring; -using Foundation; - -App.Configure(); - -var trace = Performance.StartTrace("load_home"); -if (trace is not null) -{ - trace.IncrementMetric("items_loaded", 1); - trace.Stop(); -} - -var metric = new HttpMetric(new NSUrl("https://example.com/api"), HttpMethod.Get); -metric.Start(); -metric.ResponseCode = 200; -metric.ResponseContentType = "application/json"; -metric.Stop(); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Installations` - package metadata references Firebase Installations for underlying Firebase identity support. -- `AdamE.Firebase.iOS.ABTesting` and `AdamE.Firebase.iOS.RemoteConfig` - package metadata references these Firebase packages as companion dependencies. +## Binding Notes -## Firebase app configuration +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +Runtime collection controls are on `Firebase.PerformanceMonitoring.Performance.SharedInstance`. Set `InstrumentationEnabled` and `DataCollectionEnabled` before `Firebase.Core.App.Configure()` when you need startup-time behavior to change. -If the official Firebase docs for this feature require additional setup, follow those docs first. +## Version Alignment -## Package versioning rules (important) +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.PerformanceMonitoring`. API names closely mirror the native Firebase Performance Monitoring SDK surface and expose Apple-native concepts such as `NSUrl`, attributes, and explicit start/stop tracing. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/RemoteConfig.md b/docs/Firebase/NuGet/RemoteConfig.md index 2afc70521..5b7489f86 100644 --- a/docs/Firebase/NuGet/RemoteConfig.md +++ b/docs/Firebase/NuGet/RemoteConfig.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.RemoteConfig -.NET bindings for Firebase Remote Config on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Firebase Remote Config on Apple platforms. -## What this package provides +## Scope -This package binds the Firebase Remote Config Apple SDK surface exposed in the `Firebase.RemoteConfig` namespace. It provides access to `RemoteConfig`, fetch and activation APIs, default values, config values, config settings, real-time update listeners, and custom signals. +Remote Config defaults, fetch, activation, realtime update, custom signal, settings, and value APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- Remote Config fetch and activation from C# -- default config values and typed config value access -- real-time config update listeners -- Remote Config settings such as fetch timeout and minimum fetch interval +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Firebase Remote Config documentation: https://firebase.google.com/docs/remote-config/use-config-ios -## Supported target frameworks +## Package -This package is intended for Apple platform TFMs such as: +- Package ID: `AdamE.Firebase.iOS.RemoteConfig` +- Managed namespace: `Firebase.RemoteConfig` + +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,88 +39,19 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.RemoteConfig ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using System.Collections.Generic; -using Firebase.Core; -using Firebase.RemoteConfig; - -App.Configure(); - -var remoteConfig = RemoteConfig.SharedInstance; -remoteConfig.SetDefaults(new Dictionary -{ - { "welcome_message", "Hello" }, -}); - -remoteConfig.FetchAndActivate((status, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - Console.WriteLine(remoteConfig.GetConfigValue("welcome_message").NSStringValue); -}); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Installations` - package metadata references Firebase Installations for underlying Firebase identity support. -- `AdamE.Firebase.iOS.ABTesting` - package metadata references Firebase A/B Testing for experiment payload handling. +## Binding Notes -## Firebase app configuration +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +## Version Alignment -If the official Firebase docs for this feature require additional setup, follow those docs first. +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -## Package versioning rules (important) +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. - -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.RemoteConfig`. API names closely mirror the native Firebase Remote Config SDK surface and expose Apple-native concepts such as `NSDictionary`, `NSError`, `NSDate`, `NSSet`, and callback-based completion handlers. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/NuGet/Storage.md b/docs/Firebase/NuGet/Storage.md index adeb756ea..d2f084eb6 100644 --- a/docs/Firebase/NuGet/Storage.md +++ b/docs/Firebase/NuGet/Storage.md @@ -1,53 +1,35 @@ # AdamE.Firebase.iOS.Storage -.NET bindings for Cloud Storage for Firebase on Apple platforms, for use from .NET iOS and Mac Catalyst apps. +.NET bindings for Cloud Storage for Firebase on Apple platforms. -## What this package provides +## Scope -This package binds the Cloud Storage for Firebase Apple SDK surface exposed in the `Firebase.Storage` namespace. It provides access to `Storage`, storage references, uploads, downloads, metadata, listing, task observation, retry controls, and emulator configuration. +Storage bucket, reference, upload, download, metadata, listing, retry, observable task, and emulator APIs exposed by the Firebase Apple SDK. -Use this package when you need: +These packages are thin bindings over the native Firebase Apple SDK. The native documentation is the source of truth for product behavior, Firebase console setup, quotas, policy requirements, and feature workflows. -- file or data uploads to Cloud Storage for Firebase -- file downloads, download URLs, metadata reads, and metadata updates -- storage reference navigation by path or URL -- upload and download task observation and management +## Native Documentation -Most apps using this package also reference `AdamE.Firebase.iOS.Core` for Firebase app initialization. - -## Official Firebase documentation comes first - -These packages are **thin .NET bindings over the official Firebase Apple SDKs**. - -Use the official Firebase documentation as the starting point for: - -- Firebase configuration and platform setup -- feature usage and behavioral guidance -- troubleshooting and best practices - -These bindings primarily: - -- expose the native Firebase Apple SDK APIs to .NET through C# -- deliver the packaged native Firebase SDK artifacts through NuGet - -- Firebase documentation: https://firebase.google.com/docs -- Firebase Apple platform setup: https://firebase.google.com/docs/ios/setup +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup - Cloud Storage for Firebase documentation: https://firebase.google.com/docs/storage/ios/start -## Supported target frameworks +## Package -This package is intended for Apple platform TFMs such as: +- Package ID: `AdamE.Firebase.iOS.Storage` +- Managed namespace: `Firebase.Storage` + +Supported target frameworks include: - `net9.0-ios` - `net10.0-ios` - `net9.0-maccatalyst` - `net10.0-maccatalyst` -When multi-targeting, condition the package reference so it only restores for Apple targets. +When multi-targeting, condition package references so they restore only for Apple targets: ```xml - + ``` @@ -57,89 +39,19 @@ When multi-targeting, condition the package reference so it only restores for Ap dotnet add package AdamE.Firebase.iOS.Storage ``` -## Basic usage - -This package does not itself perform Firebase app initialization; call `Firebase.Core.App.Configure()` from the app before using Firebase feature APIs. - -```csharp -using System; -using Firebase.Core; -using Firebase.Storage; -using Foundation; - -App.Configure(); - -var uploadData = NSData.FromString("hello", NSStringEncoding.UTF8); -var storage = Storage.DefaultInstance; -var reference = storage.GetReferenceFromPath("uploads/profile.jpg"); -var metadata = new StorageMetadata { ContentType = "image/jpeg" }; - -reference.PutData(uploadData, metadata, (uploadedMetadata, error) => -{ - if (error is not null) - { - Console.WriteLine(error.LocalizedDescription); - return; - } - - reference.GetDownloadUrl((url, downloadError) => - { - Console.WriteLine(url); - }); -}); -``` - -## Common companion packages - -- `AdamE.Firebase.iOS.Core` - Firebase app initialization. -- `AdamE.Firebase.iOS.Auth` - commonly used when Cloud Storage security rules depend on Firebase Authentication. +## Binding Notes -## Firebase app configuration +Use the official Firebase Apple docs for setup and usage. In .NET, call the equivalent APIs from the managed namespace listed above. Keep app-specific Firebase configuration, such as `GoogleService-Info.plist`, in the application project. -Firebase apps commonly require app-specific configuration from your own Firebase project, such as `GoogleService-Info.plist`. +Most Firebase feature packages require `AdamE.Firebase.iOS.Core` and app startup should call `Firebase.Core.App.Configure()` before feature APIs are used. This is the .NET binding for native `FirebaseApp.configure()`. -Keep app-specific Firebase configuration in application projects, not in reusable library projects. +## Version Alignment -If the official Firebase docs for this feature require additional setup, follow those docs first. +Firebase Apple SDKs are packaged as native xcframeworks. Applications should pin package versions intentionally and keep all `AdamE.Firebase.iOS.*` packages on the same major/minor Firebase line. -## Package versioning rules (important) +Avoid mixing unrelated Firebase binding package sets or mismatched Firebase native SDK lines in one application. That can cause duplicate symbols, linker failures, runtime loading failures, or undefined native SDK behavior. -Because Firebase Apple SDKs are packaged as native xcframeworks and distributed here through NuGet, consumers should explicitly pin package versions. - -Due to packaging differences between CocoaPods and NuGet, it is highly recommended that applications follow these rules: - -1. Keep the MAJOR.MINOR version aligned across all Firebase packages in the app, for example `12.6.*.*`. -2. Then use the latest available PATCH.REVISION for each individual package. - -Example: - -```xml - - - - - -``` - -Avoid mixing mismatched Firebase package lines such as `12.6.x.x` with `12.5.x.x`, or `12.x.x.x` with `11.x.x.x`. Doing so can lead to native dependency conflicts, duplicate symbols, runtime failures, or other undefined behavior. - -## Notes on native dependency conflicts - -Google and Firebase Apple SDKs share native dependencies. Avoid mixing multiple unrelated binding packages that embed overlapping Google/Firebase native SDK binaries in the same app unless you are certain they are compatible. - -## API surface notes - -The public namespace is `Firebase.Storage`. API names closely mirror the native Cloud Storage for Firebase SDK surface and expose Apple-native concepts such as `NSData`, `NSUrl`, `NSError`, metadata objects, and observable tasks. - -## Repository / support +## Repository - Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents - Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues - -## Support the project - -Keeping Firebase Apple bindings current for .NET requires ongoing work across SDK updates, native dependency changes, and API surface maintenance. - -If this package is valuable in your app or organization, sponsorship helps support continued maintenance and updates. - -- GitHub Sponsors: https://github.com/sponsors/AdamEssenmacher diff --git a/docs/Firebase/PerformanceMonitoring/Details.md b/docs/Firebase/PerformanceMonitoring/Details.md index b9c822a31..357959b71 100755 --- a/docs/Firebase/PerformanceMonitoring/Details.md +++ b/docs/Firebase/PerformanceMonitoring/Details.md @@ -1,47 +1,18 @@ -Firebase Performance Monitoring is a service that helps you to gain insight into the performance characteristics of your iOS and Android apps. You use the Performance Monitoring SDK to collect performance data from your app, and then review and analyze that data in the Firebase console. Performance Monitoring helps you to understand where and when the performance of your app can be improved so that you can use that information to fix performance issues. +# Firebase Performance Monitoring -Performance Monitoring is currently in [beta release](https://support.google.com/firebase/answer/7011258). +This path is retained for existing links, but this repository does not maintain a copied Firebase Performance Monitoring walkthrough. -## Key capabilities +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -| Capability | Description | -|-----------:|-------------| -| **Automatically measure app startup time, HTTP/S network requests, and more** | When you integrate the Performance Monitoring SDK into your iOS or Android app, you don't need to write any code before your app starts monitoring several critical aspects of app performance: startup time, activity while in the foreground, activity while in the background, and HTTP/S network requests. | -| **Gain insight into situations where app performance could be improved** | Optimizing the performance of your app can be challenging when you don't know exactly why it is falling short of user expectations. That's why Performance Monitoring lets you see performance metrics broken down by country, device, app version, and OS level. | -| **Customize Performance Monitoring for your app** | You can create traces to capture your app's performance in specific situations, like when you load a new screen. And, you can create counters to count events that you define (like cache hits) during those traces. | +## Native Documentation -## How does it work? +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Performance Monitoring documentation: https://firebase.google.com/docs/perf-mon/get-started-ios -Performance Monitoring monitors traces and HTTP/S network requests in your app. +## Binding Package -A trace is a report of performance data captured between two points in time in your app. When installed, the Performance Monitoring SDK automatically provides app start traces, which measure the time between when the user opens the app and when the app is responsive. It also provides app in foreground traces and app in background traces to give you insight into how your app performs when in the foreground or when idle. To learn more about these types of traces, see [Firebase Performance Monitoring Automatic Traces](https://firebase.google.com/docs/perf-mon/automatic). +- Package README: [../NuGet/PerformanceMonitoring.md](../NuGet/PerformanceMonitoring.md) +- Package ID: `AdamE.Firebase.iOS.PerformanceMonitoring` +- Managed namespace: `Firebase.PerformanceMonitoring` -You can also configure custom traces. A custom trace is a report of performance data associated with some of the code in your app. You define the beginning and end of a custom trace using the APIs provided by the Performance Monitoring SDK. A custom trace can be further configured to record counters for performance-related events that occur within its scope. For example, you could create a counter for the number of cache hits and misses or the number of times that the UI becomes unresponsive for a noticeable period of time. - -An HTTP/S network request is a report that captures the time between when your app issues a request to a service endpoint and when the response from that endpoint is complete. For any endpoint that your app makes a request to, the SDK will capture several metrics: - -* **Response time**: Time between when the request is made and when the response is fully received -* **Payload size**: Byte size of the network payload downloaded and uploaded by the app -* **Success rate**: Percentage of successful responses compared to total responses (to measure network or server failures) - -For both traces and HTTP/S network requests, you can see performance monitoring data categorized as follows: - -| Traces | HTTP/S network requests | -|--------|-------------------------| -| App version | App version | -| Country | Country | -| Device | Device | -| OS | OS | -| Radio | Radio | -| Carrier | Carrier | -| | MIME type | - -## User data - -Performance Monitoring does not permanently store any personally identifiable information (such as names, email addresses, or phone numbers). While monitoring HTTP/S network requests, Performance Monitoring uses URLs (not including URL parameters) to build aggregated and anonymous URL patterns that are eventually persisted and shown in the Firebase console. - -For a full list of data collected by Performance Monitoring, see [Data collection](https://support.google.com/firebase/answer/6383877?hl=en&ref_topic=6317497). - - - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/perf-mon/) to see original Firebase documentation._ \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/PerformanceMonitoring/GettingStarted.md b/docs/Firebase/PerformanceMonitoring/GettingStarted.md index 2e8048838..357959b71 100755 --- a/docs/Firebase/PerformanceMonitoring/GettingStarted.md +++ b/docs/Firebase/PerformanceMonitoring/GettingStarted.md @@ -1,323 +1,18 @@ -# Firebase Performance Monitoring for iOS +# Firebase Performance Monitoring -## Table of content +This path is retained for existing links, but this repository does not maintain a copied Firebase Performance Monitoring walkthrough. -- [Prerequisites](#prerequisites) -- [Add Firebase to your app](#add-firebase-to-your-app) -- [Configure Performance Monitoring in your app](#configure-performance-monitoring-in-your-app) - - [(Optional) Define a custom trace and one or more counters in your app](#optional-define-a-custom-trace-and-one-or-more-counters-in-your-app) - - [Check the Firebase console for Performance Monitoring results](#check-the-firebase-console-for-performance-monitoring-results) - - [Deploy your app and review results in the Firebase console](#deploy-your-app-and-review-results-in-the-firebase-console) -- [Automatic Traces](#automatic-traces) - - [Automatic trace definitions](#automatic-trace-definitions) -- [Disable the Firebase Performance Monitoring SDK](#disable-the-firebase-performance-monitoring-sdk) - - [Disable Performance Monitoring during your app build process](#disable-performance-monitoring-during-your-app-build-process) - - [Disable your app at runtime using Remote Config](#disable-your-app-at-runtime-using-remote-config) -- [Known issues](#known-issues) +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -## Prerequisites +## Native Documentation -Firebase Performance Monitoring requires iOS 8 or newer. +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Performance Monitoring documentation: https://firebase.google.com/docs/perf-mon/get-started-ios -## Add Firebase to your app +## Binding Package -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. +- Package README: [../NuGet/PerformanceMonitoring.md](../NuGet/PerformanceMonitoring.md) +- Package ID: `AdamE.Firebase.iOS.PerformanceMonitoring` +- Managed namespace: `Firebase.PerformanceMonitoring` -## Configure Performance Monitoring in your app - -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -App.Configure (); -``` - -Compile your app. Automatic traces and HTTP/S network requests are now monitored. - -### (Optional) Define a custom trace and one or more counters in your app - -A custom trace is a report of performance data associated with some of the code in your app. You can have multiple custom traces in your app, and it is possible to have more than one custom trace running at a time. Each custom trace can have one or more counters to count performance-related events in your app, and those counters are associated with the traces that create them. - -1. Add the Firebase Performance Monitoring namespace to your namespaces: - - ```csharp - using Firebase.PerformanceMonitoring; - ``` - -2. Just before the code where you want to start a trace in your app, add the following lines of code to start a trace called **test trace**: - - ```csharp - var trace = Performance.StartTrace ("test trace"); - ``` - -3. To count performance-related events that occur in your app (such as cache hits or retries), add a line of code similar to the following each time that the event occurs, using a string other than retry to name that event if you are counting a different type of event: - - ```csharp - trace.IncrementCounter ("retry"); - ``` - -4. Just after the code where you want to stop your trace, add the following line of code: - - ```csharp - trace.Stop (); - ``` - -### Check the Firebase console for Performance Monitoring results - -1. Run your app in the simulator or device. -2. Confirm that Performance Monitoring results appear in the Firebase console. Results should appear within 12 hours. - -### Deploy your app and review results in the Firebase console - -After you have validated Performance Monitoring using simulators and one or more test devices, you can deploy the updated version of your app to your users and use the Firebase console to monitor performance data. - -### (Optional) Add monitoring for specific network requests - -Performance Monitoring collects network requests automatically. Although this includes most network requests for your app, some might not be reported. To include specific network requests in Performance Monitoring, add the following code to your app: - -```csharp -var metric = new HttpMetric ("https://www.google.com", HttpMethod.Get); -metric.Start (); - -var url = new NSUrl ("https://www.google.com"); -var request = new NSUrlRequest (url); -var session = NSUrlSession.FromConfiguration (NSUrlSessionConfiguration.DefaultSessionConfiguration); - -var dataTask = session.CreateDataTask (request, HandleNSUrlSessionResponse); -dataTask.Resume (); - -void HandleNSUrlSessionResponse (NSData data, NSUrlResponse response, NSError error) -{ - if (response is NSHttpUrlResponse httpResponse) - metric.ResponseCode = httpResponse.StatusCode; - - metric.Stop (); -} - -// async/await way: - -var metric = new HttpMetric ("https://www.google.com", HttpMethod.Get); -metric.Start (); - -var url = new NSUrl ("https://www.google.com"); -var request = new NSUrlRequest (url); -var session = NSUrlSession.FromConfiguration (NSUrlSessionConfiguration.DefaultSessionConfiguration); - -try { - var dataTaskResult = await session.CreateDataTaskAsync (request); - - if (dataTaskResult.Response is NSHttpUrlResponse httpResponse) - metric.ResponseCode = httpResponse.StatusCode; - - metric.Stop (); -} catch (NSErrorException ex) { - // Handle error -} -``` - -The HTTP/s network requests you specifically capture this way appear in the Firebase console along with the network requests Performance Monitoring captures automatically. - -## Automatic Traces - -A trace is a report of performance data captured between two points in time in your app. When installed, the Performance Monitoring SDK automatically provides the following types of traces: - -* _App start_ traces, which measure the time between when the user opens the app and when the app is responsive. -* _App in background_ traces, which measure the time when the app is running in the background. -* _App in foreground_ traces, which measure the time when the app is running in the foreground and available to the user. - -### Automatic trace definitions - -Performance Monitoring uses method calls and notifications in your app to determine when each type of automatic trace starts and stops: - -| Trace Name | iOS | -|------------|-----| -| App start | Starts when the application loads the first **Object** to memory and stops after the first successful run loop that occurs after the application receives the **UIApplicationDidBecomeActiveNotification** notification. | -| App in background | Starts when the application receives the **UIApplicationWillResignActiveNotification** notification and stops when it receives the **UIApplicationDidBecomeActiveNotification** notification. | -| App in foreground | Starts when the application receives the **UIApplicationDidBecomeActiveNotification** notification and stops when it receives the **UIApplicationWillResignActiveNotification** notification. | - -## Monitor Custom Attributes - -In Firebase Performance Monitoring, you can use attributes to segment performance data and focus on your app's performance in different real-world scenarios. A variety of attributes are available out-of-the-box, including operating system information, country, carrier, device, and app version. In addition, you can also create custom attributes, to segment data by categories specific to your app. For example, in a game, you can segment data by game level. - -### Create custom attributes - -You can use custom attributes on specific traces. You're limited to 5 custom attributes per trace. - -To use custom attributes, add code to your app defining the attribute and applying it to a specific trace, like the following examples: - -```csharp -var trace = Firebase.PerformanceMonitoring.Performance.SharedInstance.GetTrace ("myTrace"); -trace.SetValue ("A", "experiment"); - -// Update scenario. -trace.SetValue ("B", "experiment"); - -// Reading scenario. -var experimentValue = trace.GetValue ("experiment"); - -// Delete scenario. -trace.RemoveAttribute ("experiment"); - -// Read attributes. -var attributes = trace.Attributes; -``` - -### Monitor custom attributes - -In the Firebase console, go to the *Traces* tab in the [Performance section][6]. Each of your custom attributes has a card showing performance data for that segment. You can also filter by custom attributes. - -## Disable the Firebase Performance Monitoring SDK - -To let you users opt-in or opt-out of using Firebase Performance Monitoring, you might want to configure your app so that you can enable and disable Performance Monitoring. You might also find this capability to be useful during app development and testing. - -You can disable the Performance Monitoring SDK when building your app with the option to re-enable it at runtime, or build your app with Performance Monitoring enabled and then have the option to disable it at runtime using [Firebase Remote Config][3]. You can also completely deactivate Performance Monitoring, with no option to enable it at runtime. - -### Disable Performance Monitoring during your app build process - -One situation where disabling Performance Monitoring during your app build process could be useful is to avoid reporting performance data from a pre-release version of your app during app development and testing. - -You can add one of two keys to the property list file (**Info.plist**) for your iOS app to disable or deactivate Performance Monitoring: - -* To disable Performance Monitoring, but allow your app to enable it at runtime, set `firebase_performance_collection_enabled` to `True` in your app's **Info.plist** file. -* To completely deactivate Performance Monitoring with no option to enable it at runtime, set `firebase_performance_collection_deactivated` to true in your app's **Info.plist** file. This setting overrides the `firebase_performance_collection_enabled` setting and must be removed from your app's **Info.plist** file to re-enable Performance Monitoring. - -### Disable your app at runtime using Remote Config - -Remote Config lets you make changes to the behavior and appearance of your app, so it provides an ideal way to let you disable Performance Monitoring in deployed instances of your app. - -You can use the example code shown below to disable Performance Monitoring data collection the next time that your iOS app starts. For more information about using Remote Config in an iOS app, see [Use Firebase Remote Config on iOS][4]. - -1. In your project solution in Visual Studio, add **Xamarin.Firebase.iOS.RemoteConfig** NuGet package. -2. In your `AppDelegate` file, add the `Firebase.RemoteConfig` namespace. - - ```csharp - using Firebase.RemoteConfig; - ``` - -3. In your `AppDelegate` file, add the following code in `FinishedLaunching` method: - - ```csharp - var remoteConfig = RemoteConfig.SharedInstance; - - // You can change the "false" below to "true" to permit more fetches when validating - // your app, but you should change it back to "false" or remove this statement before - // distributing your app in production. - remoteConfig.ConfigSettings = new RemoteConfigSettings (true); - - // You can set default parameter values using an NSDictionary object or a plist file. - var defaultPlist = NSBundle.MainBundle.PathForResource ("RemoteConfigDefaults", "plist"); - - if (defaultPlist != null) { - // Load in-app defaults from a plist file that sets perf_enable - // to true until you update values in the Firebase Console. - remoteConfig.SetDefaults ("RemoteConfigDefaults"); - } else { - // If the plist file doesn't exist, load the value by code. - object [] values = { true }; - object [] keys = { "perf_enable" }; - var defaultValues = NSDictionary.FromObjectsAndKeys (values, keys, keys.Length); - remoteConfig.SetDefaults (defaultValues); - } - - // Important! This needs to be applied before App.Configure() - var isPerformanceMonitoringInstrumentation = remoteConfig ["perf_enable"].BoolValue; - - // The following line enables/disables automatic traces and HTTP/S network monitoring - Performance.SharedInstance.InstrumentationEnabled = isPerformanceMonitoringInstrumentation; - - // The following line enables/disables custom traces - Performance.SharedInstance.DataCollectionEnabled = isPerformanceMonitoringInstrumentation; - - // Use Firebase library to configure APIs - App.Configure (); - ``` - -4. In your `ViewController` add the following code to fetch and activate Remote Config values: - - ```csharp - remoteConfig.Fetch (30, (status, error) => { - switch (status) { - case RemoteConfigFetchStatus.Success: - Console.WriteLine ("Config fetched"); - remoteConfig.ActivateFetched (); - break; - - case RemoteConfigFetchStatus.Throttled: - case RemoteConfigFetchStatus.NoFetchYet: - case RemoteConfigFetchStatus.Failure: - Console.WriteLine ("Config not fetched..."); - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - break; - } - }); - ``` - -5. To disable Performance Monitoring in the Firebase console, create a `perf_enable` parameter in your app's project, and then set its value to `false`. If you set the value of `perf_enable` to `true`, Performance Monitoring will remain enabled. - -#### Disable automatic or manual data collection separately - -You could make some changes to the code shown above and in the Firebase console to let you disable automatic data collection (app start traces and HTTP/S network requests) separately from manual data collection (custom traces). To do this, you would add the following code to the `FinishedLaunching` method, instead of what is shown in step 3 above: - -```csharp -var remoteConfig = RemoteConfig.SharedInstance; -remoteConfig.ConfigSettings = new RemoteConfigSettings (true); - -var defaultPlist = NSBundle.MainBundle.PathForResource ("RemoteConfigDefaults", "plist"); - -if (defaultPlist != null) { - remoteConfig.SetDefaults ("RemoteConfigDefaults"); -} else { - // If the plist file doesn't exist, load the values by code. - object [] values = { true, true }; - object [] keys = { "perf_enable_auto", "perf_enable_manual" }; - var defaultValues = NSDictionary.FromObjectsAndKeys (values, keys, keys.Length); - remoteConfig.SetDefaults (defaultValues); -} - -// Important! This needs to be applied before App.Configure() -var isPerformanceMonitoringInstrumentationEnabled = remoteConfig ["perf_enable_auto"].BoolValue; -var isPerformanceMonitoringDataCollectionEnabled = remoteConfig ["perf_enable_manual"].BoolValue; - -// The following line enables/disables automatic traces and HTTP/S network monitoring -Performance.SharedInstance.InstrumentationEnabled = isPerformanceMonitoringInstrumentationEnabled; - -// The following line enables/disables custom traces -Performance.SharedInstance.DataCollectionEnabled = isPerformanceMonitoringDataCollectionEnabled; - -// Use Firebase library to configure APIs -App.Configure (); -``` - -Then, do the following in the Firebase console: - -* To disable automatic traces and HTTP/S network monitoring, create a `perf_enable_auto` parameter in your app's project, and then set its value to `false`. -* To disable custom traces, create a `perf_enable_manual` parameter in your app's project, and then set its value to `false`. - -To enable either of these aspects of Performance Monitoring in your app, set the value of the corresponding parameter to `true` in the Firebase console. - -## View Data in the Firebase Console - -To learn how to read your data in Firebase Console, please, read the following [documentation][7]. - -## Known issues - -* Performance Monitoring has known compatibility issues with GTMSQLite. We recommend not using Performance Monitoring with apps that use GTMSQLite. -* Performance Monitoring does not support network requests made using either `WebClient` or `HttpClient` class. -* Method swizzling after calling `App.Configure ()` might interfere with the Performance Monitoring SDK. -* Known issues with the iOS 8.0-8.2 Simulator prevent Performance Monitoring from capturing performance events. These issues are fixed in the iOS 8.3 Simulator and later versions. -* Connections established using `NSUrlSession`'s BackgroundSessionConfiguration will exhibit longer than expected connection times. These connections are executed out-of-process and the timings reflect in-process callback events. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/dynamic-links/ios) to see original Firebase documentation._ - -[1]: https://firebase.google.com/console/ -[2]: http://support.google.com/firebase/answer/7015592 -[3]: https://components.xamarin.com/view/firebaseiosremoteconfig -[4]: https://components.xamarin.com/gettingstarted/firebaseiosremoteconfig -[6]: https://console.firebase.google.com/project/_/performance/ -[7]: https://firebase.google.com/docs/perf-mon/help +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/RemoteConfig/Details.md b/docs/Firebase/RemoteConfig/Details.md index 65f9411ce..d72406526 100755 --- a/docs/Firebase/RemoteConfig/Details.md +++ b/docs/Firebase/RemoteConfig/Details.md @@ -1,31 +1,18 @@ -Change the behavior and appearance of your app without publishing an app update. +# Firebase Remote Config -Firebase Remote Config is a cloud service that lets you change the behavior and appearance of your app without requiring users to download an app update. When using Remote Config, you create in-app default values that control the behavior and appearance of your app. Then, you can later use the Firebase console to override in-app default values for all app users or for segments of your userbase. Your app controls when updates are applied, and it can frequently check for updates and apply them with a negligible impact on performance. +This path is retained for existing links, but this repository does not maintain a copied Firebase Remote Config walkthrough. -## Key capabilities +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -| | | -|-:|--| -| **Quickly roll out changes to your app's userbase** | You can make changes to your app's default behavior and appearance by changing server-side parameter values. For example, you could change your app's layout or color theme to support a seasonal promotion, with no need to publish an app update. | -| **Customize your app for segments of your userbase** | You can use Remote Config to provide variations on your app's user experience to different segments of your userbase by app version, by Firebase Analytics audience, by language, and more. | -| **Run A/B tests to improve your app** | You can use Remote Config random percentile targeting with Firebase Analytics to A/B test improvements to your app across different segments of your userbase so that you can validate improvements before rolling them out to your entire userbase. | +## Native Documentation -## How does it work? +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Remote Config documentation: https://firebase.google.com/docs/remote-config/use-config-ios -Remote Config includes a client library that handles important tasks like fetching parameter values and caching them, while still giving you control over when new values are activated so that they affect your app's user experience. This lets you safeguard your app experience by controlling the timing of any changes. +## Binding Package -The Remote Config client library get methods provide a single access point for parameter values. Your app gets server-side values using the same logic it uses to get in-app default values, so you can add the capabilities of Remote Config to your app without writing a lot of code. +- Package README: [../NuGet/RemoteConfig.md](../NuGet/RemoteConfig.md) +- Package ID: `AdamE.Firebase.iOS.RemoteConfig` +- Managed namespace: `Firebase.RemoteConfig` -To override in-app default values, you use the Firebase console to create parameters with the same names as the parameters used in your app. For each parameter, you can set a server-side default value to override the in-app default value, and you can also create conditional values to override the in-app default value for app instances that meet certain conditions. - -## Policies and limits - -Note the following policies: - -* Don't use Remote Config to make app updates that should require a user's authorization. This could cause your app to be perceived as untrustworthy. -* Don't store confidential data in Remote Config parameter keys or parameter values. It is possible to decode any parameter keys or values stored in the Remote Config settings for your project. -* Don't attempt to circumvent the requirements of your app's target platform using Remote Config. - -Remote Config parameters and conditions are subject to certain limits. To learn more, see [Limits on parameters and conditions](https://firebase.google.com/docs/remote-config/parameters#limits_on_parameters_and_conditions). - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/remote-config/) to see original Firebase documentation._ \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/RemoteConfig/GettingStarted.md b/docs/Firebase/RemoteConfig/GettingStarted.md index 11bb505af..d72406526 100755 --- a/docs/Firebase/RemoteConfig/GettingStarted.md +++ b/docs/Firebase/RemoteConfig/GettingStarted.md @@ -1,190 +1,18 @@ -# Use Firebase Remote Config on iOS +# Firebase Remote Config -You can use Firebase Remote Config to define parameters in your app and update their values in the cloud, allowing you to modify the appearance and behavior of your app without distributing an app update. +This path is retained for existing links, but this repository does not maintain a copied Firebase Remote Config walkthrough. -## Table of content +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -- [Remote Config Parameters and Conditions](#remote-config-parameters-and-conditions) -- [Firebase Remote Config API Overview](#firebase-remote-config-api-overview) - - [Key Features of the Remote Config APIs](#key-features-of-the-remote-config-apis) - - [Remote Config library](#remote-config-library) - - [API architecture](#api-architecture) -- [Add Firebase to your app](#add-firebase-to-your-app) -- [Configure Remote Config in your app](#configure-remote-config-in-your-app) -- [Add Settings to Remote Config class](#add-settings-to-remote-config-class) -- [Set in-app default parameter values](#set-in-app-default-parameter-values) - - [Set in-app default parameter values with .plist file](#set-in-app-default-parameter-values-with-plist-file) - - [Set in-app default parameter values with a NSDictionary](#set-in-app-default-parameter-values-with-a-nsdictionary) -- [Set parameter values in Firebase Console](#set-parameter-values-in-firebase-console) -- [Fetch and activate values from the server](#fetch-and-activate-values-from-the-server) -- [Use parameter values](#use-parameter-values) -- [Caching and throttling](#caching-and-throttling) +## Native Documentation -## Remote Config Parameters and Conditions +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Firebase Remote Config documentation: https://firebase.google.com/docs/remote-config/use-config-ios -Please, read this [Firebase documentation][5] to learn about Parameters and Conditions. +## Binding Package -## Firebase Remote Config API Overview +- Package README: [../NuGet/RemoteConfig.md](../NuGet/RemoteConfig.md) +- Package ID: `AdamE.Firebase.iOS.RemoteConfig` +- Managed namespace: `Firebase.RemoteConfig` -Firebase Remote Config has APIs that make it easy to change the behavior and appearance of your app without requiring users to download an app update. This overview describes the following: - -* Key features of the Remote Config APIs. -* The Remote Config library and API architecture. - -### Key Features of the Remote Config APIs - -Remote Config APIs implement the following features: - -* **Your app controls when new parameter values are applied:** Because changes to parameter values affect the behavior and appearance of your app, the API design implements a singleton object that fetches values in the background, caches them, and then lets your app activate them at the right time. -* **In-app default parameter values:** You set in-app default values for all Remote Config parameters in your app. These values are available to your app immediately, even if a device does not have connectivity. You get fetched and activated values using the same methods that you use to get in-app default values. -* **Fetching and applying values is efficient:** Fetching and activating values from the Remote Config Server is efficient and can be done safely and repeatedly, so there is no need to add logic to your app that listens for a callback or that determines if it is safe to activate fetched values. In fact, you can write your app so that it sends a request to fetch parameter values and activates any previously fetched parameter values each time that a user starts your app, or even more frequently than that. If no fetched and activated values are available, your app will use in-app default values with a negligible impact on performance from the fetch request or the call to `ActivateFetched`. - -### Remote Config library - -The cornerstone of the Remote Config API architecture is the Remote Config Library. The Remote Config Library implements a singleton class, the `RemoteConfig` class. Use the Remote Config object to do the following: - -* **Set default values:** You don't need to manage (or even create) parameters in the Remote Config service for your app to work as intended. You can instrument your app with as many Remote Config parameters as you need and create in-app default values. Later, you can override a subset of your app's parameters by creating parameters on the Remote Config Server. -* **Fetch, store, and manage parameter values:** The Remote Config object contains three stores of parameter values: the **Default Config** (stores in-app default values), the **Active Config** (stores values that are available to the app using get methods), and the **Fetched Config** (stores values most recently fetched from the Remote Config Server). -* **Activate the Fetched Config, which updates the Active Config:** When the Fetched Config is activated, the parameter values in the Fetched Config are copied to the Active Config. This makes the recently fetched values available to your app. - -### API architecture - -The following diagram shows how your app interacts with Remote Config: - -![Firebase_Remote_Config_API_Architecture](https://firebase.google.com/docs/remote-config/images/api-use.png) - -The following table provides additional details on interactions between your app and the Remote Config Library: - -| Methods and properties | Notes | -|------------------------|-------| -| Get Remote Config object property: `SharedInstance` | Step #1: Your app calls this property to get the Remote Config object (or have it recovered from persistent storage). If the object was newly created, the Fetched Config, the Active Config, and the Default Config are initially "empty," containing no parameter values. | -| Set Default Config method: `SetDefaults` | Step #2: Your app calls this method to set values in the Default Config. If your app attempts to get a value from a new Remote Config object before that value exists in the Active Config, the value from the Default Config is provided instead. | -| Fetch method: `Fetch` | Your app uses this method to initiate a call to the Remote Config Server and obtain fresh parameter values, which are stored in the Fetched Config.
**Note:** Fetch method do not have an immediate effect on the behavior or appearance of your app. | -| Activate method: `ActivateFetched` | Your app activates the Fetched Config, which copies values stored there to the Active Config. | -| Get\ method: `GetConfigValue` | Your app calls this method to get parameter values from the Active Config. | -| Config settings method: `RemoteConfigSettings`' constructor | Used for custom settings. Currently only used for settings that allow app developers to refresh app data more quickly than is allowed for production apps. | -| Info methods and properties: `LastFetchTime` | Your app uses this property to get information about the Remote Config object. You can use these methods for debugging during app development. | - -> _**Note:**_ _Some Remote Config methods allow you to specify a namespace. These methods are reserved for future use. You do not need to specify a namespace to use Remote Config._ - -## Add Firebase to your app - -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. - -## Configure Remote Config in your app - -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -App.Configure (); -``` - -## Add Settings to Remote Config class - -Create settings for `RemoteConfig` class, you can enable developer mode to allow for frequent refreshes of the cache (don't forget to import `Firebase.RemoteConfig` namespace): - -```csharp -// Enabling developer mode, allows for frequent refreshes of the cache -RemoteConfig.SharedInstance.ConfigSettings = new RemoteConfigSettings (true); -``` - -## Set in-app default parameter values - -You can set in-app default parameter values in the Remote Config object, so that your app behaves as intended before it connects to the Remote Config Server, and so that default values are available if none are set on the server. You can achieve this with a **.plist** file or with an **NSDictionary** variable. - -### Set in-app default parameter values with .plist file - -In your Xamarin Studio app project do the following steps to add default parameters with .plist file: - -1. In your project app name do Right click/Add/New File... -2. In **New File** dialog, select iOS tab and choose Property List -3. Name the Property List as you want and click **New** -4. Change the **Build ACtion** of your just created file to **BundleResource** by Right clicking it/Build Action -5. Open your **.plist** file and add all the parameters that you want. - -After you have setup all your default parameters, set the **.plist** to the `RemoteConfig` class: - -```csharp -RemoteConfig.SharedInstance.SetDefaults (""); -``` - -### Set in-app default parameter values with a NSDictionary - -Instead of using a **.plist file** to load default parameter values to your app, you can create a `NSDictionary` to load them: - -```csharp -object [] values = { 5, 20 }; -object [] keys = { "times_table", "from_zero_to" }; -var defaultValues = NSDictionary.FromObjectsAndKeys (values, keys, keys.Length); -RemoteConfig.SharedInstance.SetDefaults (defaultValues); -``` - -## Set parameter values in Firebase Console - -1. In the [Firebase console][1], open your project. -2. Select **Remote Config** from the menu to view the Remote Config dashboard. -3. Define parameters with the same names as the parameters that you defined in your **.plist** file or in your **NSDictionary**. For each parameter, you can set a default value (which will eventually override the in-app default value) and you can also set conditional values. To learn more, see [Remote Config Parameters and Conditions][3]. - -## Fetch and activate values from the server - -After you have setup your parameter values in your app and in your server, you are ready to fetch those values in your app from the server so you can override your local values. Call `RemoteConfig.Fetch` instance method to retrieve values from server and call `RemoteConfig.ActivateFetched` instance method to make fetched parameter values available to your app: - -```csharp -// CacheExpirationSeconds is set to CacheExpiration here, indicating that any previously -// fetched and cached config would be considered expired because it would have been fetched -// more than CacheExpiration seconds ago. Thus the next fetch would go to the server unless -// throttling is in progress. The default expiration duration is 43200 (12 hours). -RemoteConfig.SharedInstance.Fetch (10, (status, error) => { - switch (status) { - case RemoteConfigFetchStatus.Success: - Console.WriteLine ("Config Fetched!"); - - // Call this method to make fetched parameter values available to your app - RemoteConfig.SharedInstance.ActivateFetched (); - - // Update your UI from here - ... - break; - - case RemoteConfigFetchStatus.Throttled: - case RemoteConfigFetchStatus.NoFetchYet: - case RemoteConfigFetchStatus.Failure: - Console.WriteLine ("Config not fetched..."); - break; - } -}); -``` - -## Use parameter values - -The way you can use parameter values in your app is by calling `RemoteConfig.GetConfigValue` instance method or using `RemoteConfig` indexer method: - -```csharp -var myValue = RemoteConfig.SharedInstance ["myKey"].NumberValue; -var myOtherValue = RemoteConfig.SharedInstance.GetConfigValue ("myOtherKey").StringValue; -``` - -## Caching and throttling - -Remote Config caches values locally after the first successful request. By default the cache expires after 12 hours, but you can change the cache expiration for a specific request by passing the desired cache expiration, in seconds, to `Fetch` method. If the values in the cache are older than the desired cache expiration, Remote Config will request fresh config values from the server. If your app requests fresh values using `Fetch` several times, requests are throttled and your app is provided with a cached value. - -During app development, you might want to refresh the cache very frequently (many times per hour) to let you rapidly iterate as you develop and test your app. To accommodate rapid iteration on a project with up to 10 developers, you can temporarily add a `RemoteConfigSettings` property with `IsDeveloperModeEnabled` set to true to your app, changing the caching settings of the `RemoteConfig` object. - -## Use Firebase Remote Config with Analytics - -When you build an app that includes both Firebase Remote Config and Google Analytics for Firebase, you gain the ability to understand your app users better and to respond to their needs more quickly. Read this [Firebase documentation][6] to learn more about this. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/remote-config/use-config-ios) to see original Firebase documentation._ - -[1]: https://firebase.google.com/console/ -[2]: http://support.google.com/firebase/answer/7015592 -[3]: https://firebase.google.com/docs/remote-config/parameters -[5]: https://firebase.google.com/docs/remote-config/parameters -[6]: https://firebase.google.com/docs/remote-config/config-analytics \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Storage/Details.md b/docs/Firebase/Storage/Details.md index c5c3bf7fe..8fa75a28f 100755 --- a/docs/Firebase/Storage/Details.md +++ b/docs/Firebase/Storage/Details.md @@ -1,27 +1,18 @@ -Firebase Storage is built for app developers who need to store and serve user-generated content, such as photos or videos. +# Cloud Storage for Firebase -Firebase Storage is a powerful, simple, and cost-effective object storage service built for Google scale. Firebase Storage adds Google security to file uploads and downloads for your Firebase apps, regardless of network quality. You can use it to store images, audio, video, or other user-generated content. Firebase Storage is backed by Google Cloud Storage, a powerful, simple, and cost-effective object storage service. +This path is retained for existing links, but this repository does not maintain a copied Cloud Storage for Firebase walkthrough. -## Key capabilities +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -| | | -|-:|--| -| **Robust operations** | Firebase Storage performs uploads and downloads regardless of network quality. Uploads and downloads are robust, meaning they restart where they stopped, saving your users time and bandwidth. | -| **Strong security** | Firebase Storage integrates with Firebase Authentication to provide simple and intuitive authentication for developers. You can use our declarative security model to allow access based on filename, size, content type, and other metadata. | -| **High scalability** | Firebase Storage is built for exabyte scale when your app goes viral. Effortlessly grow from prototype to production using the same infrastructure that powers Spotify and Google Photos. | +## Native Documentation -## How does it work? +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Cloud Storage for Firebase documentation: https://firebase.google.com/docs/storage/ios/start -Developers use the Firebase Storage SDK to upload and download files directly from clients. If the network connection is poor, the client is able to retry the operation right where it left off, saving your users time and bandwidth. +## Binding Package -Firebase Storage stores your files in a [Google Cloud Storage][1] bucket, making them accessible through both Firebase and Google Cloud APIs. This allows you the flexibility to upload and download files from mobile clients via Firebase and do server-side processing such as image filtering or video transcoding using [Google Cloud Platform][2]. Firebase Storage scales automatically, meaning that there's no need to migrate from Firebase Storage to Google Cloud Storage or any other provider. Learn more about all the benefits of our [integration with Google Cloud Platform][3]. +- Package README: [../NuGet/Storage.md](../NuGet/Storage.md) +- Package ID: `AdamE.Firebase.iOS.Storage` +- Managed namespace: `Firebase.Storage` -Firebase Storage integrates seamlessly with [Firebase Authentication][4] to identify users, and provides a [declarative security language][5] that lets you set access controls on individual files or groups of files, so you can make files as public or private as you want. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/storage/) to see original Firebase documentation._ - -[1]: https://cloud.google.com/storage -[2]: https://cloud.google.com/ -[3]: https://firebase.google.com/docs/storage/gcp-integration -[4]: https://components.xamarin.com/view/firebaseiosauth -[5]: https://firebase.google.com/docs/storage/security/start +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Firebase/Storage/GettingStarted.md b/docs/Firebase/Storage/GettingStarted.md index c852ec3bc..8fa75a28f 100755 --- a/docs/Firebase/Storage/GettingStarted.md +++ b/docs/Firebase/Storage/GettingStarted.md @@ -1,810 +1,18 @@ -# Get Started +# Cloud Storage for Firebase -Firebase Storage lets you upload and share user generated content, such as images and video, which allows you to build rich media content into your apps. Firebase Storage stores this data in a [Google Cloud Storage][1] bucket, an exabyte scale object storage solution with high availability and global redundancy. Firebase Storage lets you securely upload these files directly from mobile devices and web browsers, handling spotty networks with ease. +This path is retained for existing links, but this repository does not maintain a copied Cloud Storage for Firebase walkthrough. -## Table of content +These packages are thin .NET bindings over the native Firebase Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -- [Get Started](#get-started) - - [Table of content](#table-of-content) - - [Add Firebase to your app](#add-firebase-to-your-app) - - [Configure Storage in your app](#configure-storage-in-your-app) - - [Recommended documentation to get a better understanding of the Security & Rules of Firebase Storage](#recommended-documentation-to-get-a-better-understanding-of-the-security-rules-of-firebase-storage) - - [Set up public access](#set-up-public-access) - - [Advanced setup](#advanced-setup) - - [Use multiple storage buckets](#use-multiple-storage-buckets) - - [Working with imported buckets](#working-with-imported-buckets) - - [Use a custom Firebase App](#use-a-custom-firebase-app) -- [Create a Storage Reference](#create-a-storage-reference) - - [Create a Reference](#create-a-reference) - - [Navigate with References](#navigate-with-references) - - [Reference Properties](#reference-properties) - - [Limitations on References](#limitations-on-references) -- [Upload Files on iOS](#upload-files-on-ios) - - [Create a Reference](#create-a-reference) - - [Upload Files](#upload-files) - - [Upload from data in memory](#upload-from-data-in-memory) - - [Upload from a local file](#upload-from-a-local-file) - - [Add File Metadata](#add-file-metadata) -- [Download Files on iOS](#download-files-on-ios) - - [Create a Reference](#create-a-reference) - - [Download Files](#download-files) - - [Download in memory](#download-in-memory) - - [Download to a local file](#download-to-a-local-file) - - [Generate a download Url](#generate-a-download-url) -- [Manage Uploads and Downloads](#manage-uploads-and-downloads) - - [Monitor Upload and Download Progress](#monitor-upload-and-download-progress) - - [Error Handling](#error-handling) -- [Use File Metadata on iOS](#use-file-metadata-on-ios) - - [Get File Metadata](#get-file-metadata) - - [Update File Metadata](#update-file-metadata) - - [Error Handling](#error-handling) - - [Custom Metadata](#custom-metadata) - - [File Metadata Properties](#file-metadata-properties) -- [Delete Files on iOS](#delete-files-on-ios) - - [Delete a File](#delete-a-file) -- [Handle Errors](#handle-errors) - - [Handle Error Messages](#handle-error-messages) -- [Extend Cloud Storage with Cloud Functions](#extend-cloud-storage-with-cloud-functions) -- [Integrate with Google Cloud Platform](#integrate-with-google-cloud-platform) +## Native Documentation -## Add Firebase to your app +- Firebase Apple setup: https://firebase.google.com/docs/ios/setup +- Cloud Storage for Firebase documentation: https://firebase.google.com/docs/storage/ios/start -1. Create a Firebase project in the [Firebase console][2], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][3]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][3] again at any time. +## Binding Package -## Configure Storage in your app +- Package README: [../NuGet/Storage.md](../NuGet/Storage.md) +- Package ID: `AdamE.Firebase.iOS.Storage` +- Managed namespace: `Firebase.Storage` -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: - -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Add the following line of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace): - -```csharp -App.Configure (); -``` - -## Recommended documentation to get a better understanding of the Security & Rules of Firebase Storage - -Before you continue, I invite you to read the following docs to make your coding easier: - -* [Understand Security][4] -* [Get Started][5] -* [Secure Files][6] -* [User Based Security][7] - -## Set up public access - -Cloud Storage for Firebase provides a declarative rules language that allows you to define how your data should be structured, how it should be indexed, and when your data can be read from and written to. By default, read and write access to Storage is restricted so only authenticated users can read or write data. To get started without setting up [Authentication][8], you can [configure your rules for public access][9]. - -This does make Storage open to anyone, even people not using your app, so be sure to restrict your Storage again when you set up authentication. - -## Advanced setup - -There are a few use cases that require additional setup: - -Using storage buckets in [multiple geographic regions][10] -Using storage buckets in [different storage classes][11] -Using storage buckets with multiple authenticated users in the same app - -The first use case is perfect if you have users across the world, and want to store their data near them. For instance, you can create buckets in the US, Europe, and Asia to store data for users in those regions to reduce latency. - -The second use case is helpful if you have data with different access patterns. For instance: you can set up a multi-regional or regional bucket that stores pictures or other frequently accessed content, and a nearline or coldline bucket that stores user backups or other infrequently accessed content. - -In either of these use cases, you'll want to [use multiple storage buckets](#use-multiple-storage-buckets). - -The third use case is useful if you're building an app, like Google Drive, which lets users have multiple logged in accounts (for instance, a personal account and a work account). You can [use a custom Firebase App](#use-a-custom-firebase-app) instance to authenticate each additional account. - -### Use multiple storage buckets - -If you want to use a storage bucket other than the default provided above, or use multiple storage buckets in a single app, you can create an instance of `Storage` that references your custom bucket: - -```csharp -// Get a non-default Storage bucket -var storage = Storage.From ("gs://my-custom-bucket"); -``` - -### Working with imported buckets - -When importing an existing Cloud Storage bucket into Firebase, you'll have to grant Firebase the ability to access these files using the `gsutil` tool, included in the [Google Cloud SDK][12]: - -``` -gsutil -m acl ch -r -u firebase-storage@system.gserviceaccount.com:O gs:// -``` - -This does not affect newly created buckets, as those have the default access control set to allow Firebase. This is a temporary measure, and will be performed automatically in the future. - -### Use a custom Firebase App - -If you're building a more complicated app using a custom `FirebaseApp`, you can create an instance of Storage initialized with that app: - -```csharp -// Get the default bucket from a custom FirebaseApp -storage = Storage.From (customApp); - -// Get a non-default bucket from a custom FirebaseApp -storage = Storage.From (customApp, "gs://my-custom-bucket"); -``` - ---- - -# Create a Storage Reference - -Your files are stored in a [Google Cloud Storage][1] bucket. The files in this bucket are presented in a hierarchical structure, just like the file system on your local hard disk, or the data in the Firebase Database. By creating a reference to a file, your app gains access to it. These references can then be used to upload or download data, get or update metadata or delete the file. A reference can either point to a specific file or to a higher level in the hierarchy. - -## Create a Reference - -Create a reference to upload, download, or delete a file, or to get or update its metadata. A reference can be thought of as a pointer to a file in the cloud. References are lightweight, so you can create as many as you need. They are also reusable for multiple operations. - -References are created from the storage service on your Firebase app by calling the `GetReferenceFromUrl` method and passing in a URL of the form **gs://\**. You can find this URL in the Storage section of the [Firebase console][2]. - -```csharp -// Get a reference to the storage service, using the default Firebase App -var storage = Storage.DefaultInstance; - -// Create a storage reference from our storage service -StorageReference rootRef = storage.GetReferenceFromUrl ("gs://") - -// This is the same result as above -StorageReference rootRef = storage.GetRootReference (); -``` - -You can create a reference to a location lower in the tree, say **images/space.jpg**, by using the `GetChild` method on an existing reference: - -```csharp -// Create a child reference -// imagesRef now points to "images" ("gs:///images") -StorageReference imagesRef = rootRef.GetChild ("images"); - -// Child references can also take paths delimited by '/' -// spaceRef now points to "images/space.jpg" ("gs:///images/space.jpg") -// imagesRef still points to "images" -StorageReference spaceRef = rootRef.GetChild ("images/space.jpg"); - -// This is equivalent to creating the full reference -StorageReference spaceRef = storage.GetReferenceFromUrl ("gs:///images/space.jpg"); -``` - -## Navigate with References - -You can also use the `Parent` and `Root` properties to navigate up in our file hierarchy. `Parent` navigates up one level, while `Root` navigates all the way to the top. - -```csharp -// Parent allows us to move to the parent of a reference -// imagesRef now points to 'images' -StorageReference imagesRef = spaceRef.Parent; - -// Root allows us to move all the way back to the top of our bucket -// rootRef now points to the root -StorageReference *rootRef = spaceRef.Parent; -``` - -`GetChild` method, `Parent` and `Root` properties can be chained together multiple times, as each returns a reference. The exception is the `Parent` of `rootRef`, which is `null`: - -```csharp -// References can be chained together multiple times -// earthRef points to "images/earth.jpg" -StorageReference earthRef = spaceRef.Parent.GetChild ("earth"); - -// nullRef is null, since the Parent of Root is null -StorageReference nullRef = spaceRef.Root.Parent; -``` - -## Reference Properties - -You can inspect references to better understand the files they point to using the `FullPath`, `Name`, and `Bucket` properties. These properties get the file's full path, name, and bucket: - -```csharp -// Reference's path is: "images/space.jpg" -// This is analogous to a file path on disk -spaceRef.FullPath; - -// Reference's name is the last segment of the full path: "space.jpg" -// This is analogous to the file name -spaceRef.Name; - -// Reference's bucket is the name of the storage bucket where files are stored -spaceRef.Bucket; -``` - -## Limitations on References - -Reference paths and names can contain any sequence of valid Unicode characters, but certain restrictions are imposed including: - -1. Total length of reference.FullPath must be between 1 and 1024 bytes when UTF-8 encoded. -2. No Carriage Return or Line Feed characters. -3. Avoid using **#**, **[**, **]**, **\***, or **?**, as these do not work well with other tools such as the Firebase Database or [gsutil][13]. - ---- - -# Upload Files on iOS - -Firebase Storage allows developers to quickly and easily upload files to a [Google Cloud Storage][1] bucket provided and managed by Firebase. - -> ![note_icon] **_Note_**: _By default, Firebase Storage buckets require Firebase Authentication to upload files. You can [change your Firebase Storage Security Rules][9] to allow unauthenticated access. Since the default Google App Engine app and Firebase share this bucket, configuring public access may make newly uploaded App Engine files publicly accessible as well. Be sure to restrict access to your Storage bucket again when you set up authentication._ - -## Create a Reference - -To upload a file, first create a Firebase Storage reference to the location in Firebase Storage you want to upload the file to. - -You can create a reference by appending child paths to the storage root: - -```csharp -// Create a root reference -StorageReference rootRef = storage.GetRootReference (); - -// Create a reference to "mountains.jpg" -StorageReference mountainsRef = root.GetChild ("mountains.jpg"); - -// Create a reference to 'images/mountains.jpg' -StorageReference mountainImagesRef = root.GetChild ("images/mountains.jpg"); - -// While the file names are the same, the references point to different files -mountainsRef.Name == mountainImagesRef.Name; // true -mountainsRef.FullPath == mountainImagesRef.FullPath; // false -``` - -You **cannot upload data** with a reference to the root of your Google Cloud Storage bucket. Your reference must point to a child URL. - -## Upload Files - -Once you have a reference, you can upload files to Firebase Storage in two ways: - -1. Upload from `NSData` in memory. -2. Upload from an `NSUrl` representing a file on device. - -### Upload from data in memory - -The `PutData` method is the simplest way to upload a file to Firebase Storage. `PutData` takes an `NSData` object and returns a `StorageUploadTask`, which you can use to manage your upload and monitor its status: - -```csharp -// Data in memory -NSData data = ... - -// Create a reference to the file you want to upload -StorageReference riversRef = rootRef.GetChild ("images/rivers.jpg"); - -// Upload the file to the path "images/rivers.jpg" -StorageUploadTask uploadTask = riversRef.PutData (data, null, HandleStorageGetPutUpdateCompletion); - -void HandleStorageGetPutUpdateCompletion (StorageMetadata metadata, NSError error) -{ - if (error != null) { - // Uh-oh, an error occurred! - return; - } - - // Metadata contains file metadata such as size, content-type, and download URL. - var downloadUrl = metadata.DownloadUrl; -} -``` - -### Upload from a local file - -You can upload local files on the devices, such as photos and videos from the camera, with the `PutFile` method. `PutFile` takes an `NSUrl` and returns a `StorageUploadTask`, which you can use to manage your upload and monitor its status: - -```csharp -// Data in memory -NSUrl localFile = ... - -// Create a reference to the file you want to upload -StorageReference riversRef = rootRef.GetChild ("images/rivers.jpg"); - -// Upload the file to the path "images/rivers.jpg" -StorageUploadTask uploadTask = riversRef.PutFile (localFile, null, HandleStorageGetPutUpdateCompletion); - -void HandleStorageGetPutUpdateCompletion (StorageMetadata metadata, NSError error) -{ - if (error != null) { - // Uh-oh, an error occurred! - return; - } - - // Metadata contains file metadata such as size, content-type, and download URL. - var downloadUrl = metadata.DownloadUrl; -} -``` - -If you want to actively manage your upload, you can use the `PutData` or `PutFile` methods and observe the upload task, rather than using the completion handler. See [Manage Uploads and Downloads](#manage-uploads-and-downloads) section below for more information. - -## Add File Metadata - -You can also include metadata when you upload files. This metadata contains typical file metadata properties such as `Name`, `Size`, and `ContentType` (commonly referred to as MIME type). The `PutFile` method automatically infers the content type from the `NSUrl` filename extension, but you can override the auto-detected type by specifying `ContentType` in the metadata. If you do not provide a `ContentType` and Cloud Storage cannot infer a default from the file extension, Firebase Storage uses **application/octet-stream**. See the [Use File Metadata](#use-file-metadata) section below for more information about file metadata. - -```csharp -// Create storage reference -StorageReference mountainsRef = rootRef.GetChild ("images/mountains.jpg"); - -// Create file metadata including the content type -var imageMetadata = new StorageMetadata { ContentType = "image/jpeg" }; - -// Upload data and metadata -StorageUploadTask uploadTask = mountainsRef.PutData (data, metadata); - -// Upload file and metadata -StorageUploadTask uploadTask = mountainsRef.PutFile (localFile, metadata); -``` - ---- - -# Download Files on iOS - -Firebase Storage allows developers to quickly and easily download files from a [Google Cloud Storage][1] bucket provided and managed by Firebase. - -> ![note_icon] **_Note_**: _By default, Cloud Storage buckets require Firebase Authentication to download files. You can change your [Firebase Storage Security Rules][9] to allow unauthenticated access. Since the default Google App Engine app and Firebase share this bucket, configuring public access may make newly uploaded App Engine files publicly accessible as well. Be sure to restrict access to your Storage bucket again when you set up authentication._ - -## Create a Reference - -To download a file, first create a Firebase Storage reference to the file you want to download. - -You can create a reference by appending child paths to the storage root, or you can create a reference from an existing **gs://** or **https://** URL referencing an object in Cloud Storage: - -```csharp -// Create a reference with an initial file path and name -var pathRef = storage.GetReferenceFromPath ("images/stars.jpg"); - -// Create a reference from a Google Cloud Storage URI -var gsRef = storage.GetReferenceFromUrl ("gs:///images/stars.jpg"); - -// Create a reference from an HTTPS URL -// Note that in the URL, characters are URL escaped! -var httpRef = storage.GetReferenceFromUrl ("https://firebasestorage.googleapis.com/b/bucket/o/images%20stars.jpg"); -``` - -## Download Files - -Once you have a reference, you can download files from Firebase Storage in three ways: - -1. Download to `NSData` in memory. -2. Download to an `NSUrl` representing a file on device. -3. Generate an `NSUrl` representing the file online. - -### Download in memory - -Download the file to an `NSData` object in memory using the `GetData` method. This is the easiest way to quickly download a file, but it must load entire contents of your file into memory. If you request a file larger than your app's available memory, your app will crash. To protect against memory issues, make sure to set the max size to something you know your app can handle, or use another download method: - -```csharp -// Create a reference to the file you want to download -StorageReference islandRef = rootRef.GetChild ("images/island.jpg"); - -// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes) -islandRef.GetData (1 * 1024 * 1024, HandleStorageGetDataCompletion); - -void HandleStorageGetDataCompletion (NSData data, NSError error) -{ - if (error != nil) { - // Uh-oh, an error occurred! - return; - } - - // Data for "images/island.jpg" is returned - var islandImage = UIImage.LoadFromData (data); -} -``` - -### Download to a local file - -The `WriteToFile` method downloads a file directly to a local device. Use this if your users want to have access to the file while offline or to share in a different app. `WriteToFile` returns an `StorageDownloadTask` which you can use to manage your download and monitor the status of the upload: - -```csharp -// Create a reference to the file you want to download -StorageReference islandRef = rootRef.GetChild ("images/island.jpg"); - -// Create local filesystem Url -var localUrl = NSUrl.FromString ("path/to/images/island.jpg"); - -// Download to the local filesystem -StorageDownloadTask downloadTask = islandRef.WriteToFile (localUrl, HandleStorageWriteToFileCompletion); - -void HandleStorageWriteToFileCompletion (NSUrl url, NSError error) -{ - if (error != null) { - // Uh-oh, an error occurred! - return; - } - - // Local file Url for "images/island.jpg" is returned -} -``` - -If you want to actively manage your download, you can use the `WriteToFile` method and observe the download task, rather than use the completion handler. See [Manage Uploads and Downloads](#manage-uploads-and-downloads) section below for more information. - -### Generate a download Url - -If you already have download infrastructure based around Urls, or just want a Url to share, you can get the download Url for a file by calling the `GetDownloadUrl` method on a storage reference: - -```csharp -// Create a reference to the file you want to download -StorageReference starsRef = rootRef.GetChild ("images/stars.jpg"); - -// Fetch the download Url -starsRef.GetDownloadUrl (HandleStorageDownloadUrlCompletion); - -void HandleStorageDownloadUrlCompletion (NSUrl url, NSError error) -{ - if (error != null) { - // Handle any errors - return; - } - - // Get the download URL for 'images/stars.jpg' -} -``` - -An `async`/`await` version of this: - -```csharp -// Create a reference to the file you want to download -StorageReference starsRef = rootRef.GetChild ("images/stars.jpg"); - -try { - NSUrl url = await starsRef.GetDownloadUrlAsync (); - - // Get the download URL for 'images/stars.jpg' -} catch (NSErrorException ex) { - // Handle any errors -} -``` - ---- - -# Manage Uploads and Downloads - -In addition to starting uploads and downloads, you can pause, resume, and cancel uploads and downloads, using the `Pause`, `Resume`, and `Cancel` methods. These methods raise pause, resume, and cancel events that you can observe. Canceling an upload causes the upload to fail with an error indicating that the upload was canceled: - -```csharp -// Start uploading/downloading a file -StorageUploadTask uploadTask = mountainsRef.PutFile (localFile, metadata); -StorageDownloadTask downloadTask = islandRef.WriteToFile (localUrl); - -// Pause the upload/download -uploadTask.Pause (); -downloadTask.Pause (); - -// Resume the upload/download -uploadTask.Resume (); -downloadTask.Resume (); - -// Cancel the upload/download -uploadTask.Cancel (); -downloadTask.Cancel (); -``` - -## Monitor Upload and Download Progress - -You can attach observers to `StorageUploadTask` in order to monitor the progress of the upload. Adding an observer returns a `string` that can be used to remove the observer: - -```csharp -// Add a progress observer to an upload task -string observer = uploadTask.ObserveStatus (StorageTaskStatus.Progress, (snapshot) => { - // A progress event occurred -}); - -// Add a progress observer to a download task -string observer = downloadTask.ObserveStatus (StorageTaskStatus.Progress, (snapshot) => { - // A progress event occurred -}); -``` - -These observers can be added to an `StorageTaskStatus` event: - -| StorageTaskStatus Event | Typical Usage | -|:------------------------------:|---------------| -| **StorageTaskStatus.Resume** | This event fires when the task starts or resumes uploading/downloading, and is often used in conjunction with the `StorageTaskStatus.Pause` event. | -| **StorageTaskStatus.Progress** | This event fires any time data is uploading/downloading to Firebase Storage, and can be used to populate an upload progress indicator. | -| **StorageTaskStatus.Pause** | This event fires any time the upload/download is paused, and is often used in conjunction with the `StorageTaskStatus.Resume` event. | -| **StorageTaskStatus.Success** | This event fires when a upload/download has completed sucessfully. | -| **StorageTaskStatus.Failure** | This event fires when a upload/download has failed. Inspect the error to determine the failure reason. | - -When an event occurs, an `StorageTaskSnapshot` object is passed back. This snapshot is an immutable view of the task, at the time the event occurred. This object contains the following properties/methods: - -| Name | Kind | Type | Description | -|:----------------:|:------------:|:----------------------------------------------:|-------------| -| **Progress** | **Property** | **NSProgress** | An `NSProgress` object containing the progress of the upload/download. | -| **Error** | **Property** | **NSError** | An error that occurred during upload/download, if any. | -| **Metadata** | **Property** | **StorageMetadata** | During upload contains metadata being uploaded. After an `StorageTaskStatus.Success` event, contains the uploaded file's metadata. `null` on downloads. | -| **GetTask\** | **Method** | **StorageUploadTask/
StorageDownloadTask** | The task this is a snapshot of, which can be used to manage (pause, resume, cancel) the task. | -| **Reference** | **Property** | **StorageReference** | The reference this task came from. | - -You can also remove observers, either individually, by status, or by removing all of them: - -```csharp -// Create a task listener handle -string observer = ... - -// Remove an individual observer -uploadTask.RemoveObserver (observer); -downloadTask.RemoveObserver (observer); - -// Remove all observers of a particular status -uploadTask.RemoveAllObservers (StorageTaskStatus.Progress); -downloadTask.RemoveAllObservers (StorageTaskStatus.Progress); - -// Remove all observers -uploadTask.RemoveAllObservers (); -downloadTask.RemoveAllObservers (); -``` - -To prevent memory leaks, all observers are removed after an `StorageTaskStatus.Success` or `StorageTaskStatus.Failure` occurs. - -## Error Handling - -There are a number of reasons why errors may occur on upload, including the local file not existing, or the user not having permission to upload the desired file. You can find more information about errors in the [Handle Errors](#handle-errors) section below of the docs. - ---- - -# Use File Metadata on iOS - -After uploading a file to Firebase Storage reference, you can also get and update the file metadata, for example to update the content type. Files can also store custom key/value pairs with additional file metadata. - -> ![note_icon] **_Note_**: _By default, Cloud Storage buckets require Firebase Authentication to get and update metadata. You can change your [Firebase Storage Security Rules][9] to allow unauthenticated access. Since the default Google App Engine app and Firebase share this bucket, configuring public access may make newly uploaded App Engine files publicly accessible as well. Be sure to restrict access to your Storage bucket again when you set up authentication._ - -## Get File Metadata - -File metadata contains common properties such as `Name`, `Size`, and `ContentType` (often referred to as MIME type) in addition to some less common ones like `ContentDisposition` and `TimeCreated`. This metadata can be retrieved from a Firebase Storage reference using the `GetMetadata` method: - -```csharp -// Create reference to the file whose metadata we want to retrieve -StorageReference forestRef = rootRef.GetChild ("images/forest.jpg"); - -// Get metadata properties -forestRef.GetMetadata (HandleStorageGetPutUpdateCompletion); - -void HandleStorageGetPutUpdateCompletion (StorageMetadata metadata, NSError error) -{ - if (error != null) { - // Uh-oh, an error occurred! - return; - } - - // Metadata now contains the metadata for 'images/forest.jpg' -} -``` - -An `async`/`await` version of this: - -```csharp -// Create reference to the file whose metadata we want to retrieve -StorageReference forestRef = rootRef.GetChild ("images/forest.jpg"); - -try { - StorageMetadata metadata = await starsRef.GetMetadataAsync (); - - // Metadata now contains the metadata for 'images/forest.jpg' -} catch (NSErrorException ex) { - // Uh-oh, an error occurred! -} -``` - -## Update File Metadata - -You can update file metadata at any time after the file upload completes by using the `UpdateMetadata` method. Refer to the [full list](#ile-metadata-properties) for more information on what properties can be updated. Only the properties specified in the metadata are updated, all others are left unmodified. - -```csharp -// Create reference to the file whose metadata we want to change -StorageReference forestRef = rootRef.GetChild ("images/forest.jpg"); - -// Create file metadata to update -var newMetadata = new StorageMetadata { - CacheControl = "public,max-age=300", - ContentType = "image/jpeg" -}; - -// Update metadata properties -forestRef.UpdateMetadata (newMetadata, HandleStorageGetPutUpdateCompletion); - -void HandleStorageGetPutUpdateCompletion (StorageMetadata metadata, NSError error) -{ - if (error != null) { - // Uh-oh, an error occurred! - return; - } - - // Updated metadata for 'images/forest.jpg' is returned -} -``` - -An `async`/`await` version of this: - -```csharp -// Create reference to the file whose metadata we want to change -StorageReference forestRef = rootRef.GetChild ("images/forest.jpg"); - -// Create file metadata to update -var newMetadata = new StorageMetadata { - CacheControl = "public,max-age=300", - ContentType = "image/jpeg" -}; - -try { - StorageMetadata metadata = await forestRef.UpdateMetadataAsync (newMetadata); - - // Updated metadata for 'images/forest.jpg' is returned -} catch (NSErrorException ex) { - // Uh-oh, an error occurred! -} -``` - -You can delete writable metadata properties by passing the empty string: - -```csharp -var newMetadata = new StorageMetadata { - ContentType = "" -}; - -// Delete the metadata property -forestRef.UpdateMetadata (newMetadata, HandleStorageGetPutUpdateCompletion); - -void HandleStorageGetPutUpdateCompletion (StorageMetadata metadata, NSError error) -{ - if (error != null) { - // Uh-oh, an error occurred! - return; - } - - // Updated metadata for 'images/forest.jpg' is returned -} -``` - -## Error Handling - -There are a number of reasons why errors may occur on getting or updating metadata, including the local file not existing, or the user not having permission to upload the desired file. You can find more information about errors in the [Handle Errors](#handle-errors) section below of the docs. - -## Custom Metadata - -You can specify custom metadata as an `NSDictionary` containing `NSString` properties: - -```csharp -object [] keys = { "location", "activity" }; -object [] values = { "Yosemite, CA, USA", "Hiking" }; -var customMetadata = NSDictionary.FromObjectsAndKeys (values, keys, keys.Length); - -var metadata = NSDictionary.FromObjectAndKey (customMetadata, new NSString ("customMetadata")); -``` - -You can store app-specific data for each file in custom metadata, but Firebase highly recommend using a database (such as the [Firebase Database][14]) to store and synchronize this type of data. - -### File Metadata Properties - -A full list of metadata properties on a file is available below: - -| Property | Type | Writable | -|:----------------------:|:--------------------------------:|:--------:| -| **Bucket** | string | NO | -| **Generation** | long | NO | -| **Metageneration** | long | NO | -| **Path** | string | NO | -| **Name** | string | NO | -| **Size** | long | NO | -| **TimeCreated** | NSDate | NO | -| **Updated** | NSDate | NO | -| **Md5Hash** | string | YES | -| **CacheControl** | string | YES | -| **ContentDisposition** | string | YES | -| **ContentEncoding** | string | YES | -| **ContentLanguage** | string | YES | -| **ContentType** | string | YES | -| **DownloadUrls** | NSUrl [] | NO | -| **CustomMetadata** | NSDictionary | YES | - -> ![note_icon] _**Note:**_ _at present, setting the md5Hash property on upload doesn't affect the upload, as hash verification is not yet implemented._ - ---- - -# Delete Files on iOS - -> ![note_icon] **_Note_**: _By default, Cloud Storage buckets require Firebase Authentication to delete files. You can change your [Firebase Storage Security Rules][9] to allow unauthenticated access. Since the default Google App Engine app and Firebase share this bucket, configuring public access may make newly uploaded App Engine files publicly accessible as well. Be sure to restrict access to your Storage bucket again when you set up authentication._ - -## Delete a File - -To delete a file, first create a reference to that file. Then call the `Delete` method on that reference: - -```csharp -// Create a reference to the file to delete -StorageReference desertRef = rootRef.GetChild ("images/desert.jpg"); - -// Delete the file -desertRef.Delete (HandleStorageDeleteCompletion); - -void HandleStorageDeleteCompletion (NSError error) -{ - if (error != null) { - // Uh-oh, an error occurred! - return; - } - - // File deleted successfully -} -``` - -An `async`/`await` version of this: - -```csharp -// Create a reference to the file to delete -StorageReference desertRef = rootRef.GetChild ("images/desert.jpg"); - -// Delete the file -desertRef.Delete (HandleStorageDeleteCompletion); - -try { - await desertRef.DeleteAsync (); - - // File deleted successfully -} catch (NSErrorException ex) { - // Uh-oh, an error occurred! -} -``` - -> ![note_icon] **_Note:_** _Deleting a file is a permenant action! If you care about restoring deleted files, make sure to back up your files, or [enable object versioning][15] on your Google Cloud Storage bucket._ - ---- - -# Handle Errors - -Sometimes when you're building an app, things don't go as planned and an error occurs. - -When in doubt, check the error returned, and see what the error message says. - -> ![note_icon] **_Note_**: _By default, Cloud Storage buckets require Firebase Authentication to perform any action. You can change your [Firebase Storage Security Rules][9] to allow unauthenticated access. Since the default Google App Engine app and Firebase share this bucket, configuring public access may make newly uploaded App Engine files publicly accessible as well. Be sure to restrict access to your Storage bucket again when you set up authentication._ - -If you've checked the error message and have Storage Security Rules that allow your action, but are still struggling to fix the error, visit [Firebase Support page][16] and let them know how they can help. - -### Handle Error Messages - -There are a number of reasons why errors may occur, including the file not existing, the user not having permission to access the desired file, or the user cancelling the file upload. - -To properly diagnose the issue and handle the error, here is a full list of all the errors our client will raise, and how they can occur. - -| Name | Reason | -|:-----------------------------------------:|--------| -| **StorageErrorCode.Unknown** | An unknown error occurred. | -| **StorageErrorCode.ObjectNotFound** | No object exists at the desired reference. | -| **StorageErrorCode.BucketNotFound** | No bucket is configured for Firebase Storage. | -| **StorageErrorCode.ProjectNotFound** | No project is configured for Firebase Storage. | -| **StorageErrorCode.QuotaExceeded** | Quota on your Firebase Storage bucket has been exceeded. If you're on the free tier, upgrade to a paid plan. If you're on a paid plan, reach out to Firebase support. -| **StorageErrorCode.Unauthenticated** | User is unauthenticated. Authenticate and try again. | -| **StorageErrorCode.Unauthorized** | User is not authorized to perform the desired action. Check your rules to ensure they are correct. | -| **StorageErrorCode.RetryLimitExceeded** | The maximum time limit on an operation (upload, download, delete, etc.) has been exceeded. Try uploading again. | -| **StorageErrorCode.NonMatchingChecksum** | File on the client does not match the checksum of the file recieved by the server. Try uploading again. | -| **StorageErrorCode.Cancelled** | User cancelled the operation. -| **StorageErrorCode.DownloadSizeExceeded** | Size of the downloaded file exceeds the amount of memory allocated for the download. Increase memory cap and try downloading again. | - ---- - -# Extend Cloud Storage with Cloud Functions - -You can trigger a function in response to the uploading, updating, or deleting of files and folders in Cloud Storage. For more information, read the following [documentation][17]. - ---- - -# Integrate with Google Cloud Platform - -Firebase Storage is tightly integrated with [Google Cloud Platform][1]. The Firebase SDKs for Storage store files directly in [Google Cloud Storage buckets][18], and as your app grows, you can easily integrate other Cloud services, such as managed compute like App Engine or Cloud Functions, or machine learning APIs like Cloud Vision or Google Translate. - -For more information, visit the following [documentation][19]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://firebase.google.com/docs/storage/ios/start) to see original Firebase documentation._ - -[1]: https://cloud.google.com/storage -[2]: https://firebase.google.com/console/ -[3]: http://support.google.com/firebase/answer/7015592 -[4]: https://firebase.google.com/docs/storage/security -[5]: https://firebase.google.com/docs/storage/security/start -[6]: https://firebase.google.com/docs/storage/security/secure-files -[7]: https://firebase.google.com/docs/storage/security/user-security -[8]: https://components.xamarin.com/view/firebaseiosauth -[9]: https://firebase.google.com/docs/storage/security/start#sample-rules -[10]: https://cloud.google.com/storage/docs/bucket-locations -[11]: https://cloud.google.com/storage/docs/storage-classes -[12]: https://cloud.google.com/sdk/docs/ -[13]: https://cloud.google.com/storage/docs/gsutil -[14]: https://components.xamarin.com/view/firebaseiosdatabase -[15]: https://cloud.google.com/storage/docs/object-versioning#_Enabling -[16]: https://firebase.google.com/support -[17]: https://firebase.google.com/docs/storage/extend-with-functions -[18]: https://cloud.google.com/storage/docs/key-terms#buckets -[19]: https://firebase.google.com/docs/storage/gcp-integration -[note_icon]: https://cdn3.iconfinder.com/data/icons/UltimateGnome/22x22/apps/gnome-app-install-star.png -[warning_icon]: https://cdn2.iconfinder.com/data/icons/freecns-cumulus/32/519791-101_Warning-20.png +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Analytics/Details.md b/docs/Google/Analytics/Details.md index 97182fb02..137a8f56e 100755 --- a/docs/Google/Analytics/Details.md +++ b/docs/Google/Analytics/Details.md @@ -1,7 +1,21 @@ -Set up and customize tracking for your mobile apps. +# Google Analytics -## Report and analyze +This path is retained for existing links, but this repository does not maintain a copied Google Analytics walkthrough. -Use our simple and powerful APIs to retrieve report data from Google Analytics. With the reporting APIs, you can save time by automating complex reporting tasks. You can also integrate Google Analytics data with your own business data for deeper insights. +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. +## Native Documentation +- Google Analytics documentation: https://developers.google.com/analytics/devguides/collection/ios/v3 + +## Binding Project + +- Package ID: `AdamE.Google.iOS.Analytics` +- Managed namespace: `Google.Analytics` +- Status note: This binding project is present in the repository. Check the top-level README and package feed for current publication status before depending on it. + +## Binding Notes + +The native Google Analytics v3 singleton maps to `Google.Analytics.Gai.SharedInstance` in this binding. + +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Analytics/GettingStarted.md b/docs/Google/Analytics/GettingStarted.md index e82ad0ffa..137a8f56e 100755 --- a/docs/Google/Analytics/GettingStarted.md +++ b/docs/Google/Analytics/GettingStarted.md @@ -1,97 +1,21 @@ -# Get your Google Analytics Tracking Id +# Google Analytics -Follow the instructions in [this page](https://support.google.com/analytics/answer/2614741) to set up and get the tracking ID for a new app property in either a new or existing Google Analytics account. +This path is retained for existing links, but this repository does not maintain a copied Google Analytics walkthrough. -# iOS Getting Started +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -There are two steps to getting started with the iOS SDK: +## Native Documentation -1. Initialize the tracker -2. Add screen measurement +- Google Analytics documentation: https://developers.google.com/analytics/devguides/collection/ios/v3 -After completing these steps, you'll be able to measure the following with Google Analytics: +## Binding Project -* App installations -* Active users and demographics -* Screens and user engagement -* Crashes and exceptions +- Package ID: `AdamE.Google.iOS.Analytics` +- Managed namespace: `Google.Analytics` +- Status note: This binding project is present in the repository. Check the top-level README and package feed for current publication status before depending on it. -## Initializing the tracker +## Binding Notes -To initialize the tracker, use the `Google.Analytics` namespace in your AppDelegate and add this code to your AppDelegate's `FinishedLaunching` method: +The native Google Analytics v3 singleton maps to `Google.Analytics.Gai.SharedInstance` in this binding. -```csharp -using Google.Analytics; -//... - -// Shared GA tracker -public ITracker Tracker; - -// Learn how to get your own Tracking Id from: -// https://support.google.com/analytics/answer/2614741?hl=en -public static readonly string TrackingId = "UA-TRACKING-ID"; - -public override bool FinishedLaunching (UIApplication app, NSDictionary options) -{ - - // Optional: set Google Analytics dispatch interval to e.g. 20 seconds. - Gai.SharedInstance.DispatchInterval = 20; - - // Optional: automatically send uncaught exceptions to Google Analytics. - Gai.SharedInstance.TrackUncaughtExceptions = true; - - // Initialize tracker. - Tracker = Gai.SharedInstance.GetTracker (TrackingId); -} -``` - -**Note**: When you obtain a tracker for a given tracking Id, the tracker instance is persisted in the library. When you call `GetTracker` with the same tracking Id later, the same tracker instance will be returned. Also, the Google Analytics SDK exposes a default tracker instance that gets set to the first tracker instance created. It can be accessed by: - -```csharp -Gai.SharedInstance.DefaultTracker -``` - -Note that in the above example, "UA-TRACKING-ID" here is a placeholder for the tracking ID assigned to you when you created your Google Analytics property. If you are only using one property ID in your app, using the default tracker method is best. - -## Implementing screen measurement - -To implement it you must provide the view name to be used in your Google Analytics reports. A good place to put this is the view controller's initializer method, if you have one, or the `ViewDidAppear` method: - -```csharp -using Google.Analytics; -//... - -public override void ViewDidAppear (bool animated) -{ - base.ViewDidAppear (animated); - - // This screen name value will remain set on the tracker and sent with - // hits until it is set to a new value or to null. - Gai.SharedInstance.DefaultTracker.Set (GaiConstants.ScreenName, "Main View"); - - Gai.SharedInstance.DefaultTracker.Send (DictionaryBuilder.CreateAppView ().Build ()); -} -``` - -**Note**: `GaiConstants` is a static class containing all the constants you can set in the `Set` method from the `DictionaryBuilder` class. - -To learn more about screen measurement, see the [Screens Developer Guide](https://developers.google.com/analytics/devguides/collection/ios/v3/screens). - -Congratulations! Your Xamarin.iOS app is now setup to send data to Google Analytics. - -## Next steps - -You can do much more with Google Analytics, including measuring campaigns, in-app payments and transactions, and user interaction events. See the following developer guides to learn how to add these features to your implementation: - -* [Advanced Configuration](https://developers.google.com/analytics/devguides/collection/ios/v3/advanced) – Learn more about advanced configuration options, including using multiple trackers. -* [Measuring Campaigns](https://developers.google.com/analytics/devguides/collection/ios/v3/campaigns) – Learn how to implement campaign measurement to understand which channels and campaigns are driving app installs. -* [Measuring Events](https://developers.google.com/analytics/devguides/collection/ios/v3/events) – Learn how to measure user engagement with interactive content like buttons, videos, and other media using Events. -* [Measuring In-App Payments](https://developers.google.com/analytics/devguides/collection/ios/v3/ecommerce) – Learn how to measure in-app payments and transactions. -* [User timings](https://developers.google.com/analytics/devguides/collection/ios/v3/usertimings) – Learn how to measure user timings in your app to measure load times, engagement with media, and more. - -# More Resources - -* [Google Analytics Developer Portal](https://developers.google.com/analytics/devguides/collection/) - - -###### The [original content material](https://developers.google.com/analytics/devguides/collection/) of this page is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and has been adapted to match this page format. \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Cast/Details.md b/docs/Google/Cast/Details.md index 6bbdd0130..48c7f97fa 100755 --- a/docs/Google/Cast/Details.md +++ b/docs/Google/Cast/Details.md @@ -1,11 +1,21 @@ -Google Cast is a technology that enables multi-screen experiences and lets a user send and control content like video from a small computing device like a phone, tablet, or laptop to a large display device like a television. +# Google Cast -The sender may be a phone or tablet running on Android or iOS, or it may be a laptop computer running Chrome OS, Mac OS, or Windows. A sender application running on the sender device uses the Google Cast API appropriate to its operating system to discover and transmit to the receiver application running on the receiver device. You can use the sender APIs to enable your Android, iOS, or Chrome app to send content to a large display. +This path is retained for existing links, but this repository does not maintain a copied Google Cast walkthrough. -The receiver device is optimized for video playback with a receiver application that receives data over Internet Protocol and transmits it to the television. The receiver API lets you customize the messaging between the sender and receiver applications for authentication and other scenarios. +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -![https://developers.google.com/cast/images/Diagram.png](https://developers.google.com/cast/images/Diagram.png) +## Native Documentation -While content is playing on a TV, a user can multitask on their device without interrupting the video playback. For example, a user can search for a video on their phone’s YouTube application and then send the video to their TV via a Google Cast device. The user can play, pause, seek, and control volume using their phone, they can search for other videos to watch, and even check their email while the content keeps playing on the TV. +- Google Cast documentation: https://developers.google.com/cast/docs/ios_sender -#####Portions of this page are modifications based on [work](https://developers.google.com/cast/) created and shared by [Google Inc.](http://google.com) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). \ No newline at end of file +## Binding Project + +- Package ID: `AdamE.Google.iOS.Cast` +- Managed namespace: `Google.Cast` +- Status note: This binding project is present in the repository. Check the top-level README and package feed for current publication status before depending on it. + +## Binding Notes + +The native `GCKCastContext.setSharedInstanceWithOptions` setup maps to `Google.Cast.CastContext.SetSharedInstance(options)`. After setup, use `Google.Cast.CastContext.SharedInstance`. + +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Cast/GettingStarted.md b/docs/Google/Cast/GettingStarted.md index 49ff37518..48c7f97fa 100755 --- a/docs/Google/Cast/GettingStarted.md +++ b/docs/Google/Cast/GettingStarted.md @@ -1,708 +1,21 @@ -# Table of Content +# Google Cast -- [Table of Content](#table-of-content) -- [Integrate CAF Sender into your iOS App](#integrate-caf-sender-into-your-ios-app) - - [App flow](#app-flow) - - [Call methods from main thread](#call-methods-from-main-thread) - - [Initialize the Cast Context](#initialize-the-cast-context) - - [The Cast UX Widgets](#the-cast-ux-widgets) - - [Add a Cast Button](#add-a-cast-button) - - [Configure device discovery](#configure-device-discovery) - - [How Session Management Works](#how-session-management-works) - - [Automatic Reconnection](#automatic-reconnection) - - [How Media Control Works](#how-media-control-works) - - [Set Media Metadata](#set-media-metadata) - - [Load media](#load-media) - - [4K Video Format](#4k-video-format) - - [Add Mini Controllers](#add-mini-controllers) - - [Add Expanded Controller](#add-expanded-controller) - - [Handle Errors](#handle-errors) - - [Logging](#logging) -- [Add Advanced CAF Sender Features to your iOS App](#add-advanced-caf-sender-features-to-your-ios-app) - - [Style the Widgets](#style-the-widgets) - - [Customize the Widgets](#customize-the-widgets) - - [Choose Controller Buttons](#choose-controller-buttons) - - [How Volume Control Works](#how-volume-control-works) - - [Using Media Tracks](#using-media-tracks) - - [Styling Text Tracks](#styling-text-tracks) - - [Receive Status Updates](#receive-status-updates) - - [Satisfy CORS Requirements](#satisfy-cors-requirements) - - [Add a Custom Channel](#add-a-custom-channel) - - [Use Queueing](#use-queueing) - - [Create and Load Media Queue Items](#create-and-load-media-queue-items) - - [Receive Media Queue Status Update](#receive-media-queue-status-update) - - [Edit the Queue](#edit-the-queue) - - [Supporting Autoplay](#supporting-autoplay) - - [Override Image Selection and Caching](#override-image-selection-and-caching) -- [Apply Custom Styles to Your iOS App](#apply-custom-styles-to-your-ios-app) - - [Applying a style to a UI element of a widget](#applying-a-style-to-a-ui-element-of-a-widget) - - [Table of Views and Styles](#table-of-views-and-styles) +This path is retained for existing links, but this repository does not maintain a copied Google Cast walkthrough. -# Integrate CAF Sender into your iOS App +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -The mobile device or laptop is the sender which controls the playback, and the Google Cast device is the receiver which displays the content on the TV. +## Native Documentation -The sender framework refers to the Cast framework bundle, which consists of the class library binary and associated header files and resources. The sender app or Cast app refers to an app running on the sender. The receiver app refers to the HTML application running on the receiver. +- Google Cast documentation: https://developers.google.com/cast/docs/ios_sender -Google Cast uses the delegation pattern to inform the sender app of events and to move between various states of the Cast app life cycle. +## Binding Project -## App flow +- Package ID: `AdamE.Google.iOS.Cast` +- Managed namespace: `Google.Cast` +- Status note: This binding project is present in the repository. Check the top-level README and package feed for current publication status before depending on it. -The following steps describe the typical high-level execution flow for a sender iOS app: +## Binding Notes -* The Cast framework automatically starts device discovery based on the view controller lifecycle. -* When the user clicks on the Cast button, the framework presents the Cast dialog with the list of discovered Cast devices. -* When the user selects a Cast device, the framework attempts to launch the receiver app on the Cast device. -* The framework invokes callbacks in the sender app to confirm that the receiver app was launched. -* The framework creates a communication channel between the sender and receiver apps. -* The framework uses the communication channel to load and control media playback on the receiver. -* The framework synchronizes the media playback state between sender and receiver: when the user makes sender UI actions, the framework passes those media control requests to the receiver, and when the receiver sends media status updates, the framework updates the state of the sender UI. -* When the user clicks on the Cast button to disconnect from the Cast device, the framework will disconnect the sender app from the receiver. +The native `GCKCastContext.setSharedInstanceWithOptions` setup maps to `Google.Cast.CastContext.SetSharedInstance(options)`. After setup, use `Google.Cast.CastContext.SharedInstance`. -To troubleshoot your sender, you need to enable [logging](#logging). - -## Call methods from main thread - -All SDK methods must be called from the main thread. - -## Initialize the Cast Context - -The Cast framework has a global singleton object, the `CastContext`, which coordinates all of the framework's activities. This object must be initialized early in the application's lifecycle, typically in the `FinishedLaunching` method of the app delegate, so that automatic session resumption on sender app restart can trigger properly. - -A `CastOptions` object must be supplied when initializing the `CastContext`. This class contains options that affect the behavior of the framework. The most important of these is the **Receiver Application ID**, which is used to filter discovery results and to launch the receiver app when a Cast session is started. - -The `FinishedLaunching` method is also a good place to set up a **Logging Delegate** to receive the logging messages from the framework. These can be useful for debugging and troubleshooting: - -```csharp -using Google.Cast; - -... - -// You can add your own app id here that you get by registering -// with the Google Cast SDK Developer Console https://cast.google.com/publish -public static readonly string ReceiverApplicationId = "A1B2C3D4"; - -public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions) -{ - - // Contains options that affect the behavior of the framework. - var discoveryCreiteria = new DiscoveryCriteria (ReceiverApplicationId); - var options = new CastOptions (discoveryCreiteria); - - // CastContext coordinates all of the framework's activities. - CastContext.SetSharedInstance (options); - - // Google Cast Logger - Logger.SharedInstance.Delegate = this; - - ... - - return true; -} - -#region Logger Delegate - -[Export ("logMessage:fromFunction:")] -void LogMessage (string message, string function) -{ - Console.WriteLine ($"{function} {message}"); -} - -#endregion -``` - -## The Cast UX Widgets - -The Cast framework provides these widgets that comply with the Cast Design Checklist: - -* [Introductory Overlay][1]: The `CastContext` class has a method, `PresentCastInstructionsViewControllerOnce`, which can be used to spotlight the **Cast button** the first time a Cast receiver is available. The Sender app can customize the text, position of the title text and the Dismiss button. -* [Cast Button][2]: The cast button is visible when a receiver is discovered that supports your app. When the user first clicks on the cast button, a cast dialog is displayed which lists the discovered devices. When the user clicks on the cast button while the device is connected, it displays the current media metadata (such as title, name of the recording studio and a thumbnail image) or allows the user to disconnect from the cast device. -* [Mini Controller][3]: When the user is casting content and has navigated away from the current content page or expanded controller to another screen in the sender app, the mini controller is displayed at the bottom of the screen to allow the user to see the currently casting media metadata and to control the playback. -* [Expanded Controller][4]: When the user is casting content, if they click on the media notification or mini controller, the expanded controller launches, which displays the currently playing media metadata and provides several buttons to control the media playback. - -## Add a Cast Button - -The framework provides a Cast button component as a `UIButton` subclass. It can be added to the app's title bar by wrapping it in a `UIBarButtonItem`. A typical `UIViewController` subclass can install a Cast button as follows: - -```csharp -// Cast button to allow the user to select a Cast device. -var btnCast = new UICastButton (new CGRect (0, 0, 24, 24)) { TintColor = UIColor.White }; -NavigationItem.RightBarButtonItem = new UIBarButtonItem (btnCast); -``` - -By default, tapping the button will open the Cast dialog that is provided by the framework. - -`UICastButton` can also be added directly to the storyboard. - -## Configure device discovery - -In the framework, device discovery happens automatically. There is no need to explicitly start or stop the discovery process. - -Discovery in the framework is managed by the class `DiscoveryManager`, which is a property of the `CastContext`. The framework provides a default Cast dialog component for device selection and control. The device list is ordered lexicographically by device friendly name. - -## How Session Management Works - -The Cast framework introduces the concept of a Cast session, the establishment of which combines the steps of connecting to a device, launching (or joining) a receiver app, connecting to that app, and initializing a media control channel, if appropriate. - -Sessions are managed by the class `SessionManager`, which is a property of the `CastContext`. Individual sessions are represented by subclasses of the class `Session`: for example, `CastSession` represents sessions with Cast devices. You can access the currently active Cast session (if any), as the `CurrentCastSession` property of `SessionManager` class. - -The `ISessionManagerListener` interface can be used to monitor session events, such as session creation, suspension, resumption, and termination. The framework automatically suspends sessions when the sender app is going into the background and attempts to resume them when the app returns to the foreground (or is relaunched after an abnormal/abrupt app termination while a session was active). - -If the Cast dialog is being used, then sessions are created and torn down automatically in response to user gestures. Otherwise, the app can start and end sessions explicitly via methods on `SessionManager`. - -If the app needs to do special processing in response to session lifecycle events, it can register one or more `ISessionManagerListener` instances with the `SessionManager`. `ISessionManagerListener` is an interface which defines callbacks for such events as session start, session end, and so on. - -## Automatic Reconnection - -The Cast framework adds reconnection logic to automatically handle reconnection in many subtle corner cases, such as: - -* Recover from a temporary loss of WiFi -* Recover from device sleep -* Recover from backgrounding the app -* Recover if the app crashed - -## How Media Control Works - -If a Cast session is established with a receiver app that supports the media namespace, an instance of RemoteMediaClient will be created automatically by the framework; it can be accessed as the `RemoteMediaClient` property of the `CastSession` instance. - -All methods on `RemoteMediaClient` which issue requests to the receiver will return a `Request` object which can be used to track that request. A `IRequestDelegate` can be assigned to this object to receive notifications about the eventual result of the operation. - -It is expected that the instance of `RemoteMediaClient` may be shared by multiple parts of the app, and indeed some internal components of the framework such as the Cast dialog and mini media controls do share the instance. To that end, `RemoteMediaClient` supports the registration of multiple `IRemoteMediaClientListeners`, unlike `MediaControlChannel`, which only supported a single delegate. - -## Set Media Metadata - -The `MediaMetadata` class represents information about a media item you want to cast. The following example creates a new `MediaMetadata` instance of a movie and sets the title, subtitle, recording studio's name, and two images: - -```csharp -var metadata = new MediaMetadata (MediaMetadataType.Movie); -metadata.SetString (video.Title, MetadataKey.Title); -metadata.SetString (video.Subtitle, MetadataKey.Subtitle); -metadata.SetString (video.Studio, MetadataKey.Studio); -metadata.AddImage (new Image (video.ImageUrl, 480, 720)); -metadata.AddImage (new Image (video.PosterUrl, 780, 1200)); -``` - -## Load media - -To load a media item, create a `MediaInformation` instance using the media's metadata. Then get the current `CastSession` and use its `RemoteMediaClient` to load the media on the receiver app. You can then use `RemoteMediaClient` for controlling a media player app running on the receiver, such as for play, pause and stop: - -```csharp -var mediaInformation = new MediaInformation (videoUrl, - MediaStreamType.Buffered, - "video/mp4", - metadata, - video.Duration, - null, - null, - null); - -var castSession = CastContext.SharedInstance.SessionManager.CurrentCastSession; - -// Make sure that we are connected to a Cast device. -if (castSession != null) { - // Play video on Cast device. - castSession.RemoteMediaClient.LoadMedia (mediaInformation, true); -} -``` - -### 4K Video Format - -To determine what video format your media is, use the property `VideoInfo` of `MediaStatus` class to get the current instance of `VideoInfo`. This instance contains the type of HDR TV format and the height and width in pixels. Variants of 4K format are indicated in the hdrType property by enum values `VideoInfoHdrType`. - -## Add Mini Controllers - -According to the [Cast Design Checklist][5], a sender app should provide a persistent control known as the [mini controller][6] that should appear when the user navigates away from the current content page. The mini controller provides instant access and a visible reminder for the current Cast session. - -The Cast framework provides a control bar, `UIMiniMediaControlsViewController`, which can be added to the scenes in which you want to show the mini controller. - -There are two ways to add the mini controller to a sender app: - -* Let the Cast framework manage the layout of the mini controller by wrapping your existing view controller with its own view controller. -* Manage the layout of the mini controller widget yourself by adding it to your existing view controller by providing a subview in the storyboard. - -The first way is to use the `UICastContainerViewController` which wraps another view controller and adds a `UIMiniMediaControlsViewController` at the bottom. This approach is limited in that you cannot customize the animation and cannot configure the behavior of the container view controller. - -This first way is typically done in the `FinishedLaunching` method of the app delegate: - -```csharp -public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions) -{ - ... - - var appStoryboard = UIStoryboard.FromName ("Main", null); - var navigationController = appStoryboard.InstantiateViewController ("MainNavigation") as UINavigationController; - var castContainer = CastContext.SharedInstance.CreateCastContainerController (navigationController); - castContainer.MiniMediaControlsItemEnabled = true; - - ... -} -``` - -Add this property to control the visibility of the mini controller: - -```csharp -public bool CastControlBarsEnabled { - get { - var castContainer = Window.RootViewController as UICastContainerViewController; - return castContainer.MiniMediaControlsItemEnabled; - } - set { - var castContainer = Window.RootViewController as UICastContainerViewController; - castContainer.MiniMediaControlsItemEnabled = value; - } -} -``` - -The second way is to add the mini controller directly to your existing view controller by using `CreateMiniMediaControlsViewController` to create a `UIMiniMediaControlsViewController` instance and then adding it to the container view controller as a subview: - -```csharp -using Google.Cast; - -UIView MiniMediaControlsContainerView; -bool miniMediaControlsViewEnabled; -NSLayoutConstraint miniMediaControlsHeightConstraint; -UIMiniMediaControlsViewController miniMediaControlsViewController; - -public override void ViewDidLoad () -{ - base.ViewDidLoad (); - - miniMediaControlsViewController = CastContext.SharedInstance.CreateMiniMediaControlsViewController (); - miniMediaControlsViewController.ShouldAppear += (sender, e) => { - UpdateControlBarsVisibility (); - }; - AddChildViewController (miniMediaControlsViewController); - - miniMediaControlsViewController.View.Frame = MiniMediaControlsContainerView.Bounds; - MiniMediaControlsContainerView.AddSubview (miniMediaControlsViewController.View); - miniMediaControlsViewController.DidMoveToParentViewController (this); - - UpdateControlBarsVisibility (); -} -``` - -The `UIMiniMediaControlsViewController.ShouldAppear` event tells the host view controller when the mini controller should be visible: - -```csharp -public override void ViewDidLoad () -{ - ... - - miniMediaControlsViewController.ShouldAppear += (sender, e) => { - UpdateControlBarsVisibility (); - }; - - ... -} - -void UpdateControlBarsVisibility () -{ - if (miniMediaControlsViewEnabled && - miniMediaControlsViewController.Active) { - miniMediaControlsHeightConstraint.Constant = miniMediaControlsViewController.MinHeight; - } else { - miniMediaControlsHeightConstraint.Constant = 0; - } - - UIView.Animate (0.5, () => { View.LayoutIfNeeded () }); -} - -``` - -When your sender app is playing a video or audio live stream, the SDK automatically displays a play/stop button in place of the play/pause button in the mini controller. - -See **Style the Widgets** section below for how your sender app can configure the appearance of the widgets across your app. - -## Add Expanded Controller - -The Google Cast Design Checklist requires a sender app to provide an expanded controller for the media being cast. The expanded controller is a full screen version of the mini controller. - -The expanded controller is a full screen view which offers full control of the remote media playback. This view should allow a casting app to manage every manageable aspect of a cast session, with the exception of receiver volume control and session lifecycle (connect/stop casting). It also provides all the status information about the media session (artwork, title, subtitle, and so forth). - -The functionality of this view is implemented by the `UIExpandedMediaControlsViewController` class. - -The first thing you have to do is enable the default expanded controller in the cast context. Modify `FinishedLaunching` to enable the default expanded controller: - -```csharp -public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions) -{ - ... - - // Use Default Expanded Media Controls - CastContext.SharedInstance.UseDefaultExpandedMediaControls = true; - - ... -} -``` - -Add the following code in your controller to load the expanded controller when the user starts to cast a video: - -```csharp -// Reproduces the video on Cast device. -void PlayVideoRemotely (MediaInformation mediaInformation) -{ - ... - - NavigationItem.BackBarButtonItem = new UIBarButtonItem ("", UIBarButtonItemStyle.Plain, null, null); - (UIApplication.SharedApplication.Delegate as AppDelegate).CastControlBarsEnabled = false; - CastContext.SharedInstance.PresentDefaultExpandedMediaControls (); - - ... -} -``` - -The expanded controller will also be launched automatically when the user taps the mini controller. - -When your sender app is playing a video or audio live stream, the SDK automatically displays a play/stop button in place of the play/pause button in the expanded controller. - -See **Style the Widgets** section below for how your sender app can configure the appearance of the widgets across your app. - -## Handle Errors - -It is very important for sender apps to handle all error callbacks and decide the best response for each stage of the Cast life cycle. The app can display error dialogs to the user or it can decide to end the Cast session. - -## Logging - -`Logger` class is a singleton used for logging by the framework. Use the `ILoggerDelegate` to customize how you handle log messages: - -```csharp -public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions) -{ - ... - - // Google Cast Logger - Logger.SharedInstance.Delegate = this; - - ... -} - -#region Logger Delegate - -[Export ("logMessage:fromFunction:")] -void LogMessage (string message, string function) -{ - Console.WriteLine ($"{function} {message}"); -} - -#endregion -``` - -> ***Note:*** *Don't forget to implement the `ILoggerDelegate` interface to your class.* - ---- - -# Add Advanced CAF Sender Features to your iOS App - -## Style the Widgets - -You can customize [Cast widgets][7] by setting the colors, styling the buttons, text and thumbnail appearance, and by choosing the types of buttons to display - -### Customize the Widgets - -The Cast framework widgets supports the [Apple UIAppearance Protocol in UIKit][8] to change the appearance of the widgets across your app, such as the position or border of a button. Use this protocol to style the Cast framework widgets to match an existing apps styling. - -### Choose Controller Buttons - -Both the expanded controller class (`UIExpandedMediaControlsViewController`) and the mini controller class (`UIMiniMediaControlsViewController`) contain a button bar, and clients can configure which buttons are presented on those bars. This is achieved by both classes conforming to `IUIMediaButtonBarProtocol` interface. - -The mini controller bar has 3 configurable slots for buttons: - -``` -SLOT SLOT SLOT - 1 2 3 -``` - -The expanded controller bar has a permanent play/pause toggle button in the middle of the bar plus 4 configurable slots: - -``` -SLOT SLOT PLAY/PAUSE SLOT SLOT - 1 2 BUTTON 3 4 -``` - -Your app can get a reference to the expanded controller with the `CastContext.DefaultExpandedMediaControlsViewController` property and can get a reference to the mini controller if using `CastContext.CreateMiniMediaControlsViewController` method. - -Each slot can contain either a framework button, a custom button, or be empty. The list of framework control buttons are defined as: - -| UIMediaButtonType | Description | -|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| **None** | No button, results in empty space at a button position. | -| **PlayPauseToggle** | A default button that toggles between play and pause states. | -| **SkipNext** | A default "next" button. When tapped, playback moves to the next media item in the queue. It becomes disabled if there is no next media item in the queue. | -| **SkipPrevious** | A default "previous" button. When tapped, playback moves to the previous media item in the queue. It becomes disabled if there is no previous media item in the queue. | -| **Rewind30Seconds** | A default "rewind 30 seconds" button. When tapped, playback skips 30 seconds back in the currently playing media item. | -| **Forward30Seconds** | A default "forward 30 seconds" button. When tapped, playback skips 30 seconds forward in the currently playing media item. | -| **MuteToggle** | A default "mute toggle" button. When tapped, the receiver's mute state is toggled. | -| **ClosedCaptions** | A default "closed captions" button. When the button is tapped, the media tracks selection UI is displayed to the user. | -| **Stop** | A default "stop" button. When the button is tapped, playback of the current media item is terminated on the receiver. | -| **Custom** | A button created and managed by the client. | - -Add a button as follows: - -* To add a framework button to a bar requires only a call to `IUIMediaButtonBarProtocol.SetButtonType` method. -* To add a custom button to a bar, your app must call `IUIMediaButtonBarProtocol.SetButtonType` with buttonType parameter set to `UIMediaButtonType.Custom`, and then call `IUIMediaButtonBarProtocol.SetCustomButton` passing the `UIButton` with the same index. - -## How Volume Control Works - -The Cast framework automatically manages the volume for the sender app. The framework automatically synchronizes with the receiver volume for the supplied UI widgets. To sync a slider provided by the app, use `UIDeviceVolumeController`. - -## Using Media Tracks - -A media track can be an audio or video stream object, or a text object (subtitle or caption). - -> ***Note:*** *Currently, the Styled and Default Media Receivers allow you to use only the text tracks with the API. To work with the audio and video tracks, you must develop a Custom Receiver.* - -A `MediaTrack` object represents a track. It consists of a unique numeric identifier and other attributes such as a content ID and title. A `MediaTrack` instance can be created as follows: - -```csharp -var track = new MediaTrack (1, - "https://some-url/caption_en.vtt", - "text/vtt", - MediaTrackType.Text, - MediaTextTrackSubtype.Captions, - "English Captions", - "en", - null); -``` - -A media item can have multiple tracks; for example, it can have multiple subtitles (each for a different language) or multiple alternative audio streams (for different languages). `MediaInformation` is the class that represents a media item. To associate a collection of `MediaTrack` objects with a media item, your app should update its `MediaTracks` property. Your app needs to make his association before it loads the media to the receiver, as in the following code: - -```csharp -MediaTrack [] tracks = { track }; - -var mediaInformation = new MediaInformation ("https://some-url/", - MediaStreamType.None, - "video/mp4", - metadata, - 0, - tracks, - null, - null); -``` - -Activate one or more tracks that were associated with the media item (after the media is loaded) by calling `RemoteMediaClient.SetActiveTrackIDs` method and passing the IDs of the tracks to be activated. For example, the following code activates the captions track created above: - -```csharp -remoteMediaClient.SetActiveTrackIDs (new nuint [] { 1 }); -``` - -To deactivate a track on the current media item, call `RemoteMediaClient.SetActiveTrackIDs` method with an empty array or `null`. The following code disables the captions track: - -```csharp -remoteMediaClient.SetActiveTrackIDs (new nuint [] { }); -// or -remoteMediaClient.SetActiveTrackIDs (null); -``` - -### Styling Text Tracks - -The `MediaTextTrackStyle` class encapsulates the style information of a text track. A track style can be applied to the currently playing media item by calling `RemoteMediaClient.SetTextTrackStyle` method. The track style created in the code below turns text red (FF) at 50% opacity (80) and sets a serif font: - -```csharp -var textTrackStyle = MediaTextTrackStyle.CreateDefault (); -textTrackStyle.ForegroundColor = new Color ("#FF000080"); -textTrackStyle.FontFamily = "serif"; -styleChangeRequest = remoteMediaClient.SetTextTrackStyle (textTrackStyle); -``` - -You can use the returned GCKRequest object for tracking this request. - -```csharp -styleChangeRequest.Completed += (sender, e) => { - var request = sender as Request; - - if (request == styleChangeRequest) { - Console.WriteLine ("Style update completed."); - styleChangeRequest = null; - } -}; -``` - -See **Status updates** section below for more information. Apps should allow users to update the style for text tracks, either using the settings provided by the system or by the app itself. There is a default style provided (in iOS 7 and later), which can be retrieved via the static method `MediaTextTrackStyle.CreateDefault`. The following text track style elements can be changed: - -* Foreground (text) color and opacity -* Background color and opacity -* Edge type -* Edge Color -* Font Scale -* Font Family -* Font Style - -### Receive Status Updates - -When multiple senders are connected to the same receiver, it is important for each sender to be aware of the changes on the receiver even if those changes were initiated from other senders. - -> ***Note:*** *this is important for all apps, not only those that explicitly support multiple senders, because some Cast devices have control inputs (remotes, buttons) that behave as virtual senders, affecting the status on the receiver.* - -To this end, your app should register a `IRemoteMediaClientListener`. If the `MediaTextTrackStyle` of the current media changes, then all of the connected senders will be notified through both the `MediaControlChannelDelegate.DidUpdateMetadata` interface method or `MediaControlChannel.StatusUpdated` event and the `MediaControlChannelDelegate.DidUpdateStatus` interface method or `MediaControlChannel.MetadataUpdated` event. In this case, the receiver classes do not verify whether the new style is different from the previous one and notifies all the connected senders regardless. If, however, the list of active tracks is updated, only the `MediaControlChannelDelegate.DidUpdateStatus` interface method or `MediaControlChannel.MetadataUpdated` event in connected senders will be notified. - -> ***Note:*** *The list of tracks associated with the currently loaded media cannot be changed. If needed, you have to update the tracks information on the MediaInformation object and reload the media.* - -### Satisfy CORS Requirements - -For adaptive media streaming, Google Cast requires the presence of CORS headers, but even simple mp4 media streams require CORS if they include Tracks. If you want to enable Tracks for any media, you must enable CORS for both your track streams and your media streams. So, if you do not have CORS headers available for your simple mp4 media on your server, and you then add a simple subtitle track, you will not be able to stream your media unless you update your server to include the appropriate CORS header. In addition, you need to allow at least the following headers: **Content-Type**, **Accept-Encoding**, and **Range**. Note that the last two headers are additional headers that you may not have needed previously. - -## Add a Custom Channel - -Cast v3.x retains the `CastChannel` and `GenericChannel` classes of Cast v2.x. The former class is meant to be subclassed to implement non-trivial channels that have associated state. The latter class is provided as an alternative to subclassing; it passes its received messages to a delegate so that they can be processed elsewhere. - -In Cast v2.x, custom channels were enabled/disabled by registering/unregistering them with the `DeviceManager`. That class is deprecated in Cast SDK v3.x; channels must now be registered/unregistered with the `CastSession` instead, using its `AddChannel` and `RemoveChannel` methods. - -The Cast framework provides two ways to create a channel to send custom messages to a receiver: - -1. `CastChannel` class is meant to be subclassed to implement non-trivial channels that have associated state. -2. `GenericChannel` is provided as an alternative to subclassing; it passes its received messages to a delegate so that they can be processed elsewhere. - -Channels must be registered/unregistered with the `CastSession` class, using its `AddChannel` and `RemoveChannel` methods. - -Here is an example of a GCKCastChannel implementation: - -```csharp -public class MyTextChannel : CastChannel -{ - public MyTextChannel (string protocolNamespace) : base (protocolNamespace) - { - } - - public override void DidReceiveTextMessage (string message) - { - Console.WriteLine ($"Received message: {message}"); - } -} -``` - -A channel can be registered at any time; if the session is not currently in a connected state, the channel will automatically become connected when the session itself is connected, provided that the channel's namespace is present in the receiver app metadata's list of supported namespaces. - -Each custom channel is defined by a unique namespace and must start with the prefix `urn:x-cast:`, for example, `urn:x-cast:com.example.custom`. It is possible to have multiple custom channels, each with a unique namespace. The receiver app can also send and receive messages using the same namespace: - -```csharp -var myTextChannel = new MyTextChannel ("urn:x-cast:com.google.cast.sample.helloworld"); -castSession.AddChannel (myTextChannel); -myTextChannel.SendTextMessage ("Hello World!"); -``` - -To provide logic that needs to execute when a particular channel becomes connected or disconnected, override the `DidConnect` and `DidDisconnect` methods if using `CastChannel`, or provide implementations for the `DidConnect` and `DidDisconnect` methods of the `IGenericChannelDelegate` interface or `Connected` and `Disconnected` events if using `GenericChannel`. - -## Use Queueing - -The Cast framework provides queueing APIs that support the creation of lists of content items, such as video or audio streams, to play sequentially on the Cast receiver. The queue of content items may be edited, reordered, updated, and so forth. - -Review the [Google Cast Autoplay UX Guidelines][9] for best practices when designing an autoplay/queueing experience for Cast. - -The Cast receiver classes maintain the queue and responds to operations on the queue as long as the queue has at least one item currently active (playing or paused). Senders can join the session and add items to the queue. The receiver maintains a session for queue items until the last item completes playback or the sender stops the playback and terminates the session, or until a sender loads a new queue on the receiver. By default, the receiver does not maintain any information about terminated queues. Once the last item in the queue finishes, the media session ends and the queue vanishes. - -> ***Note:*** *The [styled media receiver][10] and [default media receiver][11] do not support a queue of images; only a queue of audio or video streams is supported in the styled and default receivers.* - -### Create and Load Media Queue Items - -In iOS, a media queue item is represented in the Cast framework as a `MediaQueueItem` instance. When you create a media queue item, if you are using the Media Player Library with adaptive content, you can set the preload time so that the player can begin buffering the media queue item before the item ahead of it in the queue finishes playing. Setting the item's autoplay attribute to true allows the receiver to play it automatically. For example, you can use a builder pattern to create your media queue item as follows: - -```csharp -var builder = new MediaQueueItemBuilder { - MediaInformation = mediaInformation, - Autoplay = true, - PreloadTime = NSUserDefaults.StandardUserDefaults.DoubleForKey (prefPreloadTimeKey) -}; -MediaQueueItem newItem = builder.Build (); -``` - -Load an array of media queue items in the queue by using the appropriate `QueueLoadItems` method of the `RemoteMediaClient` class. - -### Receive Media Queue Status Update - -When the receiver loads a media queue item, it assigns a unique ID to the item which persists for the duration of the session (and the life of the queue). You can learn the status of the queue indicating which item is currently loaded (it might not be playing), loading, or preloaded. You can also get an ordered list of all the items in the queue. The `MediaStatus` class provides this status information: - -* PreloadedItemID property - The ID of the item that is currently preloaded, if any. -* LoadingItemID property - The ID of the item that is currently loading, -* CurrentItemID property - The ID of the current queue item, if any. -* QueueItemCount property - Returns the number of items in the playback queue. -* QueueItemAt method - Returns the item at the specified index in the playback queue. - -Use these members together with the other media status members to inform your app about the status of the queue and the items in the queue. In addition to media status updates from the receiver, you can listen for changes to the queue by implementing `IRemoteMediaClientListener.DidUpdateQueue` interface method. - -> ***Note:*** *To provide the best user experience, the sender app must show the next autoplay item in the queue in the sender UI.* - -### Edit the Queue - -To work with the items in the queue, use the queue methods of `RemoteMediaClient`, you have several APIs. These let you load an array of items into a new queue, insert items into an existing queue, update the properties of items in the queue, make an item jump forward or backward in the queue, set the properties of the queue itself (for example, change the RepeatMode that selects the next item), remove items from the queue, and reorder the items in the queue. - -## Supporting Autoplay - -See [Autoplay & Queueing APIs][12]. - -## Override Image Selection and Caching - -Various components of the framework (namely the Cast dialog, the mini controller, the expanded controller, and the `UIMediaController` if so configured) will display artwork for the currently casting media. The URLs to the image artwork are typically included in the `MediaMetadata` for the media, but the sender app may have an alternate source for the URLs. The `IUIImagePicker` interface defines a means for selecting an appropriate image for a given usage and desired size. It has a single method, `GetImage`, which takes a `UIImageHints` object and a `MediaMetadata` object as parameters, and returns a `Image` object as a result. The framework provides a default implementation of `IUIImagePicker` which always selects the first image in the list of images in the `MediaMetadata` object, but the app can provide an alternate implementation by setting the ImagePicker property of the `CastContext` singleton. - -The `IUIImageCache` interface defines a means of caching images that are downloaded by the framework via HTTPS. The framework provides a default implementation of `UIImageCache` which stores downloaded image files in the app's cache directory, but the app can provide an alternate implementation by setting the ImageCache property of the CastContext singleton. - ---- - -# Apply Custom Styles to Your iOS App - -The Cast framework enables you to style the font, color, and images of UI elements of the default widgets in your Cast application, giving the views a look and feel that matches the rest of your app. This styling mechanism is available only with Cast iOS SDK v3 or later. - -The following section shows you how to apply custom styles to any of the Cast widgets or group of widgets. - -## Applying a style to a UI element of a widget - -This procedure uses the example of setting the body text color of your app's mini controller to red. - -1. Look in the [table of views and styles](#table-of-views-and-styles) to find the view name of the widget or group of widgets that you want to style. Group names are marked with ▼. - - Example: `MiniController` widget view - -2. Find the names of the attributes you want to change from the list of properties in the corresponding style class listed in this table. - - Example: `BodyTextColor` is a property of the `UIStyleAttributesMiniController` class. - -3. Write the code. - - Example: - - ```csharp - var castStyle = UIStyle.SharedInstance; - castStyle.CastViews.MediaControl.MiniController.BodyTextColor = UIColor.Red; - // Assign colors, fonts and images to other properties, as needed - castStyle.ApplyStyle (); - ``` - - Descriptions of each line of code: - - 1. Get the shared instance of `Google.Cast.UIStyle`. - 2. Create a color using the Apple class `UIColor`, then assign the color to the `BodyTextColor` property of the `MiniController`. - 3. Similarly, assign other colors, fonts (using `UIFont`), and images (using `UIImage`) to properties of widgets and groups. - 4. Use the `ApplyStyle` method to refresh all currently visible views with their assigned styles. - -Use this pattern for applying any style to any UI element of any widget. - -## Table of Views and Styles - -This table shows the seven widget views and three groups (marked with ▼) that you can apply styles to. - -| View name | Type | Style class | -|-------------------------|--------|-----------------------------------------| -| ▼ CastViews | Group | UIStyleAttributesCastViews | -| ▼ DeviceControl | Group | UIStyleAttributesDeviceControl | -| DeviceChooser | Widget | UIStyleAttributesDeviceChooser | -| ConnectionController | Widget | UIStyleAttributesConnectionController | -| GuestModePairingDialog | Widget | UIStyleAttributesGuestModePairingDialog | -| ▼ MediaControl | Group | UIStyleAttributesMediaControl | -| MiniController | Widget | UIStyleAttributesMiniController | -| ExpandedController | Widget | UIStyleAttributesExpandedController | -| TrackSelector | Widget | UIStyleAttributesTrackSelector | -| Instructions | Widget | UIStyleAttributesInstructions | - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://developers.google.com/cast/docs/ios_sender_integrate) to see original Google documentation._ - -[1]: https://developers.google.com/cast/docs/design_checklist/cast-button#prompting -[2]: https://developers.google.com/cast/docs/design_checklist/cast-button#sender-cast-icon-available -[3]: https://developers.google.com/cast/docs/design_checklist/sender#sender-mini-controller -[4]: https://developers.google.com/cast/docs/design_checklist/sender#sender-control-elements -[5]: https://developers.google.com/cast/docs/design_checklist/sender#sender-mini-controller -[6]: https://developers.google.com/cast/docs/design_checklist/sender#mini-controller -[7]: https://developers.google.com/cast/docs/android_sender_integrate#the_cast_ux_widgets -[8]: https://developer.apple.com/reference/uikit/uiappearance -[9]: https://developers.google.com/cast/downloads/GoogleCastAutoplayUXGuidelines.pdf -[10]: https://developers.google.com/cast/docs/styled_receiver -[11]: https://developers.google.com/cast/docs/receiver_apps#default -[12]: https://developers.google.com/cast/docs/autoplay +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Maps/Details.md b/docs/Google/Maps/Details.md index 5c8b5c610..f66b54936 100755 --- a/docs/Google/Maps/Details.md +++ b/docs/Google/Maps/Details.md @@ -1,62 +1,21 @@ -With the Google Maps SDK for iOS, you can add maps based on Google maps data to your application. The SDK automatically handles access to the Google Maps servers, map display, and response to user gestures such as clicks and drags. You can also add markers, polylines, ground overlays and info windows to your map. These objects provide additional information for map locations, and allow user interaction with the map. +# Google Maps -Showing a Map -============= +This path is retained for existing links, but this repository does not maintain a copied Google Maps walkthrough. -### AppDelegate +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -```csharp -using Google.Maps; -... +## Native Documentation -const string MapsApiKey = ""; +- Google Maps documentation: https://developers.google.com/maps/documentation/ios-sdk -public override bool FinishedLaunching (UIApplication app, NSDictionary options) -{ - MapServices.ProvideAPIKey (MapsApiKey); - ... -} -``` +## Binding Project -### Your View Controller +- Package ID: `AdamE.Google.iOS.Maps` +- Managed namespace: `Google.Maps` +- Status note: This binding project is present in the repository and is part of the current Google package set described by the top-level README. -```csharp -using Google.Maps; -... +## Binding Notes -MapView mapView; +The native `GMSServices.provideAPIKey` setup maps to `Google.Maps.MapServices.ProvideApiKey(apiKey)` in this binding. The managed namespace is `Google.Maps`. -public override void LoadView () -{ - base.LoadView (); - - CameraPosition camera = CameraPosition.FromCamera (latitude: 37.797865, - longitude: -122.402526, - zoom: 6); - mapView = MapView.FromCamera (CGRect.Empty, camera); - mapView.MyLocationEnabled = true; - - View = mapView; -} - -public override void ViewWillAppear (bool animated) -{ - base.ViewWillAppear (animated); - mapView.StartRendering (); -} - -public override void ViewWillDisappear (bool animated) -{ - mapView.StopRendering (); - base.ViewWillDisappear (animated); -} -``` - -Attribution Requirements -======================== - -If you use the Google Maps SDK for iOS in your application, you must include the attribution text as part of a legal notices section in your application. Including legal notices as an independent menu item, or as part of an "About" menu item, is recommended. - -You can get the attribution text by making a call to `Google.Maps.MapServices.OpenSourceLicenseInfo` - -*Screenshots assembled with [PlaceIt](http://placeit.breezi.com/).* +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Maps/GettingStarted.md b/docs/Google/Maps/GettingStarted.md index f25213cd3..f66b54936 100755 --- a/docs/Google/Maps/GettingStarted.md +++ b/docs/Google/Maps/GettingStarted.md @@ -1,112 +1,21 @@ -Showing a Map -============= +# Google Maps -### AppDelegate +This path is retained for existing links, but this repository does not maintain a copied Google Maps walkthrough. -```csharp -using Google.Maps; -... +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -const string MapsApiKey = ""; +## Native Documentation -public override bool FinishedLaunching (UIApplication app, NSDictionary options) -{ - MapServices.ProvideAPIKey (MapsApiKey); - ... -} -``` +- Google Maps documentation: https://developers.google.com/maps/documentation/ios-sdk -### Your View Controller +## Binding Project -```csharp -using Google.Maps; -... +- Package ID: `AdamE.Google.iOS.Maps` +- Managed namespace: `Google.Maps` +- Status note: This binding project is present in the repository and is part of the current Google package set described by the top-level README. -MapView mapView; +## Binding Notes -public override void LoadView () -{ - base.LoadView (); - - // Create a GMSCameraPosition that tells the map to display the - // coordinate 37.79,-122.40 at zoom level 6. - var camera = CameraPosition.FromCamera (latitude: 37.79, - longitude: -122.40, - zoom: 6); - mapView = MapView.FromCamera (CGRect.Empty, camera); - mapView.MyLocationEnabled = true; - View = mapView; -} -``` +The native `GMSServices.provideAPIKey` setup maps to `Google.Maps.MapServices.ProvideApiKey(apiKey)` in this binding. The managed namespace is `Google.Maps`. -Attribution Requirements -======================== - -If you use the Google Maps SDK for iOS in your application, you must include the attribution text as part of a legal notices section in your application. Including legal notices as an independent menu item, or as part of an "About" menu item, is recommended. - -You can get the attribution text by making a call to `Google.Maps.MapServices.OpenSourceLicenseInfo` - -Enable the required APIs on the Google Developers Console -=== - -You need to activate the Google Maps SDK for iOS, and optionally the Google Places API for iOS, in your project on the Google Developers Console. - -If you want to be guided through the process and activate both the APIs automatically, click [this link][1]. - -Alternatively, you can activate the APIs yourself in the Developers Console, by doing the following: - -- Go to the [Google Developers Console][2]. -- Select a project, or create a new one. -- Enable the **Google Maps SDK for iOS**, and optionally the **Google Places API for iOS**: [Open the API Library][3] in the Google Developers Console. If prompted, select a project or create a new one. Select the **Enabled APIs** link in the API section to see a list of all your enabled APIs. Make sure that the API is on the list of enabled APIs. If you have not enabled it, select the API from the list of APIs, then select the **Enable API** button for the API. - -The Google Maps API Key -======================= - -Using an API key enables you to monitor your application's Maps API usage, and ensures that Google can contact you about your application if necessary. The key is free, you can use it with any of your applications that call the Maps API, and it supports an unlimited number of users. You obtain a Maps API key from the Google APIs Console by providing your application's bundle identifier. Once you have the key, you add it to your AppDelegate as described in the next section. - -### Obtaining an API Key - -You can obtain a key for your app in the [Google APIs Console](https://code.google.com/apis/console/). - -1. Click on "Use Google APIs" blue card. -2. In the sidebar on the left, select Credentials. -3. If your project doesn't already have an iOS API key, create one now by selecting **Add credentials** > **API key** > **iOS key**. -4. In the resulting dialog, enter your app's bundle identifier, such as `com.example.myapp`. -5. Click **Create**. Your new iOS API key appears in the list of API keys for your project. -6. Add your API key to your `AppDelegate` class as follows: - -```csharp -using Google.Maps; -... - -[Register ("AppDelegate")] -public partial class AppDelegate : UIApplicationDelegate -{ - const string MapsApiKey = ""; - - public override bool FinishedLaunching (UIApplication app, NSDictionary options) - { - MapServices.ProvideAPIKey (MapsApiKey); - ... - } -} -``` - -Declare the URL schemes used by the API -=== - -With iOS 9, apps must declare the URL schemes that they intend to open, by specifying the schemes in the app's Info.plist file. The Google Maps SDK for iOS opens the Google Maps mobile app when the user clicks the Google logo on the map, and your app therefore needs to declare the relevant URL schemes. - -To declare the URL schemes used by the Google Maps SDK for iOS, add the following lines to your Info.plist: - -```xml -LSApplicationQueriesSchemes - - googlechromes - comgooglemaps - -``` - -[1]: https://console.developers.google.com/flows/enableapi?apiid=placesios,maps_ios_backend&keyType=CLIENT_SIDE_IOS -[2]: https://console.developers.google.com/ -[3]: https://console.developers.google.com/project/_/apiui/apis/library \ No newline at end of file +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Places/Details.md b/docs/Google/Places/Details.md index 72ebd4ed5..2f00f5d53 100755 --- a/docs/Google/Places/Details.md +++ b/docs/Google/Places/Details.md @@ -1,12 +1,21 @@ -Get data from the same database used by Google Maps. Places features over 100 million businesses and points of interest that are updated frequently through owner-verified listings and user-moderated contributions. +# Google Places -* **Place picker**: Add the built-in place picker UI widget to your app, so users can choose from a set of nearby places displayed on a map. -* **Place autocomplete**: Assist users by automatically completing the name and address of a place as they type. -* **Place details**: Retrieve rich details about a place, including name, address, phone number, website link and more. +This path is retained for existing links, but this repository does not maintain a copied Google Places walkthrough. -Attribution Requirements -======================== +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -If you use the Google Places SDK for iOS in your application, you must include the attribution text as part of a legal notices section in your application. Including legal notices as an independent menu item, or as part of an "About" menu item, is recommended. +## Native Documentation -You can get the attribution text by making a call to `Google.Places.PlacesClient.OpenSourceLicenseInfo` +- Google Places documentation: https://developers.google.com/maps/documentation/places/ios-sdk + +## Binding Project + +- Package ID: `AdamE.Google.iOS.Places` +- Managed namespace: `Google.Places` +- Status note: This binding project is present in the repository and is part of the current Google package set described by the top-level README. + +## Binding Notes + +The native `GMSPlacesClient.provideAPIKey` setup maps to `Google.Places.PlacesClient.ProvideApiKey(apiKey)` in this binding. The managed namespace is `Google.Places`. + +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/Places/GettingStarted.md b/docs/Google/Places/GettingStarted.md index e2d683a4a..2f00f5d53 100755 --- a/docs/Google/Places/GettingStarted.md +++ b/docs/Google/Places/GettingStarted.md @@ -1,1093 +1,21 @@ -# Get Started with the Places API for iOS +# Google Places -> _**Note**_: _The Google Places API for iOS enforces a default limit of 1,000 requests per 24 hour period. You can request an increase by following a simple review process. See the instructions in the [usage limits][1] guide._ +This path is retained for existing links, but this repository does not maintain a copied Google Places walkthrough. -## Add Google Places for iOS to your project +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -### Via Components Store +## Native Documentation -1. In your Visual Studio project, do double click on **Components** folder. -2. If needed, click on **Log in to your Xamarin Account** button and enter your credentials. -3. Click on **Get More Components** button -4. Search for **Google Places for iOS** and click on the result -5. Click on **Add to App** +- Google Places documentation: https://developers.google.com/maps/documentation/places/ios-sdk -### Via NuGet +## Binding Project -1. In your Visual Studio project, do double click on **Packages** folder. -2. Search for **Xamarin.Google.iOS.Places** -3. Do double click on the result to install it or check the result and click on **Add Package** button +- Package ID: `AdamE.Google.iOS.Places` +- Managed namespace: `Google.Places` +- Status note: This binding project is present in the repository and is part of the current Google package set described by the top-level README. -### Get an API key +## Binding Notes -To get started using the Google Places API for iOS, follow these steps to get an API key: +The native `GMSPlacesClient.provideAPIKey` setup maps to `Google.Places.PlacesClient.ProvideApiKey(apiKey)` in this binding. The managed namespace is `Google.Places`. -1. Go to the [Google API Console][2]. -2. Create or select a project. -3. Click **Continue** to enable the Google Places API for iOS. -4. On the **Credentials** page, get an **API key**. - - Note: If you have a key with iOS restrictions, you may use that key. You can use the same key with any of your iOS applications within the same project. - -5. From the dialog displaying the API key, select **Restrict key** to set an iOS restriction on the API key. -6. In the **Restrictions** section, select **iOS apps**, then enter your app's bundle identifier. For example: `com.example.helloplaces`. -7. Click **Save**. - -Your new iOS key appears in the list of API keys for your project. An API key is a string of characters, something like this: - -``` -AaAaAaAaAa-AaAaAaAaAaAaAaAaAaAaAaAaAaAa -``` - -You can also [look up an existing key][3] in the Google API Console. - -For more information on using the Google API Console, see [API Console Help][4]. - -### Add the API key to your application - -The following code examples show how to add the API key to an application: - -* Add the following namespace statement: - - ```csharp - using Google.Places; - ``` - -* Add the following to your `FinishedLaunching` method, replacing `YOUR_API_KEY` with your API key: - - ```csharp - PlacesClient.ProvideApiKey (YOUR_API_KEY); - ``` - -* If you are also using the Place Picker, you will need to add the Google.Maps components or NuGet and add your key again as shown here: - - ```csharp - using Google.Maps; - - MapServices.ProvideAPIKey (YOUR_API_KEY); - ``` - ---- - -# Place Picker - -> **Deprecation notice: `PlacePicker` class** -> -> _**Notice**_: _The implementation of the place picker has changed. As of version 2.3 of the Google Places API for iOS, the `PlacePicker` class is deprecated, replaced by `PlacePickerViewController` class. Use of the `PlacePicker` class will only be supported until May 1, 2018. We recommend that you update your code to use `PlacePickerViewController` when possible._ - -The place picker is a simple and yet flexible built-in UI widget, part of the Google Places API for iOS. - -## Introducing the place picker - -The `PlacePickerViewController` class provides a UI that displays an interactive map and a list of nearby places, including places corresponding to geographical addresses and local businesses. Users can choose a place, and your app can then retrieve the details of the selected place. - -The place picker provides the following advantages over developing your own UI widget: - -* The user experience is consistent with other apps using the place picker, including Google apps and third parties. This means users of your app already know how to interact with the place picker. -* The map is integrated into the place picker. -* Accessibility is built in. -* It saves development time. - -The place picker features autocomplete functionality, which displays place predictions based on user search input. This functionality is present in all place picker integrations, so you don't need to do anything extra to enable autocomplete. For more information about autocomplete, go to [Place Autocomplete](#place-autocomplete) section below. - -## Request location authorization - -If your app uses the place picker, you must request permission to use location services. First add one or both of the following keys to your **Info.plist** file, to request 'when in use' or 'always' authorization: - -* `NSLocationWhenInUseUsageDescription` -* `NSLocationAlwaysUsageDescription` - -For the place picker, it's enough to request 'when in use' authorization, but you may want to request 'always' authorization for other functionality in your app. For each key, add a string informing the user why you need the location services. For example: - -```xml -NSLocationWhenInUseUsageDescription -Show your location on the map -``` - -## Add a place picker - -The code snippet below shows how to create a `PlacePickerViewController` instance centered at the current device location, and display details of the selected place: - -```csharp -public partial class ViewController : UIViewController, IPlacePickerViewControllerDelegate -{ - partial void BtnPickPlace_TouchUpInside (UIButton sender) - { - var config = new PlacePickerConfig (null); - placePickerViewController = new PlacePickerViewController (config) { Delegate = this }; - PresentViewController (placePickerViewController, true, null); - } - - // To receive the results from the place picker 'self' will need to conform to - // IPlacePickerViewControllerDelegate interface and implement this code. - public void DidPickPlace (PlacePickerViewController viewController, Place place) - { - // Dismiss the place picker, as it cannot dismiss itself. - DismissViewController (true, null); - - Console.WriteLine ($"Place name: {place.Name}"); - Console.WriteLine ($"Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Place attributions: {place.Attributions}"); - } - - [Export ("placePickerDidCancel:")] - void DidCancel (PlacePickerViewController viewController) - { - // Dismiss the place picker, as it cannot dismiss itself. - DismissViewController (true, null); - Console.WriteLine ("No place selected"); - } -} -``` - -As shown in the above code sample, you can initalize the place picker with a given configuration using a `PlacePickerConfig` object. If a value of `null` is assigned to the viewport, the place picker will center on the current device location. To center on a specific location, specify a **viewport** containing a `CoordinateBounds` object defining the initial rectangular map area to display. The following example shows creating a `CoordinateBounds` to center the map on Sydney, Australia: - -```csharp -var center = new CLLocationCoordinate2D (-33.865143, 151.2099); -var northEast = new CLLocationCoordinate2D (center.Latitude + .001, center.Longitude + .001); -var southWest = new CLLocationCoordinate2D (center.Latitude - .001, center.Longitude - .001); -var viewport = new Google.Maps.CoordinateBounds (northEast, southWest); -var config = new PlacePickerConfig (viewport); -``` - -To be notified when the user selects a place you must set the delegate of the place picker to be an object implementing `IPlacePickerViewControllerDelegate` interface. When you do this your app will receive a callback to the `DidPickPlace` method implemented by the delegate and is passed the place the user selected. If the user did not select a place, `DidCancel` method is called instead. - -As the place picker is a normal view controller it can be displayed any way you want. For example, in a popover, fullscreen, pushed onto a navigation stack, or even as part of a custom app UI. Because of this flexibility the place picker is unable to dismiss itself, so your app must programmatically dismiss it when `DidPickPlace` is called. In some cases it may be necessary to dismiss the place picker from `DidCancel` as well. - -## Display attributions in your app - -When your app displays information obtained via the place picker, the app must also display attributions. See this [Attribution's documentation][8] to learn more about this. - -## Old Client Libraries - -Prior to version 2.3 of the Google Places API for iOS `PlacePicker` was the only available way to use the place picker. This class has now been deprecated and it is recommended that `PlacePickerViewController` is used instead. This new class is more flexible and is not limited to being displayed full-screen. - ---- - -# Current Place - -Using the Google Places API for iOS, you can discover the place where the device is currently located. That is, the place at the device's currently-reported location. Examples of places include local businesses, points of interest, and geographic locations. - -## Request location authorization - -If your app uses `PlacesClient` `CurrentPlace` instance method, you must request permission to use location services. Add the `NSLocationWhenInUseUsageDescription` key to your **Info.plist** file, to define the string informing the user why you need the location services. For example: - -```xml -NSLocationWhenInUseUsageDescription -Show your location on the map -``` - -If you want to call `PlacesClient` `CurrentPlace` instance method when the app is in the background, without triggering a confirmation dialog, take the following steps prior to making the call: - -1. Add the `NSLocationAlwaysUsageDescription` key to your **Info.plist** file. -2. Call `RequestAlwaysAuthorization` on any instance of `CLLocationManager` before calling the method. - -Request authorization from `CLLocationManager` as follows: - -```csharp -locationManager.RequestAlwaysAuthorization (); -``` - -## Usage limits - -Use of the `PlacesClient` `CurrentPlace` instance method is subject to tiered query limits. See the documentation on [usage limits][1]. - -## Get the current location - -To find the local business or other place where the device is currently located, call `PlacesClient.CurrentPlace`. Include a callback method to handle the results. - -The API invokes the specified callback method, passing in an array of `PlaceLikelihood` objects. - -Each `PlaceLikelihood` object represents a place. For each place, the result includes an indication of the likelihood that the place is the right one. A higher value means a greater probability that the place is the best match. The buffer may be empty, if there is no known place corresponding to the device location. - -The following code sample retrieves the list of places where the device is most likely to be located, and logs the name, likelihood, and other details for each place: - -```csharp -placesClient.CurrentPlace ((likelihoodList, error) => { - if (error != null) { - Console.WriteLine ($"Pick Place error: {error.LocalizedDescription}"); - return; - } - - foreach (var likelihood in likelihoodList?.Likelihoods) { - var place = likelihood.Place; - Console.WriteLine ($"Current Place name: {place.Name} at likelihood: {likelihood.Likelihood}"); - Console.WriteLine ($"Current Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Current Place attributions: {place.Attributions}"); - Console.WriteLine ($"Current Place Id: {place.Id}"); - } -}); -``` - -Notes about the likelihood values: - -* The likelihood provides a relative probability of the place being the best match within the list of returned places for a single request. You can't compare likelihoods across different requests. -* The value of the likelihood will be between 0 and 1.0. -* The sum of the likelihoods in a returned array of `PlaceLikelihood` objects is always less than or equal to 1.0. Note that the sum isn't necessarily 1.0. - -For example, to represent a 55% likelihood that the correct place is Place A, and a 35% likelihood that it's Place B, the likelihood array has two members: Place A with a likelihood of 0.55 and Place B with a likelihood of 0.35. - -## Display attributions in your app - -When your app displays information obtained from `PlacesClient` `CurrentPlace` instance method, the app must also display attributions. See this [Attribution's documentation][8] to learn more about this. - ---- - -# Place Autocomplete - -The autocomplete service in the Google Places API for iOS returns place predictions in response to user search queries. As the user types, the autocomplete service returns suggestions for places such as businesses, addresses and points of interest. - -You can add autocomplete to your app in the following ways: - -* Add an autocomplete UI control to save development time and ensure a consistent user experience. -* Get place predictions programmatically to create a customized user experience. - -## Add an autocomplete UI control - -The autocomplete UI control is a search dialog with built-in autocomplete functionality. As a user enters search terms, the control presents a list of predicted places to choose from. When the user makes a selection, a `Place` instance is returned, which your app can then use to get details about the selected place. - -You can add the autocomplete UI control to your app in the following ways: - -* Add a full-screen control -* Add a results controller -* Use a table data source - -### Add a full-screen control - -Use the full-screen control when you want a modal context, where the autocomplete UI temporarily replaces the UI of your app until the user has made their selection. This functionality is provided by the `AutocompleteViewController` class. When the user selects a place, your app receives a callback. - -To add a full-screen control to your app: - -* Create a UI element in your main app to launch the autocomplete UI control, for example a touch handler on a `UIButton`. -* Implement the `IAutocompleteViewControllerDelegate` interface in the parent view controller. -* Create an instance of `AutocompleteViewController` and assign the parent view controller as the `Delegate` property. -* Present the `AutocompleteViewController` using `PresentViewController` method. -* Handle the user's selection in the `DidAutocomplete` interface method. -* Dismiss the controller in the `DidAutocomplete`, `DidFailAutocomplete`, and `WasCancelled` interface methods. - -The following example demonstrates one possible way to launch `AutocompleteViewController` in response to the user tapping on a button: - -```csharp -using UIKit; -using Foundation; -using Google.Places; - -public partial class ViewController : UIViewController, IAutocompleteViewControllerDelegate -{ - partial void BtnShow_TouchUpInside (UIButton sender) - { - var autocompleteViewController = new AutocompleteViewController { Delegate = this }; - PresentViewController (autocompleteViewController, true, null); - } - - #region AutocompleteViewController Delegate - - public void DidAutocomplete (AutocompleteViewController viewController, Place place) - { - // Handle the user's selection. - DismissViewController (true, null); - Console.WriteLine ($"Place name: {place.Name}"); - Console.WriteLine ($"Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Place attributions: {place.Attributions}"); - } - - public void DidFailAutocomplete (AutocompleteViewController viewController, NSError error) - { - // TODO: handle the error. - DismissViewController (true, null); - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - } - - // User canceled the operation. - public void WasCancelled (AutocompleteViewController viewController) - { - DismissViewController (true, null); - } - - // Turn the network activity indicator on and off again. - [Export ("didRequestAutocompletePredictions:")] - public void DidRequestAutocompletePredictions (AutocompleteViewController viewController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true; - } - - [Export ("didUpdateAutocompletePredictions:")] - public void DidUpdateAutocompletePredictions (AutocompleteViewController viewController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false; - } - - #endregion -} -``` - -### Add a results controller - -Use a results controller when you want more control over the text input UI. The results controller dynamically toggles the visibility of the results list based on input UI focus. - -To add a results controller to your app: - -1. Create a `AutocompleteResultsViewController`. -2. Implement the `IAutocompleteResultsViewControllerDelegate` interface in the parent view controller and assign the parent view controller as the `Delegate` property. -3. Create a `UISearchController` object, passing in the `AutocompleteResultsViewController` as the results controller argument. -4. Set the `AutocompleteResultsViewController` as the `SearchResultsUpdater` property of the `UISearchController`. -5. Add the `SearchBar` for the `UISearchController` to your app's UI. -6. Handle the user's selection in the `DidAutocomplete` interface method. - -There are several ways to place the search bar of a `UISearchController` into your app's UI: - -* Add a search bar to the navigation bar -* Add a search bar to the top of a view -* Add a search bar using popover results - -#### Add a search bar to the navigation bar - -The following code example demonstrates adding a results controller, adding the `SearchBar` to the navigation bar, and handling the user's selection: - -```csharp -using UIKit; -using Foundation; -using Google.Places; - -public partial class ViewController : UIViewController, IAutocompleteResultsViewControllerDelegate -{ - UISearchController searchController; - AutocompleteResultsViewController autocompleteResultsViewController; - - public override void ViewDidLoad () - { - base.ViewDidLoad (); - - autocompleteResultsViewController = new AutocompleteResultsViewController { Delegate = this }; - searchController = new UISearchController (autocompleteResultsViewController) { - // Prevent the navigation bar from being hidden when searching. - HidesNavigationBarDuringPresentation = false, - SearchResultsUpdater = autocompleteResultsViewController - }; - - // Put the search bar in the navigation bar. - searchController.SearchBar.SizeToFit (); - NavigationItem.TitleView = searchController.SearchBar; - - // When UISearchController presents the results view, present it in - // this view controller, not one further up the chain. - DefinesPresentationContext = true; - } - - #region AutocompleteResultsViewController Delegate - - // Handle the user's selection. - public void DidAutocomplete (AutocompleteResultsViewController resultsController, Place place) - { - searchController.Active = false; - - // Do something with the selected place. - Console.WriteLine ($"Place name: {place.Name}"); - Console.WriteLine ($"Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Place attributions: {place.Attributions}"); - } - - public void DidFailAutocomplete (AutocompleteResultsViewController resultsController, NSError error) - { - searchController.Active = false; - - // TODO: handle the error. - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - } - - // Turn the network activity indicator on and off again. - [Export ("didRequestAutocompletePredictionsForResultsController:")] - public void DidRequestAutocompletePredictions (AutocompleteResultsViewController resultsController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true; - } - - [Export ("didUpdateAutocompletePredictionsForResultsController:")] - public void DidUpdateAutocompletePredictions (AutocompleteResultsViewController resultsController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false; - } - - #endregion -} -``` - -> _**Note:**_ _For the search bar to display properly, your app's view controller must be enclosed within a `UINavigationController`._ - -#### Add a search bar to the top of a view - -The following code example shows adding the `SearchBar` to the top of a view: - -```csharp -using UIKit; -using Foundation; -using CoreGraphics; -using Google.Places; - -public partial class ViewController : UIViewController, IAutocompleteResultsViewControllerDelegate -{ - UISearchController searchController; - AutocompleteResultsViewController autocompleteResultsViewController; - - public override void ViewDidLoad () - { - base.ViewDidLoad (); - - autocompleteResultsViewController = new AutocompleteResultsViewController { Delegate = this }; - searchController = new UISearchController (autocompleteResultsViewController) { - // Prevent the navigation bar from being hidden when searching. - HidesNavigationBarDuringPresentation = false, - SearchResultsUpdater = autocompleteResultsViewController - }; - - var subview = new UIView (new CGRect (0, 65, 350, 45)); - subview.AddSubview (searchController.SearchBar); - View.AddSubview (subview); - searchController.SearchBar.SizeToFit (); - - // When UISearchController presents the results view, present it in - // this view controller, not one further up the chain. - DefinesPresentationContext = true; - } - - #region AutocompleteResultsViewController Delegate - - // Handle the user's selection. - public void DidAutocomplete (AutocompleteResultsViewController resultsController, Place place) - { - searchController.Active = false; - - // Do something with the selected place. - Console.WriteLine ($"Place name: {place.Name}"); - Console.WriteLine ($"Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Place attributions: {place.Attributions}"); - } - - public void DidFailAutocomplete (AutocompleteResultsViewController resultsController, NSError error) - { - searchController.Active = false; - - // TODO: handle the error. - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - } - - // Turn the network activity indicator on and off again. - [Export ("didRequestAutocompletePredictionsForResultsController:")] - public void DidRequestAutocompletePredictions (AutocompleteResultsViewController resultsController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true; - } - - [Export ("didUpdateAutocompletePredictionsForResultsController:")] - public void DidUpdateAutocompletePredictions (AutocompleteResultsViewController resultsController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false; - } - - #endregion -} -``` - -By default, `UISearchController` hides the navigation bar when presenting (this can be disabled). In cases where the navigation bar is visible and opaque, `UISearchController` does not set the placement correctly. Use the following code as a workaround: - -```csharp -NavigationController.NavigationBar.Translucent = false; -searchController.HidesNavigationBarDuringPresentation = false; - -// This makes the view area include the nav bar even though it is opaque. -// Adjust the view placement down. -ExtendedLayoutIncludesOpaqueBars = true; -EdgesForExtendedLayout = UIRectEdge.Top; -``` - -#### Add a search bar using popover results - -The following code example shows placing a search bar on the right side of the navigation bar, and displaying results in a popover: - -> _**Caution:**_ _Popovers will only appear on iPad, and on the iPhone 6+ in landscape mode. On all other devices this will fallback to a fullscreen view controller._ - -```csharp -```csharp -using UIKit; -using Foundation; -using CoreGraphics; -using Google.Places; - -public partial class ViewController : UIViewController, IAutocompleteResultsViewControllerDelegate -{ - UISearchController searchController; - AutocompleteResultsViewController autocompleteResultsViewController; - - public override void ViewDidLoad () - { - base.ViewDidLoad (); - - autocompleteResultsViewController = new AutocompleteResultsViewController { Delegate = this }; - searchController = new UISearchController (autocompleteResultsViewController) { - // Prevent the navigation bar from being hidden when searching. - HidesNavigationBarDuringPresentation = false, - ModalPresentationStyle = UIModalPresentationStyle.Popover, - SearchResultsUpdater = autocompleteResultsViewController - }; - - // Add the search bar to the right of the nav bar, - // use a popover to display the results. - // Set an explicit size as we don't want to use the entire nav bar. - searchController.SearchBar.Frame = new CGRect (0, 0, 250, 44); - NavigationItem.RightBarButtonItem = new UIBarButtonItem (searchController.SearchBar); - - // When UISearchController presents the results view, present it in - // this view controller, not one further up the chain. - DefinesPresentationContext = true; - } - - #region AutocompleteResultsViewController Delegate - - // Handle the user's selection. - public void DidAutocomplete (AutocompleteResultsViewController resultsController, Place place) - { - searchController.Active = false; - - // Do something with the selected place. - Console.WriteLine ($"Place name: {place.Name}"); - Console.WriteLine ($"Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Place attributions: {place.Attributions}"); - } - - public void DidFailAutocomplete (AutocompleteResultsViewController resultsController, NSError error) - { - searchController.Active = false; - - // TODO: handle the error. - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - } - - // Turn the network activity indicator on and off again. - [Export ("didRequestAutocompletePredictionsForResultsController:")] - public void DidRequestAutocompletePredictions (AutocompleteResultsViewController resultsController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true; - } - - [Export ("didUpdateAutocompletePredictionsForResultsController:")] - public void DidUpdateAutocompletePredictions (AutocompleteResultsViewController resultsController) - { - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false; - } - - #endregion -} -``` - -> _**Note:**_ _For the search bar to display properly, your app's view controller must be enclosed within a `UINavigationController`._ - -### Use a table data source - -If your app must support iOS 7, you can use the `AutocompleteTableDataSource` class to drive the table view of a `UISearchDisplayController`. - -* The use of `UISearchDisplayController` is recommended only for iOS 7 support. We recommend using `UISearchController` in all other cases. -* The full-screen control is also supported in iOS 7. - -To use `AutocompleteTableDataSource` to display a search controller: - -1. Implement the `IAutocompleteTableDataSourceDelegate` and `IUISearchDisplayDelegate` interfaces in the parent view controller. -2. Create a `AutocompleteTableDataSource` instance and assign the parent view controller as the `Delegate` property. -3. Create an instance of `UISearchDisplayController`. -4. Add the `SearchBar` for the `UISearchController` to your app's UI. -5. Handle the user's selection in the `DidAutocomplete` interface method. -6. Dismiss the controller in the `DidAutocomplete`, `DidFailAutocomplete`, and `WasCancelled` interface methods. - -The following code example demonstrates using the `AutocompleteTableDataSource` class to drive the table view of a `UISearchDisplayController`. - -```csharp -using UIKit; -using Foundation; -using CoreGraphics; -using Google.Places; - -public partial class ViewController : UIViewController, IAutocompleteTableDataSourceDelegate, IUISearchDisplayDelegate -{ - UISearchBar searchBar; - AutocompleteTableDataSource autocompleteTableDataSource; - UISearchDisplayController searchDisplayController; - - public override void ViewDidLoad () - { - base.ViewDidLoad (); - // Perform any additional setup after loading the view, typically from a nib. - - searchBar = new UISearchBar (new CGRect (0, 0, 250, 44)); - autocompleteTableDataSource = new AutocompleteTableDataSource { Delegate = this }; - searchDisplayController = new UISearchDisplayController (searchBar, this) { - SearchResultsDataSource = autocompleteTableDataSource, - SearchResultsDelegate = autocompleteTableDataSource, - Delegate = this - }; - - View.AddSubview (searchBar); - } - - #region UISearchDisplay Delegate - - [Export ("searchDisplayController:shouldReloadTableForSearchString:")] - public bool ShouldReloadForSearchString (UISearchDisplayController controller, string forSearchString) - { - return false; - } - - #endregion - - #region AutocompleteTable Data Source Delegate - - public void DidAutocomplete (AutocompleteTableDataSource tableDataSource, Place place) - { - searchDisplayController.Active = false; - - // Do something with the selected place. - Console.WriteLine ($"Place name: {place.Name}"); - Console.WriteLine ($"Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Place attributions: {place.Attributions}"); - } - - public void DidFailAutocomplete (AutocompleteTableDataSource tableDataSource, NSError error) - { - searchDisplayController.Active = false; - - // TODO: handle the error. - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - } - - [Export ("tableDataSource:didSelectPrediction:")] - public bool DidSelectPrediction (AutocompleteTableDataSource tableDataSource, AutocompletePrediction prediction) - { - return true; - } - - [Export ("didRequestAutocompletePredictionsForTableDataSource:")] - public void DidRequestAutocompletePredictions (AutocompleteTableDataSource tableDataSource) - { - // Turn the network activity indicator on. - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true; - - // Reload table data. - searchDisplayController.SearchResultsTableView.ReloadData (); - } - - [Export ("didUpdateAutocompletePredictionsForTableDataSource:")] - public void DidUpdateAutocompletePredictions (AutocompleteTableDataSource tableDataSource) - { - // Turn the network activity indicator off. - UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false; - - // Reload table data. - searchDisplayController.SearchResultsTableView.ReloadData (); - } - - #endregion -} -``` - -> _**Note:**_ _The `UISearchDisplayController` API does not support the concept of asynchronous data updates, so it is necessary to force table updates by reloading table data in the `DidUpdateAutocompletePredictions` and `DidRequestAutocompletePredictions` methods of the `IAutoCompleteResultsDelegate` interface._ - -## Get place predictions programmatically - -You can create a custom search UI as an alternative to the UI provided by the autocomplete widget. To do this, your app must get place predictions programmatically. Your app can get a list of predicted place names and/or addresses in one of the following ways: - -* Call `PlacesClient` -* Use the fetcher - -### Call PlacesClient - -To get a list of predicted place names and/or addresses, call the `PlacesClient` `Autocomplete` instance method with the following parameters: - -* An `autocompleteQuery` string containing the text typed by the user. -* A `CoordinateBounds` object biasing the results to a specific area specified by latitude and longitude bounds. -* A `AutocompleteFilter` restricting the results to a specific type of place. To retrieve all types, supply a value of `null`. The following place types are supported in the filter: - * `PlacesAutocompleteTypeFilter.NoFilter` – An empty filter; all results are returned. - * `PlacesAutocompleteTypeFilter.Geocode` – Returns only geocoding results, rather than businesses. Use this request to disambiguate results where the specified location may be indeterminate. - * `PlacesAutocompleteTypeFilter.Address` – Returns only autocomplete results with a precise address. Use this type when you know the user is looking for a fully specified address. - * `PlacesAutocompleteTypeFilter.Establishment` – Returns only places that are businesses. - * `PlacesAutocompleteTypeFilter.Region` – Returns only places that match one of the following types: - * locality - * sublocality - * postal_code - * country - * administrative_area_level_1 - * administrative_area_level_2 - * `PlacesAutocompleteTypeFilter.City` – Returns only results matching `locality` or `administrative_area_level_3`. - -For more information, see [place types][5]. - -* A callback method to handle the returned predictions. - -The code examples below show a simplified call to `Autocomplete` method: - -```csharp -void PlaceAutocomplete () -{ - var filter = new AutocompleteFilter { Type = PlacesAutocompleteTypeFilter.Establishment }; - PlacesClient.SharedInstance.Autocomplete ("Sydney Oper", null, filter, AutocompletePredictionsHandler); - - void AutocompletePredictionsHandler (AutocompletePrediction [] results, NSError error) - { - if (error != null) { - Console.WriteLine ($"Autocomplete error: {error.LocalizedDescription}"); - return; - } - - foreach (var result in results) - Console.WriteLine ($"Result: {result.AttributedFullText} with Place Id: {result.PlaceId}"); - } -} -``` - -The API invokes the specified callback method, passing in an array of `AutocompletePrediction` objects. - -Each `AutocompletePrediction` object contains the following information: - -* `AttributedFullText` – The full text of the prediction, in the form of an `NSAttributedString`. For example, 'Sydney Opera House, Sydney, New South Wales, Australia'. Every text range that matches the user input has an attribute, `AutocompleteMatchAttribute`. You can use this attribute to highlight the matching text in the user's query, for example, as shown below. -* `PlaceId` – The place ID of the predicted place. A place ID is a textual identifier that uniquely identifies a place. For more information about place IDs, see the [Place ID overview][6]. - -The following code example illustrates how to highlight in bold text the parts of the result that match text in the user's query, using EnumerateAttribute: - -```csharp -var regularFont = UIFont.SystemFontOfSize (UIFont.LabelFontSize); -var boldFont = UIFont.BoldSystemFontOfSize (UIFont.LabelFontSize); - -var bolded = prediction.AttributedFullText.MutableCopy () as NSMutableAttributedString; -bolded.EnumerateAttribute (AutocompletePrediction.AutocompleteMatchAttribute, new NSRange (0, bolded.Length), 0, EnumerateAttributeCallback); -label.AttributedText = bolded; - -void EnumerateAttributeCallback (NSObject value, NSRange range, ref bool stop) -{ - var font = value == null ? regularFont : boldFont; - bolded.AddAttribute (UIStringAttributeKey.Font, font, range); -} -``` - -### Use the fetcher - -If you want to build your own autocomplete control from scratch, you can use `AutocompleteFetcher`, which wraps the `Autocomplete` method on `PlacesClient` class. The fetcher throttles requests, returning only results for the most recently entered search text. It provides no UI elements. - -To implement `AutocompleteFetcher`, take the following steps: - -1. Implement the `IAutocompleteFetcherDelegate` interface. -2. Create a `AutocompleteFetcher` object. -3. Call `SourceTextHasChanged` on the fetcher as the user types. -4. Handle predictions and errors using the `DidAutcomplete` and `DidFailAutocomplete` interface methods. - -The following code example demonstrates using the fetcher to take user input and display place matches in a text view. Functionality for selecting a place has been omitted. `FetcherSampleViewController` derives from `UIViewController` in FetcherSampleViewController.h. - -```csharp -using UIKit; -using Foundation; -using CoreLocation; -using CoreGraphics; -using Google.Places; -using Google.Maps; - -public partial class FetcherSampleViewController : UIViewController, IAutocompleteFetcherDelegate -{ - UITextField textField; - UITextView resultText; - AutocompleteFetcher fetcher; - - public override void ViewDidLoad () - { - base.ViewDidLoad (); - // Perform any additional setup after loading the view, typically from a nib. - - View.BackgroundColor = UIColor.White; - EdgesForExtendedLayout = UIRectEdge.All; - - // Set bounds to inner-west Sydney Australia. - var neBoundsCorner = new CLLocationCoordinate2D (-33.843366, 151.134002); - var swBoundsCorner = new CLLocationCoordinate2D (-33.875725, 151.200349); - var bounds = new CoordinateBounds (neBoundsCorner, swBoundsCorner); - - // Set up the autocomplete filter. - var filter = new AutocompleteFilter { Type = PlacesAutocompleteTypeFilter.Establishment }; - - // Create the fetcher. - fetcher = new AutocompleteFetcher (bounds, filter) { Delegate = this }; - - textField = new UITextField (new CGRect (5, 10, View.Bounds.Width - 5, 44)) { - AutoresizingMask = UIViewAutoresizing.FlexibleWidth - }; - textField.ValueChanged += TextField_ValueChanged; - - resultText = new UITextView (new CGRect (0, 45, View.Bounds.Width, View.Bounds.Height - 45)) { - BackgroundColor = UIColor.FromWhiteAlpha (.95f, 1), - Text = "No Results", - Editable = false - }; - - View.AddSubview (textField); - View.AddSubview (resultText); - } - - void TextField_ValueChanged (object sender, EventArgs e) - { - fetcher.SourceTextHasChanged (textField.Text); - } - - #region AutocompleteFetcher Delegate - - public void DidAutocomplete (AutocompletePrediction [] predictions) - { - var results = string.Empty; - foreach (var prediction in predictions) - results += prediction.AttributedPrimaryText.Value; - resultText.Text = results; - } - - public void DidFailAutocomplete (NSError error) - { - resultText.Text = error.LocalizedDescription; - } - - #endregion -} -``` - -## Set the CoordinateBounds of autocomplete - -You can supply a `CoordinateBounds` to autocomplete to guide the supplied completions. For example, if you already have a Google Map in your view controller, you can use the bounds of the current viewport to bias autocomplete results: - -```csharp -void PlaceAutocomplete () -{ - var visibleRegion = mapView.Projection.VisibleRegion; - var bounds = new Google.Maps.CoordinateBounds (visibleRegion.FarLeft, visibleRegion.NearRight); - - var filter = new AutocompleteFilter { Type = PlacesAutocompleteTypeFilter.Establishment }; - PlacesClient.SharedInstance.Autocomplete ("Sydney Oper", bounds, filter, AutocompletePredictionsHandler); - - void AutocompletePredictionsHandler (AutocompletePrediction [] results, NSError error) - { - if (error != null) { - Console.WriteLine ($"Autocomplete error: {error.LocalizedDescription}"); - return; - } - - foreach (var result in results) - Console.WriteLine ($"Result: {result.AttributedFullText} with Place Id: {result.PlaceId}"); - } -} -``` - -## Usage limits - -Use of the `PlacesClient` `Autocomplete` instance method is subject to tiered query limits. See the documentation on [usage limits][1]. - -## Display attributions in your app - -* If your app uses the autocomplete service programmatically, your UI must either display a 'Powered by Google' attribution, or appear within a Google-branded map. -* If your app uses the autocomplete UI control no additional action is required (the required attribution is displayed by default). -* If you retrieve and display additional place information after getting a place by ID, you must display third-party attributions too. - -See this [Attribution's documentation][8] to learn more about this. - -## Controlling the network activity indicator - -To control the network activity indicator in the applications status bar you must implement the appropriate optional delegate methods for the autocomplete class you are using and turn the network indicator on and off yourself. - -* For `AutocompleteViewController` you must implement the interface methods `DidRequestAutocompletePredictions` and `DidUpdateAutocompletePredictions`. -* For `AutocompleteResultsViewController` you must implement the interface methods `DidRequestAutocompletePredictions` and `DidUpdateAutocompletePredictions`. -* For `AutocompleteTableDataSource` you must implement the delegate methods `DidRequestAutocompletePredictions` and `DidUpdateAutocompletePredictions`. - -By implementing these methods and setting `UIApplication.SharedApplication.NetworkActivityIndicatorVisible` to `true` and `false` respectively the status bar will correctly match the autocomplete UI. - -## Troubleshooting - -Although a wide variety of errors can occur, the majority of the errors your app is likely to experience are usually caused by configuration errors (for example, the wrong API key was used, or the API key was configured incorrectly), or quota errors (your app has exceeded its quota). See [Usage Limits][1] for more information about quotas. - -Errors that occur in the use of the autocomplete controls are returned in the `DidFailAutocomplete` method of the various interface protocols. The code property of the supplied `NSError` object is set to one of the values of the `PlacesErrorCode` enumeration. - ---- - -# Place Photos - -You can use the Google Places API for iOS to request place photos to display in your application. Photos returned by the photos service come from a variety of sources, including business owners and user-contributed photos. To retrieve images for a place, you must take the following steps: - -1. Call `PlacesClient` `LookUpPhotos` instance method, passing a string with a place ID and a callback. This will call the callback with a `PlacePhotoMetadataList` object. -2. On the `PlacePhotoMetadataList` object access the `Results` property and select the photos to load from the array. -3. For each `PlacePhotoMetadata` to load from this list call `PlacesClient` `LoadPlacePhoto` instance method. These will call the callback with a usable UIImage. - -> _**Note:**_ _Whenever you display a place photo make sure that the attribution guidelines are being followed. See this [Attribution's documentation][8] to learn more about this._ - -## Sample code - -The following example method takes a place ID and gets the first photo in the returned list. You can use this method as a template for the method you will create in your own app: - -```csharp -void LoadFirstPlacePhoto (string placeId) -{ - PlacesClient.SharedInstance.LookUpPhotos (placeId, PlacePhotoMetadataResultHandler); - - void PlacePhotoMetadataResultHandler (PlacePhotoMetadataList photos, NSError error) - { - if (error != null) { - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - return; - } - - var firstPhoto = photos.Results.FirstOrDefault (); - if (firstPhoto != null) - LoadImage (firstPhoto); - } -} - -void LoadImage (PlacePhotoMetadata photoMetadata) -{ - PlacesClient.SharedInstance.LoadPlacePhoto (photoMetadata, PlacePhotoImageResultHandler); - - void PlacePhotoImageResultHandler (UIImage photo, NSError error) - { - if (error != null) { - Console.WriteLine ($"Error: {error.LocalizedDescription}"); - return; - } - - imageView.Image = photo; - attributionTextView.AttributedText = photoMetadata.Attributions; - } -} -``` - -## Caching - -Photos loaded using `PlacesClient` `LoadPlacePhoto` instance method are cached both on disk and in-memory by the [Foundation URL loading system][7] in the shared `NSUrlCache`. - -To configure the caching behavior you can change the shared URL cache using `NSUrlCache.SharedCache` in your application delegate's `FinishedLaunching` method. - -If you do not want your application to share a `NSUrlCache` with the Google Places API for iOS you can create a new `NSUrlCache` and use this exclusively within your app without setting it as the shared cache. - -## Attributions - -In most cases, place photos can be used without attribution, or will have the required attribution included as part of the image. However, if the returned `PlacePhotoMetadata` instance includes an attribution, you must include the additional attribution in your application wherever you display the image. Note that links in the attribution must be tappable. See this [Attribution's documentation][8] to learn more about this. - -## Usage limits - -Retrieving an image costs one unit of quota; there are no usage limits for retrieving photo metadata. See the documentation on [usage limits][1]. - ---- - -# Place Report - -An app can report that the device is currently located at a particular place. By reporting places that users have confirmed, you can help Google build a local model of the world. You should report that a device is at a place only if you're confident that the user is at the place, at the time when you report it. - -To indicate that a device is located at a specific place, call `PlacesClient` `ReportDeviceAtPlace` instance method, passing the `placeId` of the place you are reporting. You can retrieve this place ID from the `Place` object. For more information about place IDs, see the [place ID overview][6]. - -Reporting the location of a device is similar to a checkin. It's not possible to retrieve the report later, and the report is not linked to the user's account. - -The following sample reports that the device is at Darling Island Wharf, in Pyrmont, Australia: - -```csharp -// Place ID for Darling Island Wharf Pyrmont -var placeId = "ChIJO1H1TzeuEmsR5bJONIMc4jk" -PlacesClient.SharedInstance.ReportDeviceAtPlace (placeId); -``` - ---- - -# Place IDs and Details - -The Google Places API for iOS provides your app with rich information about places, including the place's name and address, the geographical location specified as latitude/longitude coordinates, the type of place (such as night club, pet store, museum), and more. To access this information for a specific place, you can use the place ID, a stable identifier that uniquely identifies a place. - -## Place details - -The `Place` class provides information about a specific place. You can get hold of a `Place` object in the following ways: - -* Call `PlacesClient` `CurrentPlace` instance method. See the guide to [getting current place](#current-place). -* Add a `PlacePicker` UI widget, which allows the user to select a place. Call `PickPlace` and supply a callback to receive the chosen place. See the guide to [adding a place picker](#place-picker). -* Call `PlacesClient` `LookUpPlaceId` instance method, passing a place ID and a callback method. See the [Get a place by ID](#get-a-place-by-id) section below. - -The `Place` class provides the following information: - -* `Name` – The place's name. -* `Id` – The textual identifier for the place. Read more about place IDs in the rest of this page. -* `Coordinate` – The geographical location of the place, specified as latitude and longitude coordinates. -* `OpenNowStatus` – Indicates whether the place is open at the time when the request for place information is made. -* `PhoneNumber` – The place's phone number, in international format. -* `FormattedAddress` – The human-readable address of this location. - - Often this address is equivalent to the postal address. Note that some countries, such as the United Kingdom, do not allow distribution of true postal addresses due to licensing restrictions. - - The formatted address is logically composed of one or more address components. For example, the address "111 8th Avenue, New York, NY" consists of the following components: "111" (the street number), "8th Avenue" (the route), "New York" (the city) and "NY" (the US state). - - Do not parse the formatted address programmatically. Instead you should use the individual address components, which the API response includes in addition to the formatted address field. - -* Rating – An aggregated rating of the place, returned as a float with values ranging from 1.0 to 5.0, based on aggregated user reviews. -* PriceLevel – The price level for this place, returned as an integer with values ranging from 0 (cheapest) to 4 (most expensive). -* Types – A list of place types that characterize this place. See the documentation for the [supported types][9]. -* Website – The URI of the place's website, if known. This is the website maintained by the business or other entity associated with the place. -* Attributions – An `NSAttributedString` containing the attributions that you must display to the user if your app uses place details retrieved from the Google Places API for iOS. For details on retrieving and displaying attributions, See this [Attribution's documentation][8] to learn more about this. -* AddressComponents – An array of `AddressComponent` objects representing components of the address for a place. These components are provided for the purpose of extracting structured information about a place's address, for example finding the city in which a place is located. Do not use these components for address formatting; instead, use the `FormattedAddress` property, which provides a localized formatted address. - - Note the following facts about the `AddressComponents` array: - - * The array of address components may contain more components than the `FormattedAddress`. - * The array does not necessarily include all the political entities that contain an address, apart from those included in the `FormattedAddress`. - * The format of the response is not guaranteed to remain the same between requests. In particular, the number of `AddressComponents` varies based on the address requested and can change over time for the same address. A component can change position in the array. The type of the component can change. A particular component may be missing in a later response. - -## Get a place by ID - -> _**Note:**_ _Use of the `PlacesClient` `LookUpPlaceId` instance method is subject to tiered query limits. See the documentation on [usage limits][1]._ - -A place ID is a textual identifier that uniquely identifies a place. In the Google Places API for iOS, you can retrieve the ID of a place from a `Place` object. You can store the place ID and use it to retrieve the `Place` object again later. - -To get a place by ID, call `PlacesClient` `LookUpPlaceId` instance method, passing a place ID and a callback method. - -The API invokes the specified callback method, passing in a `Place` object. If the place is not found, the place object is `null`. - -```csharp -// A hotel in Saigon with an attribution. -var placeId = "ChIJV4k8_9UodTERU5KXbkYpSYs"; -PlacesClient.SharedInstance.LookUpPlaceId (placeId, PlaceResultHandler); - -void PlaceResultHandler (Place place, NSError error) -{ - if (error != null) { - Console.WriteLine ($"Look up place id query error: {error.LocalizedDescription}"); - return; - } - - if (place == null) { - Console.WriteLine ($"No place details for {placeId}"); - return; - } - - Console.WriteLine ($"Place name: {place.Name}"); - Console.WriteLine ($"Place address: {place.FormattedAddress}"); - Console.WriteLine ($"Place id: {place.Id}"); - Console.WriteLine ($"Place attributions: {place.Attributions}"); -} -``` - -## Display attributions in your app - -When your app displays information obtained from `PlacesClient` `LookUpPlaceId`, the app must also display attributions. See this [Attribution's documentation][8] to learn more about this. - -## More about place IDs - -The place ID used in the Google Places API for iOS is the same identifier as used in the [Google Places API Web Service, Google Places API for Android and other Google APIs][10]. - -There are some circumstances that may cause a place to get a new place ID. For example, this may happen if a business moves to a new location. - -When you request a place by specifying a place ID, you can be confident that you will always receive the same place in the response (if the place still exists). Note, however, that the response may contain a place ID that is different from the one in your request. - -For more information, see the [place ID overview][6]. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://developers.google.com/places/ios-api/start) to see original Google documentation._ - -[1]: https://developers.google.com/places/ios-api/usage -[2]: https://console.developers.google.com/flows/enableapi?apiid=placesios&reusekey=true -[3]: https://console.developers.google.com/project/_/apiui/credential -[4]: https://support.google.com/googleapi -[5]: https://developers.google.com/places/ios-api/supported_types#table3 -[6]: https://developers.google.com/places/ios-api/place-id -[7]: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/URLLoadingSystem.html -[8]: https://developers.google.com/places/ios-api/attributions -[9]: https://developers.google.com/places/ios-api/supported_types -[10]: https://developers.google.com/places/ +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/SignIn/Details.md b/docs/Google/SignIn/Details.md index cc8be5240..71ce4d43d 100755 --- a/docs/Google/SignIn/Details.md +++ b/docs/Google/SignIn/Details.md @@ -1,4 +1,21 @@ +# Google Sign-In -Google Sign-In is a secure authentication system that reduces the burden of login for your users, by enabling them to sign in with their Google account—the same account they already use with Gmail, Play, Google+, and other Google services. +This path is retained for existing links, but this repository does not maintain a copied Google Sign-In walkthrough. -Google Sign-In is also your gateway to connecting with Google’s users and services in a secure manner. You can give your users the opportunity to pay with Android Pay, share with their Google-wide contacts, save a file to Drive, add an event to Calendar, and more. Integrate Google’s user-centric APIs and services inside your app to help your users take action and convert. \ No newline at end of file +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. + +## Native Documentation + +- Google Sign-In documentation: https://developers.google.com/identity/sign-in/ios/start-integrating + +## Binding Project + +- Package ID: `AdamE.Google.iOS.SignIn` +- Managed namespace: `Google.SignIn` +- Status note: This binding project is present in the repository and is part of the current Google package set described by the top-level README. + +## Binding Notes + +Use `Google.SignIn.Configuration` for native `GIDConfiguration`, assign it to `SignIn.SharedInstance.Configuration`, and pass callback URLs to `SignIn.SharedInstance.HandleUrl(url)`. Sign-in entry points are exposed on `SignIn.SharedInstance`. + +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/SignIn/GettingStarted.md b/docs/Google/SignIn/GettingStarted.md index c1daeade1..71ce4d43d 100755 --- a/docs/Google/SignIn/GettingStarted.md +++ b/docs/Google/SignIn/GettingStarted.md @@ -1,143 +1,21 @@ -Configuring your App --------------------- +# Google Sign-In -Google provides an easy to use configuration web tool to generate a config file for your app: +This path is retained for existing links, but this repository does not maintain a copied Google Sign-In walkthrough. -1. Open [Google's configuration tool][1] to create a config file for your app. -2. Enter your app's name and iOS Bundle ID and click continue -3. Click *Enable Sign-In* -4. Click *continue* to generate the configuration files -5. Click *Download Google-Service-Info.plist* -6. Add `GoogleService-Info.plist` to your Xamarin.iOS app project and set the *Build Action* to `BundleResource` -7. In your Xamarin.iOS app project's `Info.plist` file, add the following URL Types: - - Role: `Editor` URL Schemes: `your.app.bundle.id` - - Role: `Editor` URL Schemes: `value of REVERSED_CLIENT_ID from GoogleService-Info.plist` - -Setup your AppDelegate ----------------------- +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -In order for Sign-In to work properly, you must tell the SDK about some of your application lifecycle events. +## Native Documentation -In your `AppDelegate.cs`, in the `FinishedLaunching (..)` override, you should add the following code to the start of the method: +- Google Sign-In documentation: https://developers.google.com/identity/sign-in/ios/start-integrating -``` csharp -public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions) -{ - ... +## Binding Project - // You can get the GoogleService-Info.plist file at https://developers.google.com/mobile/add - var googleServiceDictionary = NSDictionary.FromFile ("GoogleService-Info.plist"); - SignIn.SharedInstance.ClientID = googleServiceDictionary ["CLIENT_ID"].ToString (); +- Package ID: `AdamE.Google.iOS.SignIn` +- Managed namespace: `Google.SignIn` +- Status note: This binding project is present in the repository and is part of the current Google package set described by the top-level README. - ... +## Binding Notes - return true; -} -``` +Use `Google.SignIn.Configuration` for native `GIDConfiguration`, assign it to `SignIn.SharedInstance.Configuration`, and pass callback URLs to `SignIn.SharedInstance.HandleUrl(url)`. Sign-in entry points are exposed on `SignIn.SharedInstance`. -Next, you will need to override the `OpenUrl` method in your `AppDelegate` class or, if it already exists, add the code inside the method to the existing implementation: - -``` csharp -// For iOS 9 or newer -public override bool OpenUrl (UIApplication app, NSUrl url, NSDictionary options) -{ - var openUrlOptions = new UIApplicationOpenUrlOptions (options); - return SignIn.SharedInstance.HandleUrl (url, openUrlOptions.SourceApplication, openUrlOptions.Annotation); -} - -// For iOS 8 and older -public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation) -{ - return SignIn.SharedInstance.HandleUrl (url, sourceApplication, annotation); -} -``` - -Signing In ----------- - -Google Sign-In provides a `SignInButton` to add to your views and handles starting the sign in process. You can add the button to your app in code or by using storyboards: - -``` csharp -SignInButton = new SignInButton (); -SignInButton.Frame = new CGRect (20, 100, 150, 44); -View.AddSubview (SignInButton); - -// Assign the SignIn Delegates to receive callbacks -SignIn.SharedInstance.UIDelegate = this; -SignIn.SharedInstance.Delegate = this; -``` - -You also must implement `ISignInDelegate` as well as `ISignInUIDelegate` and provide a `DidSignIn` method to know when the sign-in completed and if it was successful: - -``` csharp -public void DidSignIn (SignIn signIn, GoogleUser user, NSError error) -{ - if (user != null && error == null) - // Disable the SignInButton -} -``` - -> ***Note:*** *The Sign-In SDK automatically acquires access tokens, but the access tokens will be refreshed only when you call `SignIn` or `SignInSilently` methods. To explicitly refresh the access token, call the `RefreshTokens` method. If you need the access token and want the SDK to automatically handle refreshing it, you can use the `GetAccessToken` method.* - -The `SignInUserSilently` method attempts to sign in a previously authenticated user without interaction. This can be done in a `ViewDidLoad` method or `ViewDidAppear` of your `UIViewController`: - -``` csharp -// Assign the SignIn Delegates to receive callbacks -SignIn.SharedInstance.UIDelegate = this; -SignIn.SharedInstance.Delegate = this; - -// Sign the user in automatically -SignIn.SharedInstance.SignInUserSilently (); -``` - -> ***Note:*** *When users silently sign in, the Sign-In SDK automatically acquires access tokens and automatically refreshes them when necessary. If you need the access token and want the SDK to automatically handle refreshing it, you can use the `RefreshTokens` method. To explicitly refresh the access token, call the `RefreshAccessToken` method.* - -If, in your project, the class that implements `ISignInUIDelegate` interface is not a subclass of `UIViewController`, you will need to implement the `WillDispatch`, `PresentViewController`, and `DismissViewController` methods of the `ISignInUIDelegate` interface. For example: - -```csharp -[Export ("signInWillDispatch:error:")] -public void WillDispatch (SignIn signIn, NSError error) -{ - myActivityIndicator.StopAnimating (); -} - -[Export ("signIn:presentViewController:")] -public void PresentViewController (SignIn signIn, UIViewController viewController) -{ - PresentViewController (viewController, true, null); -} - -[Export ("signIn:dismissViewController:")] -public void DismissViewController (SignIn signIn, UIViewController viewController) -{ - DismissViewController (true, null); -} -``` - -Signing Out and Disconnecting ----------- - -To sign out a user simply call the `SignOutUser` method on the `SignIn` object: - -``` csharp -SignOutButton.TouchUpInside += (sender, e) => { - SignIn.SharedInstance.SignOutUser (); - - SignInButton.Enabled = true; - SignOutButton.Enabled = false; -}; -``` - -To completely disconnect the current user from the app and revoke previous authentication call the `DisconnectUser` method on the `SignIn` object. - -Optionally, you can provide a `DidDisconnect` method to know when the sign out was completed and if it was successful: - -```csharp -[Export ("signIn:didDisconnectWithUser:withError:")] -public void DidDisconnect (SignIn signIn, GoogleUser user, NSError error) -{ - // Perform any operations when the user disconnects from app here. -} -``` - -[1]: https://developers.google.com/mobile/add?platform=ios&cntapi=gcm +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/TagManager/Details.md b/docs/Google/TagManager/Details.md index b14ab6cb3..aa4165a28 100755 --- a/docs/Google/TagManager/Details.md +++ b/docs/Google/TagManager/Details.md @@ -1,16 +1,21 @@ -Developers can use the Google Tag Manager interface to implement and manage measurement tags and pixels in their mobile applications, without having to rebuild and resubmit application binaries to app marketplaces. Developers who are working with Firebase Analytics can easily add Google Tag Manager to help manage and make changes to the implementation, even after the app has shipped. - -Developers can log important events, and decide later which tracking tags or pixels should be fired. Tag Manager currently supports tags for the following products: - -* Firebase Analytics -* Google Analytics -* DoubleClick -* AdWords -* adjust -* AppsFlyer -* Apsalar -* Kochava -* Tune -* Custom Function Calls (for other products) - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://developers.google.com/tag-manager/ios/v5/) to see original Google documentation._ \ No newline at end of file +# Google Tag Manager + +This path is retained for existing links, but this repository does not maintain a copied Google Tag Manager walkthrough. + +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. + +## Native Documentation + +- Google Tag Manager documentation: https://developers.google.com/tag-platform/tag-manager/ios/v5 + +## Binding Project + +- Package ID: `AdamE.Google.iOS.TagManager` +- Managed namespace: `Google.TagManager` +- Status note: This binding project is present in the repository. Check the top-level README and package feed for current publication status before depending on it. + +## Binding Notes + +Tag Manager initialization is exposed as `Google.TagManager.TagManager.Configure()`. Firebase-backed Tag Manager usage also depends on Firebase app initialization through `Firebase.Core.App.Configure()`. + +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/Google/TagManager/GettingStarted.md b/docs/Google/TagManager/GettingStarted.md index b4f17905f..aa4165a28 100755 --- a/docs/Google/TagManager/GettingStarted.md +++ b/docs/Google/TagManager/GettingStarted.md @@ -1,168 +1,21 @@ -## Prerequisites +# Google Tag Manager -* Create a [Google Tag Manager account][3]. -* [Configure a Google Tag Manager container][4]. +This path is retained for existing links, but this repository does not maintain a copied Google Tag Manager walkthrough. -## Add Firebase to your app +These packages are thin .NET bindings over native Google Apple SDKs. Use the official native documentation as the source of truth for setup, product behavior, console workflows, quotas, policy requirements, and examples. -1. Create a Firebase project in the [Firebase console][1], if you don't already have one. If you already have an existing Google project associated with your mobile app, click **Import Google Project**. Otherwise, click **Create New Project**. -2. Click **Add Firebase to your iOS app** and follow the setup steps. If you're importing an existing Google project, this may happen automatically and you can just [download the config file][2]. -3. When prompted, enter your app's bundle ID. It's important to enter the bundle ID your app is using; this can only be set when you add an app to your Firebase project. -4. At the end, you'll download a `GoogleService-Info.plist` file. You can [download this file][2] again at any time. +## Native Documentation -## Configure Tag Manager in your app +- Google Tag Manager documentation: https://developers.google.com/tag-platform/tag-manager/ios/v5 -Once you have your `GoogleService-Info.plist` file downloaded in your computer, do the following steps in Xamarin Studio: +## Binding Project -1. Add `GoogleService-Info.plist` file to your app project. -2. Set `GoogleService-Info.plist` **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. -3. Open `GoogleService-Info.plist` file and change `IS_ANALYTICS_ENABLED` value to `Yes`. -4. Add the following lines of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` and `Google.TagManager` namespaces): +- Package ID: `AdamE.Google.iOS.TagManager` +- Managed namespace: `Google.TagManager` +- Status note: This binding project is present in the repository. Check the top-level README and package feed for current publication status before depending on it. -```csharp -TagManager.Configure (); -App.Configure (); -``` +## Binding Notes -## Download your container and add it to your project +Tag Manager initialization is exposed as `Google.TagManager.TagManager.Configure()`. Firebase-backed Tag Manager usage also depends on Firebase app initialization through `Firebase.Core.App.Configure()`. -1. Sign in to your [Google Tag Manager][5] account. -2. Select a mobile container. -3. Click Versions in the top navigation bar. -4. Click Actions > Download on the selected container version. -5. The name of the downloaded file is the container ID with a .json extension. -6. In Xamarin Studio, create a folder named **container** inside of your **Resources** folder. -7. Add **GTM-XXXXXX.json** file into your **container** folder in your project. -8. Verify that **GTM-XXXXXX.json** build action behaviour is `Bundle Resource` by Right clicking/Build Action. - -## Log events and variables - -Google Tag Manager uses Firebase Analytics' events, parameters, and user properties to trigger and build tags you've configured in the Google Tag Manager web interface. In this sense, your Firebase Analytics implementation acts as your data layer. - -To learn how to log events and properties, see [Firebase Analytics for iOS Getting Started][6] guide. - -### Configure variables in Tag Manager - -To capture the value of Firebase event parameters and user properties for use in Google Tag Manager, you can [configure variables][7] in the Tag Manager interface. - -For example, if you log the following custom event: - -```csharp -NSString [] keys = { new NSString ("image_name"), new NSString ("full_text") }; -NSObject [] values = { name, text }; -var parameters = NSDictionary.FromObjectsAndKeys (keys, values, keys.Length); -Analytics.LogEvent ("share_image", parameters); -``` - -You could configure new **Event Parameter** variables in Google Tag Manager to capture the `image_name` and `full_text` parameter values: - -* **Variable Name**: Image Name -* **Variable Type**: Event Parameter -* **Event Parameter Key Name**: image_name - -and: - -* **Variable Name**: Full Text -* **Variable Type**: Event Parameter -* **Event Parameter Key Name**: full_text - -It is similar for user properties, for example: - -```csharp -Analytics.SetUserProperty (food, "favorite_food"); -``` - -You could configure a new **Firebase User Property** variable in Google Tag Manager to capture the `favorite_food` value: - -* **Variable Name**: Favorite Food -* **Variable Type**: Firebase User Property -* **Event Parameter Key Name**: favorite_food - -### Modify and block Firebase Analytics events - -Google Tag Manager enables you to modify and block events before they are logged in Firebase Analytics. Modifying events can help you—without app updates—add, remove, or change the values of event parameters or adjust event names. Events that are not blocked will be logged in Firebase Analytics. - -Firebase Analytics also automatically logs some [events][8] and [user properties][9]; you don't need to add any code to enable them. These automatically collected events and properties can be used in Google Tag Manager, but cannot be blocked. - -## Fire tags - -Firebase event name variables, Firebase event parameter variables, and other variables are used to set up [triggers][10]. Trigger conditions are evaluated whenever you log a Firebase event. By default, Firebase Analytics events automatically fire. It is possible to add a Firebase Analytics tag in Tag Manager to block events from being sent to Firebase Analytics. - -## Preview, debug, and publish your container - -Before publishing a version of your container, you'll want to preview it to make sure it works as intended. Google Tag Manager enables you to preview versions of your container by generating links and QR codes in the Google Tag Manager web interface and using them to open your application. - -### Preview container - -To preview a container, generate a preview URL in the Google Tag Manager web interface: - -1. Sign in to your [Google Tag Manager][5] account. -2. Select a mobile container. -3. Click **Versions** in the top navigation bar. -4. Click **Actions > Preview** on the container version you'd like to preview. -5. Enter your application's Bundle ID. -6. Click **Generate begin preview link**. -7. Save the preview URL. - -To enable container previews, you must define the Google Tag Manager preview URL scheme in your **Info.plist** file: - -1. In Xamarin Studio, open your Info.plist file and go to **Advance** tab. -2. Click on **Add URL Type**. -3. As Identifer set your Bundle ID. -4. As URL Schemes set `tagmanager.c.`. -5. Open the preview URL on an emulator or physical device to preview the draft container in your app. - -### Debug container - -When you run your app in a simulator or in preview mode, Tag Manager automatically turns logging to verbose. - -### Publish container - -After previewing your container and verifying that it is working, you can [publish it][11]. After you have published your container, your tag configurations are available to mobile app users. When user devices are online, they typically will receive the new configurations within a day. - -## Advanced Configuration - -To extend the functionality of Google Tag Manager, you can add Function Call variables and Function Call tags. Function Call variables let you capture the values returned by calls to pre-registered functions. Function Call tags let you execute pre-registered functions (e.g. to trigger hits for additional measurement and remarketing tools that are not currently supported with tag templates in Google Tag Manager). - -### Add custom tags and variables - -To create a custom tag, create a class that implements the `ICustomFunction` interface (don't forget to import `Google.TagManager` namespace): - -```csharp -public class MyCustomTag : NSObject, ICustomFunction -{ - public NSObject Execute (NSDictionary parameters) - { - // Add custom tag implementation here - } -} -``` - -To create a custom variable, create a class that implements the `ICustomFunction` interface: - -```csharp -public class MyCustomVariable : NSObject, ICustomFunction -{ - public NSObject Execute (NSDictionary parameters) - { - // Return the value of the custom variable. - return NSNumber.FromNInt (42); - } -} -``` - -After you finished creating your custom classes, go to Google Tag Manager's web interface and create **Tags** or **Variables** with **Function Call** as type. - -_Portions of this page are modifications based on work created and [shared by Google](https://developers.google.com/readme/policies/) and used according to terms described in the [Creative Commons 3.0 Attribution License](http://creativecommons.org/licenses/by/3.0/). Click [here](https://developers.google.com/tag-manager/ios/v5/) to see original Google documentation._ - -[1]: https://firebase.google.com/console/ -[2]: http://support.google.com/firebase/answer/7015592 -[3]: https://www.google.com/analytics/tag-manager/ -[4]: https://support.google.com/tagmanager/answer/6103696#CreatingAnAccount -[5]: https://tagmanager.google.com/ -[6]: https://components.xamarin.com/gettingstarted/firebaseiosanalytics -[7]: https://support.google.com/tagmanager/answer/6106899 -[8]: https://support.google.com/firebase/answer/6317485 -[9]: https://support.google.com/firebase/answer/6317486 -[10]: https://support.google.com/tagmanager/answer/6106961 -[11]: https://support.google.com/tagmanager/answer/6107163 +Document only binding-specific caveats in this repository. Product usage guidance belongs in the official native docs. diff --git a/docs/NUGET_README.md b/docs/NUGET_README.md index 434e82229..1b64c99e6 100644 --- a/docs/NUGET_README.md +++ b/docs/NUGET_README.md @@ -1,19 +1,39 @@ -# Overview +# Package Notes -This package is part of a set of .NET bindings for Google and Firebase native iOS SDKs. +This package is part of a set of .NET bindings for native Google, Firebase, and ML Kit Apple SDKs. + +## Native documentation is authoritative + +These packages expose native Apple SDK APIs to .NET. Use the official native SDK documentation for product setup, feature behavior, console configuration, quotas, and troubleshooting. + +This package README covers only binding and NuGet concerns. ## Target frameworks -These packages are intended for iOS and Mac Catalyst TFMs (for example `net9.0-ios`, `net10.0-ios`, `net9.0-maccatalyst`, `net10.0-maccatalyst`). +These packages are intended for iOS and Mac Catalyst TFMs, for example: + +- `net9.0-ios` +- `net10.0-ios` +- `net9.0-maccatalyst` +- `net10.0-maccatalyst` -When multi-targeting, condition the reference so it only restores for iOS: +When multi-targeting, condition the reference so it restores only for Apple targets: ```xml - - + + ``` -## Notes on native dependency conflicts +Replace the package ID and version with the package you are consuming. + +## Native dependency conflicts + +Google, Firebase, and ML Kit Apple SDKs share native xcframework dependencies. Avoid mixing independent binding package sets that embed overlapping native Google/Firebase binaries in the same app unless you have verified that the native dependency versions are compatible. + +For application projects, pin package versions intentionally and keep related Google/Firebase/ML Kit packages on compatible lines. + +## Repository -Google/Firebase iOS SDKs share native dependencies (xcframeworks). Avoid mixing multiple independent bindings that ship overlapping Google/Firebase native SDK binaries in the same app, as it can lead to duplicate symbols or runtime issues. +- Repository: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents +- Issues: https://github.com/AdamEssenmacher/GoogleApisForiOSComponents/issues diff --git a/docs/PUBLISHING_GITHUB_PACKAGES.md b/docs/PUBLISHING_GITHUB_PACKAGES.md index db7b3c1ea..9c1c9ba34 100644 --- a/docs/PUBLISHING_GITHUB_PACKAGES.md +++ b/docs/PUBLISHING_GITHUB_PACKAGES.md @@ -1,24 +1,21 @@ -# Publishing to GitHub Packages (NuGet) +# Publishing to GitHub Packages -This repository can publish `.nupkg` files to GitHub Packages (NuGet feed) via GitHub Actions. +This repository can publish `.nupkg` files to GitHub Packages through GitHub Actions. -## How publishing works +## How Publishing Works - A tag push triggers the publish workflow. - The workflow builds and packs NuGet packages. -- Optionally, you can run the workflow manually (`workflow_dispatch`) and pass a custom Cake `--names` value. -- The workflow pushes packages to: - - `https://nuget.pkg.github.com//index.json` +- The workflow can also be run manually with `workflow_dispatch` and a custom Cake `--names` value. +- Packages are pushed to `https://nuget.pkg.github.com//index.json`. -## Consuming packages +The publish workflow uses the built-in `GITHUB_TOKEN`; no app-specific publishing secret is required. -Add GitHub Packages as a NuGet source and authenticate using a GitHub PAT (or workflow token in CI). +## Consuming Packages -Notes: -- The publish workflow uses the built-in `GITHUB_TOKEN` (no extra repository secret is required). -- Local development typically uses a GitHub PAT with `read:packages` (and `repo` for private repos). +Add GitHub Packages as a NuGet source and authenticate using a GitHub token that can read packages for the owner account. Local development usually needs a PAT with `read:packages`; private repositories can also require repository access. -Create a local `NuGet.Config` (or update your existing one) and add: +Example local `NuGet.Config` source: ```xml @@ -28,4 +25,4 @@ Create a local `NuGet.Config` (or update your existing one) and add: ``` -Then restore using credentials for the `` account. +Keep credentials in local user-level NuGet configuration or local environment variables. Do not commit package-feed credentials. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..cb2561388 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,40 @@ +# Documentation + +This repository contains thin .NET bindings for native Google, Firebase, and ML Kit Apple SDKs. The native SDK documentation is the source of truth for product behavior, platform setup, console workflows, quotas, policy requirements, and feature examples. + +The docs in this repository should cover only the binding layer: package IDs, managed namespaces, supported target frameworks, NuGet consumption, version alignment, build and release workflows, dependency conflict notes, and maintainer decisions that cannot live in the native SDK docs. + +## What belongs here + +- NuGet package names and target framework guidance. +- Binding-specific initialization or packaging caveats. +- Native dependency and version-alignment rules for these packages. +- Repository build, validation, CI, and publishing workflows. +- Maintainer notes for binding API shape, audit policy, and runtime-drift triage. + +## What does not belong here + +- Rewritten Firebase, Google, or ML Kit product walkthroughs. +- Console setup flows, quotas, billing, policy, or dashboard behavior. +- App-specific examples, secrets, private configuration, or private business context. +- Broad native SDK tutorials that are already maintained by Google or Firebase. + +## Current package docs + +Firebase package READMEs are in [Firebase/NuGet](Firebase/NuGet). These files are packed as `NUGET_README.md` for the Firebase packages and should stay short. + +Shared package README text for Google/support packages is in [NUGET_README.md](NUGET_README.md). Package-specific Google and ML Kit docs should be added only when they document binding-specific details that the native docs cannot cover. + +## Build and release docs + +- [Building locally](BUILDING.md) +- [Publishing to GitHub Packages](PUBLISHING_GITHUB_PACKAGES.md) + +## Maintainer notes + +- [Firebase Binding API Shape Policy](firebase-binding-api-shape-policy.md) +- [Firebase Runtime Failure Backlog](firebase-runtime-failure-backlog.md) + +## Legacy component paths + +Some older component paths are intentionally retained so existing links do not break. Those files now point readers to the official native documentation and to the relevant package README instead of duplicating native SDK docs.