Push from command line

This commit is contained in:
Deniz Duezgoeren
2019-08-12 11:20:21 +02:00
parent 3a919dcb23
commit f1345eac14
512 changed files with 103288 additions and 1930 deletions
@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A961337B2299867300B541D4"
BuildableName = "AvH Plan.app"
BlueprintName = "AvH Plan"
ReferencedContainer = "container:AvH Plan.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A96133912299867A00B541D4"
BuildableName = "AvH PlanTests.xctest"
BlueprintName = "AvH PlanTests"
ReferencedContainer = "container:AvH Plan.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A961339C2299867A00B541D4"
BuildableName = "AvH PlanUITests.xctest"
BlueprintName = "AvH PlanUITests"
ReferencedContainer = "container:AvH Plan.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A961337B2299867300B541D4"
BuildableName = "AvH Plan.app"
BlueprintName = "AvH Plan"
ReferencedContainer = "container:AvH Plan.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A961337B2299867300B541D4"
BuildableName = "AvH Plan.app"
BlueprintName = "AvH Plan"
ReferencedContainer = "container:AvH Plan.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A961337B2299867300B541D4"
BuildableName = "AvH Plan.app"
BlueprintName = "AvH Plan"
ReferencedContainer = "container:AvH Plan.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+17 -56
View File
@@ -19,25 +19,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
FirebaseApp.configure()
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
// if #available(iOS 10.0, *) {
// // For iOS 10 display notification (sent via APNS)
// UNUserNotificationCenter.current().delegate = self
//
// let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
// UNUserNotificationCenter.current().requestAuthorization(
// options: authOptions,
// completionHandler: {_, _ in })
// } else {
// let settings: UIUserNotificationSettings =
// UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
// application.registerUserNotificationSettings(settings)
// }
//
// application.registerForRemoteNotifications()
application.registerForRemoteNotifications()
Messaging.messaging().subscribe(toTopic: "substitutions-ios") { error in
print("Subscribed to substitutions topic")
}
Messaging.messaging().subscribe(toTopic: "substitutions-ios")
Messaging.messaging().subscribe(toTopic: "substitutions-broadcast")
return true
}
@@ -62,43 +61,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print(userInfo)
completionHandler(UIBackgroundFetchResult.newData)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

