This commit is contained in:
SotoiGhost 2019-06-27 13:57:45 -05:00
Родитель 31d4c6a3c9
Коммит c7d543d801
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 72EA2B8D62E38FAB
54 изменённых файлов: 14978 добавлений и 0 удалений

22
docs/Firebase/AdMob/Details.md Executable file
Просмотреть файл

@ -0,0 +1,22 @@
AdMob by Google is an easy way to monetize mobile apps with targeted, in-app advertising.
AdMob by Google is a mobile advertising platform that you can use to generate revenue from your app. Using AdMob with Firebase Analytics provides you with additional app usage data and analytics capabilities.
## Key capabilities
| | |
|:-:|-|
| **Earn more from AdMob's in-app ads** | Show ads from millions of Google advertisers in real time, or use AdMob Mediation to earn from over 40 premium networks through the AdMob platform to simplify your ad operations, improve competition, and earn more, for free. AdMob mediation has [ad network optimization](https://support.google.com/admob/answer/3379794) built in, which automatically adjusts the positions of your other ad networks in your mediation stack to ensure you maximize your revenue. |
| **Improve user experience** | Native and video ads create a positive user experience as you monetize by matching the look and feel of your app. Choose from different ad templates, customize them, and experiment with different layouts on the fly without republishing your app. |
| **Scale fast** | When your app's a global or domestic hit, you can monetize users quickly with AdMob, by showing ads to users in more than 200 markets. More than one app? AdMob house ads is a free tool that enables you to cross-promote your apps to your userbase, across your family of apps. |
| **Access monetization reports** | AdMob is the premier monetization platform for mobile. While generating ad revenue, AdMob also produces its own monetization reports that you can use to make smarter decisions about product strategy. |
## How does it work?
AdMob by Google helps you monetize your mobile app through in-app advertising. Ads can be displayed as banner ads, interstitial ads, video ads, or native ads — which are seamlessly added to platform native UI components. On Android, you can additionally display in-app purchase ads, which allow users to purchase advertised products from within your app.
Before you can display ads within your app, you'll need to create an AdMob account and activate one or more Ad Unit IDs. This is a unique identifier for the places in your app where ads are displayed. If you are already using AdMob in your app, all of your existing Ad Unit IDs continue to work after you've added Firebase to your app.
AdMob uses the Google Mobile Ads SDK. The Google Mobile Ads SDK helps app developers gain insights about their users, drive more in-app purchases, and maximize ad revenue. In order to do so, the default integration of the Mobile Ads SDK collects information such as device information, publisher-provided location information, and general in-app purchase information such as item purchase price and currency.
<sub>_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/admob/) to see original Firebase documentation._</sub>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,20 @@
Firebase Analytics is a free app measurement solution that provides insight on app usage and user engagement.
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.
## Key capabilities
| | |
|-:|-|
| **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. |
## How does it work?
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.
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.
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.
<sub>_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._</sub>

Просмотреть файл