+127
View File
@@ -0,0 +1,127 @@
//
// ColourPickerView.swift
// AvH Plan
//
// Created by Deniz Duezgoeren on 19.07.19.
// Copyright © 2019 Deniz Duezgoeren. All rights reserved.
//
import UIKit
import MagazineLayout
class ColourPickerView: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateMagazineLayout {
let identifier = "colour-cell"
var course = ""
var currentIndexPath: IndexPath? = nil
var internalCourse = ""
@IBOutlet weak var toolbar: UINavigationBar!
let prefs = UserDefaults.standard
let df = DataFetcher.sharedInstance
var index = 0
var key = ""
@IBOutlet weak var collectionView: UICollectionView!
var previousSelected: IndexPath? = nil
var currentSelected: IndexPath? = nil
var selectedNewItem = false
@IBAction func done(_ sender: UIBarButtonItem) {
prefs.set(currentSelected!.item, forKey: key) // index is used for the colour array in DataFetcher
let dict: [String: IndexPath] = ["indexPath": currentIndexPath!]
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil, userInfo: dict)
self.dismiss(animated: true, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
key = "colour-index-\(internalCourse)"
self.collectionView.register(UINib(nibName: "ColourViewCell", bundle: nil), forCellWithReuseIdentifier: identifier)
}
override func viewDidLoad() {
super.viewDidLoad()
index = prefs.integer(forKey: key)
self.toolbar!.topItem!.title = "\(course)"
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// For remove previously selection
if currentSelected != nil {
if let cell = collectionView.cellForItem(at: currentSelected!) as? ColourViewCell {
cell.imageView.image = UIImage(named: "ic_empty")
}
}
if let cell = collectionView.cellForItem(at: indexPath) as? ColourViewCell {
cell.imageView.image = UIImage(named: "checkmark")
}
currentSelected = indexPath
// // For reload the selected cell
self.collectionView.reloadItems(at: [currentSelected!])
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeModeForItemAt indexPath: IndexPath) -> MagazineLayoutItemSizeMode {
let widthMode = MagazineLayoutItemWidthMode.fullWidth(respectsHorizontalInsets: true)
let heightMode = MagazineLayoutItemHeightMode.static(height: 55)
return MagazineLayoutItemSizeMode(widthMode: widthMode, heightMode: heightMode)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForHeaderInSectionAtIndex index: Int) -> MagazineLayoutHeaderVisibilityMode {
return .hidden
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForFooterInSectionAtIndex index: Int) -> MagazineLayoutFooterVisibilityMode {
return .hidden
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, visibilityModeForBackgroundInSectionAtIndex index: Int) -> MagazineLayoutBackgroundVisibilityMode {
return .hidden
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, horizontalSpacingForItemsInSectionAtIndex index: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, verticalSpacingForElementsInSectionAtIndex index: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForSectionAtIndex index: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetsForItemsInSectionAtIndex index: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.df.getColourPalette().count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath as IndexPath) as! ColourViewCell
cell.label.text = self.df.getColourPaletteNames()[indexPath.item]
cell.colorView.layer.cornerRadius = cell.colorView.frame.height / 2
cell.colorView.backgroundColor = self.df.getColourPalette()[indexPath.item]
cell.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
cell.colorView.layer.borderWidth = 0.4
cell.colorView.layer.borderColor = #colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1)
if currentSelected == indexPath {
cell.imageView.image = UIImage(named: "checkmark")
} else {
cell.imageView.image = UIImage(named: "ic_empty")
}
if !selectedNewItem, indexPath.item == prefs.integer(forKey: key) {
currentSelected = indexPath
cell.imageView.image = UIImage(named: "checkmark")
selectedNewItem = true
}
return cell
}
}
View File
+154
View File
@@ -0,0 +1,154 @@
#import <FirebaseCore/FirebaseCore.h>
#if !defined(__has_include)
#error "Firebase.h won't import anything if your compiler doesn't support __has_include. Please \
import the headers individually."
#else
#if __has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
#import <FirebaseAnalytics/FirebaseAnalytics.h>
#endif
#if __has_include(<FirebaseAuth/FirebaseAuth.h>)
#import <FirebaseAuth/FirebaseAuth.h>
#endif
#if __has_include(<FirebaseDatabase/FirebaseDatabase.h>)
#import <FirebaseDatabase/FirebaseDatabase.h>
#endif
#if __has_include(<FirebaseDynamicLinks/FirebaseDynamicLinks.h>)
#import <FirebaseDynamicLinks/FirebaseDynamicLinks.h>
#if !__has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
#ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#warning "FirebaseAnalytics.framework is not included in your target. Please add \
`Firebase/Analytics` to your Podfile or add FirebaseAnalytics.framework to your project to ensure \
Firebase Dynamic Links works as intended."
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#endif
#endif
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
#import <FirebaseFirestore/FirebaseFirestore.h>
#endif
#if __has_include(<FirebaseFunctions/FirebaseFunctions.h>)
#import <FirebaseFunctions/FirebaseFunctions.h>
#endif
#if __has_include(<FirebaseInAppMessaging/FirebaseInAppMessaging.h>)
#import <FirebaseInAppMessaging/FirebaseInAppMessaging.h>
#if !__has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
#ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#warning "FirebaseAnalytics.framework is not included in your target. Please add \
`Firebase/Analytics` to your Podfile or add FirebaseAnalytics.framework to your project to ensure \
Firebase In App Messaging works as intended."
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#endif
#endif
#if __has_include(<FirebaseInstanceID/FirebaseInstanceID.h>)
#import <FirebaseInstanceID/FirebaseInstanceID.h>
#endif
#if __has_include(<FirebaseMessaging/FirebaseMessaging.h>)
#import <FirebaseMessaging/FirebaseMessaging.h>
#if !__has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
#ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#warning "FirebaseAnalytics.framework is not included in your target. Please add \
`Firebase/Analytics` to your Podfile or add FirebaseAnalytics.framework to your project to ensure \
Firebase Messaging works as intended."
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#endif
#endif
#if __has_include(<FirebaseMLCommon/FirebaseMLCommon.h>)
#import <FirebaseMLCommon/FirebaseMLCommon.h>
#endif
#if __has_include(<FirebaseMLModelInterpreter/FirebaseMLModelInterpreter.h>)
#import <FirebaseMLModelInterpreter/FirebaseMLModelInterpreter.h>
#endif
#if __has_include(<FirebaseMLNLLanguageID/FirebaseMLNLLanguageID.h>)
#import <FirebaseMLNLLanguageID/FirebaseMLNLLanguageID.h>
#endif
#if __has_include(<FirebaseMLNLSmartReply/FirebaseMLNLSmartReply.h>)
#import <FirebaseMLNLSmartReply/FirebaseMLNLSmartReply.h>
#endif
#if __has_include(<FirebaseMLNLTranslate/FirebaseMLNLTranslate.h>)
#import <FirebaseMLNLTranslate/FirebaseMLNLTranslate.h>
#endif
#if __has_include(<FirebaseMLNaturalLanguage/FirebaseMLNaturalLanguage.h>)
#import <FirebaseMLNaturalLanguage/FirebaseMLNaturalLanguage.h>
#endif
#if __has_include(<FirebaseMLVision/FirebaseMLVision.h>)
#import <FirebaseMLVision/FirebaseMLVision.h>
#endif
#if __has_include(<FirebaseMLVisionAutoML/FirebaseMLVisionAutoML.h>)
#import <FirebaseMLVisionAutoML/FirebaseMLVisionAutoML.h>
#endif
#if __has_include(<FirebaseMLVisionBarcodeModel/FirebaseMLVisionBarcodeModel.h>)
#import <FirebaseMLVisionBarcodeModel/FirebaseMLVisionBarcodeModel.h>
#endif
#if __has_include(<FirebaseMLVisionFaceModel/FirebaseMLVisionFaceModel.h>)
#import <FirebaseMLVisionFaceModel/FirebaseMLVisionFaceModel.h>
#endif
#if __has_include(<FirebaseMLVisionLabelModel/FirebaseMLVisionLabelModel.h>)
#import <FirebaseMLVisionLabelModel/FirebaseMLVisionLabelModel.h>
#endif
#if __has_include(<FirebaseMLVisionObjectDetection/FirebaseMLVisionObjectDetection.h>)
#import <FirebaseMLVisionObjectDetection/FirebaseMLVisionObjectDetection.h>
#endif
#if __has_include(<FirebaseMLVisionTextModel/FirebaseMLVisionTextModel.h>)
#import <FirebaseMLVisionTextModel/FirebaseMLVisionTextModel.h>
#endif
#if __has_include(<FirebasePerformance/FirebasePerformance.h>)
#import <FirebasePerformance/FirebasePerformance.h>
#if !__has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
#ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#warning "FirebaseAnalytics.framework is not included in your target. Please add \
`Firebase/Analytics` to your Podfile or add FirebaseAnalytics.framework to your project to ensure \
Firebase Performance works as intended."
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#endif
#endif
#if __has_include(<FirebaseRemoteConfig/FirebaseRemoteConfig.h>)
#import <FirebaseRemoteConfig/FirebaseRemoteConfig.h>
#if !__has_include(<FirebaseAnalytics/FirebaseAnalytics.h>)
#ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#warning "FirebaseAnalytics.framework is not included in your target. Please add \
`Firebase/Analytics` to your Podfile or add FirebaseAnalytics.framework to your project to ensure \
Firebase Remote Config works as intended."
#endif // #ifndef FIREBASE_ANALYTICS_SUPPRESS_WARNING
#endif
#endif
#if __has_include(<FirebaseStorage/FirebaseStorage.h>)
#import <FirebaseStorage/FirebaseStorage.h>
#endif
#if __has_include(<GoogleMobileAds/GoogleMobileAds.h>)
#import <GoogleMobileAds/GoogleMobileAds.h>
#endif
#if __has_include(<Fabric/Fabric.h>)
#import <Fabric/Fabric.h>
#endif
#if __has_include(<Crashlytics/Crashlytics.h>)
#import <Crashlytics/Crashlytics.h>
#endif
#endif // defined(__has_include)
+4
View File
@@ -0,0 +1,4 @@
module Firebase {
export *
header "Firebase.h"
}
Generated Executable
+93
View File
@@ -0,0 +1,93 @@
# Firebase APIs for iOS
Simplify your iOS development, grow your user base, and monetize more
effectively with Firebase services.
Much more information can be found at [https://firebase.google.com](https://firebase.google.com).
## Install a Firebase SDK using CocoaPods
Firebase distributes several iOS specific APIs and SDKs via CocoaPods.
You can install the CocoaPods tool on OS X by running the following command from
the terminal. Detailed information is available in the [Getting Started
guide](https://guides.cocoapods.org/using/getting-started.html#getting-started).
```
$ sudo gem install cocoapods
```
## Try out an SDK
You can try any of the SDKs with `pod try`. Run the following command and select
the SDK you are interested in when prompted:
```
$ pod try Firebase
```
Note that some SDKs may require credentials. More information is available in
the SDK-specific documentation at [https://firebase.google.com/docs/](https://firebase.google.com/docs/).
## Add a Firebase SDK to your iOS app
CocoaPods is used to install and manage dependencies in existing Xcode projects.
1. Create an Xcode project, and save it to your local machine.
2. Create a file named `Podfile` in your project directory. This file defines
your project's dependencies, and is commonly referred to as a Podspec.
3. Open `Podfile`, and add your dependencies. A simple Podspec is shown here:
```
platform :ios, '8.0'
pod 'Firebase'
```
4. Save the file.
5. Open a terminal and `cd` to the directory containing the Podfile.
```
$ cd <path-to-project>/project/
```
6. Run the `pod install` command. This will install the SDKs specified in the
Podspec, along with any dependencies they may have.
```
$ pod install
```
7. Open your app's `.xcworkspace` file to launch Xcode. Use this file for all
development on your app.
8. You can also install other Firebase SDKs by adding the subspecs in the
Podfile.
```
pod 'Firebase/AdMob'
pod 'Firebase/Analytics'
pod 'Firebase/Auth'
pod 'Firebase/Database'
pod 'Firebase/DynamicLinks'
pod 'Firebase/Firestore'
pod 'Firebase/Functions'
pod 'Firebase/InAppMessaging'
pod 'Firebase/InAppMessagingDisplay'
pod 'Firebase/Messaging'
pod 'Firebase/MLCommon'
pod 'Firebase/MLModelInterpreter'
pod 'Firebase/MLNLLanguageID'
pod 'Firebase/MLNLSmartReply'
pod 'Firebase/MLNLTranslate'
pod 'Firebase/MLNaturalLanguage'
pod 'Firebase/MLVision'
pod 'Firebase/MLVisionAutoML'
pod 'Firebase/MLVisionBarcodeModel'
pod 'Firebase/MLVisionFaceModel'
pod 'Firebase/MLVisionLabelModel'
pod 'Firebase/MLVisionObjectDetection'
pod 'Firebase/MLVisionTextModel'
pod 'Firebase/Performance'
pod 'Firebase/RemoteConfig'
pod 'Firebase/Storage'
```
@@ -0,0 +1,60 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@protocol FIRAnalyticsInteropListener;
NS_ASSUME_NONNULL_BEGIN
/// Connector for bridging communication between Firebase SDKs and FirebaseAnalytics API.
@protocol FIRAnalyticsInterop
/// Sets user property when trigger event is logged. This API is only available in the SDK.
- (void)setConditionalUserProperty:(NSDictionary<NSString *, id> *)conditionalUserProperty;
/// Clears user property if set.
- (void)clearConditionalUserProperty:(NSString *)userPropertyName
forOrigin:(NSString *)origin
clearEventName:(NSString *)clearEventName
clearEventParameters:(NSDictionary<NSString *, NSString *> *)clearEventParameters;
/// Returns currently set user properties.
- (NSArray<NSDictionary<NSString *, NSString *> *> *)conditionalUserProperties:(NSString *)origin
propertyNamePrefix:
(NSString *)propertyNamePrefix;
/// Returns the maximum number of user properties.
- (NSInteger)maxUserProperties:(NSString *)origin;
/// Logs events.
- (void)logEventWithOrigin:(NSString *)origin
name:(NSString *)name
parameters:(nullable NSDictionary<NSString *, id> *)parameters;
/// Sets user property.
- (void)setUserPropertyWithOrigin:(NSString *)origin name:(NSString *)name value:(id)value;
/// Registers an Analytics listener for the given origin.
- (void)registerAnalyticsListener:(id<FIRAnalyticsInteropListener>)listener
withOrigin:(NSString *)origin;
/// Unregisters an Analytics listener for the given origin.
- (void)unregisterAnalyticsListenerWithOrigin:(NSString *)origin;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,24 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// Handles events and messages from Analytics.
@protocol FIRAnalyticsInteropListener <NSObject>
/// Triggers when an Analytics event happens for the registered origin with
/// `FIRAnalyticsInterop`s `registerAnalyticsListener:withOrigin:`.
- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters;
@end
@@ -0,0 +1,28 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// @file FIRInteropEventNames.h
#import <Foundation/Foundation.h>
/// Notification open event name.
static NSString *const kFIRIEventNotificationOpen = @"_no";
/// Notification foreground event name.
static NSString *const kFIRIEventNotificationForeground = @"_nf";
/// Campaign event name.
static NSString *const kFIRIEventFirebaseCampaign = @"_cmp";
@@ -0,0 +1,73 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/// @file FIRInteropParameterNames.h
///
/// Predefined event parameter names used by Firebase. This file is a subset of the
/// FirebaseAnalytics FIRParameterNames.h public header.
///
/// The origin of your traffic, such as an Ad network (for example, google) or partner (urban
/// airship). Identify the advertiser, site, publication, etc. that is sending traffic to your
/// property. Highly recommended (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterSource : @"InMobi",
/// // ...
/// };
/// </pre>
static NSString *const kFIRIParameterSource NS_SWIFT_NAME(AnalyticsParameterSource) = @"source";
/// The advertising or marketing medium, for example: cpc, banner, email, push. Highly recommended
/// (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterMedium : @"email",
/// // ...
/// };
/// </pre>
static NSString *const kFIRIParameterMedium NS_SWIFT_NAME(AnalyticsParameterMedium) = @"medium";
/// The individual campaign name, slogan, promo code, etc. Some networks have pre-defined macro to
/// capture campaign information, otherwise can be populated by developer. Highly Recommended
/// (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCampaign : @"winter_promotion",
/// // ...
/// };
/// </pre>
static NSString *const kFIRIParameterCampaign NS_SWIFT_NAME(AnalyticsParameterCampaign) =
@"campaign";
/// Message identifier.
static NSString *const kFIRIParameterMessageIdentifier = @"_nmid";
/// Message name.
static NSString *const kFIRIParameterMessageName = @"_nmn";
/// Message send time.
static NSString *const kFIRIParameterMessageTime = @"_nmt";
/// Message device time.
static NSString *const kFIRIParameterMessageDeviceTime = @"_ndt";
/// Topic message.
static NSString *const kFIRIParameterTopic = @"_nt";
/// Stores the message_id of the last notification opened by the app.
static NSString *const kFIRIUserPropertyLastNotification = @"_ln";
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+223
View File
@@ -0,0 +1,223 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
[https://firebase.google.com](https://firebase.google.com).
## Installation
See the three subsections for details about three different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
### Standard pod install
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
For releases starting with 5.0.0, the source for each release is also deployed
to CocoaPods master and available via standard
[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See
[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All of the official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
```
To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
```
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution are at
[Carthage](Carthage.md).
### Rome
Instructions for installing binary frameworks via
[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.1 (or later)
* CocoaPods 1.7.2 (or later)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See
[Firestore/README.md](Firestore/README.md) and
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
before creating a PR.
Travis will verify that any code changes are done in a style compliant way. Install
`clang-format` and `swiftformat`.
These commands will get the right versions:
```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage
First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
After running the `AllUnitTests_iOS` scheme in Xcode, execute
`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
### Running Sample Apps
In order to run the sample apps and integration tests, you'll need valid
`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
files without real values, but can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g. `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
(e.g. in [Example/Database/App/](Example/Database/App/));
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
special Apple capabilities, and you will have to change the sample app to use a unique bundle
identifier that you can control in your own Apple Developer account.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
If you're doing specific Firebase Auth development, see
[the Auth Sample README](Example/Auth/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
To run the Database Integration tests, make your database authentication rules
[public](https://firebase.google.com/docs/database/security/quickstart).
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to actually test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account, and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
In order to receive push notifications, you'll have to follow the steps above and run the app on a
physical device.
## Community Supported Efforts
We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
participate in the Firebase community.
### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS.
For tvOS, checkout the [Sample](Example/tvOSSample).
Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile:
```
pod 'FirebaseAuth'
pod 'FirebaseCore'
pod 'FirebaseDatabase'
pod 'FirebaseFirestore'
pod 'FirebaseFunctions'
pod 'FirebaseMessaging'
pod 'FirebaseStorage'
```
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
iOS SDK.
## License
The contents of this repository is licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
@@ -0,0 +1,62 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
#import "Private/FIRAnalyticsConfiguration.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
@implementation FIRAnalyticsConfiguration
#pragma clang diagnostic pop
+ (FIRAnalyticsConfiguration *)sharedInstance {
static FIRAnalyticsConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FIRAnalyticsConfiguration alloc] init];
});
return sharedInstance;
}
- (void)postNotificationName:(NSString *)name value:(id)value {
if (!name.length || !value) {
return;
}
[[NSNotificationCenter defaultCenter] postNotificationName:name
object:self
userInfo:@{name : value}];
}
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled {
[self setAnalyticsCollectionEnabled:analyticsCollectionEnabled persistSetting:YES];
}
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
persistSetting:(BOOL)shouldPersist {
// Persist the measurementEnabledState. Use FIRAnalyticsEnabledState values instead of YES/NO.
FIRAnalyticsEnabledState analyticsEnabledState =
analyticsCollectionEnabled ? kFIRAnalyticsEnabledStateSetYes : kFIRAnalyticsEnabledStateSetNo;
if (shouldPersist) {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@(analyticsEnabledState)
forKey:kFIRAPersistedConfigMeasurementEnabledStateKey];
[userDefaults synchronize];
}
[self postNotificationName:kFIRAnalyticsConfigurationSetEnabledNotification
value:@(analyticsCollectionEnabled)];
}
@end
+836
View File
@@ -0,0 +1,836 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sys/utsname.h>
#if __has_include(<UIKit/UIKit.h>)
#import <UIKit/UIKit.h>
#endif
#if __has_include(<AppKit/AppKit.h>)
#import <AppKit/AppKit.h>
#endif
#import "FIRApp.h"
#import "Private/FIRAnalyticsConfiguration.h"
#import "Private/FIRAppInternal.h"
#import "Private/FIRBundleUtil.h"
#import "Private/FIRComponentContainerInternal.h"
#import "Private/FIRConfigurationInternal.h"
#import "Private/FIRCoreDiagnosticsConnector.h"
#import "Private/FIRLibrary.h"
#import "Private/FIRLogger.h"
#import "Private/FIROptionsInternal.h"
// The kFIRService strings are only here while transitioning CoreDiagnostics from the Analytics
// pod to a Core dependency. These symbols are not used and should be deleted after the transition.
NSString *const kFIRServiceAdMob;
NSString *const kFIRServiceAuth;
NSString *const kFIRServiceAuthUI;
NSString *const kFIRServiceCrash;
NSString *const kFIRServiceDatabase;
NSString *const kFIRServiceDynamicLinks;
NSString *const kFIRServiceFirestore;
NSString *const kFIRServiceFunctions;
NSString *const kFIRServiceInstanceID;
NSString *const kFIRServiceInvites;
NSString *const kFIRServiceMessaging;
NSString *const kFIRServiceMeasurement;
NSString *const kFIRServicePerformance;
NSString *const kFIRServiceRemoteConfig;
NSString *const kFIRServiceStorage;
NSString *const kGGLServiceAnalytics;
NSString *const kGGLServiceSignIn;
NSString *const kFIRDefaultAppName = @"__FIRAPP_DEFAULT";
NSString *const kFIRAppReadyToConfigureSDKNotification = @"FIRAppReadyToConfigureSDKNotification";
NSString *const kFIRAppDeleteNotification = @"FIRAppDeleteNotification";
NSString *const kFIRAppIsDefaultAppKey = @"FIRAppIsDefaultAppKey";
NSString *const kFIRAppNameKey = @"FIRAppNameKey";
NSString *const kFIRGoogleAppIDKey = @"FIRGoogleAppIDKey";
NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat =
@"/google/firebase/global_data_collection_enabled:%@";
NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey =
@"FirebaseDataCollectionDefaultEnabled";
NSString *const kFIRAppDiagnosticsNotification = @"FIRAppDiagnosticsNotification";
NSString *const kFIRAppDiagnosticsConfigurationTypeKey = @"ConfigType";
NSString *const kFIRAppDiagnosticsErrorKey = @"Error";
NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRApp";
NSString *const kFIRAppDiagnosticsSDKNameKey = @"SDKName";
NSString *const kFIRAppDiagnosticsSDKVersionKey = @"SDKVersion";
// Auth internal notification notification and key.
NSString *const FIRAuthStateDidChangeInternalNotification =
@"FIRAuthStateDidChangeInternalNotification";
NSString *const FIRAuthStateDidChangeInternalNotificationAppKey =
@"FIRAuthStateDidChangeInternalNotificationAppKey";
NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey =
@"FIRAuthStateDidChangeInternalNotificationTokenKey";
NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey =
@"FIRAuthStateDidChangeInternalNotificationUIDKey";
/**
* The URL to download plist files.
*/
static NSString *const kPlistURL = @"https://console.firebase.google.com/";
/**
* An array of all classes that registered as `FIRCoreConfigurable` in order to receive lifecycle
* events from Core.
*/
static NSMutableArray<Class<FIRLibrary>> *sRegisteredAsConfigurable;
@interface FIRApp ()
#ifdef DEBUG
@property(nonatomic) BOOL alreadyOutputDataCollectionFlag;
#endif // DEBUG
@end
@implementation FIRApp
// This is necessary since our custom getter prevents `_options` from being created.
@synthesize options = _options;
static NSMutableDictionary *sAllApps;
static FIRApp *sDefaultApp;
static NSMutableDictionary *sLibraryVersions;
+ (void)configure {
FIROptions *options = [FIROptions defaultOptions];
if (!options) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"`[FIRApp configure];` (`FirebaseApp.configure()` in Swift) could not find "
@"a valid GoogleService-Info.plist in your project. Please download one "
@"from %@.",
kPlistURL];
}
[FIRApp configureWithOptions:options];
#if TARGET_OS_OSX || TARGET_OS_TV
FIRLogNotice(kFIRLoggerCore, @"I-COR000028",
@"tvOS and macOS SDK support is not part of the official Firebase product. "
@"Instead they are community supported. Details at "
@"https://github.com/firebase/firebase-ios-sdk/blob/master/README.md.");
#endif
}
+ (void)configureWithOptions:(FIROptions *)options {
if (!options) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"Options is nil. Please pass a valid options."];
}
[FIRApp configureWithName:kFIRDefaultAppName options:options];
}
+ (NSCharacterSet *)applicationNameAllowedCharacters {
static NSCharacterSet *applicationNameAllowedCharacters;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableCharacterSet *allowedNameCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
[allowedNameCharacters addCharactersInString:@"-_"];
applicationNameAllowedCharacters = [allowedNameCharacters copy];
});
return applicationNameAllowedCharacters;
}
+ (void)configureWithName:(NSString *)name options:(FIROptions *)options {
if (!name || !options) {
[NSException raise:kFirebaseCoreErrorDomain format:@"Neither name nor options can be nil."];
}
if (name.length == 0) {
[NSException raise:kFirebaseCoreErrorDomain format:@"Name cannot be empty."];
}
if ([name isEqualToString:kFIRDefaultAppName]) {
if (sDefaultApp) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"Default app has already been configured."];
}
FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configuring the default app.");
} else {
// Validate the app name and ensure it hasn't been configured already.
NSCharacterSet *nameCharacters = [NSCharacterSet characterSetWithCharactersInString:name];
if (![[self applicationNameAllowedCharacters] isSupersetOfSet:nameCharacters]) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"App name can only contain alphanumeric, "
@"hyphen (-), and underscore (_) characters"];
}
@synchronized(self) {
if (sAllApps && sAllApps[name]) {
[NSException raise:kFirebaseCoreErrorDomain
format:@"App named %@ has already been configured.", name];
}
}
FIRLogDebug(kFIRLoggerCore, @"I-COR000002", @"Configuring app named %@", name);
}
@synchronized(self) {
FIRApp *app = [[FIRApp alloc] initInstanceWithName:name options:options];
if (app.isDefaultApp) {
sDefaultApp = app;
}
[FIRApp addAppToAppDictionary:app];
[FIRApp sendNotificationsToSDKs:app];
}
}
+ (FIRApp *)defaultApp {
if (sDefaultApp) {
return sDefaultApp;
}
FIRLogError(kFIRLoggerCore, @"I-COR000003",
@"The default Firebase app has not yet been "
@"configured. Add `[FIRApp configure];` (`FirebaseApp.configure()` in Swift) to your "
@"application initialization. Read more: https://goo.gl/ctyzm8.");
return nil;
}
+ (FIRApp *)appNamed:(NSString *)name {
@synchronized(self) {
if (sAllApps) {
FIRApp *app = sAllApps[name];
if (app) {
return app;
}
}
FIRLogError(kFIRLoggerCore, @"I-COR000004", @"App with name %@ does not exist.", name);
return nil;
}
}
+ (NSDictionary *)allApps {
@synchronized(self) {
if (!sAllApps) {
FIRLogError(kFIRLoggerCore, @"I-COR000005", @"No app has been configured yet.");
}
return [sAllApps copy];
}
}
// Public only for tests
+ (void)resetApps {
@synchronized(self) {
sDefaultApp = nil;
[sAllApps removeAllObjects];
sAllApps = nil;
[sLibraryVersions removeAllObjects];
sLibraryVersions = nil;
}
}
- (void)deleteApp:(FIRAppVoidBoolCallback)completion {
@synchronized([self class]) {
if (sAllApps && sAllApps[self.name]) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000006", @"Deleting app named %@", self.name);
// Remove all cached instances from the container before deleting the app.
[self.container removeAllCachedInstances];
[sAllApps removeObjectForKey:self.name];
[self clearDataCollectionSwitchFromUserDefaults];
if ([self.name isEqualToString:kFIRDefaultAppName]) {
sDefaultApp = nil;
}
NSDictionary *appInfoDict = @{kFIRAppNameKey : self.name};
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppDeleteNotification
object:[self class]
userInfo:appInfoDict];
completion(YES);
} else {
FIRLogError(kFIRLoggerCore, @"I-COR000007", @"App does not exist.");
completion(NO);
}
}
}
+ (void)addAppToAppDictionary:(FIRApp *)app {
if (!sAllApps) {
sAllApps = [NSMutableDictionary dictionary];
}
if ([app configureCore]) {
sAllApps[app.name] = app;
} else {
[NSException raise:kFirebaseCoreErrorDomain
format:@"Configuration fails. It may be caused by an invalid GOOGLE_APP_ID in "
@"GoogleService-Info.plist or set in the customized options."];
}
}
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options {
self = [super init];
if (self) {
_name = [name copy];
_options = [options copy];
_options.editingLocked = YES;
_isDefaultApp = [name isEqualToString:kFIRDefaultAppName];
_container = [[FIRComponentContainer alloc] initWithApp:self];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (BOOL)configureCore {
[self checkExpectedBundleID];
if (![self isAppIDValid]) {
return NO;
}
[self logCoreTelemetryIfEnabled];
#if TARGET_OS_IOS
// Initialize the Analytics once there is a valid options under default app. Analytics should
// always initialize first by itself before the other SDKs.
if ([self.name isEqualToString:kFIRDefaultAppName]) {
Class firAnalyticsClass = NSClassFromString(@"FIRAnalytics");
if (firAnalyticsClass) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
SEL startWithConfigurationSelector = @selector(startWithConfiguration:options:);
#pragma clang diagnostic pop
if ([firAnalyticsClass respondsToSelector:startWithConfigurationSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[firAnalyticsClass performSelector:startWithConfigurationSelector
withObject:[FIRConfiguration sharedInstance].analyticsConfiguration
withObject:_options];
#pragma clang diagnostic pop
}
}
}
#endif
[self subscribeForAppDidBecomeActiveNotifications];
return YES;
}
- (FIROptions *)options {
return [_options copy];
}
- (void)setDataCollectionDefaultEnabled:(BOOL)dataCollectionDefaultEnabled {
#ifdef DEBUG
FIRLogDebug(kFIRLoggerCore, @"I-COR000034", @"Explicitly %@ data collection flag.",
dataCollectionDefaultEnabled ? @"enabled" : @"disabled");
self.alreadyOutputDataCollectionFlag = YES;
#endif // DEBUG
NSString *key =
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
[[NSUserDefaults standardUserDefaults] setBool:dataCollectionDefaultEnabled forKey:key];
// Core also controls the FirebaseAnalytics flag, so check if the Analytics flags are set
// within FIROptions and change the Analytics value if necessary. Analytics only works with the
// default app, so return if this isn't the default app.
if (!self.isDefaultApp) {
return;
}
// Check if the Analytics flag is explicitly set. If so, no further actions are necessary.
if ([self.options isAnalyticsCollectionExpicitlySet]) {
return;
}
// The Analytics flag has not been explicitly set, so update with the value being set.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[FIRAnalyticsConfiguration sharedInstance]
setAnalyticsCollectionEnabled:dataCollectionDefaultEnabled
persistSetting:NO];
#pragma clang diagnostic pop
}
- (BOOL)isDataCollectionDefaultEnabled {
// Check if it's been manually set before in code, and use that as the higher priority value.
NSNumber *defaultsObject = [[self class] readDataCollectionSwitchFromUserDefaultsForApp:self];
if (defaultsObject != nil) {
#ifdef DEBUG
if (!self.alreadyOutputDataCollectionFlag) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000031", @"Data Collection flag is %@ in user defaults.",
[defaultsObject boolValue] ? @"enabled" : @"disabled");
self.alreadyOutputDataCollectionFlag = YES;
}
#endif // DEBUG
return [defaultsObject boolValue];
}
// Read the Info.plist to see if the flag is set. If it's not set, it should default to `YES`.
// As per the implementation of `readDataCollectionSwitchFromPlist`, it's a cached value and has
// no performance impact calling multiple times.
NSNumber *collectionEnabledPlistValue = [[self class] readDataCollectionSwitchFromPlist];
if (collectionEnabledPlistValue != nil) {
#ifdef DEBUG
if (!self.alreadyOutputDataCollectionFlag) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000032", @"Data Collection flag is %@ in plist.",
[collectionEnabledPlistValue boolValue] ? @"enabled" : @"disabled");
self.alreadyOutputDataCollectionFlag = YES;
}
#endif // DEBUG
return [collectionEnabledPlistValue boolValue];
}
#ifdef DEBUG
if (!self.alreadyOutputDataCollectionFlag) {
FIRLogDebug(kFIRLoggerCore, @"I-COR000033", @"Data Collection flag is not set.");
self.alreadyOutputDataCollectionFlag = YES;
}
#endif // DEBUG
return YES;
}
#pragma mark - private
+ (void)sendNotificationsToSDKs:(FIRApp *)app {
// TODO: Remove this notification once all SDKs are registered with `FIRCoreConfigurable`.
NSNumber *isDefaultApp = [NSNumber numberWithBool:app.isDefaultApp];
NSDictionary *appInfoDict = @{
kFIRAppNameKey : app.name,
kFIRAppIsDefaultAppKey : isDefaultApp,
kFIRGoogleAppIDKey : app.options.googleAppID
};
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppReadyToConfigureSDKNotification
object:self
userInfo:appInfoDict];
// This is the new way of sending information to SDKs.
// TODO: Do we want this on a background thread, maybe?
@synchronized(self) {
for (Class<FIRLibrary> library in sRegisteredAsConfigurable) {
[library configureWithApp:app];
}
}
}
+ (NSError *)errorForMissingOptions {
NSDictionary *errorDict = @{
NSLocalizedDescriptionKey :
@"Unable to parse GoogleService-Info.plist in order to configure services.",
NSLocalizedRecoverySuggestionErrorKey :
@"Check formatting and location of GoogleService-Info.plist."
};
return [NSError errorWithDomain:kFirebaseCoreErrorDomain
code:FIRErrorCodeInvalidPlistFile
userInfo:errorDict];
}
+ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain
errorCode:(FIRErrorCode)code
service:(NSString *)service
reason:(NSString *)reason {
NSString *description =
[NSString stringWithFormat:@"Configuration failed for service %@.", service];
NSDictionary *errorDict =
@{NSLocalizedDescriptionKey : description, NSLocalizedFailureReasonErrorKey : reason};
return [NSError errorWithDomain:domain code:code userInfo:errorDict];
}
+ (NSError *)errorForInvalidAppID {
NSDictionary *errorDict = @{
NSLocalizedDescriptionKey : @"Unable to validate Google App ID",
NSLocalizedRecoverySuggestionErrorKey :
@"Check formatting and location of GoogleService-Info.plist or GoogleAppID set in the "
@"customized options."
};
return [NSError errorWithDomain:kFirebaseCoreErrorDomain
code:FIRErrorCodeInvalidAppID
userInfo:errorDict];
}
+ (BOOL)isDefaultAppConfigured {
return (sDefaultApp != nil);
}
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version {
// Create the set of characters which aren't allowed, only if this feature is used.
NSMutableCharacterSet *allowedSet = [NSMutableCharacterSet alphanumericCharacterSet];
[allowedSet addCharactersInString:@"-_."];
NSCharacterSet *disallowedSet = [allowedSet invertedSet];
// Make sure the library name and version strings do not contain unexpected characters, and
// add the name/version pair to the dictionary.
if ([name rangeOfCharacterFromSet:disallowedSet].location == NSNotFound &&
[version rangeOfCharacterFromSet:disallowedSet].location == NSNotFound) {
@synchronized(self) {
if (!sLibraryVersions) {
sLibraryVersions = [[NSMutableDictionary alloc] init];
}
sLibraryVersions[name] = version;
}
} else {
FIRLogError(kFIRLoggerCore, @"I-COR000027",
@"The library name (%@) or version number (%@) contain invalid characters. "
@"Only alphanumeric, dash, underscore and period characters are allowed.",
name, version);
}
}
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name
withVersion:(nonnull NSString *)version {
// This is called at +load time, keep the work to a minimum.
// Ensure the class given conforms to the proper protocol.
if (![(Class)library conformsToProtocol:@protocol(FIRLibrary)] ||
![(Class)library respondsToSelector:@selector(componentsToRegister)]) {
[NSException raise:NSInvalidArgumentException
format:@"Class %@ attempted to register components, but it does not conform to "
@"`FIRLibrary or provide a `componentsToRegister:` method.",
library];
}
[FIRComponentContainer registerAsComponentRegistrant:library];
if ([(Class)library respondsToSelector:@selector(configureWithApp:)]) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sRegisteredAsConfigurable = [[NSMutableArray alloc] init];
});
@synchronized(self) {
[sRegisteredAsConfigurable addObject:library];
}
}
[self registerLibrary:name withVersion:version];
}
+ (NSString *)firebaseUserAgent {
@synchronized(self) {
NSMutableArray<NSString *> *libraries =
[[NSMutableArray<NSString *> alloc] initWithCapacity:sLibraryVersions.count];
for (NSString *libraryName in sLibraryVersions) {
[libraries addObject:[NSString stringWithFormat:@"%@/%@", libraryName,
sLibraryVersions[libraryName]]];
}
[libraries sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
return [libraries componentsJoinedByString:@" "];
}
}
- (void)checkExpectedBundleID {
NSArray *bundles = [FIRBundleUtil relevantBundles];
NSString *expectedBundleID = [self expectedBundleID];
// The checking is only done when the bundle ID is provided in the serviceInfo dictionary for
// backward compatibility.
if (expectedBundleID != nil && ![FIRBundleUtil hasBundleIdentifierPrefix:expectedBundleID
inBundles:bundles]) {
FIRLogError(kFIRLoggerCore, @"I-COR000008",
@"The project's Bundle ID is inconsistent with "
@"either the Bundle ID in '%@.%@', or the Bundle ID in the options if you are "
@"using a customized options. To ensure that everything can be configured "
@"correctly, you may need to make the Bundle IDs consistent. To continue with this "
@"plist file, you may change your app's bundle identifier to '%@'. Or you can "
@"download a new configuration file that matches your bundle identifier from %@ "
@"and replace the current one.",
kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
}
}
#pragma mark - private - App ID Validation
/**
* Validates the format and fingerprint of the app ID contained in GOOGLE_APP_ID in the plist file.
* This is the main method for validating app ID.
*
* @return YES if the app ID fulfills the expected format and fingerprint, NO otherwise.
*/
- (BOOL)isAppIDValid {
NSString *appID = _options.googleAppID;
BOOL isValid = [FIRApp validateAppID:appID];
if (!isValid) {
NSString *expectedBundleID = [self expectedBundleID];
FIRLogError(kFIRLoggerCore, @"I-COR000009",
@"The GOOGLE_APP_ID either in the plist file "
@"'%@.%@' or the one set in the customized options is invalid. If you are using "
@"the plist file, use the iOS version of bundle identifier to download the file, "
@"and do not manually edit the GOOGLE_APP_ID. You may change your app's bundle "
@"identifier to '%@'. Or you can download a new configuration file that matches "
@"your bundle identifier from %@ and replace the current one.",
kServiceInfoFileName, kServiceInfoFileType, expectedBundleID, kPlistURL);
};
return isValid;
}
+ (BOOL)validateAppID:(NSString *)appID {
// Failing validation only occurs when we are sure we are looking at a V2 app ID and it does not
// have a valid fingerprint, otherwise we just warn about the potential issue.
if (!appID.length) {
return NO;
}
NSScanner *stringScanner = [NSScanner scannerWithString:appID];
stringScanner.charactersToBeSkipped = nil;
NSString *appIDVersion;
if (![stringScanner scanCharactersFromSet:[NSCharacterSet decimalDigitCharacterSet]
intoString:&appIDVersion]) {
return NO;
}
if (![stringScanner scanString:@":" intoString:NULL]) {
// appIDVersion must be separated by ":"
return NO;
}
NSArray *knownVersions = @[ @"1" ];
if (![knownVersions containsObject:appIDVersion]) {
// Permit unknown yet properly formatted app ID versions.
FIRLogInfo(kFIRLoggerCore, @"I-COR000010", @"Unknown GOOGLE_APP_ID version: %@", appIDVersion);
return YES;
}
if (![self validateAppIDFormat:appID withVersion:appIDVersion]) {
return NO;
}
if (![self validateAppIDFingerprint:appID withVersion:appIDVersion]) {
return NO;
}
return YES;
}
+ (NSString *)actualBundleID {
return [[NSBundle mainBundle] bundleIdentifier];
}
/**
* Validates that the format of the app ID string is what is expected based on the supplied version.
* The version must end in ":".
*
* For v1 app ids the format is expected to be
* '<version #>:<project number>:ios:<fingerprint of bundle id>'.
*
* This method does not verify that the contents of the app id are correct, just that they fulfill
* the expected format.
*
* @param appID Contents of GOOGLE_APP_ID from the plist file.
* @param version Indicates what version of the app id format this string should be.
* @return YES if provided string fufills the expected format, NO otherwise.
*/
+ (BOOL)validateAppIDFormat:(NSString *)appID withVersion:(NSString *)version {
if (!appID.length || !version.length) {
return NO;
}
NSScanner *stringScanner = [NSScanner scannerWithString:appID];
stringScanner.charactersToBeSkipped = nil;
// Skip version part
// '*<version #>*:<project number>:ios:<fingerprint of bundle id>'
if (![stringScanner scanString:version intoString:NULL]) {
// The version part is missing or mismatched
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>*:*<project number>:ios:<fingerprint of bundle id>'
if (![stringScanner scanString:@":" intoString:NULL]) {
// appIDVersion must be separated by ":"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:*<project number>*:ios:<fingerprint of bundle id>'.
NSInteger projectNumber = NSNotFound;
if (![stringScanner scanInteger:&projectNumber]) {
// NO project number found.
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>*:*ios:<fingerprint of bundle id>'.
if (![stringScanner scanString:@":" intoString:NULL]) {
// The project number must be separated by ":"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:*ios*:<fingerprint of bundle id>'.
NSString *platform;
if (![stringScanner scanUpToString:@":" intoString:&platform]) {
return NO;
}
if (![platform isEqualToString:@"ios"]) {
// The platform must be @"ios"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:ios*:*<fingerprint of bundle id>'.
if (![stringScanner scanString:@":" intoString:NULL]) {
// The platform must be separated by ":"
return NO;
}
// Validate version part (see part between '*' symbols below)
// '<version #>:<project number>:ios:*<fingerprint of bundle id>*'.
unsigned long long fingerprint = NSNotFound;
if (![stringScanner scanHexLongLong:&fingerprint]) {
// Fingerprint part is missing
return NO;
}
if (!stringScanner.isAtEnd) {
// There are not allowed characters in the fingerprint part
return NO;
}
return YES;
}
/**
* Validates that the fingerprint of the app ID string is what is expected based on the supplied
* version.
*
* Note that the v1 hash algorithm is not permitted on the client and cannot be fully validated.
*
* @param appID Contents of GOOGLE_APP_ID from the plist file.
* @param version Indicates what version of the app id format this string should be.
* @return YES if provided string fufills the expected fingerprint and the version is known, NO
* otherwise.
*/
+ (BOOL)validateAppIDFingerprint:(NSString *)appID withVersion:(NSString *)version {
// Extract the supplied fingerprint from the supplied app ID.
// This assumes the app ID format is the same for all known versions below. If the app ID format
// changes in future versions, the tokenizing of the app ID format will need to take into account
// the version of the app ID.
NSArray *components = [appID componentsSeparatedByString:@":"];
if (components.count != 4) {
return NO;
}
NSString *suppliedFingerprintString = components[3];
if (!suppliedFingerprintString.length) {
return NO;
}
uint64_t suppliedFingerprint;
NSScanner *scanner = [NSScanner scannerWithString:suppliedFingerprintString];
if (![scanner scanHexLongLong:&suppliedFingerprint]) {
return NO;
}
if ([version isEqual:@"1"]) {
// The v1 hash algorithm is not permitted on the client so the actual hash cannot be validated.
return YES;
}
// Unknown version.
return NO;
}
- (NSString *)expectedBundleID {
return _options.bundleID;
}
// end App ID validation
#pragma mark - Reading From Plist & User Defaults
/**
* Clears the data collection switch from the standard NSUserDefaults for easier testing and
* readability.
*/
- (void)clearDataCollectionSwitchFromUserDefaults {
NSString *key =
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, self.name];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
/**
* Reads the data collection switch from the standard NSUserDefaults for easier testing and
* readability.
*/
+ (nullable NSNumber *)readDataCollectionSwitchFromUserDefaultsForApp:(FIRApp *)app {
// Read the object in user defaults, and only return if it's an NSNumber.
NSString *key =
[NSString stringWithFormat:kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat, app.name];
id collectionEnabledDefaultsObject = [[NSUserDefaults standardUserDefaults] objectForKey:key];
if ([collectionEnabledDefaultsObject isKindOfClass:[NSNumber class]]) {
return collectionEnabledDefaultsObject;
}
return nil;
}
/**
* Reads the data collection switch from the Info.plist for easier testing and readability. Will
* only read once from the plist and return the cached value.
*/
+ (nullable NSNumber *)readDataCollectionSwitchFromPlist {
static NSNumber *collectionEnabledPlistObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// Read the data from the `Info.plist`, only assign it if it's there and an NSNumber.
id plistValue = [[NSBundle mainBundle]
objectForInfoDictionaryKey:kFIRGlobalAppDataCollectionEnabledPlistKey];
if (plistValue && [plistValue isKindOfClass:[NSNumber class]]) {
collectionEnabledPlistObject = (NSNumber *)plistValue;
}
});
return collectionEnabledPlistObject;
}
#pragma mark - Sending Logs
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
- (void)sendLogsWithServiceName:(NSString *)serviceName
version:(NSString *)version
error:(NSError *)error {
// Do nothing. Please remove calls to this method.
}
#pragma clang diagnostic pop
#pragma mark - App Life Cycle
- (void)subscribeForAppDidBecomeActiveNotifications {
#if TARGET_OS_IOS || TARGET_OS_TV
NSNotificationName notificationName = UIApplicationDidBecomeActiveNotification;
#endif
#if TARGET_OS_OSX
NSNotificationName notificationName = NSApplicationDidBecomeActiveNotification;
#endif
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidBecomeActive:)
name:notificationName
object:nil];
}
- (void)appDidBecomeActive:(NSNotification *)notification {
[self logCoreTelemetryIfEnabled];
}
- (void)logCoreTelemetryIfEnabled {
if ([self isDataCollectionDefaultEnabled]) {
[FIRCoreDiagnosticsConnector logCoreTelemetryWithOptions:_options];
}
}
@end
@@ -0,0 +1,47 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Private/FIRAppAssociationRegistration.h"
#import <objc/runtime.h>
@implementation FIRAppAssociationRegistration
+ (nullable id)registeredObjectWithHost:(id)host
key:(NSString *)key
creationBlock:(id _Nullable (^)(void))creationBlock {
@synchronized(self) {
SEL dictKey = @selector(registeredObjectWithHost:key:creationBlock:);
NSMutableDictionary<NSString *, id> *objectsByKey = objc_getAssociatedObject(host, dictKey);
if (!objectsByKey) {
objectsByKey = [[NSMutableDictionary alloc] init];
objc_setAssociatedObject(host, dictKey, objectsByKey, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
id obj = objectsByKey[key];
NSValue *creationBlockBeingCalled = [NSValue valueWithPointer:dictKey];
if (obj) {
if ([creationBlockBeingCalled isEqual:obj]) {
[NSException raise:@"Reentering registeredObjectWithHost:key:creationBlock: not allowed"
format:@"host: %@ key: %@", host, key];
}
return obj;
}
objectsByKey[key] = creationBlockBeingCalled;
obj = creationBlock();
objectsByKey[key] = obj;
return obj;
}
}
@end
+75
View File
@@ -0,0 +1,75 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Private/FIRBundleUtil.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
@implementation FIRBundleUtil
+ (NSArray *)relevantBundles {
return @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ];
}
+ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
andFileType:(NSString *)fileType
inBundles:(NSArray *)bundles {
// Loop through all bundles to find the config dict.
for (NSBundle *bundle in bundles) {
NSString *path = [bundle pathForResource:resourceName ofType:fileType];
// Use the first one we find.
if (path) {
return path;
}
}
return nil;
}
+ (NSArray *)relevantURLSchemes {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (NSBundle *bundle in [[self class] relevantBundles]) {
NSArray *urlTypes = [bundle objectForInfoDictionaryKey:@"CFBundleURLTypes"];
for (NSDictionary *urlType in urlTypes) {
[result addObjectsFromArray:urlType[@"CFBundleURLSchemes"]];
}
}
return result;
}
+ (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles {
for (NSBundle *bundle in bundles) {
// This allows app extensions that have the app's bundle as their prefix to pass this test.
NSString *applicationBundleIdentifier =
[GULAppEnvironmentUtil isAppExtension]
? [self bundleIdentifierByRemovingLastPartFrom:bundle.bundleIdentifier]
: bundle.bundleIdentifier;
if ([applicationBundleIdentifier isEqualToString:bundleIdentifier]) {
return YES;
}
}
return NO;
}
+ (NSString *)bundleIdentifierByRemovingLastPartFrom:(NSString *)bundleIdentifier {
NSString *bundleIDComponentsSeparator = @".";
NSMutableArray<NSString *> *bundleIDComponents =
[[bundleIdentifier componentsSeparatedByString:bundleIDComponentsSeparator] mutableCopy];
[bundleIDComponents removeLastObject];
return [bundleIDComponents componentsJoinedByString:bundleIDComponentsSeparator];
}
@end
+65
View File
@@ -0,0 +1,65 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRComponent.h"
#import "Private/FIRComponentContainer.h"
#import "Private/FIRDependency.h"
@interface FIRComponent ()
- (instancetype)initWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
dependencies:(NSArray<FIRDependency *> *)dependencies
creationBlock:(FIRComponentCreationBlock)creationBlock;
@end
@implementation FIRComponent
+ (instancetype)componentWithProtocol:(Protocol *)protocol
creationBlock:(FIRComponentCreationBlock)creationBlock {
return [[FIRComponent alloc] initWithProtocol:protocol
instantiationTiming:FIRInstantiationTimingLazy
dependencies:@[]
creationBlock:creationBlock];
}
+ (instancetype)componentWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
dependencies:(NSArray<FIRDependency *> *)dependencies
creationBlock:(FIRComponentCreationBlock)creationBlock {
return [[FIRComponent alloc] initWithProtocol:protocol
instantiationTiming:instantiationTiming
dependencies:dependencies
creationBlock:creationBlock];
}
- (instancetype)initWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
dependencies:(NSArray<FIRDependency *> *)dependencies
creationBlock:(FIRComponentCreationBlock)creationBlock {
self = [super init];
if (self) {
_protocol = protocol;
_instantiationTiming = instantiationTiming;
_dependencies = [dependencies copy];
_creationBlock = creationBlock;
}
return self;
}
@end
+176
View File
@@ -0,0 +1,176 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRComponentContainer.h"
#import "Private/FIRAppInternal.h"
#import "Private/FIRComponent.h"
#import "Private/FIRLibrary.h"
#import "Private/FIRLogger.h"
NS_ASSUME_NONNULL_BEGIN
@interface FIRComponentContainer ()
/// The dictionary of components that are registered for a particular app. The key is an NSString
/// of the protocol.
@property(nonatomic, strong) NSMutableDictionary<NSString *, FIRComponentCreationBlock> *components;
/// Cached instances of components that requested to be cached.
@property(nonatomic, strong) NSMutableDictionary<NSString *, id> *cachedInstances;
@end
@implementation FIRComponentContainer
// Collection of all classes that register to provide components.
static NSMutableSet<Class> *sFIRComponentRegistrants;
#pragma mark - Public Registration
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sFIRComponentRegistrants = [[NSMutableSet<Class> alloc] init];
});
[self registerAsComponentRegistrant:klass inSet:sFIRComponentRegistrants];
}
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass
inSet:(NSMutableSet<Class> *)allRegistrants {
[allRegistrants addObject:klass];
}
#pragma mark - Internal Initialization
- (instancetype)initWithApp:(FIRApp *)app {
return [self initWithApp:app registrants:sFIRComponentRegistrants];
}
- (instancetype)initWithApp:(FIRApp *)app registrants:(NSMutableSet<Class> *)allRegistrants {
self = [super init];
if (self) {
_app = app;
_cachedInstances = [NSMutableDictionary<NSString *, id> dictionary];
_components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary];
[self populateComponentsFromRegisteredClasses:allRegistrants forApp:app];
}
return self;
}
- (void)populateComponentsFromRegisteredClasses:(NSSet<Class> *)classes forApp:(FIRApp *)app {
// Loop through the verified component registrants and populate the components array.
for (Class<FIRLibrary> klass in classes) {
// Loop through all the components being registered and store them as appropriate.
// Classes which do not provide functionality should use a dummy FIRComponentRegistrant
// protocol.
for (FIRComponent *component in [klass componentsToRegister]) {
// Check if the component has been registered before, and error out if so.
NSString *protocolName = NSStringFromProtocol(component.protocol);
if (self.components[protocolName]) {
FIRLogError(kFIRLoggerCore, @"I-COR000029",
@"Attempted to register protocol %@, but it already has an implementation.",
protocolName);
continue;
}
// Store the creation block for later usage.
self.components[protocolName] = component.creationBlock;
// Instantiate the
BOOL shouldInstantiateEager =
(component.instantiationTiming == FIRInstantiationTimingAlwaysEager);
BOOL shouldInstantiateDefaultEager =
(component.instantiationTiming == FIRInstantiationTimingEagerInDefaultApp &&
[app isDefaultApp]);
if (shouldInstantiateEager || shouldInstantiateDefaultEager) {
[self instantiateInstanceForProtocol:component.protocol withBlock:component.creationBlock];
}
}
}
}
#pragma mark - Instance Creation
/// Instantiate an instance of a class that conforms to the specified protocol.
/// This will:
/// - Call the block to create an instance if possible,
/// - Validate that the instance returned conforms to the protocol it claims to,
/// - Cache the instance if the block requests it
- (nullable id)instantiateInstanceForProtocol:(Protocol *)protocol
withBlock:(FIRComponentCreationBlock)creationBlock {
if (!creationBlock) {
return nil;
}
// Create an instance using the creation block.
BOOL shouldCache = NO;
id instance = creationBlock(self, &shouldCache);
if (!instance) {
return nil;
}
// An instance was created, validate that it conforms to the protocol it claims to.
NSString *protocolName = NSStringFromProtocol(protocol);
if (![instance conformsToProtocol:protocol]) {
FIRLogError(kFIRLoggerCore, @"I-COR000030",
@"An instance conforming to %@ was requested, but the instance provided does not "
@"conform to the protocol",
protocolName);
}
// The instance is ready to be returned, but check if it should be cached first before returning.
if (shouldCache) {
self.cachedInstances[protocolName] = instance;
}
return instance;
}
#pragma mark - Internal Retrieval
- (nullable id)instanceForProtocol:(Protocol *)protocol {
// Check if there is a cached instance, and return it if so.
NSString *protocolName = NSStringFromProtocol(protocol);
id cachedInstance = self.cachedInstances[protocolName];
if (cachedInstance) {
return cachedInstance;
}
// Use the creation block to instantiate an instance and return it.
FIRComponentCreationBlock creationBlock = self.components[protocolName];
return [self instantiateInstanceForProtocol:protocol withBlock:creationBlock];
}
#pragma mark - Lifecycle
- (void)removeAllCachedInstances {
// Loop through the cache and notify each instance that is a maintainer to clean up after itself.
for (id instance in self.cachedInstances.allValues) {
if ([instance conformsToProtocol:@protocol(FIRComponentLifecycleMaintainer)] &&
[instance respondsToSelector:@selector(appWillBeDeleted:)]) {
[instance appWillBeDeleted:self.app];
}
}
[self.cachedInstances removeAllObjects];
}
@end
NS_ASSUME_NONNULL_END
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRComponentType.h"
#import "Private/FIRComponentContainerInternal.h"
@implementation FIRComponentType
+ (id)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container {
// Forward the call to the container.
return [container instanceForProtocol:protocol];
}
@end
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Private/FIRConfigurationInternal.h"
#import "Private/FIRAnalyticsConfiguration.h"
extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
@implementation FIRConfiguration
+ (instancetype)sharedInstance {
static FIRConfiguration *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FIRConfiguration alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
_analyticsConfiguration = [FIRAnalyticsConfiguration sharedInstance];
}
return self;
}
- (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel {
NSAssert(loggerLevel <= FIRLoggerLevelMax && loggerLevel >= FIRLoggerLevelMin,
@"Invalid logger level, %ld", (long)loggerLevel);
FIRSetLoggerLevel(loggerLevel);
}
@end
@@ -0,0 +1,61 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRCoreDiagnosticsConnector.h"
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsInterop.h>
#import <FirebaseCore/FIROptions.h>
#import "Private/FIRAppInternal.h"
#import "Private/FIRDiagnosticsData.h"
#import "Private/FIROptionsInternal.h"
// Define the interop class symbol declared as an extern in FIRCoreDiagnosticsInterop.
Class<FIRCoreDiagnosticsInterop> FIRCoreDiagnosticsImplementation;
@implementation FIRCoreDiagnosticsConnector
+ (void)initialize {
if (!FIRCoreDiagnosticsImplementation) {
FIRCoreDiagnosticsImplementation = NSClassFromString(@"FIRCoreDiagnostics");
if (FIRCoreDiagnosticsImplementation) {
NSAssert([FIRCoreDiagnosticsImplementation
conformsToProtocol:@protocol(FIRCoreDiagnosticsInterop)],
@"If FIRCoreDiagnostics is implemented, it must conform to the interop protocol.");
NSAssert(
[FIRCoreDiagnosticsImplementation respondsToSelector:@selector(sendDiagnosticsData:)],
@"If FIRCoreDiagnostics is implemented, it must implement +sendDiagnosticsData.");
}
}
}
+ (void)logCoreTelemetryWithOptions:(FIROptions *)options {
if (FIRCoreDiagnosticsImplementation) {
FIRDiagnosticsData *diagnosticsData = [[FIRDiagnosticsData alloc] init];
[diagnosticsData insertValue:@(YES) forKey:kFIRCDIsDataCollectionDefaultEnabledKey];
[diagnosticsData insertValue:[FIRApp firebaseUserAgent] forKey:kFIRCDFirebaseUserAgentKey];
[diagnosticsData insertValue:@(FIRConfigTypeCore) forKey:kFIRCDConfigurationTypeKey];
[diagnosticsData insertValue:options.googleAppID forKey:kFIRCDGoogleAppIDKey];
[diagnosticsData insertValue:options.bundleID forKey:kFIRCDBundleIDKey];
[diagnosticsData insertValue:@(options.usingOptionsFromDefaultPlist)
forKey:kFIRCDUsingOptionsFromDefaultPlistKey];
[diagnosticsData insertValue:options.libraryVersionID forKey:kFIRCDLibraryVersionIDKey];
[FIRCoreDiagnosticsImplementation sendDiagnosticsData:diagnosticsData];
}
}
@end
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRDependency.h"
@interface FIRDependency ()
- (instancetype)initWithProtocol:(Protocol *)protocol isRequired:(BOOL)required;
@end
@implementation FIRDependency
+ (instancetype)dependencyWithProtocol:(Protocol *)protocol {
return [[self alloc] initWithProtocol:protocol isRequired:YES];
}
+ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required {
return [[self alloc] initWithProtocol:protocol isRequired:required];
}
- (instancetype)initWithProtocol:(Protocol *)protocol isRequired:(BOOL)required {
self = [super init];
if (self) {
_protocol = protocol;
_isRequired = required;
}
return self;
}
@end
+66
View File
@@ -0,0 +1,66 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRDiagnosticsData.h"
#import <FirebaseCore/FIRApp.h>
#import "Private/FIRAppInternal.h"
#import "Private/FIROptionsInternal.h"
@implementation FIRDiagnosticsData {
/** Backing ivar for the diagnosticObjects property. */
NSMutableDictionary<NSString *, id> *_diagnosticObjects;
}
- (instancetype)init {
self = [super init];
if (self) {
_diagnosticObjects = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)insertValue:(nullable id)value forKey:(NSString *)key {
if (key) {
_diagnosticObjects[key] = value;
}
}
#pragma mark - FIRCoreDiagnosticsData
- (NSDictionary<NSString *, id> *)diagnosticObjects {
if (!_diagnosticObjects[kFIRCDllAppsCountKey]) {
_diagnosticObjects[kFIRCDllAppsCountKey] = @([FIRApp allApps].count);
}
if (!_diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey]) {
_diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey] =
@([[FIRApp defaultApp] isDataCollectionDefaultEnabled]);
}
if (!_diagnosticObjects[kFIRCDFirebaseUserAgentKey]) {
_diagnosticObjects[kFIRCDFirebaseUserAgentKey] = [FIRApp firebaseUserAgent];
}
return _diagnosticObjects;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
- (void)setDiagnosticObjects:(NSDictionary<NSString *, id> *)diagnosticObjects {
NSAssert(NO, @"Please use -insertValue:forKey:");
}
#pragma clang diagnostic pop
@end
+21
View File
@@ -0,0 +1,21 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Private/FIRErrors.h"
NSString *const kFirebaseErrorDomain = @"com.firebase";
NSString *const kFirebaseConfigErrorDomain = @"com.firebase.config";
NSString *const kFirebaseCoreErrorDomain = @"com.firebase.core";
NSString *const kFirebasePerfErrorDomain = @"com.firebase.perf";
NSString *const kFirebaseStorageErrorDomain = @"com.firebase.storage";
+179
View File
@@ -0,0 +1,179 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Private/FIRLogger.h"
#import <FirebaseCore/FIRLoggerLevel.h>
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#import <GoogleUtilities/GULLogger.h>
#import "Private/FIRVersion.h"
FIRLoggerService kFIRLoggerCore = @"[Firebase/Core]";
// All the FIRLoggerService definitions should be migrated to clients. Do not add new ones!
FIRLoggerService kFIRLoggerABTesting = @"[Firebase/ABTesting]";
FIRLoggerService kFIRLoggerAdMob = @"[Firebase/AdMob]";
FIRLoggerService kFIRLoggerAnalytics = @"[Firebase/Analytics]";
FIRLoggerService kFIRLoggerAuth = @"[Firebase/Auth]";
FIRLoggerService kFIRLoggerCrash = @"[Firebase/Crash]";
FIRLoggerService kFIRLoggerMLKit = @"[Firebase/MLKit]";
FIRLoggerService kFIRLoggerPerf = @"[Firebase/Performance]";
FIRLoggerService kFIRLoggerRemoteConfig = @"[Firebase/RemoteConfig]";
/// Arguments passed on launch.
NSString *const kFIRDisableDebugModeApplicationArgument = @"-FIRDebugDisabled";
NSString *const kFIREnableDebugModeApplicationArgument = @"-FIRDebugEnabled";
NSString *const kFIRLoggerForceSDTERRApplicationArgument = @"-FIRLoggerForceSTDERR";
/// Key for the debug mode bit in NSUserDefaults.
NSString *const kFIRPersistedDebugModeKey = @"/google/firebase/debug_mode";
/// NSUserDefaults that should be used to store and read variables. If nil, `standardUserDefaults`
/// will be used.
static NSUserDefaults *sFIRLoggerUserDefaults;
static dispatch_once_t sFIRLoggerOnceToken;
// The sFIRAnalyticsDebugMode flag is here to support the -FIRDebugEnabled/-FIRDebugDisabled
// flags used by Analytics. Users who use those flags expect Analytics to log verbosely,
// while the rest of Firebase logs at the default level. This flag is introduced to support
// that behavior.
static BOOL sFIRAnalyticsDebugMode;
#ifdef DEBUG
/// The regex pattern for the message code.
static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$";
static NSRegularExpression *sMessageCodeRegex;
#endif
void FIRLoggerInitializeASL() {
dispatch_once(&sFIRLoggerOnceToken, ^{
// Register Firebase Version with GULLogger.
GULLoggerRegisterVersion(FIRVersionString);
// Override the aslOptions to ASL_OPT_STDERR if the override argument is passed in.
NSArray *arguments = [NSProcessInfo processInfo].arguments;
BOOL overrideSTDERR = [arguments containsObject:kFIRLoggerForceSDTERRApplicationArgument];
// Use the standard NSUserDefaults if it hasn't been explicitly set.
if (sFIRLoggerUserDefaults == nil) {
sFIRLoggerUserDefaults = [NSUserDefaults standardUserDefaults];
}
BOOL forceDebugMode = NO;
BOOL debugMode = [sFIRLoggerUserDefaults boolForKey:kFIRPersistedDebugModeKey];
if ([arguments containsObject:kFIRDisableDebugModeApplicationArgument]) { // Default mode
[sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey];
} else if ([arguments containsObject:kFIREnableDebugModeApplicationArgument] ||
debugMode) { // Debug mode
[sFIRLoggerUserDefaults setBool:YES forKey:kFIRPersistedDebugModeKey];
forceDebugMode = YES;
}
GULLoggerInitializeASL();
if (overrideSTDERR) {
GULLoggerEnableSTDERR();
}
if (forceDebugMode) {
GULLoggerForceDebug();
}
});
}
__attribute__((no_sanitize("thread"))) void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode) {
sFIRAnalyticsDebugMode = analyticsDebugMode;
}
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel) {
FIRLoggerInitializeASL();
GULSetLoggerLevel((GULLoggerLevel)loggerLevel);
}
#ifdef DEBUG
void FIRResetLogger() {
extern void GULResetLogger(void);
sFIRLoggerOnceToken = 0;
[sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey];
sFIRLoggerUserDefaults = nil;
GULResetLogger();
}
void FIRSetLoggerUserDefaults(NSUserDefaults *defaults) {
sFIRLoggerUserDefaults = defaults;
}
#endif
/**
* Check if the level is high enough to be loggable.
*
* Analytics can override the log level with an intentional race condition.
* Add the attribute to get a clean thread sanitizer run.
*/
__attribute__((no_sanitize("thread"))) BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel,
BOOL analyticsComponent) {
FIRLoggerInitializeASL();
if (sFIRAnalyticsDebugMode && analyticsComponent) {
return YES;
}
return GULIsLoggableLevel((GULLoggerLevel)loggerLevel);
}
void FIRLogBasic(FIRLoggerLevel level,
FIRLoggerService service,
NSString *messageCode,
NSString *message,
va_list args_ptr) {
FIRLoggerInitializeASL();
GULLogBasic((GULLoggerLevel)level, service,
sFIRAnalyticsDebugMode && [kFIRLoggerAnalytics isEqualToString:service], messageCode,
message, args_ptr);
}
/**
* Generates the logging functions using macros.
*
* Calling FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configure %@ failed.", @"blah") shows:
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Error> [Firebase/Core][I-COR000001] Configure blah failed.
* Calling FIRLogDebug(kFIRLoggerCore, @"I-COR000001", @"Configure succeed.") shows:
* yyyy-mm-dd hh:mm:ss.SSS sender[PID] <Debug> [Firebase/Core][I-COR000001] Configure succeed.
*/
#define FIR_LOGGING_FUNCTION(level) \
void FIRLog##level(FIRLoggerService service, NSString *messageCode, NSString *message, ...) { \
va_list args_ptr; \
va_start(args_ptr, message); \
FIRLogBasic(FIRLoggerLevel##level, service, messageCode, message, args_ptr); \
va_end(args_ptr); \
}
FIR_LOGGING_FUNCTION(Error)
FIR_LOGGING_FUNCTION(Warning)
FIR_LOGGING_FUNCTION(Notice)
FIR_LOGGING_FUNCTION(Info)
FIR_LOGGING_FUNCTION(Debug)
#undef FIR_MAKE_LOGGER
#pragma mark - FIRLoggerWrapper
@implementation FIRLoggerWrapper
+ (void)logWithLevel:(FIRLoggerLevel)level
withService:(FIRLoggerService)service
withCode:(NSString *)messageCode
withMessage:(NSString *)message
withArgs:(va_list)args {
FIRLogBasic(level, service, messageCode, message, args);
}
@end
+454
View File
@@ -0,0 +1,454 @@
// Copyright 2017 Google
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Private/FIRAppInternal.h"
#import "Private/FIRBundleUtil.h"
#import "Private/FIRErrors.h"
#import "Private/FIRLogger.h"
#import "Private/FIROptionsInternal.h"
#import "Private/FIRVersion.h"
// Keys for the strings in the plist file.
NSString *const kFIRAPIKey = @"API_KEY";
NSString *const kFIRTrackingID = @"TRACKING_ID";
NSString *const kFIRGoogleAppID = @"GOOGLE_APP_ID";
NSString *const kFIRClientID = @"CLIENT_ID";
NSString *const kFIRGCMSenderID = @"GCM_SENDER_ID";
NSString *const kFIRAndroidClientID = @"ANDROID_CLIENT_ID";
NSString *const kFIRDatabaseURL = @"DATABASE_URL";
NSString *const kFIRStorageBucket = @"STORAGE_BUCKET";
// The key to locate the expected bundle identifier in the plist file.
NSString *const kFIRBundleID = @"BUNDLE_ID";
// The key to locate the project identifier in the plist file.
NSString *const kFIRProjectID = @"PROJECT_ID";
NSString *const kFIRIsMeasurementEnabled = @"IS_MEASUREMENT_ENABLED";
NSString *const kFIRIsAnalyticsCollectionEnabled = @"FIREBASE_ANALYTICS_COLLECTION_ENABLED";
NSString *const kFIRIsAnalyticsCollectionDeactivated = @"FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED";
NSString *const kFIRIsAnalyticsEnabled = @"IS_ANALYTICS_ENABLED";
NSString *const kFIRIsSignInEnabled = @"IS_SIGNIN_ENABLED";
// Library version ID formatted like:
// @"5" // Major version (one or more digits)
// @"04" // Minor version (exactly 2 digits)
// @"01" // Build number (exactly 2 digits)
// @"000"; // Fixed "000"
NSString *kFIRLibraryVersionID;
// Plist file name.
NSString *const kServiceInfoFileName = @"GoogleService-Info";
// Plist file type.
NSString *const kServiceInfoFileType = @"plist";
// Exception raised from attempting to modify a FIROptions after it's been copied to a FIRApp.
NSString *const kFIRExceptionBadModification =
@"Attempted to modify options after it's set on FIRApp. Please modify all properties before "
@"initializing FIRApp.";
@interface FIROptions ()
/**
* This property maintains the actual configuration key-value pairs.
*/
@property(nonatomic, readwrite) NSMutableDictionary *optionsDictionary;
/**
* Calls `analyticsOptionsDictionaryWithInfoDictionary:` using [NSBundle mainBundle].infoDictionary.
* It combines analytics options from both the infoDictionary and the GoogleService-Info.plist.
* Values which are present in the main plist override values from the GoogleService-Info.plist.
*/
@property(nonatomic, readonly) NSDictionary *analyticsOptionsDictionary;
/**
* Combination of analytics options from both the infoDictionary and the GoogleService-Info.plist.
* Values which are present in the infoDictionary override values from the GoogleService-Info.plist.
*/
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary;
/**
* Throw exception if editing is locked when attempting to modify an option.
*/
- (void)checkEditingLocked;
@end
@implementation FIROptions {
/// Backing variable for self.analyticsOptionsDictionary.
NSDictionary *_analyticsOptionsDictionary;
}
static FIROptions *sDefaultOptions = nil;
static NSDictionary *sDefaultOptionsDictionary = nil;
#pragma mark - Public only for internal class methods
+ (FIROptions *)defaultOptions {
if (sDefaultOptions != nil) {
return sDefaultOptions;
}
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
if (defaultOptionsDictionary == nil) {
return nil;
}
sDefaultOptions = [[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
return sDefaultOptions;
}
#pragma mark - Private class methods
+ (void)initialize {
// Report FirebaseCore version for useragent string
[FIRApp registerLibrary:@"fire-ios"
withVersion:[NSString stringWithUTF8String:FIRCoreVersionString]];
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
NSString *xcodeVersion = info[@"DTXcodeBuild"];
NSString *sdkVersion = info[@"DTSDKBuild"];
if (xcodeVersion) {
[FIRApp registerLibrary:@"xcode" withVersion:xcodeVersion];
}
if (sdkVersion) {
[FIRApp registerLibrary:@"apple-sdk" withVersion:sdkVersion];
}
}
+ (NSDictionary *)defaultOptionsDictionary {
if (sDefaultOptionsDictionary != nil) {
return sDefaultOptionsDictionary;
}
NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
if (plistFilePath == nil) {
return nil;
}
sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
if (sDefaultOptionsDictionary == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000011",
@"The configuration file is not a dictionary: "
@"'%@.%@'.",
kServiceInfoFileName, kServiceInfoFileType);
}
return sDefaultOptionsDictionary;
}
// Returns the path of the plist file with a given file name.
+ (NSString *)plistFilePathWithName:(NSString *)fileName {
NSArray *bundles = [FIRBundleUtil relevantBundles];
NSString *plistFilePath =
[FIRBundleUtil optionsDictionaryPathWithResourceName:fileName
andFileType:kServiceInfoFileType
inBundles:bundles];
if (plistFilePath == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000012", @"Could not locate configuration file: '%@.%@'.",
fileName, kServiceInfoFileType);
}
return plistFilePath;
}
+ (void)resetDefaultOptions {
sDefaultOptions = nil;
sDefaultOptionsDictionary = nil;
}
#pragma mark - Private instance methods
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)optionsDictionary {
self = [super init];
if (self) {
_optionsDictionary = [optionsDictionary mutableCopy];
_usingOptionsFromDefaultPlist = YES;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
FIROptions *newOptions = [[[self class] allocWithZone:zone] init];
if (newOptions) {
newOptions.optionsDictionary = self.optionsDictionary;
newOptions.deepLinkURLScheme = self.deepLinkURLScheme;
newOptions.appGroupID = self.appGroupID;
newOptions.editingLocked = self.isEditingLocked;
newOptions.usingOptionsFromDefaultPlist = self.usingOptionsFromDefaultPlist;
}
return newOptions;
}
#pragma mark - Public instance methods
- (instancetype)initWithContentsOfFile:(NSString *)plistPath {
self = [super init];
if (self) {
if (plistPath == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000013", @"The plist file path is nil.");
return nil;
}
_optionsDictionary = [[NSDictionary dictionaryWithContentsOfFile:plistPath] mutableCopy];
if (_optionsDictionary == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000014",
@"The configuration file at %@ does not exist or "
@"is not a well-formed plist file.",
plistPath);
return nil;
}
// TODO: Do we want to validate the dictionary here? It says we do that already in
// the public header.
}
return self;
}
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID GCMSenderID:(NSString *)GCMSenderID {
self = [super init];
if (self) {
NSMutableDictionary *mutableOptionsDict = [NSMutableDictionary dictionary];
[mutableOptionsDict setValue:googleAppID forKey:kFIRGoogleAppID];
[mutableOptionsDict setValue:GCMSenderID forKey:kFIRGCMSenderID];
[mutableOptionsDict setValue:[[NSBundle mainBundle] bundleIdentifier] forKey:kFIRBundleID];
self.optionsDictionary = mutableOptionsDict;
}
return self;
}
- (NSString *)APIKey {
return self.optionsDictionary[kFIRAPIKey];
}
- (void)checkEditingLocked {
if (self.isEditingLocked) {
[NSException raise:kFirebaseCoreErrorDomain format:kFIRExceptionBadModification];
}
}
- (void)setAPIKey:(NSString *)APIKey {
[self checkEditingLocked];
_optionsDictionary[kFIRAPIKey] = [APIKey copy];
}
- (NSString *)clientID {
return self.optionsDictionary[kFIRClientID];
}
- (void)setClientID:(NSString *)clientID {
[self checkEditingLocked];
_optionsDictionary[kFIRClientID] = [clientID copy];
}
- (NSString *)trackingID {
return self.optionsDictionary[kFIRTrackingID];
}
- (void)setTrackingID:(NSString *)trackingID {
[self checkEditingLocked];
_optionsDictionary[kFIRTrackingID] = [trackingID copy];
}
- (NSString *)GCMSenderID {
return self.optionsDictionary[kFIRGCMSenderID];
}
- (void)setGCMSenderID:(NSString *)GCMSenderID {
[self checkEditingLocked];
_optionsDictionary[kFIRGCMSenderID] = [GCMSenderID copy];
}
- (NSString *)projectID {
return self.optionsDictionary[kFIRProjectID];
}
- (void)setProjectID:(NSString *)projectID {
[self checkEditingLocked];
_optionsDictionary[kFIRProjectID] = [projectID copy];
}
- (NSString *)androidClientID {
return self.optionsDictionary[kFIRAndroidClientID];
}
- (void)setAndroidClientID:(NSString *)androidClientID {
[self checkEditingLocked];
_optionsDictionary[kFIRAndroidClientID] = [androidClientID copy];
}
- (NSString *)googleAppID {
return self.optionsDictionary[kFIRGoogleAppID];
}
- (void)setGoogleAppID:(NSString *)googleAppID {
[self checkEditingLocked];
_optionsDictionary[kFIRGoogleAppID] = [googleAppID copy];
}
- (NSString *)libraryVersionID {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// The unit tests are set up to catch anything that does not properly convert.
NSString *version = [NSString stringWithUTF8String:FIRCoreVersionString];
NSArray *components = [version componentsSeparatedByString:@"."];
NSString *major = [components objectAtIndex:0];
NSString *minor = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:1] intValue]];
NSString *patch = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:2] intValue]];
kFIRLibraryVersionID = [NSString stringWithFormat:@"%@%@%@000", major, minor, patch];
});
return kFIRLibraryVersionID;
}
- (void)setLibraryVersionID:(NSString *)libraryVersionID {
_optionsDictionary[kFIRLibraryVersionID] = [libraryVersionID copy];
}
- (NSString *)databaseURL {
return self.optionsDictionary[kFIRDatabaseURL];
}
- (void)setDatabaseURL:(NSString *)databaseURL {
[self checkEditingLocked];
_optionsDictionary[kFIRDatabaseURL] = [databaseURL copy];
}
- (NSString *)storageBucket {
return self.optionsDictionary[kFIRStorageBucket];
}
- (void)setStorageBucket:(NSString *)storageBucket {
[self checkEditingLocked];
_optionsDictionary[kFIRStorageBucket] = [storageBucket copy];
}
- (void)setDeepLinkURLScheme:(NSString *)deepLinkURLScheme {
[self checkEditingLocked];
_deepLinkURLScheme = [deepLinkURLScheme copy];
}
- (NSString *)bundleID {
return self.optionsDictionary[kFIRBundleID];
}
- (void)setBundleID:(NSString *)bundleID {
[self checkEditingLocked];
_optionsDictionary[kFIRBundleID] = [bundleID copy];
}
- (void)setAppGroupID:(NSString *)appGroupID {
[self checkEditingLocked];
_appGroupID = [appGroupID copy];
}
#pragma mark - Internal instance methods
- (NSDictionary *)analyticsOptionsDictionaryWithInfoDictionary:(NSDictionary *)infoDictionary {
if (_analyticsOptionsDictionary == nil) {
NSMutableDictionary *tempAnalyticsOptions = [[NSMutableDictionary alloc] init];
NSArray *measurementKeys = @[
kFIRIsMeasurementEnabled, kFIRIsAnalyticsCollectionEnabled,
kFIRIsAnalyticsCollectionDeactivated
];
for (NSString *key in measurementKeys) {
id value = infoDictionary[key] ?: self.optionsDictionary[key] ?: nil;
if (!value) {
continue;
}
tempAnalyticsOptions[key] = value;
}
_analyticsOptionsDictionary = tempAnalyticsOptions;
}
return _analyticsOptionsDictionary;
}
- (NSDictionary *)analyticsOptionsDictionary {
return [self analyticsOptionsDictionaryWithInfoDictionary:[NSBundle mainBundle].infoDictionary];
}
/**
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
* GoogleService-Info.plist. This uses the old plist flag IS_MEASUREMENT_ENABLED, which should still
* be supported.
*/
- (BOOL)isMeasurementEnabled {
if (self.isAnalyticsCollectionDeactivated) {
return NO;
}
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
if (value == nil) {
// TODO: This could probably be cleaned up since FIROptions shouldn't know about FIRApp or have
// to check if it's the default app. The FIROptions instance can't be modified after
// `+configure` is called, so it's not a good place to copy it either in case the flag is
// changed at runtime.
// If no values are set for Analytics, fall back to the global collection switch in FIRApp.
// Analytics only supports the default FIRApp, so check that first.
if (![FIRApp isDefaultAppConfigured]) {
return NO;
}
// Fall back to the default app's collection switch when the key is not in the dictionary.
return [FIRApp defaultApp].isDataCollectionDefaultEnabled;
}
return [value boolValue];
}
- (BOOL)isAnalyticsCollectionExpicitlySet {
// If it's de-activated, it classifies as explicity set. If not, it's not a good enough indication
// that the developer wants FirebaseAnalytics enabled so continue checking.
if (self.isAnalyticsCollectionDeactivated) {
return YES;
}
// Check if the current Analytics flag is set.
id collectionEnabledObject = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
if (collectionEnabledObject && [collectionEnabledObject isKindOfClass:[NSNumber class]]) {
// It doesn't matter what the value is, it's explicitly set.
return YES;
}
// Check if the old measurement flag is set.
id measurementEnabledObject = self.analyticsOptionsDictionary[kFIRIsMeasurementEnabled];
if (measurementEnabledObject && [measurementEnabledObject isKindOfClass:[NSNumber class]]) {
// It doesn't matter what the value is, it's explicitly set.
return YES;
}
// No flags are set to explicitly enable or disable FirebaseAnalytics.
return NO;
}
- (BOOL)isAnalyticsCollectionEnabled {
if (self.isAnalyticsCollectionDeactivated) {
return NO;
}
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionEnabled];
if (value == nil) {
return self.isMeasurementEnabled; // Fall back to older plist flag.
}
return [value boolValue];
}
- (BOOL)isAnalyticsCollectionDeactivated {
NSNumber *value = self.analyticsOptionsDictionary[kFIRIsAnalyticsCollectionDeactivated];
if (value == nil) {
return NO; // Analytics Collection is not deactivated when the key is not in the dictionary.
}
return [value boolValue];
}
- (BOOL)isAnalyticsEnabled {
return [self.optionsDictionary[kFIRIsAnalyticsEnabled] boolValue];
}
- (BOOL)isSignInEnabled {
return [self.optionsDictionary[kFIRIsSignInEnabled] boolValue];
}
@end
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef Firebase_VERSION
#error "Firebase_VERSION is not defined: add -DFirebase_VERSION=... to the build invocation"
#endif
#ifndef FIRCore_VERSION
#error "FIRCore_VERSION is not defined: add -DFIRCore_VERSION=... to the build invocation"
#endif
// The following two macros supply the incantation so that the C
// preprocessor does not try to parse the version as a floating
// point number. See
// https://www.guyrutenberg.com/2008/12/20/expanding-macros-into-string-constants-in-c/
#define STR(x) STR_EXPAND(x)
#define STR_EXPAND(x) #x
const char *const FIRVersionString = (const char *const)STR(Firebase_VERSION);
const char *const FIRCoreVersionString = (const char *const)STR(FIRCore_VERSION);
@@ -0,0 +1,56 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/// Values stored in analyticsEnabledState. Never alter these constants since they must match with
/// values persisted to disk.
typedef NS_ENUM(int64_t, FIRAnalyticsEnabledState) {
// 0 is the default value for keys not found stored in persisted config, so it cannot represent
// kFIRAnalyticsEnabledStateSetNo. It must represent kFIRAnalyticsEnabledStateNotSet.
kFIRAnalyticsEnabledStateNotSet = 0,
kFIRAnalyticsEnabledStateSetYes = 1,
kFIRAnalyticsEnabledStateSetNo = 2,
};
/// The user defaults key for the persisted measurementEnabledState value. FIRAPersistedConfig reads
/// measurementEnabledState using this same key.
static NSString *const kFIRAPersistedConfigMeasurementEnabledStateKey =
@"/google/measurement/measurement_enabled_state";
static NSString *const kFIRAnalyticsConfigurationSetEnabledNotification =
@"FIRAnalyticsConfigurationSetEnabledNotification";
static NSString *const kFIRAnalyticsConfigurationSetMinimumSessionIntervalNotification =
@"FIRAnalyticsConfigurationSetMinimumSessionIntervalNotification";
static NSString *const kFIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification =
@"FIRAnalyticsConfigurationSetSessionTimeoutIntervalNotification";
@interface FIRAnalyticsConfiguration : NSObject
/// Returns the shared instance of FIRAnalyticsConfiguration.
+ (FIRAnalyticsConfiguration *)sharedInstance;
// Sets whether analytics collection is enabled for this app on this device. This setting is
// persisted across app sessions. By default it is enabled.
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled;
/// Sets whether analytics collection is enabled for this app on this device, and a flag to persist
/// the value or not. The setting should not be persisted if being set by the global data collection
/// flag.
- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled
persistSetting:(BOOL)shouldPersist;
@end
@@ -0,0 +1,49 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
// TODO: Remove this once Auth moves over to Core's instance registration system.
/** @class FIRAppAssociationRegistration
@brief Manages object associations as a singleton-dependent: At most one object is
registered for any given host/key pair, and the object shall be created on-the-fly when
asked for.
*/
@interface FIRAppAssociationRegistration<ObjectType> : NSObject
/** @fn registeredObjectWithHost:key:creationBlock:
@brief Retrieves the registered object with a particular host and key.
@param host The host object.
@param key The key to specify the registered object on the host.
@param creationBlock The block to return the object to be registered if not already.
The block is executed immediately before this method returns if it is executed at all.
It can also be executed multiple times across different method invocations if previous
execution of the block returns @c nil.
@return The registered object for the host/key pair, or @c nil if no object is registered
and @c creationBlock returns @c nil.
@remarks The method is thread-safe but non-reentrant in the sense that attempting to call this
method again within the @c creationBlock with the same host/key pair raises an exception.
The registered object is retained by the host.
*/
+ (nullable ObjectType)registeredObjectWithHost:(id)host
key:(NSString *)key
creationBlock:(ObjectType _Nullable (^)(void))creationBlock;
@end
NS_ASSUME_NONNULL_END
+162
View File
@@ -0,0 +1,162 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <FirebaseCore/FIRApp.h>
#import <FirebaseCore/FIRErrors.h>
@class FIRComponentContainer;
@protocol FIRLibrary;
/**
* The internal interface to FIRApp. This is meant for first-party integrators, who need to receive
* FIRApp notifications, log info about the success or failure of their configuration, and access
* other internal functionality of FIRApp.
*
* TODO(b/28296561): Restructure this header.
*/
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, FIRConfigType) {
FIRConfigTypeCore = 1,
FIRConfigTypeSDK = 2,
};
extern NSString *const kFIRDefaultAppName;
extern NSString *const kFIRAppReadyToConfigureSDKNotification;
extern NSString *const kFIRAppDeleteNotification;
extern NSString *const kFIRAppIsDefaultAppKey;
extern NSString *const kFIRAppNameKey;
extern NSString *const kFIRGoogleAppIDKey;
/**
* The format string for the User Defaults key used for storing the data collection enabled flag.
* This includes formatting to append the Firebase App's name.
*/
extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
/**
* The plist key used for storing the data collection enabled flag.
*/
extern NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey;
/**
* A notification fired containing diagnostic information when SDK errors occur.
*/
extern NSString *const kFIRAppDiagnosticsNotification;
/** @var FIRAuthStateDidChangeInternalNotification
@brief The name of the @c NSNotificationCenter notification which is posted when the auth state
changes (e.g. a new token has been produced, a user logs in or out). The object parameter of
the notification is a dictionary possibly containing the key:
@c FIRAuthStateDidChangeInternalNotificationTokenKey (the new access token.) If it does not
contain this key it indicates a sign-out event took place.
*/
extern NSString *const FIRAuthStateDidChangeInternalNotification;
/** @var FIRAuthStateDidChangeInternalNotificationTokenKey
@brief A key present in the dictionary object parameter of the
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
key will contain the new access token.
*/
extern NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey;
/** @var FIRAuthStateDidChangeInternalNotificationAppKey
@brief A key present in the dictionary object parameter of the
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
key will contain the FIRApp associated with the auth instance.
*/
extern NSString *const FIRAuthStateDidChangeInternalNotificationAppKey;
/** @var FIRAuthStateDidChangeInternalNotificationUIDKey
@brief A key present in the dictionary object parameter of the
@c FIRAuthStateDidChangeInternalNotification notification. The value associated with this
key will contain the new user's UID (or nil if there is no longer a user signed in).
*/
extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
@interface FIRApp ()
/**
* A flag indicating if this is the default app (has the default app name).
*/
@property(nonatomic, readonly) BOOL isDefaultApp;
/*
* The container of interop SDKs for this app.
*/
@property(nonatomic) FIRComponentContainer *container;
/**
* Creates an error for failing to configure a subspec service. This method is called by each
* FIRApp notification listener.
*/
+ (NSError *)errorForSubspecConfigurationFailureWithDomain:(NSString *)domain
errorCode:(FIRErrorCode)code
service:(NSString *)service
reason:(NSString *)reason;
/**
* Checks if the default app is configured without trying to configure it.
*/
+ (BOOL)isDefaultAppConfigured;
/**
* Registers a given third-party library with the given version number to be reported for
* analytics.
*
* @param name Name of the library.
* @param version Version of the library.
*/
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version;
/**
* Registers a given internal library with the given version number to be reported for
* analytics.
*
* @param library Optional parameter for component registration.
* @param name Name of the library.
* @param version Version of the library.
*/
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name
withVersion:(nonnull NSString *)version;
/**
* A concatenated string representing all the third-party libraries and version numbers.
*/
+ (NSString *)firebaseUserAgent;
/**
* Used by each SDK to send logs about SDK configuration status to Clearcut.
*
* @note This API is a no-op, please remove calls to it.
*/
- (void)sendLogsWithServiceName:(NSString *)serviceName
version:(NSString *)version
error:(NSError *)error;
/**
* Can be used by the unit tests in eack SDK to reset FIRApp. This method is thread unsafe.
*/
+ (void)resetApps;
/**
* Can be used by the unit tests in each SDK to set customized options.
*/
- (instancetype)initInstanceWithName:(NSString *)name options:(FIROptions *)options;
@end
NS_ASSUME_NONNULL_END
+53
View File
@@ -0,0 +1,53 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/**
* This class provides utilities for accessing resources in bundles.
*/
@interface FIRBundleUtil : NSObject
/**
* Finds all relevant bundles, starting with [NSBundle mainBundle].
*/
+ (NSArray *)relevantBundles;
/**
* Reads the options dictionary from one of the provided bundles.
*
* @param resourceName The resource name, e.g. @"GoogleService-Info".
* @param fileType The file type (extension), e.g. @"plist".
* @param bundles The bundles to expect, in priority order. See also
* +[FIRBundleUtil relevantBundles].
*/
+ (NSString *)optionsDictionaryPathWithResourceName:(NSString *)resourceName
andFileType:(NSString *)fileType
inBundles:(NSArray *)bundles;
/**
* Finds URL schemes defined in all relevant bundles, starting with those from
* [NSBundle mainBundle].
*/
+ (NSArray *)relevantURLSchemes;
/**
* Checks if any of the given bundles have a matching bundle identifier prefix (removing extension
* suffixes).
*/
+ (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles;
@end
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRApp;
@class FIRComponentContainer;
NS_ASSUME_NONNULL_BEGIN
/// Provides a system to clean up cached instances returned from the component system.
NS_SWIFT_NAME(ComponentLifecycleMaintainer)
@protocol FIRComponentLifecycleMaintainer
/// The associated app will be deleted, clean up any resources as they are about to be deallocated.
- (void)appWillBeDeleted:(FIRApp *)app;
@end
typedef _Nullable id (^FIRComponentCreationBlock)(FIRComponentContainer *container,
BOOL *isCacheable)
NS_SWIFT_NAME(ComponentCreationBlock);
@class FIRDependency;
/// Describes the timing of instantiation. Note: new components should default to lazy unless there
/// is a strong reason to be eager.
typedef NS_ENUM(NSInteger, FIRInstantiationTiming) {
FIRInstantiationTimingLazy,
FIRInstantiationTimingAlwaysEager,
FIRInstantiationTimingEagerInDefaultApp
} NS_SWIFT_NAME(InstantiationTiming);
/// A component that can be used from other Firebase SDKs.
NS_SWIFT_NAME(Component)
@interface FIRComponent : NSObject
/// The protocol describing functionality provided from the Component.
@property(nonatomic, strong, readonly) Protocol *protocol;
/// The timing of instantiation.
@property(nonatomic, readonly) FIRInstantiationTiming instantiationTiming;
/// An array of dependencies for the component.
@property(nonatomic, copy, readonly) NSArray<FIRDependency *> *dependencies;
/// A block to instantiate an instance of the component with the appropriate dependencies.
@property(nonatomic, copy, readonly) FIRComponentCreationBlock creationBlock;
// There's an issue with long NS_SWIFT_NAMES that causes compilation to fail, disable clang-format
// for the next two methods.
// clang-format off
/// Creates a component with no dependencies that will be lazily initialized.
+ (instancetype)componentWithProtocol:(Protocol *)protocol
creationBlock:(FIRComponentCreationBlock)creationBlock
NS_SWIFT_NAME(init(_:creationBlock:));
/// Creates a component to be registered with the component container.
///
/// @param protocol - The protocol describing functionality provided by the component.
/// @param instantiationTiming - When the component should be initialized. Use .lazy unless there's
/// a good reason to be instantiated earlier.
/// @param dependencies - Any dependencies the `implementingClass` has, optional or required.
/// @param creationBlock - A block to instantiate the component with a container, and if
/// @return A component that can be registered with the component container.
+ (instancetype)componentWithProtocol:(Protocol *)protocol
instantiationTiming:(FIRInstantiationTiming)instantiationTiming
dependencies:(NSArray<FIRDependency *> *)dependencies
creationBlock:(FIRComponentCreationBlock)creationBlock
NS_SWIFT_NAME(init(_:instantiationTiming:dependencies:creationBlock:));
// clang-format on
/// Unavailable.
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,44 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FIRComponentType.h"
#import "FIRLibrary.h"
NS_ASSUME_NONNULL_BEGIN
/// A type-safe macro to retrieve a component from a container. This should be used to retrieve
/// components instead of using the container directly.
#define FIR_COMPONENT(type, container) \
[FIRComponentType<id<type>> instanceForProtocol:@protocol(type) inContainer:container]
@class FIRApp;
/// A container that holds different components that are registered via the
/// `registerAsComponentRegistrant:` call. These classes should conform to `FIRComponentRegistrant`
/// in order to properly register components for Core.
NS_SWIFT_NAME(FirebaseComponentContainer)
@interface FIRComponentContainer : NSObject
/// A weak reference to the app that an instance of the container belongs to.
@property(nonatomic, weak, readonly) FIRApp *app;
/// Unavailable. Use the `container` property on `FIRApp`.
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,43 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FIRComponent.h"
#import "FIRComponentContainer.h"
@class FIRApp;
NS_ASSUME_NONNULL_BEGIN
@interface FIRComponentContainer (Private)
/// Initializes a contain for a given app. This should only be called by the app itself.
- (instancetype)initWithApp:(FIRApp *)app;
/// Retrieves an instance that conforms to the specified protocol. This will return `nil` if the
/// protocol wasn't registered, or if the instance couldn't instantiate for the provided app.
- (nullable id)instanceForProtocol:(Protocol *)protocol NS_SWIFT_NAME(instance(for:));
/// Remove all of the cached instances stored and allow them to clean up after themselves.
- (void)removeAllCachedInstances;
/// Register a class to provide components for the interoperability system. The class should conform
/// to `FIRComponentRegistrant` and provide an array of `FIRComponent` objects.
+ (void)registerAsComponentRegistrant:(Class<FIRLibrary>)klass;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,34 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRComponentContainer;
NS_ASSUME_NONNULL_BEGIN
/// Do not use directly. A placeholder type in order to provide a macro that will warn users of
/// mis-matched protocols.
NS_SWIFT_NAME(ComponentType)
@interface FIRComponentType<__covariant T> : NSObject
/// Do not use directly. A factory method to retrieve an instance that provides a specific
/// functionality.
+ (T)instanceForProtocol:(Protocol *)protocol inContainer:(FIRComponentContainer *)container;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,29 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRConfiguration.h"
@class FIRAnalyticsConfiguration;
@interface FIRConfiguration ()
/**
* The configuration class for Firebase Analytics. This should be removed once the logic for
* enabling and disabling Analytics is moved to Analytics.
*/
@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration;
@end
@@ -0,0 +1,35 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRDiagnosticsData;
@class FIROptions;
NS_ASSUME_NONNULL_BEGIN
/** Connects FIRCore with the CoreDiagnostics library. */
@interface FIRCoreDiagnosticsConnector : NSObject
/** Logs FirebaseCore related data.
*
* @param options The options object containing data to log.
*/
+ (void)logCoreTelemetryWithOptions:(FIROptions *)options;
@end
NS_ASSUME_NONNULL_END
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// A dependency on a specific protocol's functionality.
NS_SWIFT_NAME(Dependency)
@interface FIRDependency : NSObject
/// The protocol describing functionality being depended on.
@property(nonatomic, strong, readonly) Protocol *protocol;
/// A flag to specify if the dependency is required or not.
@property(nonatomic, readonly) BOOL isRequired;
/// Initializes a dependency that is required. Calls `initWithProtocol:isRequired` with `YES` for
/// the required parameter.
/// Creates a required dependency on the specified protocol's functionality.
+ (instancetype)dependencyWithProtocol:(Protocol *)protocol;
/// Creates a dependency on the specified protocol's functionality and specify if it's required for
/// the class's functionality.
+ (instancetype)dependencyWithProtocol:(Protocol *)protocol isRequired:(BOOL)required;
/// Use `dependencyWithProtocol:isRequired:` instead.
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,35 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsData.h>
NS_ASSUME_NONNULL_BEGIN
/** Implements the FIRCoreDiagnosticsData protocol to log diagnostics data. */
@interface FIRDiagnosticsData : NSObject <FIRCoreDiagnosticsData>
/** Inserts values into the diagnosticObjects dictionary if the value isn't nil.
*
* @param value The value to insert if it's not nil.
* @param key The key to associate it with.
*/
- (void)insertValue:(nullable id)value forKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** Error codes in Firebase error domain. */
typedef NS_ENUM(NSInteger, FIRErrorCode) {
/**
* Unknown error.
*/
FIRErrorCodeUnknown = 0,
/**
* Loading data from the GoogleService-Info.plist file failed. This is a fatal error and should
* not be ignored. Further calls to the API will fail and/or possibly cause crashes.
*/
FIRErrorCodeInvalidPlistFile = -100,
/**
* Validating the Google App ID format failed.
*/
FIRErrorCodeInvalidAppID = -101,
/**
* Error code for failing to configure a specific service.
*/
FIRErrorCodeConfigFailed = -114,
};
+24
View File
@@ -0,0 +1,24 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#include "FIRErrorCode.h"
extern NSString *const kFirebaseErrorDomain;
extern NSString *const kFirebaseConfigErrorDomain;
extern NSString *const kFirebaseCoreErrorDomain;
extern NSString *const kFirebasePerfErrorDomain;
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FIRLibrary_h
#define FIRLibrary_h
#import <Foundation/Foundation.h>
#import "FIRComponent.h"
@class FIRApp;
NS_ASSUME_NONNULL_BEGIN
/// Provide an interface to register a library for userAgent logging and availability to others.
NS_SWIFT_NAME(Library)
@protocol FIRLibrary
/// Returns one or more FIRComponents that will be registered in
/// FIRApp and participate in dependency resolution and injection.
+ (NSArray<FIRComponent *> *)componentsToRegister;
@optional
/// Implement this method if the library needs notifications for lifecycle events. This method is
/// called when the developer calls `FirebaseApp.configure()`.
+ (void)configureWithApp:(FIRApp *)app;
@end
NS_ASSUME_NONNULL_END
#endif /* FIRLibrary_h */
+151
View File
@@ -0,0 +1,151 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <FirebaseCore/FIRLoggerLevel.h>
NS_ASSUME_NONNULL_BEGIN
/**
* The Firebase services used in Firebase logger.
*/
typedef NSString *const FIRLoggerService;
extern FIRLoggerService kFIRLoggerABTesting;
extern FIRLoggerService kFIRLoggerAdMob;
extern FIRLoggerService kFIRLoggerAnalytics;
extern FIRLoggerService kFIRLoggerAuth;
extern FIRLoggerService kFIRLoggerCrash;
extern FIRLoggerService kFIRLoggerCore;
extern FIRLoggerService kFIRLoggerMLKit;
extern FIRLoggerService kFIRLoggerPerf;
extern FIRLoggerService kFIRLoggerRemoteConfig;
/**
* The key used to store the logger's error count.
*/
extern NSString *const kFIRLoggerErrorCountKey;
/**
* The key used to store the logger's warning count.
*/
extern NSString *const kFIRLoggerWarningCountKey;
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Enables or disables Analytics debug mode.
* If set to YES, the logging level for Analytics will be set to FIRLoggerLevelDebug.
* Enabling the debug mode has no effect if the app is running from App Store.
* (required) analytics debug mode flag.
*/
void FIRSetAnalyticsDebugMode(BOOL analyticsDebugMode);
/**
* Changes the default logging level of FIRLoggerLevelNotice to a user-specified level.
* The default level cannot be set above FIRLoggerLevelNotice if the app is running from App Store.
* (required) log level (one of the FIRLoggerLevel enum values).
*/
void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
/**
* Checks if the specified logger level is loggable given the current settings.
* (required) log level (one of the FIRLoggerLevel enum values).
* (required) whether or not this function is called from the Analytics component.
*/
BOOL FIRIsLoggableLevel(FIRLoggerLevel loggerLevel, BOOL analyticsComponent);
/**
* Logs a message to the Xcode console and the device log. If running from AppStore, will
* not log any messages with a level higher than FIRLoggerLevelNotice to avoid log spamming.
* (required) log level (one of the FIRLoggerLevel enum values).
* (required) service name of type FIRLoggerService.
* (required) message code starting with "I-" which means iOS, followed by a capitalized
* three-character service identifier and a six digit integer message ID that is unique
* within the service.
* An example of the message code is @"I-COR000001".
* (required) message string which can be a format string.
* (optional) variable arguments list obtained from calling va_start, used when message is a format
* string.
*/
extern void FIRLogBasic(FIRLoggerLevel level,
FIRLoggerService service,
NSString *messageCode,
NSString *message,
// On 64-bit simulators, va_list is not a pointer, so cannot be marked nullable
// See: http://stackoverflow.com/q/29095469
#if __LP64__ && TARGET_OS_SIMULATOR || TARGET_OS_OSX
va_list args_ptr
#else
va_list _Nullable args_ptr
#endif
);
/**
* The following functions accept the following parameters in order:
* (required) service name of type FIRLoggerService.
* (required) message code starting from "I-" which means iOS, followed by a capitalized
* three-character service identifier and a six digit integer message ID that is unique
* within the service.
* An example of the message code is @"I-COR000001".
* See go/firebase-log-proposal for details.
* (required) message string which can be a format string.
* (optional) the list of arguments to substitute into the format string.
* Example usage:
* FIRLogError(kFIRLoggerCore, @"I-COR000001", @"Configuration of %@ failed.", app.name);
*/
extern void FIRLogError(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogWarning(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogNotice(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogInfo(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
extern void FIRLogDebug(FIRLoggerService service, NSString *messageCode, NSString *message, ...)
NS_FORMAT_FUNCTION(3, 4);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
@interface FIRLoggerWrapper : NSObject
/**
* Objective-C wrapper for FIRLogBasic to allow weak linking to FIRLogger
* (required) log level (one of the FIRLoggerLevel enum values).
* (required) service name of type FIRLoggerService.
* (required) message code starting with "I-" which means iOS, followed by a capitalized
* three-character service identifier and a six digit integer message ID that is unique
* within the service.
* An example of the message code is @"I-COR000001".
* (required) message string which can be a format string.
* (optional) variable arguments list obtained from calling va_start, used when message is a format
* string.
*/
+ (void)logWithLevel:(FIRLoggerLevel)level
withService:(FIRLoggerService)service
withCode:(NSString *)messageCode
withMessage:(NSString *)message
withArgs:(va_list)args;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,114 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <FirebaseCore/FIROptions.h>
/**
* Keys for the strings in the plist file.
*/
extern NSString *const kFIRAPIKey;
extern NSString *const kFIRTrackingID;
extern NSString *const kFIRGoogleAppID;
extern NSString *const kFIRClientID;
extern NSString *const kFIRGCMSenderID;
extern NSString *const kFIRAndroidClientID;
extern NSString *const kFIRDatabaseURL;
extern NSString *const kFIRStorageBucket;
extern NSString *const kFIRBundleID;
extern NSString *const kFIRProjectID;
/**
* Keys for the plist file name
*/
extern NSString *const kServiceInfoFileName;
extern NSString *const kServiceInfoFileType;
/**
* This header file exposes the initialization of FIROptions to internal use.
*/
@interface FIROptions ()
/**
* resetDefaultOptions and initInternalWithOptionsDictionary: are exposed only for unit tests.
*/
+ (void)resetDefaultOptions;
/**
* Initializes the options with dictionary. The above strings are the keys of the dictionary.
* This is the designated initializer.
*/
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary;
/**
* defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp and
* other first party services.
*/
+ (FIROptions *)defaultOptions;
+ (NSDictionary *)defaultOptionsDictionary;
/**
* Indicates whether or not Analytics collection was explicitly enabled via a plist flag or at
* runtime.
*/
@property(nonatomic, readonly) BOOL isAnalyticsCollectionExpicitlySet;
/**
* Whether or not Analytics Collection was enabled. Analytics Collection is enabled unless
* explicitly disabled in GoogleService-Info.plist.
*/
@property(nonatomic, readonly) BOOL isAnalyticsCollectionEnabled;
/**
* Whether or not Analytics Collection was completely disabled. If YES, then
* isAnalyticsCollectionEnabled will be NO.
*/
@property(nonatomic, readonly) BOOL isAnalyticsCollectionDeactivated;
/**
* The version ID of the client library, e.g. @"1100000".
*/
@property(nonatomic, readonly, copy) NSString *libraryVersionID;
/**
* The flag indicating whether this object was constructed with the values in the default plist
* file.
*/
@property(nonatomic) BOOL usingOptionsFromDefaultPlist;
/**
* Whether or not Measurement was enabled. Measurement is enabled unless explicitly disabled in
* GoogleService-Info.plist.
*/
@property(nonatomic, readonly) BOOL isMeasurementEnabled;
/**
* Whether or not Analytics was enabled in the developer console.
*/
@property(nonatomic, readonly) BOOL isAnalyticsEnabled;
/**
* Whether or not SignIn was enabled in the developer console.
*/
@property(nonatomic, readonly) BOOL isSignInEnabled;
/**
* Whether or not editing is locked. This should occur after FIROptions has been set on a FIRApp.
*/
@property(nonatomic, getter=isEditingLocked) BOOL editingLocked;
@end
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/** The version of the Firebase SDK. */
FOUNDATION_EXPORT const char *const FIRVersionString;
/** The version of the FirebaseCore Component. */
FOUNDATION_EXPORT const char *const FIRCoreVersionString;
+127
View File
@@ -0,0 +1,127 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIROptions;
NS_ASSUME_NONNULL_BEGIN
/** A block that takes a BOOL and has no return value. */
typedef void (^FIRAppVoidBoolCallback)(BOOL success) NS_SWIFT_NAME(FirebaseAppVoidBoolCallback);
/**
* The entry point of Firebase SDKs.
*
* Initialize and configure FIRApp using +[FIRApp configure]
* or other customized ways as shown below.
*
* The logging system has two modes: default mode and debug mode. In default mode, only logs with
* log level Notice, Warning and Error will be sent to device. In debug mode, all logs will be sent
* to device. The log levels that Firebase uses are consistent with the ASL log levels.
*
* Enable debug mode by passing the -FIRDebugEnabled argument to the application. You can add this
* argument in the application's Xcode scheme. When debug mode is enabled via -FIRDebugEnabled,
* further executions of the application will also be in debug mode. In order to return to default
* mode, you must explicitly disable the debug mode with the application argument -FIRDebugDisabled.
*
* It is also possible to change the default logging level in code by calling setLoggerLevel: on
* the FIRConfiguration interface.
*/
NS_SWIFT_NAME(FirebaseApp)
@interface FIRApp : NSObject
/**
* Configures a default Firebase app. Raises an exception if any configuration step fails. The
* default app is named "__FIRAPP_DEFAULT". This method should be called after the app is launched
* and before using Firebase services. This method is thread safe and contains synchronous file I/O
* (reading GoogleService-Info.plist from disk).
*/
+ (void)configure;
/**
* Configures the default Firebase app with the provided options. The default app is named
* "__FIRAPP_DEFAULT". Raises an exception if any configuration step fails. This method is thread
* safe.
*
* @param options The Firebase application options used to configure the service.
*/
+ (void)configureWithOptions:(FIROptions *)options NS_SWIFT_NAME(configure(options:));
/**
* Configures a Firebase app with the given name and options. Raises an exception if any
* configuration step fails. This method is thread safe.
*
* @param name The application's name given by the developer. The name should should only contain
Letters, Numbers and Underscore.
* @param options The Firebase application options used to configure the services.
*/
// clang-format off
+ (void)configureWithName:(NSString *)name
options:(FIROptions *)options NS_SWIFT_NAME(configure(name:options:));
// clang-format on
/**
* Returns the default app, or nil if the default app does not exist.
*/
+ (nullable FIRApp *)defaultApp NS_SWIFT_NAME(app());
/**
* Returns a previously created FIRApp instance with the given name, or nil if no such app exists.
* This method is thread safe.
*/
+ (nullable FIRApp *)appNamed:(NSString *)name NS_SWIFT_NAME(app(name:));
/**
* Returns the set of all extant FIRApp instances, or nil if there are no FIRApp instances. This
* method is thread safe.
*/
@property(class, readonly, nullable) NSDictionary<NSString *, FIRApp *> *allApps;
/**
* Cleans up the current FIRApp, freeing associated data and returning its name to the pool for
* future use. This method is thread safe.
*/
- (void)deleteApp:(FIRAppVoidBoolCallback)completion;
/**
* FIRApp instances should not be initialized directly. Call +[FIRApp configure],
* +[FIRApp configureWithOptions:], or +[FIRApp configureWithNames:options:] directly.
*/
- (instancetype)init NS_UNAVAILABLE;
/**
* Gets the name of this app.
*/
@property(nonatomic, copy, readonly) NSString *name;
/**
* Gets a copy of the options for this app. These are non-modifiable.
*/
@property(nonatomic, copy, readonly) FIROptions *options;
/**
* Gets or sets whether automatic data collection is enabled for all products. Defaults to `YES`
* unless `FirebaseDataCollectionDefaultEnabled` is set to `NO` in your app's Info.plist. This value
* is persisted across runs of the app so that it can be set once when users have consented to
* collection.
*/
@property(nonatomic, readwrite, getter=isDataCollectionDefaultEnabled)
BOOL dataCollectionDefaultEnabled;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,45 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <FirebaseCore/FIRLoggerLevel.h>
NS_ASSUME_NONNULL_BEGIN
/**
* This interface provides global level properties that the developer can tweak.
*/
NS_SWIFT_NAME(FirebaseConfiguration)
@interface FIRConfiguration : NSObject
/** Returns the shared configuration object. */
@property(class, nonatomic, readonly) FIRConfiguration *sharedInstance NS_SWIFT_NAME(shared);
/**
* Sets the logging level for internal Firebase logging. Firebase will only log messages
* that are logged at or below loggerLevel. The messages are logged both to the Xcode
* console and to the device's log. Note that if an app is running from AppStore, it will
* never log above FIRLoggerLevelNotice even if loggerLevel is set to a higher (more verbose)
* setting.
*
* @param loggerLevel The maximum logging level. The default level is set to FIRLoggerLevelNotice.
*/
- (void)setLoggerLevel:(FIRLoggerLevel)loggerLevel;
@end
NS_ASSUME_NONNULL_END
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Note that importing GULLoggerLevel.h will lead to a non-modular header
// import error.
/**
* The log levels used by internal logging.
*/
typedef NS_ENUM(NSInteger, FIRLoggerLevel) {
/** Error level, matches ASL_LEVEL_ERR. */
FIRLoggerLevelError = 3,
/** Warning level, matches ASL_LEVEL_WARNING. */
FIRLoggerLevelWarning = 4,
/** Notice level, matches ASL_LEVEL_NOTICE. */
FIRLoggerLevelNotice = 5,
/** Info level, matches ASL_LEVEL_INFO. */
FIRLoggerLevelInfo = 6,
/** Debug level, matches ASL_LEVEL_DEBUG. */
FIRLoggerLevelDebug = 7,
/** Minimum log level. */
FIRLoggerLevelMin = FIRLoggerLevelError,
/** Maximum log level. */
FIRLoggerLevelMax = FIRLoggerLevelDebug
} NS_SWIFT_NAME(FirebaseLoggerLevel);
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* This class provides constant fields of Google APIs.
*/
NS_SWIFT_NAME(FirebaseOptions)
@interface FIROptions : NSObject <NSCopying>
/**
* Returns the default options. The first time this is called it synchronously reads
* GoogleService-Info.plist from disk.
*/
+ (nullable FIROptions *)defaultOptions NS_SWIFT_NAME(defaultOptions());
/**
* An iOS API key used for authenticating requests from your app, e.g.
* @"AIzaSyDdVgKwhZl0sTTTLZ7iTmt1r3N2cJLnaDk", used to identify your app to Google servers.
*/
@property(nonatomic, copy, nullable) NSString *APIKey NS_SWIFT_NAME(apiKey);
/**
* The bundle ID for the application. Defaults to `[[NSBundle mainBundle] bundleID]` when not set
* manually or in a plist.
*/
@property(nonatomic, copy) NSString *bundleID;
/**
* The OAuth2 client ID for iOS application used to authenticate Google users, for example
* @"12345.apps.googleusercontent.com", used for signing in with Google.
*/
@property(nonatomic, copy, nullable) NSString *clientID;
/**
* The tracking ID for Google Analytics, e.g. @"UA-12345678-1", used to configure Google Analytics.
*/
@property(nonatomic, copy, nullable) NSString *trackingID;
/**
* The Project Number from the Google Developer's console, for example @"012345678901", used to
* configure Google Cloud Messaging.
*/
@property(nonatomic, copy) NSString *GCMSenderID NS_SWIFT_NAME(gcmSenderID);
/**
* The Project ID from the Firebase console, for example @"abc-xyz-123".
*/
@property(nonatomic, copy, nullable) NSString *projectID;
/**
* The Android client ID used in Google AppInvite when an iOS app has its Android version, for
* example @"12345.apps.googleusercontent.com".
*/
@property(nonatomic, copy, nullable) NSString *androidClientID;
/**
* The Google App ID that is used to uniquely identify an instance of an app.
*/
@property(nonatomic, copy) NSString *googleAppID;
/**
* The database root URL, e.g. @"http://abc-xyz-123.firebaseio.com".
*/
@property(nonatomic, copy, nullable) NSString *databaseURL;
/**
* The URL scheme used to set up Durable Deep Link service.
*/
@property(nonatomic, copy, nullable) NSString *deepLinkURLScheme;
/**
* The Google Cloud Storage bucket name, e.g. @"abc-xyz-123.storage.firebase.com".
*/
@property(nonatomic, copy, nullable) NSString *storageBucket;
/**
* The App Group identifier to share data between the application and the application extensions.
* The App Group must be configured in the application and on the Apple Developer Portal. Default
* value `nil`.
*/
@property(nonatomic, copy, nullable) NSString *appGroupID;
/**
* Initializes a customized instance of FIROptions from the file at the given plist file path. This
* will read the file synchronously from disk.
* For example,
* NSString *filePath =
* [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
* FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
* Returns nil if the plist file does not exist or is invalid.
*/
- (nullable instancetype)initWithContentsOfFile:(NSString *)plistPath;
/**
* Initializes a customized instance of FIROptions with required fields. Use the mutable properties
* to modify fields for configuring specific services.
*/
// clang-format off
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID
GCMSenderID:(NSString *)GCMSenderID
NS_SWIFT_NAME(init(googleAppID:gcmSenderID:));
// clang-format on
@end
NS_ASSUME_NONNULL_END
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2017 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRApp.h"
#import "FIRConfiguration.h"
#import "FIRLoggerLevel.h"
#import "FIROptions.h"
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+223
View File
@@ -0,0 +1,223 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
[https://firebase.google.com](https://firebase.google.com).
## Installation
See the three subsections for details about three different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
### Standard pod install
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
For releases starting with 5.0.0, the source for each release is also deployed
to CocoaPods master and available via standard
[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See
[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All of the official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
```
To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
```
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution are at
[Carthage](Carthage.md).
### Rome
Instructions for installing binary frameworks via
[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.1 (or later)
* CocoaPods 1.7.2 (or later)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See
[Firestore/README.md](Firestore/README.md) and
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
before creating a PR.
Travis will verify that any code changes are done in a style compliant way. Install
`clang-format` and `swiftformat`.
These commands will get the right versions:
```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage
First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
After running the `AllUnitTests_iOS` scheme in Xcode, execute
`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
### Running Sample Apps
In order to run the sample apps and integration tests, you'll need valid
`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
files without real values, but can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g. `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
(e.g. in [Example/Database/App/](Example/Database/App/));
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
special Apple capabilities, and you will have to change the sample app to use a unique bundle
identifier that you can control in your own Apple Developer account.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
If you're doing specific Firebase Auth development, see
[the Auth Sample README](Example/Auth/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
To run the Database Integration tests, make your database authentication rules
[public](https://firebase.google.com/docs/database/security/quickstart).
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to actually test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account, and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
In order to receive push notifications, you'll have to follow the steps above and run the app on a
physical device.
## Community Supported Efforts
We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
participate in the Firebase community.
### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS.
For tvOS, checkout the [Sample](Example/tvOSSample).
Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile:
```
pod 'FirebaseAuth'
pod 'FirebaseCore'
pod 'FirebaseDatabase'
pod 'FirebaseFirestore'
pod 'FirebaseFunctions'
pod 'FirebaseMessaging'
pod 'FirebaseStorage'
```
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
iOS SDK.
## License
The contents of this repository is licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
@@ -0,0 +1,674 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <objc/runtime.h>
#include <sys/utsname.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <GoogleDataTransport/GDTEvent.h>
#import <GoogleDataTransport/GDTTargets.h>
#import <GoogleDataTransport/GDTTransport.h>
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#import <GoogleUtilities/GULLogger.h>
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsData.h>
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsInterop.h>
#import <nanopb/pb.h>
#import <nanopb/pb_decode.h>
#import <nanopb/pb_encode.h>
#import "FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h"
#import "FIRCDLibrary/FIRCoreDiagnosticsDateFileStorage.h"
/** The logger service string to use when printing to the console. */
static GULLoggerService kFIRCoreDiagnostics = @"[FirebaseCoreDiagnostics/FIRCoreDiagnostics]";
#ifdef FIREBASE_BUILD_ZIP_FILE
static BOOL kUsingZipFile = YES;
#else // FIREBASE_BUILD_ZIP_FILE
static BOOL kUsingZipFile = NO;
#endif // FIREBASE_BUILD_ZIP_FILE
#ifdef FIREBASE_BUILD_CARTHAGE
#define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_CARTHAGE
#elif FIREBASE_BUILD_ZIP_FILE
#define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_ZIP_FILE
#else
#define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_COCOAPODS
#endif
static NSString *const kFIRServiceMLVisionOnDeviceAutoML = @"MLVisionOnDeviceAutoML";
static NSString *const kFIRServiceMLVisionOnDeviceFace = @"MLVisionOnDeviceFace";
static NSString *const kFIRServiceMLVisionOnDeviceBarcode = @"MLVisionOnDeviceBarcode";
static NSString *const kFIRServiceMLVisionOnDeviceText = @"MLVisionOnDeviceText";
static NSString *const kFIRServiceMLVisionOnDeviceLabel = @"MLVisionOnDeviceLabel";
static NSString *const kFIRServiceMLVisionOnDeviceObjectDetection =
@"MLVisionOnDeviceObjectDetection";
static NSString *const kFIRServiceMLModelInterpreter = @"MLModelInterpreter";
static NSString *const kFIRServiceAdMob = @"AdMob";
static NSString *const kFIRServiceAuth = @"Auth";
static NSString *const kFIRServiceAuthUI = @"AuthUI";
static NSString *const kFIRServiceCrash = @"Crash";
static NSString *const kFIRServiceDatabase = @"Database";
static NSString *const kFIRServiceDynamicLinks = @"DynamicLinks";
static NSString *const kFIRServiceFirestore = @"Firestore";
static NSString *const kFIRServiceFunctions = @"Functions";
static NSString *const kFIRServiceIAM = @"InAppMessaging";
static NSString *const kFIRServiceInstanceID = @"InstanceID";
static NSString *const kFIRServiceInvites = @"Invites";
static NSString *const kFIRServiceMessaging = @"Messaging";
static NSString *const kFIRServiceMeasurement = @"Measurement";
static NSString *const kFIRServicePerformance = @"Performance";
static NSString *const kFIRServiceRemoteConfig = @"RemoteConfig";
static NSString *const kFIRServiceStorage = @"Storage";
static NSString *const kGGLServiceAnalytics = @"Analytics";
static NSString *const kGGLServiceSignIn = @"SignIn";
static NSString *const kFIRAppDiagnosticsConfigurationTypeKey =
@"FIRAppDiagnosticsConfigurationTypeKey";
static NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRAppDiagnosticsFIRAppKey";
static NSString *const kFIRAppDiagnosticsSDKNameKey = @"FIRAppDiagnosticsSDKNameKey";
static NSString *const kFIRAppDiagnosticsSDKVersionKey = @"FIRAppDiagnosticsSDKVersionKey";
/**
* The file name to the recent heartbeat date.
*/
NSString *const kFIRCoreDiagnosticsHeartbeatDateFileName = @"FIREBASE_DIAGNOSTICS_HEARTBEAT_DATE";
/**
* @note This should implement the GDTEventDataObject protocol, but can't because of weak-linking.
*/
@interface FIRCoreDiagnosticsLog : NSObject
/** The config that will be converted to proto bytes. */
@property(nonatomic) logs_proto_mobilesdk_ios_ICoreConfiguration config;
@end
@implementation FIRCoreDiagnosticsLog
- (instancetype)initWithConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration)config {
self = [super init];
if (self) {
_config = config;
}
return self;
}
// Provided and required by the GDTEventDataObject protocol.
- (NSData *)transportBytes {
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
// Encode 1 time to determine the size.
if (!pb_encode(&sizestream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
GDTLogError(GDTMCETransportBytesError, @"Error in nanopb encoding for size: %s",
PB_GET_ERROR(&sizestream));
}
// Encode a 2nd time to actually get the bytes from it.
size_t bufferSize = sizestream.bytes_written;
CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
if (!pb_encode(&ostream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
GDTLogError(GDTMCETransportBytesError, @"Error in nanopb encoding for bytes: %s",
PB_GET_ERROR(&ostream));
}
CFDataSetLength(dataRef, ostream.bytes_written);
return CFBridgingRelease(dataRef);
}
- (void)dealloc {
pb_release(logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config);
}
@end
NS_ASSUME_NONNULL_BEGIN
/** This class produces a protobuf containing diagnostics and usage data to be logged. */
@interface FIRCoreDiagnostics : NSObject <FIRCoreDiagnosticsInterop>
/** The queue on which all diagnostics collection will occur. */
@property(nonatomic, readonly) dispatch_queue_t diagnosticsQueue;
/** The transport object used to send data. */
@property(nonatomic, readonly) GDTTransport *transport;
/** The storage to store the date of the last sent heartbeat. */
@property(nonatomic, readonly) FIRCoreDiagnosticsDateFileStorage *heartbeatDateStorage;
@end
NS_ASSUME_NONNULL_END
@implementation FIRCoreDiagnostics
+ (instancetype)sharedInstance {
static FIRCoreDiagnostics *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FIRCoreDiagnostics alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
GDTTransport *transport = [[GDTTransport alloc] initWithMappingID:@"137"
transformers:nil
target:kGDTTargetCCT];
FIRCoreDiagnosticsDateFileStorage *dateStorage = [[FIRCoreDiagnosticsDateFileStorage alloc]
initWithFileURL:[[self class] filePathURLWithName:kFIRCoreDiagnosticsHeartbeatDateFileName]];
return [self initWithTransport:transport heartbeatDateStorage:dateStorage];
}
/** Initializer for unit tests.
*
* @param transport A `GDTTransport` instance which that be used to send event.
* @param heartbeatDateStorage An instanse of date storage to track heartbeat sending.
* @return Returns the initialized `FIRCoreDiagnostics` instance.
*/
- (instancetype)initWithTransport:(GDTTransport *)transport
heartbeatDateStorage:(FIRCoreDiagnosticsDateFileStorage *)heartbeatDateStorage {
self = [super init];
if (self) {
_diagnosticsQueue =
dispatch_queue_create("com.google.FIRCoreDiagnostics", DISPATCH_QUEUE_SERIAL);
_transport = transport;
_heartbeatDateStorage = heartbeatDateStorage;
}
return self;
}
#pragma mark - File path helpers
/** Returns the URL path of the file with name fileName under the Application Support folder for
* local logging. Creates the Application Support folder if the folder doesn't exist.
*
* @return the URL path of the file with the name fileName in Application Support.
*/
+ (NSURL *)filePathURLWithName:(NSString *)fileName {
@synchronized(self) {
NSArray<NSString *> *paths =
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSArray<NSString *> *components = @[ paths.lastObject, @"Google/FIRApp" ];
NSString *directoryString = [NSString pathWithComponents:components];
NSURL *directoryURL = [NSURL fileURLWithPath:directoryString];
NSError *error;
if (![directoryURL checkResourceIsReachableAndReturnError:&error]) {
// If fail creating the Application Support directory, return nil.
if (![[NSFileManager defaultManager] createDirectoryAtURL:directoryURL
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
GULLogWarning(kFIRCoreDiagnostics, YES, @"I-COR100001",
@"Unable to create internal state storage: %@", error);
return nil;
}
}
return [directoryURL URLByAppendingPathComponent:fileName];
}
}
#pragma mark - Metadata helpers
/** Returns the model of iOS device. Sample platform strings are @"iPhone7,1" for iPhone 6 Plus,
* @"iPhone7,2" for iPhone 6, etc. Refer to the Hardware strings at
* https://en.wikipedia.org/wiki/List_of_iOS_devices
*
* @return The device model as an NSString.
*/
+ (NSString *)deviceModel {
static NSString *deviceModel = nil;
if (deviceModel == nil) {
struct utsname systemInfo;
uname(&systemInfo);
deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
}
return deviceModel;
}
#pragma mark - nanopb helper functions
/** Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
*
* @note Memory needs to be free manually, through pb_free or pb_release.
* @param string The string to encode as pb_bytes.
*/
pb_bytes_array_t *FIREncodeString(NSString *string) {
NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding];
return FIREncodeData(stringBytes);
}
/** Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
*
* @note Memory needs to be free manually, through pb_free or pb_release.
* @param data The data to copy into the new bytes array.
*/
pb_bytes_array_t *FIREncodeData(NSData *data) {
pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
memcpy(pbBytes->bytes, [data bytes], data.length);
pbBytes->size = (pb_size_t)data.length;
return pbBytes;
}
/** Maps a service string to the representative nanopb enum.
*
* @param serviceString The SDK service string to convert.
* @return The representative nanopb enum.
*/
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType FIRMapFromServiceStringToTypeEnum(
NSString *serviceString) {
static NSDictionary<NSString *, NSNumber *> *serviceStringToTypeEnum;
if (serviceStringToTypeEnum == nil) {
serviceStringToTypeEnum = @{
kFIRServiceAdMob : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ADMOB),
kFIRServiceMessaging : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MESSAGING),
kFIRServiceMeasurement :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MEASUREMENT),
kFIRServiceRemoteConfig :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_REMOTE_CONFIG),
kFIRServiceDatabase : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DATABASE),
kFIRServiceDynamicLinks :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DYNAMIC_LINKS),
kFIRServiceAuth : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH),
kFIRServiceAuthUI : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH_UI),
kFIRServiceFirestore : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FIRESTORE),
kFIRServiceFunctions : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FUNCTIONS),
kFIRServicePerformance :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_PERFORMANCE),
kFIRServiceStorage : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_STORAGE),
kFIRServiceMLVisionOnDeviceAutoML :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_AUTOML),
kFIRServiceMLVisionOnDeviceFace :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_FACE),
kFIRServiceMLVisionOnDeviceBarcode :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_BARCODE),
kFIRServiceMLVisionOnDeviceText :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_TEXT),
kFIRServiceMLVisionOnDeviceLabel :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_LABEL),
kFIRServiceMLVisionOnDeviceObjectDetection : @(
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION),
kFIRServiceMLModelInterpreter :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_MODEL_INTERPRETER),
kGGLServiceAnalytics : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ANALYTICS),
kGGLServiceSignIn : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SIGN_IN),
kFIRServiceIAM : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_IN_APP_MESSAGING),
};
}
if (serviceStringToTypeEnum[serviceString] != nil) {
return (int32_t)serviceStringToTypeEnum[serviceString].longLongValue;
}
return logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE;
}
#pragma mark - Proto population functions
/** Populates the given proto with data related to an SDK logDiagnostics call from the
* diagnosticObjects dictionary.
*
* @param config The proto to populate
* @param diagnosticObjects The dictionary of diagnostics objects.
*/
void FIRPopulateProtoWithInfoFromUserInfoParams(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
NSDictionary<NSString *, id> *diagnosticObjects) {
NSNumber *configurationType = diagnosticObjects[kFIRCDConfigurationTypeKey];
if (configurationType != nil) {
switch (configurationType.integerValue) {
case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE:
config->configuration_type =
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE;
config->has_configuration_type = 1;
break;
case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK:
config->configuration_type =
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK;
config->has_configuration_type = 1;
break;
default:
break;
}
}
NSString *sdkName = diagnosticObjects[kFIRCDSdkNameKey];
if (sdkName) {
config->sdk_name = FIRMapFromServiceStringToTypeEnum(sdkName);
config->has_sdk_name = 1;
}
NSString *version = diagnosticObjects[kFIRCDSdkVersionKey];
if (version) {
config->sdk_version = FIREncodeString(version);
}
}
/** Populates the given proto with data from the calling FIRApp using the given
* diagnosticObjects dictionary.
*
* @param config The proto to populate
* @param diagnosticObjects The dictionary of diagnostics objects.
*/
void FIRPopulateProtoWithCommonInfoFromApp(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
NSDictionary<NSString *, id> *diagnosticObjects) {
config->pod_name = logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE;
config->has_pod_name = 1;
if (!diagnosticObjects[kFIRCDllAppsCountKey]) {
GDTLogError(GDTMCEGeneralError, @"%@", @"App count is a required value in the data dict.");
}
config->app_count = (int32_t)[diagnosticObjects[kFIRCDllAppsCountKey] integerValue];
config->has_app_count = 1;
NSString *googleAppID = diagnosticObjects[kFIRCDGoogleAppIDKey];
if (googleAppID.length) {
config->app_id = FIREncodeString(googleAppID);
}
NSString *bundleID = diagnosticObjects[kFIRCDBundleIDKey];
if (bundleID.length) {
config->bundle_id = FIREncodeString(bundleID);
}
NSString *firebaseUserAgent = diagnosticObjects[kFIRCDFirebaseUserAgentKey];
if (firebaseUserAgent.length) {
config->platform_info = FIREncodeString(firebaseUserAgent);
}
NSNumber *usingOptionsFromDefaultPlist = diagnosticObjects[kFIRCDUsingOptionsFromDefaultPlistKey];
if (usingOptionsFromDefaultPlist != nil) {
config->use_default_app = [usingOptionsFromDefaultPlist boolValue];
config->has_use_default_app = 1;
}
NSString *libraryVersionID = diagnosticObjects[kFIRCDLibraryVersionIDKey];
if (libraryVersionID) {
config->icore_version = FIREncodeString(libraryVersionID);
}
NSString *deviceModel = [FIRCoreDiagnostics deviceModel];
if (deviceModel.length) {
config->device_model = FIREncodeString(deviceModel);
}
NSString *osVersion = [GULAppEnvironmentUtil systemVersion];
if (osVersion.length) {
config->os_version = FIREncodeString(osVersion);
}
config->using_zip_file = kUsingZipFile;
config->has_using_zip_file = 1;
config->deployment_type = kDeploymentType;
config->has_deployment_type = 1;
config->deployed_in_app_store = [GULAppEnvironmentUtil isFromAppStore];
config->has_deployed_in_app_store = 1;
}
/** Populates the given proto with installed services data.
*
* @param config The proto to populate
*/
void FIRPopulateProtoWithInstalledServices(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
NSMutableArray<NSNumber *> *sdkServiceInstalledArray = [NSMutableArray array];
// AdMob
if (NSClassFromString(@"GADBannerView") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAdMob))];
}
// CloudMessaging
if (NSClassFromString(@"FIRMessaging") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMessaging))];
}
// RemoteConfig
if (NSClassFromString(@"FIRRemoteConfig") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceRemoteConfig))];
}
// Measurement/Analtyics
if (NSClassFromString(@"FIRAnalytics") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMeasurement))];
}
// ML Vision On Device AutoML.
if (NSClassFromString(@"FIRVisionOnDeviceAutoMLImageLabelerOptions") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceAutoML))];
}
// ML Vision On Device Face.
if (NSClassFromString(@"FIRVisionFaceDetector") != nil &&
NSClassFromString(@"GMVFaceDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceFace))];
}
// ML Vision On Device Barcode.
if (NSClassFromString(@"FIRVisionBarcodeDetector") != nil &&
NSClassFromString(@"GMVBarcodeDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceBarcode))];
}
// ML Vision On Device Text.
if (NSClassFromString(@"FIRVisionTextDetector") != nil &&
NSClassFromString(@"GMVTextDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceText))];
}
// ML Vision On Device Image Label.
if (NSClassFromString(@"FIRVisionLabelDetector") != nil &&
NSClassFromString(@"GMVLabelDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceLabel))];
}
// ML Vision On Device Object.
if (NSClassFromString(@"FIRVisionObjectDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceObjectDetection))];
}
// ML Model Interpreter
if (NSClassFromString(@"FIRCustomModelInterpreter") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLModelInterpreter))];
}
// Database
if (NSClassFromString(@"FIRDatabase") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDatabase))];
}
// DynamicDeepLink
if (NSClassFromString(@"FIRDynamicLinks") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDynamicLinks))];
}
// Auth
if (NSClassFromString(@"FIRAuth") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuth))];
}
// AuthUI
if (NSClassFromString(@"FUIAuth") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuthUI))];
}
// Firestore
if (NSClassFromString(@"FIRFirestore") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFirestore))];
}
// Functions
if (NSClassFromString(@"FIRFunctions") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFunctions))];
}
// Performance
if (NSClassFromString(@"FIRPerformance") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServicePerformance))];
}
// Storage
if (NSClassFromString(@"FIRStorage") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceStorage))];
}
// SignIn via Google pod
if (NSClassFromString(@"GIDSignIn") != nil && NSClassFromString(@"GGLContext") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceSignIn))];
}
// Analytics via Google pod
if (NSClassFromString(@"GAI") != nil && NSClassFromString(@"GGLContext") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceAnalytics))];
}
// In-App Messaging
if (NSClassFromString(@"FIRInAppMessaging") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceIAM))];
}
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *servicesInstalled =
malloc(sizeof(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType) *
sdkServiceInstalledArray.count);
for (NSUInteger i = 0; i < sdkServiceInstalledArray.count; i++) {
NSNumber *typeEnum = sdkServiceInstalledArray[i];
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType serviceType =
(int32_t)typeEnum.integerValue;
servicesInstalled[i] = serviceType;
}
config->sdk_service_installed = servicesInstalled;
config->sdk_service_installed_count = (int32_t)sdkServiceInstalledArray.count;
}
/** Populates the proto with the number of linked frameworks.
*
* @param config The proto to populate.
*/
void FIRPopulateProtoWithNumberOfLinkedFrameworks(
logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
int numFrameworks = -1; // Subtract the app binary itself.
unsigned int numImages;
const char **imageNames = objc_copyImageNames(&numImages);
for (unsigned int i = 0; i < numImages; i++) {
NSString *imageName = [NSString stringWithUTF8String:imageNames[i]];
if ([imageName rangeOfString:@"System/Library"].length != 0 // Apple .frameworks
|| [imageName rangeOfString:@"Developer/Library"].length != 0 // Xcode debug .frameworks
|| [imageName rangeOfString:@"usr/lib"].length != 0) { // Public .dylibs
continue;
}
numFrameworks++;
}
free(imageNames);
config->dynamic_framework_count = numFrameworks;
config->has_dynamic_framework_count = 1;
}
/** Populates the proto with Info.plist values.
*
* @param config The proto to populate.
*/
void FIRPopulateProtoWithInfoPlistValues(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
NSString *xcodeVersion = info[@"DTXcodeBuild"] ?: @"";
NSString *sdkVersion = info[@"DTSDKBuild"] ?: @"";
NSString *combinedVersions = [NSString stringWithFormat:@"%@-%@", xcodeVersion, sdkVersion];
config->apple_framework_version = FIREncodeString(combinedVersions);
NSString *minVersion = info[@"MinimumOSVersion"];
if (minVersion) {
config->min_supported_ios_version = FIREncodeString(minVersion);
}
// Apps can turn off swizzling in the Info.plist, check if they've explicitly set the value and
// report it. It's enabled by default.
NSNumber *appDelegateSwizzledNum = info[@"FirebaseAppDelegateProxyEnabled"];
BOOL appDelegateSwizzled = YES;
if ([appDelegateSwizzledNum isKindOfClass:[NSNumber class]]) {
appDelegateSwizzled = [appDelegateSwizzledNum boolValue];
}
config->swizzling_enabled = appDelegateSwizzled;
config->has_swizzling_enabled = 1;
}
#pragma mark - FIRCoreDiagnosticsInterop
+ (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
FIRCoreDiagnostics *diagnostics = [FIRCoreDiagnostics sharedInstance];
[diagnostics sendDiagnosticsData:diagnosticsData];
}
- (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
dispatch_async(self.diagnosticsQueue, ^{
NSDictionary<NSString *, id> *diagnosticObjects = diagnosticsData.diagnosticObjects;
NSNumber *isDataCollectionDefaultEnabled =
diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey];
if (isDataCollectionDefaultEnabled && ![isDataCollectionDefaultEnabled boolValue]) {
return;
}
// Create the proto.
logs_proto_mobilesdk_ios_ICoreConfiguration icore_config =
logs_proto_mobilesdk_ios_ICoreConfiguration_init_default;
icore_config.using_gdt = 1;
icore_config.has_using_gdt = 1;
// Populate the proto with information.
FIRPopulateProtoWithInfoFromUserInfoParams(&icore_config, diagnosticObjects);
FIRPopulateProtoWithCommonInfoFromApp(&icore_config, diagnosticObjects);
FIRPopulateProtoWithInstalledServices(&icore_config);
FIRPopulateProtoWithNumberOfLinkedFrameworks(&icore_config);
FIRPopulateProtoWithInfoPlistValues(&icore_config);
[self setHeartbeatFlagIfNeededToConfig:&icore_config];
// This log object is capable of converting the proto to bytes.
FIRCoreDiagnosticsLog *log = [[FIRCoreDiagnosticsLog alloc] initWithConfig:icore_config];
// Send the log as a telemetry event.
GDTEvent *event = [self.transport eventForTransport];
event.dataObject = (id<GDTEventDataObject>)log;
[self.transport sendTelemetryEvent:event];
});
}
#pragma mark - Heartbeat
- (void)setHeartbeatFlagIfNeededToConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration *)config {
// Check if need to send a heartbeat.
NSDate *currentDate = [NSDate date];
NSDate *lastCheckin = [self.heartbeatDateStorage date];
if (lastCheckin) {
// Ensure the previous checkin was on a different date in the past.
if ([self isDate:currentDate inSameDayOrBeforeThan:lastCheckin]) {
return;
}
}
// Update heartbeat sent date.
NSError *error;
if (![self.heartbeatDateStorage setDate:currentDate error:&error]) {
GULLogError(kFIRCoreDiagnostics, NO, @"I-COR100004", @"Unable to persist internal state: %@",
error);
}
// Set the flag.
config->sdk_name = logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ICORE;
config->has_sdk_name = 1;
}
- (BOOL)isDate:(NSDate *)date1 inSameDayOrBeforeThan:(NSDate *)date2 {
return [[NSCalendar currentCalendar] isDate:date1 inSameDayAsDate:date2] ||
[date1 compare:date2] == NSOrderedAscending;
}
@end
@@ -0,0 +1,47 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// Stores a date to a specified file.
@interface FIRCoreDiagnosticsDateFileStorage : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Default initializer.
* @param fileURL The URL of the file to store the date. The directory must exist, the file may not
* exist, it will be created if needed.
*/
- (instancetype)initWithFileURL:(NSURL *)fileURL;
/**
* Saves the date to the specified file.
* @return YES on success, NO otherwise.
*/
- (BOOL)setDate:(nullable NSDate *)date error:(NSError **)outError;
/**
* Reads the date to the specified file.
* @return Returns date if exists, otherwise `nil`.
*/
- (nullable NSDate *)date;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,64 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRCDLibrary/FIRCoreDiagnosticsDateFileStorage.h"
@interface FIRCoreDiagnosticsDateFileStorage ()
@property(nonatomic, readonly) NSURL *fileURL;
@end
@implementation FIRCoreDiagnosticsDateFileStorage
- (instancetype)initWithFileURL:(NSURL *)fileURL {
if (fileURL == nil) {
return nil;
}
self = [super init];
if (self) {
_fileURL = fileURL;
}
return self;
}
- (BOOL)setDate:(nullable NSDate *)date error:(NSError **)outError {
NSString *stringToSave = @"";
if (date != nil) {
NSTimeInterval timestamp = [date timeIntervalSinceReferenceDate];
stringToSave = [NSString stringWithFormat:@"%f", timestamp];
}
return [stringToSave writeToURL:self.fileURL
atomically:YES
encoding:NSUTF8StringEncoding
error:outError];
}
- (nullable NSDate *)date {
NSString *timestampString = [NSString stringWithContentsOfURL:self.fileURL
encoding:NSUTF8StringEncoding
error:nil];
if (timestampString.length == 0) {
return nil;
}
NSTimeInterval timestamp = timestampString.doubleValue;
return [NSDate dateWithTimeIntervalSinceReferenceDate:timestamp];
}
@end
@@ -0,0 +1,72 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.3.9.2 */
#include "firebasecore.nanopb.h"
/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
const pb_field_t logs_proto_mobilesdk_ios_ICoreConfiguration_fields[34] = {
PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, logs_proto_mobilesdk_ios_ICoreConfiguration, configuration_type, configuration_type, 0),
PB_FIELD( 2, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, version_name, configuration_type, 0),
PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, build_number, version_name, 0),
PB_FIELD( 4, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, build_type, build_number, 0),
PB_FIELD( 5, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, plist_version, build_type, 0),
PB_FIELD( 6, UENUM , REPEATED, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_service_enabled, plist_version, 0),
PB_FIELD( 7, UENUM , REPEATED, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_service_installed, sdk_service_enabled, 0),
PB_FIELD( 9, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, device_model, sdk_service_installed, 0),
PB_FIELD( 10, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, app_id, device_model, 0),
PB_FIELD( 11, INT64 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, project_number, app_id, 0),
PB_FIELD( 12, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, bundle_id, project_number, 0),
PB_FIELD( 13, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, client_id, bundle_id, 0),
PB_FIELD( 14, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, install, client_id, 0),
PB_FIELD( 16, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, pod_name, install, 0),
PB_FIELD( 18, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, icore_version, pod_name, 0),
PB_FIELD( 19, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_version, icore_version, 0),
PB_FIELD( 20, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_name, sdk_version, 0),
PB_FIELD( 21, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, app_count, sdk_name, 0),
PB_FIELD( 22, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, os_version, app_count, 0),
PB_FIELD( 23, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, itunes_id, os_version, 0),
PB_FIELD( 24, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, min_supported_ios_version, itunes_id, 0),
PB_FIELD( 25, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, use_default_app, min_supported_ios_version, 0),
PB_FIELD( 26, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, deployed_in_app_store, use_default_app, 0),
PB_FIELD( 27, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, dynamic_framework_count, deployed_in_app_store, 0),
PB_FIELD( 28, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, apple_framework_version, dynamic_framework_count, 0),
PB_FIELD( 29, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, using_zip_file, apple_framework_version, 0),
PB_FIELD( 30, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, deployment_type, using_zip_file, 0),
PB_FIELD( 31, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, platform_info, deployment_type, 0),
PB_FIELD( 32, INT64 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, app_extensions, platform_info, 0),
PB_FIELD( 33, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, swizzling_enabled, app_extensions, 0),
PB_FIELD( 34, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, log_error_count, swizzling_enabled, 0),
PB_FIELD( 35, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, log_warning_count, log_error_count, 0),
PB_FIELD( 36, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, using_gdt, log_warning_count, 0),
PB_LAST_FIELD
};
/* @@protoc_insertion_point(eof) */
@@ -0,0 +1,224 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.9.2 */
#ifndef PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED
#define PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED
#include <nanopb/pb.h>
/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType {
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_UNKNOWN_CONFIGURATION_TYPE = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK = 2
} logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_UNKNOWN_CONFIGURATION_TYPE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType)(logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType {
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_UNKNOWN_BUILD_TYPE = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_INTERNAL = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_EAP = 2,
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_PROD = 3
} logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_UNKNOWN_BUILD_TYPE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_PROD
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType)(logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_PROD+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType {
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ICORE = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ADMOB = 2,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_APP_INVITE = 3,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SIGN_IN = 5,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_GCM = 6,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MAPS = 7,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SCION = 8,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ANALYTICS = 9,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_APP_INDEXING = 10,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_CONFIG = 11,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DURABLE_DEEP_LINKS = 12,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_CRASH = 13,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH = 14,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DATABASE = 15,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_STORAGE = 16,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MESSAGING = 17,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MEASUREMENT = 18,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_REMOTE_CONFIG = 19,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DYNAMIC_LINKS = 20,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_INVITES = 21,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH_UI = 22,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FIRESTORE = 23,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_PERFORMANCE = 24,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_FACE = 26,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_BARCODE = 27,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_TEXT = 28,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_LABEL = 29,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_MODEL_INTERPRETER = 30,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_IN_APP_MESSAGING = 31,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FUNCTIONS = 32,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_NATURAL_LANGUAGE = 33,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_AUTOML = 34,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION = 35
} logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType)(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName {
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_UNKNOWN_POD_NAME = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_GOOGLE = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE = 2
} logs_proto_mobilesdk_ios_ICoreConfiguration_PodName;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_UNKNOWN_POD_NAME
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_PodName)(logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType {
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_UNKNOWN = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_COCOAPODS = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_ZIP_FILE = 2,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_CARTHAGE = 3,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM = 4
} logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_UNKNOWN
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType)(logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM+1))
/* Struct definitions */
typedef struct _logs_proto_mobilesdk_ios_ICoreConfiguration {
bool has_configuration_type;
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType configuration_type;
pb_bytes_array_t *version_name;
bool has_build_number;
int64_t build_number;
bool has_build_type;
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType build_type;
pb_bytes_array_t *plist_version;
pb_size_t sdk_service_enabled_count;
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *sdk_service_enabled;
pb_size_t sdk_service_installed_count;
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *sdk_service_installed;
pb_bytes_array_t *device_model;
pb_bytes_array_t *app_id;
bool has_project_number;
int64_t project_number;
pb_bytes_array_t *bundle_id;
pb_bytes_array_t *client_id;
pb_bytes_array_t *install;
bool has_pod_name;
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName pod_name;
pb_bytes_array_t *icore_version;
pb_bytes_array_t *sdk_version;
bool has_sdk_name;
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType sdk_name;
bool has_app_count;
int32_t app_count;
pb_bytes_array_t *os_version;
pb_bytes_array_t *itunes_id;
pb_bytes_array_t *min_supported_ios_version;
bool has_use_default_app;
bool use_default_app;
bool has_deployed_in_app_store;
bool deployed_in_app_store;
bool has_dynamic_framework_count;
int32_t dynamic_framework_count;
pb_bytes_array_t *apple_framework_version;
bool has_using_zip_file;
bool using_zip_file;
bool has_deployment_type;
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType deployment_type;
pb_bytes_array_t *platform_info;
bool has_app_extensions;
int64_t app_extensions;
bool has_swizzling_enabled;
bool swizzling_enabled;
bool has_log_error_count;
int32_t log_error_count;
bool has_log_warning_count;
int32_t log_warning_count;
bool has_using_gdt;
bool using_gdt;
/* @@protoc_insertion_point(struct:logs_proto_mobilesdk_ios_ICoreConfiguration) */
} logs_proto_mobilesdk_ios_ICoreConfiguration;
/* Default values for struct fields */
/* Initializer values for message structs */
#define logs_proto_mobilesdk_ios_ICoreConfiguration_init_default {false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MIN, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MIN, NULL, 0, NULL, 0, NULL, NULL, NULL, false, 0, NULL, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MIN, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MIN, false, 0, NULL, NULL, NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MIN, NULL, false, 0, false, 0, false, 0, false, 0, false, 0}
#define logs_proto_mobilesdk_ios_ICoreConfiguration_init_zero {false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MIN, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MIN, NULL, 0, NULL, 0, NULL, NULL, NULL, false, 0, NULL, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MIN, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MIN, false, 0, NULL, NULL, NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MIN, NULL, false, 0, false, 0, false, 0, false, 0, false, 0}
/* Field tags (for use in manual encoding/decoding) */
#define logs_proto_mobilesdk_ios_ICoreConfiguration_pod_name_tag 16
#define logs_proto_mobilesdk_ios_ICoreConfiguration_configuration_type_tag 1
#define logs_proto_mobilesdk_ios_ICoreConfiguration_version_name_tag 2
#define logs_proto_mobilesdk_ios_ICoreConfiguration_icore_version_tag 18
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_version_tag 19
#define logs_proto_mobilesdk_ios_ICoreConfiguration_build_number_tag 3
#define logs_proto_mobilesdk_ios_ICoreConfiguration_build_type_tag 4
#define logs_proto_mobilesdk_ios_ICoreConfiguration_plist_version_tag 5
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_service_enabled_tag 6
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_service_installed_tag 7
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_name_tag 20
#define logs_proto_mobilesdk_ios_ICoreConfiguration_device_model_tag 9
#define logs_proto_mobilesdk_ios_ICoreConfiguration_os_version_tag 22
#define logs_proto_mobilesdk_ios_ICoreConfiguration_app_id_tag 10
#define logs_proto_mobilesdk_ios_ICoreConfiguration_project_number_tag 11
#define logs_proto_mobilesdk_ios_ICoreConfiguration_bundle_id_tag 12
#define logs_proto_mobilesdk_ios_ICoreConfiguration_client_id_tag 13
#define logs_proto_mobilesdk_ios_ICoreConfiguration_itunes_id_tag 23
#define logs_proto_mobilesdk_ios_ICoreConfiguration_min_supported_ios_version_tag 24
#define logs_proto_mobilesdk_ios_ICoreConfiguration_install_tag 14
#define logs_proto_mobilesdk_ios_ICoreConfiguration_use_default_app_tag 25
#define logs_proto_mobilesdk_ios_ICoreConfiguration_app_count_tag 21
#define logs_proto_mobilesdk_ios_ICoreConfiguration_deployed_in_app_store_tag 26
#define logs_proto_mobilesdk_ios_ICoreConfiguration_dynamic_framework_count_tag 27
#define logs_proto_mobilesdk_ios_ICoreConfiguration_apple_framework_version_tag 28
#define logs_proto_mobilesdk_ios_ICoreConfiguration_using_zip_file_tag 29
#define logs_proto_mobilesdk_ios_ICoreConfiguration_deployment_type_tag 30
#define logs_proto_mobilesdk_ios_ICoreConfiguration_platform_info_tag 31
#define logs_proto_mobilesdk_ios_ICoreConfiguration_app_extensions_tag 32
#define logs_proto_mobilesdk_ios_ICoreConfiguration_swizzling_enabled_tag 33
#define logs_proto_mobilesdk_ios_ICoreConfiguration_log_error_count_tag 34
#define logs_proto_mobilesdk_ios_ICoreConfiguration_log_warning_count_tag 35
#define logs_proto_mobilesdk_ios_ICoreConfiguration_using_gdt_tag 36
/* Struct field encoding specification for nanopb */
extern const pb_field_t logs_proto_mobilesdk_ios_ICoreConfiguration_fields[34];
/* Maximum encoded size of messages (where known) */
/* logs_proto_mobilesdk_ios_ICoreConfiguration_size depends on runtime parameters */
/* Message IDs (where set with "msgid" option) */
#ifdef PB_MSGID
#define FIREBASECORE_MESSAGES \
#endif
/* @@protoc_insertion_point(eof) */
#endif
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+223
View File
@@ -0,0 +1,223 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
[https://firebase.google.com](https://firebase.google.com).
## Installation
See the three subsections for details about three different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
### Standard pod install
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
For releases starting with 5.0.0, the source for each release is also deployed
to CocoaPods master and available via standard
[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See
[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All of the official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
```
To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
```
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution are at
[Carthage](Carthage.md).
### Rome
Instructions for installing binary frameworks via
[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.1 (or later)
* CocoaPods 1.7.2 (or later)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See
[Firestore/README.md](Firestore/README.md) and
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
before creating a PR.
Travis will verify that any code changes are done in a style compliant way. Install
`clang-format` and `swiftformat`.
These commands will get the right versions:
```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage
First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
After running the `AllUnitTests_iOS` scheme in Xcode, execute
`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
### Running Sample Apps
In order to run the sample apps and integration tests, you'll need valid
`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
files without real values, but can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g. `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
(e.g. in [Example/Database/App/](Example/Database/App/));
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
special Apple capabilities, and you will have to change the sample app to use a unique bundle
identifier that you can control in your own Apple Developer account.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
If you're doing specific Firebase Auth development, see
[the Auth Sample README](Example/Auth/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
To run the Database Integration tests, make your database authentication rules
[public](https://firebase.google.com/docs/database/security/quickstart).
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to actually test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account, and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
In order to receive push notifications, you'll have to follow the steps above and run the app on a
physical device.
## Community Supported Efforts
We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
participate in the Firebase community.
### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS.
For tvOS, checkout the [Sample](Example/tvOSSample).
Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile:
```
pod 'FirebaseAuth'
pod 'FirebaseCore'
pod 'FirebaseDatabase'
pod 'FirebaseFirestore'
pod 'FirebaseFunctions'
pod 'FirebaseMessaging'
pod 'FirebaseStorage'
```
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
iOS SDK.
## License
The contents of this repository is licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
@@ -0,0 +1,63 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** If present, is a BOOL wrapped in an NSNumber. */
static NSString *const kFIRCDIsDataCollectionDefaultEnabledKey =
@"FIRCDIsDataCollectionDefaultEnabledKey";
/** If present, is an int32_t wrapped in an NSNumber. */
static NSString *const kFIRCDConfigurationTypeKey = @"FIRCDConfigurationTypeKey";
/** If present, is an NSString. */
static NSString *const kFIRCDSdkNameKey = @"FIRCDSdkNameKey";
/** If present, is an NSString. */
static NSString *const kFIRCDSdkVersionKey = @"FIRCDSdkVersionKey";
/** If present, is an int32_t wrapped in an NSNumber. */
static NSString *const kFIRCDllAppsCountKey = @"FIRCDllAppsCountKey";
/** If present, is an NSString. */
static NSString *const kFIRCDGoogleAppIDKey = @"FIRCDGoogleAppIDKey";
/** If present, is an NSString. */
static NSString *const kFIRCDBundleIDKey = @"FIRCDBundleID";
/** If present, is a BOOL wrapped in an NSNumber. */
static NSString *const kFIRCDUsingOptionsFromDefaultPlistKey =
@"FIRCDUsingOptionsFromDefaultPlistKey";
/** If present, is an NSString. */
static NSString *const kFIRCDLibraryVersionIDKey = @"FIRCDLibraryVersionIDKey";
/** If present, is an NSString. */
static NSString *const kFIRCDFirebaseUserAgentKey = @"FIRCDFirebaseUserAgentKey";
/** Defines the interface of a data object needed to log diagnostics data. */
@protocol FIRCoreDiagnosticsData <NSObject>
@required
/** A dictionary containing data (non-exhaustive) to be logged in diagnostics. */
@property(nonatomic) NSDictionary<NSString *, id> *diagnosticObjects;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,34 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FIRCoreDiagnosticsData.h"
NS_ASSUME_NONNULL_BEGIN
/** Allows the interoperation of FirebaseCore and FirebaseCoreDiagnostics. */
@protocol FIRCoreDiagnosticsInterop <NSObject>
/** Sends the given diagnostics data.
*
* @param diagnosticsData The diagnostics data object to send.
*/
+ (void)sendDiagnosticsData:(id<FIRCoreDiagnosticsData>)diagnosticsData;
@end
NS_ASSUME_NONNULL_END
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+223
View File
@@ -0,0 +1,223 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
[https://firebase.google.com](https://firebase.google.com).
## Installation
See the three subsections for details about three different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
### Standard pod install
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
For releases starting with 5.0.0, the source for each release is also deployed
to CocoaPods master and available via standard
[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See
[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All of the official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
```
To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
```
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution are at
[Carthage](Carthage.md).
### Rome
Instructions for installing binary frameworks via
[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.1 (or later)
* CocoaPods 1.7.2 (or later)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See
[Firestore/README.md](Firestore/README.md) and
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
before creating a PR.
Travis will verify that any code changes are done in a style compliant way. Install
`clang-format` and `swiftformat`.
These commands will get the right versions:
```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage
First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
After running the `AllUnitTests_iOS` scheme in Xcode, execute
`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
### Running Sample Apps
In order to run the sample apps and integration tests, you'll need valid
`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
files without real values, but can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g. `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
(e.g. in [Example/Database/App/](Example/Database/App/));
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
special Apple capabilities, and you will have to change the sample app to use a unique bundle
identifier that you can control in your own Apple Developer account.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
If you're doing specific Firebase Auth development, see
[the Auth Sample README](Example/Auth/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
To run the Database Integration tests, make your database authentication rules
[public](https://firebase.google.com/docs/database/security/quickstart).
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to actually test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account, and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
In order to receive push notifications, you'll have to follow the steps above and run the app on a
physical device.
## Community Supported Efforts
We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
participate in the Firebase community.
### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS.
For tvOS, checkout the [Sample](Example/tvOSSample).
Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile:
```
pod 'FirebaseAuth'
pod 'FirebaseCore'
pod 'FirebaseDatabase'
pod 'FirebaseFirestore'
pod 'FirebaseFunctions'
pod 'FirebaseMessaging'
pod 'FirebaseStorage'
```
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
iOS SDK.
## License
The contents of this repository is licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
@@ -0,0 +1,167 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
// The format of the debug code will show in the log as: e.g.
// for code 1000, it will show as I-IID001000.
typedef NS_ENUM(NSInteger, FIRInstanceIDMessageCode) {
// DO NOT USE 2000, 2002.
kFIRInstanceIDMessageCodeFIRApp000 = 1000, // I-IID001000
kFIRInstanceIDMessageCodeFIRApp001 = 1001,
kFIRInstanceIDMessageCodeFIRApp002 = 1002,
kFIRInstanceIDMessageCodeInternal001 = 2001,
kFIRInstanceIDMessageCodeInternal002 = 2002,
// FIRInstanceID.m
// DO NOT USE 4000.
kFIRInstanceIDMessageCodeInstanceID000 = 3000,
kFIRInstanceIDMessageCodeInstanceID001 = 3001,
kFIRInstanceIDMessageCodeInstanceID002 = 3002,
kFIRInstanceIDMessageCodeInstanceID003 = 3003,
kFIRInstanceIDMessageCodeInstanceID004 = 3004,
kFIRInstanceIDMessageCodeInstanceID005 = 3005,
kFIRInstanceIDMessageCodeInstanceID006 = 3006,
kFIRInstanceIDMessageCodeInstanceID007 = 3007,
kFIRInstanceIDMessageCodeInstanceID008 = 3008,
kFIRInstanceIDMessageCodeInstanceID009 = 3009,
kFIRInstanceIDMessageCodeInstanceID010 = 3010,
kFIRInstanceIDMessageCodeInstanceID011 = 3011,
kFIRInstanceIDMessageCodeInstanceID012 = 3012,
kFIRInstanceIDMessageCodeInstanceID013 = 3013,
kFIRInstanceIDMessageCodeInstanceID014 = 3014,
kFIRInstanceIDMessageCodeInstanceID015 = 3015,
kFIRInstanceIDMessageCodeRefetchingTokenForAPNS = 3016,
kFIRInstanceIDMessageCodeInstanceID017 = 3017,
kFIRInstanceIDMessageCodeInstanceID018 = 3018,
// FIRInstanceIDAuthService.m
kFIRInstanceIDMessageCodeAuthService000 = 5000,
kFIRInstanceIDMessageCodeAuthService001 = 5001,
kFIRInstanceIDMessageCodeAuthService002 = 5002,
kFIRInstanceIDMessageCodeAuthService003 = 5003,
kFIRInstanceIDMessageCodeAuthService004 = 5004,
kFIRInstanceIDMessageCodeAuthServiceCheckinInProgress = 5004,
// FIRInstanceIDBackupExcludedPlist.m
kFIRInstanceIDMessageCodeBackupExcludedPlist000 = 6000,
kFIRInstanceIDMessageCodeBackupExcludedPlist001 = 6001,
kFIRInstanceIDMessageCodeBackupExcludedPlist002 = 6002,
kFIRInstanceIDMessageCodeBackupExcludedPlistInvalidPlistEnum = 6003,
// FIRInstanceIDCheckinService.m
kFIRInstanceIDMessageCodeService000 = 7000,
kFIRInstanceIDMessageCodeService001 = 7001,
kFIRInstanceIDMessageCodeService002 = 7002,
kFIRInstanceIDMessageCodeService003 = 7003,
kFIRInstanceIDMessageCodeService004 = 7004,
kFIRInstanceIDMessageCodeService005 = 7005,
kFIRInstanceIDMessageCodeService006 = 7006,
kFIRInstanceIDInvalidNetworkSession = 7007,
kFIRInstanceIDInvalidSettingResponse = 7008,
// FIRInstanceIDCheckinStore.m
// DO NOT USE 8002, 8004 - 8008
kFIRInstanceIDMessageCodeCheckinStore000 = 8000,
kFIRInstanceIDMessageCodeCheckinStore001 = 8001,
kFIRInstanceIDMessageCodeCheckinStore003 = 8003,
kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistDeleted = 8009,
kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistSaved = 8010,
// FIRInstanceIDKeyPair.m
// DO NOT USE 9001, 9003
kFIRInstanceIDMessageCodeKeyPair000 = 9000,
kFIRInstanceIDMessageCodeKeyPair002 = 9002,
kFIRInstanceIDMessageCodeKeyPairMigrationError = 9004,
kFIRInstanceIDMessageCodeKeyPairMigrationSuccess = 9005,
kFIRInstanceIDMessageCodeKeyPairNoLegacyKeyPair = 9006,
// FIRInstanceIDKeyPairStore.m
kFIRInstanceIDMessageCodeKeyPairStore000 = 10000,
kFIRInstanceIDMessageCodeKeyPairStore001 = 10001,
kFIRInstanceIDMessageCodeKeyPairStore002 = 10002,
kFIRInstanceIDMessageCodeKeyPairStore003 = 10003,
kFIRInstanceIDMessageCodeKeyPairStore004 = 10004,
kFIRInstanceIDMessageCodeKeyPairStore005 = 10005,
kFIRInstanceIDMessageCodeKeyPairStore006 = 10006,
kFIRInstanceIDMessageCodeKeyPairStore007 = 10007,
kFIRInstanceIDMessageCodeKeyPairStore008 = 10008,
kFIRInstanceIDMessageCodeKeyPairStoreCouldNotLoadKeyPair = 10009,
// FIRInstanceIDKeyPairUtilities.m
kFIRInstanceIDMessageCodeKeyPairUtilities000 = 11000,
kFIRInstanceIDMessageCodeKeyPairUtilities001 = 11001,
kFIRInstanceIDMessageCodeKeyPairUtilitiesFirstConcatenateParamNil = 11002,
// DO NOT USE 12000 - 12014
// FIRInstanceIDStore.m
// DO NOT USE 13004, 13005, 13007, 13008, 13010, 13011, 13013, 13014
kFIRInstanceIDMessageCodeStore000 = 13000,
kFIRInstanceIDMessageCodeStore001 = 13001,
kFIRInstanceIDMessageCodeStore002 = 13002,
kFIRInstanceIDMessageCodeStore003 = 13003,
kFIRInstanceIDMessageCodeStore006 = 13006,
kFIRInstanceIDMessageCodeStore009 = 13009,
kFIRInstanceIDMessageCodeStore012 = 13012,
// FIRInstanceIDTokenManager.m
// DO NOT USE 14002, 14005
kFIRInstanceIDMessageCodeTokenManager000 = 14000,
kFIRInstanceIDMessageCodeTokenManager001 = 14001,
kFIRInstanceIDMessageCodeTokenManager003 = 14003,
kFIRInstanceIDMessageCodeTokenManager004 = 14004,
kFIRInstanceIDMessageCodeTokenManagerErrorDeletingFCMTokensOnAppReset = 14006,
kFIRInstanceIDMessageCodeTokenManagerDeletedFCMTokensOnAppReset = 14007,
kFIRInstanceIDMessageCodeTokenManagerSavedAppVersion = 14008,
kFIRInstanceIDMessageCodeTokenManagerErrorInvalidatingAllTokens = 14009,
kFIRInstanceIDMessageCodeTokenManagerAPNSChanged = 14010,
kFIRInstanceIDMessageCodeTokenManagerAPNSChangedTokenInvalidated = 14011,
kFIRInstanceIDMessageCodeTokenManagerInvalidateStaleToken = 14012,
// FIRInstanceIDTokenStore.m
// DO NOT USE 15002 - 15013
kFIRInstanceIDMessageCodeTokenStore000 = 15000,
kFIRInstanceIDMessageCodeTokenStore001 = 15001,
kFIRInstanceIDMessageCodeTokenStoreExceptionUnarchivingTokenInfo = 15015,
// DO NOT USE 16000, 18004
// FIRInstanceIDUtilities.m
kFIRInstanceIDMessageCodeUtilitiesMissingBundleIdentifier = 18000,
kFIRInstanceIDMessageCodeUtilitiesAppEnvironmentUtilNotAvailable = 18001,
kFIRInstanceIDMessageCodeUtilitiesCannotGetHardwareModel = 18002,
kFIRInstanceIDMessageCodeUtilitiesCannotGetSystemVersion = 18003,
// FIRInstanceIDTokenOperation.m
kFIRInstanceIDMessageCodeTokenOperationFailedToSignParams = 19000,
// FIRInstanceIDTokenFetchOperation.m
// DO NOT USE 20004, 20005
kFIRInstanceIDMessageCodeTokenFetchOperationFetchRequest = 20000,
kFIRInstanceIDMessageCodeTokenFetchOperationRequestError = 20001,
kFIRInstanceIDMessageCodeTokenFetchOperationBadResponse = 20002,
kFIRInstanceIDMessageCodeTokenFetchOperationBadTokenStructure = 20003,
// FIRInstanceIDTokenDeleteOperation.m
kFIRInstanceIDMessageCodeTokenDeleteOperationFetchRequest = 21000,
kFIRInstanceIDMessageCodeTokenDeleteOperationRequestError = 21001,
kFIRInstanceIDMessageCodeTokenDeleteOperationBadResponse = 21002,
// FIRInstanceIDTokenInfo.m
kFIRInstanceIDMessageCodeTokenInfoBadAPNSInfo = 22000,
kFIRInstanceIDMessageCodeTokenInfoFirebaseAppIDChanged = 22001,
kFIRInstanceIDMessageCodeTokenInfoLocaleChanged = 22002,
// FIRInstanceIDKeychain.m
kFIRInstanceIDKeychainReadItemError = 23000,
kFIRInstanceIDKeychainAddItemError = 23001,
kFIRInstanceIDKeychainDeleteItemError = 23002,
kFIRInstanceIDKeychainCreateKeyPairError = 23003,
kFIRInstanceIDKeychainUpdateItemError = 23004,
// FIRInstanceIDStringEncoding.m
kFIRInstanceIDStringEncodingBufferUnderflow = 24000,
kFIRInstanceIDStringEncodingBufferOverflow = 24001,
};
@@ -0,0 +1,49 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceID.h"
#import "FIRInstanceIDCheckinService.h"
/**
* Internal API used by Firebase SDK teams by calling in reflection or internal teams.
*/
// TODO(chliangGoogle) Rename this to Internal.
@interface FIRInstanceID (Private)
/**
* Fetches checkin info for the app. If the app has valid cached checkin preferences
* they are returned instead of making a network request.
*
* @param handler The completion handler to invoke once the request has completed.
*/
- (void)fetchCheckinInfoWithHandler:(nullable FIRInstanceIDDeviceCheckinCompletion)handler;
/**
* Get the InstanceID for the app. If an ID was created before and cached
* successfully we will return that ID. If no cached ID exists we create
* a new ID, cache it and return that.
*
* This is a blocking call and should not really be called on the main thread.
*
* @param error The error object that represents the error while trying to
* retrieve the instance id.
*
* @return The InstanceID for the app.
*/
- (nullable NSString *)appInstanceID:(NSError *_Nullable *_Nullable)error;
@end
@@ -0,0 +1,42 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceID+Private.h"
#import "FIRInstanceIDAuthService.h"
#import "FIRInstanceIDKeyPairStore.h"
#import "FIRInstanceIDTokenManager.h"
@interface FIRInstanceID ()
@property(nonatomic, readonly, strong) FIRInstanceIDTokenManager *tokenManager;
@property(nonatomic, readonly, strong) FIRInstanceIDKeyPairStore *keyPairStore;
@end
@implementation FIRInstanceID (Private)
// This method just wraps our pre-configured auth service to make the request.
// This method is only needed by first-party users, like Remote Config.
- (void)fetchCheckinInfoWithHandler:(FIRInstanceIDDeviceCheckinCompletion)handler {
[self.tokenManager.authService fetchCheckinInfoWithHandler:handler];
}
- (NSString *)appInstanceID:(NSError **)error {
return [self.keyPairStore appIdentityWithError:error];
}
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Represents an APNS device token and whether its environment is for sandbox.
* It can read from and write to an NSDictionary for simple serialization.
*/
@interface FIRInstanceIDAPNSInfo : NSObject <NSCoding>
/// The APNs device token, provided by the OS to the application delegate
@property(nonatomic, readonly, strong) NSData *deviceToken;
/// Represents whether or not this is deviceToken is for the sandbox
/// environment, or production.
@property(nonatomic, readonly, getter=isSandbox) BOOL sandbox;
/**
* Initializes the receiver with an APNs device token, and boolean
* representing whether that token is for the sandbox environment.
*
* @param deviceToken The APNs device token typically provided by the
* operating system.
* @param isSandbox YES if the APNs device token is for the sandbox
* environment, or NO if it is for production.
* @return An instance of FIRInstanceIDAPNSInfo.
*/
- (instancetype)initWithDeviceToken:(NSData *)deviceToken isSandbox:(BOOL)isSandbox;
/**
* Initializes the receiver from a token options dictionary containing data
* within the `kFIRInstanceIDTokenOptionsAPNSKey` and
* `kFIRInstanceIDTokenOptionsAPNSIsSandboxKey` keys. The token should be an
* NSData blob, and the sandbox value should be an NSNumber
* representing a boolean value.
*
* @param dictionary A dictionary containing values under the keys
* `kFIRInstanceIDTokenOptionsAPNSKey` and
* `kFIRInstanceIDTokenOptionsAPNSIsSandboxKey`.
* @return An instance of FIRInstanceIDAPNSInfo, or nil if the
* dictionary data was invalid or missing.
*/
- (nullable instancetype)initWithTokenOptionsDictionary:(NSDictionary *)dictionary;
- (BOOL)isEqualToAPNSInfo:(FIRInstanceIDAPNSInfo *)otherInfo;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,79 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDAPNSInfo.h"
#import "FIRInstanceIDConstants.h"
/// The key used to find the APNs device token in an archive.
NSString *const kFIRInstanceIDAPNSInfoTokenKey = @"device_token";
/// The key used to find the sandbox value in an archive.
NSString *const kFIRInstanceIDAPNSInfoSandboxKey = @"sandbox";
@implementation FIRInstanceIDAPNSInfo
- (instancetype)initWithDeviceToken:(NSData *)deviceToken isSandbox:(BOOL)isSandbox {
self = [super init];
if (self) {
_deviceToken = [deviceToken copy];
_sandbox = isSandbox;
}
return self;
}
- (instancetype)initWithTokenOptionsDictionary:(NSDictionary *)dictionary {
id deviceToken = dictionary[kFIRInstanceIDTokenOptionsAPNSKey];
if (![deviceToken isKindOfClass:[NSData class]]) {
return nil;
}
id isSandbox = dictionary[kFIRInstanceIDTokenOptionsAPNSIsSandboxKey];
if (![isSandbox isKindOfClass:[NSNumber class]]) {
return nil;
}
self = [super init];
if (self) {
_deviceToken = (NSData *)deviceToken;
_sandbox = ((NSNumber *)isSandbox).boolValue;
}
return self;
}
#pragma mark - NSCoding
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
id deviceToken = [aDecoder decodeObjectForKey:kFIRInstanceIDAPNSInfoTokenKey];
if (![deviceToken isKindOfClass:[NSData class]]) {
return nil;
}
BOOL isSandbox = [aDecoder decodeBoolForKey:kFIRInstanceIDAPNSInfoSandboxKey];
return [self initWithDeviceToken:(NSData *)deviceToken isSandbox:isSandbox];
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.deviceToken forKey:kFIRInstanceIDAPNSInfoTokenKey];
[aCoder encodeBool:self.sandbox forKey:kFIRInstanceIDAPNSInfoSandboxKey];
}
- (BOOL)isEqualToAPNSInfo:(FIRInstanceIDAPNSInfo *)otherInfo {
if ([super isEqual:otherInfo]) {
return YES;
}
return ([self.deviceToken isEqualToData:otherInfo.deviceToken] &&
self.isSandbox == otherInfo.isSandbox);
}
@end
@@ -0,0 +1,98 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
extern NSString *__nonnull const kFIRInstanceIDKeychainWildcardIdentifier;
NS_ASSUME_NONNULL_BEGIN
/**
* Wrapper around storing FCM auth data in iOS keychain.
*/
@interface FIRInstanceIDAuthKeychain : NSObject
/**
* Designated Initializer. Init a generic `SecClassGenericPassword` keychain with `identifier`
* as the `kSecAttrGeneric`.
*
* @param identifier The generic attribute to be used by the keychain.
*
* @return A Keychain object with `kSecAttrGeneric` attribute set to identifier.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier;
/**
* Get keychain items matching the given service and account. The service and/or account
* can be a wildcard (`kFIRInstanceIDKeychainWildcardIdentifier`), which case the query
* will include all items matching any services and/or accounts.
*
* @param service The kSecAttrService used to save the password. Can be wildcard.
* @param account The kSecAttrAccount used to save the password. Can be wildcard.
*
* @return An array of |NSData|s matching the provided inputs.
*/
- (NSArray<NSData *> *)itemsMatchingService:(NSString *)service account:(NSString *)account;
/**
* Get keychain item for a given service and account.
*
* @param service The kSecAttrService used to save the password.
* @param account The kSecAttrAccount used to save the password.
*
* @return A cached keychain item for a given account and service, or nil if it was not
* found or could not be retrieved.
*/
- (NSData *)dataForService:(NSString *)service account:(NSString *)account;
/**
* Remove the cached items from the keychain matching the service, account and access group.
* In case the items do not exist, YES is returned but with a valid error object with code
* `errSecItemNotFound`.
*
* @param service The kSecAttrService used to save the password.
* @param account The kSecAttrAccount used to save the password.
* @param handler The callback handler which is invoked when the remove operation is complete, with
* an error if there is any.
*/
- (void)removeItemsMatchingService:(NSString *)service
account:(NSString *)account
handler:(nullable void (^)(NSError *error))handler;
/**
* Set the data for a given service and account with a specific accessibility. If
* accessibility is NULL we use `kSecAttrAccessibleAlwaysThisDeviceOnly` which
* prevents backup and restore to iCloud, and works for app extension that can
* execute right after a device is restarted (and not unlocked).
*
* @param data The data to save.
* @param service The `kSecAttrService` used to save the password.
* @param accessibility The `kSecAttrAccessibility` used to save the password. If NULL
* set this to `kSecAttrAccessibleAlwaysThisDeviceOnly`.
* @param account The `kSecAttrAccount` used to save the password.
* @param handler The callback handler which is invoked when the add operation is complete,
* with an error if there is any.
*
*/
- (void)setData:(NSData *)data
forService:(NSString *)service
accessibility:(nullable CFTypeRef)accessibility
account:(NSString *)account
handler:(nullable void (^)(NSError *))handler;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,223 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDAuthKeyChain.h"
#import "FIRInstanceIDKeychain.h"
#import "FIRInstanceIDLogger.h"
/**
* The error type representing why we couldn't read data from the keychain.
*/
typedef NS_ENUM(int, FIRInstanceIDKeychainErrorType) {
kFIRInstanceIDKeychainErrorBadArguments = -1301,
};
NSString *const kFIRInstanceIDKeychainWildcardIdentifier = @"*";
@interface FIRInstanceIDAuthKeychain ()
@property(nonatomic, copy) NSString *generic;
// cachedKeychainData is keyed by service and account, the value is an array of NSData.
// It is used to cache the tokens per service, per account, as well as checkin data per service,
// per account inside the keychain.
@property(nonatomic)
NSMutableDictionary<NSString *, NSMutableDictionary<NSString *, NSArray<NSData *> *> *>
*cachedKeychainData;
@end
@implementation FIRInstanceIDAuthKeychain
- (instancetype)initWithIdentifier:(NSString *)identifier {
self = [super init];
if (self) {
_generic = [identifier copy];
_cachedKeychainData = [[NSMutableDictionary alloc] init];
}
return self;
}
+ (NSMutableDictionary *)keychainQueryForService:(NSString *)service
account:(NSString *)account
generic:(NSString *)generic {
NSDictionary *query = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword};
NSMutableDictionary *finalQuery = [NSMutableDictionary dictionaryWithDictionary:query];
if ([generic length] && ![kFIRInstanceIDKeychainWildcardIdentifier isEqualToString:generic]) {
finalQuery[(__bridge NSString *)kSecAttrGeneric] = generic;
}
if ([account length] && ![kFIRInstanceIDKeychainWildcardIdentifier isEqualToString:account]) {
finalQuery[(__bridge NSString *)kSecAttrAccount] = account;
}
if ([service length] && ![kFIRInstanceIDKeychainWildcardIdentifier isEqualToString:service]) {
finalQuery[(__bridge NSString *)kSecAttrService] = service;
}
return finalQuery;
}
- (NSMutableDictionary *)keychainQueryForService:(NSString *)service account:(NSString *)account {
return [[self class] keychainQueryForService:service account:account generic:self.generic];
}
- (NSArray<NSData *> *)itemsMatchingService:(NSString *)service account:(NSString *)account {
// If query wildcard service, it asks for all the results, which always query from keychain.
if (![service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] &&
![account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] &&
_cachedKeychainData[service][account]) {
// As long as service, account array exist, even it's empty, it means we've queried it before,
// returns the cache value.
return _cachedKeychainData[service][account];
}
NSMutableDictionary *keychainQuery = [self keychainQueryForService:service account:account];
NSMutableArray<NSData *> *results;
keychainQuery[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue;
#if TARGET_OS_IOS || TARGET_OS_TV
keychainQuery[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue;
keychainQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitAll;
// FIRInstanceIDKeychain should only take a query and return a result, will handle the query here.
NSArray *passwordInfos =
CFBridgingRelease([[FIRInstanceIDKeychain sharedInstance] itemWithQuery:keychainQuery]);
#elif TARGET_OS_OSX
keychainQuery[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
NSData *passwordInfos =
CFBridgingRelease([[FIRInstanceIDKeychain sharedInstance] itemWithQuery:keychainQuery]);
#endif
if (!passwordInfos) {
// Nothing was found, simply return from this sync block.
// Make sure to label the cache entry empty, signaling that we've queried this entry.
if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] ||
[account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) {
// Do not update cache if it's wildcard query.
return @[];
} else if (_cachedKeychainData[service]) {
[_cachedKeychainData[service] setObject:@[] forKey:account];
} else {
[_cachedKeychainData setObject:[@{account : @[]} mutableCopy] forKey:service];
}
return @[];
}
results = [[NSMutableArray alloc] init];
#if TARGET_OS_IOS || TARGET_OS_TV
NSInteger numPasswords = passwordInfos.count;
for (NSUInteger i = 0; i < numPasswords; i++) {
NSDictionary *passwordInfo = [passwordInfos objectAtIndex:i];
if (passwordInfo[(__bridge id)kSecValueData]) {
[results addObject:passwordInfo[(__bridge id)kSecValueData]];
}
}
#elif TARGET_OS_OSX
[results addObject:passwordInfos];
#endif
// We query the keychain because it didn't exist in cache, now query is done, update the result in
// the cache.
if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] ||
[account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) {
// Do not update cache if it's wildcard query.
return [results copy];
} else if (_cachedKeychainData[service]) {
[_cachedKeychainData[service] setObject:[results copy] forKey:account];
} else {
NSMutableDictionary *entry = [@{account : [results copy]} mutableCopy];
[_cachedKeychainData setObject:entry forKey:service];
}
return [results copy];
}
- (NSData *)dataForService:(NSString *)service account:(NSString *)account {
NSArray<NSData *> *items = [self itemsMatchingService:service account:account];
// If items is nil or empty, nil will be returned.
return items.firstObject;
}
- (void)removeItemsMatchingService:(NSString *)service
account:(NSString *)account
handler:(void (^)(NSError *error))handler {
if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) {
// Delete all keychain items.
_cachedKeychainData = [[NSMutableDictionary alloc] init];
} else if ([account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) {
// Delete all entries under service,
if (_cachedKeychainData[service]) {
_cachedKeychainData[service] = [[NSMutableDictionary alloc] init];
}
} else if (_cachedKeychainData[service]) {
// We should keep the service/account entry instead of nil so we know
// it's "empty entry" instead of "not query from keychain yet".
[_cachedKeychainData[service] setObject:@[] forKey:account];
} else {
[_cachedKeychainData setObject:[@{account : @[]} mutableCopy] forKey:service];
}
NSMutableDictionary *keychainQuery = [self keychainQueryForService:service account:account];
[[FIRInstanceIDKeychain sharedInstance] removeItemWithQuery:keychainQuery handler:handler];
}
- (void)setData:(NSData *)data
forService:(NSString *)service
accessibility:(CFTypeRef)accessibility
account:(NSString *)account
handler:(void (^)(NSError *))handler {
if ([service isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier] ||
[account isEqualToString:kFIRInstanceIDKeychainWildcardIdentifier]) {
if (handler) {
handler([NSError errorWithDomain:kFIRInstanceIDKeychainErrorDomain
code:kFIRInstanceIDKeychainErrorBadArguments
userInfo:nil]);
}
return;
}
[self removeItemsMatchingService:service
account:account
handler:^(NSError *error) {
if (error) {
if (handler) {
handler(error);
}
return;
}
if (data.length > 0) {
NSMutableDictionary *keychainQuery =
[self keychainQueryForService:service account:account];
keychainQuery[(__bridge id)kSecValueData] = data;
if (accessibility != NULL) {
keychainQuery[(__bridge id)kSecAttrAccessible] =
(__bridge id)accessibility;
} else {
// Defaults to No backup
keychainQuery[(__bridge id)kSecAttrAccessible] =
(__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly;
}
[[FIRInstanceIDKeychain sharedInstance]
addItemWithQuery:keychainQuery
handler:handler];
}
}];
// Set the cache value. This must happen after removeItemsMatchingService:account:handler was
// called, so the cache value was reset before setting a new value.
if (_cachedKeychainData[service]) {
if (_cachedKeychainData[service][account]) {
_cachedKeychainData[service][account] = @[ data ];
} else {
[_cachedKeychainData[service] setObject:@[ data ] forKey:account];
}
} else {
[_cachedKeychainData setObject:[@{account : @[ data ]} mutableCopy] forKey:service];
}
}
@end
@@ -0,0 +1,91 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FIRInstanceIDCheckinService.h"
@class FIRInstanceIDCheckinPreferences;
@class FIRInstanceIDStore;
/**
* FIRInstanceIDAuthService is responsible for retrieving, caching, and supplying checkin info
* for the rest of Instance ID. A checkin can be scheduled, meaning that it will keep retrying the
* checkin request until it is successful. A checkin can also be requested directly, with a
* completion handler.
*/
@interface FIRInstanceIDAuthService : NSObject
/**
* Used only for testing. In addition to taking a store (for locally caching the checkin info), it
* also takes a checkinService.
*/
- (instancetype)initWithCheckinService:(FIRInstanceIDCheckinService *)checkinService
store:(FIRInstanceIDStore *)store;
/**
* Initializes the auth service given a store (which provides the local caching of checkin info).
* This initializer will create its own instance of FIRInstanceIDCheckinService.
*/
- (instancetype)initWithStore:(FIRInstanceIDStore *)store;
#pragma mark - Checkin Service
/**
* Checks if the current deviceID and secret are valid or not.
*
* @return YES if the checkin credentials are valid else NO.
*/
- (BOOL)hasValidCheckinInfo;
/**
* Fetch checkin info from the server. This would usually refresh the existing
* checkin credentials for the current app.
*
* @param handler The completion handler to invoke once the checkin info has been
* refreshed.
*/
- (void)fetchCheckinInfoWithHandler:(FIRInstanceIDDeviceCheckinCompletion)handler;
/**
* Schedule checkin. Will hit the network only if the currently loaded checkin
* preferences are stale.
*
* @param immediately YES if we want it to be scheduled immediately else NO.
*/
- (void)scheduleCheckin:(BOOL)immediately;
/**
* Returns the checkin preferences currently loaded in memory. The Checkin preferences
* can be either valid or invalid.
*
* @return The checkin preferences loaded in memory.
*/
- (FIRInstanceIDCheckinPreferences *)checkinPreferences;
/**
* Cancels any ongoing checkin fetch, if any.
*/
- (void)stopCheckinRequest;
/**
* Resets the checkin information.
*
* @param handler The callback handler which is invoked when checkin reset is complete,
* with an error if there is any.
*/
- (void)resetCheckinWithHandler:(void (^)(NSError *error))handler;
@end
@@ -0,0 +1,302 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDAuthService.h"
#import "FIRInstanceIDCheckinPreferences+Internal.h"
#import "FIRInstanceIDCheckinPreferences.h"
#import "FIRInstanceIDCheckinPreferences_Private.h"
#import "FIRInstanceIDConstants.h"
#import "FIRInstanceIDDefines.h"
#import "FIRInstanceIDLogger.h"
#import "FIRInstanceIDStore.h"
#import "NSError+FIRInstanceID.h"
// Max time interval between checkin retry in seconds.
static const int64_t kMaxCheckinRetryIntervalInSeconds = 1 << 5;
@interface FIRInstanceIDAuthService ()
// Used to retrieve and cache the checkin info to disk and Keychain.
@property(nonatomic, readwrite, strong) FIRInstanceIDStore *store;
// Used to perform single checkin fetches.
@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinService *checkinService;
// The current checkin info. It will be compared to what is retrieved to determine whether it is
// different than what is in the cache.
@property(nonatomic, readwrite, strong) FIRInstanceIDCheckinPreferences *checkinPreferences;
// This array will track multiple handlers waiting for checkin to be performed. When a checkin
// request completes, all the handlers will be notified.
// Changes to the checkinHandlers array should happen in a thread-safe manner.
@property(nonatomic, readonly, strong)
NSMutableArray<FIRInstanceIDDeviceCheckinCompletion> *checkinHandlers;
// This is set to true if there is a checkin request in-flight.
@property(atomic, readwrite, assign) BOOL isCheckinInProgress;
// This timer is used a perform checkin retries. It is cancellable.
@property(atomic, readwrite, strong) NSTimer *scheduledCheckinTimer;
// The number of times checkin has been retried during a scheduled checkin.
@property(atomic, readwrite, assign) int checkinRetryCount;
@end
@implementation FIRInstanceIDAuthService
- (instancetype)initWithCheckinService:(FIRInstanceIDCheckinService *)checkinService
store:(FIRInstanceIDStore *)store {
self = [super init];
if (self) {
_store = store;
_checkinPreferences = [_store cachedCheckinPreferences];
_checkinService = checkinService;
_checkinHandlers = [NSMutableArray array];
}
return self;
}
- (void)dealloc {
[_scheduledCheckinTimer invalidate];
}
- (instancetype)initWithStore:(FIRInstanceIDStore *)store {
FIRInstanceIDCheckinService *checkinService = [[FIRInstanceIDCheckinService alloc] init];
return [self initWithCheckinService:checkinService store:store];
}
#pragma mark - Schedule Checkin
- (void)scheduleCheckin:(BOOL)immediately {
// Checkin is still valid, so a remote checkin is not required.
if ([self.checkinPreferences hasValidCheckinInfo]) {
return;
}
// Checkin is already scheduled, so this (non-immediate) request can be ignored.
if (!immediately && [self.scheduledCheckinTimer isValid]) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService000,
@"Checkin sync already scheduled. Will not schedule.");
return;
}
if (immediately) {
[self performScheduledCheckin];
} else {
int64_t checkinRetryDuration = [self calculateNextCheckinRetryIntervalInSeconds];
[self startCheckinTimerWithDuration:(NSTimeInterval)checkinRetryDuration];
}
}
- (void)startCheckinTimerWithDuration:(NSTimeInterval)timerDuration {
self.scheduledCheckinTimer =
[NSTimer scheduledTimerWithTimeInterval:timerDuration
target:self
selector:@selector(onScheduledCheckinTimerFired:)
userInfo:nil
repeats:NO];
// Add some tolerance to the timer, to allow iOS to be more flexible with this timer
self.scheduledCheckinTimer.tolerance = 0.5;
}
- (void)clearScheduledCheckinTimer {
[self.scheduledCheckinTimer invalidate];
self.scheduledCheckinTimer = nil;
}
- (void)onScheduledCheckinTimerFired:(NSTimer *)timer {
[self performScheduledCheckin];
}
- (void)performScheduledCheckin {
// No checkin scheduled as of now.
[self clearScheduledCheckinTimer];
// Checkin is still valid, so a remote checkin is not required.
if ([self.checkinPreferences hasValidCheckinInfo]) {
return;
}
FIRInstanceID_WEAKIFY(self);
[self
fetchCheckinInfoWithHandler:^(FIRInstanceIDCheckinPreferences *preferences, NSError *error) {
FIRInstanceID_STRONGIFY(self);
self.checkinRetryCount++;
if (error) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService001, @"Checkin error %@.",
error);
dispatch_async(dispatch_get_main_queue(), ^{
// Schedule another checkin
[self scheduleCheckin:NO];
});
} else {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService002, @"Checkin success.");
}
}];
}
- (int64_t)calculateNextCheckinRetryIntervalInSeconds {
// persistent failures can lead to overflow prevent that.
if (self.checkinRetryCount >= 10) {
return kMaxCheckinRetryIntervalInSeconds;
}
return MIN(1 << self.checkinRetryCount, kMaxCheckinRetryIntervalInSeconds);
}
#pragma mark - Checkin Service
- (BOOL)hasValidCheckinInfo {
return [self.checkinPreferences hasValidCheckinInfo];
}
- (void)fetchCheckinInfoWithHandler:(nonnull FIRInstanceIDDeviceCheckinCompletion)handler {
// Perform any changes to self.checkinHandlers and _isCheckinInProgress in a thread-safe way.
@synchronized(self) {
[self.checkinHandlers addObject:handler];
if (_isCheckinInProgress) {
// Nothing more to do until our checkin request is done
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthServiceCheckinInProgress,
@"Checkin is in progress\n");
return;
}
}
// Checkin is still valid, so a remote checkin is not required.
if ([self.checkinPreferences hasValidCheckinInfo]) {
[self notifyCheckinHandlersWithCheckin:self.checkinPreferences error:nil];
return;
}
@synchronized(self) {
_isCheckinInProgress = YES;
}
[self.checkinService
checkinWithExistingCheckin:self.checkinPreferences
completion:^(FIRInstanceIDCheckinPreferences *checkinPreferences,
NSError *error) {
@synchronized(self) {
self->_isCheckinInProgress = NO;
}
if (error) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService003,
@"Failed to checkin device %@", error);
[self notifyCheckinHandlersWithCheckin:nil error:error];
return;
}
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeAuthService004,
@"Successfully got checkin credentials");
BOOL hasSameCachedPreferences =
[self cachedCheckinMatchesCheckin:checkinPreferences];
checkinPreferences.hasPreCachedAuthCredentials = hasSameCachedPreferences;
// Update to the most recent checkin preferences
self.checkinPreferences = checkinPreferences;
// Save the checkin info to disk
// Keychain might not be accessible, so confirm that checkin preferences can
// be saved
[self.store
saveCheckinPreferences:checkinPreferences
handler:^(NSError *checkinSaveError) {
if (checkinSaveError && !hasSameCachedPreferences) {
// The checkin info was new, but it couldn't be
// written to the Keychain. Delete any stuff that was
// cached in memory. This doesn't delete any
// previously persisted preferences.
FIRInstanceIDLoggerError(
kFIRInstanceIDMessageCodeService004,
@"Unable to save checkin info, resetting "
@"checkin preferences "
"in memory.");
[checkinPreferences reset];
[self
notifyCheckinHandlersWithCheckin:nil
error:
checkinSaveError];
} else {
// The checkin is either new, or it was the same (and
// it couldn't be saved). Either way, report that the
// checkin preferences were received successfully.
[self notifyCheckinHandlersWithCheckin:
checkinPreferences
error:nil];
if (!hasSameCachedPreferences) {
// Checkin is new.
// Notify any listeners that might be waiting for
// checkin to be fetched, such as Firebase
// Messaging (for its MCS connection).
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName:
kFIRInstanceIDCheckinFetchedNotification
object:nil];
});
}
}
}];
}];
}
- (FIRInstanceIDCheckinPreferences *)checkinPreferences {
return _checkinPreferences;
}
- (void)stopCheckinRequest {
[self.checkinService stopFetching];
}
- (void)resetCheckinWithHandler:(void (^)(NSError *error))handler {
[self.store removeCheckinPreferencesWithHandler:^(NSError *error) {
if (!error) {
self.checkinPreferences = nil;
}
if (handler) {
handler(error);
}
}];
}
#pragma mark - Private
/**
* Goes through the current list of checkin handlers and fires them with the same checkin and/or
* error info. The checkin handlers will get cleared after.
*/
- (void)notifyCheckinHandlersWithCheckin:(nullable FIRInstanceIDCheckinPreferences *)checkin
error:(nullable NSError *)error {
@synchronized(self) {
for (FIRInstanceIDDeviceCheckinCompletion handler in self.checkinHandlers) {
handler(checkin, error);
}
[self.checkinHandlers removeAllObjects];
}
}
/**
* Given a |checkin|, it will compare it to the current checkinPreferences to see if the
* deviceID and secretToken are the same.
*/
- (BOOL)cachedCheckinMatchesCheckin:(FIRInstanceIDCheckinPreferences *)checkin {
if (self.checkinPreferences && checkin) {
return ([self.checkinPreferences.deviceID isEqualToString:checkin.deviceID] &&
[self.checkinPreferences.secretToken isEqualToString:checkin.secretToken]);
}
return NO;
}
@end
@@ -0,0 +1,81 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@interface FIRInstanceIDBackupExcludedPlist : NSObject
/**
* Caches the plist contents in memory so we don't hit the disk each time we want
* to query something in the plist. This is loaded lazily i.e. if you write to the
* plist the contents you want to write will be stored here if the write was
* successful. The other case where it is loaded is if you read the plist contents
* by calling `contentAsDictionary`.
*
* In case you write to the plist and then try to read the file using
* `contentAsDictionary` we would just return the cachedPlistContents since it would
* represent the disk contents.
*/
@property(nonatomic, readonly, strong) NSDictionary *cachedPlistContents;
/**
* Init a backup excluded plist file.
*
* @param fileName The filename for the plist file.
* @param subDirectory The subdirectory in Application Support to save the plist.
*
* @return Helper which allows to read write data to a backup excluded plist.
*/
- (instancetype)initWithFileName:(NSString *)fileName subDirectory:(NSString *)subDirectory;
/**
* Write dictionary data to the backup excluded plist file. If the file does not exist
* it would be created before writing to it.
*
* @param dict The data to be written to the plist.
* @param error The error object if any while writing the data.
*
* @return YES if the write was successful else NO.
*/
- (BOOL)writeDictionary:(NSDictionary *)dict error:(NSError **)error;
/**
* Delete the backup excluded plist created with the above filename.
*
* @param error The error object if any while deleting the file.
*
* @return YES If the delete was successful else NO.
*/
- (BOOL)deleteFile:(NSError **)error;
/**
* The contents of the plist file. We also store the contents of the file in-memory.
* If the in-memory contents are valid we return the in-memory contents else we read
* the file from disk.
*
* @return A dictionary object that contains the contents of the plist file if the file
* exists else nil.
*/
- (NSDictionary *)contentAsDictionary;
/**
* Check if the plist exists on the disk or not.
*
* @return YES if the file exists on the disk else NO.
*/
- (BOOL)doesFileExist;
@end
@@ -0,0 +1,202 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDBackupExcludedPlist.h"
#import "FIRInstanceIDLogger.h"
typedef enum : NSUInteger {
FIRInstanceIDPlistDirectoryUnknown,
FIRInstanceIDPlistDirectoryDocuments,
FIRInstanceIDPlistDirectoryApplicationSupport,
} FIRInstanceIDPlistDirectory;
@interface FIRInstanceIDBackupExcludedPlist ()
@property(nonatomic, readwrite, copy) NSString *fileName;
@property(nonatomic, readwrite, copy) NSString *subDirectoryName;
@property(nonatomic, readwrite, assign) BOOL fileInStandardDirectory;
@property(nonatomic, readwrite, strong) NSDictionary *cachedPlistContents;
@end
@implementation FIRInstanceIDBackupExcludedPlist
- (instancetype)initWithFileName:(NSString *)fileName subDirectory:(NSString *)subDirectory {
self = [super init];
if (self) {
_fileName = [fileName copy];
_subDirectoryName = [subDirectory copy];
#if TARGET_OS_IOS
_fileInStandardDirectory = [self moveToApplicationSupportSubDirectory:subDirectory];
#else
// For tvOS and macOS, we never store the content in document folder, so
// the migration is unnecessary.
_fileInStandardDirectory = YES;
#endif
}
return self;
}
- (BOOL)writeDictionary:(NSDictionary *)dict error:(NSError **)error {
NSString *path = [self plistPathInDirectory:[self plistDirectory]];
if (![dict writeToFile:path atomically:YES]) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist000,
@"Failed to write to %@.plist", self.fileName);
return NO;
}
// Successfully wrote contents -- change the in-memory contents
self.cachedPlistContents = [dict copy];
NSURL *URL = [NSURL fileURLWithPath:path];
if (error) {
*error = nil;
}
NSDictionary *preferences = [URL resourceValuesForKeys:@[ NSURLIsExcludedFromBackupKey ]
error:error];
if ([preferences[NSURLIsExcludedFromBackupKey] boolValue]) {
return YES;
}
BOOL success = [URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:error];
if (!success) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist001,
@"Error excluding %@ from backup, %@", [URL lastPathComponent],
error ? *error : @"");
}
return success;
}
- (BOOL)deleteFile:(NSError **)error {
BOOL success = YES;
NSString *path = [self plistPathInDirectory:[self plistDirectory]];
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
success = [[NSFileManager defaultManager] removeItemAtPath:path error:error];
}
// remove the in-memory contents
self.cachedPlistContents = nil;
return success;
}
- (NSDictionary *)contentAsDictionary {
if (!self.cachedPlistContents) {
NSString *path = [self plistPathInDirectory:[self plistDirectory]];
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
self.cachedPlistContents = [[NSDictionary alloc] initWithContentsOfFile:path];
}
}
return self.cachedPlistContents;
}
- (BOOL)moveToApplicationSupportSubDirectory:(NSString *)subDirectoryName {
NSArray *directoryPaths =
NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES);
// This only going to happen inside iOS so it is an applicationSupportDirectory.
NSString *applicationSupportDirPath = directoryPaths.lastObject;
NSArray *components = @[ applicationSupportDirPath, subDirectoryName ];
NSString *subDirectoryPath = [NSString pathWithComponents:components];
BOOL hasSubDirectory;
if (![[NSFileManager defaultManager] fileExistsAtPath:subDirectoryPath
isDirectory:&hasSubDirectory]) {
// Cannot move to non-existent directory
return NO;
}
if ([self doesFileExistInDirectory:FIRInstanceIDPlistDirectoryDocuments]) {
NSString *oldPlistPath = [self plistPathInDirectory:FIRInstanceIDPlistDirectoryDocuments];
NSString *newPlistPath =
[self plistPathInDirectory:FIRInstanceIDPlistDirectoryApplicationSupport];
if ([self doesFileExistInDirectory:FIRInstanceIDPlistDirectoryApplicationSupport]) {
// File exists in both Documents and ApplicationSupport
return NO;
}
NSError *moveError;
if (![[NSFileManager defaultManager] moveItemAtPath:oldPlistPath
toPath:newPlistPath
error:&moveError]) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlist002,
@"Failed to move file %@ from %@ to %@. Error: %@", self.fileName,
oldPlistPath, newPlistPath, moveError);
return NO;
}
}
// We moved the file if it existed, otherwise we didn't need to do anything
return YES;
}
- (BOOL)doesFileExist {
return [self doesFileExistInDirectory:[self plistDirectory]];
}
#pragma mark - Private
- (FIRInstanceIDPlistDirectory)plistDirectory {
if (_fileInStandardDirectory) {
return FIRInstanceIDPlistDirectoryApplicationSupport;
} else {
return FIRInstanceIDPlistDirectoryDocuments;
};
}
- (NSString *)plistPathInDirectory:(FIRInstanceIDPlistDirectory)directory {
return [self pathWithName:self.fileName inDirectory:directory];
}
- (NSString *)pathWithName:(NSString *)plistName
inDirectory:(FIRInstanceIDPlistDirectory)directory {
NSArray *directoryPaths;
NSArray *components = @[];
NSString *plistNameWithExtension = [NSString stringWithFormat:@"%@.plist", plistName];
switch (directory) {
case FIRInstanceIDPlistDirectoryDocuments:
directoryPaths =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
components = @[ directoryPaths.lastObject, plistNameWithExtension ];
break;
case FIRInstanceIDPlistDirectoryApplicationSupport:
directoryPaths =
NSSearchPathForDirectoriesInDomains([self supportedDirectory], NSUserDomainMask, YES);
components = @[ directoryPaths.lastObject, _subDirectoryName, plistNameWithExtension ];
break;
default:
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeBackupExcludedPlistInvalidPlistEnum,
@"Invalid plist directory type: %lu", (unsigned long)directory);
NSAssert(NO, @"Invalid plist directory type: %lu", (unsigned long)directory);
break;
}
return [NSString pathWithComponents:components];
}
- (BOOL)doesFileExistInDirectory:(FIRInstanceIDPlistDirectory)directory {
NSString *path = [self plistPathInDirectory:directory];
return [[NSFileManager defaultManager] fileExistsAtPath:path];
}
- (NSSearchPathDirectory)supportedDirectory {
#if TARGET_OS_TV
return NSCachesDirectory;
#else
return NSApplicationSupportDirectory;
#endif
}
@end
@@ -0,0 +1,64 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDCheckinPreferences.h"
@interface FIRInstanceIDCheckinPreferences (Internal)
/**
* Parse the checkin auth credentials saved in the Keychain to initialize checkin
* preferences.
*
* @param keychainContent The checkin auth credentials saved in the Keychain.
*
* @return A valid checkin preferences object if the checkin auth credentials in the
* keychain can be parsed successfully else nil.
*/
+ (FIRInstanceIDCheckinPreferences *)preferencesFromKeychainContents:(NSString *)keychainContent;
/**
* Default initializer for InstanceID checkin preferences.
*
* @param deviceID The deviceID for the app.
* @param secretToken The secret token the app uses to authenticate with the server.
*
* @return A checkin preferences object with given deviceID and secretToken.
*/
- (instancetype)initWithDeviceID:(NSString *)deviceID secretToken:(NSString *)secretToken;
/**
* Update checkin preferences from the preferences dict persisted as a plist. The dict contains
* all the checkin preferences retrieved from the server except the deviceID and secret which
* are stored in the Keychain.
*
* @param checkinPlistContent The checkin preferences saved in a plist on the disk.
*/
- (void)updateWithCheckinPlistContents:(NSDictionary *)checkinPlistContent;
/**
* Reset the current checkin preferences object.
*/
- (void)reset;
/**
* The string that contains the checkin auth credentials i.e. deviceID and secret. This
* needs to be stored in the Keychain.
*
* @return The checkin auth credential string containing the deviceID and secret.
*/
- (NSString *)checkinKeychainContent;
@end
@@ -0,0 +1,112 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDCheckinPreferences+Internal.h"
#import "FIRInstanceIDCheckinService.h"
#import "FIRInstanceIDUtilities.h"
static NSString *const kCheckinKeychainContentSeparatorString = @"|";
@interface FIRInstanceIDCheckinPreferences ()
@property(nonatomic, readwrite, copy) NSString *deviceID;
@property(nonatomic, readwrite, copy) NSString *secretToken;
@property(nonatomic, readwrite, copy) NSString *digest;
@property(nonatomic, readwrite, copy) NSString *versionInfo;
@property(nonatomic, readwrite, copy) NSString *deviceDataVersion;
@property(nonatomic, readwrite, strong) NSMutableDictionary *gServicesData;
@property(nonatomic, readwrite, assign) int64_t lastCheckinTimestampMillis;
@end
@implementation FIRInstanceIDCheckinPreferences (Internal)
+ (FIRInstanceIDCheckinPreferences *)preferencesFromKeychainContents:(NSString *)keychainContent {
NSString *deviceID = [self checkinDeviceIDFromKeychainContent:keychainContent];
NSString *secret = [self checkinSecretFromKeychainContent:keychainContent];
if ([deviceID length] && [secret length]) {
return [[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:deviceID secretToken:secret];
} else {
return nil;
}
}
- (instancetype)initWithDeviceID:(NSString *)deviceID secretToken:(NSString *)secretToken {
self = [super init];
if (self) {
self.deviceID = [deviceID copy];
self.secretToken = [secretToken copy];
}
return self;
}
- (void)reset {
self.deviceID = nil;
self.secretToken = nil;
self.digest = nil;
self.versionInfo = nil;
self.gServicesData = nil;
self.deviceDataVersion = nil;
self.lastCheckinTimestampMillis = 0;
}
- (void)updateWithCheckinPlistContents:(NSDictionary *)checkinPlistContent {
for (NSString *key in checkinPlistContent) {
if ([kFIRInstanceIDDigestStringKey isEqualToString:key]) {
self.digest = [checkinPlistContent[key] copy];
} else if ([kFIRInstanceIDVersionInfoStringKey isEqualToString:key]) {
self.versionInfo = [checkinPlistContent[key] copy];
} else if ([kFIRInstanceIDLastCheckinTimeKey isEqualToString:key]) {
self.lastCheckinTimestampMillis = [checkinPlistContent[key] longLongValue];
} else if ([kFIRInstanceIDGServicesDictionaryKey isEqualToString:key]) {
self.gServicesData = [checkinPlistContent[key] mutableCopy];
} else if ([kFIRInstanceIDDeviceDataVersionKey isEqualToString:key]) {
self.deviceDataVersion = [checkinPlistContent[key] copy];
}
// Otherwise we have some keys we don't care about
}
}
- (NSString *)checkinKeychainContent {
if ([self.deviceID length] && [self.secretToken length]) {
return [NSString stringWithFormat:@"%@%@%@", self.deviceID,
kCheckinKeychainContentSeparatorString, self.secretToken];
} else {
return nil;
}
}
+ (NSString *)checkinDeviceIDFromKeychainContent:(NSString *)keychainContent {
return [self checkinKeychainContent:keychainContent forIndex:0];
}
+ (NSString *)checkinSecretFromKeychainContent:(NSString *)keychainContent {
return [self checkinKeychainContent:keychainContent forIndex:1];
}
+ (NSString *)checkinKeychainContent:(NSString *)keychainContent forIndex:(int)index {
NSArray *keychainComponents =
[keychainContent componentsSeparatedByString:kCheckinKeychainContentSeparatorString];
if (index >= 0 && index < 2 && [keychainComponents count] == 2) {
return keychainComponents[index];
} else {
return nil;
}
}
@end
@@ -0,0 +1,62 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/**
* The preferences InstanceID loads from checkin server. The deviceID and secret that checkin
* provides is used to authenticate all future requests to the server. Besides the deviceID
* and secret the other information that checkin provides is stored in a plist on the device.
* The deviceID and secret are persisted in the device keychain.
*/
@interface FIRInstanceIDCheckinPreferences : NSObject
/**
* DeviceID and secretToken are the checkin auth credentials and are stored in the Keychain.
*/
@property(nonatomic, readonly, copy) NSString *deviceID;
@property(nonatomic, readonly, copy) NSString *secretToken;
/**
* All the other checkin preferences other than deviceID and secret are stored in a plist.
*/
@property(nonatomic, readonly, copy) NSString *deviceDataVersion;
@property(nonatomic, readonly, copy) NSString *digest;
@property(nonatomic, readonly, copy) NSString *versionInfo;
@property(nonatomic, readonly, assign) int64_t lastCheckinTimestampMillis;
/**
* The content retrieved from checkin server that should be persisted in a plist. This
* doesn't contain the deviceID and secret which are stored in the Keychain since they
* should be more private.
*
* @return The checkin preferences that should be persisted in a plist.
*/
- (NSDictionary *)checkinPlistContents;
/**
* Return whether checkin info exists, valid or not.
*/
- (BOOL)hasCheckinInfo;
/**
* Verify if checkin preferences are valid or not.
*
* @return YES if valid checkin preferences else NO.
*/
- (BOOL)hasValidCheckinInfo;
@end
@@ -0,0 +1,95 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDCheckinPreferences.h"
#import <GoogleUtilities/GULUserDefaults.h>
#import "FIRInstanceIDCheckinService.h"
#import "FIRInstanceIDUtilities.h"
const NSTimeInterval kFIRInstanceIDDefaultCheckinInterval = 7 * 24 * 60 * 60; // 7 days.
@interface FIRInstanceIDCheckinPreferences ()
@property(nonatomic, readwrite, copy) NSString *deviceID;
@property(nonatomic, readwrite, copy) NSString *secretToken;
@property(nonatomic, readwrite, copy) NSString *digest;
@property(nonatomic, readwrite, copy) NSString *versionInfo;
@property(nonatomic, readwrite, copy) NSString *deviceDataVersion;
@property(nonatomic, readwrite, strong) NSMutableDictionary *gServicesData;
@property(nonatomic, readwrite, assign) int64_t lastCheckinTimestampMillis;
// This flag indicates that we have already saved the above deviceID and secret
// to our keychain and hence we don't need to save again. This is helpful since
// on checkin refresh we can avoid writing to the Keychain which can sometimes
// be very buggy. For info check this https://forums.developer.apple.com/thread/4743
@property(nonatomic, readwrite, assign) BOOL hasPreCachedAuthCredentials;
@end
@implementation FIRInstanceIDCheckinPreferences
- (NSDictionary *)checkinPlistContents {
NSMutableDictionary *checkinPlistContents = [NSMutableDictionary dictionary];
checkinPlistContents[kFIRInstanceIDDigestStringKey] = self.digest ?: @"";
checkinPlistContents[kFIRInstanceIDVersionInfoStringKey] = self.versionInfo ?: @"";
checkinPlistContents[kFIRInstanceIDDeviceDataVersionKey] = self.deviceDataVersion ?: @"";
checkinPlistContents[kFIRInstanceIDLastCheckinTimeKey] = @(self.lastCheckinTimestampMillis);
checkinPlistContents[kFIRInstanceIDGServicesDictionaryKey] =
[self.gServicesData count] ? self.gServicesData : @{};
return checkinPlistContents;
}
- (BOOL)hasCheckinInfo {
return (self.deviceID.length && self.secretToken.length);
}
- (BOOL)hasValidCheckinInfo {
int64_t currentTimestampInMillis = FIRInstanceIDCurrentTimestampInMilliseconds();
int64_t timeSinceLastCheckinInMillis = currentTimestampInMillis - self.lastCheckinTimestampMillis;
BOOL hasCheckinInfo = [self hasCheckinInfo];
NSString *lastLocale =
[[GULUserDefaults standardUserDefaults] stringForKey:kFIRInstanceIDUserDefaultsKeyLocale];
// If it's app's first time open and checkin is already fetched and no locale information is
// stored, then checkin info is valid. We should not checkin again because locale is considered
// "changed".
if (hasCheckinInfo && !lastLocale) {
NSString *currentLocale = FIRInstanceIDCurrentLocale();
[[GULUserDefaults standardUserDefaults] setObject:currentLocale
forKey:kFIRInstanceIDUserDefaultsKeyLocale];
return YES;
}
// If locale has changed, checkin info is no longer valid.
// Also update locale information if changed. (Only do it here not in token refresh)
if (FIRInstanceIDHasLocaleChanged()) {
NSString *currentLocale = FIRInstanceIDCurrentLocale();
[[GULUserDefaults standardUserDefaults] setObject:currentLocale
forKey:kFIRInstanceIDUserDefaultsKeyLocale];
return NO;
}
return (hasCheckinInfo &&
(timeSinceLastCheckinInMillis / 1000.0 < kFIRInstanceIDDefaultCheckinInterval));
}
- (void)setHasPreCachedAuthCredentials:(BOOL)hasPreCachedAuthCredentials {
_hasPreCachedAuthCredentials = hasPreCachedAuthCredentials;
}
@end
@@ -0,0 +1,27 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDCheckinPreferences.h"
/** Checkin refresh interval. **/
FOUNDATION_EXPORT const NSTimeInterval kFIRInstanceIDDefaultCheckinInterval;
@interface FIRInstanceIDCheckinPreferences ()
- (BOOL)hasPreCachedAuthCredentials;
- (void)setHasPreCachedAuthCredentials:(BOOL)hasPreCachedAuthCredentials;
@end
@@ -0,0 +1,81 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FIRInstanceIDUtilities.h"
NS_ASSUME_NONNULL_BEGIN
// keys in Checkin preferences
FOUNDATION_EXPORT NSString *const kFIRInstanceIDDeviceAuthIdKey;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDSecretTokenKey;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDDigestStringKey;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDLastCheckinTimeKey;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDVersionInfoStringKey;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDGServicesDictionaryKey;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDDeviceDataVersionKey;
@class FIRInstanceIDCheckinPreferences;
/**
* @related FIRInstanceIDCheckinService
*
* The completion handler invoked once the fetch from Checkin server finishes.
* For successful fetches we returned checkin information by the checkin service
* and `nil` error, else we return the appropriate error object as reported by the
* Checkin Service.
*
* @param checkinPreferences The checkin preferences as fetched from the server.
* @param error The error object which fetching GServices data.
*/
typedef void (^FIRInstanceIDDeviceCheckinCompletion)(
FIRInstanceIDCheckinPreferences *_Nullable checkinPreferences, NSError *_Nullable error);
/**
* Register the device with Checkin Service and get back the `authID`, `secret
* token` etc. for the client. Checkin results are cached in the
* `FIRInstanceIDCache` and periodically refreshed to prevent them from being stale.
* Each client needs to register with checkin before registering with InstanceID.
*/
@interface FIRInstanceIDCheckinService : NSObject
/**
* Execute a device checkin request to obtain an deviceID, secret token,
* gService data.
*
* @param existingCheckin An existing checkin preference object, if available.
* @param completion Completion hander called on success or failure of device checkin.
*/
- (void)checkinWithExistingCheckin:(nullable FIRInstanceIDCheckinPreferences *)existingCheckin
completion:(FIRInstanceIDDeviceCheckinCompletion)completion;
/**
* This would stop any request that the service made to the checkin backend and also
* release any callback handlers that it holds.
*/
- (void)stopFetching;
/**
* Set test block for mock testing network requests.
*
* @param block The block to invoke as a mock response from the network.
*/
+ (void)setCheckinTestBlock:(nullable FIRInstanceIDURLRequestTestBlock)block;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,244 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDCheckinService.h"
#import "FIRInstanceIDCheckinPreferences+Internal.h"
#import "FIRInstanceIDCheckinPreferences_Private.h"
#import "FIRInstanceIDDefines.h"
#import "FIRInstanceIDLogger.h"
#import "FIRInstanceIDStore.h"
#import "FIRInstanceIDUtilities.h"
#import "NSError+FIRInstanceID.h"
static NSString *const kDeviceCheckinURL = @"https://device-provisioning.googleapis.com/checkin";
// keys in Checkin preferences
NSString *const kFIRInstanceIDDeviceAuthIdKey = @"GMSInstanceIDDeviceAuthIdKey";
NSString *const kFIRInstanceIDSecretTokenKey = @"GMSInstanceIDSecretTokenKey";
NSString *const kFIRInstanceIDDigestStringKey = @"GMSInstanceIDDigestKey";
NSString *const kFIRInstanceIDLastCheckinTimeKey = @"GMSInstanceIDLastCheckinTimestampKey";
NSString *const kFIRInstanceIDVersionInfoStringKey = @"GMSInstanceIDVersionInfo";
NSString *const kFIRInstanceIDGServicesDictionaryKey = @"GMSInstanceIDGServicesData";
NSString *const kFIRInstanceIDDeviceDataVersionKey = @"GMSInstanceIDDeviceDataVersion";
static NSUInteger const kCheckinType = 2; // DeviceType IOS in l/w/a/_checkin.proto
static NSUInteger const kCheckinVersion = 2;
static NSUInteger const kFragment = 0;
static FIRInstanceIDURLRequestTestBlock testBlock;
@interface FIRInstanceIDCheckinService ()
@property(nonatomic, readwrite, strong) NSURLSession *session;
@end
@implementation FIRInstanceIDCheckinService
;
- (instancetype)init {
self = [super init];
if (self) {
// Create an URLSession once, even though checkin should happen about once a day
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.timeoutIntervalForResource = 60.0f; // 1 minute
config.allowsCellularAccess = YES;
_session = [NSURLSession sessionWithConfiguration:config];
_session.sessionDescription = @"com.google.iid-checkin";
}
return self;
}
- (void)dealloc {
testBlock = nil;
[self.session invalidateAndCancel];
}
- (void)checkinWithExistingCheckin:(FIRInstanceIDCheckinPreferences *)existingCheckin
completion:(FIRInstanceIDDeviceCheckinCompletion)completion {
if (self.session == nil) {
FIRInstanceIDLoggerError(kFIRInstanceIDInvalidNetworkSession,
@"Inconsistent state: NSURLSession has been invalidated");
NSError *error =
[NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn];
if (completion) {
completion(nil, error);
}
return;
}
NSURL *url = [NSURL URLWithString:kDeviceCheckinURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setValue:@"application/json" forHTTPHeaderField:@"content-type"];
NSDictionary *checkinParameters = [self checkinParametersWithExistingCheckin:existingCheckin];
NSData *checkinData = [NSJSONSerialization dataWithJSONObject:checkinParameters
options:0
error:nil];
request.HTTPMethod = @"POST";
request.HTTPBody = checkinData;
void (^handler)(NSData *, NSURLResponse *, NSError *) =
^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService000,
@"Device checkin HTTP fetch error. Error Code: %ld",
(long)error.code);
if (completion) {
completion(nil, error);
}
return;
}
NSError *serializationError;
NSDictionary *dataResponse = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&serializationError];
if (serializationError) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService001,
@"Error serializing json object. Error Code: %ld",
_FIRInstanceID_L(serializationError.code));
if (completion) {
completion(nil, serializationError);
}
return;
}
NSString *deviceAuthID = [dataResponse[@"android_id"] stringValue];
NSString *secretToken = [dataResponse[@"security_token"] stringValue];
if ([deviceAuthID length] == 0) {
NSError *error =
[NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidRequest];
if (completion) {
completion(nil, error);
}
return;
}
int64_t lastCheckinTimestampMillis = [dataResponse[@"time_msec"] longLongValue];
int64_t currentTimestampMillis = FIRInstanceIDCurrentTimestampInMilliseconds();
// Somehow the server clock gets out of sync with the device clock.
// Reset the last checkin timestamp in case this happens.
if (lastCheckinTimestampMillis > currentTimestampMillis) {
FIRInstanceIDLoggerDebug(
kFIRInstanceIDMessageCodeService002, @"Invalid last checkin timestamp %@ in future.",
[NSDate dateWithTimeIntervalSince1970:lastCheckinTimestampMillis / 1000.0]);
lastCheckinTimestampMillis = currentTimestampMillis;
}
NSString *deviceDataVersionInfo = dataResponse[@"device_data_version_info"] ?: @"";
NSString *digest = dataResponse[@"digest"] ?: @"";
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService003,
@"Checkin successful with authId: %@, "
@"digest: %@, "
@"lastCheckinTimestamp: %lld",
deviceAuthID, digest, lastCheckinTimestampMillis);
NSString *versionInfo = dataResponse[@"version_info"] ?: @"";
NSMutableDictionary *gservicesData = [NSMutableDictionary dictionary];
// Read gServices data.
NSArray *flatSettings = dataResponse[@"setting"];
for (NSDictionary *dict in flatSettings) {
if (dict[@"name"] && dict[@"value"]) {
gservicesData[dict[@"name"]] = dict[@"value"];
} else {
FIRInstanceIDLoggerDebug(kFIRInstanceIDInvalidSettingResponse,
@"Invalid setting in checkin response: (%@: %@)",
dict[@"name"], dict[@"value"]);
}
}
FIRInstanceIDCheckinPreferences *checkinPreferences =
[[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:deviceAuthID
secretToken:secretToken];
NSDictionary *preferences = @{
kFIRInstanceIDDigestStringKey : digest,
kFIRInstanceIDVersionInfoStringKey : versionInfo,
kFIRInstanceIDLastCheckinTimeKey : @(lastCheckinTimestampMillis),
kFIRInstanceIDGServicesDictionaryKey : gservicesData,
kFIRInstanceIDDeviceDataVersionKey : deviceDataVersionInfo,
};
[checkinPreferences updateWithCheckinPlistContents:preferences];
if (completion) {
completion(checkinPreferences, nil);
}
};
// Test block
if (testBlock) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService005,
@"Test block set, will not hit the server");
testBlock(request, handler);
return;
}
NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:handler];
[task resume];
}
- (void)stopFetching {
[self.session invalidateAndCancel];
// The session cannot be reused after invalidation. Dispose it to prevent accident reusing.
self.session = nil;
}
#pragma mark - Private
- (NSDictionary *)checkinParametersWithExistingCheckin:
(nullable FIRInstanceIDCheckinPreferences *)checkinPreferences {
NSString *deviceModel = FIRInstanceIDDeviceModel();
NSString *systemVersion = FIRInstanceIDOperatingSystemVersion();
NSString *osVersion = [NSString stringWithFormat:@"IOS_%@", systemVersion];
// Get locale from GCM if GCM exists else use system API.
NSString *locale = FIRInstanceIDCurrentLocale();
NSInteger userNumber = 0; // Multi Profile may change this.
NSInteger userSerialNumber = 0; // Multi Profile may change this
uint32_t loggingID = arc4random();
NSString *timeZone = [NSTimeZone localTimeZone].name;
int64_t lastCheckingTimestampMillis = checkinPreferences.lastCheckinTimestampMillis;
NSDictionary *checkinParameters = @{
@"checkin" : @{
@"iosbuild" : @{@"model" : deviceModel, @"os_version" : osVersion},
@"type" : @(kCheckinType),
@"user_number" : @(userNumber),
@"last_checkin_msec" : @(lastCheckingTimestampMillis),
},
@"fragment" : @(kFragment),
@"logging_id" : @(loggingID),
@"locale" : locale,
@"version" : @(kCheckinVersion),
@"digest" : checkinPreferences.digest ?: @"",
@"time_zone" : timeZone,
@"user_serial_number" : @(userSerialNumber),
@"id" : @([checkinPreferences.deviceID longLongValue]),
@"security_token" : @([checkinPreferences.secretToken longLongValue]),
};
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeService006, @"Checkin parameters: %@",
checkinParameters);
return checkinParameters;
}
+ (void)setCheckinTestBlock:(FIRInstanceIDURLRequestTestBlock)block {
testBlock = [block copy];
}
@end
@@ -0,0 +1,108 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRInstanceIDAuthKeychain;
@class FIRInstanceIDBackupExcludedPlist;
@class FIRInstanceIDCheckinPreferences;
// These values exposed for testing
extern NSString *const kFIRInstanceIDCheckinKeychainService;
extern NSString *const kFIRInstanceIDLegacyCheckinKeychainAccount;
extern NSString *const kFIRInstanceIDLegacyCheckinKeychainService;
/**
* Checkin preferences backing store.
*/
@interface FIRInstanceIDCheckinStore : NSObject
/**
* Designated Initializer. Initialize a checkin store with the given backup excluded
* plist filename.
*
* @param checkinFilename The backup excluded plist filename to persist checkin
* preferences.
*
* @param subDirectoryName Sub-directory in standard directory where we write
* InstanceID plist.
*
* @return Store to persist checkin preferences.
*/
- (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename
subDirectoryName:(NSString *)subDirectoryName;
/**
* Initialize a checkin store with the given backup excluded plist and keychain.
*
* @param plist The backup excluded plist to persist checkin preferences.
* @param keychain The keychain used to persist checkin auth preferences.
*
* @return Store to persist checkin preferences.
*/
- (instancetype)initWithCheckinPlist:(FIRInstanceIDBackupExcludedPlist *)plist
keychain:(FIRInstanceIDAuthKeychain *)keychain;
/**
* Checks whether the backup excluded checkin preferences are present on the disk or not.
*
* @return YES if the backup excluded checkin plist exists on the disks else NO.
*/
- (BOOL)hasCheckinPlist;
#pragma mark - Save
/**
* Save the checkin preferences to backing store.
*
* @param preferences Checkin preferences to save.
* @param handler The callback handler which is invoked when the operation is complete,
* with an error if there is any.
*/
- (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
handler:(void (^)(NSError *error))handler;
#pragma mark - Delete
/**
* Remove the cached checkin preferences.
*
* @param handler The callback handler which is invoked when the operation is complete,
* with an error if there is any.
*/
- (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler;
#pragma mark - Get
/**
* Get the cached device secret. If we cannot access it for some reason we
* return the appropriate error object.
*
* @return The cached checkin preferences if present else nil.
*/
- (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences;
/**
* Migrate the checkin item from old service/account to the new one.
* The new account is dynamic as it uses bundle ID.
* This is to ensure checkin is not shared across apps, but still the same
* if app has used GCM before.
* This call should only happen once.
*
*/
- (void)migrateCheckinItemIfNeeded;
@end
@@ -0,0 +1,238 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDCheckinStore.h"
#import "FIRInstanceIDAuthKeyChain.h"
#import "FIRInstanceIDBackupExcludedPlist.h"
#import "FIRInstanceIDCheckinPreferences+Internal.h"
#import "FIRInstanceIDCheckinPreferences_Private.h"
#import "FIRInstanceIDCheckinService.h"
#import "FIRInstanceIDLogger.h"
#import "FIRInstanceIDUtilities.h"
#import "FIRInstanceIDVersionUtilities.h"
#import "NSError+FIRInstanceID.h"
static NSString *const kFIRInstanceIDCheckinKeychainGeneric = @"com.google.iid";
NSString *const kFIRInstanceIDCheckinKeychainService = @"com.google.iid.checkin";
NSString *const kFIRInstanceIDLegacyCheckinKeychainAccount = @"com.google.iid.checkin-account";
NSString *const kFIRInstanceIDLegacyCheckinKeychainService = @"com.google.iid.checkin-service";
// Checkin plist used to have the deviceID and secret stored in them and that's why they
// had 6 items in it. Since the deviceID and secret have been moved to the keychain
// there would only be 4 items.
static const NSInteger kOldCheckinPlistCount = 6;
@interface FIRInstanceIDCheckinStore ()
@property(nonatomic, readwrite, strong) FIRInstanceIDBackupExcludedPlist *plist;
@property(nonatomic, readwrite, strong) FIRInstanceIDAuthKeychain *keychain;
// Checkin will store items under
// Keychain account: <app bundle id>,
// Keychain service: |kFIRInstanceIDCheckinKeychainService|
@property(nonatomic, readonly) NSString *bundleIdentifierForKeychainAccount;
@end
@implementation FIRInstanceIDCheckinStore
- (instancetype)initWithCheckinPlistFileName:(NSString *)checkinFilename
subDirectoryName:(NSString *)subDirectoryName {
FIRInstanceIDBackupExcludedPlist *plist =
[[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:checkinFilename
subDirectory:subDirectoryName];
FIRInstanceIDAuthKeychain *keychain =
[[FIRInstanceIDAuthKeychain alloc] initWithIdentifier:kFIRInstanceIDCheckinKeychainGeneric];
return [self initWithCheckinPlist:plist keychain:keychain];
}
- (instancetype)initWithCheckinPlist:(FIRInstanceIDBackupExcludedPlist *)plist
keychain:(FIRInstanceIDAuthKeychain *)keychain {
self = [super init];
if (self) {
_plist = plist;
_keychain = keychain;
}
return self;
}
- (BOOL)hasCheckinPlist {
return [self.plist doesFileExist];
}
- (NSString *)bundleIdentifierForKeychainAccount {
static NSString *bundleIdentifier;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
bundleIdentifier = FIRInstanceIDAppIdentifier();
});
return bundleIdentifier;
}
- (void)saveCheckinPreferences:(FIRInstanceIDCheckinPreferences *)preferences
handler:(void (^)(NSError *error))handler {
NSDictionary *checkinPlistContents = [preferences checkinPlistContents];
NSString *checkinKeychainContent = [preferences checkinKeychainContent];
if (![checkinKeychainContent length]) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore000,
@"Failed to get checkin keychain content from memory.");
if (handler) {
handler([NSError
errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]);
}
return;
}
if (![checkinPlistContents count]) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore001,
@"Failed to get checkin plist contents from memory.");
if (handler) {
handler([NSError
errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeRegistrarFailedToCheckIn]);
}
return;
}
// Save all other checkin preferences in a plist
NSError *error;
if (![self.plist writeDictionary:checkinPlistContents error:&error]) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStore003,
@"Failed to save checkin plist contents."
@"Will delete auth credentials");
[self.keychain removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
account:self.bundleIdentifierForKeychainAccount
handler:nil];
if (handler) {
handler(error);
}
return;
}
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistSaved,
@"Checkin plist file is saved");
// Save the deviceID and secret in the Keychain
if (!preferences.hasPreCachedAuthCredentials) {
NSData *data = [checkinKeychainContent dataUsingEncoding:NSUTF8StringEncoding];
[self.keychain setData:data
forService:kFIRInstanceIDCheckinKeychainService
accessibility:nil
account:self.bundleIdentifierForKeychainAccount
handler:^(NSError *error) {
if (error) {
if (handler) {
handler(error);
}
return;
}
if (handler) {
handler(nil);
}
}];
} else {
handler(nil);
}
}
- (void)removeCheckinPreferencesWithHandler:(void (^)(NSError *error))handler {
// Delete the checkin preferences plist first to avoid delay.
NSError *deletePlistError;
if (![self.plist deleteFile:&deletePlistError]) {
handler(deletePlistError);
return;
}
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeCheckinStoreCheckinPlistDeleted,
@"Deleted checkin plist file.");
// Remove deviceID and secret from Keychain
[self.keychain
removeItemsMatchingService:kFIRInstanceIDCheckinKeychainService
account:self.bundleIdentifierForKeychainAccount
handler:^(NSError *error) {
// Try to remove from old location as well because migration
// is no longer needed. Consider this is either a fresh install
// or an identity wipe.
[self.keychain
removeItemsMatchingService:kFIRInstanceIDLegacyCheckinKeychainService
account:kFIRInstanceIDLegacyCheckinKeychainAccount
handler:nil];
handler(error);
}];
}
- (FIRInstanceIDCheckinPreferences *)cachedCheckinPreferences {
// Query the keychain for deviceID and secret
NSData *item = [self.keychain dataForService:kFIRInstanceIDCheckinKeychainService
account:self.bundleIdentifierForKeychainAccount];
// Check info found in keychain
NSString *checkinKeychainContent = [[NSString alloc] initWithData:item
encoding:NSUTF8StringEncoding];
FIRInstanceIDCheckinPreferences *checkinPreferences =
[FIRInstanceIDCheckinPreferences preferencesFromKeychainContents:checkinKeychainContent];
NSDictionary *checkinPlistContents = [self.plist contentAsDictionary];
NSString *plistDeviceAuthID = checkinPlistContents[kFIRInstanceIDDeviceAuthIdKey];
NSString *plistSecretToken = checkinPlistContents[kFIRInstanceIDSecretTokenKey];
// If deviceID and secret not found in the keychain verify that we don't have them in the
// checkin preferences plist.
if (![checkinPreferences.deviceID length] && ![checkinPreferences.secretToken length]) {
if ([plistDeviceAuthID length] && [plistSecretToken length]) {
// Couldn't find checkin credentials in keychain but found them in the plist.
checkinPreferences =
[[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:plistDeviceAuthID
secretToken:plistSecretToken];
} else {
// Couldn't find checkin credentials in keychain nor plist
return nil;
}
} else if (kOldCheckinPlistCount == checkinPlistContents.count) {
// same check as above but just to be extra sure that we cover all upgrade cases properly.
// TODO(chliangGoogle): Remove this case, after verifying it's not needed
if ([plistDeviceAuthID length] && [plistSecretToken length]) {
checkinPreferences =
[[FIRInstanceIDCheckinPreferences alloc] initWithDeviceID:plistDeviceAuthID
secretToken:plistSecretToken];
}
}
[checkinPreferences updateWithCheckinPlistContents:checkinPlistContents];
return checkinPreferences;
}
- (void)migrateCheckinItemIfNeeded {
// Check for checkin in the old location, using the legacy keys
// Query the keychain for deviceID and secret
NSData *dataInOldLocation =
[self.keychain dataForService:kFIRInstanceIDLegacyCheckinKeychainService
account:kFIRInstanceIDLegacyCheckinKeychainAccount];
if (dataInOldLocation) {
// Save to new location
[self.keychain setData:dataInOldLocation
forService:kFIRInstanceIDCheckinKeychainService
accessibility:NULL
account:self.bundleIdentifierForKeychainAccount
handler:nil];
// Remove from old location
[self.keychain removeItemsMatchingService:kFIRInstanceIDLegacyCheckinKeychainService
account:kFIRInstanceIDLegacyCheckinKeychainAccount
handler:nil];
}
}
@end
@@ -0,0 +1,31 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
* A generic class to combine several handler blocks into a single block in a thread-safe manner
*/
@interface FIRInstanceIDCombinedHandler<ResultType> : NSObject
- (void)addHandler:(void (^)(ResultType _Nullable result, NSError* _Nullable error))handler;
- (void (^)(ResultType _Nullable result, NSError* _Nullable error))combinedHandler;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,64 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDCombinedHandler.h"
NS_ASSUME_NONNULL_BEGIN
typedef void (^FIRInstanseIDHandler)(id _Nullable result, NSError *_Nullable error);
@interface FIRInstanceIDCombinedHandler <ResultType>()
@property(atomic, readonly, strong) NSMutableArray<FIRInstanseIDHandler> *handlers;
@end
NS_ASSUME_NONNULL_END
@implementation FIRInstanceIDCombinedHandler
- (instancetype)init {
self = [super init];
if (self) {
_handlers = [NSMutableArray array];
}
return self;
}
- (void)addHandler:(FIRInstanseIDHandler)handler {
if (!handler) {
return;
}
@synchronized(self) {
[self.handlers addObject:handler];
}
}
- (FIRInstanseIDHandler)combinedHandler {
FIRInstanseIDHandler combinedHandler = nil;
@synchronized(self) {
NSArray<FIRInstanseIDHandler> *handlers = [self.handlers copy];
combinedHandler = ^(id result, NSError *error) {
for (FIRInstanseIDHandler handler in handlers) {
handler(result, error);
}
};
}
return combinedHandler;
}
@end
@@ -0,0 +1,63 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#pragma mark - Commands
/**
* Value included in a structured response or GCM message from IID, indicating
* an identity reset.
*/
FOUNDATION_EXPORT NSString *const kFIRInstanceID_CMD_RST;
#pragma mark - Notifications
/// Notification used to deliver GCM messages for InstanceID.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDCheckinFetchedNotification;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDAPNSTokenNotification;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDDefaultGCMTokenNotification;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDDefaultGCMTokenFailNotification;
FOUNDATION_EXPORT NSString *const kFIRInstanceIDIdentityInvalidatedNotification;
#pragma mark - Miscellaneous
/// The scope used to save the IID "*" scope token. This is used for saving the
/// IID auth token that we receive from the server. This feature was never
/// implemented on the server side.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDAllScopeIdentifier;
/// The scope used to save the IID "*" scope token.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDDefaultTokenScope;
/// Subdirectory in search path directory to store InstanceID preferences.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDSubDirectoryName;
/// The key for APNS token in options dictionary.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenOptionsAPNSKey;
/// The key for APNS token environment type in options dictionary.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenOptionsAPNSIsSandboxKey;
/// The key for GMP AppID sent in registration requests.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDTokenOptionsFirebaseAppIDKey;
/// The key to enable auto-register by swizzling AppDelegate's methods.
FOUNDATION_EXPORT NSString *const kFIRInstanceIDAppDelegateProxyEnabledInfoPlistKey;
/// Error code for missing entitlements in Keychain. iOS Keychain error
/// https://forums.developer.apple.com/thread/4743
FOUNDATION_EXPORT const int kFIRInstanceIDSecMissingEntitlementErrorCode;
@@ -0,0 +1,46 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDConstants.h"
// Commands
NSString *const kFIRInstanceID_CMD_RST = @"RST";
// NOTIFICATIONS
NSString *const kFIRInstanceIDCheckinFetchedNotification = @"com.google.gcm.notif-checkin-fetched";
NSString *const kFIRInstanceIDAPNSTokenNotification = @"com.firebase.iid.notif.apns-token";
NSString *const kFIRInstanceIDDefaultGCMTokenNotification = @"com.firebase.iid.notif.fcm-token";
NSString *const kFIRInstanceIDDefaultGCMTokenFailNotification =
@"com.firebase.iid.notif.fcm-token-fail";
NSString *const kFIRInstanceIDIdentityInvalidatedNotification = @"com.google.iid.identity-invalid";
// Miscellaneous
NSString *const kFIRInstanceIDAllScopeIdentifier = @"iid-all";
NSString *const kFIRInstanceIDDefaultTokenScope = @"*";
NSString *const kFIRInstanceIDSubDirectoryName = @"Google/FirebaseInstanceID";
// Registration Options
NSString *const kFIRInstanceIDTokenOptionsAPNSKey = @"apns_token";
NSString *const kFIRInstanceIDTokenOptionsAPNSIsSandboxKey = @"apns_sandbox";
NSString *const kFIRInstanceIDTokenOptionsFirebaseAppIDKey = @"gmp_app_id";
NSString *const kFIRInstanceIDAppDelegateProxyEnabledInfoPlistKey =
@"FirebaseAppDelegateProxyEnabled";
// iOS Keychain error https://forums.developer.apple.com/thread/4743
// An undocumented error code hence need to be redeclared.
const int kFIRInstanceIDSecMissingEntitlementErrorCode = -34018;
@@ -0,0 +1,47 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FIRInstanceIDLib_FIRInstanceIDDefines_h
#define FIRInstanceIDLib_FIRInstanceIDDefines_h
#define _FIRInstanceID_VERBOSE_LOGGING 1
// Verbose Logging
#if (_FIRInstanceID_VERBOSE_LOGGING)
#define FIRInstanceID_DEV_VERBOSE_LOG(...) NSLog(__VA_ARGS__)
#else
#define FIRInstanceID_DEV_VERBOSE_LOG(...) \
do { \
} while (0)
#endif // VERBOSE_LOGGING
// WEAKIFY & STRONGIFY
// Helper macro.
#define _FIRInstanceID_WEAKNAME(VAR) VAR##_weak_
#define FIRInstanceID_WEAKIFY(VAR) __weak __typeof__(VAR) _FIRInstanceID_WEAKNAME(VAR) = (VAR);
#define FIRInstanceID_STRONGIFY(VAR) \
_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wshadow\"") \
__strong __typeof__(VAR) VAR = _FIRInstanceID_WEAKNAME(VAR); \
_Pragma("clang diagnostic pop")
// Type Conversions (used for NSInteger etc)
#ifndef _FIRInstanceID_L
#define _FIRInstanceID_L(v) (long)(v)
#endif
#endif
@@ -0,0 +1,78 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@interface FIRInstanceIDKeyPair : NSObject
- (instancetype)init __attribute__((
unavailable("Use -initWithPrivateKey:publicKey:publicKeyData:privateKeyData: instead.")));
;
/**
* Initialize a new 2048 bit RSA keypair. This also stores the keypair in the Keychain
* Preferences.
*
* @param publicKey The publicKey stored in Keychain.
* @param privateKey The privateKey stored in Keychain.
* @param publicKeyData The publicKey in NSData format.
* @param privateKeyData The privateKey in NSData format.
*
* @return A new KeyPair instance with the generated public and private key.
*/
- (instancetype)initWithPrivateKey:(SecKeyRef)privateKey
publicKey:(SecKeyRef)publicKey
publicKeyData:(NSData *)publicKeyData
privateKeyData:(NSData *)privateKeyData NS_DESIGNATED_INITIALIZER;
/**
* The public key in the RSA 20148 bit generated KeyPair.
*
* @return The 2048 bit RSA KeyPair's public key.
*/
@property(nonatomic, readonly, strong) NSData *publicKeyData;
/**
* The private key in the RSA 20148 bit generated KeyPair.
*
* @return The 2048 bit RSA KeyPair's private key.
*/
@property(nonatomic, readonly, strong) NSData *privateKeyData;
#pragma mark - Info
/**
* Checks if the private and public keyPair are valid or not.
*
* @return YES if keypair is valid else NO.
*/
- (BOOL)isValid;
/**
* The public key in the RSA 2048 bit generated KeyPair.
*
* @return The 2048 bit RSA KeyPair's public key.
*/
- (SecKeyRef)publicKey;
/**
* The private key in the RSA 2048 bit generated KeyPair.
*
* @return The 2048 bit RSA KeyPair's private key.
*/
- (SecKeyRef)privateKey;
@end
@@ -0,0 +1,73 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDKeyPair.h"
#import <Security/Security.h>
#import "FIRInstanceIDKeyPairUtilities.h"
#import "FIRInstanceIDKeychain.h"
#import "FIRInstanceIDLogger.h"
#import "NSError+FIRInstanceID.h"
@interface FIRInstanceIDKeyPair () {
SecKeyRef _privateKey;
SecKeyRef _publicKey;
}
@property(nonatomic, readwrite, strong) NSData *publicKeyData;
@property(nonatomic, readwrite, strong) NSData *privateKeyData;
@end
@implementation FIRInstanceIDKeyPair
- (instancetype)initWithPrivateKey:(SecKeyRef)privateKey
publicKey:(SecKeyRef)publicKey
publicKeyData:(NSData *)publicKeyData
privateKeyData:(NSData *)privateKeyData {
self = [super init];
if (self) {
_privateKey = privateKey;
_publicKey = publicKey;
_publicKeyData = publicKeyData;
_privateKeyData = privateKeyData;
}
return self;
}
- (void)dealloc {
if (_privateKey) {
CFRelease(_privateKey);
}
if (_publicKey) {
CFRelease(_publicKey);
}
}
#pragma mark - Info
- (BOOL)isValid {
return _privateKey != NULL && _publicKey != NULL;
}
- (SecKeyRef)publicKey {
return _publicKey;
}
- (SecKeyRef)privateKey {
return _privateKey;
}
@end
@@ -0,0 +1,85 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRInstanceIDKeyPair;
extern NSString *const kFIRInstanceIDKeyPairSubType;
@class FIRInstanceIDKeyPairStore;
@interface FIRInstanceIDKeyPairStore : NSObject
/**
* Invalidates the cached keypairs in the Keychain, if needed. The keypair metadata plist is
* checked for existence. If the plist file does not exist, it is a signal of a new installation,
* and therefore the key pairs are not valid.
*
* Returns YES if keypair has been invalidated.
*/
- (BOOL)invalidateKeyPairsIfNeeded;
/**
* Delete the cached RSA keypair from Keychain with the given subtype.
*
* @param subtype The subtype used to cache the RSA keypair in Keychain.
* @param handler The callback handler which is invoked when the keypair deletion is
* complete, with an error if there is any.
*/
- (void)deleteSavedKeyPairWithSubtype:(NSString *)subtype handler:(void (^)(NSError *))handler;
/**
* Delete the plist that caches KeyPair generation timestamps.
*
* @param error The error if any while deleting the plist else nil.
*
* @return YES if the delete was successful else NO.
*/
- (BOOL)removeKeyPairCreationTimePlistWithError:(NSError **)error;
/**
* Loads a cached KeyPair if it exists in the Keychain else generate a new
* one. If a keyPair already exists in memory this will just return that. This should
* not be called from the main thread since it could potentially lead to creating a new
* RSA-2048 bit keyPair which is an expensive operation.
*
* @param error The error, if any, while accessing the Keychain.
*
* @return A valid 2048 bit RSA key pair.
*/
- (FIRInstanceIDKeyPair *)loadKeyPairWithError:(NSError **)error;
/**
* Check if the Keychain has any cached keypairs or not.
*
* @return YES if the Keychain has cached RSA KeyPairs else NO.
*/
- (BOOL)hasCachedKeyPairs;
/**
* Return an identifier for the app instance. The result is a short identifier that can
* be used as a key when storing information about the app. This method will return the same
* ID as long as the application identity remains active. If the identity has been revoked or
* expired the method will generate and return a new identifier.
*
* @param error The error if any while loading the RSA KeyPair.
*
* @return The identifier, as url safe string.
*/
- (NSString *)appIdentityWithError:(NSError *__autoreleasing *)error;
@end
@@ -0,0 +1,525 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDKeyPairStore.h"
#import "FIRInstanceIDBackupExcludedPlist.h"
#import "FIRInstanceIDConstants.h"
#import "FIRInstanceIDKeyPair.h"
#import "FIRInstanceIDKeyPairUtilities.h"
#import "FIRInstanceIDKeychain.h"
#import "FIRInstanceIDLogger.h"
#import "FIRInstanceIDUtilities.h"
#import "NSError+FIRInstanceID.h"
// NOTE: These values should be in sync with what InstanceID saves in as.
static NSString *const kFIRInstanceIDKeyPairStoreFileName = @"com.google.iid-keypair";
static NSString *const kFIRInstanceIDStoreKeyGenerationTime = @"cre";
static NSString *const kFIRInstanceIDStoreKeyPrefix = @"com.google.iid-";
static NSString *const kFIRInstanceIDStoreKeyPublic = @"|P|";
static NSString *const kFIRInstanceIDStoreKeyPrivate = @"|K|";
static NSString *const kFIRInstanceIDStoreKeySubtype = @"|S|";
static NSString *const kFIRInstanceIDKeyPairPublicTagPrefix = @"com.google.iid.keypair.public-";
static NSString *const kFIRInstanceIDKeyPairPrivateTagPrefix = @"com.google.iid.keypair.private-";
static const int kMaxMissingEntitlementErrorCount = 3;
NSString *const kFIRInstanceIDKeyPairSubType = @"";
// Query the key with NSData format
NSData *FIRInstanceIDKeyDataWithTag(NSString *tag) {
if (![tag length]) {
return NULL;
}
NSDictionary *queryKey = FIRInstanceIDKeyPairQuery(tag, YES, YES);
CFTypeRef result = [[FIRInstanceIDKeychain sharedInstance] itemWithQuery:queryKey];
if (!result) {
return NULL;
}
return (__bridge NSData *)result;
}
// Query the key given a tag
SecKeyRef FIRInstanceIDCachedKeyRefWithTag(NSString *tag) {
if (!tag.length) {
return NULL;
}
NSDictionary *queryKey = FIRInstanceIDKeyPairQuery(tag, YES, NO);
CFTypeRef result = [[FIRInstanceIDKeychain sharedInstance] itemWithQuery:queryKey];
return (SecKeyRef)result;
}
// Check if keypair has been migrated from the legacy to the new version
BOOL FIRInstanceIDHasMigratedKeyPair(NSString *legacyPublicKeyTag, NSString *newPublicKeyTag) {
NSData *oldPublicKeyData = FIRInstanceIDKeyDataWithTag(legacyPublicKeyTag);
NSData *newPublicKeyData = FIRInstanceIDKeyDataWithTag(newPublicKeyTag);
return [oldPublicKeyData isEqualToData:newPublicKeyData];
}
// The legacy value is hardcoded to be the same key. This is a potential problem in shared keychain
// environments.
NSString *FIRInstanceIDLegacyPublicTagWithSubtype(NSString *subtype) {
NSString *prefix = kFIRInstanceIDStoreKeyPrefix;
return [NSString stringWithFormat:@"%@%@%@", prefix, subtype, kFIRInstanceIDStoreKeyPublic];
}
// The legacy value is hardcoded to be the same key. This is a potential problem in shared keychain
// environments.
NSString *FIRInstanceIDLegacyPrivateTagWithSubtype(NSString *subtype) {
NSString *prefix = kFIRInstanceIDStoreKeyPrefix;
return [NSString stringWithFormat:@"%@%@%@", prefix, subtype, kFIRInstanceIDStoreKeyPrivate];
}
NSString *FIRInstanceIDPublicTagWithSubtype(NSString *subtype) {
static NSString *publicTag;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *mainAppBundleID = FIRInstanceIDAppIdentifier();
publicTag =
[NSString stringWithFormat:@"%@%@", kFIRInstanceIDKeyPairPublicTagPrefix, mainAppBundleID];
});
return publicTag;
}
NSString *FIRInstanceIDPrivateTagWithSubtype(NSString *subtype) {
static NSString *privateTag;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *mainAppBundleID = FIRInstanceIDAppIdentifier();
privateTag =
[NSString stringWithFormat:@"%@%@", kFIRInstanceIDKeyPairPrivateTagPrefix, mainAppBundleID];
});
return privateTag;
}
NSString *FIRInstanceIDCreationTimeKeyWithSubtype(NSString *subtype) {
return [NSString stringWithFormat:@"%@%@%@", subtype, kFIRInstanceIDStoreKeySubtype,
kFIRInstanceIDStoreKeyGenerationTime];
}
@interface FIRInstanceIDKeyPairStore ()
@property(nonatomic, readwrite, strong) FIRInstanceIDBackupExcludedPlist *plist;
@property(atomic, readwrite, strong) FIRInstanceIDKeyPair *keyPair;
@property(nonatomic, readwrite, assign) NSInteger keychainEntitlementsErrorCount;
@end
@implementation FIRInstanceIDKeyPairStore
- (instancetype)init {
self = [super init];
if (self) {
NSString *fileName = [[self class] keyStoreFileName];
_plist =
[[FIRInstanceIDBackupExcludedPlist alloc] initWithFileName:fileName
subDirectory:kFIRInstanceIDSubDirectoryName];
}
return self;
}
- (BOOL)invalidateKeyPairsIfNeeded {
// Currently keypairs are always invalidated if self.plist is missing. This normally indicates
// a fresh install (or an uninstall/reinstall). In those situations the key pairs should be
// deleted.
// NOTE: Although this class refers to multiple key pairs, with different subtypes, in practice
// only a single subtype is currently supported. (b/64906549)
if (![self.plist doesFileExist]) {
// A fresh install, clear all the key pairs in the key chain. Do not perform migration as all
// key pairs are gone.
[self deleteSavedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType handler:nil];
return YES;
}
// Not a fresh install, perform migration at early state.
[self migrateKeyPairCacheIfNeededWithHandler:nil];
return NO;
}
- (BOOL)hasCachedKeyPairs {
NSError *error;
if ([self cachedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType error:&error] == nil) {
if (error) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairStore000,
@"Failed to get the cached keyPair %@", error);
}
error = nil;
[self removeKeyPairCreationTimePlistWithError:&error];
if (error) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairStore001,
@"Failed to remove keyPair creationTime plist %@", error);
}
return NO;
}
return YES;
}
- (NSString *)appIdentityWithError:(NSError *__autoreleasing *)error {
// Load the keyPair from Keychain (or generate a key pair, if this is the first run of the app).
FIRInstanceIDKeyPair *keyPair = [self loadKeyPairWithError:error];
if (!keyPair) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairStoreCouldNotLoadKeyPair,
@"Keypair could not be loaded from Keychain. Error: %@", (*error));
return nil;
}
if (error) {
*error = nil;
}
NSString *appIdentity = FIRInstanceIDAppIdentity(keyPair);
if (!appIdentity.length) {
if (error) {
*error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeUnknown];
}
}
return appIdentity;
}
- (FIRInstanceIDKeyPair *)loadKeyPairWithError:(NSError **)error {
// In case we call this from different threads we don't want to generate or fetch the
// keyPair multiple times. Once we have a keyPair in the cache it would mostly be used
// from there.
@synchronized(self) {
if ([self.keyPair isValid]) {
return self.keyPair;
}
if (self.keychainEntitlementsErrorCount >= kMaxMissingEntitlementErrorCount) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairStore002,
@"Keychain not accessible, Entitlements missing error (-34018). "
@"Will not check token in cache.");
return nil;
}
if (!self.keyPair) {
self.keyPair = [self validCachedKeyPairWithSubtype:kFIRInstanceIDKeyPairSubType error:error];
}
if ((*error).code == kFIRInstanceIDSecMissingEntitlementErrorCode) {
self.keychainEntitlementsErrorCount++;
}
if (!self.keyPair) {
self.keyPair = [self generateAndSaveKeyWithSubtype:kFIRInstanceIDKeyPairSubType
creationTime:FIRInstanceIDCurrentTimestampInSeconds()
error:error];
}
}
return self.keyPair;
}
// TODO(chliangGoogle: Remove subtype support, as it's not being used.
- (FIRInstanceIDKeyPair *)generateAndSaveKeyWithSubtype:(NSString *)subtype
creationTime:(int64_t)creationTime
error:(NSError **)error {
NSString *publicKeyTag = FIRInstanceIDPublicTagWithSubtype(subtype);
NSString *privateKeyTag = FIRInstanceIDPrivateTagWithSubtype(subtype);
FIRInstanceIDKeyPair *keyPair =
[[FIRInstanceIDKeychain sharedInstance] generateKeyPairWithPrivateTag:privateKeyTag
publicTag:publicKeyTag];
if (![keyPair isValid]) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairStore003,
@"Unable to generate keypair.");
return nil;
}
NSString *creationTimeKey = FIRInstanceIDCreationTimeKeyWithSubtype(subtype);
NSDictionary *keyPairData = @{creationTimeKey : @(creationTime)};
if (error) {
*error = nil;
}
NSMutableDictionary *allKeyPairs = [[self.plist contentAsDictionary] mutableCopy];
if (allKeyPairs.count) {
[allKeyPairs addEntriesFromDictionary:keyPairData];
} else {
allKeyPairs = [keyPairData mutableCopy];
}
if (![self.plist writeDictionary:allKeyPairs error:error]) {
[FIRInstanceIDKeyPairStore deleteKeyPairWithPrivateTag:privateKeyTag
publicTag:publicKeyTag
handler:nil];
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairStore004,
@"Failed to save keypair data to plist %@", error ? *error : @"");
return nil;
}
return keyPair;
}
- (FIRInstanceIDKeyPair *)validCachedKeyPairWithSubtype:(NSString *)subtype
error:(NSError **)error {
// On a new install (or if the ID was deleted), the plist will be missing, which should trigger
// a reset of the key pairs in Keychain (if they exist).
NSDictionary *allKeyPairs = [self.plist contentAsDictionary];
NSString *creationTimeKey = FIRInstanceIDCreationTimeKeyWithSubtype(subtype);
if (allKeyPairs[creationTimeKey] > 0) {
return [self cachedKeyPairWithSubtype:subtype error:error];
} else {
// There is no need to reset keypair again here as FIRInstanceID init call is always
// going to be ahead of this call, which already trigger keypair reset if it's new install
FIRInstanceIDErrorCode code = kFIRInstanceIDErrorCodeInvalidKeyPairCreationTime;
if (error) {
*error = [NSError errorWithFIRInstanceIDErrorCode:code];
}
return nil;
}
}
- (FIRInstanceIDKeyPair *)cachedKeyPairWithSubtype:(NSString *)subtype
error:(NSError *__autoreleasing *)error {
// base64 encoded keys
NSString *publicKeyTag = FIRInstanceIDPublicTagWithSubtype(subtype);
NSString *privateKeyTag = FIRInstanceIDPrivateTagWithSubtype(subtype);
return [FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:privateKeyTag
publicKeyTag:publicKeyTag
error:error];
}
+ (FIRInstanceIDKeyPair *)keyPairForPrivateKeyTag:(NSString *)privateKeyTag
publicKeyTag:(NSString *)publicKeyTag
error:(NSError *__autoreleasing *)error {
if (![privateKeyTag length] || ![publicKeyTag length]) {
if (error) {
*error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeInvalidKeyPairTags];
}
return nil;
}
SecKeyRef privateKeyRef = FIRInstanceIDCachedKeyRefWithTag(privateKeyTag);
SecKeyRef publicKeyRef = FIRInstanceIDCachedKeyRefWithTag(publicKeyTag);
if (!privateKeyRef || !publicKeyRef) {
if (error) {
*error = [NSError errorWithFIRInstanceIDErrorCode:kFIRInstanceIDErrorCodeMissingKeyPair];
}
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPair000,
@"No keypair info is found with tag %@", privateKeyTag);
return nil;
}
NSData *publicKeyData = FIRInstanceIDKeyDataWithTag(publicKeyTag);
NSData *privateKeyData = FIRInstanceIDKeyDataWithTag(privateKeyTag);
FIRInstanceIDKeyPair *keyPair = [[FIRInstanceIDKeyPair alloc] initWithPrivateKey:privateKeyRef
publicKey:publicKeyRef
publicKeyData:publicKeyData
privateKeyData:privateKeyData];
return keyPair;
}
// Migrates from keypair saved under legacy keys (hardcoded value) to dynamic keys (stable, but
// unique for the app's bundle id
- (void)migrateKeyPairCacheIfNeededWithHandler:(void (^)(NSError *error))handler {
// Attempt to load keypair using legacy keys
NSString *legacyPublicKeyTag =
FIRInstanceIDLegacyPublicTagWithSubtype(kFIRInstanceIDKeyPairSubType);
NSString *legacyPrivateKeyTag =
FIRInstanceIDLegacyPrivateTagWithSubtype(kFIRInstanceIDKeyPairSubType);
NSError *error;
FIRInstanceIDKeyPair *keyPair =
[FIRInstanceIDKeyPairStore keyPairForPrivateKeyTag:legacyPrivateKeyTag
publicKeyTag:legacyPublicKeyTag
error:&error];
if (![keyPair isValid]) {
FIRInstanceIDLoggerDebug(kFIRInstanceIDMessageCodeKeyPairNoLegacyKeyPair,
@"There's no legacy keypair so no need to do migration.");
if (handler) {
handler(nil);
}
return;
}
// Check whether migration already done.
NSString *publicKeyTag = FIRInstanceIDPublicTagWithSubtype(kFIRInstanceIDKeyPairSubType);
if (FIRInstanceIDHasMigratedKeyPair(legacyPublicKeyTag, publicKeyTag)) {
if (handler) {
handler(nil);
}
return;
}
// Also cache locally since we are sure to use the migrated key pair.
self.keyPair = keyPair;
// Either new key pair doesn't exist or it's different than legacy key pair, start the migration.
__block NSError *updateKeyRefError;
NSString *privateKeyTag = FIRInstanceIDPrivateTagWithSubtype(kFIRInstanceIDKeyPairSubType);
[self updateKeyRef:keyPair.publicKey
withTag:publicKeyTag
handler:^(NSError *error) {
if (error) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairMigrationError,
@"Unable to migrate key pair from legacy ones.");
updateKeyRefError = error;
}
}];
[self updateKeyRef:keyPair.privateKey
withTag:privateKeyTag
handler:^(NSError *error) {
if (error) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairMigrationError,
@"Unable to migrate key pair from legacy ones.");
updateKeyRefError = error;
}
if (handler) {
handler(updateKeyRefError);
}
}];
}
// Used for migrating from legacy tags to updated tags. The legacy keychain is not deleted for
// backward compatibility.
// TODO(chliangGoogle) Delete the legacy keychain when GCM is fully deprecated.
- (void)updateKeyRef:(SecKeyRef)keyRef
withTag:(NSString *)tag
handler:(void (^)(NSError *error))handler {
NSData *updatedTagData = [tag dataUsingEncoding:NSUTF8StringEncoding];
__block NSError *keychainError;
// Always delete the old keychain before adding a new one to avoid conflicts.
NSDictionary *deleteQuery = @{
(__bridge id)kSecAttrApplicationTag : updatedTagData,
(__bridge id)kSecClass : (__bridge id)kSecClassKey,
(__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA,
(__bridge id)kSecReturnRef : @(YES),
};
[[FIRInstanceIDKeychain sharedInstance] removeItemWithQuery:deleteQuery
handler:^(NSError *error) {
if (error) {
keychainError = error;
}
}];
NSDictionary *addQuery = @{
(__bridge id)kSecAttrApplicationTag : updatedTagData,
(__bridge id)kSecClass : (__bridge id)kSecClassKey,
(__bridge id)kSecValueRef : (__bridge id)keyRef,
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleAlwaysThisDeviceOnly,
};
[[FIRInstanceIDKeychain sharedInstance] addItemWithQuery:addQuery
handler:^(NSError *addError) {
if (addError) {
keychainError = addError;
}
if (handler) {
handler(keychainError);
}
}];
}
- (void)deleteSavedKeyPairWithSubtype:(NSString *)subtype
handler:(void (^)(NSError *error))handler {
NSDictionary *allKeyPairs = [self.plist contentAsDictionary];
NSString *publicKeyTag = FIRInstanceIDPublicTagWithSubtype(subtype);
NSString *privateKeyTag = FIRInstanceIDPrivateTagWithSubtype(subtype);
NSString *creationTimeKey = FIRInstanceIDCreationTimeKeyWithSubtype(subtype);
// remove the creation time
if (allKeyPairs[creationTimeKey] > 0) {
NSMutableDictionary *newKeyPairs = [NSMutableDictionary dictionaryWithDictionary:allKeyPairs];
[newKeyPairs removeObjectForKey:creationTimeKey];
NSError *plistError;
if (![self.plist writeDictionary:newKeyPairs error:&plistError]) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairStore006,
@"Unable to remove keypair creation time from plist %@", plistError);
}
}
self.keyPair = nil;
[FIRInstanceIDKeyPairStore
deleteKeyPairWithPrivateTag:privateKeyTag
publicTag:publicKeyTag
handler:^(NSError *error) {
// Delete legacy key pairs from GCM/FCM If they exist. All key pairs
// should be deleted when app is newly installed.
NSString *legacyPublicKeyTag =
FIRInstanceIDLegacyPublicTagWithSubtype(subtype);
NSString *legacyPrivateKeyTag =
FIRInstanceIDLegacyPrivateTagWithSubtype(subtype);
[FIRInstanceIDKeyPairStore
deleteKeyPairWithPrivateTag:legacyPrivateKeyTag
publicTag:legacyPublicKeyTag
handler:nil];
if (error) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairStore007,
@"Unable to remove RSA keypair, error: %@",
error);
if (handler) {
handler(error);
}
} else {
if (handler) {
handler(nil);
}
}
}];
}
+ (void)deleteKeyPairWithPrivateTag:(NSString *)privateTag
publicTag:(NSString *)publicTag
handler:(void (^)(NSError *))handler {
NSDictionary *queryPublicKey = FIRInstanceIDKeyPairQuery(publicTag, NO, NO);
NSDictionary *queryPrivateKey = FIRInstanceIDKeyPairQuery(privateTag, NO, NO);
__block NSError *keychainError;
// Always remove public key first because it is the key we generate IID.
[[FIRInstanceIDKeychain sharedInstance] removeItemWithQuery:queryPublicKey
handler:^(NSError *error) {
if (error) {
keychainError = error;
}
}];
[[FIRInstanceIDKeychain sharedInstance] removeItemWithQuery:queryPrivateKey
handler:^(NSError *error) {
if (error) {
keychainError = error;
}
if (handler) {
handler(keychainError);
}
}];
}
- (BOOL)removeKeyPairCreationTimePlistWithError:(NSError *__autoreleasing *)error {
if (![self.plist deleteFile:error]) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPairStore008,
@"Unable to delete keypair creation times plist");
return NO;
}
return YES;
}
+ (NSString *)keyStoreFileName {
return kFIRInstanceIDKeyPairStoreFileName;
}
@end
@@ -0,0 +1,36 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRInstanceIDKeyPair;
/**
* A web-safe base64 encoded string with no padding.
*
* @param data The data to encode.
*
* @return A web-safe base 64 encoded string with no padding.
*/
FOUNDATION_EXPORT NSString *FIRInstanceIDWebSafeBase64(NSData *data);
FOUNDATION_EXPORT NSData *FIRInstanceIDSHA1(NSData *data);
FOUNDATION_EXPORT NSDictionary *FIRInstanceIDKeyPairQuery(NSString *tag,
BOOL addReturnAttr,
BOOL returnData);
FOUNDATION_EXPORT NSString *FIRInstanceIDAppIdentity(FIRInstanceIDKeyPair *keyPair);
@@ -0,0 +1,83 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRInstanceIDKeyPairUtilities.h"
#import <CommonCrypto/CommonDigest.h>
#import "FIRInstanceIDKeyPair.h"
#import "FIRInstanceIDLogger.h"
#import "FIRInstanceIDStringEncoding.h"
NSString *FIRInstanceIDWebSafeBase64(NSData *data) {
// Websafe encoding with no padding.
FIRInstanceIDStringEncoding *encoding =
[FIRInstanceIDStringEncoding rfc4648Base64WebsafeStringEncoding];
[encoding setDoPad:NO];
return [encoding encode:data];
}
NSData *FIRInstanceIDSHA1(NSData *data) {
unsigned int outputLength = CC_SHA1_DIGEST_LENGTH;
unsigned char output[outputLength];
unsigned int length = (unsigned int)[data length];
CC_SHA1(data.bytes, length, output);
return [NSMutableData dataWithBytes:output length:outputLength];
}
NSDictionary *FIRInstanceIDKeyPairQuery(NSString *tag, BOOL addReturnAttr, BOOL returnData) {
NSMutableDictionary *queryKey = [NSMutableDictionary dictionary];
NSData *tagData = [tag dataUsingEncoding:NSUTF8StringEncoding];
queryKey[(__bridge id)kSecClass] = (__bridge id)kSecClassKey;
queryKey[(__bridge id)kSecAttrApplicationTag] = tagData;
queryKey[(__bridge id)kSecAttrKeyType] = (__bridge id)kSecAttrKeyTypeRSA;
if (addReturnAttr) {
if (returnData) {
queryKey[(__bridge id)kSecReturnData] = @(YES);
} else {
queryKey[(__bridge id)kSecReturnRef] = @(YES);
}
}
return queryKey;
}
NSString *FIRInstanceIDAppIdentity(FIRInstanceIDKeyPair *keyPair) {
// An Instance-ID is a 64 bit (8 byte) integer with a fixed 4-bit header of 0111 (=^ 0x7).
// The variable 60 bits are obtained by truncating the SHA1 of the app-instance's public key.
SecKeyRef publicKeyRef = [keyPair publicKey];
if (!publicKeyRef) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeKeyPair002,
@"Unable to create a valid asymmetric crypto key");
return nil;
}
NSData *publicKeyData = keyPair.publicKeyData;
NSData *publicKeySHA1 = FIRInstanceIDSHA1(publicKeyData);
const uint8_t *bytes = publicKeySHA1.bytes;
NSMutableData *identityData = [NSMutableData dataWithData:publicKeySHA1];
uint8_t b0 = bytes[0];
// Take the first byte and make the initial four 7 by initially making the initial 4 bits 0
// and then adding 0x70 to it.
b0 = 0x70 + (0xF & b0);
// failsafe should give you back b0 itself
b0 = (b0 & 0xFF);
[identityData replaceBytesInRange:NSMakeRange(0, 1) withBytes:&b0];
NSData *data = [identityData subdataWithRange:NSMakeRange(0, 8 * sizeof(Byte))];
return FIRInstanceIDWebSafeBase64(data);
}
@@ -0,0 +1,76 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
/* The Keychain error domain */
extern NSString *const kFIRInstanceIDKeychainErrorDomain;
@class FIRInstanceIDKeyPair;
/*
* Wrapping the keychain operations in a serialize queue. This is to avoid keychain operation
* blocking main queue.
*/
@interface FIRInstanceIDKeychain : NSObject
/**
* FIRInstanceIDKeychain.
*
* @return A shared instance of FIRInstanceIDKeychain.
*/
+ (instancetype)sharedInstance;
/**
* Get keychain items matching the given a query.
*
* @param keychainQuery The keychain query.
*
* @return An CFTypeRef result matching the provided inputs.
*/
- (CFTypeRef)itemWithQuery:(NSDictionary *)keychainQuery;
/**
* Remove the cached items from the keychain matching the query.
*
* @param keychainQuery The keychain query.
* @param handler The callback handler which is invoked when the remove operation is
* complete, with an error if there is any.
*/
- (void)removeItemWithQuery:(NSDictionary *)keychainQuery handler:(void (^)(NSError *error))handler;
/**
* Add the item with a given query.
*
* @param keychainQuery The keychain query.
* @param handler The callback handler which is invoked when the add operation is
* complete, with an error if there is any.
*/
- (void)addItemWithQuery:(NSDictionary *)keychainQuery handler:(void (^)(NSError *))handler;
#pragma mark - Keypair
/**
* Generate a public/private key pair given their tags.
*
* @param privateTag The private tag associated with the private key.
* @param publicTag The public tag associated with the public key.
*
* @return A new FIRInstanceIDKeyPair object.
*/
- (FIRInstanceIDKeyPair *)generateKeyPairWithPrivateTag:(NSString *)privateTag
publicTag:(NSString *)publicTag;
@end

Some files were not shown because too many files have changed in this diff Show More