@ -0,0 +1,308 @@
# Get Started with Firebase Analytics for iOS
Firebase Analytics collects usage and behavior data for your app. The SDK logs two primary types of information:
* **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.
## Table of Content
- [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)
## 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 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<NSString, NSObject>.FromObjectsAndKeys (values, keys, keys.Length);
Analytics.LogEvent (EventNamesConstants.SelectContent, parameters);
// Or
var parameters = new Dictionary<object, object> {
{ 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<NSString, NSObject>.FromObjectsAndKeys (values, keys, keys.Length);
Analytics.LogEvent ("share_image", parameters);
// Or
var parameters = new Dictionary<object, object> { { "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<NSString, NSObject>);
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].
<sub>_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._</sub>
[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

26
docs/Firebase/Auth/Details.md Executable file
Просмотреть файл

@ -0,0 +1,26 @@
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 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.
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.
## Key capabilities
| | |
|-:|-|
| **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. |
## How does it work?
![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.
<sub>_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._</sub>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,27 @@
Use Firebase flexible, scalable NoSQL cloud database to store and sync data for client- and server-side development.
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.
_**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)._
## Key capabilities
| | |
|--:|---|
| 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. |
## How does it work?
Firebase Cloud Firestore is a cloud-hosted, NoSQL database that your iOS, Android, and web apps can access directly via native SDKs.
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.
<sub>_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._</sub>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,17 @@
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.
## Key capabilities
| | |
|-:|--|
| **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 FCMs reliable and battery-efficient connection channel. |
## How does it work?
![FirebaseCloudMessaging_HowItWorks](https://firebase.google.com/docs/cloud-messaging/images/messaging-overview.png)
A Firebase Cloud Messaging implementation includes an app server that interacts with FCM via HTTP or XMPP protocol, and a client app.
<sub>_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._</sub>

Просмотреть файл

@ -0,0 +1,597 @@
# Firebase Cloud Messaging on iOS
## Table of content
- [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)
## About Firebase Cloud Messaging
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.
## Method swizzling in Firebase Cloud Messaging
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 apps 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.
---
# 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 [Apples 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<UIBackgroundFetchResult> 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<UIBackgroundFetchResult> 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<UNNotificationPresentationOptions> 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<object, object>` 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].
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,39 @@
## **Important note:** *This component is only compatible with Xamarin Studio and Visual Studio for Mac.*
Firebase Crash Reporting uses shell scripts to upload symbols to Firebase Console, but scripts use commands that are only available in Mac. Therefore, this component is only compatible with Xamarin Studio and Visual Studio for Mac.
# Firebase Crash Reporting on iOS
Comprehensive and actionable information to help diagnose and fix problems in your app.
Crash Reporting creates detailed reports of the errors in your app. Errors are grouped into clusters of similar stack traces and triaged by the severity of impact on your users. In addition to automatic reports, you can log custom events to help capture the steps leading up to a crash.
## Key capabilities
| | |
|-:|--|
| **Monitor fatal errors** | Monitor fatal errors in iOS. Reports are triaged by the severity of impact on users. |
| **Collect the data you need to diagnose problems** | Each report contains a full stack trace as well as device characteristics, performance data, and user circumstances when the error took place. Similar reports are automatically clustered to make it easier to identify related bugs. |
| **Email alerts** | Enable email alerts to receive frequent updates when new crashes are uncovered or regressions are detected. |
| **Integrate with Analytics** | Errors captured are set as **app_exception** events in Firebase Analytics, allowing you to filter audiences based on who sees errors. In addition to stack traces, Crash Reporting also integrates with Analytics to provide you with the list of events that preceded a crash. This information helps to simplify your debugging process. |
| **Free and easy** | Crash Reporting is free to use. Once you've added Firebase to your app, it's just a few lines of code to enable comprehensive error reporting. |
## User privacy
Firebase Crash Reporting does not itself collect any personally identifiable information (such as names, email addresses, or phone numbers). Developers can collect additional data using Crash Reporting with log and exception messages. Such data collected through Crash Reporting should not contain information that personally identifies an individual to Google.
Here is an example of a log message that does not contain personally identifiable information:
```csharp
Firebase.CrashReporting.CrashReporting.Log ("SQL database failed to initialize");
```
And here is another one that does contain personally identifiable information:
```csharp
Firebase.CrashReporting.CrashReporting.Log ($"{user.Email} purchased product {product.Id}");
```
If identifying a user is necessary to diagnose an issue, then you must use adequate obfuscation measures to render the data you send to Google anonymous.
<sub>_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/crash/) to see original Firebase documentation._</sub>

Просмотреть файл

@ -0,0 +1,157 @@
## **Important note:** *This component is only compatible with Xamarin Studio and Visual Studio for Mac.*
Firebase Crash Reporting uses shell scripts to upload symbols to Firebase Console, but scripts use commands that are only available in Mac. Therefore, this component is only compatible with Xamarin Studio and Visual Studio for Mac.
# Use Firebase Crash Reporting on iOS
Firebase Crash Reporting creates detailed reports of the errors in your app. Errors are grouped into clusters of similar stack traces, and triaged by the severity of impact on your users. In addition to automatic reports, you can log custom events to help capture the steps leading up to a crash.
## Table of Content
- [User privacy](#user-privacy)
- [Add Firebase to your app](#add-firebase-to-your-app)
- [Configure Crash Reporting in your app](#configure-crash-reporting-in-your-app)
- [Create your first error](#create-your-first-error)
- [Upload symbol files](#upload-symbol-files)
- [Upload symbol files with Visual Studio](#upload-symbol-files-with-visual-studio)
- [Upload symbol files with Terminal](#upload-symbol-files-with-terminal)
- [Upload your first error to Firebase](#upload-your-first-error-to-firebase)
- [Create custom logs](#create-custom-logs)
- [Known issues](#known-issues)
## User privacy
Firebase Crash Reporting does not itself collect any personally identifiable information (such as names, email addresses, or phone numbers). Developers can collect additional data using Crash Reporting with log and exception messages. Such data collected through Crash Reporting should not contain information that personally identifies an individual to Google.
Here is an example of a log message that does not contain personally identifiable information:
```csharp
Firebase.CrashReporting.CrashReporting.Log ("SQL database failed to initialize");
```
And here is another one that does contain personally identifiable information:
```csharp
Firebase.CrashReporting.CrashReporting.Log ($"{user.Email} purchased product {product.Id}");
```
If identifying a user is necessary to diagnose an issue, then you must use adequate obfuscation measures to render the data you send to Google anonymous.
## 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 Crash Reporting 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` and `Firebase.CrashReporting` namespaces):
```csharp
CrashReporting.Configure ();
App.Configure ();
```
## Create your first error
Add a crash code right after Firebase initialization call in your AppDelegate's `FinishedLaunching` method to cause a crash when the app launches:
```csharp
App.Configure ();
// Cause crash code
var crash = new NSObject ();
crash.PerformSelector (new Selector ("doesNotRecognizeSelector"), crash, 0);
```
Don't run the app yet, please read **Upload symbol files** before running.
## Upload symbol files
In order to view human-readable crash reports, you will need to upload symbol files to Firebase Console after each build. To achieve this, you will need to upload the executable of your app that is generated after each build (the executable file lives inside of **Your.app** that is generated commonly inside of **bin** folder of your project). But first, you will need to download the **service account key** to authenticate your uploads:
1. Go to [Firebase Console][1] and select your project.
2. Open **project settings** and go to **Service Accounts** tab.
3. Select **Crash Reporting** and click on **Generate New Private Key** button.
4. Name the file as **service-account.json** and save it in the root of your project folder.
### Upload symbol files with Visual Studio
Follow these steps to upload your app symbols with Xamarin Studio:
* In Visual Studio, open **Project Options** of your app and go to **Build** > **Custom Commands**.
* Double check that **Debug** configuration and **iPhone** platform is selected.
* In Combobox select **After Build** option.
* Paste the following command in **Command** text field:
```
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
```
* Save options changed.
* Now, build your app with **Debug** configuration.
Depending of your internet connection, the build can take some minutes because the script is uploading your symbols to Firebase.
> ***Note:*** *If you don't want to keep uploading your symbols after each build, just remove the **After Build** command.*
### Upload symbol files with Terminal
Follow these steps to upload your app symbols with Terminal:
* In Xamarin Studio, select **Debug** configuration, **iPhone** Platform and build the app (cmd + k) (don't run it).
* In Terminal, go to your project folder and run the following command:
```
# Don't forget to replace [YourAppName] value
sh scripts/FirebaseCrashReporting/xamarin_upload_symbols.sh -n [YourAppName] -b bin/iPhone/Debug -i Info.plist -p GoogleService-Info.plist -s service-account.json
```
* Depending of your internet connection, the script can take some minutes because it's uploading your symbols to Firebase.
## Upload your first error to Firebase
After you uploaded your symbol files to Firebase, do the following steps to view your crash in Firebase Console:
1. Launch the app from Xamarin Studio.
2. Wait until your app crashes, then, stop the debugging.
3. Launch the app directly from the home screen on the device.
4. Wait until your app crashes.
5. Remove the crashing line so your app can start successfully.
6. Run your app again.
7. Within 20 minutes your crash should show up in **Crash Reporting** section of Firebase Console.
## Create custom logs
You can use `Log` method to create custom log messages that are included in your crash reports. The following example demonstrates creating a log message:
```csharp
using Firebase.CrashReporting;
// ...
CrashReporting.Log ("Cause Crash button clicked");
// Cause crash code
var crash = new NSObject ();
crash.PerformSelector (new Selector ("doesNotRecognizeSelector"), crash, 0);
```
> _**Note:**_ _The string given to this method 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._
### Known issues
* App doesn't compile when `Incremental builds` is enabled. (Bug [#43689][3])
* The Firebase SDK does not currently support using the `NSException` class in the Xcode simulator. If you use this class, it will result in malformed stack traces in the Firebase console. As a workaround, either use a physical device or test with a different type of exception.
* Crash Reporting uses a unique ID to identify each user. Due to a known bug in Xcode 8.1, creating this ID fails on iOS 10 simulators, preventing the upload of error reports. To work around this in Xcode 8.1, you can run tests on a device, or turn on **Keychain Sharing** in your **Entitlements.plist** file. This bug has been addressed in the beta release of Xcode 8.2.
<sub>_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/crash/ios) to see original Firebase documentation._</sub>
[1]: https://firebase.google.com/console/
[2]: http://support.google.com/firebase/answer/7015592
[3]: https://bugzilla.xamarin.com/show_bug.cgi?id=43689

Просмотреть файл

@ -0,0 +1,16 @@
Get clear, actionable insight into app issues with this powerful crash reporting solution.
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.
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.
## Key capabilities
| | |
|-:|-|
| **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. |
<sub>_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._</sub>

Просмотреть файл

@ -0,0 +1,356 @@
# Get Started with Firebase Crashlytics for iOS
## Table of Content
- [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)
## Before you begin
* Enable Crashlytics: Click **Set up Crashlytics** in the [Firebase console][1].
## 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 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 <br /> 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 cant 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<object, object> {
{ "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, its 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 apps 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<object, object> {
{ 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 threads 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 doesnt 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 Apples [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 youd 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].
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,23 @@
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.
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.
## Key capabilities
| | |
|-:|--|
| **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; theres 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. |
## How does it work?
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.
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.
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].
<sub>_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._</sub>
[1]: https://firebase.google.com/docs/database/web/structure-data

Просмотреть файл

@ -0,0 +1,734 @@
# Firebase Database on iOS
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.
## Table of content
- [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)
## 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 Database 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 ();
```
## 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> (T value)`, `SetValues<T> (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> (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<NSDictionary> (data);
```
You can avoid creating variables for each reference and assign the value directly:
```csharp
rootNode.GetChild ("users").GetChild (user.Uid).SetValue<NSDictionary> (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<NSString> (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<NSDictionary> ();
// 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<T>`, `GetValues` or `GetValues<T>` 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<NSNumber> ().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<T>` 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<NSObject> (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<NSDictionary> ();
// 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<NSDictionary> ();
if (noteData == null)
return TransactionResult.Success (currentData);
var mutableNoteData = noteData.MutableCopy () as NSMutableDictionary;
mutableNoteData ["content"] = content;
mutableNoteData ["title"] = title;
currentData.SetValue<NSMutableDictionary> (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<NSDictionary> ();
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<NSDictionary> ();
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<NSDictionary> ();
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<NSDictionary> ();
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<NSDictionary> ();
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<NSDictionary> ();
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<NSString> (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<NSNumber> ().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<NSDictionary> (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<NSNumber> ().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].
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,20 @@
Firebase Dynamic Links are links that work the way you want, on multiple platforms, and whether or not your app is already installed.
With Dynamic Links, your users get the best available experience for the platform they open your link on. If a user opens a Dynamic Link on iOS or Android, they can be taken directly to the linked content in your native app. If a user opens the same Dynamic Link in a desktop browser, they can be taken to the equivalent content on your website.
In addition, Dynamic Links work across app installs: if a user opens a Dynamic Link on iOS or Android and doesn't have your app installed, the user can be prompted to install it; then, after installation, your app starts and can access the link.
## How does it work?
You create a Dynamic Link either by using the Firebase console, using a REST API, iOS or Android Builder API, or by forming a URL by adding Dynamic Link parameters to a domain specific to your app. These parameters specify the links you want to open, depending on the user's platform and whether your app is installed.
When a user opens one of your Dynamic Links, if your app isn't yet installed, the user is sent to the Play Store or App Store to install your app (unless you specify otherwise), and your app opens. You can then retrieve the link that was passed to your app and handle the link as appropriate for your app.
| | |
|--|--|
| Set up Firebase and the Dynamic Links SDK | Enable Firebase Dynamic Links for your Firebase project in the Firebase console. Then, include the Dynamic Links SDK in your app. |
| Create Dynamic Links | You can create Dynamic Links programmatically or by using the Firebase console. |
| Handle Dynamic Links in your app | When your app opens, use the Dynamic Links SDK to check if a Dynamic Link was passed to it. If so, get the deep link from the Dynamic Link data and handle the deep link as necessary. |
| View analytics data | Track the performance of your Dynamic Links in the Firebase console. |
<sub>_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/) to see original Firebase documentation._</sub>

Просмотреть файл

@ -0,0 +1,281 @@
# Firebase Dynamic Links on iOS
With Firebase Dynamic Links, you can give new users of your app a personalized onboarding experience, and thus increase user sign-ups, user retention, and long-term user engagement.
Dynamic Links are links into an app that work whether or not users have installed the app yet. When users open a Dynamic Link into an app that is not installed, the app's App Store page opens, where users can install the app. After users install and open the app, the app handles the link.
An app might handle the link by opening the app's content using custom URL schemes on iOS 8, or handling Universal Links on iOS 9 and newer. Or, an app could handle the link by initiating some app-specific logic such as crediting the user with a coupon, or displaying a specific welcome screen.
## Table of content
- [Prerequisites](#prerequisites)
- [Add Firebase to your app](#add-firebase-to-your-app)
- [Configure Dynamic Links in your app](#configure-dynamic-links-in-your-app)
- [Use the iOS Builder API](#use-the-ios-builder-api)
- [Create a long link from parameters](#create-a-long-link-from-parameters)
- [Set the length of a short Dynamic Link](#set-the-length-of-a-short-dynamic-link)
- [Create a short link from a long link](#create-a-short-link-from-a-long-link)
- [Manually constructing a Dynamic Link URL](#manually-constructing-a-dynamic-link-url)
- [Open Dynamic Links in your app](#open-dynamic-links-in-your-app)
- [Known issues](#known-issues)
## Prerequisites
Firebase Dynamic Links requires iOS 8 or newer. You can target iOS 7 in your app, but Firebase Dynamic Links SDK calls only function on apps running iOS 8 or newer.
## 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.
5. Go to **Project settings**, in **General** tab select your recently created iOS bundle ID.
6. Set your App Store ID, you can find your App Store ID in your apps URL. (e.g. https://itunes.apple.com/us/app/yourapp/id123456789, **123456789** is your App Store ID).
7. Set your Team ID, you can find your Team ID in the Apple Member Center under the [membership tab][3].
8. In [Firebase console][1], in your project menu go to Dynamic Links tab, accept the terms of service if you are prompted to do so, copy Url link, paste it into your Navigation bar and add **/apple-app-site-association** (e.g https://app_code.app.goo.gl//apple-app-site-association)
9. Your app is connected if the apple-app-site-association file contains a reference to your app's App Store ID and bundle ID, for example:
```
{"applinks":{"apps":[],"details":[{"appID":"1234567890.com.example.ios","paths":["/*"]}]}}
```
If the details field is empty, double-check that you specified your Team ID.
## Configure Dynamic Links 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. In your `Entitlements.plist` file, enable **Associated Domains**.
4. Add `applinks:app_code.app.goo.gl` to **Domains** where **app_code** is your Dynamic Links identifier.
5. In your `Info.plist` file, go to **Advance** and add an Url Type.
6. Set the **Identifier** field to a unique value and the **URL scheme** field to either your Bundle ID or a unique value. If you set the **URL scheme** to a value other than your Bundle ID, you must specify the Bundle ID when you create your Dynamic Links.
7. Add the following lines of code somewhere in your app, typically in your AppDelegate's `FinishedLaunching` method (don't forget to import `Firebase.Core` namespace):
```csharp
App.Configure ();
```
## Create Dynamic Links
There are four ways you can create a Dynamic Link:
* Using the [Firebase console][4]. This is useful if you're creating one-off links to share on social media.
* Using the [Dynamic Link Builder API](#use-the-ios-builder-api). This is the preferred way to dynamically create links in your app for user-to-user sharing or in any situation that requires many links. You can track the performance of Dynamic Links created with the Builder API using the Dynamic Links [Analytics API][5].
* Using the [REST API][6]. This is the preferred way to dynamically create links on platforms that don't have a Builder API. You can track the performance of Dynamic Links created with the REST API using the Dynamic Links [Analytics API][5].
* [Manually](#manually-constructing-a-dynamic-link-url). If you don't need to track click data and you don't care if the links are long, you can manually construct Dynamic Links using URL parameters, and by doing so, avoid an extra network round trip.
## Use the iOS Builder API
You can create short or long Dynamic Links with the Firebase Dynamic Links iOS Builder API. This API accepts either a long Dynamic Link or an object containing Dynamic Link parameters, and returns a URL like the following example:
```
https://abc123.app.goo.gl/WXYZ
```
### Create a long link from parameters
You can create a Dynamic Link programmatically by setting the following parameters and getting the `DynamicLinkComponents.Url` parameter:
```csharp
var link = NSUrl.FromString (dictionary ["Link"].ToString ());
var components = DynamicLinkComponents.FromLink (link, Dynamic_Link_Domain);
var source = dictionary ["Source"].ToString ();
var medium = dictionary ["Medium"].ToString ();
var campaign = dictionary ["Campaign"].ToString ();
var analyticsParams = DynamicLinkGoogleAnalyticsParameters.FromSource (source, medium, campaign);
analyticsParams.Term = dictionary ["Term"].ToString ();
analyticsParams.Content = dictionary ["Content"].ToString ();
components.AnalyticsParameters = analyticsParams;
var bundleId = dictionary ["BundleID"].ToString ();
if (!string.IsNullOrWhiteSpace (bundleId)) {
var iOSParams = DynamicLinkiOSParameters.FromBundleId (bundleId);
iOSParams.FallbackUrl = NSUrl.FromString (dictionary ["FallbackURL"].ToString ());
iOSParams.MinimumAppVersion = dictionary ["MinimumAppVersion"].ToString ();
iOSParams.CustomScheme = dictionary ["CustomScheme"].ToString ();
iOSParams.IPadBundleId = dictionary ["IPadBundleId"].ToString ();
iOSParams.IPadFallbackUrl = NSUrl.FromString (dictionary ["IPadFallbackUrl"].ToString ());
iOSParams.AppStoreId = dictionary ["AppStoreId"].ToString ();
components.IOSParameters = iOSParams;
var appStoreParams = DynamicLinkiTunesConnectAnalyticsParameters.Create ();
appStoreParams.AffiliateToken = dictionary ["AffiliateToken"].ToString ();
appStoreParams.CampaignToken = dictionary ["CampaignToken"].ToString ();
appStoreParams.ProviderToken = dictionary ["ProviderToken"].ToString ();
components.ITunesConnectParameters = appStoreParams;
}
var packageName = dictionary ["PackageName"].ToString ();
if (!string.IsNullOrWhiteSpace (packageName)) {
var androidParams = DynamicLinkAndroidParameters.FromPackageName (packageName);
androidParams.FallbackUrl = NSUrl.FromString (dictionary ["FallbackURL"].ToString ());
androidParams.MinimumVersion = nint.Parse (dictionary ["MinimumAppVersion"].ToString ());
components.AndroidParameters = androidParams;
}
var socialParams = DynamicLinkSocialMetaTagParameters.Create ();
socialParams.Title = dictionary ["Title"].ToString ();
socialParams.DescriptionText = dictionary ["DescriptionText"].ToString ();
socialParams.ImageUrl = NSUrl.FromString (dictionary ["ImageUrl"].ToString ());
components.SocialMetaTagParameters = socialParams;
var longLink = components.Url;
Console.WriteLine (longLink.AbsoluteString);
```
### Set the length of a short Dynamic Link
You can also set the `PathLength` parameter to specify how the path component of the short Dynamic Link is generated:
```csharp
var options = DynamicLinkComponentsOptions.Create ();
options.PathLength = ShortDynamicLinkPathLength.Unguessable;
components.Options = options;
```
By default, or if you set the parameter to `Unguessable`, the path component will be a 17-character string, like in the following example:
```
https://abc123.app.goo.gl/UVWXYZuvwxyz12345
```
Such strings are created by base62-encoding randomly generated 96-bit numbers. Use this setting to prevent your Dynamic Links URLs from being guessed and crawled, which can potentially expose sensitive information to unintended recipients.
If you set the parameter to `Short`, the path component will be a string that is only as long as needed to be unique, with a minimum length of 4 characters:
```
https://abc123.app.goo.gl/WXYZ
```
Use this method if sensitive information would not be exposed if a short Dynamic Link URL were guessed.
### Create a short link from a long link
You can use the Firebase Dynamic Links API to shorten a long Dynamic Link. To do so, call:
```csharp
components.GetShortenUrl ((shortUrl, warnings, error) => {
// Handle shortURL or error.
if (error != null) {
Console.WriteLine ($"Error generating short link: {error.LocalizedDescription}");
return;
}
Console.WriteLine (shortUrl.AbsoluteString);
});
// async/await way:
try {
var result = await components.GetShortenUrlAsync ();
Console.WriteLine (result.ShortUrl.AbsoluteString);
} catch (NSErrorException ex) {
Console.WriteLine ($"Error generating short link: {ex.Error.LocalizedDescription}");
}
```
### Specifying a custom URL scheme for Dynamic Links
By default, Dynamic Links uses your app's bundle identifier as the URL scheme needed to open up your application. We recommend staying with this default value to keep your implementation simple.
However, developers who are already using a custom URL scheme for other purposes may wish to use this same custom URL scheme for their Dynamic Links as well. If you are in this situation, you can specify a different URL scheme for your Firebase Dynamic Links by following these steps:
1. When setting up your app, make sure you specify the default URL scheme to be used by your application before configuring your FirebaseApp shared instance:
```csharp
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Set DeepLinkUrlScheme to the custom URL scheme you defined in your
// Info.plist.
Options.DefaultInstance.DeepLinkUrlScheme = "com.xamarin.firebase.ios.dynamiclinkssample";
App.Configure ();
return true;
}
```
2. Whenever you create any Dynamic Link, you will need to specify the custom URL scheme that your app uses. You can do this through the Firebase console, setting the `customScheme` in the Builder API, specifying the `ius` parameter in your URL, or sending the `iosCustomScheme` parameter to the REST API
## Manually constructing a Dynamic Link URL
Please, read this [Firebase documentation][7] to learn how to construct a Dynamic Link URL manually.
## Open Dynamic Links in your app
You will need to override `OpenUrl (UIApplication, NSUrl, string, NSObject)` method to support iOS 8 or older and `OpenUrl (UIApplication, NSUrl, NSDictionary)` method to support iOS 9 or newer. These methods handle links received through your app's custom URL scheme. These methods are called when your app receives a link on iOS 8 and older, and when your app is opened for the first time after installation on any version of iOS:
```csharp
// Handle Custom Url Schemes for iOS 9 or newer
public override bool OpenUrl (UIApplication app, NSUrl url, NSDictionary options)
{
return OpenUrl (app, url, null, null);
}
// Handle Custom Url Schemes for iOS 8 or older
public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
Console.WriteLine ("I'm handling a link through the OpenUrl method.");
var dynamicLink = DynamicLinks.SharedInstance?.FromCustomSchemeUrl (url);
if (dynamicLink == null)
return false;
// Handle the deep link. For example, show the deep-linked content or
// apply a promotional offer to the user's account.
return true;
}
```
Override `ContinueUserActivity (UIApplication, NSUserActivity, UIApplicationRestorationHandler)` method to handle links received as [Universal Links][8] on iOS 9 and newer:
```csharp
// Handle links received as Universal Links on iOS 9 or later
public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
return DynamicLinks.SharedInstance.HandleUniversalLink (userActivity.WebPageUrl, (dynamicLink, error) => {
if (error != null) {
System.Console.WriteLine (error.LocalizedDescription);
return;
}
// Handle Universal Link
});
}
```
## View Dynamic Links Analytics Data
To help you gauge the effectiveness of your promotions and campaigns, Firebase Dynamic Links provides several ways to view analytics data and integrate with analytics tools.
To learn more about this, please, read the following [documentation][10].
## Debugging Dynamic Links
To help you debug your Dynamic Links, you can preview your Dynamic Links' behavior on different platforms and configurations with an automatically-generated flowchart. Generate the flowchart by adding the d=1 parameter to any short or long Dynamic Link. For example, app_code.app.goo.gl/path?d=1 for a short Dynamic Link.
To learn more about this, please, read the following [documentation][11].
## Generate link previews with social metadata
To learn about this, please, read the following [documentation][12].
<sub>_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._</sub>
[1]: https://firebase.google.com/console/
[2]: http://support.google.com/firebase/answer/7015592
[3]: https://developer.apple.com/account/#/membership
[4]: https://console.firebase.google.com/project/_/durablelinks/links/
[5]: https://firebase.google.com/docs/reference/dynamic-links/analytics
[6]: https://firebase.google.com/docs/dynamic-links/rest
[7]: https://firebase.google.com/docs/dynamic-links/create-manually
[8]: https://developer.apple.com/library/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html
[10]: https://firebase.google.com/docs/dynamic-links/analytics
[11]: https://firebase.google.com/docs/dynamic-links/debug
[12]: https://firebase.google.com/docs/dynamic-links/link-previews

Просмотреть файл

@ -0,0 +1,39 @@
Firebase Invites are an out-of-the-box solution for app referrals and sharing via email or SMS.
Word of mouth is one of the most effective ways of getting users to install your app. In a [recent study](https://think.storage.googleapis.com/docs/mobile-app-marketing-insights.pdf) of thousands of smartphone users, researchers found that the #1 reason people discovered an app is because they heard about it from a friend or colleague. Firebase Invites makes it easy to turn your app's users into your app's strongest advocates.
Firebase Invites builds on Firebase Dynamic Links. which ensures that recipients of links have the best possible experience for their platform and the apps they have installed.
## Key capabilities
| | |
|-:|--|
| **Rich sharing that's easy for users** | Firebase Invites makes it simple for users to send content to their friends, over both SMS and email, by ensuring that referral codes, recipe entries, or other shared content gets passed along with the invitation—no cutting-and-pasting required. |
| **Rich sharing that's easy to implement** | Firebase Invites handles the invitation flow for you, allowing you to deliver a straightforward user experience without taking engineering time away from the rest of your app. |
| **Invitations that survive the installation process** | Because Firebase Invites is built on Dynamic Links, invitations work across the App Store and Play Store installation processes and ensure that recipients get the referral code or shared content, whether or not they have your app installed. |
### Complete list of features
| Sending invitations | |
|--------------------:|--|
| **Combines the most common sharing channels** | Firebase Invites can be sent over SMS or email. |
| **Merged contacts selector** | The share screen's contact list is populated from the user's Google Contacts and the contacts stored locally on the device. |
| **Recipient recommendations** | The share screen recommends recipients based on the contacts the user communicates with frequently. |
| **Customizable invitation message** | You can set the default message to be sent with invitations. This message can be edited by the user when sending invitations. |
| **Customizable rich-text email invitations** | You can customize email invitations in either of two ways: <br /> <ul><li>Provide custom images that will be used along with additional text and graphics from the app's entry in the App Store or Play Store.</li> <br /> <li>Provide HTML for a fully customized email invitation.</li></ul> |
| Receiving invitations | |
|----------------------:|--|
| **Installation flow initiation** | Firebase Invites smartly directs the recipient to the appropriate store when they open the link and need to install the app. iOS users are sent to the App Store, Android users are sent to the Play Store, and web users are sent to the store for the sender's platform. |
| **Installation flow survival** | Invitations use Dynamic Links, which ensure that the link information contained in the invitation doesn't get lost, even if the user has to install the app first. |
| **Low friction for users** | iOS and Android users can receive invitations without signing in to their Google Accounts. |
## How does it work?
![FirebaseInvites_HowItWorks](https://firebase.google.com/docs/invites/images/send-invitations.png)
When a user taps one of your app's Share buttons and chooses the Firebase Invites channel—usually named "Email and SMS"—the Firebase Invites sharing screen opens. From the sharing screen, the user selects recipients from their Google contacts and contacts stored locally on the device, optionally customizes the invitation message and sends the invitations. Invitations are sent by email or SMS, depending on the available contact information, and contain a Dynamic Link to your app.
When the invitation's recipients open the Dynamic Link in the invitation, they are sent to the Play Store or App Store if they need to install your app; then, your app opens and can retrieve and handle the link.
<sub>_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/invites/) to see original Firebase documentation._</sub>

Просмотреть файл

@ -0,0 +1,167 @@
# Send and Receive Firebase Invites from Your iOS App
## Table of content
- [Send and Receive Firebase Invites from Your iOS App](#send-and-receive-firebase-invites-from-your-ios-app)
- [Table of content](#table-of-content)
- [Prerequisites](#prerequisites)
- [Add Firebase to your app](#add-firebase-to-your-app)
- [Configure Invites in your app](#configure-invites-in-your-app)
- [Give your app access to your Contacts](#give-your-app-access-to-your-contacts)
- [Handle incoming app invites](#handle-incoming-app-invites)
- [Enable your users to send app invites](#enable-your-users-to-send-app-invites)
## Prerequisites
Firebase Invites requires iOS 8 or newer. You can target iOS 7 in your app, but all Firebase Invites SDK calls will be no-ops if the app isn't running on iOS 8 or newer.
## 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.
5. Go to **Project settings**, in **General** tab select your recently created iOS bundle ID.
6. Set your App Store ID, you can find your App Store ID in your apps URL. (e.g. https://itunes.apple.com/us/app/yourapp/id123456789, **123456789** is your App Store ID).
7. Set your Team ID, you can find your Team ID in the Apple Member Center under the [membership tab][3].
8. You must enable Firebase Dynamic Links to use Firebase Invites. In [Firebase console][1], in your project menu go to Dynamic Links tab, accept the terms of service if you are prompted to do so, copy Url link, paste it into your Navigation bar and add **/apple-app-site-association** (e.g https://app_code.app.goo.gl//apple-app-site-association)
9. Your app is connected if the apple-app-site-association file contains a reference to your app's App Store ID and bundle ID, for example:
```
{"applinks":{"apps":[],"details":[{"appID":"1234567890.com.example.ios","paths":["/*"]}]}}
```
If the details field is empty, double-check that you specified your Team ID.
If you haven't created a Dynamic Link follow this [documentation][3].
## Configure Invites 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_SIGNIN_ENABLED` and `IS_APPINVITE_ENABLED` values 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 ();
```
## Give your app access to your Contacts
Invites, before sending your invitation, access to your Contacts to show you a list with them so you can invite everyone to try your awesome app! To allow Invites to achieve this, you need to do the following steps in Visual Studio:
1. Open your `Info.plist` and go to **Source** tab.
2. Add a new entry and search for **Privacy - Contacts Usage Description**.
3. Add your message that will be displayed when Invites tries to access to your contacts as value. For example: "MyRecipeApp uses your contacts to make it easy to share recipes with your friends."
## Handle incoming app invites
After you have configured your app, you must next enable your app to handle incoming app invites.
When a user selects an incoming app invite on their iOS device, if the user has not yet installed your app, they can choose to install your app from its iTunes App Store page. When the user opens your app for the first time, it's important for your app to provide a personalized onboarding experience to increase the likelihood they will become an engaged, long-term user of your app. To help you do this, the Invites SDK provides the deeplink and invitation ID associated with the app invite received by the user.
> ![note_icon] **_Note:_** _If the Invites SDK indicates a weak match for a deeplink, it means that the match between the deeplink and the receiving device may not be perfect. In this case your app should reveal no personal information from the deeplink._
```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)
{
// Handle Sign In
if (SignIn.SharedInstance.HandleUrl (url, sourceApplication ?? "", annotation))
return true;
// Handle App Invite requests
return Invites.HandleUniversalLink (url, HandleInvitesUniversalLink);
void HandleInvitesUniversalLink (ReceivedInvite receivedInvite, NSError error)
{
// ...
}
}
```
## Enable your users to send app invites
Now that your app is ready to handle incoming invites correctly, it is time to enable your app to send invitations to the user's contacts.
Before a user can send Invites, the user must be signed in with their Google Account. Follow [Google Sign-In getting started][4] to integrate Sign-In into your
app.
To send invitations, first declare a class that implements the `IInviteDelegate` interface:
```csharp
public class ViewController : UIViewController, IInviteDelegate
```
Then, add a **Send Invitation** button to your app. You can add this button as an option in your main menu, or add this button alongside your deep-linkable content, so that users can send specific content along with the invitation. See the [Firebase Invites best practices][5].
When users tap your **Send Invitation** button, open the invitation dialog:
```csharp
// NOTE: You must have the App Store ID set in your developer console project
// in order for invitations to successfully be sent.
public void SendInvite ()
{
// When you create the invitation dialog, you must specify the title
// of the invitation dialog and the invitation message to send.
// You can also customize the image and deep link URL that
// get sent in the invitation
var inviteDialog = Invites.GetInviteDialog ();
inviteDialog.SetInviteDelegate (this);
inviteDialog.SetTitle ("Invites Sample");
// A message hint for the dialog. Note this manifests differently depending on the
// received invitation type. For example, in an email invite this appears as the subject.
inviteDialog.SetMessage ($"Try this out! {anUrl}");
// These following values are optionals and are only sent via email
inviteDialog.SetDeepLink ("app_url");
inviteDialog.SetDescription ("A description of the app");
inviteDialog.SetCustomImage ("The url of the image");
inviteDialog.SetCallToActionText ("Button title of the invitations");
// If you have an Android version of your app and you want to send
// an invitation that can be opened on Android in addition to iOS
var targetApp = new InvitesTargetApplication { AndroidClientId = "Android ID" };
inviteDialog.SetOtherPlatformsTargetApplication (targetApp);
inviteDialog.Open ();
}
```
If you want to learn more about Invitation parts, see the following [documentation][6].
The `SendInvite` method above then opens the contact chooser dialog where the user selects the contacts to invite. Invitations are sent by email or SMS. After the user sends the invitation, your app receives a callback to the `InviteFinished` method of `IInviteDelegate` interface:
```csharp
[Export ("inviteFinishedWithInvitations:error:")]
public void InviteFinished (string [] invitationIds, NSError error)
{
if (error == null) {
foreach (var id in invitationIds)
System.Console.WriteLine (id);
} else {
System.Console.WriteLine (error.LocalizedDescription);
}
}
```
<sub>_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/invites/ios) to see original Firebase documentation._</sub>
[1]: https://firebase.google.com/console/
[2]: http://support.google.com/firebase/answer/7015592
[3]: https://firebase.google.com/docs/dynamic-links/ios#create-a-dynamic-link
[4]: https://components.xamarin.com/gettingstarted/googleiossignin
[5]: https://firebase.google.com/docs/invites/best-practices
[6]: https://firebase.google.com/docs/invites/ios#customize-the-invitation
[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

Просмотреть файл

@ -0,0 +1,19 @@
# Custom Models
If you're an experienced ML developer and ML Kit's pre-built models don't meet your needs, you can use a custom [TensorFlow Lite][1] model with ML Kit.
Host your TensorFlow Lite models using Firebase or package them with your app. Then, use the ML Kit SDK to perform inference using the best-available version of your custom model. If you host your model with Firebase, ML Kit automatically updates your users with the latest version.
## Key capabilities
| | |
|--|--|
| **TensorFlow Lite model hosting** | Host your models using Firebase to reduce your app's binary size and to make sure your app is always using the most recent version available of your model. |
| **On-device ML inference** | Perform inference in an iOS or Android app by using the ML Kit SDK to run your custom TensorFlow Lite model. The model can be bundled with the app, hosted in the Cloud, or both. |
| **Automatic model fallback** | Specify multiple model sources; use a locally-stored model when the Cloud-hosted model is unavailable. |
| **Automatic model updates** | Configure the conditions under which your app automatically downloads new versions of your model: when the user's device is idle, is charging, or has a Wi-Fi connection. |
<sub>_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/ml-kit/use-custom-models) to see original Firebase documentation._</sub>
[1]: https://www.tensorflow.org/mobile/tflite/

Просмотреть файл

@ -0,0 +1,268 @@
# Use a TensorFlow Lite model for inference with ML Kit on iOS
You can use ML Kit to perform on-device inference with a TensorFlow Lite model.
ML Kit can use TensorFlow Lite models only on devices running iOS 9 and newer.
## Table of Content
- [Use a TensorFlow Lite model for inference with ML Kit on iOS](#use-a-tensorflow-lite-model-for-inference-with-ml-kit-on-ios)
- [Table of Content](#table-of-content)
- [Add Firebase to your app](#add-firebase-to-your-app)
- [Configure MLKit in your app](#configure-mlkit-in-your-app)
- [Host or bundle your model](#host-or-bundle-your-model)
- [Host models on Firebase](#host-models-on-firebase)
- [Bundle models with an app](#bundle-models-with-an-app)
- [Load the model](#load-the-model)
- [Configure a Firebase-hosted model source](#configure-a-firebase-hosted-model-source)
- [Configure a local model source](#configure-a-local-model-source)
- [Create an interpreter from your model sources](#create-an-interpreter-from-your-model-sources)
- [Specify the model's input and output](#specify-the-models-input-and-output)
- [Perform inference on input data](#perform-inference-on-input-data)
- [Appendix: Model security](#appendix-model-security)
- [Use a custom TensorFlow Lite build](#use-a-custom-tensorflow-lite-build)
- [Prerequisites](#prerequisites)
- [Building the Tensorflow Lite library](#building-the-tensorflow-lite-library)
- [Add your custom TensorFlow Lite framework](#add-your-custom-tensorflow-lite-framework)
## 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 MLKit 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** 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 ();
```
Convert the TensorFlow model you want to use to TensorFlow Lite format. See [TOCO: TensorFlow Lite Optimizing Converter][3].
## Host or bundle your model
Before you can use a TensorFlow Lite model for inference in your app, you must make the model available to ML Kit. ML Kit can use TensorFlow Lite models hosted remotely using Firebase, bundled with the app binary, or both.
By hosting a model on Firebase, you can update the model without releasing a new app version, and you can use Remote Config and A/B Testing to dynamically serve different models to different sets of users.
If you choose to only provide the model by hosting it with Firebase, and not bundle it with your app, you can reduce the initial download size of your app. Keep in mind, though, that if the model is not bundled with your app, any model-related functionality will not be available until your app downloads the model for the first time.
By bundling your model with your app, you can ensure your app's ML features still work when the Firebase-hosted model isn't available.
> ![note_icon] _Before you use a custom model in a publicly-available app, be aware of the [security implications][4]._
### Host models on Firebase
To host your TensorFlow Lite model on Firebase:
1. In the **ML Kit** section of the [Firebase console][1], click the **Custom** tab.
2. Click **Add custom model** (or **Add another model**).
3. Specify a name that will be used to identify your model in your Firebase project, then upload the TensorFlow Lite model file (usually ending in `.tflite` or `.lite`).
After you add a custom model to your Firebase project, you can reference the model in your apps using the name you specified. At any time, you can upload a new TensorFlow Lite model, and your app will download the new model and start using it when the app next restarts. You can define the device conditions required for your app to attempt to update the model (see below).
### Bundle models with an app
To bundle your TensorFlow Lite model with your app, add the model file (usually ending in `.tflite` or `.lite`) into the **Resources** folder in your Visual Studio project, or, add it anywhere on your project tree and change **build action** behaviour to `Bundle Resource` by Right clicking/Build Action. The model file will be included in the app bundle and available to ML Kit.
## Load the model
To use your TensorFlow Lite model in your app, first configure ML Kit with the locations where your model is available: on the cloud using Firebase, in local storage, or both. If you specify both a local and cloud model source, ML Kit will use the cloud source if it is available, and fall back to the locally-stored model if the cloud source isn't available.
### Configure a Firebase-hosted model source
If you hosted your model with Firebase, register a `CloudModelSource` object, specifying the name you assigned the model when you uploaded it, and the conditions under which ML Kit should download the model initially and when updates are available:
```csharp
var conditions = new ModelDownloadConditions (true, true);
var cloudModelSource = new CloudModelSource ("my_cloud_model", true, conditions, conditions);
var registrationSuccessful = ModelManager.SharedInstance.Register (cloudModelSource);
```
### Configure a local model source
If you bundled the model with your app, register a `LocalModelSource` object, specifying the filename of the TensorFlow Lite model and assigning the model a name you will use in the next step:
```csharp
var modelPath = NSBundle.MainBundle.PathForResource ("my_model", "tflite");
if (modelPath == null)
return;
var localModelSource = new LocalModelSource ("my_local_model", modelPath);
var registrationSuccessful = ModelManager.SharedInstance.Register (localModelSource);
```
### Create an interpreter from your model sources
After you configure your model sources, create a `ModelOptions` object with the Cloud source, the local source, or both, and use it to get an instance of `ModelInterpreter`. If you only have one source, specify `null` for the source type you don't use:
```csharp
var options = new ModelOptions ("my_cloud_model", "my_local_model");
var interpreter = ModelInterpreter.Create (options);
```
## Specify the model's input and output
Next, configure the model interpreter's input and output formats.
A TensorFlow Lite model takes as input and produces as output one or more multidimensional arrays. These arrays contain either `byte`, `int`, `long`, or `float` values. You must configure ML Kit with the number and dimensions ("shape") of the arrays your model uses.
If you don't know the shape and data type of your model's input and output, you can use the TensorFlow Lite Python interpreter to inspect your model. For example:
```python
import tensorflow as tf
interpreter = tf.lite.Interpreter(model_path="my_model.tflite")
interpreter.allocate_tensors()
# Print input shape and type
print(interpreter.get_input_details()[0]['shape']) # Example: [1 224 224 3]
print(interpreter.get_input_details()[0]['dtype']) # Example: <class 'numpy.float32'>
# Print output shape and type
print(interpreter.get_output_details()[0]['shape']) # Example: [1 1000]
print(interpreter.get_output_details()[0]['dtype']) # Example: <class 'numpy.float32'>
```
After you determine the format of your model's input and output, configure your app's model interpreter by creating a `ModelInputOutputOptions` object.
For example, a floating-point image classification model might take as input an _**N**x224x224x3_ array of `float` values, representing a batch of **N** _224x224_ three-channel (RGB) images, and produce as output a list of 1000 `float` values, each representing the probability the image is a member of one of the 1000 categories the model predicts.
For such a model, you would configure the model interpreter's input and output as shown below:
```csharp
var ioOptions = new ModelInputOutputOptions ();
ioOptions.SetInputFormat (0, ModelElementType.Float32, new nuint[] { 1, 224, 224, 3 }, out NSError inputError);
if (inputError != null) { return; }
ioOptions.SetOutputFormat (0, ModelElementType.Float32, new nuint [] { 1, 1000 }, out NSError outputError);
if (outputError != null) { return; }
```
## Perform inference on input data
Finally, to perform inference using the model, get your input data, perform any transformations on the data that might be necessary for your model, and build a `NSData` object that contains the data:
```csharp
var inputData = new NSMutableData (0);
// Prepare your input data
var modelInputs = new ModelInputs ();
modelInputs.AddInput (inputData, out NSError error);
if (error != null) { return; }
```
After you prepare your model input, pass the input and input/output options to your `ModelInterpreter`'s `Run` method.
```csharp
interpreter.Run (modelInputs, ioOptions, HandleModelInterpreterRunCallback);
void HandleModelInterpreterRunCallback (ModelOutputs outputs, NSError error)
{
if (error != null || outputs == null) {
return;
}
// Process outputs
}
// or use async / await
try {
ModelOutputs outputs = await interpreter.RunAsync (modelInputs, ioOptions);
if (outputs == null) {
return;
}
// Process outputs
} catch (NSErrorException ex) {
}
```
You can get the output by calling the `GetOutput` method of the object that is returned. For example:
```csharp
// Get first and only output of inference with a batch size of 1
var output = outputs.GetOutput (0, out NSError outputError) as NSArray;
var probabilities = output.GetItem<NSArray<NSNumber>> (0);
```
How you use the output depends on the model you are using.
## Appendix: Model security
Regardless of how you make your TensorFlow Lite models available to ML Kit, ML Kit stores them in the standard serialized protobuf format in local storage.
In theory, this means that anybody can copy your model. However, in practice, most models are so application-specific and obfuscated by optimizations that the risk is similar to that of competitors disassembling and reusing your code. Nevertheless, you should be aware of this risk before you use a custom model in your app.
# Use a custom TensorFlow Lite build
If you're an experienced ML developer and the pre-built TensorFlow Lite library doesn't meet your needs, you can use a custom [TensorFlow Lite][5] build with ML Kit. For example, you may want to add custom ops.
## Prerequisites
* A working [TensorFlow Lite][6] build environment
* A checkout of TensorFlow Lite 1.10.1
You can check out the correct version using Git:
```
$ git checkout -b work
$ git reset --hard tflite-v1.10.1
$ git cherry-pick 4dcfddc5d12018a5a0fdca652b9221ed95e9eb23
```
## Building the Tensorflow Lite library
1. Build Tensorflow Lite (with your modifications) following the [standard instructions][7]
2. Build the framework:
```
$ tensorflow/lite/lib_package/create_ios_frameworks.sh
```
The generated framework can be found at `tensorflow/lite/gen/ios_frameworks/tensorflow_lite.framework.zip`
> ![note_icon] **_Note:_** _There have been [build issues reported][8] with Xcode 9.3_
## Add your custom TensorFlow Lite framework
To be able to use your TensorFlow Lite framework, the framework must be copied to TensorFlow Lite Xamarin Build Download cache folder, to do so, follow these steps:
1. Unzip the tensorflow_lite.framework.zip file generated above
2. Copy the unzipped tensorflow_lite.framework to the TensorFlow Lite Xamarin Build Download cache folder
* Mac: `~/Library/Caches/XamarinBuildDownload/TnsrFlwLt-1.10.1/Frameworks`
* Windows: `%LOCALAPPDATA%\XamarinBuildDownloadCache\TnsrFlwLt-1.10.1\Frameworks`
> ![note_icon] _**Note:**_ _If you cannot find the folder, please, open Visual Studio and build your project so Xamarin Build Download can create the TensorFlow Lite cache folder. The `1.10.1` version can vary._
3. Erase your `bin` and `obj` folders of your project and build again on Visual Studio
If you want to use the default TensorFlow Lite framework again, just erase everything that start with `TnsrFlwLt-1.10.1` in the Xamarin Build Download cache folder, erase the `bin` and `obj` folders and build again your project on Visual Studio.
<sub>_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/ml-kit/ios/use-custom-models) to see original Firebase documentation._</sub>
[1]: https://firebase.google.com/console/
[2]: http://support.google.com/firebase/answer/7015592
[3]: https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/toco
[4]: https://firebase.google.com/docs/ml-kit/ios/use-custom-models#model_security
[5]: https://www.tensorflow.org/mobile/tflite/
[6]: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/README.md#building-tensorflow-lite-and-the-demo-app-from-source
[7]: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/ios.md
[8]: https://github.com/tensorflow/tensorflow/issues/18356
[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

31
docs/Firebase/MLKit/Details.md Executable file
Просмотреть файл

@ -0,0 +1,31 @@
Use machine learning in your apps to solve real-world problems.
ML Kit is a mobile SDK that brings Google's machine learning expertise to Android and iOS apps in a powerful yet easy-to-use package. Whether you're new or experienced in machine learning, you can implement the functionality you need in just a few lines of code. There's no need to have deep knowledge of neural networks or model optimization to get started. On the other hand, if you are an experienced ML developer, ML Kit provides convenient APIs that help you use your custom TensorFlow Lite models in your mobile apps.
## Key capabilities
| | |
|-:|-|
| **Production-ready for common use cases:** | ML Kit comes with a set of ready-to-use APIs for common mobile use cases: recognizing text, detecting faces, identifying landmarks, scanning barcodes, and labeling images. Simply pass in data to the ML Kit library and it gives you the information you need. |
| **On-device or in the cloud:** | ML Kits selection of APIs run on-device or in the cloud. Our on-device APIs can process your data quickly and work even when theres no network connection. Our cloud-based APIs, on the other hand, leverage the power of Google Cloud Platform's machine learning technology to give you an even higher level of accuracy. |
| **Deploy custom models:** | If ML Kit's APIs don't cover your use cases, you can always bring your own existing TensorFlow Lite models. Just upload your model to Firebase, and we'll take care of hosting and serving it to your app. ML Kit acts as an API layer to your custom model, making it simpler to run and use. |
## How does it work?
ML Kit makes it easy to apply ML techniques in your apps by bringing Google's ML technologies, such as the Google Cloud Vision API, TensorFlow Lite, and the Android Neural Networks API together in a single SDK. Whether you need the power of cloud-based processing, the real-time capabilities of mobile-optimized on-device models, or the flexibility of custom TensorFlow Lite models, ML Kit makes it possible with just a few lines of code.
### What features are available on device or in the cloud?
| Feature | On-device | Cloud |
|------------------------|:-----------------:|:-----------------:|
| Text recognition | ![available_icon] | ![available_icon] |
| Face detection | ![available_icon] | |
| Barcode scanning | ![available_icon] | |
| Image labeling | ![available_icon] | ![available_icon] |
| Landmark recognition | | ![available_icon] |
| Custom model inference | ![available_icon] | |
<sub>_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._</sub>
[available_icon]: https://cdn3.iconfinder.com/data/icons/flat-actions-icons-9/512/Tick_Mark-24.png

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,47 @@
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.
Performance Monitoring is currently in [beta release](https://support.google.com/firebase/answer/7011258).
## Key capabilities
| 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. |
## How does it work?
Performance Monitoring monitors traces and HTTP/S network requests in your app.
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).
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).
<sub>_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._</sub>

Просмотреть файл

@ -0,0 +1,323 @@
# Firebase Performance Monitoring for iOS
## Table of content
- [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)
## Prerequisites
Firebase Performance Monitoring requires iOS 8 or newer.
## 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 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.
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,31 @@
Change the behavior and appearance of your app without publishing an app update.
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.
## Key capabilities
| | |
|-:|--|
| **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. |
## How does it work?
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.
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.
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).
<sub>_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._</sub>

Просмотреть файл

@ -0,0 +1,190 @@
# Use Firebase Remote Config on iOS
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.
## Table of content
- [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)
## Remote Config Parameters and Conditions
Please, read this [Firebase documentation][5] to learn about Parameters and Conditions.
## Firebase Remote Config API Overview
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. <br /> **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\<type\> 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 ("<Your plist name here>");
```
### 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.
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,27 @@
Firebase Storage is built for app developers who need to store and serve user-generated content, such as photos or videos.
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.
## Key capabilities
| | |
|-:|--|
| **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. |
## How does it work?
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.
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].
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.
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,810 @@
# Get Started
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.
## Table of content
- [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)
## Add Firebase to your app
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.
## Configure Storage 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 ();
```
## 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://<your-cloud-storage-bucket>
```
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://\<your-firebase-storage-bucket\>**. 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://<your-firebase-storage-bucket>")
// 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://<your-firebase-storage-bucket>/images")
StorageReference imagesRef = rootRef.GetChild ("images");
// Child references can also take paths delimited by '/'
// spaceRef now points to "images/space.jpg" ("gs://<your-firebase-storage-bucket>/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://<your-firebase-storage-bucket>/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://<your-firebase-storage-bucket>/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\<T\>** | **Method** | **StorageUploadTask/</br>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<NSString, NSString> | 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].
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,7 @@
Set up and customize tracking for your mobile apps.
## Report and analyze
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.

Просмотреть файл

@ -0,0 +1,97 @@
# Get your Google Analytics Tracking Id
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.
# iOS Getting Started
There are two steps to getting started with the iOS SDK:
1. Initialize the tracker
2. Add screen measurement
After completing these steps, you'll be able to measure the following with Google Analytics:
* App installations
* Active users and demographics
* Screens and user engagement
* Crashes and exceptions
## Initializing the tracker
To initialize the tracker, use the `Google.Analytics` namespace in your AppDelegate and add this code to your AppDelegate's `FinishedLaunching` method:
```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.

Просмотреть файл

@ -0,0 +1,23 @@
App Indexing puts your app in front of users who use Google Search. It works by indexing the URL patterns you provide in your app manifest and using API calls from your app to make content within your app available to both existing and new users. Specifically, when you support URLs for your app content, iOS users can go directly to it from Search results on their device. Finally, Google uses App Indexing API calls from your app as a ranking signal for your URLs.
## How App Indexing works for iOS
### Support HTTP URLs to your mobile app.
iOS apps support HTTP URLs and use site association to verify the relationship between the app and its website. This allows Google systems to index URLs that work for both your site and your app and to serve them in search results.
Once you associate your app to your website, Google systems quickly recognize the association and begin the crawling and discovery process for your app URLs. This can take between 24 hours and a number of days, depending on a number of factors. Because most app content is currently associated with web content, crawl scheduling works in a fashion similar to that of webpages: Google take into account multiple factors like frequency of content updates, server load, importance, and freshness of the page. Google will send you a message in Search console when content from your app shows up in search (“first impression”). If its been a couple weeks and you still dont see any app links in search, check for crawl errors. Read about [Google Search crawl frequency][1] on Google Help Center.
[1]: https://support.google.com/webmasters/answer/34439
### Add the App Indexing API or SDK.
Use the App Indexing SDK for iOS 9. The use of Google SDK enhances ranking performance for the URLs and provides the basis for link titles and content snippets. This provides the titles and description snippets associated with your content, as well as the history of actions to your app. Users of your app can delete past activity in apps at https://history.google.com/.
### Check your implementation.
Test URLs to your app in your development environment to make sure they lead to the correct content.
## Technical requirements
Google App Indexing documentation for iOS 9 serves iOS universal links from Google Search in Safari. App Indexing for iOS versions 7 and 8 is now deprecated and no longer available for new integrations.

Просмотреть файл

@ -0,0 +1,114 @@
## Support HTTP URLs
App Indexing for iOS 9 uses HTTP URLs to direct users to content in your app. If youve already followed the instructions to [support universal links][1] in your app, you can skip this section. Otherwise, do the following:
### Create the app-to-site association.
This involves two things:
- Add a `com.apple.developer.associated-domains` entitlement in entitlement.plist file that lists each domain associated with your app.
- Create an `apple-app-site-association` file for each associated domain with the content your app supports and host it at the root level.
*Note: The association file must be hosted on a domain that supports HTTPS/TLS, even if the HTTP deep links are not themselves served via HTTPS.*
#### Preparing Your Website
1. Create a apple-app-site-association file. Note that theres no .json file type
2. Place the apple-app-site-association file and identify app IDs and paths on your website. You can define several apps in this file and iOS will follow the app order when looking for a match so you can specify different apps to handle different paths on your website.
Here's an example of file's content:
```
{
"applinks": {
"apps": [],
"details": [
{
"appID": "9JA89QQLNQ.com.apple.wwdc",
"paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]
},
{
"appID": "TeamID.BundleID2",
"paths": [ "*" ]
}
]
}
}
```
The apps key in an apple-app-site-association file must be present and its value must be an empty array. The value of the details key is an array of dictionaries, one dictionary per app that your website supports. The order of the dictionaries in the array determines the order the system follows when looking for a match, so you can specify an app to handle a particular part of your website.
Each app-specific dictionary contains an appID key and a paths key. The value of the appID key is the apps team ID and the bundle ID; the value of the paths key is an array of strings that specify the parts of your website that are supported by the app and the parts of your website that you dont want to associate with the app. To specify an area that should not be handled as a universal link, add “NOT ” (including a space after the T) to the beginning of the path string. For example:
```
"paths": [ "/wwdc/news/", "NOT /videos/wwdc/2010/*", "/videos/wwdc/201?/*"]
```
Upload the apple-app-site-association file in the root of your HTTPS web server. The file should be accessible without redirects at http://<domain>/apple-app-site-association
### Preparing your App
#### Creating an App ID and provisioning profile
To make Universal Links works, you need to create a Provisioning Profile with an Explicit App Id with Associated Domains Services enabled. To achieve this, follow this steps:
1. On the Apple Developer Member Center home page, click Certificates, Identifiers & Profiles.
2. On the Certificates, Identifiers & Profiles page, under the iOS Apps column, click Identifiers.
3. On the iOS App IDs page, click on the **+** button.
4. On the Registering an App ID page, enter an App ID Description/Name, select **Explicit App ID**, enter your apps Bundle ID, enable **Associated Domains
** Service and then click Continue.
5. On the Confirm your App ID page, click Submit.
6. On the Registration complete page, click Done.
7. Create a provisioning profile with the new App ID.
#### Enabling Associated Domains in your app
1. In you Xamarin iOS project, open the Entitlements.plist file
2. Open the Associated Domains header and check **Enable Associated Domains**
3. Add your domains starting with `applinks:`, for example: `applinks:xamarin.com`
#### Add handling for universal links to your app.
Adopt the [UIApplicationDelegate][2] methods so that your app opens the appropriate app content in response to the users click from search results.
```csharp
public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
if (userActivity.ActivityType == NSUserActivityType.BrowsingWeb) {
// Do your magic here.
}
return true;
}
```
[1]: https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1
[2]: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intf/UIApplicationDelegate
## Register your app to Google system
Call the `RegisterApp` method in your `FinishedLaunching` method.
```csharp
AppIndexing.SharedInstance.RegisterApp ("YOUR_ITUNES_ID");
```
You're all set! Once Google's crawlers discover the the URLs in your association file, Google will automatically start indexing any existing or new HTTP deep links to your app.
## Check Your Implementation
Once you've completed setup for App Indexing, you can verify your universal links prior to their appearance in Google Search by tapping a universal link in Safari on your device and making sure that it takes you to the right place in the app.
*Note: You cannot test universal links on simulator.*
## Enhance Your Search Performance
A key way to enhance your app's ranking is to provide high-quality content in your site and your app. Specifically, keep in mind the following:
- Make sure your associated site meets Google's [design and content guidelines][3].
- Follow the same layout practices recommended for your [mobile-friendly homepage][4]. For example, keep your titles short and avoid full-width menus.
In order to ensure a great search experience for users, Google may take corrective action in cases where they see abuse, deception, or other actions that hurt the search experience for users. This can include actions such as demoting or removing your app deep links from Google Search results.
[3]: https://support.google.com/webmasters/answer/35769?vid=1-635785548570479109-2344616627#design_and_content_guidelines
[4]: https://developers.google.com/web/fundamentals/layouts/principles/

Просмотреть файл

@ -0,0 +1,27 @@
App Invites provide a powerful way to organically grow your app, user-to-user. Your users recommend your app to their friends using personalized, contextual invitations powered by Google. App Invites provide a great onboarding experience to your new users. Google optimizes your app install rates by reducing friction and using relevant context at every step of the user invitation flow.
### App Invites flow
App Invites always begins with a user sending an invite from your app. The following diagram illustrates the App Invites flow.
![AppInviteFlow](https://developers.google.com/app-invites/images/ai-ios-flow.svg)
### Sending an invitation
You allow a user to send an invitation from your application by using the `IInviteBuilder` interface in your apps ViewController file. The invite must include a title and can also include a message and deep link data.
The Open method of `IInviteBuilder` opens the contact chooser dialog where the user selects the contacts to invite. The invite can be sent via email or SMS.
### Receiving an invitation
When a user receives and choose to open an invitation URL, the invitation flow branches according to whether or not your app is already installed on the recipients device. If the recipient doesnt have your app, the flow branches to let the user install it. If the recipient is already one of your users, then the optional deep link information in the invite passes to your app for processing.
**App is already installed**
If the recipient has already installed your app, the app will receive the invite URL containing the optional deep link data.
**App is not installed**
If the recipient has not yet installed the app, they can choose to install the app from the iTunes App Store. When the app is opened for the first time, the App Invites SDK will supply a deep link if one is available.

Просмотреть файл

@ -0,0 +1,113 @@
Configuring your App
--------------------
Google provides an easy to use configuration web tool to generate a config file for your app:
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. Enter your App Store ID and click *Enable App Invites*
(If you don't have an App Store ID you can use one of our favorites - 1000846120 or 783488172)
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:
- Identifier: `google` Role: `Editor` URL Schemes: `your.app.bundle.id`
- Identifier: `google` Role: `Editor` URL Schemes: `value of REVERSED_CLIENT_ID from GoogleService-Info.plist`
Setup your AppDelegate
----------------------
In order for App Invites to work properly, you must tell the SDK about some of your application lifecycle events.
In your `AppDelegate.cs`, in the `FinishedLaunching (..)` override, you should add the following code to the start of the method:
```csharp
NSError configureError;
Context.SharedInstance.Configure (out configureError);
if (configureError != null)
Console.WriteLine ("Error configuring the Google context: {0}", configureError);
Invite.ApplicationDidFinishLaunching ();
```
Next, add the following override to your AppDelegate class (or if it already exists, add the code inside the method to the existing implementation:
```csharp
public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
// Handle App Invite requests
var invite = Invite.HandleUrl (url, sourceApplication, annotation);
if (invite != null) {
var message =string.Format ("Deep link from {0} \nInvite ID: {1}\nApp URL: {2}",
sourceApplication, invite.InviteId, invite.DeepLink);
new UIAlertView (@"Deep-link Data", message, null, "OK").Show ();
return true;
}
return SignIn.SharedInstance.HandleUrl (url, sourceApplication, annotation);
}
```
Signing In
----------
In order for app invites to work, your app's user must sign in to their Google account. To do this, you should request a login in your View Controller. This can be done in the `ViewDidLoad` override:
```csharp
// Sign the user in automatically
SignIn.SharedInstance.UIDelegate = this;
SignIn.SharedInstance.Delegate = this;
SignIn.SharedInstance.SignInUser ();
```
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)
// Enable App Invite button
}
```
For more information on Google Sign-In please refer to the [Getting Started Guide][2]
Displaying the AppInvite UI
---------------------------
Once your user is signed in, you can display the AppInvite UI with the following code:
```csharp
var inviteDialog = Invite.InviteDialog;
inviteDialog.SetInviteDelegate (this);
// NOTE: You must have the App Store ID set in your developer console project
// in order for invitations to successfully be sent.
var message = string.Format ("Try this out!\n -{0}",
SignIn.SharedInstance.CurrentUser.Profile.Name);
// A message hint for the dialog. Note this manifests differently depending on the
// received invation type. For example, in an email invite this appears as the subject.
inviteDialog.SetMessage (message);
// Title for the dialog, this is what the user sees before sending the invites.
inviteDialog.SetTitle ("App Invites Example");
inviteDialog.SetDeepLink ("app_url");
inviteDialog.Open ();
```
You can should also implement `IInviteDelegate` and optionally the `InviteFinished` method to be notified when the user has completed the AppInvite dialog:
```csharp
[Export ("inviteFinishedWithInvitations:error:")]
public virtual void InviteFinished (string[] invitationIds, NSError error)
{
var message = error != null ? error.LocalizedDescription :
string.Format ("{0} invites sent", invitationIds.Length);
new UIAlertView ("Done", message, null, "OK").Show ();
}
```
[1]: https://developers.google.com/mobile/add?platform=ios&cntapi=appinvite
[2]: http://components.xamarin.com/gettingstarted/googleiosappinvite

11
docs/Google/Cast/Details.md Executable file
Просмотреть файл

@ -0,0 +1,11 @@
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.
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.
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.
![https://developers.google.com/cast/images/Diagram.png](https://developers.google.com/cast/images/Diagram.png)
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 phones 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.
#####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/).

Просмотреть файл

@ -0,0 +1,708 @@
# Table of Content
- [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)
# Integrate CAF Sender into your iOS App
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.
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 uses the delegation pattern to inform the sender app of events and to move between various states of the Cast app life cycle.
## App flow
The following steps describe the typical high-level execution flow for a sender iOS app:
* 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.
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 |
<sub>_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._</sub>
[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

Просмотреть файл

@ -0,0 +1,14 @@
Send data from your server to your users' devices, and receive messages from devices on the same connection. The GCM service handles all aspects of queueing of messages and delivery to client applications running on target devices, and it is completely free.
### Versatile Messaging Targets
Distribute messages to your client app in any of three ways — to single devices, to groups of devices, or to devices subscribed to topics.
### Downstream Messaging
For purposes such as alerting users, chat messaging or kicking off background processing before the user opens the client app, GCM provides a reliable and battery-efficient connection between your server and devices.
### Upstream Messaging
Send acknowledgments, chats, and other messages from devices back to your server over GCMs reliable and battery-efficient connection channel.

Просмотреть файл

@ -0,0 +1,204 @@
Configuring APNS
----------------
To allow Google to send APNS notifications on your behalf, you will need to export your Developer and/or your Production APNS certificates from the Apple Developer portal as .p12 files.
For more information on this process, see [Apple's Documentation][2].
Configuring your App
--------------------
Google provides an easy to use configuration web tool to generate a config file for your app:
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. Upload your Developer APNS .p12 certificate. You may also want to upload your production .p12 certificate at this time as well.
3. Click *Enable Cloud Messaging*
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. Note the *Sender ID* value. You will use this in your code as the `GCM_SENDER_ID` value.
Setting up your AppDelegate
---------------------------
Your `AppDelegate` will contain most of the GCM related code.
In your `FinishedLaunching` method you should start GCM and request to register for remote notifications:
```csharp
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Configure and Start GCM
var gcmConfig = Google.GoogleCloudMessaging.Config.DefaultConfig;
gcmConfig.ReceiverDelegate = this;
Service.SharedInstance.Start (gcmConfig);
// Register for remote notifications
var notTypes = UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge;
var settings = UIUserNotificationSettings.GetSettingsForTypes (notTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings (settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications ();
// Your user code, if any, here
return true;
}
```
Since in the code above the GCM Config's delegate was set to `this` (the AppDelegate), you should also implement `IReceiverDelegate` in your `AppDelegate` class:
Next, add an override for handling the registration for remote notifications:
```csharp
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
{
// Save our token in memory for future calls to GCM
DeviceToken = deviceToken;
// Configure and start Instance ID
var config = Google.InstanceID.Config.DefaultConfig;
InstanceId.SharedInstance.Start (config);
// Get a GCM token
GetToken ();
}
```
In this method, you will save the `deviceToken` to a variable for later use, as well as configure and start InstanceID which is needed to obtain a GCM Registration Token.
Finally, this method calls `GetToken()` to actually request a GCM Registration token, which is defined as:
```csharp
void GetToken ()
{
// Register APNS Token to GCM
var options = new NSDictionary ();
options.SetValueForKey (DeviceToken, Constants.RegisterAPNSOption);
options.SetValueForKey (new NSNumber(true), Constants.APNSServerTypeSandboxOption);
// Get our token
InstanceId.SharedInstance.Token (
GCM_SENDER_ID,
Constants.ScopeGCM,
options,
(token, error) => Log ("GCM Registration ID: " + token));
}
```
If the `error` value is null in the callback that was passed into the `Token()` method, you should have a valid GCM Registration token which you will then use on your server to send messages to this device.
### Receiving Notifications
Notifications (both APNS and from GCM) will occur inside of the `DidReceiveRemoteNotification` override. When you receive a message you should inform GCM that you successfully received it:
```csharp
public override void DidReceiveRemoteNotification (UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
// Your own notification handling logic here
// Notify GCM we received the message
Service.SharedInstance.AppDidReceiveMessage (userInfo);
}
```
### Connecting to GCM Servers Directly
When your application is in the foreground you should connect directly to GCM's servers:
```csharp
public override void OnActivated (UIApplication application)
{
Service.SharedInstance.Connect (error => {
if (error != null)
Console.WriteLine ("Could not connect to GCM: {0}", error.LocalizedDescription);
else
Console.WriteLine ("Connected to GCM");
});
}
```
It's also important to disconnect from the GCM Server when your application enters the background so you can continue to receive APNS notifications instead:
```csharp
public override void DidEnterBackground (UIApplication application)
{
Service.SharedInstance.Disconnect ();
}
```
### Unregistering from GCM
It's possible to unregister from GCM by deleting your token:
```csharp
public void DeleteToken ()
{
InstanceId.SharedInstance.DeleteToken (
GCM_SENDER_ID,
Constants.ScopeGCM,
error => {
// Callback, non-null error if there was a problem
if (error != null)
Console.WriteLine ("Deleted Token");
else
Console.WriteLine ("Error deleting token");
});
}
```
### Sending Upstream Messages
One of the advantages to being directly connected to GCM's Servers when your app is in the foreground is the ability to send upstream messages back to the server:
```csharp
int messageId = 1;
// We can send upstream messages back to GCM
public void SendUpstreamMessage ()
{
var msg = new NSDictionary ();
msg.SetValueForKey (new NSString ("1234"), new NSString ("userId"));
msg.SetValueForKey (new NSString ("hello world"), new NSString ("msg"));
var to = GCM_SENDER_ID + "@gcm.googleapis.com";
Service.SharedInstance.SendMessage (msg, to, (messageId++).ToString ());
}
```
### Handling Callbacks for Upstream Messages
Since you implemented `IReceiverDelegate` in your AppDelegate, you can add some methods to be invoked as callbacks for upstream messages:
```csharp
[Export ("didDeleteMessagesOnServer")]
public void DidDeleteMessagesOnServer ()
{
// ...
}
[Export ("didSendDataMessageWithID:")]
public void DidSendDataMessage (string messageID)
{
// ...
}
[Export ("willSendDataMessageWithID:error:")]
public void WillSendDataMessage (string messageID, NSError error)
{
// ...
}
```
[1]: https://developers.google.com/mobile/add?platform=ios&cntapi=signin
[2]: https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/ConfiguringPushNotifications/ConfiguringPushNotifications.html

Просмотреть файл

@ -0,0 +1,32 @@
Instance ID provides a unique ID per instance of your Android and iOS apps.
## Key features
In addition to providing unique IDs for authentication, Instance ID can generate security tokens for use with other services. Other features include:
### Generate Security Tokens
Instance ID provides a simple API to generate security tokens that authorize third parties to access your app's server side managed resources. Use these tokens now to authorize push messages for your apps via Google Cloud Messaging.
### Confirm app device is active
The Instance ID server can tell you when the device on which your app is installed was last used. Use this to decide whether to keep data from your app or send a push message to reengage with your users.
### Identify and track apps
Instance ID is unique across all app instances across the world, so your database can use it to uniquely identify and track app instances. Your server-side code can verify, via the Instance ID cloud service, that an Instance ID is genuine and is the same ID as the original app that registered with your server. For privacy, your app can delete an Instance ID so it is no longer associated with any history in the database. The next time your app calls Instance ID it will get an entirely new Instance ID with no relationship to its previous one.
## Instance ID lifecycle
The Instance ID service issues an InstanceID when your app comes online. The InstanceID is backed by a public/private key pair with the private key stored on the local device and the public key registered with the Instance ID service.
Your app can request a fresh InstanceID whenever needed using the `GetID (..)` method. Your app can store it on your server if you have one that supports your app.
Your app can request tokens from the Instance ID service as needed using the `GetToken (..)` method, and like InstanceID, your app can also store tokens on your own server. All tokens issued to your app belong to the app's InstanceID.
Tokens are unique and secure, but your app or the Instance ID service may need to refresh tokens in the event of a security issue or when a user uninstalls and reinstalls your app during device restoration. Your app must implement a listener to respond to token refresh requests from the Instance ID service.
![InstanceIDLifecycle](https://developers.google.com/instance-id/guides/images/iid-lifecycle.png)

Просмотреть файл

@ -0,0 +1,29 @@
## Generating / Fetching an Instance ID
Before attempting to generate or fetch an Instance ID, you must start the InstanceID engine and pass it an instance of `Config`. You can use the Default instance of `Config`:
```csharp
InstanceId.SharedInstance.Start (Config.DefaultConfig);
```
After you've called `Start (..)`, generating and fetching an InstanceID is done the same way:
```csharp
var instanceId = await InstanceId.SharedInstance.GetIDAsync ();
```
This call may take several seconds to complete, so you should plan on not blocking the UI when making this call. You should always receive the same Instance ID value back after the first time you call this method.
## Deleting an Instance ID
It is possible to delete an Instance ID. Once you have deleted a particular Instance ID the next time you call `GetID` or `GetIDAsync` to generate an Instance ID, you will receive a new value.
To delete an instance ID, simply call `DeleteID` or `DeleteIDAsync`:
```csharp
await InstanceId.SharedInstance.DeleteIDAsync ();
```
**Important Note**: In this version of Instance ID, Google is internally invoking the callback handler that you pass into the `DeleteID` method. They first call back with a non-null error value (error Code 1), and then immediately call back again with a null error, indicating the call succeeded. To work around this, the `DeleteIDAsync` call ignores

62
docs/Google/Maps/Details.md Executable file
Просмотреть файл

@ -0,0 +1,62 @@
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.
Showing a Map
=============
### AppDelegate
```csharp
using Google.Maps;
...
const string MapsApiKey = "<Get your ID at https://code.google.com/apis/console/>";
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
MapServices.ProvideAPIKey (MapsApiKey);
...
}
```
### Your View Controller
```csharp
using Google.Maps;
...
MapView mapView;
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/).*

Просмотреть файл

@ -0,0 +1,112 @@
Showing a Map
=============
### AppDelegate
```csharp
using Google.Maps;
...
const string MapsApiKey = "<Get your ID at https://code.google.com/apis/console/>";
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
MapServices.ProvideAPIKey (MapsApiKey);
...
}
```
### Your View Controller
```csharp
using Google.Maps;
...
MapView mapView;
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;
}
```
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 = "<Get your ID at https://code.google.com/apis/console/>";
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
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
</array>
```
[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

Просмотреть файл

@ -0,0 +1,12 @@
Quickly monetize your app with Google AdMob, one of the world's largest mobile
advertising platforms. This SDK features:
* Simplified APIs
* Access to the latest HTML5 ad units from AdMob
With this component, developers can easily incorporate Google AdMob ads into their mobile
apps. Mobile-friendly text and image banners are available, along with rich, full-screen
web apps known as interstitials. An ever-growing set of "calls-to-action" are supported
in response to user-generated events, including redirection to the App Store, iTunes,
mapping applications, videos, and the dialer. Ads can be targeted by location and
demographic data.

Просмотреть файл

@ -0,0 +1,71 @@
Quickly monetize your app with Google AdMob, one of the world's largest mobile
advertising platforms. This SDK features:
* Simplified APIs
* Access to the latest HTML5 ad units from AdMob
With this component, developers can easily incorporate Google AdMob ads into their mobile
apps. Mobile-friendly text and image banners are available, along with rich, full-screen
web apps known as interstitials. An ever-growing set of "calls-to-action" are supported
in response to user-generated events, including redirection to the App Store, iTunes,
mapping applications, videos, and the dialer. Ads can be targeted by location and
demographic data.
## Banners
Creating a banner ad unit and loading the request:
```csharp
using Google.MobileAds;
...
const string bannerId = "<Get your ID at google.com/ads/admob>";
BannerView adView;
bool viewOnScreen = false;
public void AddBanner ()
{
// Setup your BannerView, review AdSizeCons class for more Ad sizes.
adView = new BannerView (size: AdSizeCons.Banner, origin: new CGPoint (-10, 0)) {
AdUnitID = bannerId,
RootViewController = this
};
// Wire AdReceived event to know when the Ad is ready to be displayed
adView.AdReceived += (object sender, EventArgs e) => {
if (!viewOnScreen) View.AddSubview (adView);
viewOnScreen = true;
};
adView.LoadRequest (Request.GetDefaultRequest ());
}
```
## Interstitial Ad
Creating an Interstitial ad and loading the request:
```csharp
using Google.MobileAds;
...
const string intersitialId = "<Get your ID at google.com/ads/admob>";
Interstitial adInterstitial;
public void AddInterstitial ()
{
adInterstitial = new Interstitial (intersitialId);
adInterstitial.LoadRequest (Request.GetDefaultRequest ());
// We need to wait until the Intersitial is ready to show
do {
await Task.Delay (100);
} while (!adInterstitial.IsReady);
// Once is ready, show the ad on Main thread
InvokeOnMainThread (() => adInterstitial.PresentFromRootViewController (navController));
}
```

12
docs/Google/Places/Details.md Executable file
Просмотреть файл

@ -0,0 +1,12 @@
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.
* **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.
Attribution Requirements
========================
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.
You can get the attribution text by making a call to `Google.Places.PlacesClient.OpenSourceLicenseInfo`

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,3 @@
Welcome to iOS game development with Google Play games services!
The Play Games SDK provides cross-platform Google Play games services that lets you easily integrate popular gaming features such as achievements, leaderboards, and multiplayer to your tablet and mobile games.

Просмотреть файл

@ -0,0 +1,239 @@
## Add your game to the Google Play Developer Console
Create an entry for your game in the Google Play Developer Console. This enables Google Play games services for your application, and creates an OAuth 2.0 client ID, if you don't already have one.
1. Add an entry for your iOS game by following the steps described in [Setting Up Google Play Games Services][1].
2. Take note of your game's OAuth 2.0 [client ID][2]; you will need this later.
3. (Optional) Add achievements and leaderboards to your game by following the steps described in [Configuring Achievements and Leaderboards][3].
4. Add accounts for other members of your team to test your game by following the steps described in [Publishing Your Game Changes][4].
## Add a sign-in and sign-out button
In your view controller, add a sign-in button and a sign-out button. Make sure your sign-in button conforms to the [Google+ branding guidelines][5]. To reduce your development effort, many of the built-in user-interfaces provided by Google Play games services already include a sign-out option so you don't need to add this manually.
```csharp
using Google.Play.GameServices;
...
const string CLIENT_ID = "123456789012.apps.googleusercontent.com";
UIButton signinButton;
UIButton signoutButton;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
signinButton = new UIButton (new CGRect (93, 99, 125, 44));
signinButton.SetTitle ("Sign In", UIControlState.Normal);
signinButton.TouchUpInside += delegate {
Manager.SharedInstance.SignIn (CLIENT_ID, false);
};
signoutButton = new UIButton (new CGRect (93, 299, 125, 44));
signoutButton.SetTitle ("Sign Out", UIControlState.Normal);
signoutButton.TouchUpInside += delegate {
Manager.SharedInstance.Signout ();
};
Add (signinButton);
Add (signoutButton);
}
```
When a player signs in to Google, the sign-in process sends them to the Google+ app, Chrome for iOS, or Mobile Safari (in that sequence). After the player signs in, the app opens a URL that points back to your game and contains the information necessary to complete the sign-in process. Make sure your application can handle the URL that redirects back to your game. Using the options provided in the Google Sign-In SDK, it is possible to selectively enable or disable redirection to Google first-party sign-in apps, Chrome/Safari, or in-app webviews.
Your app must be setup to accept a URL scheme. You can do this by opening your app's `Info.plist` file, and navigating to the *Advanced* tab.
In the *URL Types* section, add two *URL Type*s:
- In one *URL type*, specify a unique string in the Identifier field, and specify your client ID in reversed order in the *URL Schemas* field. For example, if your client ID for iOS is `CLIENT_ID_CODE.apps.googleusercontent.com`, then specify `com.googleusercontent.apps.CLIENT_ID_CODE` in the *URL Schemas* field.
- In the other *URL type*, specify a unique string in the Identifier field, and specify your app's bundle identifier (com.company.appname) in the *URL Schemas* field.
In your AppDelegate file, add `Google.SignIn` to libraries and override the `OpenUrl` method. This method handles the URL that your application receives at the end of the authentication process:
```csharp
using Google.SignIn;
...
public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
return SignIn.SharedInstance.HandleUrl (url, sourceApplication, annotation);
}
```
In some class of your app (where signin and singout buttons live for example), set `SignIn.SharedInstance.UIDelegate` property:
```csharp
SignIn.SharedInstance.UIDelegate = this;
```
The class assigned to `UIDelegate` property must implement the `ISignInUIDelegate` interface.
You can now test your application and be able to sign in and out. When testers sign in, they will be redirected to Google+, Chrome, or Safari to complete the sign-in process, and then redirected back to your application.
## Add a GPGStatusDelegate
Next, add the code to let your app know that when sign-in process is completed.
Make a class implements `IStatusDelegate` interface and assign it to `Manager.SharedInstance.StatusDelegate` property. Also, Implement these `IStatusDelegate` methods: `GamesSignInFinished` (to handle completion of player sign-in) and `GamesSignOutFinished` (to handle completion of player sign-out):
```csharp
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
Manager.SharedInstance.StatusDelegate = this;
}
[Export ("didFinishGamesSignInWithError:")]
public void GamesSignInFinished (NSError error)
{
if (error != null)
Console.WriteLine ("ERROR signing in: {0}", error.LocalizedDescription);
else
Console.WriteLine ("Finished with games sign in!");
RefreshYourUI ();
}
[Export ("didFinishGamesSignOutWithError:")]
public virtual void GamesSignOutFinished (NSError error)
{
if (error != null)
Console.WriteLine ("ERROR signing out: {0}", error.LocalizedDescription);
else
Console.WriteLine ("Signed out!");
RefreshYourUI ();
}
```
After you sign in or you sign out, refresh your UI to determine what button you should show:
```csharp
void RefreshYourUI ()
{
var signedIn = Manager.SharedInstance.SignedIn;
signinButton.Hidden = signedIn;
signoutButton.Hidden = !signedIn;
}
```
Now, when testers finish signing in, the sign-in button will be hidden. When they sign out, the sign-out button will be hidden and the sign-in button should re-appear.
## Automatically sign in returning players
You can also sign players in automatically, to avoid having them sign in every time they launch your game. The `Google.Play.GameServices.Manager` will automatically sign the player in when you specify **true** in `Manager.SharedInstance.SignIn` method. This call succeeds if all the following conditions are met:
- The player has authorized your application in the past;
- The player has not revoked access to your application; and
- The app is not requesting new scopes since the player last signed in.
Using this behavior, you can sign the player in automatically to your game by adding the `Manager.SharedInstance.SignIn` method to the end of your `ViewDidLoad` method, with silently param (second param) set to **true**.
```csharp
Manager.SharedInstance.SignIn (CLIENT_ID, true);
```
Run your application and notice that, unless you signed out when you last used your application, you are now signed in automatically.
## Add some interface refinements
When the application starts player sign-in automatically, there is a small delay between the time sign-in starts and completes. Your game should disable the UI during this time. To do this, use the fact that the `Manager.SharedInstance.SignIn` method returns **true** if it is attempting to sign the player in automatically. You could do something like this:
```
using Google.Play.GameServices;
...
const string CLIENT_ID = "123456789012.apps.googleusercontent.com";
UIButton signinButton;
UIButton signoutButton;
bool currentlySigningIn;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
Manager.SharedInstance.StatusDelegate = this;
signinButton = new UIButton (new CGRect (93, 99, 125, 44));
signinButton.SetTitle ("Sign In", UIControlState.Normal);
signinButton.TouchUpInside += delegate {
Manager.SharedInstance.SignIn (CLIENT_ID, false);
};
signoutButton = new UIButton (new CGRect (93, 299, 125, 44));
signoutButton.SetTitle ("Sign Out", UIControlState.Normal);
signoutButton.TouchUpInside += delegate {
Manager.SharedInstance.Signout ();
};
Add (signinButton);
Add (signoutButton);
currentlySigningIn = Manager.SharedInstance.SignIn (CLIENT_ID, true);
RefreshYourUI ();
}
[Export ("didFinishGamesSignInWithError:")]
public void GamesSignInFinished (NSError error)
{
if (error != null)
Console.WriteLine ("ERROR signing in: {0}", error.LocalizedDescription);
else
Console.WriteLine ("Finished with games sign in!");
currentlySigningIn = false;
RefreshYourUI ();
}
[Export ("didFinishGamesSignOutWithError:")]
public virtual void GamesSignOutFinished (NSError error)
{
if (error != null)
Console.WriteLine ("ERROR signing out: {0}", error.LocalizedDescription);
else
Console.WriteLine ("Signed out!");
currentlySigningIn = false;
RefreshYourUI ();
}
void RefreshYourUI ()
{
var signedIn = Manager.SharedInstance.SignedIn;
signinButton.Hidden = signedIn;
signoutButton.Hidden = !signedIn;
signinButton.Enabled = !currentlySigningIn;
signoutButton.Enabled = !currentlySigningIn;
}
```
## More with Play Games
* [Achievements][6]
* [Leaderboards][7]
* [Saved Games][8]
* [Real-time Multiplayer][9]
* [Turn-based Multiplayer][10]
* [Events and Quests][11]
* [Push Notifications][12]
[1]: https://developers.google.com/games/services/console/enabling
[2]: https://developers.google.com/games/services/console/enabling#client_id
[3]: https://developers.google.com/games/services/console/configuring#configuring_achievements_and_leaderboards
[4]: https://developers.google.com/games/services/console/testpub#enabling_accounts_for_testing
[5]: https://developers.google.com/+/branding-guidelines
[6]: https://developers.google.com/games/services/ios/achievements
[7]: https://developers.google.com/games/services/ios/leaderboards
[8]: https://developers.google.com/games/services/ios/savedgames
[9]: https://developers.google.com/games/services/ios/realtimeMultiplayer
[10]: https://developers.google.com/games/services/ios/turnbasedMultiplayer
[11]: https://developers.google.com/games/services/ios/quests
[12]: https://developers.google.com/games/services/ios/notifications

4
docs/Google/SignIn/Details.md Executable file
Просмотреть файл

@ -0,0 +1,4 @@
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.
Google Sign-In is also your gateway to connecting with Googles 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 Googles user-centric APIs and services inside your app to help your users take action and convert.

Просмотреть файл

@ -0,0 +1,143 @@
Configuring your App
--------------------
Google provides an easy to use configuration web tool to generate a config file for your app:
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
----------------------
In order for Sign-In to work properly, you must tell the SDK about some of your application lifecycle events.
In your `AppDelegate.cs`, in the `FinishedLaunching (..)` override, you should add the following code to the start of the method:
``` csharp
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
...
// 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 ();
...
return true;
}
```
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

Просмотреть файл

@ -0,0 +1,16 @@
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)
<sub>_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._</sub>

Просмотреть файл

@ -0,0 +1,168 @@
## Prerequisites
* Create a [Google Tag Manager account][3].
* [Configure a Google Tag Manager container][4].
## 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 Tag Manager 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 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):
```csharp
TagManager.Configure ();
App.Configure ();
```
## Download your container and add it to your project
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<NSString, NSObject>.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.<Your Bundle ID>`.
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.
<sub>_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._</sub>
[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