Compare commits

...

19 Commits

Author SHA1 Message Date
Diego Mello 2354e93537 Firebase 2021-06-22 13:54:52 -03:00
Diego Mello 228485e3d0 Remove emotion dev 2021-06-17 17:45:36 -03:00
Diego Mello bc9b6d1c33 document picker 2021-06-17 17:39:27 -03:00
Diego Mello d24ec17a9a try save husky 2021-06-17 16:53:15 -03:00
Diego Mello f0d1aeaffa vector icons, xregexp, popover 2021-06-17 16:24:32 -03:00
Diego Mello b35ee3f618 react-native-picker-select 2021-06-17 14:48:20 -03:00
Diego Mello 62cc318260 localize 2021-06-17 14:24:41 -03:00
Diego Mello 6727c760e6 mocha 2021-06-17 14:21:45 -03:00
Diego Mello 0503c48b73 Device info 2021-06-14 14:59:53 -03:00
Diego Mello 4fd192c42b Update navigation 2021-06-14 14:17:49 -03:00
Diego Mello b63a17db26 Patch cookies 2021-06-14 13:55:53 -03:00
Diego Mello ff5aac023e datepicker, netinfo, base64 and bootsplash 2021-06-11 17:34:19 -03:00
Diego Mello ee4d42ee95
Merge branch 'develop' into update-minors-patches 2021-06-11 15:25:09 -03:00
Diego Mello 0ec8289971 Cookies 2021-06-11 15:20:43 -03:00
Diego Mello 583a0f2943 Update few non semver 2021-06-11 14:39:35 -03:00
Diego Mello f267039643 Update dev minors 2021-06-11 14:20:16 -03:00
Diego Mello 2c90b232db Update minors 2021-06-11 14:09:19 -03:00
Diego Mello 7ad547343d Update dev patches 2021-06-08 17:34:34 -03:00
Diego Mello 233cf5c5ce Update non-dev patches 2021-06-08 17:33:12 -03:00
958 changed files with 45532 additions and 45286 deletions

1
.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run precommit

View File

@ -5,8 +5,9 @@ apply plugin: "com.android.application"
apply plugin: 'kotlin-android'
if (!isFoss) {
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.bugsnag.android.gradle'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
}
import com.android.build.OutputFile
@ -262,7 +263,7 @@ dependencies {
addUnimodulesDependencies()
implementation project(':watermelondb')
implementation project(':@react-native-community_viewpager')
playImplementation project(':reactnativenotifications')
playImplementation project(':react-native-notifications')
playImplementation project(':@react-native-firebase_app')
playImplementation project(':@react-native-firebase_analytics')
playImplementation project(':@react-native-firebase_crashlytics')
@ -303,6 +304,3 @@ task copyDownloadableDepsToLibs(type: Copy) {
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
if (!isFoss) {
apply plugin: 'com.google.gms.google-services'
}

View File

@ -31,8 +31,8 @@ buildscript {
dependencies {
if (isPlay) {
classpath 'com.google.gms:google-services:4.2.0'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
classpath 'com.google.gms:google-services:4.3.8'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.0'
classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+'
}
classpath 'com.android.tools.build:gradle:3.5.3'

View File

@ -4,8 +4,8 @@ includeUnimodulesProjects()
rootProject.name = 'RocketChatRN'
include ':watermelondb'
project(':watermelondb').projectDir = new File(rootProject.projectDir, '../node_modules/@nozbe/watermelondb/native/android')
include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app')
include ':react-native-notifications'
project(':react-native-notifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/lib/android/app')
include ':@react-native-community_viewpager'
project(':@react-native-community_viewpager').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/viewpager/android')
include ':@react-native-firebase_app'

View File

@ -1,5 +1,5 @@
import EJSON from 'ejson';
import PushNotification from './push';
// import PushNotification from './push';
import store from '../../lib/createStore';
import { deepLinkingOpen } from '../../actions/deepLinking';
import { isFDroidBuild } from '../../constants/environment';
@ -36,13 +36,14 @@ export const onNotification = (notification) => {
}
};
export const getDeviceToken = () => PushNotification.getDeviceToken();
export const setBadgeCount = count => PushNotification.setBadgeCount(count);
export const getDeviceToken = () => {} // PushNotification.getDeviceToken();
export const setBadgeCount = count => {} // PushNotification.setBadgeCount(count);
export const initializePushNotifications = () => {
if (!isFDroidBuild) {
setBadgeCount();
return PushNotification.configure({
onNotification
});
// return PushNotification.configure({
// onNotification
// });
return null;
}
};

View File

@ -67,8 +67,8 @@ const restore = function* restore() {
}
};
const start = function start() {
RNBootSplash.hide();
const start = function* start() {
yield RNBootSplash.hide();
};
const root = function* root() {

View File

@ -103,7 +103,7 @@ export const localAuthenticate = async(server) => {
// if screen lock is enabled
if (serverRecord?.autoLock) {
// Make sure splash screen has been hidden
RNBootSplash.hide();
await RNBootSplash.hide();
// Check if the app has passcode
const result = await checkHasPasscode({ serverRecord });

View File

@ -39,7 +39,7 @@ export const logEvent = (eventName, payload) => {
export const setCurrentScreen = (currentScreen) => {
if (!isFDroidBuild) {
analytics().setCurrentScreen(currentScreen);
analytics().logScreenView({ screen_name: currentScreen });
leaveBreadcrumb(currentScreen, { type: 'navigation' });
}
};

View File

@ -5,7 +5,7 @@ import {
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import FastImage from '@rocket.chat/react-native-fast-image';
import CookieManager from '@react-native-community/cookies';
import CookieManager from '@react-native-cookies/cookies';
import { logout as logoutAction } from '../../actions/login';
import { selectServerRequest as selectServerRequestAction } from '../../actions/server';

View File

@ -35,6 +35,8 @@ target 'ShareRocketChatRN' do
pod 'RNFBApp', :path => '../node_modules/@react-native-firebase/app'
pod 'RNFBAnalytics', :path => '../node_modules/@react-native-firebase/analytics'
pod 'RNFBCrashlytics', :path => '../node_modules/@react-native-firebase/crashlytics'
$RNFirebaseAnalyticsWithoutAdIdSupport = true
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'
pod 'RNCMaskedView', :path => '../node_modules/@react-native-community/masked-view'
pod 'RNFastImage', :path => '../node_modules/@rocket.chat/react-native-fast-image'

View File

@ -34,7 +34,7 @@ PODS:
- EXPermissions (9.0.1):
- UMCore
- UMPermissionsInterface
- EXVideoThumbnails (5.0.0):
- EXVideoThumbnails (5.1.0):
- UMCore
- UMFileSystemInterface
- EXWebBrowser (8.3.1):
@ -47,45 +47,51 @@ PODS:
- React-Core (= 0.63.4)
- React-jsi (= 0.63.4)
- ReactCommon/turbomodule/core (= 0.63.4)
- Firebase/Analytics (6.27.1):
- Firebase/Core
- Firebase/Core (6.27.1):
- Firebase/AnalyticsWithoutAdIdSupport (8.1.1):
- Firebase/CoreOnly
- FirebaseAnalytics (= 6.6.2)
- Firebase/CoreOnly (6.27.1):
- FirebaseCore (= 6.8.1)
- Firebase/Crashlytics (6.27.1):
- FirebaseAnalytics/WithoutAdIdSupport (~> 8.0.0)
- Firebase/CoreOnly (8.1.1):
- FirebaseCore (= 8.1.0)
- Firebase/Crashlytics (8.1.1):
- Firebase/CoreOnly
- FirebaseCrashlytics (~> 4.2.0)
- FirebaseAnalytics (6.6.2):
- FirebaseCore (~> 6.8)
- FirebaseInstallations (~> 1.4)
- GoogleAppMeasurement (= 6.6.2)
- GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (~> 1.30905.0)
- FirebaseCore (6.8.1):
- FirebaseCoreDiagnostics (~> 1.3)
- GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5)
- FirebaseCoreDiagnostics (1.4.0):
- GoogleDataTransportCCTSupport (~> 3.1)
- GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5)
- nanopb (~> 1.30905.0)
- FirebaseCrashlytics (4.2.0):
- FirebaseCore (~> 6.8)
- FirebaseInstallations (~> 1.1)
- GoogleDataTransport (~> 6.1)
- GoogleDataTransportCCTSupport (~> 3.1)
- nanopb (~> 1.30905.0)
- FirebaseCrashlytics (~> 8.1.0)
- FirebaseAnalytics/Base (8.0.0):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.4)
- GoogleUtilities/MethodSwizzler (~> 7.4)
- GoogleUtilities/Network (~> 7.4)
- "GoogleUtilities/NSData+zlib (~> 7.4)"
- nanopb (~> 2.30908.0)
- FirebaseAnalytics/WithoutAdIdSupport (8.0.0):
- FirebaseAnalytics/Base (= 8.0.0)
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleAppMeasurement/WithoutAdIdSupport (= 8.0.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.4)
- GoogleUtilities/MethodSwizzler (~> 7.4)
- GoogleUtilities/Network (~> 7.4)
- "GoogleUtilities/NSData+zlib (~> 7.4)"
- nanopb (~> 2.30908.0)
- FirebaseCore (8.1.0):
- FirebaseCoreDiagnostics (~> 8.0)
- GoogleUtilities/Environment (~> 7.4)
- GoogleUtilities/Logger (~> 7.4)
- FirebaseCoreDiagnostics (8.1.0):
- GoogleDataTransport (~> 9.0)
- GoogleUtilities/Environment (~> 7.4)
- GoogleUtilities/Logger (~> 7.4)
- nanopb (~> 2.30908.0)
- FirebaseCrashlytics (8.1.0):
- FirebaseCore (~> 8.0)
- FirebaseInstallations (~> 8.0)
- GoogleDataTransport (~> 9.0)
- nanopb (~> 2.30908.0)
- PromisesObjC (~> 1.2)
- FirebaseInstallations (1.5.0):
- FirebaseCore (~> 6.8)
- GoogleUtilities/Environment (~> 6.7)
- GoogleUtilities/UserDefaults (~> 6.7)
- FirebaseInstallations (8.1.0):
- FirebaseCore (~> 8.0)
- GoogleUtilities/Environment (~> 7.4)
- GoogleUtilities/UserDefaults (~> 7.4)
- PromisesObjC (~> 1.2)
- Flipper (0.54.0):
- Flipper-Folly (~> 2.2)
@ -143,34 +149,34 @@ PODS:
- DoubleConversion
- glog
- glog (0.3.5)
- GoogleAppMeasurement (6.6.2):
- GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (~> 1.30905.0)
- GoogleDataTransport (6.2.1)
- GoogleDataTransportCCTSupport (3.2.0):
- GoogleDataTransport (~> 6.1)
- nanopb (~> 1.30905.0)
- GoogleUtilities/AppDelegateSwizzler (6.7.1):
- GoogleAppMeasurement/WithoutAdIdSupport (8.0.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.4)
- GoogleUtilities/MethodSwizzler (~> 7.4)
- GoogleUtilities/Network (~> 7.4)
- "GoogleUtilities/NSData+zlib (~> 7.4)"
- nanopb (~> 2.30908.0)
- GoogleDataTransport (9.0.1):
- GoogleUtilities/Environment (~> 7.2)
- nanopb (~> 2.30908.0)
- PromisesObjC (~> 1.2)
- GoogleUtilities/AppDelegateSwizzler (7.4.1):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Environment (6.7.1):
- GoogleUtilities/Environment (7.4.1):
- PromisesObjC (~> 1.2)
- GoogleUtilities/Logger (6.7.1):
- GoogleUtilities/Logger (7.4.1):
- GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (6.7.1):
- GoogleUtilities/MethodSwizzler (7.4.1):
- GoogleUtilities/Logger
- GoogleUtilities/Network (6.7.1):
- GoogleUtilities/Network (7.4.1):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (6.7.1)"
- GoogleUtilities/Reachability (6.7.1):
- "GoogleUtilities/NSData+zlib (7.4.1)"
- GoogleUtilities/Reachability (7.4.1):
- GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (6.7.1):
- GoogleUtilities/UserDefaults (7.4.1):
- GoogleUtilities/Logger
- JitsiMeetSDK (2.10.2)
- KeyCommands (2.0.3):
@ -187,15 +193,15 @@ PODS:
- MMKV (1.2.1):
- MMKVCore (~> 1.2.1)
- MMKVCore (1.2.1)
- nanopb (1.30905.0):
- nanopb/decode (= 1.30905.0)
- nanopb/encode (= 1.30905.0)
- nanopb/decode (1.30905.0)
- nanopb/encode (1.30905.0)
- nanopb (2.30908.0):
- nanopb/decode (= 2.30908.0)
- nanopb/encode (= 2.30908.0)
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
- OpenSSL-Universal (1.0.2.19):
- OpenSSL-Universal/Static (= 1.0.2.19)
- OpenSSL-Universal/Static (1.0.2.19)
- PromisesObjC (1.2.9)
- PromisesObjC (1.2.12)
- RCTRequired (0.63.4)
- RCTTypeSafety (0.63.4):
- FBLazyVector (= 0.63.4)
@ -364,36 +370,36 @@ PODS:
- React-jsinspector (0.63.4)
- react-native-appearance (0.3.4):
- React
- react-native-background-timer (2.2.0):
- React
- react-native-background-timer (2.4.1):
- React-Core
- react-native-blur (0.8.0):
- React
- react-native-cameraroll (4.0.1):
- React
- react-native-cookies (4.0.0):
- React
- react-native-document-picker (3.5.3):
- React
- react-native-cameraroll (4.0.4):
- React-Core
- react-native-cookies (6.0.8):
- React-Core
- react-native-document-picker (5.2.0):
- React-Core
- react-native-jitsi-meet (2.4.0):
- JitsiMeetSDK
- React
- react-native-mmkv-storage (0.3.5):
- MMKV (= 1.2.1)
- React
- react-native-netinfo (5.9.9):
- react-native-netinfo (6.0.0):
- React-Core
- react-native-notifications (3.5.0):
- React-Core
- react-native-orientation-locker (1.3.1):
- React-Core
- react-native-restart (0.0.22):
- React-Core
- react-native-safe-area-context (3.2.0):
- React-Core
- react-native-notifications (2.1.7):
- React
- react-native-orientation-locker (1.1.8):
- React
- react-native-restart (0.0.17):
- React
- react-native-safe-area-context (3.1.1):
- React
- react-native-simple-crypto (0.4.0):
- OpenSSL-Universal
- React
- react-native-slider (3.0.2):
- react-native-slider (3.0.3):
- React
- react-native-webview (10.3.2):
- React
@ -465,35 +471,34 @@ PODS:
- React
- rn-fetch-blob (0.12.0):
- React-Core
- RNBootSplash (2.2.5):
- React
- RNCAsyncStorage (1.11.0):
- React
- RNCMaskedView (0.1.10):
- RNBootSplash (3.2.3):
- React-Core
- RNCAsyncStorage (1.12.1):
- React-Core
- RNCMaskedView (0.1.11):
- React
- RNConfigReader (1.0.0):
- React
- RNCPicker (1.8.1):
- React-Core
- RNDateTimePicker (2.6.0):
- React
- RNDeviceInfo (5.6.2):
- React
- RNDateTimePicker (3.5.2):
- React-Core
- RNDeviceInfo (8.1.3):
- React-Core
- RNFastImage (8.2.0):
- React
- SDWebImage (~> 5.0)
- SDWebImageWebPCoder (~> 0.4.1)
- RNFBAnalytics (7.3.1):
- Firebase/Analytics (~> 6.27.0)
- React
- RNFBAnalytics (12.1.0):
- Firebase/AnalyticsWithoutAdIdSupport (= 8.1.1)
- React-Core
- RNFBApp
- RNFBApp (8.2.0):
- Firebase/CoreOnly (~> 6.27.0)
- React
- RNFBCrashlytics (8.1.2):
- Firebase/Core (~> 6.27.0)
- Firebase/Crashlytics (~> 6.27.0)
- React
- RNFBApp (12.1.0):
- Firebase/CoreOnly (= 8.1.1)
- React-Core
- RNFBCrashlytics (12.1.0):
- Firebase/Crashlytics (= 8.1.1)
- React-Core
- RNFBApp
- RNGestureHandler (1.6.1):
- React
@ -506,16 +511,16 @@ PODS:
- React-Core
- React-RCTImage
- TOCropViewController
- RNLocalize (1.4.0):
- React
- RNLocalize (2.1.1):
- React-Core
- RNReanimated (1.9.0):
- React
- RNRootView (1.0.3):
- React
- RNScreens (2.9.0):
- React
- RNVectorIcons (7.0.0):
- React
- RNVectorIcons (8.1.0):
- React-Core
- SDWebImage (5.8.4):
- SDWebImage/Core (= 5.8.4)
- SDWebImage/Core (5.8.4)
@ -599,7 +604,7 @@ DEPENDENCIES:
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
- "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)"
- "react-native-cookies (from `../node_modules/@react-native-community/cookies`)"
- "react-native-cookies (from `../node_modules/@react-native-cookies/cookies`)"
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-jitsi-meet (from `../node_modules/react-native-jitsi-meet`)
- react-native-mmkv-storage (from `../node_modules/react-native-mmkv-storage`)
@ -678,7 +683,6 @@ SPEC REPOS:
- FlipperKit
- GoogleAppMeasurement
- GoogleDataTransport
- GoogleDataTransportCCTSupport
- GoogleUtilities
- libwebp
- MMKV
@ -759,7 +763,7 @@ EXTERNAL SOURCES:
react-native-cameraroll:
:path: "../node_modules/@react-native-community/cameraroll"
react-native-cookies:
:path: "../node_modules/@react-native-community/cookies"
:path: "../node_modules/@react-native-cookies/cookies"
react-native-document-picker:
:path: "../node_modules/react-native-document-picker"
react-native-jitsi-meet:
@ -895,16 +899,16 @@ SPEC CHECKSUMS:
EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812
EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8
EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8
EXVideoThumbnails: 0be939563a5d46ce0d1fa9baea7d454b99475763
EXVideoThumbnails: cd257fc6e07884a704a5674d362a6410933acb68
EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4
FBLazyVector: 3bb422f41b18121b71783a905c10e58606f7dc3e
FBReactNativeSpec: f2c97f2529dd79c083355182cc158c9f98f4bd6e
Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
FirebaseCoreDiagnostics: 4505e4d4009b1d93f605088ee7d7764d5f0d1c84
FirebaseCrashlytics: 7d0fa02ea8842cc4b2ab07b0735edafde1ad77d6
FirebaseInstallations: 3c520c951305cbf9ca54eb891ff9e6d1fd384881
Firebase: 4bb49ae87756034cef870fa3c4006235eb46f475
FirebaseAnalytics: dcb92c7c9ef4fa7ffac276e8f87bd4fc8c97f1b8
FirebaseCore: 389c4ce9a7cce4a7e25eb22326b4bee0050557b2
FirebaseCoreDiagnostics: 3e249cee3de5c5f9cfd6cc2a19997231286fec11
FirebaseCrashlytics: 1b55b3a718f9e20d59d96db46a4652d95a8ba1d2
FirebaseInstallations: 7f31798a8198c354eadcb87176d2090b62edc187
Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
@ -914,18 +918,17 @@ SPEC CHECKSUMS:
FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
Folly: b73c3869541e86821df3c387eb0af5f65addfab4
glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
GoogleAppMeasurement: 8cd1f289d60e629cf16ab03363b9e89c776b9651
GoogleDataTransport: 9a8a16f79feffc7f42096743de2a7c4815e84020
GoogleDataTransportCCTSupport: 489c1265d2c85b68187a83a911913d190012158d
GoogleUtilities: e121a3867449ce16b0e35ddf1797ea7a389ffdf2
GoogleAppMeasurement: c6bbc9753d046b5456dd4f940057fbad2c28419e
GoogleDataTransport: 04c3e9a480bbcaa2ec3f5d27f1cdeb6a92f20c8d
GoogleUtilities: f8a43108b38a68eebe8b3540e1f4f2d28843ce20
JitsiMeetSDK: ef6dd5cfa6d9badf009c7dba1a2c1365bfaae6b0
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
MMKV: 67253edee25a34edf332f91d73fa94a9e038b971
MMKVCore: fe398984acac1fa33f92795d1b5fd0a334c944af
nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
RCTRequired: 082f10cd3f905d6c124597fd1c14f6f2655ff65e
RCTTypeSafety: 8c9c544ecbf20337d069e4ae7fd9a377aadf504b
React: b0a957a2c44da4113b0c4c9853d8387f8e64e615
@ -937,20 +940,20 @@ SPEC CHECKSUMS:
React-jsiexecutor: 93bd528844ad21dc07aab1c67cb10abae6df6949
React-jsinspector: 58aef7155bc9a9683f5b60b35eccea8722a4f53a
react-native-appearance: 0f0e5fc2fcef70e03d48c8fe6b00b9158c2ba8aa
react-native-background-timer: 1f7d560647b40e6a60b01c452ba29c54bf581fc4
react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
react-native-cameraroll: 60ba0068826eab1c8d43146191bafd4363ea58a7
react-native-cookies: d79e5015a5d3a38e08f5cb39c4948532be7e9c2b
react-native-document-picker: 825552b827012282baf4b7cbf119d3b37a280c90
react-native-cameraroll: 88f4e62d9ecd0e1f253abe4f685474f2ea14bfa2
react-native-cookies: 2cb6ef472da68610dfcf0eaee68464c244943abd
react-native-document-picker: f1b5398801b332c77bc62ae0eae2116f49bdff26
react-native-jitsi-meet: f2407aca85566e031ee7b222e497ee5ecb6623de
react-native-mmkv-storage: 48729fe90e850ef2fdc9d3714b7030c7c51d82b0
react-native-netinfo: 0212ce8604e88edf686f1481b925b17a42a52449
react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d
react-native-restart: d19a0f8d053d065fe64cd2baebb6487111c77149
react-native-safe-area-context: 344b969c45af3d8464d36e8dea264942992ef033
react-native-netinfo: e849fc21ca2f4128a5726c801a82fc6f4a6db50d
react-native-notifications: 89a73cd2cd2648e1734fa10e3507681c9e4f14de
react-native-orientation-locker: 998c0744e26624407dac068c04c605b4af7304a2
react-native-restart: 733a51ad137f15b0f8dc34c4082e55af7da00979
react-native-safe-area-context: f0906bf8bc9835ac9a9d3f97e8bde2a997d8da79
react-native-simple-crypto: 564740fd8124827d82e9e8ded4a0de8c695c8a4d
react-native-slider: 0221b417686c5957f6e77cd9ac22c1478a165355
react-native-slider: e99fc201cefe81270fc9d81714a7a0f5e566b168
react-native-webview: 679b6f400176e2ea8a785acf7ae16cf282e7d1eb
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
React-RCTAnimation: 1bde3ecc0c104c55df246eda516e0deb03c4e49b
@ -966,24 +969,24 @@ SPEC CHECKSUMS:
ReactNativeUiLib: cde7263a7d308b60161cd286f95c9433e79f2f7d
rn-extensions-share: 5fd84a80e6594706f0dfa1884f2d6d591b382cf5
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNBootSplash: b3836aa90c5bec690c6cd3c9ab355fcf98d0fe1d
RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
RNBootSplash: 8ef5ffa03dadd35f66510b42960ce40f397c98bf
RNCAsyncStorage: b03032fdbdb725bea0bd9e5ec5a7272865ae7398
RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489
RNConfigReader: 396da6a6444182a76e8ae0930b9436c7575045cb
RNCPicker: 914b557e20b3b8317b084aca9ff4b4edb95f61e4
RNDateTimePicker: e386ff4ef3300964ed0cad97ce6f206e0effbfdb
RNDeviceInfo: ed8557a8bd6443cbc0ab5d375e6808a38a279744
RNDateTimePicker: 7658208086d86d09e1627b5c34ba0cf237c60140
RNDeviceInfo: 8d3a29207835f972bce883723882980143270d55
RNFastImage: f40d202ea2367239f71bc7cf11315f4bebab85b4
RNFBAnalytics: dae6d7b280ba61c96e1bbdd34aca3154388f025e
RNFBApp: 6fd8a7e757135d4168bf033a8812c241af7363a0
RNFBCrashlytics: 88de72c2476b5868a892d9523b89b86c527c540e
RNFBAnalytics: d8f885de70f3133ca1be101cb5f115e763dbd037
RNFBApp: 406bc9586c70ccf20504b4d3b54ac4341c98993f
RNFBCrashlytics: 963a2757f6a52e37ae50adab5832162a7d81f98f
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
RNImageCropPicker: 38865ab4af1b0b2146ad66061196bc0184946855
RNLocalize: b6df30cc25ae736d37874f9bce13351db2f56796
RNLocalize: 82a569022724d35461e2dc5b5d015a13c3ca995b
RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43
RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494
RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da
RNVectorIcons: da6fe858f5a65d7bbc3379540a889b0b12aa5976
RNVectorIcons: 31cebfcf94e8cf8686eb5303ae0357da64d7a5a4
SDWebImage: cf6922231e95550934da2ada0f20f2becf2ceba9
SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8
TOCropViewController: 20a14b6a7a098308bf369e7c8d700dc983a974e6
@ -1003,6 +1006,6 @@ SPEC CHECKSUMS:
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 0351d0973911a397f1cb4e45f0b0f590b14816e3
PODFILE CHECKSUM: 31a91f8b33d73b8680ec7d53c60c76a243f70a20
COCOAPODS: 1.9.3
COCOAPODS: 1.10.1

View File

@ -22,6 +22,14 @@
#import <FirebaseAnalytics/FirebaseAnalytics.h>
#endif
#if __has_include(<FirebaseAppCheck/FirebaseAppCheck.h>)
#import <FirebaseAppCheck/FirebaseAppCheck.h>
#endif
#if __has_include(<FirebaseAppDistribution/FirebaseAppDistribution.h>)
#import <FirebaseAppDistribution/FirebaseAppDistribution.h>
#endif
#if __has_include(<FirebaseAuth/FirebaseAuth.h>)
#import <FirebaseAuth/FirebaseAuth.h>
#endif
@ -36,13 +44,6 @@
#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>)
@ -55,118 +56,26 @@ Firebase Dynamic Links works as intended."
#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>
#if __has_include(<FirebaseInstallations/FirebaseInstallations.h>)
#import <FirebaseInstallations/FirebaseInstallations.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)

View File

@ -3,8 +3,12 @@
[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase)
[![Actions Status][gh-abtesting-badge]][gh-actions]
[![Actions Status][gh-appcheck-badge]][gh-actions]
[![Actions Status][gh-appdistribution-badge]][gh-actions]
[![Actions Status][gh-auth-badge]][gh-actions]
[![Actions Status][gh-cocoapods-integration-badge]][gh-actions]
[![Actions Status][gh-core-badge]][gh-actions]
[![Actions Status][gh-core-diagnostics-badge]][gh-actions]
[![Actions Status][gh-crashlytics-badge]][gh-actions]
[![Actions Status][gh-database-badge]][gh-actions]
[![Actions Status][gh-datatransport-badge]][gh-actions]
@ -12,9 +16,13 @@
[![Actions Status][gh-firebasepod-badge]][gh-actions]
[![Actions Status][gh-firestore-badge]][gh-actions]
[![Actions Status][gh-functions-badge]][gh-actions]
[![Actions Status][gh-google-utilities-badge]][gh-actions]
[![Actions Status][gh-google-utilities-components-badge]][gh-actions]
[![Actions Status][gh-inappmessaging-badge]][gh-actions]
[![Actions Status][gh-interop-badge]][gh-actions]
[![Actions Status][gh-messaging-badge]][gh-actions]
[![Actions Status][gh-mlmodeldownloader-badge]][gh-actions]
[![Actions Status][gh-performance-badge]][gh-actions]
[![Actions Status][gh-remoteconfig-badge]][gh-actions]
[![Actions Status][gh-storage-badge]][gh-actions]
[![Actions Status][gh-symbolcollision-badge]][gh-actions]
@ -23,21 +31,23 @@
# Firebase Apple Open Source Development
This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics,
FirebasePerformance, and FirebaseML.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics
and FirebaseML.
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).
The repository also includes GoogleUtilities and GoogleDataTransport source
which are utilities used by Firebase and other Google products.
**Note** _FirebaseCombineSwift_ contains support for Apple's Combine framework. This module is currently under development, and not yet supported for use in production environments. Fore more details, please refer to the [docs](FirebaseCombineSwift/README.md).
## Installation
See the three subsections for details about three different installation methods.
See the subsections below for details about the different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Swift Package Manager](SwiftPackageManager.md)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
@ -46,11 +56,12 @@ See the three subsections for details about three different installation methods
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
### Swift Package Manager
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).
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
found at [SwiftPackageManager.md](SwiftPackageManager.md).
### Installing from GitHub
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
@ -67,14 +78,14 @@ All of the official releases are tagged in this repo and available via CocoaPods
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
```ruby
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:
```
```ruby
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
@ -82,12 +93,8 @@ 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).
[Carthage](Carthage.md). If you have a new Mac with an Apple silicon chip, please see
[these instructions](AppleSilicon.md).
### Using Firebase from a Framework or a library
@ -98,13 +105,22 @@ Instructions for installing binary frameworks via
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.3 (or later)
* CocoaPods 1.7.2 (or later)
* Xcode 12.2 (or later)
CocoaPods is still the canonical way to develop, but much of the repo now supports
development with Swift Package Manager.
### CocoaPods
Install
* CocoaPods 1.10.0 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios`
```ruby
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
```
Note: If the CocoaPods cache is out of date, you may need to run
`pod repo update` before the `pod gen` command.
@ -116,7 +132,7 @@ CocoaPods workspaces.
Firestore has a self contained Xcode project. See
[Firestore/README.md](Firestore/README.md).
### Development for Catalyst
#### Development for Catalyst
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
* Check the Mac box in the App-iOS Build Settings
* Sign the App in the Settings Signing & Capabilities tab
@ -125,6 +141,18 @@ Firestore has a self contained Xcode project. See
* Select the Unit-unit scheme
* Run it to build and test
Alternatively disable signing in each target:
* Go to Build Settings tab
* Click `+`
* Select `Add User-Defined Setting`
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
### Swift Package Manager
* `open Package.swift` or double click `Package.swift` in Finder.
* Xcode will open the project
* Choose a scheme for a library to build or test suite to run
* Choose a target platform by selecting the run destination along with the scheme
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
@ -136,40 +164,24 @@ See [HeadersImports.md](HeadersImports.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)
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/check.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:
GitHub Actions will verify that any code changes are done in a style compliant
way. Install `clang-format` and `mint`:
```console
brew install clang-format@12
brew install mint
```
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
To update this section, find the versions of clang-format and swiftformat.rb to
match the versions in the CI failure logs
[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage (Deprecated)
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
In order to run the sample apps and integration tests, you'll need a valid
`GoogleService-Info.plist` file. 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:
@ -177,12 +189,11 @@ files without real values, but can be replaced with real plist files. To get you
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/));
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
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.
### Coverage Report Generation
See [scripts/code_coverage_report/README.md](scripts/code_coverage_report/README.md).
## Specific Component Instructions
See the sections below for any special instructions for those components.
@ -202,10 +213,16 @@ To run against a local emulator instance, invoke `./scripts/run_database_emulato
running the integration test.
To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to
`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
running.
### Firebase Performance Monitoring
If you're doing specific Firebase Performance Monitoring development, see
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
integrating Performance with the dev test App.
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
@ -219,7 +236,8 @@ 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)
[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.
@ -229,43 +247,39 @@ The iOS Simulator cannot register for remote notifications, and will not receive
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
## Building with Firebase on Apple platforms
At this time, not all of Firebase's products are available across all Apple platforms. However,
Firebase is constantly evolving and community supported efforts have helped expand Firebase's support.
To keep up with the latest info regarding Firebase's support across Apple platforms, refer to
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
in Firebase's documentation.
### 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.
### tvOS, macOS, watchOS and Catalyst
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on
tvOS, macOS, watchOS and Catalyst.
#### tvOS, macOS, watchOS and Catalyst
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
work on tvOS, macOS, watchOS and Catalyst.
For tvOS, checkout the [Sample](Example/tvOSSample).
For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the
[Independent Watch App Sample](Example/watchOSSample).
For tvOS, see the [Sample](Example/tvOSSample).
For watchOS, currently only Messaging, Storage and Crashlytics (and their dependencies) have limited
support. See the [Independent Watch App Sample](Example/watchOSSample).
Keep in mind that macOS, tvOS, watchOS and Catalyst 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, tvOS or watchOS. If you
encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and
this repository is actively developed primarily for iOS. While we can catch basic unit test issues
with GitHub Actions, there may be some changes where the SDK no longer works as expected on macOS,
tvOS or watchOS. If you encounter this, please
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
During app setup in the console, you may get to a step that mentions something like "Checking if the app
has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst.
During app setup in the console, you may get to a step that mentions something like "Checking if the
app has communicated with our servers". This relies on Analytics and will not work on
macOS/tvOS/watchOS/Catalyst.
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
To install, add a subset of the following to the Podfile:
```
pod 'Firebase/ABTesting' # No watchOS support yet
pod 'Firebase/Auth' # No watchOS support yet
pod 'Firebase/Crashlytics' # No watchOS support yet
pod 'Firebase/Database' # No watchOS support yet
pod 'Firebase/Firestore' # No watchOS support yet
pod 'Firebase/Functions' # No watchOS support yet
pod 'Firebase/Messaging'
pod 'Firebase/RemoteConfig' # No watchOS support yet
pod 'Firebase/Storage'
```
#### Additional Catalyst Notes
* FirebaseAuth and FirebaseMessaging require adding `Keychain Sharing Capability`
@ -273,6 +287,10 @@ to Build Settings.
* FirebaseFirestore requires signing the
[gRPC Resource target](https://github.com/firebase/firebase-ios-sdk/issues/3500#issuecomment-518741681).
#### Additional Crashlytics Notes
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
@ -293,8 +311,12 @@ Your use of Firebase is governed by the
[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions
[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg
[gh-appcheck-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/app_check/badge.svg
[gh-appdistribution-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/appdistribution/badge.svg
[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg
[gh-cocoapods-integration-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/cocoapods-integration/badge.svg
[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg
[gh-core-diagnostics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core-diagnostics/badge.svg
[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg
[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg
[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg
@ -302,9 +324,13 @@ Your use of Firebase is governed by the
[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg
[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg
[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg
[gh-google-utilities-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/google-utilities/badge.svg
[gh-google-utilities-components-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/google-utilities-components/badge.svg
[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg
[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg
[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg
[gh-mlmodeldownloader-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/mlmodeldownloader/badge.svg
[gh-performance-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/performance/badge.svg
[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg
[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg
[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg

View File

@ -1,11 +0,0 @@
framework module FIRAnalyticsConnector {
export *
module * { export * }
link "sqlite3"
link "z"
link framework "CoreData"
link framework "Security"
link framework "StoreKit"
link framework "SystemConfiguration"
link framework "UIKit"
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_i386_x86_64-simulator</string>
<key>LibraryPath</key>
<string>FirebaseAnalytics.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>i386</string>
<string>x86_64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict>
<dict>
<key>LibraryIdentifier</key>
<string>ios-arm64_armv7</string>
<key>LibraryPath</key>
<string>FirebaseAnalytics.framework</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
<string>armv7</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@ -0,0 +1,33 @@
#import <Foundation/Foundation.h>
#import "FIRAnalytics.h"
NS_ASSUME_NONNULL_BEGIN
/// The type of consent to set. Supported consent types are `ConsentType.adStorage` and
/// `ConsentType.analyticsStorage`. Omitting a type retains its previous status.
typedef NSString *FIRConsentType NS_TYPED_ENUM NS_SWIFT_NAME(ConsentType);
extern FIRConsentType const FIRConsentTypeAdStorage;
extern FIRConsentType const FIRConsentTypeAnalyticsStorage;
/// The status value of the consent type. Supported statuses are `ConsentStatus.granted` and
/// `ConsentStatus.denied`.
typedef NSString *FIRConsentStatus NS_TYPED_ENUM NS_SWIFT_NAME(ConsentStatus);
extern FIRConsentStatus const FIRConsentStatusDenied;
extern FIRConsentStatus const FIRConsentStatusGranted;
/// Sets the applicable end user consent state.
@interface FIRAnalytics (Consent)
/// Sets the applicable end user consent state (e.g. for device identifiers) for this app on this
/// device. Use the consent settings to specify individual consent type values. Settings are
/// persisted across app sessions. By default consent types are set to `ConsentStatus.granted`.
///
/// @param consentSettings An NSDictionary of consent types. Supported consent type keys are
/// `ConsentType.adStorage` and `ConsentType.analyticsStorage`. Valid values are
/// `ConsentStatus.granted` and `ConsentStatus.denied`.
+ (void)setConsent:(NSDictionary<FIRConsentType, FIRConsentStatus> *)consentSettings;
@end
NS_ASSUME_NONNULL_END

View File

@ -25,22 +25,34 @@ NS_SWIFT_NAME(Analytics)
/// <li>ad_activeview</li>
/// <li>ad_click</li>
/// <li>ad_exposure</li>
/// <li>ad_impression</li>
/// <li>ad_query</li>
/// <li>ad_reward</li>
/// <li>adunit_exposure</li>
/// <li>app_background</li>
/// <li>app_clear_data</li>
/// <li>app_exception</li>
/// <li>app_remove</li>
/// <li>app_store_refund</li>
/// <li>app_store_subscription_cancel</li>
/// <li>app_store_subscription_convert</li>
/// <li>app_store_subscription_renew</li>
/// <li>app_update</li>
/// <li>app_upgrade</li>
/// <li>dynamic_link_app_open</li>
/// <li>dynamic_link_app_update</li>
/// <li>dynamic_link_first_open</li>
/// <li>error</li>
/// <li>firebase_campaign</li>
/// <li>first_open</li>
/// <li>first_visit</li>
/// <li>in_app_purchase</li>
/// <li>notification_dismiss</li>
/// <li>notification_foreground</li>
/// <li>notification_open</li>
/// <li>notification_receive</li>
/// <li>os_update</li>
/// <li>screen_view</li>
/// <li>session_start</li>
/// <li>session_start_with_rollout</li>
/// <li>user_engagement</li>
/// </ul>
///
@ -49,7 +61,7 @@ NS_SWIFT_NAME(Analytics)
/// reserved. See FIREventNames.h for the list of reserved event names. The "firebase_",
/// "google_", and "ga_" prefixes are reserved and should not be used. Note that event names are
/// case-sensitive and that logging two events whose names differ only in case will result in
/// two distinct events.
/// two distinct events. To manually log screen view events, use the `screen_view` event name.
/// @param parameters The dictionary of event parameters. Passing nil indicates that the event has
/// no parameters. Parameter names can be up to 40 characters long and must start with an
/// alphabetic character and contain only alphanumeric characters and underscores. Only NSString
@ -85,33 +97,6 @@ NS_SWIFT_NAME(Analytics)
/// non-empty and no more than 256 characters long. Setting userID to nil removes the user ID.
+ (void)setUserID:(nullable NSString *)userID;
/// Sets the current screen name, which specifies the current visual context in your app. This helps
/// identify the areas in your app where users spend their time and how they interact with your app.
/// Must be called on the main thread.
///
/// Note that screen reporting is enabled automatically and records the class name of the current
/// UIViewController for you without requiring you to call this method. The class name can
/// optionally be overridden by calling this method in the viewDidAppear callback of your
/// UIViewController and specifying the screenClassOverride parameter.
/// `setScreenName:screenClass:` must be called after `[super viewDidAppear:]`.
///
/// If your app does not use a distinct UIViewController for each screen, you should call this
/// method and specify a distinct screenName each time a new screen is presented to the user.
///
/// The screen name and screen class remain in effect until the current UIViewController changes or
/// a new call to setScreenName:screenClass: is made.
///
/// @warning If you override `viewDidAppear:` in your UIViewController but do not call
/// `[super viewDidAppear:]`, that screen class will not be tracked.
///
/// @param screenName The name of the current screen. Should contain 1 to 100 characters. Set to nil
/// to clear the current screen name.
/// @param screenClassOverride The name of the screen class. Should contain 1 to 100 characters. By
/// default this is the class name of the current UIViewController. Set to nil to revert to the
/// default class name.
+ (void)setScreenName:(nullable NSString *)screenName
screenClass:(nullable NSString *)screenClassOverride;
/// Sets whether analytics collection is enabled for this app on this device. This setting is
/// persisted across app sessions. By default it is enabled.
///
@ -125,13 +110,33 @@ NS_SWIFT_NAME(Analytics)
/// session terminates.
+ (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval;
/// The unique ID for this instance of the application.
+ (NSString *)appInstanceID;
/// Returns the unique ID for this instance of the application or nil if
/// `ConsentType.analyticsStorage` has been set to `ConsentStatus.denied`.
///
/// @see `FIRAnalytics+Consent.h`
+ (nullable NSString *)appInstanceID;
/// Clears all analytics data for this instance from the device and resets the app instance ID.
/// FIRAnalyticsConfiguration values will be reset to the default values.
+ (void)resetAnalyticsData;
/// Adds parameters that will be set on every event logged from the SDK, including automatic ones.
/// The values passed in the parameters dictionary will be added to the dictionary of default event
/// parameters. These parameters persist across app runs. They are of lower precedence than event
/// parameters, so if an event parameter and a parameter set using this API have the same name, the
/// value of the event parameter will be used. The same limitations on event parameters apply to
/// default event parameters.
///
/// @param parameters Parameters to be added to the dictionary of parameters added to every event.
/// They will be added to the dictionary of default event parameters, replacing any existing
/// parameter with the same name. Valid parameters are NSString and NSNumber (signed 64-bit
/// integer and 64-bit floating-point number). Setting a key's value to [NSNull null] will clear
/// that parameter. Passing in a nil dictionary will clear all parameters.
+ (void)setDefaultEventParameters:(nullable NSDictionary<NSString *, id> *)parameters;
/// Unavailable.
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@ -52,6 +52,21 @@ static NSString *const kFIREventAddToCart NS_SWIFT_NAME(AnalyticsEventAddToCart)
static NSString *const kFIREventAddToWishlist NS_SWIFT_NAME(AnalyticsEventAddToWishlist) =
@"add_to_wishlist";
/// Ad Impression event. This event signifies when a user sees an ad impression. Note: If you supply
/// the @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency parameter
/// so that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterAdPlatform (NSString) (optional)</li>
/// <li>@c kFIRParameterAdFormat (NSString) (optional)</li>
/// <li>@c kFIRParameterAdSource (NSString) (optional)</li>
/// <li>@c kFIRParameterAdUnitName (NSString) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventAdImpression NS_SWIFT_NAME(AnalyticsEventAdImpression) =
@"ad_impression";
/// App Open event. By logging this event when an App becomes active, developers can understand how
/// often users leave and return during the course of a Session. Although Sessions are automatically
/// reported, this event can provide further clarification around the continuous engagement of
@ -246,6 +261,15 @@ static NSString *const kFIREventPurchaseRefund NS_SWIFT_NAME(AnalyticsEventPurch
static NSString *const kFIREventRemoveFromCart NS_SWIFT_NAME(AnalyticsEventRemoveFromCart) =
@"remove_from_cart";
/// Screen View event. This event signifies a screen view. Use this when a screen transition occurs.
/// This event can be logged irrespective of whether automatic screen tracking is enabled. Params:
///
/// <ul>
/// <li>@c kFIRParameterScreenClass (NSString) (optional)</li>
/// <li>@c kFIRParameterScreenName (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventScreenView NS_SWIFT_NAME(AnalyticsEventScreenView) = @"screen_view";
/// Search event. Apps that support search features can use this event to contextualize search
/// operations by supplying the appropriate, corresponding parameters. This event can help you
/// identify the most popular content in your app. Params:

View File

@ -22,6 +22,17 @@
static NSString *const kFIRParameterAchievementID NS_SWIFT_NAME(AnalyticsParameterAchievementID) =
@"achievement_id";
/// The ad format (e.g. Banner, Interstitial, Rewarded, Native, Rewarded Interstitial, Instream).
/// (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdFormat : @"Banner",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdFormat NS_SWIFT_NAME(AnalyticsParameterAdFormat) =
@"ad_format";
/// Ad Network Click ID (NSString). Used for network-specific click IDs which vary in format.
/// <pre>
/// NSDictionary *params = @{
@ -32,6 +43,36 @@ static NSString *const kFIRParameterAchievementID NS_SWIFT_NAME(AnalyticsParamet
static NSString *const kFIRParameterAdNetworkClickID
NS_SWIFT_NAME(AnalyticsParameterAdNetworkClickID) = @"aclid";
/// The ad platform (e.g. MoPub, IronSource) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdPlatform : @"MoPub",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdPlatform NS_SWIFT_NAME(AnalyticsParameterAdPlatform) =
@"ad_platform";
/// The ad source (e.g. AdColony) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdSource : @"AdColony",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdSource NS_SWIFT_NAME(AnalyticsParameterAdSource) =
@"ad_source";
/// The ad unit name (e.g. Banner_03) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdUnitName : @"Banner_03",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdUnitName NS_SWIFT_NAME(AnalyticsParameterAdUnitName) =
@"ad_unit_name";
/// A product affiliation to designate a supplying company or brick and mortar store location
/// (NSString). <pre>
/// NSDictionary *params = @{
@ -368,6 +409,26 @@ static NSString *const kFIRParameterQuantity NS_SWIFT_NAME(AnalyticsParameterQua
/// </pre>
static NSString *const kFIRParameterScore NS_SWIFT_NAME(AnalyticsParameterScore) = @"score";
/// Current screen class, such as the class name of the UIViewController, logged with screen_view
/// event and added to every event (NSString). <pre>
/// NSDictionary *params = @{
/// kFIRParameterScreenClass : @"LoginViewController",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterScreenClass NS_SWIFT_NAME(AnalyticsParameterScreenClass) =
@"screen_class";
/// Current screen name, such as the name of the UIViewController, logged with screen_view event and
/// added to every event (NSString). <pre>
/// NSDictionary *params = @{
/// kFIRParameterScreenName : @"LoginView",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterScreenName NS_SWIFT_NAME(AnalyticsParameterScreenName) =
@"screen_name";
/// The search string/keywords used (NSString).
/// <pre>
/// NSDictionary *params = @{

View File

@ -1,4 +1,5 @@
#import "FIRAnalytics+AppDelegate.h"
#import "FIRAnalytics+Consent.h"
#import "FIRAnalytics.h"
#import "FIREventNames.h"
#import "FIRParameterNames.h"

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>FirebaseAnalytics</string>
<key>CFBundleIdentifier</key>
<string>com.firebase.Firebase-FirebaseAnalytics</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>FirebaseAnalytics</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleVersion</key>
<string>8.0.0</string>
<key>DTSDKName</key>
<string>iphonesimulator11.2</string>
</dict>
</plist>

View File

@ -1,12 +1,13 @@
framework module FirebaseAnalytics {
umbrella header "FirebaseAnalytics.h"
export *
module * { export * }
link "sqlite3"
link "z"
link framework "CoreData"
umbrella header "FirebaseAnalytics.h"
export *
module * { export * }
link framework "CoreTelephony"
link framework "Foundation"
link framework "Security"
link framework "StoreKit"
link framework "SystemConfiguration"
link framework "UIKit"
link "c++"
link "sqlite3"
link "z"
}

View File

@ -0,0 +1,62 @@
#import <Foundation/Foundation.h>
#import "FIRAnalytics.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Provides App Delegate handlers to be used in your App Delegate.
*
* To save time integrating Firebase Analytics in an application, Firebase Analytics does not
* require delegation implementation from the AppDelegate. Instead this is automatically done by
* Firebase Analytics. Should you choose instead to delegate manually, you can turn off the App
* Delegate Proxy by adding FirebaseAppDelegateProxyEnabled into your app's Info.plist and setting
* it to NO, and adding the methods in this category to corresponding delegation handlers.
*
* To handle Universal Links, you must return YES in
* [UIApplicationDelegate application:didFinishLaunchingWithOptions:].
*/
@interface FIRAnalytics (AppDelegate)
/**
* Handles events related to a URL session that are waiting to be processed.
*
* For optimal use of Firebase Analytics, call this method from the
* [UIApplicationDelegate application:handleEventsForBackgroundURLSession:completionHandler]
* method of the app delegate in your app.
*
* @param identifier The identifier of the URL session requiring attention.
* @param completionHandler The completion handler to call when you finish processing the events.
* Calling this completion handler lets the system know that your app's user interface is
* updated and a new snapshot can be taken.
*/
+ (void)handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(nullable void (^)(void))completionHandler;
/**
* Handles the event when the app is launched by a URL.
*
* Call this method from [UIApplicationDelegate application:openURL:options:] &#40;on iOS 9.0 and
* above&#41;, or [UIApplicationDelegate application:openURL:sourceApplication:annotation:] &#40;on
* iOS 8.x and below&#41; in your app.
*
* @param url The URL resource to open. This resource can be a network resource or a file.
*/
+ (void)handleOpenURL:(NSURL *)url;
/**
* Handles the event when the app receives data associated with user activity that includes a
* Universal Link (on iOS 9.0 and above).
*
* Call this method from [UIApplication continueUserActivity:restorationHandler:] in your app
* delegate (on iOS 9.0 and above).
*
* @param userActivity The activity object containing the data associated with the task the user
* was performing.
*/
+ (void)handleUserActivity:(id)userActivity;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,33 @@
#import <Foundation/Foundation.h>
#import "FIRAnalytics.h"
NS_ASSUME_NONNULL_BEGIN
/// The type of consent to set. Supported consent types are `ConsentType.adStorage` and
/// `ConsentType.analyticsStorage`. Omitting a type retains its previous status.
typedef NSString *FIRConsentType NS_TYPED_ENUM NS_SWIFT_NAME(ConsentType);
extern FIRConsentType const FIRConsentTypeAdStorage;
extern FIRConsentType const FIRConsentTypeAnalyticsStorage;
/// The status value of the consent type. Supported statuses are `ConsentStatus.granted` and
/// `ConsentStatus.denied`.
typedef NSString *FIRConsentStatus NS_TYPED_ENUM NS_SWIFT_NAME(ConsentStatus);
extern FIRConsentStatus const FIRConsentStatusDenied;
extern FIRConsentStatus const FIRConsentStatusGranted;
/// Sets the applicable end user consent state.
@interface FIRAnalytics (Consent)
/// Sets the applicable end user consent state (e.g. for device identifiers) for this app on this
/// device. Use the consent settings to specify individual consent type values. Settings are
/// persisted across app sessions. By default consent types are set to `ConsentStatus.granted`.
///
/// @param consentSettings An NSDictionary of consent types. Supported consent type keys are
/// `ConsentType.adStorage` and `ConsentType.analyticsStorage`. Valid values are
/// `ConsentStatus.granted` and `ConsentStatus.denied`.
+ (void)setConsent:(NSDictionary<FIRConsentType, FIRConsentStatus> *)consentSettings;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,142 @@
#import <Foundation/Foundation.h>
#import "FIREventNames.h"
#import "FIRParameterNames.h"
#import "FIRUserPropertyNames.h"
NS_ASSUME_NONNULL_BEGIN
/// The top level Firebase Analytics singleton that provides methods for logging events and setting
/// user properties. See <a href="http://goo.gl/gz8SLz">the developer guides</a> for general
/// information on using Firebase Analytics in your apps.
///
/// @note The Analytics SDK uses SQLite to persist events and other app-specific data. Calling
/// certain thread-unsafe global SQLite methods like `sqlite3_shutdown()` can result in
/// unexpected crashes at runtime.
NS_SWIFT_NAME(Analytics)
@interface FIRAnalytics : NSObject
/// Logs an app event. The event can have up to 25 parameters. Events with the same name must have
/// the same parameters. Up to 500 event names are supported. Using predefined events and/or
/// parameters is recommended for optimal reporting.
///
/// The following event names are reserved and cannot be used:
/// <ul>
/// <li>ad_activeview</li>
/// <li>ad_click</li>
/// <li>ad_exposure</li>
/// <li>ad_query</li>
/// <li>ad_reward</li>
/// <li>adunit_exposure</li>
/// <li>app_background</li>
/// <li>app_clear_data</li>
/// <li>app_exception</li>
/// <li>app_remove</li>
/// <li>app_store_refund</li>
/// <li>app_store_subscription_cancel</li>
/// <li>app_store_subscription_convert</li>
/// <li>app_store_subscription_renew</li>
/// <li>app_update</li>
/// <li>app_upgrade</li>
/// <li>dynamic_link_app_open</li>
/// <li>dynamic_link_app_update</li>
/// <li>dynamic_link_first_open</li>
/// <li>error</li>
/// <li>firebase_campaign</li>
/// <li>first_open</li>
/// <li>first_visit</li>
/// <li>in_app_purchase</li>
/// <li>notification_dismiss</li>
/// <li>notification_foreground</li>
/// <li>notification_open</li>
/// <li>notification_receive</li>
/// <li>os_update</li>
/// <li>session_start</li>
/// <li>session_start_with_rollout</li>
/// <li>user_engagement</li>
/// </ul>
///
/// @param name The name of the event. Should contain 1 to 40 alphanumeric characters or
/// underscores. The name must start with an alphabetic character. Some event names are
/// reserved. See FIREventNames.h for the list of reserved event names. The "firebase_",
/// "google_", and "ga_" prefixes are reserved and should not be used. Note that event names are
/// case-sensitive and that logging two events whose names differ only in case will result in
/// two distinct events. To manually log screen view events, use the `screen_view` event name.
/// @param parameters The dictionary of event parameters. Passing nil indicates that the event has
/// no parameters. Parameter names can be up to 40 characters long and must start with an
/// alphabetic character and contain only alphanumeric characters and underscores. Only NSString
/// and NSNumber (signed 64-bit integer and 64-bit floating-point number) parameter types are
/// supported. NSString parameter values can be up to 100 characters long. The "firebase_",
/// "google_", and "ga_" prefixes are reserved and should not be used for parameter names.
+ (void)logEventWithName:(NSString *)name
parameters:(nullable NSDictionary<NSString *, id> *)parameters
NS_SWIFT_NAME(logEvent(_:parameters:));
/// Sets a user property to a given value. Up to 25 user property names are supported. Once set,
/// user property values persist throughout the app lifecycle and across sessions.
///
/// The following user property names are reserved and cannot be used:
/// <ul>
/// <li>first_open_time</li>
/// <li>last_deep_link_referrer</li>
/// <li>user_id</li>
/// </ul>
///
/// @param value The value of the user property. Values can be up to 36 characters long. Setting the
/// value to nil removes the user property.
/// @param name The name of the user property to set. Should contain 1 to 24 alphanumeric characters
/// or underscores and must start with an alphabetic character. The "firebase_", "google_", and
/// "ga_" prefixes are reserved and should not be used for user property names.
+ (void)setUserPropertyString:(nullable NSString *)value forName:(NSString *)name
NS_SWIFT_NAME(setUserProperty(_:forName:));
/// Sets the user ID property. This feature must be used in accordance with
/// <a href="https://www.google.com/policies/privacy">Google's Privacy Policy</a>
///
/// @param userID The user ID to ascribe to the user of this app on this device, which must be
/// non-empty and no more than 256 characters long. Setting userID to nil removes the user ID.
+ (void)setUserID:(nullable NSString *)userID;
/// Sets whether analytics collection is enabled for this app on this device. This setting is
/// persisted across app sessions. By default it is enabled.
///
/// @param analyticsCollectionEnabled A flag that enables or disables Analytics collection.
+ (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled;
/// Sets the interval of inactivity in seconds that terminates the current session. The default
/// value is 1800 seconds (30 minutes).
///
/// @param sessionTimeoutInterval The custom time of inactivity in seconds before the current
/// session terminates.
+ (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval;
/// Returns the unique ID for this instance of the application or nil if
/// `ConsentType.analyticsStorage` has been set to `ConsentStatus.denied`.
///
/// @see `FIRAnalytics+Consent.h`
+ (nullable NSString *)appInstanceID;
/// Clears all analytics data for this instance from the device and resets the app instance ID.
/// FIRAnalyticsConfiguration values will be reset to the default values.
+ (void)resetAnalyticsData;
/// Adds parameters that will be set on every event logged from the SDK, including automatic ones.
/// The values passed in the parameters dictionary will be added to the dictionary of default event
/// parameters. These parameters persist across app runs. They are of lower precedence than event
/// parameters, so if an event parameter and a parameter set using this API have the same name, the
/// value of the event parameter will be used. The same limitations on event parameters apply to
/// default event parameters.
///
/// @param parameters Parameters to be added to the dictionary of parameters added to every event.
/// They will be added to the dictionary of default event parameters, replacing any existing
/// parameter with the same name. Valid parameters are NSString and NSNumber (signed 64-bit
/// integer and 64-bit floating-point number). Setting a key's value to [NSNull null] will clear
/// that parameter. Passing in a nil dictionary will clear all parameters.
+ (void)setDefaultEventParameters:(nullable NSDictionary<NSString *, id> *)parameters;
/// Unavailable.
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,495 @@
/// @file FIREventNames.h
///
/// Predefined event names.
///
/// An Event is an important occurrence in your app that you want to measure. You can report up to
/// 500 different types of Events per app and you can associate up to 25 unique parameters with each
/// Event type. Some common events are suggested below, but you may also choose to specify custom
/// Event types that are associated with your specific app. Each event type is identified by a
/// unique name. Event names can be up to 40 characters long, may only contain alphanumeric
/// characters and underscores ("_"), and must start with an alphabetic character. The "firebase_",
/// "google_", and "ga_" prefixes are reserved and should not be used.
#import <Foundation/Foundation.h>
/// Add Payment Info event. This event signifies that a user has submitted their payment
/// information. Note: If you supply the @c kFIRParameterValue parameter, you must also supply the
/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCoupon (NSString) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterPaymentType (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventAddPaymentInfo NS_SWIFT_NAME(AnalyticsEventAddPaymentInfo) =
@"add_payment_info";
/// E-Commerce Add To Cart event. This event signifies that an item(s) was added to a cart for
/// purchase. Add this event to a funnel with @c kFIREventPurchase to gauge the effectiveness of
/// your checkout process. Note: If you supply the @c kFIRParameterValue parameter, you must also
/// supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
/// accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventAddToCart NS_SWIFT_NAME(AnalyticsEventAddToCart) = @"add_to_cart";
/// E-Commerce Add To Wishlist event. This event signifies that an item was added to a wishlist. Use
/// this event to identify popular gift items. Note: If you supply the @c kFIRParameterValue
/// parameter, you must also supply the @c kFIRParameterCurrency parameter so that revenue metrics
/// can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventAddToWishlist NS_SWIFT_NAME(AnalyticsEventAddToWishlist) =
@"add_to_wishlist";
/// Ad Impression event. This event signifies when a user sees an ad impression. Note: If you supply
/// the @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency parameter
/// so that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterAdPlatform (NSString) (optional)</li>
/// <li>@c kFIRParameterAdFormat (NSString) (optional)</li>
/// <li>@c kFIRParameterAdSource (NSString) (optional)</li>
/// <li>@c kFIRParameterAdUnitName (NSString) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventAdImpression NS_SWIFT_NAME(AnalyticsEventAdImpression) =
@"ad_impression";
/// App Open event. By logging this event when an App becomes active, developers can understand how
/// often users leave and return during the course of a Session. Although Sessions are automatically
/// reported, this event can provide further clarification around the continuous engagement of
/// app-users.
static NSString *const kFIREventAppOpen NS_SWIFT_NAME(AnalyticsEventAppOpen) = @"app_open";
/// E-Commerce Begin Checkout event. This event signifies that a user has begun the process of
/// checking out. Add this event to a funnel with your @c kFIREventPurchase event to gauge the
/// effectiveness of your checkout process. Note: If you supply the @c kFIRParameterValue parameter,
/// you must also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be
/// computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCoupon (NSString) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventBeginCheckout NS_SWIFT_NAME(AnalyticsEventBeginCheckout) =
@"begin_checkout";
/// Campaign Detail event. Log this event to supply the referral details of a re-engagement
/// campaign. Note: you must supply at least one of the required parameters kFIRParameterSource,
/// kFIRParameterMedium or kFIRParameterCampaign. Params:
///
/// <ul>
/// <li>@c kFIRParameterSource (NSString)</li>
/// <li>@c kFIRParameterMedium (NSString)</li>
/// <li>@c kFIRParameterCampaign (NSString)</li>
/// <li>@c kFIRParameterTerm (NSString) (optional)</li>
/// <li>@c kFIRParameterContent (NSString) (optional)</li>
/// <li>@c kFIRParameterAdNetworkClickID (NSString) (optional)</li>
/// <li>@c kFIRParameterCP1 (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventCampaignDetails NS_SWIFT_NAME(AnalyticsEventCampaignDetails) =
@"campaign_details";
/// Checkout progress. Params:
///
/// <ul>
/// <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li>
/// <li>@c kFIRParameterCheckoutOption (NSString) (optional)</li>
/// </ul>
/// <b>This constant has been deprecated.</b>
static NSString *const kFIREventCheckoutProgress NS_SWIFT_NAME(AnalyticsEventCheckoutProgress) =
@"checkout_progress";
/// Earn Virtual Currency event. This event tracks the awarding of virtual currency in your app. Log
/// this along with @c kFIREventSpendVirtualCurrency to better understand your virtual economy.
/// Params:
///
/// <ul>
/// <li>@c kFIRParameterVirtualCurrencyName (NSString)</li>
/// <li>@c kFIRParameterValue (signed 64-bit integer or double as NSNumber)</li>
/// </ul>
static NSString *const kFIREventEarnVirtualCurrency
NS_SWIFT_NAME(AnalyticsEventEarnVirtualCurrency) = @"earn_virtual_currency";
/// E-Commerce Purchase event. This event signifies that an item was purchased by a user. Note:
/// This is different from the in-app purchase event, which is reported automatically for App
/// Store-based apps. Note: If you supply the @c kFIRParameterValue parameter, you must also
/// supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
/// accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
/// <li>@c kFIRParameterTax (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterShipping (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterCoupon (NSString) (optional)</li>
/// <li>@c kFIRParameterLocation (NSString) (optional)</li>
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
/// hotel bookings</li>
/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
/// hotel bookings</li>
/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
/// for travel bookings</li>
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
/// </ul>
/// <b>This constant has been deprecated. Use @c kFIREventPurchase constant instead.</b>
static NSString *const kFIREventEcommercePurchase NS_SWIFT_NAME(AnalyticsEventEcommercePurchase) =
@"ecommerce_purchase";
/// Generate Lead event. Log this event when a lead has been generated in the app to understand the
/// efficacy of your install and re-engagement campaigns. Note: If you supply the
/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency
/// parameter so that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventGenerateLead NS_SWIFT_NAME(AnalyticsEventGenerateLead) =
@"generate_lead";
/// Join Group event. Log this event when a user joins a group such as a guild, team or family. Use
/// this event to analyze how popular certain groups or social features are in your app. Params:
///
/// <ul>
/// <li>@c kFIRParameterGroupID (NSString)</li>
/// </ul>
static NSString *const kFIREventJoinGroup NS_SWIFT_NAME(AnalyticsEventJoinGroup) = @"join_group";
/// Level End event. Log this event when the user finishes a level. Params:
///
/// <ul>
/// <li>@c kFIRParameterLevelName (NSString)</li>
/// <li>@c kFIRParameterSuccess (NSString)</li>
/// </ul>
static NSString *const kFIREventLevelEnd NS_SWIFT_NAME(AnalyticsEventLevelEnd) = @"level_end";
/// Level Start event. Log this event when the user starts a new level. Params:
///
/// <ul>
/// <li>@c kFIRParameterLevelName (NSString)</li>
/// </ul>
static NSString *const kFIREventLevelStart NS_SWIFT_NAME(AnalyticsEventLevelStart) = @"level_start";
/// Level Up event. This event signifies that a player has leveled up in your gaming app. It can
/// help you gauge the level distribution of your userbase and help you identify certain levels that
/// are difficult to pass. Params:
///
/// <ul>
/// <li>@c kFIRParameterLevel (signed 64-bit integer as NSNumber)</li>
/// <li>@c kFIRParameterCharacter (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventLevelUp NS_SWIFT_NAME(AnalyticsEventLevelUp) = @"level_up";
/// Login event. Apps with a login feature can report this event to signify that a user has logged
/// in.
static NSString *const kFIREventLogin NS_SWIFT_NAME(AnalyticsEventLogin) = @"login";
/// Post Score event. Log this event when the user posts a score in your gaming app. This event can
/// help you understand how users are actually performing in your game and it can help you correlate
/// high scores with certain audiences or behaviors. Params:
///
/// <ul>
/// <li>@c kFIRParameterScore (signed 64-bit integer as NSNumber)</li>
/// <li>@c kFIRParameterLevel (signed 64-bit integer as NSNumber) (optional)</li>
/// <li>@c kFIRParameterCharacter (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventPostScore NS_SWIFT_NAME(AnalyticsEventPostScore) = @"post_score";
/// Present Offer event. This event signifies that the app has presented a purchase offer to a user.
/// Add this event to a funnel with the kFIREventAddToCart and kFIREventEcommercePurchase to gauge
/// your conversion process. Note: If you supply the @c kFIRParameterValue parameter, you must
/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
/// accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterQuantity (signed 64-bit integer as NSNumber)</li>
/// <li>@c kFIRParameterItemID (NSString)</li>
/// <li>@c kFIRParameterItemName (NSString)</li>
/// <li>@c kFIRParameterItemCategory (NSString)</li>
/// <li>@c kFIRParameterItemLocationID (NSString) (optional)</li>
/// <li>@c kFIRParameterPrice (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
/// <b>This constant has been deprecated. Use @c kFIREventViewPromotion constant instead.</b>
static NSString *const kFIREventPresentOffer NS_SWIFT_NAME(AnalyticsEventPresentOffer) =
@"present_offer";
/// E-Commerce Purchase Refund event. This event signifies that an item purchase was refunded.
/// Note: If you supply the @c kFIRParameterValue parameter, you must also supply the
/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately.
/// Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
/// </ul>
/// <b>This constant has been deprecated. Use @c kFIREventRefund constant instead.</b>
static NSString *const kFIREventPurchaseRefund NS_SWIFT_NAME(AnalyticsEventPurchaseRefund) =
@"purchase_refund";
/// E-Commerce Remove from Cart event. This event signifies that an item(s) was removed from a cart.
/// Note: If you supply the @c kFIRParameterValue parameter, you must also supply the @c
/// kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventRemoveFromCart NS_SWIFT_NAME(AnalyticsEventRemoveFromCart) =
@"remove_from_cart";
/// Screen View event. This event signifies a screen view. Use this when a screen transition occurs.
/// This event can be logged irrespective of whether automatic screen tracking is enabled. Params:
///
/// <ul>
/// <li>@c kFIRParameterScreenClass (NSString) (optional)</li>
/// <li>@c kFIRParameterScreenName (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventScreenView NS_SWIFT_NAME(AnalyticsEventScreenView) = @"screen_view";
/// Search event. Apps that support search features can use this event to contextualize search
/// operations by supplying the appropriate, corresponding parameters. This event can help you
/// identify the most popular content in your app. Params:
///
/// <ul>
/// <li>@c kFIRParameterSearchTerm (NSString)</li>
/// <li>@c kFIRParameterStartDate (NSString) (optional)</li>
/// <li>@c kFIRParameterEndDate (NSString) (optional)</li>
/// <li>@c kFIRParameterNumberOfNights (signed 64-bit integer as NSNumber) (optional) for
/// hotel bookings</li>
/// <li>@c kFIRParameterNumberOfRooms (signed 64-bit integer as NSNumber) (optional) for
/// hotel bookings</li>
/// <li>@c kFIRParameterNumberOfPassengers (signed 64-bit integer as NSNumber) (optional)
/// for travel bookings</li>
/// <li>@c kFIRParameterOrigin (NSString) (optional)</li>
/// <li>@c kFIRParameterDestination (NSString) (optional)</li>
/// <li>@c kFIRParameterTravelClass (NSString) (optional) for travel bookings</li>
/// </ul>
static NSString *const kFIREventSearch NS_SWIFT_NAME(AnalyticsEventSearch) = @"search";
/// Select Content event. This general purpose event signifies that a user has selected some content
/// of a certain type in an app. The content can be any object in your app. This event can help you
/// identify popular content and categories of content in your app. Params:
///
/// <ul>
/// <li>@c kFIRParameterContentType (NSString)</li>
/// <li>@c kFIRParameterItemID (NSString)</li>
/// </ul>
static NSString *const kFIREventSelectContent NS_SWIFT_NAME(AnalyticsEventSelectContent) =
@"select_content";
/// Set checkout option. Params:
///
/// <ul>
/// <li>@c kFIRParameterCheckoutStep (unsigned 64-bit integer as NSNumber)</li>
/// <li>@c kFIRParameterCheckoutOption (NSString)</li>
/// </ul>
/// <b>This constant has been deprecated.</b>
static NSString *const kFIREventSetCheckoutOption NS_SWIFT_NAME(AnalyticsEventSetCheckoutOption) =
@"set_checkout_option";
/// Share event. Apps with social features can log the Share event to identify the most viral
/// content. Params:
///
/// <ul>
/// <li>@c kFIRParameterContentType (NSString)</li>
/// <li>@c kFIRParameterItemID (NSString)</li>
/// </ul>
static NSString *const kFIREventShare NS_SWIFT_NAME(AnalyticsEventShare) = @"share";
/// Sign Up event. This event indicates that a user has signed up for an account in your app. The
/// parameter signifies the method by which the user signed up. Use this event to understand the
/// different behaviors between logged in and logged out users. Params:
///
/// <ul>
/// <li>@c kFIRParameterSignUpMethod (NSString)</li>
/// </ul>
static NSString *const kFIREventSignUp NS_SWIFT_NAME(AnalyticsEventSignUp) = @"sign_up";
/// Spend Virtual Currency event. This event tracks the sale of virtual goods in your app and can
/// help you identify which virtual goods are the most popular objects of purchase. Params:
///
/// <ul>
/// <li>@c kFIRParameterItemName (NSString)</li>
/// <li>@c kFIRParameterVirtualCurrencyName (NSString)</li>
/// <li>@c kFIRParameterValue (signed 64-bit integer or double as NSNumber)</li>
/// </ul>
static NSString *const kFIREventSpendVirtualCurrency
NS_SWIFT_NAME(AnalyticsEventSpendVirtualCurrency) = @"spend_virtual_currency";
/// Tutorial Begin event. This event signifies the start of the on-boarding process in your app. Use
/// this in a funnel with kFIREventTutorialComplete to understand how many users complete this
/// process and move on to the full app experience.
static NSString *const kFIREventTutorialBegin NS_SWIFT_NAME(AnalyticsEventTutorialBegin) =
@"tutorial_begin";
/// Tutorial End event. Use this event to signify the user's completion of your app's on-boarding
/// process. Add this to a funnel with kFIREventTutorialBegin to gauge the completion rate of your
/// on-boarding process.
static NSString *const kFIREventTutorialComplete NS_SWIFT_NAME(AnalyticsEventTutorialComplete) =
@"tutorial_complete";
/// Unlock Achievement event. Log this event when the user has unlocked an achievement in your
/// game. Since achievements generally represent the breadth of a gaming experience, this event can
/// help you understand how many users are experiencing all that your game has to offer. Params:
///
/// <ul>
/// <li>@c kFIRParameterAchievementID (NSString)</li>
/// </ul>
static NSString *const kFIREventUnlockAchievement NS_SWIFT_NAME(AnalyticsEventUnlockAchievement) =
@"unlock_achievement";
/// View Item event. This event signifies that a user has viewed an item. Use the appropriate
/// parameters to contextualize the event. Use this event to discover the most popular items viewed
/// in your app. Note: If you supply the @c kFIRParameterValue parameter, you must also supply the
/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventViewItem NS_SWIFT_NAME(AnalyticsEventViewItem) = @"view_item";
/// View Item List event. Log this event when a user sees a list of items or offerings. Params:
///
/// <ul>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterItemListID (NSString) (optional)</li>
/// <li>@c kFIRParameterItemListName (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewItemList) =
@"view_item_list";
/// View Search Results event. Log this event when the user has been presented with the results of a
/// search. Params:
///
/// <ul>
/// <li>@c kFIRParameterSearchTerm (NSString)</li>
/// </ul>
static NSString *const kFIREventViewSearchResults NS_SWIFT_NAME(AnalyticsEventViewSearchResults) =
@"view_search_results";
/// Add Shipping Info event. This event signifies that a user has submitted their shipping
/// information. Note: If you supply the @c kFIRParameterValue parameter, you must also supply the
/// @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCoupon (NSString) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterShippingTier (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventAddShippingInfo NS_SWIFT_NAME(AnalyticsEventAddShippingInfo) =
@"add_shipping_info";
/// E-Commerce Purchase event. This event signifies that an item(s) was purchased by a user. Note:
/// This is different from the in-app purchase event, which is reported automatically for App
/// Store-based apps. Note: If you supply the @c kFIRParameterValue parameter, you must also supply
/// the @c kFIRParameterCurrency parameter so that revenue metrics can be computed accurately.
/// Params:
///
/// <ul>
/// <li>@c kFIRParameterAffiliation (NSString) (optional)</li>
/// <li>@c kFIRParameterCoupon (NSString) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterShipping (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterTax (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventPurchase NS_SWIFT_NAME(AnalyticsEventPurchase) = @"purchase";
/// E-Commerce Refund event. This event signifies that a refund was issued. Note: If you supply the
/// @c kFIRParameterValue parameter, you must also supply the @c kFIRParameterCurrency parameter so
/// that revenue metrics can be computed accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterAffiliation (NSString) (optional)</li>
/// <li>@c kFIRParameterCoupon (NSString) (optional)</li>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterShipping (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterTax (double as NSNumber) (optional)</li>
/// <li>@c kFIRParameterTransactionID (NSString) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventRefund NS_SWIFT_NAME(AnalyticsEventRefund) = @"refund";
/// Select Item event. This event signifies that an item was selected by a user from a list. Use the
/// appropriate parameters to contextualize the event. Use this event to discover the most popular
/// items selected. Params:
///
/// <ul>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterItemListID (NSString) (optional)</li>
/// <li>@c kFIRParameterItemListName (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventSelectItem NS_SWIFT_NAME(AnalyticsEventSelectItem) = @"select_item";
/// Select promotion event. This event signifies that a user has selected a promotion offer. Use the
/// appropriate parameters to contextualize the event, such as the item(s) for which the promotion
/// applies. Params:
///
/// <ul>
/// <li>@c kFIRParameterCreativeName (NSString) (optional)</li>
/// <li>@c kFIRParameterCreativeSlot (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterLocationID (NSString) (optional)</li>
/// <li>@c kFIRParameterPromotionID (NSString) (optional)</li>
/// <li>@c kFIRParameterPromotionName (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventSelectPromotion NS_SWIFT_NAME(AnalyticsEventSelectPromotion) =
@"select_promotion";
/// E-commerce View Cart event. This event signifies that a user has viewed their cart. Use this to
/// analyze your purchase funnel. Note: If you supply the @c kFIRParameterValue parameter, you must
/// also supply the @c kFIRParameterCurrency parameter so that revenue metrics can be computed
/// accurately. Params:
///
/// <ul>
/// <li>@c kFIRParameterCurrency (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterValue (double as NSNumber) (optional)</li>
/// </ul>
static NSString *const kFIREventViewCart NS_SWIFT_NAME(AnalyticsEventViewCart) = @"view_cart";
/// View Promotion event. This event signifies that a promotion was shown to a user. Add this event
/// to a funnel with the @c kFIREventAddToCart and @c kFIREventPurchase to gauge your conversion
/// process. Params:
///
/// <ul>
/// <li>@c kFIRParameterCreativeName (NSString) (optional)</li>
/// <li>@c kFIRParameterCreativeSlot (NSString) (optional)</li>
/// <li>@c kFIRParameterItems (NSArray) (optional)</li>
/// <li>@c kFIRParameterLocationID (NSString) (optional)</li>
/// <li>@c kFIRParameterPromotionID (NSString) (optional)</li>
/// <li>@c kFIRParameterPromotionName (NSString) (optional)</li>
/// </ul>
static NSString *const kFIREventViewPromotion NS_SWIFT_NAME(AnalyticsEventViewPromotion) =
@"view_promotion";

View File

@ -0,0 +1,731 @@
/// @file FIRParameterNames.h
///
/// Predefined event parameter names.
///
/// Params supply information that contextualize Events. You can associate up to 25 unique Params
/// with each Event type. Some Params are suggested below for certain common Events, but you are
/// not limited to these. You may supply extra Params for suggested Events or custom Params for
/// Custom events. Param names can be up to 40 characters long, may only contain alphanumeric
/// characters and underscores ("_"), and must start with an alphabetic character. Param values can
/// be up to 100 characters long. The "firebase_", "google_", and "ga_" prefixes are reserved and
/// should not be used.
#import <Foundation/Foundation.h>
/// Game achievement ID (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAchievementID : @"10_matches_won",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAchievementID NS_SWIFT_NAME(AnalyticsParameterAchievementID) =
@"achievement_id";
/// The ad format (e.g. Banner, Interstitial, Rewarded, Native, Rewarded Interstitial, Instream).
/// (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdFormat : @"Banner",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdFormat NS_SWIFT_NAME(AnalyticsParameterAdFormat) =
@"ad_format";
/// Ad Network Click ID (NSString). Used for network-specific click IDs which vary in format.
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdNetworkClickID : @"1234567",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdNetworkClickID
NS_SWIFT_NAME(AnalyticsParameterAdNetworkClickID) = @"aclid";
/// The ad platform (e.g. MoPub, IronSource) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdPlatform : @"MoPub",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdPlatform NS_SWIFT_NAME(AnalyticsParameterAdPlatform) =
@"ad_platform";
/// The ad source (e.g. AdColony) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdSource : @"AdColony",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdSource NS_SWIFT_NAME(AnalyticsParameterAdSource) =
@"ad_source";
/// The ad unit name (e.g. Banner_03) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterAdUnitName : @"Banner_03",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAdUnitName NS_SWIFT_NAME(AnalyticsParameterAdUnitName) =
@"ad_unit_name";
/// A product affiliation to designate a supplying company or brick and mortar store location
/// (NSString). <pre>
/// NSDictionary *params = @{
/// kFIRParameterAffiliation : @"Google Store",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterAffiliation NS_SWIFT_NAME(AnalyticsParameterAffiliation) =
@"affiliation";
/// 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 kFIRParameterCampaign NS_SWIFT_NAME(AnalyticsParameterCampaign) =
@"campaign";
/// Character used in game (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCharacter : @"beat_boss",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterCharacter NS_SWIFT_NAME(AnalyticsParameterCharacter) =
@"character";
/// The checkout step (1..N) (unsigned 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCheckoutStep : @"1",
/// // ...
/// };
/// </pre>
/// <b>This constant has been deprecated.</b>
static NSString *const kFIRParameterCheckoutStep NS_SWIFT_NAME(AnalyticsParameterCheckoutStep) =
@"checkout_step";
/// Some option on a step in an ecommerce flow (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCheckoutOption : @"Visa",
/// // ...
/// };
/// </pre>
/// <b>This constant has been deprecated.</b>
static NSString *const kFIRParameterCheckoutOption
NS_SWIFT_NAME(AnalyticsParameterCheckoutOption) = @"checkout_option";
/// Campaign content (NSString).
static NSString *const kFIRParameterContent NS_SWIFT_NAME(AnalyticsParameterContent) = @"content";
/// Type of content selected (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterContentType : @"news article",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterContentType NS_SWIFT_NAME(AnalyticsParameterContentType) =
@"content_type";
/// Coupon code used for a purchase (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCoupon : @"SUMMER_FUN",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterCoupon NS_SWIFT_NAME(AnalyticsParameterCoupon) = @"coupon";
/// Campaign custom parameter (NSString). Used as a method of capturing custom data in a campaign.
/// Use varies by network.
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCP1 : @"custom_data",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterCP1 NS_SWIFT_NAME(AnalyticsParameterCP1) = @"cp1";
/// The name of a creative used in a promotional spot (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCreativeName : @"Summer Sale",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterCreativeName NS_SWIFT_NAME(AnalyticsParameterCreativeName) =
@"creative_name";
/// The name of a creative slot (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCreativeSlot : @"summer_banner2",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterCreativeSlot NS_SWIFT_NAME(AnalyticsParameterCreativeSlot) =
@"creative_slot";
/// Currency of the purchase or items associated with the event, in 3-letter
/// <a href="http://en.wikipedia.org/wiki/ISO_4217#Active_codes"> ISO_4217</a> format (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterCurrency : @"USD",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterCurrency NS_SWIFT_NAME(AnalyticsParameterCurrency) =
@"currency";
/// Flight or Travel destination (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterDestination : @"Mountain View, CA",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterDestination NS_SWIFT_NAME(AnalyticsParameterDestination) =
@"destination";
/// The arrival date, check-out date or rental end date for the item. This should be in
/// YYYY-MM-DD format (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterEndDate : @"2015-09-14",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterEndDate NS_SWIFT_NAME(AnalyticsParameterEndDate) = @"end_date";
/// Flight number for travel events (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterFlightNumber : @"ZZ800",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterFlightNumber NS_SWIFT_NAME(AnalyticsParameterFlightNumber) =
@"flight_number";
/// Group/clan/guild ID (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterGroupID : @"g1",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterGroupID NS_SWIFT_NAME(AnalyticsParameterGroupID) = @"group_id";
/// The index of the item in a list (signed 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterIndex : @(5),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterIndex NS_SWIFT_NAME(AnalyticsParameterIndex) = @"index";
/// Item brand (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemBrand : @"Google",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemBrand NS_SWIFT_NAME(AnalyticsParameterItemBrand) =
@"item_brand";
/// Item category (context-specific) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemCategory : @"pants",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemCategory NS_SWIFT_NAME(AnalyticsParameterItemCategory) =
@"item_category";
/// Item ID (context-specific) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemID : @"SKU_12345",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemID NS_SWIFT_NAME(AnalyticsParameterItemID) = @"item_id";
/// The Google <a href="https://developers.google.com/places/place-id">Place ID</a> (NSString) that
/// corresponds to the associated item. Alternatively, you can supply your own custom Location ID.
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
/// // ...
/// };
/// </pre>
/// <b>This constant has been deprecated. Use @c kFIRParameterLocationID constant instead.</b>
static NSString *const kFIRParameterItemLocationID
NS_SWIFT_NAME(AnalyticsParameterItemLocationID) = @"item_location_id";
/// Item Name (context-specific) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemName : @"jeggings",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemName NS_SWIFT_NAME(AnalyticsParameterItemName) =
@"item_name";
/// The list in which the item was presented to the user (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemList : @"Search Results",
/// // ...
/// };
/// </pre>
/// <b>This constant has been deprecated. Use @c kFIRParameterItemListName constant instead.</b>
static NSString *const kFIRParameterItemList NS_SWIFT_NAME(AnalyticsParameterItemList) =
@"item_list";
/// Item variant (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemVariant : @"Black",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemVariant NS_SWIFT_NAME(AnalyticsParameterItemVariant) =
@"item_variant";
/// Level in game (signed 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterLevel : @(42),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterLevel NS_SWIFT_NAME(AnalyticsParameterLevel) = @"level";
/// Location (NSString). The Google <a href="https://developers.google.com/places/place-id">Place ID
/// </a> that corresponds to the associated event. Alternatively, you can supply your own custom
/// Location ID.
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterLocation : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterLocation NS_SWIFT_NAME(AnalyticsParameterLocation) =
@"location";
/// The advertising or marketing medium, for example: cpc, banner, email, push. Highly recommended
/// (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterMedium : @"email",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterMedium NS_SWIFT_NAME(AnalyticsParameterMedium) = @"medium";
/// Number of nights staying at hotel (signed 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterNumberOfNights : @(3),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterNumberOfNights
NS_SWIFT_NAME(AnalyticsParameterNumberOfNights) = @"number_of_nights";
/// Number of passengers traveling (signed 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterNumberOfPassengers : @(11),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterNumberOfPassengers
NS_SWIFT_NAME(AnalyticsParameterNumberOfPassengers) = @"number_of_passengers";
/// Number of rooms for travel events (signed 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterNumberOfRooms : @(2),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterNumberOfRooms NS_SWIFT_NAME(AnalyticsParameterNumberOfRooms) =
@"number_of_rooms";
/// Flight or Travel origin (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterOrigin : @"Mountain View, CA",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterOrigin NS_SWIFT_NAME(AnalyticsParameterOrigin) = @"origin";
/// Purchase price (double as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterPrice : @(1.0),
/// kFIRParameterCurrency : @"USD", // e.g. $1.00 USD
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterPrice NS_SWIFT_NAME(AnalyticsParameterPrice) = @"price";
/// Purchase quantity (signed 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterQuantity : @(1),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterQuantity NS_SWIFT_NAME(AnalyticsParameterQuantity) =
@"quantity";
/// Score in game (signed 64-bit integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterScore : @(4200),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterScore NS_SWIFT_NAME(AnalyticsParameterScore) = @"score";
/// Current screen class, such as the class name of the UIViewController, logged with screen_view
/// event and added to every event (NSString). <pre>
/// NSDictionary *params = @{
/// kFIRParameterScreenClass : @"LoginViewController",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterScreenClass NS_SWIFT_NAME(AnalyticsParameterScreenClass) =
@"screen_class";
/// Current screen name, such as the name of the UIViewController, logged with screen_view event and
/// added to every event (NSString). <pre>
/// NSDictionary *params = @{
/// kFIRParameterScreenName : @"LoginView",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterScreenName NS_SWIFT_NAME(AnalyticsParameterScreenName) =
@"screen_name";
/// The search string/keywords used (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterSearchTerm : @"periodic table",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterSearchTerm NS_SWIFT_NAME(AnalyticsParameterSearchTerm) =
@"search_term";
/// Shipping cost associated with a transaction (double as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterShipping : @(5.99),
/// kFIRParameterCurrency : @"USD", // e.g. $5.99 USD
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterShipping NS_SWIFT_NAME(AnalyticsParameterShipping) =
@"shipping";
/// Sign up method (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterSignUpMethod : @"google",
/// // ...
/// };
/// </pre>
///
/// <b>This constant has been deprecated. Use Method constant instead.</b>
static NSString *const kFIRParameterSignUpMethod NS_SWIFT_NAME(AnalyticsParameterSignUpMethod) =
@"sign_up_method";
/// A particular approach used in an operation; for example, "facebook" or "email" in the context
/// of a sign_up or login event. (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterMethod : @"google",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterMethod NS_SWIFT_NAME(AnalyticsParameterMethod) = @"method";
/// 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 kFIRParameterSource NS_SWIFT_NAME(AnalyticsParameterSource) = @"source";
/// The departure date, check-in date or rental start date for the item. This should be in
/// YYYY-MM-DD format (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterStartDate : @"2015-09-14",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterStartDate NS_SWIFT_NAME(AnalyticsParameterStartDate) =
@"start_date";
/// Tax cost associated with a transaction (double as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterTax : @(2.43),
/// kFIRParameterCurrency : @"USD", // e.g. $2.43 USD
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterTax NS_SWIFT_NAME(AnalyticsParameterTax) = @"tax";
/// If you're manually tagging keyword campaigns, you should use utm_term to specify the keyword
/// (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterTerm : @"game",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterTerm NS_SWIFT_NAME(AnalyticsParameterTerm) = @"term";
/// The unique identifier of a transaction (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterTransactionID : @"T12345",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterTransactionID NS_SWIFT_NAME(AnalyticsParameterTransactionID) =
@"transaction_id";
/// Travel class (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterTravelClass : @"business",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterTravelClass NS_SWIFT_NAME(AnalyticsParameterTravelClass) =
@"travel_class";
/// A context-specific numeric value which is accumulated automatically for each event type. This is
/// a general purpose parameter that is useful for accumulating a key metric that pertains to an
/// event. Examples include revenue, distance, time and points. Value should be specified as signed
/// 64-bit integer or double as NSNumber. Notes: Values for pre-defined currency-related events
/// (such as @c kFIREventAddToCart) should be supplied using double as NSNumber and must be
/// accompanied by a @c kFIRParameterCurrency parameter. The valid range of accumulated values is
/// [-9,223,372,036,854.77, 9,223,372,036,854.77]. Supplying a non-numeric value, omitting the
/// corresponding @c kFIRParameterCurrency parameter, or supplying an invalid
/// <a href="https://goo.gl/qqX3J2">currency code</a> for conversion events will cause that
/// conversion to be omitted from reporting.
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterValue : @(3.99),
/// kFIRParameterCurrency : @"USD", // e.g. $3.99 USD
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterValue NS_SWIFT_NAME(AnalyticsParameterValue) = @"value";
/// Name of virtual currency type (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterVirtualCurrencyName : @"virtual_currency_name",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterVirtualCurrencyName
NS_SWIFT_NAME(AnalyticsParameterVirtualCurrencyName) = @"virtual_currency_name";
/// The name of a level in a game (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterLevelName : @"room_1",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterLevelName NS_SWIFT_NAME(AnalyticsParameterLevelName) =
@"level_name";
/// The result of an operation. Specify 1 to indicate success and 0 to indicate failure (unsigned
/// integer as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterSuccess : @(1),
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterSuccess NS_SWIFT_NAME(AnalyticsParameterSuccess) = @"success";
/// Indicates that the associated event should either extend the current session
/// or start a new session if no session was active when the event was logged.
/// Specify YES to extend the current session or to start a new session; any
/// other value will not extend or start a session.
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterExtendSession : @YES,
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterExtendSession NS_SWIFT_NAME(AnalyticsParameterExtendSession) =
@"extend_session";
/// Monetary value of discount associated with a purchase (double as NSNumber).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterDiscount : @(2.0),
/// kFIRParameterCurrency : @"USD", // e.g. $2.00 USD
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterDiscount NS_SWIFT_NAME(AnalyticsParameterDiscount) =
@"discount";
/// Item Category (context-specific) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemCategory2 : @"pants",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemCategory2 NS_SWIFT_NAME(AnalyticsParameterItemCategory2) =
@"item_category2";
/// Item Category (context-specific) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemCategory3 : @"pants",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemCategory3 NS_SWIFT_NAME(AnalyticsParameterItemCategory3) =
@"item_category3";
/// Item Category (context-specific) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemCategory4 : @"pants",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemCategory4 NS_SWIFT_NAME(AnalyticsParameterItemCategory4) =
@"item_category4";
/// Item Category (context-specific) (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemCategory5 : @"pants",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemCategory5 NS_SWIFT_NAME(AnalyticsParameterItemCategory5) =
@"item_category5";
/// The ID of the list in which the item was presented to the user (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemListID : @"ABC123",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemListID NS_SWIFT_NAME(AnalyticsParameterItemListID) =
@"item_list_id";
/// The name of the list in which the item was presented to the user (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItemListName : @"Related products",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterItemListName NS_SWIFT_NAME(AnalyticsParameterItemListName) =
@"item_list_name";
/// The list of items involved in the transaction. (NSArray).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterItems : @[
/// @{kFIRParameterItemName : @"jeggings", kFIRParameterItemCategory : @"pants"},
/// @{kFIRParameterItemName : @"boots", kFIRParameterItemCategory : @"shoes"},
/// ],
/// };
/// </pre>
static NSString *const kFIRParameterItems NS_SWIFT_NAME(AnalyticsParameterItems) = @"items";
/// The location associated with the event. Preferred to be the Google
/// <a href="https://developers.google.com/places/place-id">Place ID</a> that corresponds to the
/// associated item but could be overridden to a custom location ID string.(NSString). <pre>
/// NSDictionary *params = @{
/// kFIRParameterLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterLocationID NS_SWIFT_NAME(AnalyticsParameterLocationID) =
@"location_id";
/// The chosen method of payment (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterPaymentType : @"Visa",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterPaymentType NS_SWIFT_NAME(AnalyticsParameterPaymentType) =
@"payment_type";
/// The ID of a product promotion (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterPromotionID : @"ABC123",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterPromotionID NS_SWIFT_NAME(AnalyticsParameterPromotionID) =
@"promotion_id";
/// The name of a product promotion (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterPromotionName : @"Summer Sale",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterPromotionName NS_SWIFT_NAME(AnalyticsParameterPromotionName) =
@"promotion_name";
/// The shipping tier (e.g. Ground, Air, Next-day) selected for delivery of the purchased item
/// (NSString).
/// <pre>
/// NSDictionary *params = @{
/// kFIRParameterShippingTier : @"Ground",
/// // ...
/// };
/// </pre>
static NSString *const kFIRParameterShippingTier NS_SWIFT_NAME(AnalyticsParameterShippingTier) =
@"shipping_tier";

View File

@ -0,0 +1,29 @@
/// @file FIRUserPropertyNames.h
///
/// Predefined user property names.
///
/// A UserProperty is an attribute that describes the app-user. By supplying UserProperties, you can
/// later analyze different behaviors of various segments of your userbase. You may supply up to 25
/// unique UserProperties per app, and you can use the name and value of your choosing for each one.
/// UserProperty names can be up to 24 characters long, may only contain alphanumeric characters and
/// underscores ("_"), and must start with an alphabetic character. UserProperty values can be up to
/// 36 characters long. The "firebase_", "google_", and "ga_" prefixes are reserved and should not
/// be used.
#import <Foundation/Foundation.h>
/// The method used to sign in. For example, "google", "facebook" or "twitter".
static NSString *const kFIRUserPropertySignUpMethod
NS_SWIFT_NAME(AnalyticsUserPropertySignUpMethod) = @"sign_up_method";
/// Indicates whether events logged by Google Analytics can be used to personalize ads for the user.
/// Set to "YES" to enable, or "NO" to disable. Default is enabled. See the
/// <a href="https://firebase.google.com/support/guides/disable-analytics">documentation</a> for
/// more details and information about related settings.
///
/// <pre>
/// [FIRAnalytics setUserPropertyString:@"NO"
/// forName:kFIRUserPropertyAllowAdPersonalizationSignals];
/// </pre>
static NSString *const kFIRUserPropertyAllowAdPersonalizationSignals
NS_SWIFT_NAME(AnalyticsUserPropertyAllowAdPersonalizationSignals) = @"allow_personalized_ads";

View File

@ -0,0 +1,6 @@
#import "FIRAnalytics+AppDelegate.h"
#import "FIRAnalytics+Consent.h"
#import "FIRAnalytics.h"
#import "FIREventNames.h"
#import "FIRParameterNames.h"
#import "FIRUserPropertyNames.h"

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>FirebaseAnalytics</string>
<key>CFBundleIdentifier</key>
<string>com.firebase.Firebase-FirebaseAnalytics</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>FirebaseAnalytics</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleVersion</key>
<string>8.0.0</string>
<key>DTSDKName</key>
<string>iphonesimulator11.2</string>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
framework module FirebaseAnalytics {
umbrella header "FirebaseAnalytics.h"
export *
module * { export * }
link framework "CoreTelephony"
link framework "Foundation"
link framework "Security"
link framework "SystemConfiguration"
link framework "UIKit"
link "c++"
link "sqlite3"
link "z"
}

View File

@ -22,18 +22,20 @@
#import <AppKit/AppKit.h>
#endif
#import "FirebaseCore/Sources/Public/FIRApp.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRApp.h"
#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h"
#import "FirebaseCore/Sources/FIRBundleUtil.h"
#import "FirebaseCore/Sources/FIRComponentContainerInternal.h"
#import "FirebaseCore/Sources/FIRConfigurationInternal.h"
#import "FirebaseCore/Sources/FIRVersion.h"
#import "FirebaseCore/Sources/FIRFirebaseUserAgent.h"
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
#import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h"
#import "FirebaseCore/Sources/Private/FIRLibrary.h"
#import "FirebaseCore/Sources/Private/FIRLogger.h"
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
@ -71,8 +73,6 @@ NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat =
NSString *const kFIRGlobalAppDataCollectionEnabledPlistKey =
@"FirebaseDataCollectionDefaultEnabled";
NSString *const kFIRAppDiagnosticsNotification = @"FIRAppDiagnosticsNotification";
NSString *const kFIRAppDiagnosticsConfigurationTypeKey = @"ConfigType";
NSString *const kFIRAppDiagnosticsErrorKey = @"Error";
NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRApp";
@ -90,6 +90,14 @@ NSString *const FIRAuthStateDidChangeInternalNotificationTokenKey =
NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey =
@"FIRAuthStateDidChangeInternalNotificationUIDKey";
/**
* Error domain for exceptions and NSError construction.
*/
NSString *const kFirebaseCoreErrorDomain = @"com.firebase.core";
/** The NSUserDefaults suite name for FirebaseCore, for those storage locations that use it. */
NSString *const kFirebaseCoreDefaultsSuiteName = @"com.firebase.core";
/**
* The URL to download plist files.
*/
@ -116,8 +124,6 @@ static NSMutableArray<Class<FIRLibrary>> *sRegisteredAsConfigurable;
static NSMutableDictionary *sAllApps;
static FIRApp *sDefaultApp;
static NSMutableDictionary *sLibraryVersions;
static dispatch_once_t sFirebaseUserAgentOnceToken;
+ (void)configure {
FIROptions *options = [FIROptions defaultOptions];
@ -277,9 +283,7 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
sDefaultApp = nil;
[sAllApps removeAllObjects];
sAllApps = nil;
[sLibraryVersions removeAllObjects];
sLibraryVersions = nil;
sFirebaseUserAgentOnceToken = 0;
[[self userAgent] reset];
}
}
@ -345,9 +349,6 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
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]) {
@ -368,7 +369,6 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
}
}
}
#endif
[self subscribeForAppDidBecomeActiveNotifications];
@ -479,20 +479,7 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
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];
return [NSError errorWithDomain:kFirebaseCoreErrorDomain code:-100 userInfo:errorDict];
}
+ (NSError *)errorForInvalidAppID {
@ -502,9 +489,7 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
@"Check formatting and location of GoogleService-Info.plist or GoogleAppID set in the "
@"customized options."
};
return [NSError errorWithDomain:kFirebaseCoreErrorDomain
code:FIRErrorCodeInvalidAppID
userInfo:errorDict];
return [NSError errorWithDomain:kFirebaseCoreErrorDomain code:-101 userInfo:errorDict];
}
+ (BOOL)isDefaultAppConfigured {
@ -520,12 +505,7 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
// 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;
}
[[self userAgent] setValue:version forComponent:name];
} else {
FIRLogError(kFIRLoggerCore, @"I-COR000027",
@"The library name (%@) or version number (%@) contain invalid characters. "
@ -534,6 +514,11 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
}
}
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name {
[self registerInternalLibrary:library withName:name withVersion:FIRFirebaseVersion()];
}
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name
withVersion:(nonnull NSString *)version {
@ -561,73 +546,18 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
[self registerLibrary:name withVersion:version];
}
+ (FIRFirebaseUserAgent *)userAgent {
static dispatch_once_t onceToken;
static FIRFirebaseUserAgent *_userAgent;
dispatch_once(&onceToken, ^{
_userAgent = [[FIRFirebaseUserAgent alloc] init];
[_userAgent setValue:FIRFirebaseVersion() forComponent:@"fire-ios"];
});
return _userAgent;
}
+ (NSString *)firebaseUserAgent {
@synchronized(self) {
dispatch_once(&sFirebaseUserAgentOnceToken, ^{
// 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];
}
NSString *swiftFlagValue = [self hasSwiftRuntime] ? @"true" : @"false";
[FIRApp registerLibrary:@"swift" withVersion:swiftFlagValue];
[FIRApp registerLibrary:kFIRAppDiagnosticsApplePlatformPrefix
withVersion:[self applePlatform]];
});
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:@" "];
}
}
+ (BOOL)hasSwiftRuntime {
// The class
// [Swift._SwiftObject](https://github.com/apple/swift/blob/5eac3e2818eb340b11232aff83edfbd1c307fa03/stdlib/public/runtime/SwiftObject.h#L35)
// is a part of Swift runtime, so it should be present if Swift runtime is available.
BOOL hasSwiftRuntime =
objc_lookUpClass("Swift._SwiftObject") != nil ||
// Swift object class name before
// https://github.com/apple/swift/commit/9637b4a6e11ddca72f5f6dbe528efc7c92f14d01
objc_getClass("_TtCs12_SwiftObject") != nil;
return hasSwiftRuntime;
}
+ (NSString *)applePlatform {
NSString *applePlatform = @"unknown";
// When a Catalyst app is run on macOS then both `TARGET_OS_MACCATALYST` and `TARGET_OS_IOS` are
// `true`, which means the condition list is order-sensitive.
#if TARGET_OS_MACCATALYST
applePlatform = @"maccatalyst";
#elif TARGET_OS_IOS
applePlatform = @"ios";
#elif TARGET_OS_TV
applePlatform = @"tvos";
#elif TARGET_OS_OSX
applePlatform = @"macos";
#elif TARGET_OS_WATCH
applePlatform = @"watchos";
#endif
return applePlatform;
return [[self userAgent] firebaseUserAgent];
}
- (void)checkExpectedBundleID {
@ -897,17 +827,6 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
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 {
@ -931,7 +850,9 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
- (void)logCoreTelemetryIfEnabled {
if ([self isDataCollectionDefaultEnabled]) {
[FIRCoreDiagnosticsConnector logCoreTelemetryWithOptions:_options];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
[FIRCoreDiagnosticsConnector logCoreTelemetryWithOptions:[self options]];
});
}
}

View File

@ -15,8 +15,8 @@
*/
#import <Foundation/Foundation.h>
#import "FirebaseCore/Sources/Private/FIRComponent.h"
#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
#import "FirebaseCore/Sources/Private/FIRLibrary.h"
@class FIRApp;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
#import "FIRConfiguration.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRConfiguration.h"
@class FIRAnalyticsConfiguration;

View File

@ -18,7 +18,7 @@
#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h"
#import "FirebaseCore/Sources/Public/FIROptions.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIROptions.h"
#import "FirebaseCore/Sources/FIRDiagnosticsData.h"
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"

View File

@ -16,7 +16,7 @@
#import "FirebaseCore/Sources/FIRDiagnosticsData.h"
#import "FirebaseCore/Sources/Public/FIRApp.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRApp.h"
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"

View File

@ -1,21 +0,0 @@
// 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/Sources/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";

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Google
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,18 +18,18 @@
NS_ASSUME_NONNULL_BEGIN
/** This class represents a future data object, determined at instantiation time. */
@interface GDTCORDataFuture : NSObject <NSSecureCoding>
@interface FIRFirebaseUserAgent : NSObject
/** If not nil, this data future was instantiated with this file URL. */
@property(nullable, readonly, nonatomic) NSURL *fileURL;
/** Returns the firebase user agent which consists of environment part and the components added via
* `setValue:forComponent` method. */
- (NSString *)firebaseUserAgent;
/** Initializes an instance with the given the fileURL.
*
* @param fileURL The fileURL containing the data to return in -data.
* @return An instance of this class.
*/
- (instancetype)initWithFileURL:(NSURL *)fileURL;
/** Sets value associated with the specified component. If value is `nil` then the component is
* removed. */
- (void)setValue:(nullable NSString *)value forComponent:(NSString *)componentName;
/** Resets manually added components. */
- (void)reset;
@end

View File

@ -0,0 +1,110 @@
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FirebaseCore/Sources/FIRFirebaseUserAgent.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
@interface FIRFirebaseUserAgent ()
@property(nonatomic, readonly) NSMutableDictionary<NSString *, NSString *> *valuesByComponent;
@property(nonatomic, readonly) NSDictionary<NSString *, NSString *> *environmentComponents;
@property(nonatomic, readonly) NSString *firebaseUserAgent;
@end
@implementation FIRFirebaseUserAgent
@synthesize firebaseUserAgent = _firebaseUserAgent;
@synthesize environmentComponents = _environmentComponents;
- (instancetype)init {
self = [super init];
if (self) {
_valuesByComponent = [[NSMutableDictionary alloc] init];
}
return self;
}
- (NSString *)firebaseUserAgent {
@synchronized(self) {
if (_firebaseUserAgent == nil) {
NSMutableDictionary<NSString *, NSString *> *allComponents =
[self.valuesByComponent mutableCopy];
[allComponents setValuesForKeysWithDictionary:self.environmentComponents];
__block NSMutableArray<NSString *> *components =
[[NSMutableArray<NSString *> alloc] initWithCapacity:self.valuesByComponent.count];
[allComponents enumerateKeysAndObjectsUsingBlock:^(
NSString *_Nonnull name, NSString *_Nonnull value, BOOL *_Nonnull stop) {
[components addObject:[NSString stringWithFormat:@"%@/%@", name, value]];
}];
[components sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
_firebaseUserAgent = [components componentsJoinedByString:@" "];
}
return _firebaseUserAgent;
}
}
- (void)setValue:(nullable NSString *)value forComponent:(NSString *)componentName {
@synchronized(self) {
self.valuesByComponent[componentName] = value;
// Reset cached user agent string.
_firebaseUserAgent = nil;
}
}
- (void)reset {
@synchronized(self) {
// Reset components.
_valuesByComponent = [[[self class] environmentComponents] mutableCopy];
// Reset cached user agent string.
_firebaseUserAgent = nil;
}
}
#pragma mark - Environment components
- (NSDictionary<NSString *, NSString *> *)environmentComponents {
if (_environmentComponents == nil) {
_environmentComponents = [[self class] environmentComponents];
}
return _environmentComponents;
}
+ (NSDictionary<NSString *, NSString *> *)environmentComponents {
NSMutableDictionary<NSString *, NSString *> *components = [NSMutableDictionary dictionary];
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
NSString *xcodeVersion = info[@"DTXcodeBuild"];
NSString *appleSdkVersion = info[@"DTSDKBuild"];
NSString *swiftFlagValue = [GULAppEnvironmentUtil hasSwiftRuntime] ? @"true" : @"false";
NSString *isFromAppstoreFlagValue = [GULAppEnvironmentUtil isFromAppStore] ? @"true" : @"false";
components[@"apple-platform"] = [GULAppEnvironmentUtil applePlatform];
components[@"apple-sdk"] = appleSdkVersion;
components[@"appstore"] = isFromAppstoreFlagValue;
components[@"deploy"] = [GULAppEnvironmentUtil deploymentType];
components[@"device"] = [GULAppEnvironmentUtil deviceModel];
components[@"os-version"] = [GULAppEnvironmentUtil systemVersion];
components[@"swift"] = swiftFlagValue;
components[@"xcode"] = xcodeVersion;
return [components copy];
}
@end

View File

@ -13,8 +13,11 @@
// limitations under the License.
#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h"
#import <GoogleUtilities/GULHeartbeatDateStorable.h>
#import <GoogleUtilities/GULHeartbeatDateStorage.h>
#import <GoogleUtilities/GULHeartbeatDateStorageUserDefaults.h>
#import <GoogleUtilities/GULLogger.h>
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
const static long secondsInDay = 86400;
@implementation FIRHeartbeatInfo : NSObject
@ -25,9 +28,17 @@ const static long secondsInDay = 86400;
*/
+ (BOOL)updateIfNeededHeartbeatDateForTag:(NSString *)heartbeatTag {
@synchronized(self) {
NSString *const kHeartbeatStorageFile = @"HEARTBEAT_INFO_STORAGE";
GULHeartbeatDateStorage *dataStorage =
[[GULHeartbeatDateStorage alloc] initWithFileName:kHeartbeatStorageFile];
NSString *const kHeartbeatStorageName = @"HEARTBEAT_INFO_STORAGE";
id<GULHeartbeatDateStorable> dataStorage;
#if TARGET_OS_TV
NSUserDefaults *defaults =
[[NSUserDefaults alloc] initWithSuiteName:kFirebaseCoreDefaultsSuiteName];
dataStorage =
[[GULHeartbeatDateStorageUserDefaults alloc] initWithDefaults:defaults
key:kHeartbeatStorageName];
#else
dataStorage = [[GULHeartbeatDateStorage alloc] initWithFileName:kHeartbeatStorageName];
#endif
NSDate *heartbeatTime = [dataStorage heartbeatDateForTag:heartbeatTag];
NSDate *currentDate = [NSDate date];
if (heartbeatTime != nil) {

View File

@ -16,20 +16,15 @@
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#import <GoogleUtilities/GULLogger.h>
#import "FirebaseCore/Sources/Public/FIRLoggerLevel.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRLoggerLevel.h"
#import "FirebaseCore/Sources/FIRVersion.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/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.
@ -58,10 +53,10 @@ static NSString *const kMessageCodePattern = @"^I-[A-Z]{3}[0-9]{6}$";
static NSRegularExpression *sMessageCodeRegex;
#endif
void FIRLoggerInitializeASL() {
void FIRLoggerInitializeASL(void) {
dispatch_once(&sFIRLoggerOnceToken, ^{
// Register Firebase Version with GULLogger.
GULLoggerRegisterVersion(FIRVersionString);
GULLoggerRegisterVersion(FIRFirebaseVersion());
// Override the aslOptions to ASL_OPT_STDERR if the override argument is passed in.
NSArray *arguments = [NSProcessInfo processInfo].arguments;
@ -101,7 +96,7 @@ void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel) {
}
#ifdef DEBUG
void FIRResetLogger() {
void FIRResetLogger(void) {
extern void GULResetLogger(void);
sFIRLoggerOnceToken = 0;
[sFIRLoggerUserDefaults removeObjectForKey:kFIRPersistedDebugModeKey];

View File

@ -13,10 +13,10 @@
// limitations under the License.
#import "FirebaseCore/Sources/FIRBundleUtil.h"
#import "FirebaseCore/Sources/FIRVersion.h"
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
#import "FirebaseCore/Sources/Private/FIRLogger.h"
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
// Keys for the strings in the plist file.
NSString *const kFIRAPIKey = @"API_KEY";
@ -90,40 +90,40 @@ NSString *const kFIRExceptionBadModification =
static FIROptions *sDefaultOptions = nil;
static NSDictionary *sDefaultOptionsDictionary = nil;
static dispatch_once_t sDefaultOptionsOnceToken;
static dispatch_once_t sDefaultOptionsDictionaryOnceToken;
#pragma mark - Public only for internal class methods
+ (FIROptions *)defaultOptions {
if (sDefaultOptions != nil) {
return sDefaultOptions;
}
dispatch_once(&sDefaultOptionsOnceToken, ^{
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
if (defaultOptionsDictionary != nil) {
sDefaultOptions =
[[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
}
});
NSDictionary *defaultOptionsDictionary = [self defaultOptionsDictionary];
if (defaultOptionsDictionary == nil) {
return nil;
}
sDefaultOptions = [[FIROptions alloc] initInternalWithOptionsDictionary:defaultOptionsDictionary];
return sDefaultOptions;
}
#pragma mark - Private class methods
+ (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);
}
dispatch_once(&sDefaultOptionsDictionaryOnceToken, ^{
NSString *plistFilePath = [FIROptions plistFilePathWithName:kServiceInfoFileName];
if (plistFilePath == nil) {
return;
}
sDefaultOptionsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
if (sDefaultOptionsDictionary == nil) {
FIRLogError(kFIRLoggerCore, @"I-COR000011",
@"The configuration file is not a dictionary: "
@"'%@.%@'.",
kServiceInfoFileName, kServiceInfoFileType);
}
});
return sDefaultOptionsDictionary;
}
@ -144,6 +144,8 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
+ (void)resetDefaultOptions {
sDefaultOptions = nil;
sDefaultOptionsDictionary = nil;
sDefaultOptionsOnceToken = 0;
sDefaultOptionsDictionaryOnceToken = 0;
}
#pragma mark - Private instance methods
@ -158,9 +160,9 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
}
- (id)copyWithZone:(NSZone *)zone {
FIROptions *newOptions = [[[self class] allocWithZone:zone] init];
FIROptions *newOptions = [(FIROptions *)[[self class] allocWithZone:zone]
initInternalWithOptionsDictionary:self.optionsDictionary];
if (newOptions) {
newOptions.optionsDictionary = self.optionsDictionary;
newOptions.deepLinkURLScheme = self.deepLinkURLScheme;
newOptions.appGroupID = self.appGroupID;
newOptions.editingLocked = self.isEditingLocked;
@ -171,6 +173,12 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
#pragma mark - Public instance methods
- (instancetype)init {
// Unavailable.
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initWithContentsOfFile:(NSString *)plistPath {
self = [super init];
if (self) {
@ -277,7 +285,7 @@ static NSDictionary *sDefaultOptionsDictionary = nil;
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];
NSString *version = FIRFirebaseVersion();
NSArray *components = [version componentsSeparatedByString:@"."];
NSString *major = [components objectAtIndex:0];
NSString *minor = [NSString stringWithFormat:@"%02d", [[components objectAtIndex:1] intValue]];

View File

@ -14,14 +14,12 @@
* limitations under the License.
*/
#import "FirebaseCore/Sources/Public/FirebaseCore/FIRVersion.h"
#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
@ -29,5 +27,6 @@
#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);
NSString* FIRFirebaseVersion(void) {
return @STR(Firebase_VERSION);
}

View File

@ -14,19 +14,7 @@
* limitations under the License.
*/
#if SWIFT_PACKAGE
// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM.
#import "FIRApp.h"
#else
#import <FirebaseCore/FIRApp.h>
#endif
// The has_include is a workaround so the old IID needed for the FIS tests can find FIRErrors.h
#if __has_include("FirebaseCore/Sources/Private/FIRErrors.h")
#import "FirebaseCore/Sources/Private/FIRErrors.h"
#else
#import <FirebaseCore/FIRErrors.h>
#endif
@class FIRComponentContainer;
@protocol FIRLibrary;
@ -51,6 +39,10 @@ extern NSString *const kFIRAppDeleteNotification;
extern NSString *const kFIRAppIsDefaultAppKey;
extern NSString *const kFIRAppNameKey;
extern NSString *const kFIRGoogleAppIDKey;
extern NSString *const kFirebaseCoreErrorDomain;
/** The NSUserDefaults suite name for FirebaseCore, for those storage locations that use it. */
extern NSString *const kFirebaseCoreDefaultsSuiteName;
/**
* The format string for the User Defaults key used for storing the data collection enabled flag.
@ -63,11 +55,6 @@ extern NSString *const kFIRGlobalAppDataCollectionEnabledDefaultsKeyFormat;
*/
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
@ -110,14 +97,6 @@ extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
*/
@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.
*/
@ -132,9 +111,19 @@ extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
*/
+ (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *)version;
/**
* Registers a given internal library to be reported for analytics.
*
* @param library Optional parameter for component registration.
* @param name Name of the library.
*/
+ (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
withName:(nonnull NSString *)name;
/**
* Registers a given internal library with the given version number to be reported for
* analytics.
* analytics. This should only be used for non-Firebase libraries that have their own versioning
* scheme.
*
* @param library Optional parameter for component registration.
* @param name Name of the library.
@ -149,15 +138,6 @@ extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
*/
+ (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.
*/

View File

@ -15,15 +15,6 @@
*/
#import <Foundation/Foundation.h>
// The has_include is a workaround so the old IID needed for the FIS tests can find the headers.
#if __has_include("FirebaseCore/Sources/Private/FIRComponentType.h")
#import "FirebaseCore/Sources/Private/FIRComponentType.h"
#import "FirebaseCore/Sources/Private/FIRLibrary.h"
#else
#import <FirebaseCore/FIRComponentType.h>
#import <FirebaseCore/FIRLibrary.h>
#endif
NS_ASSUME_NONNULL_BEGIN
/// A type-safe macro to retrieve a component from a container. This should be used to retrieve

View File

@ -1,39 +0,0 @@
/*
* 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. It's deprecated, but
* still used after copybara.
*/
FIRErrorCodeConfigFailed = -114,
};

View File

@ -27,7 +27,7 @@ typedef NS_ENUM(NSInteger, FIRHeartbeatInfoCode) {
};
/**
* Get heartbeat code requred for the sdk.
* Get heartbeat code required for the sdk.
* @param heartbeatTag String representing the sdk heartbeat tag.
* @return Heartbeat code indicating whether or not an sdk/global heartbeat
* needs to be sent

View File

@ -19,14 +19,8 @@
#import <Foundation/Foundation.h>
// The has_include is a workaround so the old IID needed for the FIS tests can find the headers.
#if __has_include("FirebaseCore/Sources/Private/FIRComponent.h")
#import "FirebaseCore/Sources/Private/FIRComponent.h"
#else
#import <FirebaseCore/FIRComponent.h>
#endif
@class FIRApp;
@class FIRComponent;
NS_ASSUME_NONNULL_BEGIN

View File

@ -16,12 +16,7 @@
#import <Foundation/Foundation.h>
#if SWIFT_PACKAGE
// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM.
#import "FIRLoggerLevel.h"
#else
#import <FirebaseCore/FIRLoggerLevel.h>
#endif
NS_ASSUME_NONNULL_BEGIN
@ -30,14 +25,9 @@ NS_ASSUME_NONNULL_BEGIN
*/
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;
/**

View File

@ -14,12 +14,7 @@
* limitations under the License.
*/
#if SWIFT_PACKAGE
// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM.
#import "FIROptions.h"
#else
#import <FirebaseCore/FIROptions.h>
#endif
/**
* Keys for the strings in the plist file.
@ -56,7 +51,8 @@ extern NSString *const kServiceInfoFileType;
* Initializes the options with dictionary. The above strings are the keys of the dictionary.
* This is the designated initializer.
*/
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary;
- (instancetype)initInternalWithOptionsDictionary:(NSDictionary *)serviceInfoDictionary
NS_DESIGNATED_INITIALIZER;
/**
* defaultOptions and defaultOptionsDictionary are exposed in order to be used in FIRApp and

View File

@ -15,15 +15,12 @@
// An umbrella header, for any other libraries in this repo to access Firebase Public and Private
// headers. Any package manager complexity should be handled here.
#if SWIFT_PACKAGE
@import FirebaseCore;
#else
#import <FirebaseCore/FirebaseCore.h>
#endif
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
#import "FirebaseCore/Sources/Private/FIRComponent.h"
#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
#import "FirebaseCore/Sources/Private/FIRComponentType.h"
#import "FirebaseCore/Sources/Private/FIRDependency.h"
#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h"
#import "FirebaseCore/Sources/Private/FIRLibrary.h"

View File

@ -106,7 +106,7 @@ NS_SWIFT_NAME(FirebaseOptions)
* FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
* Returns nil if the plist file does not exist or is invalid.
*/
- (nullable instancetype)initWithContentsOfFile:(NSString *)plistPath;
- (nullable instancetype)initWithContentsOfFile:(NSString *)plistPath NS_DESIGNATED_INITIALIZER;
/**
* Initializes a customized instance of FIROptions with required fields. Use the mutable properties
@ -115,9 +115,12 @@ NS_SWIFT_NAME(FirebaseOptions)
// clang-format off
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID
GCMSenderID:(NSString *)GCMSenderID
NS_SWIFT_NAME(init(googleAppID:gcmSenderID:));
NS_SWIFT_NAME(init(googleAppID:gcmSenderID:)) NS_DESIGNATED_INITIALIZER;
// clang-format on
/** Unavailable. Please use `init(contentsOfFile:)` or `init(googleAppID:gcmSenderID:)` instead. */
- (instancetype)init NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Google
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,9 +16,10 @@
#import <Foundation/Foundation.h>
#include "FIRErrorCode.h"
NS_ASSUME_NONNULL_BEGIN
extern NSString *const kFirebaseErrorDomain;
extern NSString *const kFirebaseConfigErrorDomain;
extern NSString *const kFirebaseCoreErrorDomain;
extern NSString *const kFirebasePerfErrorDomain;
/** Returns the current version of Firebase. */
NS_SWIFT_NAME(FirebaseVersion())
NSString* FIRFirebaseVersion(void);
NS_ASSUME_NONNULL_END

View File

@ -18,3 +18,4 @@
#import "FIRConfiguration.h"
#import "FIRLoggerLevel.h"
#import "FIROptions.h"
#import "FIRVersion.h"

View File

@ -3,8 +3,12 @@
[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase)
[![Actions Status][gh-abtesting-badge]][gh-actions]
[![Actions Status][gh-appcheck-badge]][gh-actions]
[![Actions Status][gh-appdistribution-badge]][gh-actions]
[![Actions Status][gh-auth-badge]][gh-actions]
[![Actions Status][gh-cocoapods-integration-badge]][gh-actions]
[![Actions Status][gh-core-badge]][gh-actions]
[![Actions Status][gh-core-diagnostics-badge]][gh-actions]
[![Actions Status][gh-crashlytics-badge]][gh-actions]
[![Actions Status][gh-database-badge]][gh-actions]
[![Actions Status][gh-datatransport-badge]][gh-actions]
@ -12,9 +16,13 @@
[![Actions Status][gh-firebasepod-badge]][gh-actions]
[![Actions Status][gh-firestore-badge]][gh-actions]
[![Actions Status][gh-functions-badge]][gh-actions]
[![Actions Status][gh-google-utilities-badge]][gh-actions]
[![Actions Status][gh-google-utilities-components-badge]][gh-actions]
[![Actions Status][gh-inappmessaging-badge]][gh-actions]
[![Actions Status][gh-interop-badge]][gh-actions]
[![Actions Status][gh-messaging-badge]][gh-actions]
[![Actions Status][gh-mlmodeldownloader-badge]][gh-actions]
[![Actions Status][gh-performance-badge]][gh-actions]
[![Actions Status][gh-remoteconfig-badge]][gh-actions]
[![Actions Status][gh-storage-badge]][gh-actions]
[![Actions Status][gh-symbolcollision-badge]][gh-actions]
@ -23,21 +31,23 @@
# Firebase Apple Open Source Development
This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics,
FirebasePerformance, and FirebaseML.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics
and FirebaseML.
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).
The repository also includes GoogleUtilities and GoogleDataTransport source
which are utilities used by Firebase and other Google products.
**Note** _FirebaseCombineSwift_ contains support for Apple's Combine framework. This module is currently under development, and not yet supported for use in production environments. Fore more details, please refer to the [docs](FirebaseCombineSwift/README.md).
## Installation
See the three subsections for details about three different installation methods.
See the subsections below for details about the different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Swift Package Manager](SwiftPackageManager.md)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
@ -46,11 +56,12 @@ See the three subsections for details about three different installation methods
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
### Swift Package Manager
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).
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
found at [SwiftPackageManager.md](SwiftPackageManager.md).
### Installing from GitHub
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
@ -67,14 +78,14 @@ All of the official releases are tagged in this repo and available via CocoaPods
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
```ruby
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:
```
```ruby
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
@ -82,12 +93,8 @@ 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).
[Carthage](Carthage.md). If you have a new Mac with an Apple silicon chip, please see
[these instructions](AppleSilicon.md).
### Using Firebase from a Framework or a library
@ -98,13 +105,22 @@ Instructions for installing binary frameworks via
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.3 (or later)
* CocoaPods 1.7.2 (or later)
* Xcode 12.2 (or later)
CocoaPods is still the canonical way to develop, but much of the repo now supports
development with Swift Package Manager.
### CocoaPods
Install
* CocoaPods 1.10.0 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios`
```ruby
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
```
Note: If the CocoaPods cache is out of date, you may need to run
`pod repo update` before the `pod gen` command.
@ -116,7 +132,7 @@ CocoaPods workspaces.
Firestore has a self contained Xcode project. See
[Firestore/README.md](Firestore/README.md).
### Development for Catalyst
#### Development for Catalyst
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
* Check the Mac box in the App-iOS Build Settings
* Sign the App in the Settings Signing & Capabilities tab
@ -125,6 +141,18 @@ Firestore has a self contained Xcode project. See
* Select the Unit-unit scheme
* Run it to build and test
Alternatively disable signing in each target:
* Go to Build Settings tab
* Click `+`
* Select `Add User-Defined Setting`
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
### Swift Package Manager
* `open Package.swift` or double click `Package.swift` in Finder.
* Xcode will open the project
* Choose a scheme for a library to build or test suite to run
* Choose a target platform by selecting the run destination along with the scheme
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
@ -136,40 +164,24 @@ See [HeadersImports.md](HeadersImports.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)
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/check.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:
GitHub Actions will verify that any code changes are done in a style compliant
way. Install `clang-format` and `mint`:
```console
brew install clang-format@12
brew install mint
```
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
To update this section, find the versions of clang-format and swiftformat.rb to
match the versions in the CI failure logs
[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage (Deprecated)
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
In order to run the sample apps and integration tests, you'll need a valid
`GoogleService-Info.plist` file. 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:
@ -177,12 +189,11 @@ files without real values, but can be replaced with real plist files. To get you
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/));
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
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.
### Coverage Report Generation
See [scripts/code_coverage_report/README.md](scripts/code_coverage_report/README.md).
## Specific Component Instructions
See the sections below for any special instructions for those components.
@ -202,10 +213,16 @@ To run against a local emulator instance, invoke `./scripts/run_database_emulato
running the integration test.
To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to
`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
running.
### Firebase Performance Monitoring
If you're doing specific Firebase Performance Monitoring development, see
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
integrating Performance with the dev test App.
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
@ -219,7 +236,8 @@ 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)
[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.
@ -229,43 +247,39 @@ The iOS Simulator cannot register for remote notifications, and will not receive
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
## Building with Firebase on Apple platforms
At this time, not all of Firebase's products are available across all Apple platforms. However,
Firebase is constantly evolving and community supported efforts have helped expand Firebase's support.
To keep up with the latest info regarding Firebase's support across Apple platforms, refer to
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
in Firebase's documentation.
### 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.
### tvOS, macOS, watchOS and Catalyst
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on
tvOS, macOS, watchOS and Catalyst.
#### tvOS, macOS, watchOS and Catalyst
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
work on tvOS, macOS, watchOS and Catalyst.
For tvOS, checkout the [Sample](Example/tvOSSample).
For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the
[Independent Watch App Sample](Example/watchOSSample).
For tvOS, see the [Sample](Example/tvOSSample).
For watchOS, currently only Messaging, Storage and Crashlytics (and their dependencies) have limited
support. See the [Independent Watch App Sample](Example/watchOSSample).
Keep in mind that macOS, tvOS, watchOS and Catalyst 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, tvOS or watchOS. If you
encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and
this repository is actively developed primarily for iOS. While we can catch basic unit test issues
with GitHub Actions, there may be some changes where the SDK no longer works as expected on macOS,
tvOS or watchOS. If you encounter this, please
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
During app setup in the console, you may get to a step that mentions something like "Checking if the app
has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst.
During app setup in the console, you may get to a step that mentions something like "Checking if the
app has communicated with our servers". This relies on Analytics and will not work on
macOS/tvOS/watchOS/Catalyst.
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
To install, add a subset of the following to the Podfile:
```
pod 'Firebase/ABTesting' # No watchOS support yet
pod 'Firebase/Auth' # No watchOS support yet
pod 'Firebase/Crashlytics' # No watchOS support yet
pod 'Firebase/Database' # No watchOS support yet
pod 'Firebase/Firestore' # No watchOS support yet
pod 'Firebase/Functions' # No watchOS support yet
pod 'Firebase/Messaging'
pod 'Firebase/RemoteConfig' # No watchOS support yet
pod 'Firebase/Storage'
```
#### Additional Catalyst Notes
* FirebaseAuth and FirebaseMessaging require adding `Keychain Sharing Capability`
@ -273,6 +287,10 @@ to Build Settings.
* FirebaseFirestore requires signing the
[gRPC Resource target](https://github.com/firebase/firebase-ios-sdk/issues/3500#issuecomment-518741681).
#### Additional Crashlytics Notes
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
@ -293,8 +311,12 @@ Your use of Firebase is governed by the
[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions
[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg
[gh-appcheck-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/app_check/badge.svg
[gh-appdistribution-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/appdistribution/badge.svg
[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg
[gh-cocoapods-integration-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/cocoapods-integration/badge.svg
[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg
[gh-core-diagnostics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core-diagnostics/badge.svg
[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg
[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg
[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg
@ -302,9 +324,13 @@ Your use of Firebase is governed by the
[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg
[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg
[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg
[gh-google-utilities-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/google-utilities/badge.svg
[gh-google-utilities-components-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/google-utilities-components/badge.svg
[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg
[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg
[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg
[gh-mlmodeldownloader-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/mlmodeldownloader/badge.svg
[gh-performance-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/performance/badge.svg
[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg
[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg
[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg

View File

@ -17,10 +17,7 @@
#import <objc/runtime.h>
#include <sys/utsname.h>
#import <GoogleDataTransport/GDTCORConsoleLogger.h>
#import <GoogleDataTransport/GDTCOREvent.h>
#import <GoogleDataTransport/GDTCORTargets.h>
#import <GoogleDataTransport/GDTCORTransport.h>
#import <GoogleDataTransport/GoogleDataTransport.h>
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#import <GoogleUtilities/GULHeartbeatDateStorage.h>
@ -33,7 +30,7 @@
#import <nanopb/pb_decode.h>
#import <nanopb/pb_encode.h>
#import "FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h"
#import "Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h"
/** The logger service string to use when printing to the console. */
static GULLoggerService kFIRCoreDiagnostics = @"[FirebaseCoreDiagnostics/FIRCoreDiagnostics]";
@ -44,7 +41,9 @@ static BOOL kUsingZipFile = YES;
static BOOL kUsingZipFile = NO;
#endif // FIREBASE_BUILD_ZIP_FILE
#ifdef FIREBASE_BUILD_CARTHAGE
#if SWIFT_PACKAGE
#define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM
#elif 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
@ -52,13 +51,6 @@ static BOOL kUsingZipFile = NO;
#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";
@ -200,24 +192,6 @@ NS_ASSUME_NONNULL_END
return self;
}
#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
/** Callocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
@ -270,18 +244,6 @@ logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType FIRMapFromServiceStringT
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),
@ -379,7 +341,7 @@ void FIRPopulateProtoWithCommonInfoFromApp(logs_proto_mobilesdk_ios_ICoreConfigu
config->icore_version = FIREncodeString(libraryVersionID);
}
NSString *deviceModel = [FIRCoreDiagnostics deviceModel];
NSString *deviceModel = [GULAppEnvironmentUtil deviceModel];
if (deviceModel.length) {
config->device_model = FIREncodeString(deviceModel);
}
@ -422,40 +384,6 @@ void FIRPopulateProtoWithInstalledServices(logs_proto_mobilesdk_ios_ICoreConfigu
[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
@ -526,29 +454,6 @@ void FIRPopulateProtoWithInstalledServices(logs_proto_mobilesdk_ios_ICoreConfigu
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.
@ -604,7 +509,6 @@ void FIRPopulateProtoWithInfoPlistValues(logs_proto_mobilesdk_ios_ICoreConfigura
FIRPopulateProtoWithInfoFromUserInfoParams(&icore_config, diagnosticObjects);
FIRPopulateProtoWithCommonInfoFromApp(&icore_config, diagnosticObjects);
FIRPopulateProtoWithInstalledServices(&icore_config);
FIRPopulateProtoWithNumberOfLinkedFrameworks(&icore_config);
FIRPopulateProtoWithInfoPlistValues(&icore_config);
[self setHeartbeatFlagIfNeededToConfig:&icore_config];

View File

@ -15,7 +15,7 @@
*/
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.3.9.5 */
/* Generated by nanopb-0.3.9.7 */
#include "firebasecore.nanopb.h"

View File

@ -15,7 +15,7 @@
*/
/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.9.5 */
/* Generated by nanopb-0.3.9.7 */
#ifndef PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED
#define PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019 Google
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,6 +14,5 @@
* limitations under the License.
*/
#import <Foundation/Foundation.h>
FOUNDATION_EXPORT const char *const FIRInstallationsVersionStr;
// There are no actual public headers in the lib. This is a dummy public header to prevent Cocoapods
// from adding all internal headers as public.

View File

@ -3,8 +3,12 @@
[![Platform](https://img.shields.io/cocoapods/p/Firebase.svg?style=flat)](https://cocoapods.org/pods/Firebase)
[![Actions Status][gh-abtesting-badge]][gh-actions]
[![Actions Status][gh-appcheck-badge]][gh-actions]
[![Actions Status][gh-appdistribution-badge]][gh-actions]
[![Actions Status][gh-auth-badge]][gh-actions]
[![Actions Status][gh-cocoapods-integration-badge]][gh-actions]
[![Actions Status][gh-core-badge]][gh-actions]
[![Actions Status][gh-core-diagnostics-badge]][gh-actions]
[![Actions Status][gh-crashlytics-badge]][gh-actions]
[![Actions Status][gh-database-badge]][gh-actions]
[![Actions Status][gh-datatransport-badge]][gh-actions]
@ -12,9 +16,13 @@
[![Actions Status][gh-firebasepod-badge]][gh-actions]
[![Actions Status][gh-firestore-badge]][gh-actions]
[![Actions Status][gh-functions-badge]][gh-actions]
[![Actions Status][gh-google-utilities-badge]][gh-actions]
[![Actions Status][gh-google-utilities-components-badge]][gh-actions]
[![Actions Status][gh-inappmessaging-badge]][gh-actions]
[![Actions Status][gh-interop-badge]][gh-actions]
[![Actions Status][gh-messaging-badge]][gh-actions]
[![Actions Status][gh-mlmodeldownloader-badge]][gh-actions]
[![Actions Status][gh-performance-badge]][gh-actions]
[![Actions Status][gh-remoteconfig-badge]][gh-actions]
[![Actions Status][gh-storage-badge]][gh-actions]
[![Actions Status][gh-symbolcollision-badge]][gh-actions]
@ -23,21 +31,23 @@
# Firebase Apple Open Source Development
This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics,
FirebasePerformance, and FirebaseML.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
This repository contains all Apple platform Firebase SDK source except FirebaseAnalytics
and FirebaseML.
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).
The repository also includes GoogleUtilities and GoogleDataTransport source
which are utilities used by Firebase and other Google products.
**Note** _FirebaseCombineSwift_ contains support for Apple's Combine framework. This module is currently under development, and not yet supported for use in production environments. Fore more details, please refer to the [docs](FirebaseCombineSwift/README.md).
## Installation
See the three subsections for details about three different installation methods.
See the subsections below for details about the different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Swift Package Manager](SwiftPackageManager.md)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
@ -46,11 +56,12 @@ See the three subsections for details about three different installation methods
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
### Swift Package Manager
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).
Instructions for [Swift Package Manager](https://swift.org/package-manager/) support can be
found at [SwiftPackageManager.md](SwiftPackageManager.md).
### Installing from GitHub
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
@ -67,14 +78,14 @@ All of the official releases are tagged in this repo and available via CocoaPods
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
```ruby
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:
```
```ruby
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
@ -82,12 +93,8 @@ 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).
[Carthage](Carthage.md). If you have a new Mac with an Apple silicon chip, please see
[these instructions](AppleSilicon.md).
### Using Firebase from a Framework or a library
@ -98,13 +105,22 @@ Instructions for installing binary frameworks via
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.3 (or later)
* CocoaPods 1.7.2 (or later)
* Xcode 12.2 (or later)
CocoaPods is still the canonical way to develop, but much of the repo now supports
development with Swift Package Manager.
### CocoaPods
Install
* CocoaPods 1.10.0 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios`
```ruby
pod gen Firebase{name here}.podspec --local-sources=./ --auto-open --platforms=ios
```
Note: If the CocoaPods cache is out of date, you may need to run
`pod repo update` before the `pod gen` command.
@ -116,7 +132,7 @@ CocoaPods workspaces.
Firestore has a self contained Xcode project. See
[Firestore/README.md](Firestore/README.md).
### Development for Catalyst
#### Development for Catalyst
* `pod gen {name here}.podspec --local-sources=./ --auto-open --platforms=ios`
* Check the Mac box in the App-iOS Build Settings
* Sign the App in the Settings Signing & Capabilities tab
@ -125,6 +141,18 @@ Firestore has a self contained Xcode project. See
* Select the Unit-unit scheme
* Run it to build and test
Alternatively disable signing in each target:
* Go to Build Settings tab
* Click `+`
* Select `Add User-Defined Setting`
* Add `CODE_SIGNING_REQUIRED` setting with a value of `NO`
### Swift Package Manager
* `open Package.swift` or double click `Package.swift` in Finder.
* Xcode will open the project
* Choose a scheme for a library to build or test suite to run
* Choose a target platform by selecting the run destination along with the scheme
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
@ -136,40 +164,24 @@ See [HeadersImports.md](HeadersImports.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)
[./scripts/check.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/check.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:
GitHub Actions will verify that any code changes are done in a style compliant
way. Install `clang-format` and `mint`:
```console
brew install clang-format@12
brew install mint
```
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c6f1cbd/Formula/clang-format.rb
brew upgrade https://raw.githubusercontent.com/Homebrew/homebrew-core/c13eda8/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
To update this section, find the versions of clang-format and swiftformat.rb to
match the versions in the CI failure logs
[here](https://github.com/Homebrew/homebrew-core/tree/master/Formula).
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage (Deprecated)
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
In order to run the sample apps and integration tests, you'll need a valid
`GoogleService-Info.plist` file. 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:
@ -177,12 +189,11 @@ files without real values, but can be replaced with real plist files. To get you
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/));
4. Download the resulting `GoogleService-Info.plist` and add it to the Xcode project.
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.
### Coverage Report Generation
See [scripts/code_coverage_report/README.md](scripts/code_coverage_report/README.md).
## Specific Component Instructions
See the sections below for any special instructions for those components.
@ -202,10 +213,16 @@ To run against a local emulator instance, invoke `./scripts/run_database_emulato
running the integration test.
To run against a production instance, provide a valid GoogleServices-Info.plist and copy it to
`Example/Database/App/GoogleService-Info.plist`. Your Security Rule must be set to
`FirebaseDatabase/Tests/Resources/GoogleService-Info.plist`. Your Security Rule must be set to
[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
running.
### Firebase Performance Monitoring
If you're doing specific Firebase Performance Monitoring development, see
[the Performance README](FirebasePerformance/README.md) for instructions about building the SDK
and [the Performance TestApp README](FirebasePerformance/Tests/TestApp/README.md) for instructions about
integrating Performance with the dev test App.
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
@ -219,7 +236,8 @@ 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)
[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.
@ -229,43 +247,39 @@ The iOS Simulator cannot register for remote notifications, and will not receive
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
## Building with Firebase on Apple platforms
At this time, not all of Firebase's products are available across all Apple platforms. However,
Firebase is constantly evolving and community supported efforts have helped expand Firebase's support.
To keep up with the latest info regarding Firebase's support across Apple platforms, refer to
[this chart](https://firebase.google.com/docs/ios/learn-more#firebase_library_support_by_platform)
in Firebase's documentation.
### 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.
### tvOS, macOS, watchOS and Catalyst
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and work on
tvOS, macOS, watchOS and Catalyst.
#### tvOS, macOS, watchOS and Catalyst
Thanks to contributions from the community, many of Firebase SDKs now compile, run unit tests, and
work on tvOS, macOS, watchOS and Catalyst.
For tvOS, checkout the [Sample](Example/tvOSSample).
For watchOS, currently only Messaging and Storage (and their dependencies) have limited support. Checkout the
[Independent Watch App Sample](Example/watchOSSample).
For tvOS, see the [Sample](Example/tvOSSample).
For watchOS, currently only Messaging, Storage and Crashlytics (and their dependencies) have limited
support. See the [Independent Watch App Sample](Example/watchOSSample).
Keep in mind that macOS, tvOS, watchOS and Catalyst 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, tvOS or watchOS. If you
encounter this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Keep in mind that macOS, tvOS, watchOS and Catalyst are not officially supported by Firebase, and
this repository is actively developed primarily for iOS. While we can catch basic unit test issues
with GitHub Actions, there may be some changes where the SDK no longer works as expected on macOS,
tvOS or watchOS. If you encounter this, please
[file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
During app setup in the console, you may get to a step that mentions something like "Checking if the app
has communicated with our servers". This relies on Analytics and will not work on macOS/tvOS/watchOS/Catalyst.
During app setup in the console, you may get to a step that mentions something like "Checking if the
app has communicated with our servers". This relies on Analytics and will not work on
macOS/tvOS/watchOS/Catalyst.
**It's safe to ignore the message and continue**, the rest of the SDKs will work as expected.
To install, add a subset of the following to the Podfile:
```
pod 'Firebase/ABTesting' # No watchOS support yet
pod 'Firebase/Auth' # No watchOS support yet
pod 'Firebase/Crashlytics' # No watchOS support yet
pod 'Firebase/Database' # No watchOS support yet
pod 'Firebase/Firestore' # No watchOS support yet
pod 'Firebase/Functions' # No watchOS support yet
pod 'Firebase/Messaging'
pod 'Firebase/RemoteConfig' # No watchOS support yet
pod 'Firebase/Storage'
```
#### Additional Catalyst Notes
* FirebaseAuth and FirebaseMessaging require adding `Keychain Sharing Capability`
@ -273,6 +287,10 @@ to Build Settings.
* FirebaseFirestore requires signing the
[gRPC Resource target](https://github.com/firebase/firebase-ios-sdk/issues/3500#issuecomment-518741681).
#### Additional Crashlytics Notes
* watchOS has limited support. Due to watchOS restrictions, mach exceptions and signal crashes are
not recorded. (Crashes in SwiftUI are generated as mach exceptions, so will not be recorded)
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
@ -293,8 +311,12 @@ Your use of Firebase is governed by the
[gh-actions]: https://github.com/firebase/firebase-ios-sdk/actions
[gh-abtesting-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/abtesting/badge.svg
[gh-appcheck-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/app_check/badge.svg
[gh-appdistribution-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/appdistribution/badge.svg
[gh-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg
[gh-cocoapods-integration-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/cocoapods-integration/badge.svg
[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/badge.svg
[gh-core-diagnostics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core-diagnostics/badge.svg
[gh-crashlytics-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/crashlytics/badge.svg
[gh-database-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/database/badge.svg
[gh-datatransport-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/datatransport/badge.svg
@ -302,9 +324,13 @@ Your use of Firebase is governed by the
[gh-firebasepod-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firebasepod/badge.svg
[gh-firestore-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/firestore/badge.svg
[gh-functions-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/functions/badge.svg
[gh-google-utilities-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/google-utilities/badge.svg
[gh-google-utilities-components-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/google-utilities-components/badge.svg
[gh-inappmessaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/inappmessaging/badge.svg
[gh-interop-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/interop/badge.svg
[gh-messaging-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/messaging/badge.svg
[gh-mlmodeldownloader-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/mlmodeldownloader/badge.svg
[gh-performance-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/performance/badge.svg
[gh-remoteconfig-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/remoteconfig/badge.svg
[gh-storage-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/storage/badge.svg
[gh-symbolcollision-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/symbolcollision/badge.svg

View File

@ -47,6 +47,11 @@ NSString* FIRCLSApplicationGetSDKBundleID(void);
*/
NSString* FIRCLSApplicationGetPlatform(void);
/**
* Returns the Operating System for filtering. Should be kept consistent with Analytics.
*/
NSString* FIRCLSApplicationGetFirebasePlatform(void);
/**
* Returns the user-facing app name
*/

View File

@ -12,10 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#import "FIRCLSApplication.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"
#import "FIRCLSHost.h"
#import "FIRCLSUtility.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSHost.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#if CLS_TARGET_OS_OSX
#import <AppKit/AppKit.h>
@ -44,9 +46,29 @@ NSString* FIRCLSApplicationGetPlatform(void) {
return @"mac";
#elif TARGET_OS_TV
return @"tvos";
#elif TARGET_OS_WATCH
return @"ios"; // TODO: temporarily use iOS until Firebase can add watchos to the backend
#endif
}
NSString* FIRCLSApplicationGetFirebasePlatform(void) {
NSString* firebasePlatform = [GULAppEnvironmentUtil applePlatform];
#if TARGET_OS_IOS
if ([firebasePlatform isEqualToString:@"ios"] &&
UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return @"ipados";
}
// This check is necessary because iOS-only apps running on iPad
// will report UIUserInterfaceIdiomPhone via UI_USER_INTERFACE_IDIOM().
if ([[UIDevice currentDevice].model.lowercaseString containsString:@"ipad"]) {
return @"ipados";
}
#endif
return firebasePlatform;
}
// these defaults match the FIRCLSInfoPlist helper in FIRCLSIDEFoundation
NSString* FIRCLSApplicationGetBundleVersion(void) {
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];

View File

@ -17,9 +17,9 @@
#include <stdbool.h>
#include <stdint.h>
#include "FIRCLSFeatures.h"
#include "FIRCLSFile.h"
#include "FIRCLSMachO.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
#include "Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.h"
__BEGIN_DECLS

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FIRCLSBinaryImage.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h"
#include <libkern/OSAtomic.h>
#include <mach-o/dyld.h>
@ -21,13 +21,13 @@
#include <stdatomic.h>
#include "FIRCLSByteUtility.h"
#include "FIRCLSFeatures.h"
#include "FIRCLSFile.h"
#include "FIRCLSGlobals.h"
#include "FIRCLSHost.h"
#include "FIRCLSMachO.h"
#include "FIRCLSUtility.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSHost.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#include "Crashlytics/Shared/FIRCLSByteUtility.h"
#include "Crashlytics/Shared/FIRCLSMachO/FIRCLSMachO.h"
#include <dispatch/dispatch.h>
@ -363,20 +363,17 @@ static void FIRCLSBinaryImageChanged(bool added,
const struct mach_header* mh,
intptr_t vmaddr_slide) {
// FIRCLSSDKLog("Binary image %s %p\n", added ? "loaded" : "unloaded", mh);
FIRCLSBinaryImageDetails imageDetails;
memset(&imageDetails, 0, sizeof(FIRCLSBinaryImageDetails));
imageDetails.slice = FIRCLSMachOSliceWithHeader((void*)mh);
imageDetails.vmaddr_slide = vmaddr_slide;
FIRCLSBinaryImageFillInImageDetails(&imageDetails);
// this is an atomic operation
FIRCLSBinaryImageStoreNode(added, imageDetails);
// this isn't, so do it on a serial queue
// Do these time-consuming operations on a background queue
dispatch_async(FIRCLSGetBinaryImageQueue(), ^{
// this is an atomic operation
FIRCLSBinaryImageStoreNode(added, imageDetails);
FIRCLSBinaryImageRecordSlice(added, imageDetails);
});
}

View File

@ -14,15 +14,15 @@
#pragma once
#include "FIRCLSAllocate.h"
#include "FIRCLSBinaryImage.h"
#include "FIRCLSException.h"
#include "FIRCLSFeatures.h"
#include "FIRCLSHost.h"
#include "FIRCLSInternalLogging.h"
#include "FIRCLSMachException.h"
#include "FIRCLSSignal.h"
#include "FIRCLSUserLogging.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSHost.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h"
#include "Crashlytics/Crashlytics/Handlers/FIRCLSException.h"
#include "Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h"
#include "Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.h"
#include <dispatch/dispatch.h>
#include <stdbool.h>
@ -57,7 +57,9 @@ typedef struct {
FIRCLSBinaryImageReadOnlyContext binaryimage;
FIRCLSExceptionReadOnlyContext exception;
FIRCLSHostReadOnlyContext host;
#if CLS_SIGNAL_SUPPORTED
FIRCLSSignalReadContext signal;
#endif
#if CLS_MACH_EXCEPTION_SUPPORTED
FIRCLSMachExceptionReadContext machException;
#endif
@ -84,7 +86,6 @@ typedef struct {
const char* rootPath;
const char* previouslyCrashedFileRootPath;
const char* sessionId;
const char* installId;
const char* betaToken;
#if CLS_MACH_EXCEPTION_SUPPORTED
exception_mask_t machExceptionMask;
@ -100,14 +101,8 @@ typedef struct {
#ifdef __OBJC__
bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
FIRCLSSettings* settings,
FIRCLSInstallIdentifierModel* installIDModel,
FIRCLSFileManager* fileManager);
// Re-writes the metadata file on the current thread
void FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report,
FIRCLSSettings* settings,
FIRCLSInstallIdentifierModel* installIDModel,
FIRCLSFileManager* fileManager);
#endif
void FIRCLSContextBaseInit(void);

View File

@ -12,23 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FIRCLSContext.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSContext.h"
#include <stdlib.h>
#include <string.h>
#import "FIRCLSFileManager.h"
#import "FIRCLSInstallIdentifierModel.h"
#import "FIRCLSInternalReport.h"
#import "FIRCLSSettings.h"
#import "Crashlytics/Shared/FIRCLSConstants.h"
#include "FIRCLSApplication.h"
#include "FIRCLSCrashedMarkerFile.h"
#include "FIRCLSDefines.h"
#include "FIRCLSFeatures.h"
#include "FIRCLSGlobals.h"
#include "FIRCLSProcess.h"
#include "FIRCLSUtility.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSSettings.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSProcess.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
// The writable size is our handler stack plus whatever scratch we need. We have to use this space
// extremely carefully, however, because thread stacks always needs to be page-aligned. Only the
@ -51,7 +53,6 @@ static void FIRCLSContextAllocate(FIRCLSContext* context);
FIRCLSContextInitData FIRCLSContextBuildInitData(FIRCLSInternalReport* report,
FIRCLSSettings* settings,
FIRCLSInstallIdentifierModel* installIDModel,
FIRCLSFileManager* fileManager) {
// Because we need to start the crash reporter right away,
// it starts up either with default settings, or cached settings
@ -62,7 +63,6 @@ FIRCLSContextInitData FIRCLSContextBuildInitData(FIRCLSInternalReport* report,
memset(&initData, 0, sizeof(FIRCLSContextInitData));
initData.customBundleId = nil;
initData.installId = [installIDModel.installID UTF8String];
initData.sessionId = [[report identifier] UTF8String];
initData.rootPath = [[report path] UTF8String];
initData.previouslyCrashedFileRootPath = [[fileManager rootPath] UTF8String];
@ -72,6 +72,7 @@ FIRCLSContextInitData FIRCLSContextBuildInitData(FIRCLSInternalReport* report,
initData.maxErrorLogSize = [settings errorLogBufferSize];
initData.maxLogSize = [settings logBufferSize];
initData.maxKeyValues = [settings maxCustomKeys];
initData.betaToken = "";
// If this is set, then we could attempt to do a synchronous submission for certain kinds of
// events (exceptions). This is a very cool feature, but adds complexity to the backend. For now,
@ -98,10 +99,8 @@ FIRCLSContextInitData FIRCLSContextBuildInitData(FIRCLSInternalReport* report,
bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
FIRCLSSettings* settings,
FIRCLSInstallIdentifierModel* installIDModel,
FIRCLSFileManager* fileManager) {
FIRCLSContextInitData initDataObj =
FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager);
FIRCLSContextInitData initDataObj = FIRCLSContextBuildInitData(report, settings, fileManager);
FIRCLSContextInitData* initData = &initDataObj;
if (!initData) {
@ -194,13 +193,18 @@ bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
FIRCLSContextAppendToRoot(rootPath, fileName);
});
// To initialize Crashlytics handlers even if the Xcode debugger is attached, replace this check
// with YES. Note that this is only possible to do on an actual device as it will cause the
// simulator to crash.
if (!_firclsContext.readonly->debuggerAttached) {
#if CLS_SIGNAL_SUPPORTED
dispatch_group_async(group, queue, ^{
_firclsContext.readonly->signal.path =
FIRCLSContextAppendToRoot(rootPath, FIRCLSReportSignalFile);
FIRCLSSignalInitialize(&_firclsContext.readonly->signal);
});
#endif
#if CLS_MACH_EXCEPTION_SUPPORTED
dispatch_group_async(group, queue, ^{
@ -255,24 +259,6 @@ bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
return true;
}
void FIRCLSContextUpdateMetadata(FIRCLSInternalReport* report,
FIRCLSSettings* settings,
FIRCLSInstallIdentifierModel* installIDModel,
FIRCLSFileManager* fileManager) {
FIRCLSContextInitData initDataObj =
FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager);
FIRCLSContextInitData* initData = &initDataObj;
NSString* rootPath = [NSString stringWithUTF8String:initData->rootPath];
const char* metaDataPath =
[[rootPath stringByAppendingPathComponent:FIRCLSReportMetadataFile] fileSystemRepresentation];
if (!FIRCLSContextRecordMetadata(metaDataPath, initData)) {
FIRCLSErrorLog(@"Unable to update context metadata");
}
}
void FIRCLSContextBaseInit(void) {
NSString* sdkBundleID = FIRCLSApplicationGetSDKBundleID();
@ -397,13 +383,15 @@ static bool FIRCLSContextRecordIdentity(FIRCLSFile* file, const FIRCLSContextIni
FIRCLSFileWriteHashStart(file);
FIRCLSFileWriteHashEntryString(file, "generator", CLS_SDK_GENERATOR_NAME);
FIRCLSFileWriteHashEntryString(file, "display_version", CLS_SDK_DISPLAY_VERSION);
FIRCLSFileWriteHashEntryString(file, "build_version", CLS_SDK_DISPLAY_VERSION);
FIRCLSFileWriteHashEntryString(file, "generator", FIRCLSSDKGeneratorName().UTF8String);
FIRCLSFileWriteHashEntryString(file, "display_version", FIRCLSSDKVersion().UTF8String);
FIRCLSFileWriteHashEntryString(file, "build_version", FIRCLSSDKVersion().UTF8String);
FIRCLSFileWriteHashEntryUint64(file, "started_at", time(NULL));
FIRCLSFileWriteHashEntryString(file, "session_id", initData->sessionId);
FIRCLSFileWriteHashEntryString(file, "install_id", initData->installId);
// install_id is written into the proto directly. This is only left here to
// support Apple Report Converter.
FIRCLSFileWriteHashEntryString(file, "install_id", "");
FIRCLSFileWriteHashEntryString(file, "beta_token", initData->betaToken);
FIRCLSFileWriteHashEntryBoolean(file, "absolute_log_timestamps", true);

View File

@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FIRCLSCrashedMarkerFile.h"
#include "FIRCLSFile.h"
#include "FIRCLSUtility.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
const char *FIRCLSCrashedMarkerFileName = "previously-crashed";

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FIRCLSContext.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSContext.h"
__BEGIN_DECLS

View File

@ -17,7 +17,7 @@
#include <mach/vm_types.h>
#include <sys/cdefs.h>
#include "FIRCLSFile.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
typedef struct {
const char* documentDirectoryPath;
@ -34,4 +34,6 @@ bool FIRCLSHostRecord(FIRCLSFile* file);
void FIRCLSHostWriteDiskUsage(FIRCLSFile* file);
bool FIRCLSHostIsRosettaTranslated(void);
__END_DECLS

View File

@ -12,18 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FIRCLSHost.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSHost.h"
#include <mach/mach.h>
#include <sys/mount.h>
#include <sys/sysctl.h>
#import "FIRCLSApplication.h"
#include "FIRCLSDefines.h"
#import "FIRCLSFABHost.h"
#include "FIRCLSFile.h"
#include "FIRCLSGlobals.h"
#include "FIRCLSUtility.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#import "Crashlytics/Shared/FIRCLSFABHost.h"
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
@ -32,9 +32,10 @@
#endif
#define CLS_HOST_SYSCTL_BUFFER_SIZE (128)
#define CLS_MAX_ARM64_NATIVE_PAGE_SIZE (1024 * 16)
#if CLS_CPU_ARM64
#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 16)
#define CLS_MAX_NATIVE_PAGE_SIZE CLS_MAX_ARM64_NATIVE_PAGE_SIZE
#else
// return 4K, which is correct for all platforms except arm64, currently
#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 4)
@ -68,22 +69,32 @@ vm_size_t FIRCLSHostGetPageSize(void) {
// these types. Turns out that sysctl will not init the data to zero, but it appears
// that sysctlbyname does. This API is nicer, but that's important to keep in mind.
int maxNativePageSize = CLS_MAX_NATIVE_PAGE_SIZE;
// On Apple Silicon, we need to use the arm64 page size
// even if we're in x86 land.
if (FIRCLSHostIsRosettaTranslated()) {
FIRCLSSDKLog("Running under Rosetta 2 emulation. Using the arm64 page size.\n");
maxNativePageSize = CLS_MAX_ARM64_NATIVE_PAGE_SIZE;
}
pageSize = 0;
size = sizeof(pageSize);
if (sysctlbyname("hw.pagesize", &pageSize, &size, NULL, 0) != 0) {
FIRCLSSDKLog("sysctlbyname failed while trying to get hw.pagesize\n");
return CLS_MAX_NATIVE_PAGE_SIZE;
return maxNativePageSize;
}
// if the returned size is not the expected value, abort
if (size != sizeof(pageSize)) {
return CLS_MAX_NATIVE_PAGE_SIZE;
return maxNativePageSize;
}
// put in some guards to make sure our size is reasonable
if (pageSize > CLS_MAX_NATIVE_PAGE_SIZE) {
return CLS_MAX_NATIVE_PAGE_SIZE;
if (pageSize > maxNativePageSize) {
return maxNativePageSize;
}
if (pageSize < CLS_MIN_NATIVE_PAGE_SIZE) {
@ -93,6 +104,29 @@ vm_size_t FIRCLSHostGetPageSize(void) {
return pageSize;
}
// This comes from the Apple documentation here:
// https://developer.apple.com/documentation/apple_silicon/about_the_rosetta_translation_environment
bool FIRCLSHostIsRosettaTranslated() {
#if TARGET_OS_MAC
int result = 0;
size_t size = sizeof(result);
if (sysctlbyname("sysctl.proc_translated", &result, &size, NULL, 0) == -1) {
// If we get an error, or 0, we're going to treat this as x86_64 macOS native
if (errno == ENOENT) {
return false;
}
// This is the error case
FIRCLSSDKLog("sysctlbyname failed while trying to get sysctl.proc_translated for Rosetta 2 "
"translation\n");
return false;
}
return result == 1;
#else
return false;
#endif
}
static void FIRCLSHostWriteSysctlEntry(
FIRCLSFile* file, const char* key, const char* sysctlKey, void* buffer, size_t bufferSize) {
if (sysctlbyname(sysctlKey, buffer, &bufferSize, NULL, 0) != 0) {
@ -124,6 +158,8 @@ static void FIRCLSHostWriteOSVersionInfo(FIRCLSFile* file) {
FIRCLSFileWriteHashEntryString(file, "os_display_version",
[FIRCLSHostOSDisplayVersion() UTF8String]);
FIRCLSFileWriteHashEntryString(file, "platform", [FIRCLSApplicationGetPlatform() UTF8String]);
FIRCLSFileWriteHashEntryString(file, "firebase_platform",
[FIRCLSApplicationGetFirebasePlatform() UTF8String]);
}
bool FIRCLSHostRecord(FIRCLSFile* file) {

View File

@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FIRCLSProcess.h"
#include "FIRCLSDefines.h"
#include "FIRCLSFeatures.h"
#include "FIRCLSGlobals.h"
#include "FIRCLSProfiling.h"
#include "FIRCLSThreadState.h"
#include "FIRCLSUnwind.h"
#include "FIRCLSUtility.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSProcess.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSThreadState.h"
#include "Crashlytics/Crashlytics/Unwind/FIRCLSUnwind.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#include <dispatch/dispatch.h>
#include <objc/message.h>
@ -57,10 +57,6 @@ bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uap
return true;
}
bool FIRCLSProcessDestroy(FIRCLSProcess *process) {
return false;
}
// https://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
bool FIRCLSProcessDebuggerAttached(void) {
int junk;
@ -167,7 +163,7 @@ static bool FIRCLSProcessGetThreadState(FIRCLSProcess *process,
thread_t thread,
FIRCLSThreadContext *context) {
if (!FIRCLSIsValidPointer(context)) {
FIRCLSSDKLogError("invalid context supplied");
FIRCLSSDKLogError("Invalid context supplied\n");
return false;
}
@ -216,9 +212,14 @@ static bool FIRCLSProcessGetThreadState(FIRCLSProcess *process,
#if !TARGET_OS_WATCH
// try to get the value by querying the thread state
mach_msg_type_number_t stateCount = FIRCLSThreadStateCount;
if (thread_get_state(thread, FIRCLSThreadState, (thread_state_t)(&(context->__ss)),
&stateCount) != KERN_SUCCESS) {
FIRCLSSDKLogError("failed to get thread state\n");
// For unknown reasons, thread_get_state returns this value on Rosetta,
// but still succeeds.
const int ROSETTA_SUCCESS = 268435459;
kern_return_t status = thread_get_state(thread, FIRCLSThreadState, (thread_state_t)(&(context->__ss)),
&stateCount);
if (status != KERN_SUCCESS && status != ROSETTA_SUCCESS) {
FIRCLSSDKLogError("Failed to get thread state via thread_get_state for thread: %i\n", thread);
return false;
}
@ -254,7 +255,7 @@ static const char *FIRCLSProcessGetThreadDispatchQueueName(FIRCLSProcess *proces
infoCount = THREAD_IDENTIFIER_INFO_COUNT;
if (thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&info, &infoCount) !=
KERN_SUCCESS) {
FIRCLSSDKLog("unable to get thread info\n");
FIRCLSSDKLog("Unable to get thread info\n");
return NULL;
}
@ -390,12 +391,12 @@ static bool FIRCLSProcessRecordThread(FIRCLSProcess *process, thread_t thread, F
FIRCLSThreadContext context;
if (!FIRCLSProcessGetThreadState(process, thread, &context)) {
FIRCLSSDKLogError("unable to get thread state");
FIRCLSSDKLogError("Unable to get thread state\n");
return false;
}
if (!FIRCLSUnwindInit(&unwindContext, context)) {
FIRCLSSDKLog("unable to init unwind context\n");
FIRCLSSDKLog("Unable to init unwind context\n");
return false;
}
@ -485,6 +486,11 @@ bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file) {
FIRCLSSDKLogInfo("recording thread %d data\n", i);
if (!FIRCLSProcessRecordThread(process, thread, file)) {
FIRCLSSDKLogError("Failed to record thread state. Closing threads JSON to prevent malformed crash report.\n");
FIRCLSFileWriteArrayEnd(file);
FIRCLSFileWriteSectionEnd(file);
return false;
}
}
@ -493,7 +499,7 @@ bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file) {
FIRCLSFileWriteSectionEnd(file);
FIRCLSSDKLogInfo("completed recording all thread data\n");
FIRCLSSDKLogInfo("Completed recording all thread data\n");
return true;
}
@ -541,6 +547,11 @@ void FIRCLSProcessRecordDispatchQueueNames(FIRCLSProcess *process, FIRCLSFile *f
name = FIRCLSProcessGetThreadDispatchQueueName(process, thread);
// Apple Report Converter will fail to parse this when "name" is null,
// so we will use an empty string instead.
if (name == NULL) {
name = "";
}
FIRCLSFileWriteArrayEntryString(file, name);
}
@ -795,6 +806,12 @@ static void FIRCLSProcessRecordCrashInfo(FIRCLSFile *file) {
continue;
}
// The crash_info_t's message may contain the device's UDID, in this case,
// make sure that we do our best to redact that information before writing the
// rest of the message to disk. This also has the effect of not uploading that
// information in the subsequent crash report.
FIRCLSRedactUUID(string);
FIRCLSFileWriteArrayEntryHexEncodedString(file, string);
}
}

View File

@ -17,7 +17,7 @@
#include <mach/mach.h>
#include <stdbool.h>
#include "FIRCLSFile.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
typedef struct {
// task info
@ -32,7 +32,6 @@ typedef struct {
} FIRCLSProcess;
bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uapVoid);
bool FIRCLSProcessDestroy(FIRCLSProcess *process);
bool FIRCLSProcessDebuggerAttached(void);
bool FIRCLSProcessSuspendAllOtherThreads(FIRCLSProcess *process);

View File

@ -14,7 +14,7 @@
#pragma once
#include "FIRCLSFile.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
__BEGIN_DECLS
@ -75,6 +75,7 @@ void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext* roContext,
#ifdef __OBJC__
void FIRCLSUserLoggingRecordUserKeyValue(NSString* key, id value);
void FIRCLSUserLoggingRecordUserKeysAndValues(NSDictionary* keysAndValues);
void FIRCLSUserLoggingRecordInternalKeyValue(NSString* key, id value);
void FIRCLSUserLoggingWriteInternalKeyValue(NSString* key, NSString* value);
@ -89,6 +90,10 @@ void FIRCLSUserLoggingRecordKeyValue(NSString* key,
FIRCLSUserLoggingKVStorage* storage,
uint32_t* counter);
void FIRCLSUserLoggingRecordKeysAndValues(NSDictionary* keysAndValues,
FIRCLSUserLoggingKVStorage* storage,
uint32_t* counter);
void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage* storage,
const char** activePath,
void (^openedFileBlock)(FIRCLSFile* file));
@ -96,6 +101,11 @@ void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage* storage,
NSArray* FIRCLSUserLoggingStoredKeyValues(const char* path);
OBJC_EXTERN void FIRCLSLog(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
OBJC_EXTERN void FIRCLSLogToStorage(FIRCLSUserLoggingABStorage* storage,
const char** activePath,
NSString* format,
...) NS_FORMAT_FUNCTION(3, 4);
#endif
__END_DECLS

View File

@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "FIRCLSUserLogging.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h"
#include <sys/time.h>
#include "FIRCLSGlobals.h"
#include "FIRCLSUtility.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#import "FIRCLSReportManager_Private.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h"
NSString *const FIRCLSStartTimeKey = @"com.crashlytics.kit-start-time";
NSString *const FIRCLSFirstRunloopTurnTimeKey = @"com.crashlytics.first-run-loop-time";
@ -33,17 +33,21 @@ NSString *const FIRCLSDevelopmentPlatformNameKey = @"com.crashlytics.development
NSString *const FIRCLSDevelopmentPlatformVersionKey =
@"com.crashlytics.development-platform-version";
// Empty string object synchronized on to prevent a race condition when accessing AB file path
NSString *const FIRCLSSynchronizedPathKey = @"";
const uint32_t FIRCLSUserLoggingMaxKVEntries = 64;
#pragma mark - Prototypes
static void FIRCLSUserLoggingWriteKeyValue(NSString *key,
NSString *value,
FIRCLSUserLoggingKVStorage *storage,
uint32_t *counter);
static void FIRCLSUserLoggingWriteKeysAndValues(NSDictionary *keysAndValues,
FIRCLSUserLoggingKVStorage *storage,
uint32_t *counter);
static void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage,
const char **activePath,
off_t fileSize);
void FIRCLSLogInternal(NSString *message);
void FIRCLSLogInternal(FIRCLSUserLoggingABStorage *storage,
const char **activePath,
NSString *message);
#pragma mark - Setup
void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext *roContext,
@ -68,8 +72,10 @@ void FIRCLSUserLoggingRecordInternalKeyValue(NSString *key, id value) {
void FIRCLSUserLoggingWriteInternalKeyValue(NSString *key, NSString *value) {
// Unsynchronized - must be run on the correct queue
FIRCLSUserLoggingWriteKeyValue(key, value, &_firclsContext.readonly->logging.internalKVStorage,
&_firclsContext.writable->logging.internalKVCount);
NSDictionary *keysAndValues = key ? @{key : value ?: [NSNull null]} : nil;
FIRCLSUserLoggingWriteKeysAndValues(keysAndValues,
&_firclsContext.readonly->logging.internalKVStorage,
&_firclsContext.writable->logging.internalKVCount);
}
void FIRCLSUserLoggingRecordUserKeyValue(NSString *key, id value) {
@ -77,6 +83,12 @@ void FIRCLSUserLoggingRecordUserKeyValue(NSString *key, id value) {
&_firclsContext.writable->logging.userKVCount);
}
void FIRCLSUserLoggingRecordUserKeysAndValues(NSDictionary *keysAndValues) {
FIRCLSUserLoggingRecordKeysAndValues(keysAndValues,
&_firclsContext.readonly->logging.userKVStorage,
&_firclsContext.writable->logging.userKVCount);
}
static id FIRCLSUserLoggingGetComponent(NSDictionary *entry,
NSString *componentName,
bool decodeHex) {
@ -140,6 +152,30 @@ NSDictionary *FIRCLSUserLoggingGetCompactedKVEntries(FIRCLSUserLoggingKVStorage
return finalKVSet;
}
static void FIRCLSUserLoggingWriteKVEntriesToFile(
NSDictionary<NSString *, NSString *> *keysAndValues, BOOL shouldHexEncode, FIRCLSFile *file) {
for (NSString *key in keysAndValues) {
NSString *valueObject = [keysAndValues objectForKey:key];
// map `NSNull` into nil
const char *value = (valueObject == (NSString *)[NSNull null] ? nil : [valueObject UTF8String]);
FIRCLSFileWriteSectionStart(file, "kv");
FIRCLSFileWriteHashStart(file);
if (shouldHexEncode) {
FIRCLSFileWriteHashEntryHexEncodedString(file, "key", [key UTF8String]);
FIRCLSFileWriteHashEntryHexEncodedString(file, "value", value);
} else {
FIRCLSFileWriteHashEntryString(file, "key", [key UTF8String]);
FIRCLSFileWriteHashEntryString(file, "value", value);
}
FIRCLSFileWriteHashEnd(file);
FIRCLSFileWriteSectionEnd(file);
}
}
void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage *storage) {
if (!FIRCLSIsValidPointer(storage)) {
FIRCLSSDKLogError("Error: storage invalid\n");
@ -167,24 +203,14 @@ void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage *storage) {
// but it's very uncommon to go down this path.
NSArray *keys = [finalKVs allKeys];
FIRCLSSDKLogInfo("Truncating KV set, which is above max %d\n", maxCount);
FIRCLSSDKLogInfo("Truncating %d keys from KV set, which is above max %d\n",
(uint32_t)(finalKVs.count - maxCount), maxCount);
finalKVs =
[finalKVs dictionaryWithValuesForKeys:[keys subarrayWithRange:NSMakeRange(0, maxCount)]];
}
for (NSString *key in finalKVs) {
NSString *value = [finalKVs objectForKey:key];
FIRCLSFileWriteSectionStart(&file, "kv");
FIRCLSFileWriteHashStart(&file);
// tricky - the values stored incrementally have already been hex-encoded
FIRCLSFileWriteHashEntryString(&file, "key", [key UTF8String]);
FIRCLSFileWriteHashEntryString(&file, "value", [value UTF8String]);
FIRCLSFileWriteHashEnd(&file);
FIRCLSFileWriteSectionEnd(&file);
}
FIRCLSUserLoggingWriteKVEntriesToFile(finalKVs, false, &file);
FIRCLSFileClose(&file);
if (unlink(storage->incrementalPath) != 0) {
@ -202,33 +228,59 @@ void FIRCLSUserLoggingRecordKeyValue(NSString *key,
return;
}
// ensure that any invalid pointer is actually set to nil
if (!FIRCLSIsValidPointer(value) && value != nil) {
FIRCLSSDKLogWarn("Bad value pointer being clamped to nil\n");
value = nil;
}
NSDictionary *keysAndValues = @{key : (value ?: [NSNull null])};
FIRCLSUserLoggingRecordKeysAndValues(keysAndValues, storage, counter);
}
void FIRCLSUserLoggingRecordKeysAndValues(NSDictionary *keysAndValues,
FIRCLSUserLoggingKVStorage *storage,
uint32_t *counter) {
if (!FIRCLSContextIsInitialized()) {
return;
}
if ([value respondsToSelector:@selector(description)]) {
value = [value description];
} else {
// passing nil will result in a JSON null being written, which is deserialized as [NSNull null],
// signaling to remove the key during compaction
value = nil;
if (keysAndValues.count == 0) {
FIRCLSSDKLogWarn("User provided empty key/value dictionary\n");
return;
}
if (!FIRCLSIsValidPointer(keysAndValues)) {
FIRCLSSDKLogWarn("User provided bad key/value dictionary\n");
return;
}
NSMutableDictionary *sanitizedKeysAndValues = [keysAndValues mutableCopy];
for (NSString *key in keysAndValues) {
if (!FIRCLSIsValidPointer(key)) {
FIRCLSSDKLogWarn("User provided bad key\n");
return;
}
id value = keysAndValues[key];
// ensure that any invalid pointer is actually set to nil
if (!FIRCLSIsValidPointer(value) && value != nil) {
FIRCLSSDKLogWarn("Bad value pointer being clamped to nil\n");
sanitizedKeysAndValues[key] = [NSNull null];
}
if ([value respondsToSelector:@selector(description)]) {
sanitizedKeysAndValues[key] = [value description];
} else {
// passing nil will result in a JSON null being written, which is deserialized as [NSNull
// null], signaling to remove the key during compaction
sanitizedKeysAndValues[key] = [NSNull null];
}
}
dispatch_sync(FIRCLSGetLoggingQueue(), ^{
FIRCLSUserLoggingWriteKeyValue(key, value, storage, counter);
FIRCLSUserLoggingWriteKeysAndValues(sanitizedKeysAndValues, storage, counter);
});
}
static void FIRCLSUserLoggingWriteKeyValue(NSString *key,
NSString *value,
FIRCLSUserLoggingKVStorage *storage,
uint32_t *counter) {
static void FIRCLSUserLoggingWriteKeysAndValues(NSDictionary *keysAndValues,
FIRCLSUserLoggingKVStorage *storage,
uint32_t *counter) {
FIRCLSFile file;
if (!FIRCLSIsValidPointer(storage) || !FIRCLSIsValidPointer(counter)) {
@ -241,16 +293,10 @@ static void FIRCLSUserLoggingWriteKeyValue(NSString *key,
return;
}
FIRCLSFileWriteSectionStart(&file, "kv");
FIRCLSFileWriteHashStart(&file);
FIRCLSFileWriteHashEntryHexEncodedString(&file, "key", [key UTF8String]);
FIRCLSFileWriteHashEntryHexEncodedString(&file, "value", [value UTF8String]);
FIRCLSFileWriteHashEnd(&file);
FIRCLSFileWriteSectionEnd(&file);
FIRCLSUserLoggingWriteKVEntriesToFile(keysAndValues, true, &file);
FIRCLSFileClose(&file);
*counter += 1;
*counter += keysAndValues.count;
if (*counter >= storage->maxIncrementalCount) {
dispatch_async(FIRCLSGetLoggingQueue(), ^{
FIRCLSUserLoggingCompactKVEntries(storage);
@ -356,7 +402,26 @@ void FIRCLSLog(NSString *format, ...) {
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
FIRCLSLogInternal(msg);
FIRCLSUserLoggingABStorage *currentStorage = &_firclsContext.readonly->logging.logStorage;
const char **activePath = &_firclsContext.writable->logging.activeUserLogPath;
FIRCLSLogInternal(currentStorage, activePath, msg);
}
void FIRCLSLogToStorage(FIRCLSUserLoggingABStorage *storage,
const char **activePath,
NSString *format,
...) {
// If the format is nil do nothing just like NSLog.
if (!format) {
return;
}
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
FIRCLSLogInternal(storage, activePath, msg);
}
#pragma mark - Properties
@ -426,7 +491,9 @@ void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage,
[[NSFileManager defaultManager] removeItemAtPath:pathString error:nil];
}
*activePath = otherPath;
@synchronized(FIRCLSSynchronizedPathKey) {
*activePath = otherPath;
}
}
void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage *storage,
@ -436,8 +503,10 @@ void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage *storage,
return;
}
if (!*activePath) {
return;
@synchronized(FIRCLSSynchronizedPathKey) {
if (!*activePath) {
return;
}
}
if (storage->restrictBySize) {
@ -484,7 +553,9 @@ void FIRCLSLogInternalWrite(FIRCLSFile *file, NSString *message, uint64_t time)
FIRCLSFileWriteSectionEnd(file);
}
void FIRCLSLogInternal(NSString *message) {
void FIRCLSLogInternal(FIRCLSUserLoggingABStorage *storage,
const char **activePath,
NSString *message) {
if (!message) {
return;
}
@ -498,7 +569,7 @@ void FIRCLSLogInternal(NSString *message) {
struct timeval te;
NSUInteger messageLength = [message length];
int maxLogSize = _firclsContext.readonly->logging.logStorage.maxSize;
int maxLogSize = storage->maxSize;
if (messageLength > maxLogSize) {
FIRCLSWarningLog(
@ -515,9 +586,7 @@ void FIRCLSLogInternal(NSString *message) {
const uint64_t time = te.tv_sec * 1000LL + te.tv_usec / 1000;
FIRCLSUserLoggingWriteAndCheckABFiles(&_firclsContext.readonly->logging.logStorage,
&_firclsContext.writable->logging.activeUserLogPath,
^(FIRCLSFile *file) {
FIRCLSLogInternalWrite(file, message, time);
});
FIRCLSUserLoggingWriteAndCheckABFiles(storage, activePath, ^(FIRCLSFile *file) {
FIRCLSLogInternalWrite(file, message, time);
});
}

View File

@ -0,0 +1,55 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
NS_ASSUME_NONNULL_BEGIN
@class FIRCLSSettings;
@protocol FIRAnalyticsInterop;
@protocol FIRAnalyticsInteropListener;
/*
* Registers a listener for Analytics events in Crashlytics
* logs (aka. breadcrumbs), and sends events to the
* Analytics SDK for Crash Free Users.
*/
@interface FIRCLSAnalyticsManager : NSObject
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
/*
* Starts listening for Analytics events for Breadcrumbs.
*/
- (void)registerAnalyticsListener;
/*
* Logs a Crashlytics crash session to Firebase Analytics for Crash Free Users.
* @param crashTimeStamp The time stamp of the crash to be logged.
*/
+ (void)logCrashWithTimeStamp:(NSTimeInterval)crashTimeStamp
toAnalytics:(id<FIRAnalyticsInterop>)analytics;
/*
* Public for testing.
*/
NSString *FIRCLSFIRAEventDictionaryToJSON(NSDictionary *eventAsDictionary);
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,135 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Crashlytics/Crashlytics/Controllers/FIRCLSAnalyticsManager.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSInternalLogging.h"
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
#import "Interop/Analytics/Public/FIRAnalyticsInteropListener.h"
static NSString *FIRCLSFirebaseAnalyticsEventLogFormat = @"$A$:%@";
// Origin for events and user properties generated by Crashlytics.
static NSString *const kFIREventOriginCrash = @"clx";
// App exception event name.
static NSString *const kFIREventAppException = @"_ae";
// Timestamp key for the event payload.
static NSString *const kFIRParameterTimestamp = @"timestamp";
// Fatal key for the event payload.
static NSString *const kFIRParameterFatal = @"fatal";
FOUNDATION_STATIC_INLINE NSNumber *timeIntervalInMillis(NSTimeInterval timeInterval) {
return @(llrint(timeInterval * 1000.0));
}
@interface FIRCLSAnalyticsManager () <FIRAnalyticsInteropListener>
@property(nonatomic, strong) id<FIRAnalyticsInterop> analytics;
@property(nonatomic, assign) BOOL registeredAnalyticsEventListener;
@end
@implementation FIRCLSAnalyticsManager
- (instancetype)initWithAnalytics:(nullable id<FIRAnalyticsInterop>)analytics {
self = [super init];
if (!self) {
return nil;
}
_analytics = analytics;
return self;
}
- (void)registerAnalyticsListener {
if (self.registeredAnalyticsEventListener) {
return;
}
if (self.analytics == nil) {
FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports:Event",
"Firebase Analytics SDK not detected. Crash-free statistics and "
"breadcrumbs will not be reported");
return;
}
[self.analytics registerAnalyticsListener:self withOrigin:kFIREventOriginCrash];
FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports:Event",
"Registered Firebase Analytics event listener to receive breadcrumb logs");
self.registeredAnalyticsEventListener = YES;
}
- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters {
NSDictionary *event = @{
@"name" : name,
@"parameters" : parameters,
};
NSString *json = FIRCLSFIRAEventDictionaryToJSON(event);
if (json != nil) {
FIRCLSLog(FIRCLSFirebaseAnalyticsEventLogFormat, json);
}
}
+ (void)logCrashWithTimeStamp:(NSTimeInterval)crashTimeStamp
toAnalytics:(id<FIRAnalyticsInterop>)analytics {
if (analytics == nil) {
return;
}
FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports:Event",
"Sending app_exception event to Firebase Analytics for crash-free statistics");
NSDictionary *params = @{
kFIRParameterTimestamp : timeIntervalInMillis(crashTimeStamp),
kFIRParameterFatal : @(INT64_C(1))
};
[analytics logEventWithOrigin:kFIREventOriginCrash name:kFIREventAppException parameters:params];
}
NSString *FIRCLSFIRAEventDictionaryToJSON(NSDictionary *eventAsDictionary) {
NSError *error = nil;
if (eventAsDictionary == nil) {
return nil;
}
if (![NSJSONSerialization isValidJSONObject:eventAsDictionary]) {
FIRCLSSDKLog("Firebase Analytics event is not valid JSON");
return nil;
}
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:eventAsDictionary
options:0
error:&error];
if (error == nil) {
NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return json;
} else {
FIRCLSSDKLog("Unable to convert Firebase Analytics event to json");
return nil;
}
}
@end

View File

@ -0,0 +1,83 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class FIRCLSManagerData;
@class FIRCLSReportUploader;
@class FIRCLSDataCollectionToken;
@class FIRCrashlyticsReport;
FOUNDATION_EXPORT NSUInteger const FIRCLSMaxUnsentReports;
@interface FIRCLSExistingReportManager : NSObject
/**
* Returns the number of unsent reports on the device, ignoring empty reports in
* the active folder, and ignoring any reports in "processing" or "prepared".
*
* In the past, this would count reports in the processed or prepared
* folders. This has been changed because reports in those paths have already
* been cleared for upload, so there isn't any point in asking for permission
* or possibly spamming end-users if a report gets stuck.
*
* The tricky part is, customers will NOT be alerted in checkForUnsentReports
* for reports in these paths, but when they choose sendUnsentReports / enable data
* collection, reports in those directories will be re-managed. This should be ok and
* just an edge case because reports should only be in processing or prepared for a split second as
* they do on-device symbolication and get converted into a GDTEvent. After a report is handed off
* to GoogleDataTransport, it is uploaded regardless of Crashlytics data collection.
*/
@property(nonatomic, readonly) NSUInteger unsentReportsCount;
/**
* This value needs to stay in sync with numUnsentReports, so if there is > 0 numUnsentReports,
* newestUnsentReport needs to return a value. Otherwise it needs to return null.
*
* FIRCLSContext needs to be initialized before the FIRCrashlyticsReport is instantiated.
*/
@property(nonatomic, readonly) FIRCrashlyticsReport *_Nullable newestUnsentReport;
- (instancetype)initWithManagerData:(FIRCLSManagerData *)managerData
reportUploader:(FIRCLSReportUploader *)reportUploader;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
/**
* This is important to call once, early in startup, before the
* new report for this run of the app has been created. Any
* reports in ExistingReportManager will be uploaded or deleted
* and we don't want to do that for the current run of the app.
*
* If there are over MAX_UNSENT_REPORTS valid reports, this will delete them.
*
* This methods is slow and should be called only once.
*/
- (void)collectExistingReports;
/**
* This is the side-effect of calling deleteUnsentReports, or collect_reports setting
* being false.
*/
- (void)deleteUnsentReports;
- (void)sendUnsentReportsWithToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,225 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Crashlytics/Crashlytics/Controllers/FIRCLSExistingReportManager.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h"
#import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
#import "Crashlytics/Crashlytics/Private/FIRCrashlyticsReport_Private.h"
#import "Crashlytics/Crashlytics/Public/FirebaseCrashlytics/FIRCrashlyticsReport.h"
// This value should stay in sync with the Android SDK
NSUInteger const FIRCLSMaxUnsentReports = 4;
@interface FIRCLSExistingReportManager ()
@property(nonatomic, strong) FIRCLSFileManager *fileManager;
@property(nonatomic, strong) FIRCLSReportUploader *reportUploader;
@property(nonatomic, strong) NSOperationQueue *operationQueue;
// This list of active reports excludes the brand new active report that will be created this run of
// the app.
@property(nonatomic, strong) NSArray *existingUnemptyActiveReportPaths;
@property(nonatomic, strong) NSArray *processingReportPaths;
@property(nonatomic, strong) NSArray *preparedReportPaths;
@property(nonatomic, strong) FIRCLSInternalReport *newestInternalReport;
@end
@implementation FIRCLSExistingReportManager
- (instancetype)initWithManagerData:(FIRCLSManagerData *)managerData
reportUploader:(FIRCLSReportUploader *)reportUploader {
self = [super init];
if (!self) {
return nil;
}
_fileManager = managerData.fileManager;
_operationQueue = managerData.operationQueue;
_reportUploader = reportUploader;
return self;
}
NSInteger compareNewer(FIRCLSInternalReport *reportA,
FIRCLSInternalReport *reportB,
void *context) {
// Compare naturally sorts with oldest first, so swap A and B
return [reportB.dateCreated compare:reportA.dateCreated];
}
- (void)collectExistingReports {
self.existingUnemptyActiveReportPaths =
[self getUnsentActiveReportsAndDeleteEmptyOrOld:self.fileManager.activePathContents];
self.processingReportPaths = self.fileManager.processingPathContents;
self.preparedReportPaths = self.fileManager.preparedPathContents;
}
- (FIRCrashlyticsReport *)newestUnsentReport {
if (self.unsentReportsCount <= 0) {
return nil;
}
return [[FIRCrashlyticsReport alloc] initWithInternalReport:self.newestInternalReport];
}
- (NSUInteger)unsentReportsCount {
// There are nuances about why we only count active reports.
// See the header comment for more information.
return self.existingUnemptyActiveReportPaths.count;
}
/*
* This has the side effect of deleting any reports over the max, starting with oldest reports.
*/
- (NSArray<NSString *> *)getUnsentActiveReportsAndDeleteEmptyOrOld:(NSArray *)reportPaths {
NSMutableArray<FIRCLSInternalReport *> *validReports = [NSMutableArray array];
for (NSString *path in reportPaths) {
FIRCLSInternalReport *_Nullable report = [FIRCLSInternalReport reportWithPath:path];
if (!report) {
continue;
}
// Delete reports without any crashes or non-fatals
if (![report hasAnyEvents]) {
[self.operationQueue addOperationWithBlock:^{
[self.fileManager removeItemAtPath:path];
}];
continue;
}
[validReports addObject:report];
}
if (validReports.count == 0) {
return @[];
}
// Sort with the newest at the end
[validReports sortUsingFunction:compareNewer context:nil];
// Set our report for updating in checkAndUpdateUnsentReports
self.newestInternalReport = [validReports firstObject];
// Delete any reports above the limit, starting with the oldest
// which should be at the start of the array.
if (validReports.count > FIRCLSMaxUnsentReports) {
NSUInteger deletingCount = validReports.count - FIRCLSMaxUnsentReports;
FIRCLSInfoLog(@"Deleting %lu unsent reports over the limit of %lu to prevent disk space from "
@"filling up. To prevent this make sure to call send/deleteUnsentReports.",
deletingCount, FIRCLSMaxUnsentReports);
}
// Not that validReports is sorted, delete any reports at indices > MAX_UNSENT_REPORTS, and
// collect the rest of the reports to return.
NSMutableArray<NSString *> *validReportPaths = [NSMutableArray array];
for (int i = 0; i < validReports.count; i++) {
if (i >= FIRCLSMaxUnsentReports) {
[self.operationQueue addOperationWithBlock:^{
NSString *path = [[validReports objectAtIndex:i] path];
[self.fileManager removeItemAtPath:path];
}];
} else {
[validReportPaths addObject:[[validReports objectAtIndex:i] path]];
}
}
return validReportPaths;
}
- (void)sendUnsentReportsWithToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent {
for (NSString *path in self.existingUnemptyActiveReportPaths) {
[self processExistingActiveReportPath:path
dataCollectionToken:dataCollectionToken
asUrgent:urgent];
}
// deal with stuff in processing more carefully - do not process again
[self.operationQueue addOperationWithBlock:^{
for (NSString *path in self.processingReportPaths) {
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
[self.reportUploader prepareAndSubmitReport:report
dataCollectionToken:dataCollectionToken
asUrgent:NO
withProcessing:NO];
}
}];
// Because this could happen quite a bit after the inital set of files was
// captured, some could be completed (deleted). So, just double-check to make sure
// the file still exists.
[self.operationQueue addOperationWithBlock:^{
for (NSString *path in self.preparedReportPaths) {
if (![[self.fileManager underlyingFileManager] fileExistsAtPath:path]) {
continue;
}
[self.reportUploader uploadPackagedReportAtPath:path
dataCollectionToken:dataCollectionToken
asUrgent:NO];
}
}];
}
- (void)processExistingActiveReportPath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent {
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
// TODO: hasAnyEvents should really be called on the background queue.
if (![report hasAnyEvents]) {
[self.operationQueue addOperationWithBlock:^{
[self.fileManager removeItemAtPath:path];
}];
return;
}
if (urgent && [dataCollectionToken isValid]) {
// We can proceed without the delegate.
[self.reportUploader prepareAndSubmitReport:report
dataCollectionToken:dataCollectionToken
asUrgent:urgent
withProcessing:YES];
return;
}
[self.operationQueue addOperationWithBlock:^{
[self.reportUploader prepareAndSubmitReport:report
dataCollectionToken:dataCollectionToken
asUrgent:NO
withProcessing:YES];
}];
}
- (void)deleteUnsentReports {
NSArray<NSString *> *reportPaths = @[];
reportPaths = [reportPaths arrayByAddingObjectsFromArray:self.existingUnemptyActiveReportPaths];
reportPaths = [reportPaths arrayByAddingObjectsFromArray:self.processingReportPaths];
reportPaths = [reportPaths arrayByAddingObjectsFromArray:self.preparedReportPaths];
[self.operationQueue addOperationWithBlock:^{
for (NSString *path in reportPaths) {
[self.fileManager removeItemAtPath:path];
}
}];
}
@end

View File

@ -0,0 +1,84 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@class FIRCLSFileManager;
@class FIRInstallations;
@class FIRCLSDataCollectionArbiter;
@class FIRCLSApplicationIdentifierModel;
@class FIRCLSInstallIdentifierModel;
@class FIRCLSExecutionIdentifierModel;
@class FIRCLSSettings;
@class FIRCLSLaunchMarkerModel;
@class GDTCORTransport;
@protocol FIRAnalyticsInterop;
/*
* Manager Data's purpose is to simplify the adding and removing of
* dependencies from each of the Manager classes so that it's easier
* to inject mock classes during testing. A lot of the Manager classes
* share these dependencies, but don't use all of them.
*
* If you plan on adding interdependencies between Managers, do not add a pointer
* to the dependency here. Instead add them as a new value to the constructor of
* the Manager, and construct them in FIRCrashlytics. This data structure should
* be for Models and other SDKs / Interops Crashlytics depends on.
*/
@interface FIRCLSManagerData : NSObject
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID
googleTransport:(GDTCORTransport *)googleTransport
installations:(FIRInstallations *)installations
analytics:(nullable id<FIRAnalyticsInterop>)analytics
fileManager:(FIRCLSFileManager *)fileManager
dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
settings:(FIRCLSSettings *)settings NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
@property(nonatomic, readonly) NSString *googleAppID;
@property(nonatomic, strong) GDTCORTransport *googleTransport;
@property(nonatomic, strong) FIRInstallations *installations;
@property(nonatomic, strong) id<FIRAnalyticsInterop> analytics;
@property(nonatomic, strong) FIRCLSFileManager *fileManager;
@property(nonatomic, strong) FIRCLSDataCollectionArbiter *dataArbiter;
// Uniquely identifies a build / binary of the app
@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel;
// Uniquely identifies an install of the app
@property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel;
// Uniquely identifies a run of the app
@property(nonatomic, strong) FIRCLSExecutionIdentifierModel *executionIDModel;
// Settings fetched from the server
@property(nonatomic, strong) FIRCLSSettings *settings;
// These queues function together as a single startup queue
@property(nonatomic, strong) NSOperationQueue *operationQueue;
@property(nonatomic, strong) dispatch_queue_t dispatchQueue;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,59 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h"
#import "Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h"
@implementation FIRCLSManagerData
- (instancetype)initWithGoogleAppID:(NSString *)googleAppID
googleTransport:(GDTCORTransport *)googleTransport
installations:(FIRInstallations *)installations
analytics:(nullable id<FIRAnalyticsInterop>)analytics
fileManager:(FIRCLSFileManager *)fileManager
dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
settings:(FIRCLSSettings *)settings {
self = [super init];
if (!self) {
return nil;
}
_googleAppID = googleAppID;
_googleTransport = googleTransport;
_installations = installations;
_analytics = analytics;
_fileManager = fileManager;
_dataArbiter = dataArbiter;
_settings = settings;
_appIDModel = [[FIRCLSApplicationIdentifierModel alloc] init];
_installIDModel = [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:installations];
_executionIDModel = [[FIRCLSExecutionIdentifierModel alloc] init];
NSString *sdkBundleID = FIRCLSApplicationGetSDKBundleID();
_operationQueue = [NSOperationQueue new];
[_operationQueue setMaxConcurrentOperationCount:1];
[_operationQueue setName:[sdkBundleID stringByAppendingString:@".work-queue"]];
_dispatchQueue = dispatch_queue_create("com.google.firebase.crashlytics.startup", 0);
_operationQueue.underlyingQueue = _dispatchQueue;
return self;
}
@end

View File

@ -1,61 +0,0 @@
// 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>
typedef NS_ENUM(NSInteger, FIRCLSNetworkClientErrorType) {
FIRCLSNetworkClientErrorTypeUnknown = -1,
FIRCLSNetworkClientErrorTypeFileUnreadable = -2
};
extern NSString *const FIRCLSNetworkClientErrorDomain;
@protocol FIRCLSNetworkClientDelegate;
@class FIRCLSDataCollectionToken;
@class FIRCLSFileManager;
@interface FIRCLSNetworkClient : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)initWithQueue:(NSOperationQueue *)queue
fileManager:(FIRCLSFileManager *)fileManager
delegate:(id<FIRCLSNetworkClientDelegate>)delegate;
@property(nonatomic, weak) id<FIRCLSNetworkClientDelegate> delegate;
@property(nonatomic, readonly) NSOperationQueue *operationQueue;
@property(nonatomic, readonly) BOOL supportsBackgroundRequests;
- (void)startUploadRequest:(NSURLRequest *)request
filePath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
immediately:(BOOL)immediate;
- (void)attemptToReconnectBackgroundSessionWithCompletionBlock:(void (^)(void))completionBlock;
@end
@class FIRCLSNetworkClient;
@protocol FIRCLSNetworkClientDelegate <NSObject>
@required
- (BOOL)networkClientCanUseBackgroundSessions:(FIRCLSNetworkClient *)client;
@optional
- (void)networkClient:(FIRCLSNetworkClient *)client
didFinishUploadWithPath:(NSString *)path
error:(NSError *)error;
@end

View File

@ -1,366 +0,0 @@
// 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 "FIRCLSNetworkClient.h"
#import "FIRCLSApplication.h"
#import "FIRCLSByteUtility.h"
#import "FIRCLSDataCollectionToken.h"
#import "FIRCLSDefines.h"
#import "FIRCLSFileManager.h"
#import "FIRCLSNetworkResponseHandler.h"
#import "FIRCLSURLSession.h"
#import "FIRCLSURLSessionConfiguration.h"
#import "FIRCLSUtility.h"
NSString *const FIRCLSNetworkClientErrorDomain = @"FIRCLSNetworkError";
NSString *const FIRCLSNetworkClientBackgroundIdentifierSuffix = @".crash.background-session";
@interface FIRCLSNetworkClient () <NSURLSessionDelegate> {
NSURLSession *_session;
}
@property(nonatomic, strong) void (^backgroundCompletionHandler)(void);
@property(nonatomic, strong, readonly) NSURLSession *session;
@property(nonatomic, assign) BOOL canUseBackgroundSession;
@property(nonatomic, strong) FIRCLSFileManager *fileManager;
@end
@implementation FIRCLSNetworkClient
- (instancetype)initWithQueue:(NSOperationQueue *)queue
fileManager:(FIRCLSFileManager *)fileManager
delegate:(id<FIRCLSNetworkClientDelegate>)delegate {
self = [super init];
if (!self) {
return nil;
}
_operationQueue = queue;
_delegate = delegate;
_fileManager = fileManager;
self.canUseBackgroundSession = [_delegate networkClientCanUseBackgroundSessions:self];
if (!self.supportsBackgroundRequests) {
FIRCLSDeveloperLog(
"Crashlytics:Crash:Client",
@"Background session uploading not supported, asynchronous uploading will be used");
}
return self;
}
#pragma mark - Background Support
- (NSURLSession *)session {
// Creating a NSURLSession takes some time. Doing it lazily saves us time in the normal case.
if (_session) {
return _session;
}
NSURLSessionConfiguration *config = nil;
Class urlSessionClass;
Class urlSessionConfigurationClass;
#if FIRCLSURLSESSION_REQUIRED
urlSessionClass = [FIRCLSURLSession class];
urlSessionConfigurationClass = [FIRCLSURLSessionConfiguration class];
#else
urlSessionClass = [NSURLSession class];
urlSessionConfigurationClass = [NSURLSessionConfiguration class];
#endif
if (self.supportsBackgroundRequests) {
NSString *sdkBundleID = FIRCLSApplicationGetSDKBundleID();
NSString *backgroundConfigName =
[sdkBundleID stringByAppendingString:FIRCLSNetworkClientBackgroundIdentifierSuffix];
config = [urlSessionConfigurationClass backgroundSessionConfiguration:backgroundConfigName];
#if TARGET_OS_IPHONE
[config setSessionSendsLaunchEvents:NO];
#endif
}
if (!config) {
// take this code path if we don't support background requests OR if we failed to create a
// background configuration
config = [urlSessionConfigurationClass defaultSessionConfiguration];
}
_session = [urlSessionClass sessionWithConfiguration:config
delegate:self
delegateQueue:self.operationQueue];
if (!_session || !config) {
FIRCLSErrorLog(@"Failed to initialize");
}
return _session;
}
#if FIRCLSURLSESSION_REQUIRED
- (BOOL)NSURLSessionAvailable {
if ([[FIRCLSURLSession class] respondsToSelector:@selector(NSURLSessionShouldBeUsed)]) {
return [FIRCLSURLSession NSURLSessionShouldBeUsed];
}
return NSClassFromString(@"NSURLSession") != nil;
}
#endif
- (BOOL)supportsBackgroundRequests {
return !FIRCLSApplicationIsExtension()
#if FIRCLSURLSESSION_REQUIRED
&& [self NSURLSessionAvailable]
#endif
&& self.canUseBackgroundSession;
}
- (void)attemptToReconnectBackgroundSessionWithCompletionBlock:(void (^)(void))completionBlock {
if (!self.supportsBackgroundRequests) {
if (completionBlock) {
completionBlock();
}
return;
}
// This is the absolute minimum necessary. Perhaps we can do better?
if (completionBlock) {
[[NSOperationQueue mainQueue] addOperationWithBlock:completionBlock];
}
}
#pragma mark - API
- (void)startUploadRequest:(NSURLRequest *)request
filePath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
immediately:(BOOL)immediate {
if (![dataCollectionToken isValid]) {
FIRCLSErrorLog(@"An upload was requested with an invalid data collection token.");
return;
}
if (immediate) {
[self startImmediateUploadRequest:request filePath:path];
return;
}
NSString *description = [self relativeTaskPathForAbsolutePath:path];
[self checkForExistingTaskMatchingDescription:description
completionBlock:^(BOOL found) {
if (found) {
FIRCLSDeveloperLog(
"Crashlytics:Crash:Client",
@"A task currently exists for this upload, skipping");
return;
}
[self startNewUploadRequest:request filePath:path];
}];
}
#pragma mark - Support API
- (void)startImmediateUploadRequest:(NSURLRequest *)request filePath:(NSString *)path {
// check the ivar directly, to avoid going back to the delegate
if (self.supportsBackgroundRequests) {
// this can be done here, because the request will be started synchronously.
[self startNewUploadRequest:request filePath:path];
return;
}
if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) {
FIRCLSSDKLog("Error: file unreadable\n");
// Following the same logic as below, do not try to inform the delegate
return;
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[mutableRequest setHTTPBodyStream:[NSInputStream inputStreamWithFileAtPath:path]];
NSURLResponse *requestResponse = nil;
[[NSURLSession sharedSession]
dataTaskWithRequest:mutableRequest
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response,
NSError *_Nullable error) {
[FIRCLSNetworkResponseHandler
clientResponseType:requestResponse
handler:^(FIRCLSNetworkClientResponseType type, NSInteger statusCode) {
if (type != FIRCLSNetworkClientResponseSuccess) {
// don't even inform the delegate of a failure here, because we don't
// want any action to be taken synchronously
return;
}
[[self delegate] networkClient:self
didFinishUploadWithPath:path
error:error];
}];
}];
}
- (void)startNewUploadRequest:(NSURLRequest *)request filePath:(NSString *)path {
if (![[NSFileManager defaultManager] isReadableFileAtPath:path]) {
[self.operationQueue addOperationWithBlock:^{
[self
handleTaskDescription:path
completedWithError:[NSError errorWithDomain:FIRCLSNetworkClientErrorDomain
code:FIRCLSNetworkClientErrorTypeFileUnreadable
userInfo:@{@"path" : path}]];
}];
return;
}
NSURLSessionUploadTask *task = [self.session uploadTaskWithRequest:request
fromFile:[NSURL fileURLWithPath:path]];
// set the description, so we can determine what file was successfully uploaded later on
[task setTaskDescription:[self relativeTaskPathForAbsolutePath:path]];
[task resume];
}
- (NSString *)rootPath {
return self.fileManager.rootPath;
}
- (NSString *)absolutePathForRelativeTaskPath:(NSString *)path {
return [self.rootPath stringByAppendingPathComponent:path];
}
- (NSString *)relativeTaskPathForAbsolutePath:(NSString *)path {
// make sure this has a tailing slash, so the path looks relative
NSString *root = [self.rootPath stringByAppendingString:@"/"];
if (![path hasPrefix:root]) {
FIRCLSSDKLog("Error: path '%s' is not at the root '%s'", [path UTF8String], [root UTF8String]);
return nil;
}
return [path stringByReplacingOccurrencesOfString:root withString:@""];
}
#pragma mark - Task Management
- (BOOL)taskArray:(NSArray *)array hasTaskMatchingDescription:(NSString *)description {
NSUInteger idx = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger arrayIdx, BOOL *stop) {
return [[obj taskDescription] isEqualToString:description];
}];
return idx != NSNotFound;
}
- (void)checkSession:(NSURLSession *)session
forTasksMatchingDescription:(NSString *)description
completionBlock:(void (^)(BOOL found))block {
if (!session) {
block(NO);
return;
}
[session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks,
NSArray *downloadTasks) {
if ([self taskArray:uploadTasks hasTaskMatchingDescription:description]) {
block(YES);
return;
}
if ([self taskArray:dataTasks hasTaskMatchingDescription:description]) {
block(YES);
return;
}
if ([self taskArray:downloadTasks hasTaskMatchingDescription:description]) {
block(YES);
return;
}
block(NO);
}];
}
- (void)checkForExistingTaskMatchingDescription:(NSString *)description
completionBlock:(void (^)(BOOL found))block {
// Do not instantiate the normal session, because if it doesn't exist yet, it cannot possibly have
// existing tasks
[_operationQueue addOperationWithBlock:^{
[self checkSession:self.session
forTasksMatchingDescription:description
completionBlock:^(BOOL found) {
block(found);
}];
}];
}
#pragma mark - Result Handling
// This method is duplicated from FIRCLSFABNetworkClient. Sharing it is a little weird - I didn't
// feel like it fit into FIRCLSNetworkResponseHandler.
- (void)runAfterRetryValueFromResponse:(NSURLResponse *)response block:(void (^)(void))block {
NSTimeInterval delay = [FIRCLSNetworkResponseHandler retryValueForResponse:response];
// FIRCLSDeveloperLog("Network", @"Restarting request after %f", delay);
FIRCLSAddOperationAfter(delay, _operationQueue, block);
}
- (void)restartTask:(NSURLSessionTask *)task {
NSURLRequest *request = [task originalRequest];
[self runAfterRetryValueFromResponse:[task response]
block:^{
NSString *path = [self
absolutePathForRelativeTaskPath:[task taskDescription]];
[self startNewUploadRequest:request filePath:path];
}];
}
- (void)handleTask:(NSURLSessionTask *)task completedWithError:(NSError *)error {
[self handleTaskDescription:[task taskDescription] completedWithError:error];
}
- (void)handleTaskDescription:(NSString *)taskDescription completedWithError:(NSError *)error {
NSString *path = [self absolutePathForRelativeTaskPath:taskDescription];
[[self delegate] networkClient:self didFinishUploadWithPath:path error:error];
}
#pragma mark - NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
FIRCLSDeveloperLog("Crashlytics:Crash:Client", @"session became invalid: %@", error);
}
// Careful! Not implementing this method appears to cause a crash when using a background task
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
[FIRCLSNetworkResponseHandler handleCompletedResponse:task.response
forOriginalRequest:task.originalRequest
error:error
block:^(BOOL restart, NSError *taskError) {
if (restart) {
[self restartTask:task];
return;
}
[self handleTask:task
completedWithError:taskError];
}];
}
@end

View File

@ -1,4 +1,4 @@
// Copyright 2019 Google
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#import "FIRCLSURLSessionAvailability.h"
#if FIRCLSURLSESSION_REQUIRED
#import <Foundation/Foundation.h>
@interface FIRCLSURLSession (PrivateMethods)
NS_ASSUME_NONNULL_BEGIN
- (void)runOnDelegateQueue:(void (^)(void))block;
@interface FIRCLSNotificationManager : NSObject
+ (instancetype)new NS_UNAVAILABLE;
- (void)registerNotificationListener;
@end
#endif
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,110 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import "Crashlytics/Crashlytics/Controllers/FIRCLSNotificationManager.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h"
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#import <AppKit/AppKit.h>
#endif
@implementation FIRCLSNotificationManager
- (void)registerNotificationListener {
[self captureInitialNotificationStates];
#if TARGET_OS_IOS
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(willBecomeActive:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didBecomeInactive:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didChangeOrientation:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(didChangeUIOrientation:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
#pragma clang diagnostic pop
#elif CLS_TARGET_OS_OSX
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(willBecomeActive:)
name:@"NSApplicationWillBecomeActiveNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didBecomeInactive:)
name:@"NSApplicationDidResignActiveNotification"
object:nil];
#endif
}
- (void)captureInitialNotificationStates {
#if TARGET_OS_IOS
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
UIInterfaceOrientation statusBarOrientation =
[FIRCLSApplicationSharedInstance() statusBarOrientation];
#endif
// It's nice to do this async, so we don't hold up the main thread while doing three
// consecutive IOs here.
dispatch_async(FIRCLSGetLoggingQueue(), ^{
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSInBackgroundKey, @"0");
#if TARGET_OS_IOS
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSDeviceOrientationKey,
[@(orientation) description]);
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSUIOrientationKey,
[@(statusBarOrientation) description]);
#endif
});
}
- (void)willBecomeActive:(NSNotification *)notification {
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @NO);
}
- (void)didBecomeInactive:(NSNotification *)notification {
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @YES);
}
#if TARGET_OS_IOS
- (void)didChangeOrientation:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDeviceOrientationKey, @(orientation));
}
- (void)didChangeUIOrientation:(NSNotification *)notification {
UIInterfaceOrientation statusBarOrientation =
[FIRCLSApplicationSharedInstance() statusBarOrientation];
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSUIOrientationKey, @(statusBarOrientation));
}
#endif
@end

View File

@ -14,38 +14,29 @@
#import <Foundation/Foundation.h>
#include "FIRCLSApplicationIdentifierModel.h"
#include "FIRCLSProfiling.h"
#include "FIRCrashlytics.h"
#import "Crashlytics/Crashlytics/Public/FirebaseCrashlytics/FIRCrashlytics.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSProfiling.h"
@class FBLPromise<T>;
@class FIRCLSExistingReportManager;
@class FIRCLSAnalyticsManager;
@class FIRCLSManagerData;
NS_ASSUME_NONNULL_BEGIN
@class FIRCLSDataCollectionArbiter;
@class FIRCLSFileManager;
@class FIRCLSInternalReport;
@class FIRCLSSettings;
@class GDTCORTransport;
@class FIRInstallations;
@protocol FIRAnalyticsInterop;
@interface FIRCLSReportManager : NSObject
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
installations:(FIRInstallations *)installations
analytics:(nullable id<FIRAnalyticsInterop>)analytics
googleAppID:(NSString *)googleAppID
dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
googleTransport:(GDTCORTransport *)googleTransport
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
settings:(FIRCLSSettings *)settings NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithManagerData:(FIRCLSManagerData *)managerData
existingReportManager:(FIRCLSExistingReportManager *)existingReportManager
analyticsManager:(FIRCLSAnalyticsManager *)analyticsManager
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (FBLPromise<NSNumber *> *)startWithProfilingMark:(FIRCLSProfileMark)mark;
- (FBLPromise<NSNumber *> *)checkForUnsentReports;
- (FBLPromise<FIRCrashlyticsReport *> *)checkForUnsentReports;
- (FBLPromise *)sendUnsentReports;
- (FBLPromise *)deleteUnsentReports;

View File

@ -38,37 +38,34 @@
#import "FBLPromises.h"
#endif
#import "FIRCLSApplication.h"
#import "FIRCLSDataCollectionArbiter.h"
#import "FIRCLSDataCollectionToken.h"
#import "FIRCLSDefines.h"
#import "FIRCLSFeatures.h"
#import "FIRCLSFileManager.h"
#import "FIRCLSInternalReport.h"
#import "FIRCLSLogger.h"
#import "FIRCLSNetworkClient.h"
#import "FIRCLSPackageReportOperation.h"
#import "FIRCLSProcessReportOperation.h"
#import "FIRCLSReportUploader.h"
#import "FIRCLSSettings.h"
#import "FIRCLSSymbolResolver.h"
#import "FIRCLSUserLogging.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSAnalyticsManager.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSExistingReportManager.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSNotificationManager.h"
#import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h"
#import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSLogger.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSLaunchMarkerModel.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSSettings.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h"
#import "Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.h"
#import "Crashlytics/Crashlytics/Settings/Models/FIRCLSApplicationIdentifierModel.h"
#include "FIRCLSGlobals.h"
#include "FIRCLSUtility.h"
#include "Crashlytics/Crashlytics/Components/FIRCLSGlobals.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#import "FIRCLSConstants.h"
#import "FIRCLSExecutionIdentifierModel.h"
#import "FIRCLSInstallIdentifierModel.h"
#import "FIRCLSSettingsOnboardingManager.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSExecutionIdentifierModel.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h"
#import "Crashlytics/Crashlytics/Settings/FIRCLSSettingsManager.h"
#import "Crashlytics/Shared/FIRCLSConstants.h"
#import "FIRCLSReportManager_Private.h"
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
#import "Interop/Analytics/Public/FIRAnalyticsInteropListener.h"
#include "FIRAEvent+Internal.h"
#include "FIRCLSFCRAnalytics.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h"
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
@ -76,29 +73,6 @@
#import <AppKit/AppKit.h>
#endif
static NSTimeInterval const CLSReportRetryInterval = 10 * 60;
static NSString *FIRCLSFirebaseAnalyticsEventLogFormat = @"$A$:%@";
@interface FIRCLSAnalyticsInteropListener : NSObject <FIRAnalyticsInteropListener> {
}
@end
@implementation FIRCLSAnalyticsInteropListener
- (void)messageTriggered:(NSString *)name parameters:(NSDictionary *)parameters {
NSDictionary *event = @{
@"name" : name,
@"parameters" : parameters,
};
NSString *json = FIRCLSFIRAEventDictionaryToJSON(event);
if (json != nil) {
FIRCLSLog(FIRCLSFirebaseAnalyticsEventLogFormat, json);
}
}
@end
/**
* A FIRReportAction is used to indicate how to handle unsent reports.
*/
@ -119,24 +93,15 @@ typedef NSNumber FIRCLSWrappedReportAction;
}
@end
/**
* This is a helper to make code using NSNumber for bools more readable.
*/
typedef NSNumber FIRCLSWrappedBool;
@interface FIRCLSReportManager () <FIRCLSNetworkClientDelegate,
FIRCLSReportUploaderDelegate,
FIRCLSReportUploaderDataSource> {
@interface FIRCLSReportManager () {
FIRCLSFileManager *_fileManager;
FIRCLSNetworkClient *_networkClient;
FIRCLSReportUploader *_uploader;
dispatch_queue_t _dispatchQueue;
NSOperationQueue *_operationQueue;
id<FIRAnalyticsInterop> _analytics;
// A promise that will be resolved when unsent reports are found on the device, and
// processReports: can be called to decide how to deal with them.
FBLPromise<FIRCLSWrappedBool *> *_unsentReportsAvailable;
FBLPromise<FIRCrashlyticsReport *> *_unsentReportsAvailable;
// A promise that will be resolved when the user has provided an action that they want to perform
// for all the unsent reports.
@ -156,63 +121,49 @@ typedef NSNumber FIRCLSWrappedBool;
}
@property(nonatomic, readonly) NSString *googleAppID;
@property(nonatomic, strong) GDTCORTransport *googleTransport;
@property(nonatomic, strong) FIRCLSDataCollectionArbiter *dataArbiter;
@property(nonatomic, strong) FIRCLSSettings *settings;
@property(nonatomic, strong) FIRCLSLaunchMarkerModel *launchMarker;
// Uniquely identifies a build / binary of the app
@property(nonatomic, strong) FIRCLSApplicationIdentifierModel *appIDModel;
// Uniquely identifies an install of the app
@property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel;
// Uniquely identifies a run of the app
@property(nonatomic, strong) FIRCLSExecutionIdentifierModel *executionIDModel;
// Settings fetched from the server
@property(nonatomic, strong) FIRCLSSettings *settings;
@property(nonatomic, strong) FIRCLSAnalyticsManager *analyticsManager;
@property(nonatomic, strong) FIRCLSExistingReportManager *existingReportManager;
// Runs the operations that fetch settings and call onboarding endpoints
@property(nonatomic, strong) FIRCLSSettingsOnboardingManager *settingsAndOnboardingManager;
@property(nonatomic, strong) GDTCORTransport *googleTransport;
// Internal Managers
@property(nonatomic, strong) FIRCLSSettingsManager *settingsManager;
@property(nonatomic, strong) FIRCLSNotificationManager *notificationManager;
@end
@implementation FIRCLSReportManager
// Used only for internal data collection E2E testing
static void (^reportSentCallback)(void);
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
installations:(FIRInstallations *)installations
analytics:(id<FIRAnalyticsInterop>)analytics
googleAppID:(NSString *)googleAppID
dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
googleTransport:(GDTCORTransport *)googleTransport
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
settings:(FIRCLSSettings *)settings {
- (instancetype)initWithManagerData:(FIRCLSManagerData *)managerData
existingReportManager:(FIRCLSExistingReportManager *)existingReportManager
analyticsManager:(FIRCLSAnalyticsManager *)analyticsManager {
self = [super init];
if (!self) {
return nil;
}
_fileManager = fileManager;
_analytics = analytics;
_googleAppID = [googleAppID copy];
_dataArbiter = dataArbiter;
_fileManager = managerData.fileManager;
_analytics = managerData.analytics;
_googleAppID = [managerData.googleAppID copy];
_dataArbiter = managerData.dataArbiter;
_googleTransport = managerData.googleTransport;
_operationQueue = managerData.operationQueue;
_dispatchQueue = managerData.dispatchQueue;
_appIDModel = managerData.appIDModel;
_installIDModel = managerData.installIDModel;
_settings = managerData.settings;
_executionIDModel = managerData.executionIDModel;
_googleTransport = googleTransport;
NSString *sdkBundleID = FIRCLSApplicationGetSDKBundleID();
_operationQueue = [NSOperationQueue new];
[_operationQueue setMaxConcurrentOperationCount:1];
[_operationQueue setName:[sdkBundleID stringByAppendingString:@".work-queue"]];
_dispatchQueue = dispatch_queue_create("com.google.firebase.crashlytics.startup", 0);
_operationQueue.underlyingQueue = _dispatchQueue;
_networkClient = [self clientWithOperationQueue:_operationQueue];
_existingReportManager = existingReportManager;
_analyticsManager = analyticsManager;
_unsentReportsAvailable = [FBLPromise pendingPromise];
_reportActionProvided = [FBLPromise pendingPromise];
@ -220,42 +171,19 @@ static void (^reportSentCallback)(void);
_checkForUnsentReportsCalled = NO;
_installIDModel = [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:installations];
_executionIDModel = [[FIRCLSExecutionIdentifierModel alloc] init];
_settingsManager = [[FIRCLSSettingsManager alloc] initWithAppIDModel:self.appIDModel
installIDModel:self.installIDModel
settings:self.settings
fileManager:self.fileManager
googleAppID:self.googleAppID];
_settings = settings;
_appIDModel = appIDModel;
_notificationManager = [[FIRCLSNotificationManager alloc] init];
_settingsAndOnboardingManager =
[[FIRCLSSettingsOnboardingManager alloc] initWithAppIDModel:appIDModel
installIDModel:self.installIDModel
settings:self.settings
fileManager:self.fileManager
googleAppID:self.googleAppID];
_launchMarker = [[FIRCLSLaunchMarkerModel alloc] initWithFileManager:_fileManager];
return self;
}
- (FIRCLSNetworkClient *)clientWithOperationQueue:(NSOperationQueue *)queue {
return [[FIRCLSNetworkClient alloc] initWithQueue:queue fileManager:_fileManager delegate:self];
}
/**
* Returns the number of unsent reports on the device, including the ones passed in.
*/
- (int)unsentReportsCountWithPreexisting:(NSArray<NSString *> *)paths {
int count = [self countSubmittableAndDeleteUnsubmittableReportPaths:paths];
count += _fileManager.processingPathContents.count;
if (self.settings.shouldUseNewReportEndpoint) {
count += _fileManager.preparedPathContents.count;
} else {
count += _fileManager.legacyPreparedPathContents.count;
}
return count;
}
// This method returns a promise that is resolved with a wrapped FIRReportAction once the user has
// indicated whether they want to upload currently cached reports.
// This method should only be called when we have determined there is at least 1 unsent report.
@ -264,8 +192,8 @@ static void (^reportSentCallback)(void);
// 2. The developer uses the processCrashReports API to indicate whether the report
// should be sent or deleted, at which point the promise will be resolved with the action.
- (FBLPromise<FIRCLSWrappedReportAction *> *)waitForReportAction {
FIRCLSDebugLog(@"[Crashlytics:Crash] Notifying that unsent reports are available.");
[_unsentReportsAvailable fulfill:@YES];
FIRCrashlyticsReport *unsentReport = self.existingReportManager.newestUnsentReport;
[_unsentReportsAvailable fulfill:unsentReport];
// If data collection gets enabled while we are waiting for an action, go ahead and send the
// reports, and any subsequent explicit response will be ignored.
@ -275,16 +203,16 @@ static void (^reportSentCallback)(void);
return @(FIRCLSReportActionSend);
}];
FIRCLSDebugLog(@"[Crashlytics:Crash] Waiting for send/deleteUnsentReports to be called.");
// Wait for either the processReports callback to be called, or data collection to be enabled.
return [FBLPromise race:@[ collectionEnabled, _reportActionProvided ]];
}
- (FBLPromise<FIRCLSWrappedBool *> *)checkForUnsentReports {
- (FBLPromise<FIRCrashlyticsReport *> *)checkForUnsentReports {
bool expectedCalled = NO;
if (!atomic_compare_exchange_strong(&_checkForUnsentReportsCalled, &expectedCalled, YES)) {
FIRCLSErrorLog(@"checkForUnsentReports should only be called once per execution.");
return [FBLPromise resolvedWith:@NO];
FIRCLSErrorLog(@"Either checkForUnsentReports or checkAndUpdateUnsentReports should be called "
@"once per execution.");
return [FBLPromise resolvedWith:nil];
}
return _unsentReportsAvailable;
}
@ -306,6 +234,10 @@ static void (^reportSentCallback)(void);
NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
[self.settings reloadFromCacheWithGoogleAppID:self.googleAppID currentTimestamp:currentTimestamp];
// This needs to be called before the new report is created for
// this run of the app.
[self.existingReportManager collectExistingReports];
if (![self validateAppIdentifiers]) {
return [FBLPromise resolvedWith:@NO];
}
@ -314,26 +246,11 @@ static void (^reportSentCallback)(void);
FIRCLSDebugLog(@"Root: %@", [_fileManager rootPath]);
#endif
if ([self.dataArbiter isLegacyDataCollectionKeyInPlist]) {
FIRCLSErrorLog(@"Found legacy data collection key in app's Info.plist: "
@"firebase_crashlytics_collection_enabled");
FIRCLSErrorLog(@"Please update your Info.plist to use the new data collection key: "
@"FirebaseCrashlyticsCollectionEnabled");
FIRCLSErrorLog(@"The legacy data collection Info.plist value could be overridden by "
@"calling: [Fabric with:...]");
FIRCLSErrorLog(@"The new value can be overridden by calling: [[FIRCrashlytics "
@"crashlytics] setCrashlyticsCollectionEnabled:<isEnabled>]");
return [FBLPromise resolvedWith:@NO];
}
if (![_fileManager createReportDirectories]) {
return [FBLPromise resolvedWith:@NO];
}
// Grab existing reports
BOOL launchFailure = [self checkForAndCreateLaunchMarker];
NSArray *preexistingReportPaths = _fileManager.activePathContents;
BOOL launchFailure = [self.launchMarker checkForAndCreateLaunchMarker];
FIRCLSInternalReport *report = [self setupCurrentReport:executionIdentifier];
if (!report) {
@ -345,14 +262,6 @@ static void (^reportSentCallback)(void);
report = nil;
}
// Regenerate the Install ID on a background thread if it needs to rotate because
// fetching the Firebase Install ID can be slow on some devices. This should happen after we
// create the session on disk so that we can update the Install ID in the written crash report
// metadata.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self checkAndRotateInstallUUIDIfNeededWithReport:report];
});
FBLPromise<NSNumber *> *promise = [FBLPromise resolvedWith:@(report != nil)];
if ([self.dataArbiter isCrashlyticsCollectionEnabled]) {
@ -360,64 +269,43 @@ static void (^reportSentCallback)(void);
FIRCLSDebugLog(@"Unsent reports will be uploaded at startup");
FIRCLSDataCollectionToken *dataCollectionToken = [FIRCLSDataCollectionToken validToken];
[self beginSettingsAndOnboardingWithToken:dataCollectionToken waitForSettingsRequest:NO];
[self beginSettingsWithToken:dataCollectionToken];
[self beginReportUploadsWithToken:dataCollectionToken
preexistingReportPaths:preexistingReportPaths
blockingSend:launchFailure
report:report];
[self beginReportUploadsWithToken:dataCollectionToken blockingSend:launchFailure];
// If data collection is enabled, the SDK will not notify the user
// when unsent reports are available, or respect Send / DeleteUnsentReports
[_unsentReportsAvailable fulfill:@NO];
[_unsentReportsAvailable fulfill:nil];
} else {
FIRCLSDebugLog(@"Automatic data collection is disabled.");
FIRCLSDebugLog(@"[Crashlytics:Crash] %d unsent reports are available. Waiting for "
@"send/deleteUnsentReports to be called.",
self.existingReportManager.unsentReportsCount);
// TODO: This counting of the file system happens on the main thread. Now that some of the other
// work below has been made async and moved to the dispatch queue, maybe we can move this code
// to the dispatch queue as well.
int unsentReportsCount = [self unsentReportsCountWithPreexisting:preexistingReportPaths];
if (unsentReportsCount > 0) {
FIRCLSDebugLog(
@"[Crashlytics:Crash] %d unsent reports are available. Checking for upload permission.",
unsentReportsCount);
// Wait for an action to get sent, either from processReports: or automatic data collection.
promise = [[self waitForReportAction]
onQueue:_dispatchQueue
then:^id _Nullable(FIRCLSWrappedReportAction *_Nullable wrappedAction) {
// Process the actions for the reports on disk.
FIRCLSReportAction action = [wrappedAction reportActionValue];
if (action == FIRCLSReportActionSend) {
FIRCLSDebugLog(@"Sending unsent reports.");
FIRCLSDataCollectionToken *dataCollectionToken =
[FIRCLSDataCollectionToken validToken];
// Wait for an action to get sent, either from processReports: or automatic data collection.
promise = [[self waitForReportAction]
onQueue:_dispatchQueue
then:^id _Nullable(FIRCLSWrappedReportAction *_Nullable wrappedAction) {
// Process the actions for the reports on disk.
FIRCLSReportAction action = [wrappedAction reportActionValue];
if (action == FIRCLSReportActionSend) {
FIRCLSDebugLog(@"Sending unsent reports.");
FIRCLSDataCollectionToken *dataCollectionToken =
[FIRCLSDataCollectionToken validToken];
// For the new report endpoint, the orgID is not needed.
// For the legacy report endpoint, wait on settings if orgID is not available.
BOOL waitForSetting =
!self.settings.shouldUseNewReportEndpoint && !self.settings.orgID;
[self beginSettingsWithToken:dataCollectionToken];
[self beginSettingsAndOnboardingWithToken:dataCollectionToken
waitForSettingsRequest:waitForSetting];
[self beginReportUploadsWithToken:dataCollectionToken blockingSend:NO];
[self beginReportUploadsWithToken:dataCollectionToken
preexistingReportPaths:preexistingReportPaths
blockingSend:NO
report:report];
} else if (action == FIRCLSReportActionDelete) {
FIRCLSDebugLog(@"Deleting unsent reports.");
[self deleteUnsentReportsWithPreexisting:preexistingReportPaths];
} else {
FIRCLSErrorLog(@"Unknown report action: %d", action);
}
return @(report != nil);
}];
} else {
FIRCLSDebugLog(@"[Crashlytics:Crash] There are no unsent reports.");
[_unsentReportsAvailable fulfill:@NO];
}
} else if (action == FIRCLSReportActionDelete) {
FIRCLSDebugLog(@"Deleting unsent reports.");
[self.existingReportManager deleteUnsentReports];
} else {
FIRCLSErrorLog(@"Unknown report action: %d", action);
}
return @(report != nil);
}];
}
if (report != nil) {
@ -435,52 +323,42 @@ static void (^reportSentCallback)(void);
NSOperationQueue *__weak queue = _operationQueue;
FBLPromise *__weak unsentReportsHandled = _unsentReportsHandled;
promise = [promise then:^id _Nullable(NSNumber *_Nullable value) {
[queue waitUntilAllOperationsAreFinished];
// Signal that to callers of processReports that everything is finished.
[unsentReportsHandled fulfill:nil];
return value;
FBLPromise *allOpsFinished = [FBLPromise pendingPromise];
[queue addOperationWithBlock:^{
[allOpsFinished fulfill:nil];
}];
return [allOpsFinished onQueue:dispatch_get_main_queue()
then:^id _Nullable(id _Nullable allOpsFinishedValue) {
// Signal that to callers of processReports that everything is
// finished.
[unsentReportsHandled fulfill:nil];
return value;
}];
}];
return promise;
}
- (void)checkAndRotateInstallUUIDIfNeededWithReport:(FIRCLSInternalReport *)report {
[self.installIDModel regenerateInstallIDIfNeededWithBlock:^(BOOL didRotate) {
if (!didRotate) {
return;
}
FIRCLSContextUpdateMetadata(report, self.settings, self.installIDModel, self->_fileManager);
}];
}
- (void)beginSettingsAndOnboardingWithToken:(FIRCLSDataCollectionToken *)token
waitForSettingsRequest:(BOOL)waitForSettings {
- (void)beginSettingsWithToken:(FIRCLSDataCollectionToken *)token {
if (self.settings.isCacheExpired) {
// This method can be called more than once if the user calls
// SendUnsentReports again, so don't repeat the settings fetch
static dispatch_once_t settingsFetchOnceToken;
dispatch_once(&settingsFetchOnceToken, ^{
[self.settingsAndOnboardingManager beginSettingsAndOnboardingWithGoogleAppId:self.googleAppID
token:token
waitForCompletion:waitForSettings];
[self.settingsManager beginSettingsWithGoogleAppId:self.googleAppID token:token];
});
}
}
- (void)beginReportUploadsWithToken:(FIRCLSDataCollectionToken *)token
preexistingReportPaths:(NSArray *)preexistingReportPaths
blockingSend:(BOOL)blockingSend
report:(FIRCLSInternalReport *)report {
blockingSend:(BOOL)blockingSend {
if (self.settings.collectReportsEnabled) {
[self processExistingReportPaths:preexistingReportPaths
dataCollectionToken:token
asUrgent:blockingSend];
[self handleContentsInOtherReportingDirectoriesWithToken:token];
[self.existingReportManager sendUnsentReportsWithToken:token asUrgent:blockingSend];
} else {
FIRCLSInfoLog(@"Collect crash reports is disabled");
[self deleteUnsentReportsWithPreexisting:preexistingReportPaths];
[self.existingReportManager deleteUnsentReports];
}
}
@ -490,13 +368,13 @@ static void (^reportSentCallback)(void);
return NO;
}
if (!FIRCLSContextInitialize(report, self.settings, self.installIDModel, _fileManager)) {
if (!FIRCLSContextInitialize(report, self.settings, _fileManager)) {
return NO;
}
[self setupStateNotifications];
[self.notificationManager registerNotificationListener];
[self registerAnalyticsEventListener];
[self.analyticsManager registerAnalyticsListener];
[self crashReportingSetupCompleted:mark];
@ -507,7 +385,9 @@ static void (^reportSentCallback)(void);
// check our handlers
FIRCLSDispatchAfter(2.0, dispatch_get_main_queue(), ^{
FIRCLSExceptionCheckHandlers((__bridge void *)(self));
#if CLS_SIGNAL_SUPPORTED
FIRCLSSignalCheckHandlers();
#endif
#if CLS_MACH_EXCEPTION_SUPPORTED
FIRCLSMachExceptionCheckHandlers();
#endif
@ -515,7 +395,7 @@ static void (^reportSentCallback)(void);
// remove the launch failure marker and record the startup time
dispatch_async(dispatch_get_main_queue(), ^{
[self removeLaunchFailureMarker];
[self.launchMarker removeLaunchFailureMarker];
dispatch_async(FIRCLSGetLoggingQueue(), ^{
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSFirstRunloopTurnTimeKey,
[@(FIRCLSProfileEnd(mark)) description]);
@ -542,26 +422,24 @@ static void (^reportSentCallback)(void);
return NO;
}
if ([self.dataArbiter isLegacyDataCollectionKeyInPlist]) {
FIRCLSErrorLog(@"Found legacy data collection key in app's Info.plist: "
@"firebase_crashlytics_collection_enabled");
FIRCLSErrorLog(@"Please update your Info.plist to use the new data collection key: "
@"FirebaseCrashlyticsCollectionEnabled");
FIRCLSErrorLog(@"The legacy data collection Info.plist value could be overridden by "
@"calling: [Fabric with:...]");
FIRCLSErrorLog(@"The new value can be overridden by calling: [[FIRCrashlytics "
@"crashlytics] setCrashlyticsCollectionEnabled:<isEnabled>]");
return NO;
}
return YES;
}
- (FIRCLSReportUploader *)uploader {
if (!_uploader) {
_uploader = [[FIRCLSReportUploader alloc] initWithQueue:self.operationQueue
delegate:self
dataSource:self
client:self.networkClient
fileManager:_fileManager
analytics:_analytics];
}
return _uploader;
}
#pragma mark - Reporting Lifecycle
- (FIRCLSInternalReport *)setupCurrentReport:(NSString *)executionIdentifier {
[self createLaunchFailureMarker];
[self.launchMarker createLaunchFailureMarker];
NSString *reportPath = [_fileManager setupNewPathForExecutionIdentifier:executionIdentifier];
@ -569,341 +447,4 @@ static void (^reportSentCallback)(void);
executionIdentifier:executionIdentifier];
}
- (int)countSubmittableAndDeleteUnsubmittableReportPaths:(NSArray *)reportPaths {
int count = 0;
for (NSString *path in reportPaths) {
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
if ([report needsToBeSubmitted]) {
count++;
} else {
[self.operationQueue addOperationWithBlock:^{
[self->_fileManager removeItemAtPath:path];
}];
}
}
return count;
}
- (void)processExistingReportPaths:(NSArray *)reportPaths
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent {
for (NSString *path in reportPaths) {
[self processExistingActiveReportPath:path
dataCollectionToken:dataCollectionToken
asUrgent:urgent];
}
}
- (void)processExistingActiveReportPath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent {
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
// TODO: needsToBeSubmitted should really be called on the background queue.
if (![report needsToBeSubmitted]) {
[self.operationQueue addOperationWithBlock:^{
[self->_fileManager removeItemAtPath:path];
}];
return;
}
if (urgent && [dataCollectionToken isValid]) {
// We can proceed without the delegate.
[[self uploader] prepareAndSubmitReport:report
dataCollectionToken:dataCollectionToken
asUrgent:urgent
withProcessing:YES];
return;
}
[self submitReport:report dataCollectionToken:dataCollectionToken];
}
- (void)submitReport:(FIRCLSInternalReport *)report
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken {
[self.operationQueue addOperationWithBlock:^{
[[self uploader] prepareAndSubmitReport:report
dataCollectionToken:dataCollectionToken
asUrgent:NO
withProcessing:YES];
}];
[self didSubmitReport];
}
// This is the side-effect of calling deleteUnsentReports, or collect_reports setting
// being false
- (void)deleteUnsentReportsWithPreexisting:(NSArray *)preexistingReportPaths {
[self removeExistingReportPaths:preexistingReportPaths];
[self removeExistingReportPaths:self.fileManager.processingPathContents];
if (self.settings.shouldUseNewReportEndpoint) {
[self removeExistingReportPaths:self.fileManager.preparedPathContents];
} else {
[self removeExistingReportPaths:self.fileManager.legacyPreparedPathContents];
}
}
- (void)removeExistingReportPaths:(NSArray *)reportPaths {
[self.operationQueue addOperationWithBlock:^{
for (NSString *path in reportPaths) {
[self.fileManager removeItemAtPath:path];
}
}];
}
- (void)handleContentsInOtherReportingDirectoriesWithToken:(FIRCLSDataCollectionToken *)token {
[self handleExistingFilesInProcessingWithToken:token];
[self handleExistingFilesInPreparedWithToken:token];
}
- (void)handleExistingFilesInProcessingWithToken:(FIRCLSDataCollectionToken *)token {
NSArray *processingPaths = _fileManager.processingPathContents;
// deal with stuff in processing more carefully - do not process again
[self.operationQueue addOperationWithBlock:^{
for (NSString *path in processingPaths) {
FIRCLSInternalReport *report = [FIRCLSInternalReport reportWithPath:path];
[[self uploader] prepareAndSubmitReport:report
dataCollectionToken:token
asUrgent:NO
withProcessing:NO];
}
}];
}
- (void)handleExistingFilesInPreparedWithToken:(FIRCLSDataCollectionToken *)token {
NSArray *preparedPaths = self.settings.shouldUseNewReportEndpoint
? _fileManager.preparedPathContents
: _fileManager.legacyPreparedPathContents;
// Give our network client a chance to reconnect here, if needed. This attempts to avoid
// trying to re-submit a prepared file that is already in flight.
[self.networkClient attemptToReconnectBackgroundSessionWithCompletionBlock:^{
[self.operationQueue addOperationWithBlock:^{
[self uploadPreexistingFiles:preparedPaths withToken:token];
}];
}];
}
- (void)uploadPreexistingFiles:(NSArray *)files withToken:(FIRCLSDataCollectionToken *)token {
// Because this could happen quite a bit after the inital set of files was
// captured, some could be completed (deleted). So, just double-check to make sure
// the file still exists.
for (NSString *path in files) {
if (![[_fileManager underlyingFileManager] fileExistsAtPath:path]) {
continue;
}
[[self uploader] uploadPackagedReportAtPath:path dataCollectionToken:token asUrgent:NO];
}
}
- (void)retryUploadForReportAtPath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)token {
FIRCLSAddOperationAfter(CLSReportRetryInterval, self.operationQueue, ^{
FIRCLSDeveloperLog("Crashlytics:Crash", @"re-attempting report submission");
[[self uploader] uploadPackagedReportAtPath:path dataCollectionToken:token asUrgent:NO];
});
}
#pragma mark - Launch Failure Detection
- (NSString *)launchFailureMarkerPath {
return [[_fileManager structurePath] stringByAppendingPathComponent:@"launchmarker"];
}
- (BOOL)createLaunchFailureMarker {
// It's tempting to use - [NSFileManger createFileAtPath:contents:attributes:] here. But that
// operation, even with empty/nil contents does a ton of work to write out nothing via a
// temporarly file. This is a much faster implemenation.
const char *path = [[self launchFailureMarkerPath] fileSystemRepresentation];
#if TARGET_OS_IPHONE
/*
* data-protected non-portable open(2) :
* int open_dprotected_np(user_addr_t path, int flags, int class, int dpflags, int mode)
*/
int fd = open_dprotected_np(path, O_WRONLY | O_CREAT | O_TRUNC, 4, 0, 0644);
#else
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
#endif
if (fd == -1) {
return NO;
}
return close(fd) == 0;
}
- (BOOL)launchFailureMarkerPresent {
return [[_fileManager underlyingFileManager] fileExistsAtPath:[self launchFailureMarkerPath]];
}
- (BOOL)removeLaunchFailureMarker {
return [_fileManager removeItemAtPath:[self launchFailureMarkerPath]];
}
- (BOOL)checkForAndCreateLaunchMarker {
BOOL launchFailure = [self launchFailureMarkerPresent];
if (launchFailure) {
FIRCLSDeveloperLog("Crashlytics:Crash",
@"Last launch failed: this may indicate a crash shortly after app launch.");
} else {
[self createLaunchFailureMarker];
}
return launchFailure;
}
#pragma mark -
- (void)registerAnalyticsEventListener {
if (_registeredAnalyticsEventListener) {
return;
}
FIRCLSAnalyticsInteropListener *listener = [[FIRCLSAnalyticsInteropListener alloc] init];
[FIRCLSFCRAnalytics registerEventListener:listener toAnalytics:_analytics];
_registeredAnalyticsEventListener = YES;
}
#pragma mark - Notifications
- (void)setupStateNotifications {
[self captureInitialNotificationStates];
#if TARGET_OS_IOS
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(willBecomeActive:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didBecomeInactive:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didChangeOrientation:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(didChangeUIOrientation:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
#pragma clang diagnostic pop
#elif CLS_TARGET_OS_OSX
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(willBecomeActive:)
name:@"NSApplicationWillBecomeActiveNotification"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didBecomeInactive:)
name:@"NSApplicationDidResignActiveNotification"
object:nil];
#endif
}
- (void)captureInitialNotificationStates {
#if TARGET_OS_IOS
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
UIInterfaceOrientation statusBarOrientation =
[FIRCLSApplicationSharedInstance() statusBarOrientation];
#endif
// It's nice to do this async, so we don't hold up the main thread while doing three
// consecutive IOs here.
dispatch_async(FIRCLSGetLoggingQueue(), ^{
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSInBackgroundKey, @"0");
#if TARGET_OS_IOS
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSDeviceOrientationKey,
[@(orientation) description]);
FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSUIOrientationKey,
[@(statusBarOrientation) description]);
#endif
});
}
- (void)willBecomeActive:(NSNotification *)notification {
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @NO);
}
- (void)didBecomeInactive:(NSNotification *)notification {
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSInBackgroundKey, @YES);
}
#if TARGET_OS_IOS
- (void)didChangeOrientation:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDeviceOrientationKey, @(orientation));
}
- (void)didChangeUIOrientation:(NSNotification *)notification {
UIInterfaceOrientation statusBarOrientation =
[FIRCLSApplicationSharedInstance() statusBarOrientation];
FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSUIOrientationKey, @(statusBarOrientation));
}
#endif
#pragma mark - FIRCLSNetworkClientDelegate
- (BOOL)networkClientCanUseBackgroundSessions:(FIRCLSNetworkClient *)client {
return !FIRCLSApplicationIsExtension();
}
- (void)networkClient:(FIRCLSNetworkClient *)client
didFinishUploadWithPath:(NSString *)path
error:(NSError *)error {
// Route this through to the reports uploader.
// Since this callback happens after an upload finished, then we can assume that the original data
// collection was authorized. This isn't ideal, but it's better than trying to plumb the data
// collection token through all the system networking callbacks.
FIRCLSDataCollectionToken *token = [FIRCLSDataCollectionToken validToken];
[[self uploader] reportUploadAtPath:path dataCollectionToken:token completedWithError:error];
}
#pragma mark - FIRCLSReportUploaderDelegate
- (void)didCompletePackageSubmission:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)token
error:(NSError *)error {
if (!error) {
FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission successful");
return;
}
FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission failed with error %@", error);
FIRCLSSDKLog("Error: failed to submit report '%s'\n", error.description.UTF8String);
[self retryUploadForReportAtPath:path dataCollectionToken:token];
}
- (void)didCompleteAllSubmissions {
[self.operationQueue addOperationWithBlock:^{
// Dealloc the reports uploader. If we need it again (if we re-enqueued submissions from
// didCompletePackageSubmission:, we can just create it again
self->_uploader = nil;
FIRCLSDeveloperLog("Crashlytics:Crash", @"report submission complete");
}];
}
#pragma mark - UITest Helpers
// Used only for internal data collection E2E testing
- (void)didSubmitReport {
if (reportSentCallback) {
dispatch_async(dispatch_get_main_queue(), ^{
reportSentCallback();
});
}
}
+ (void)setReportSentCallback:(void (^)(void))callback {
reportSentCallback = callback;
}
@end

View File

@ -12,24 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#import "FIRCLSReportManager.h"
#import "FIRCLSReportUploader.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSLaunchMarkerModel.h"
@class FIRCLSInstallIdentifierModel;
@interface FIRCLSReportManager () <FIRCLSReportUploaderDelegate, FIRCLSReportUploaderDataSource>
@interface FIRCLSReportManager ()
@property(nonatomic, strong) NSOperationQueue *operationQueue;
@property(nonatomic, strong) FIRCLSNetworkClient *networkClient;
@property(nonatomic, readonly) FIRCLSReportUploader *uploader;
@property(nonatomic, strong) FIRCLSFileManager *fileManager;
@end
@interface FIRCLSReportManager (PrivateMethods)
- (BOOL)createLaunchFailureMarker;
- (BOOL)launchFailureMarkerPresent;
@property(nonatomic, strong) FIRCLSLaunchMarkerModel *launchMarker;
- (BOOL)potentiallySubmittableCrashOccurred;

View File

@ -14,67 +14,27 @@
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTCORTransport.h>
@class FIRCLSDataCollectionToken;
@class FIRCLSInternalReport;
@class FIRCLSSettings;
@class FIRCLSManagerData;
@class FIRCLSFileManager;
@class FIRCLSNetworkClient;
@class FIRCLSReportUploader;
@protocol FIRCLSReportUploaderDelegate;
@protocol FIRCLSReportUploaderDataSource;
@protocol FIRAnalyticsInterop;
@interface FIRCLSReportUploader : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)initWithQueue:(NSOperationQueue *)queue
delegate:(id<FIRCLSReportUploaderDelegate>)delegate
dataSource:(id<FIRCLSReportUploaderDataSource>)dataSource
client:(FIRCLSNetworkClient *)client
fileManager:(FIRCLSFileManager *)fileManager
analytics:(id<FIRAnalyticsInterop>)analytics NS_DESIGNATED_INITIALIZER;
@property(nonatomic, weak) id<FIRCLSReportUploaderDelegate> delegate;
@property(nonatomic, weak) id<FIRCLSReportUploaderDataSource> dataSource;
- (instancetype)initWithManagerData:(FIRCLSManagerData *)managerData NS_DESIGNATED_INITIALIZER;
@property(nonatomic, readonly) NSOperationQueue *operationQueue;
@property(nonatomic, readonly) FIRCLSNetworkClient *networkClient;
@property(nonatomic, readonly) FIRCLSFileManager *fileManager;
- (BOOL)prepareAndSubmitReport:(FIRCLSInternalReport *)report
- (void)prepareAndSubmitReport:(FIRCLSInternalReport *)report
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent
withProcessing:(BOOL)shouldProcess;
- (BOOL)uploadPackagedReportAtPath:(NSString *)path
- (void)uploadPackagedReportAtPath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent;
- (void)reportUploadAtPath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
completedWithError:(NSError *)error;
@end
@protocol FIRCLSReportUploaderDelegate <NSObject>
@required
- (void)didCompletePackageSubmission:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)token
error:(NSError *)error;
- (void)didCompleteAllSubmissions;
@end
@protocol FIRCLSReportUploaderDataSource <NSObject>
@required
- (NSString *)googleAppID;
- (FIRCLSSettings *)settings;
- (GDTCORTransport *)googleTransport;
@end

View File

@ -14,81 +14,89 @@
#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
#import "FIRCLSApplication.h"
#import "FIRCLSDataCollectionArbiter.h"
#import "FIRCLSDataCollectionToken.h"
#import "FIRCLSDefines.h"
#import "FIRCLSFCRAnalytics.h"
#import "FIRCLSFileManager.h"
#import "FIRCLSInstallIdentifierModel.h"
#import "FIRCLSInternalReport.h"
#import "FIRCLSNetworkClient.h"
#import "FIRCLSPackageReportOperation.h"
#import "FIRCLSProcessReportOperation.h"
#import "FIRCLSReportAdapter.h"
#import "FIRCLSReportUploader_Private.h"
#import "FIRCLSSettings.h"
#import "FIRCLSSymbolResolver.h"
#import "Crashlytics/Crashlytics/Components/FIRCLSApplication.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSAnalyticsManager.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSManagerData.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h"
#import "Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h"
#import "Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSFileManager.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSInternalReport.h"
#import "Crashlytics/Crashlytics/Models/FIRCLSSymbolResolver.h"
#import "Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.h"
#import "Crashlytics/Crashlytics/Operations/Reports/FIRCLSProcessReportOperation.h"
#include "FIRCLSUtility.h"
#include "Crashlytics/Crashlytics/Helpers/FIRCLSUtility.h"
#import "FIRCLSConstants.h"
#import "FIRCLSMultipartMimeStreamEncoder.h"
#import "FIRCLSURLBuilder.h"
#import "Crashlytics/Shared/FIRCLSConstants.h"
#import "Crashlytics/Shared/FIRCLSNetworking/FIRCLSMultipartMimeStreamEncoder.h"
#import "Crashlytics/Shared/FIRCLSNetworking/FIRCLSURLBuilder.h"
#import <GoogleDataTransport/GDTCOREvent.h>
#import <GoogleDataTransport/GDTCORTransport.h>
#import <GoogleDataTransport/GoogleDataTransport.h>
@interface FIRCLSReportUploader () {
id<FIRAnalyticsInterop> _analytics;
}
@property(nonatomic, strong) GDTCORTransport *googleTransport;
@property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel;
@property(nonatomic, readonly) NSString *googleAppID;
@end
@implementation FIRCLSReportUploader
- (instancetype)initWithQueue:(NSOperationQueue *)queue
delegate:(id<FIRCLSReportUploaderDelegate>)delegate
dataSource:(id<FIRCLSReportUploaderDataSource>)dataSource
client:(FIRCLSNetworkClient *)client
fileManager:(FIRCLSFileManager *)fileManager
analytics:(id<FIRAnalyticsInterop>)analytics {
- (instancetype)initWithManagerData:(FIRCLSManagerData *)managerData {
self = [super init];
if (!self) {
return nil;
}
_operationQueue = queue;
_delegate = delegate;
_dataSource = dataSource;
_networkClient = client;
_fileManager = fileManager;
_analytics = analytics;
_operationQueue = managerData.operationQueue;
_googleAppID = managerData.googleAppID;
_googleTransport = managerData.googleTransport;
_installIDModel = managerData.installIDModel;
_fileManager = managerData.fileManager;
_analytics = managerData.analytics;
return self;
}
#pragma mark - Packaging and Submission
- (BOOL)prepareAndSubmitReport:(FIRCLSInternalReport *)report
/*
* For a crash report, this is the inital code path for uploading. A report
* will not repeat this code path after it's happened because this code path
* will move the report from the "active" folder into "processing" and then
* "prepared". Once in prepared, the report can be re-uploaded any number of times
* with uploadPackagedReportAtPath in the case of an upload failure.
*/
- (void)prepareAndSubmitReport:(FIRCLSInternalReport *)report
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent
withProcessing:(BOOL)shouldProcess {
__block BOOL success = NO;
if (![dataCollectionToken isValid]) {
FIRCLSErrorLog(@"Data collection disabled and report will not be submitted");
return NO;
}
if (!self.dataSource.settings.orgID && !self.dataSource.settings.shouldUseNewReportEndpoint) {
FIRCLSDebugLog(
@"Skipping report with id '%@' this run of the app because Organization ID was "
@"nil. Report via the legacy endpoint will upload once settings are download successfully",
report.identifier);
return YES;
return;
}
// This activity is still relevant using GoogleDataTransport because the on-device
// symbolication operation may be computationally intensive.
FIRCLSApplicationActivity(
FIRCLSApplicationActivityDefault, @"Crashlytics Crash Report Processing", ^{
// Run this only once because it can be run multiple times in succession,
// and if it's slow it could delay crash upload too much without providing
// user benefit.
static dispatch_once_t regenerateOnceToken;
dispatch_once(&regenerateOnceToken, ^{
// Check to see if the FID has rotated before we construct the payload
// so that the payload has an updated value.
[self.installIDModel regenerateInstallIDIfNeeded];
});
// Run on-device symbolication before packaging if we should process
if (shouldProcess) {
if (![self.fileManager moveItemAtPath:report.path
toDirectory:self.fileManager.processingPath]) {
@ -108,165 +116,101 @@
[processOperation start];
}
NSString *packagedPath;
FIRCLSDebugLog(@"Preparing the report for the new endpoint: %d",
self.dataSource.settings.shouldUseNewReportEndpoint);
// With the new report endpoint, the report is deleted once it is written to GDT
// Check if the report has a crash file before the report is moved or deleted
BOOL isCrash = report.isCrash;
if (self.dataSource.settings.shouldUseNewReportEndpoint) {
// For the new endpoint, just move the .clsrecords from "processing" -> "prepared"
if (![self.fileManager moveItemAtPath:report.path
toDirectory:self.fileManager.preparedPath]) {
FIRCLSErrorLog(@"Unable to move report to prepared");
return;
}
packagedPath = [self.fileManager.preparedPath
stringByAppendingPathComponent:report.path.lastPathComponent];
} else {
// For the legacy endpoint, continue generate the multipartmime file in "prepared-legacy"
FIRCLSPackageReportOperation *packageOperation =
[[FIRCLSPackageReportOperation alloc] initWithReport:report
fileManager:self.fileManager
settings:self.dataSource.settings];
[packageOperation start];
packagedPath = packageOperation.finalPath;
if (!packagedPath) {
FIRCLSErrorLog(@"Unable to package report");
return;
}
if (![self.fileManager removeItemAtPath:report.path]) {
FIRCLSErrorLog(@"Unable to remove a processing item");
}
// For the new endpoint, just move the .clsrecords from "processing" -> "prepared".
// In the old endpoint this was for packaging the report as a multipartmime file,
// so this can probably be removed for GoogleDataTransport.
if (![self.fileManager moveItemAtPath:report.path
toDirectory:self.fileManager.preparedPath]) {
FIRCLSErrorLog(@"Unable to move report to prepared");
return;
}
NSLog(@"[Firebase/Crashlytics] Packaged report with id '%@' for submission",
report.identifier);
NSString *packagedPath = [self.fileManager.preparedPath
stringByAppendingPathComponent:report.path.lastPathComponent];
success = [self uploadPackagedReportAtPath:packagedPath
dataCollectionToken:dataCollectionToken
asUrgent:urgent];
FIRCLSInfoLog(@"[Firebase/Crashlytics] Packaged report with id '%@' for submission",
report.identifier);
// If the upload was successful and the report contained a crash forward it to Google
// Analytics.
if (success && isCrash) {
[FIRCLSFCRAnalytics logCrashWithTimeStamp:report.crashedOnDate.timeIntervalSince1970
toAnalytics:self->_analytics];
[self uploadPackagedReportAtPath:packagedPath
dataCollectionToken:dataCollectionToken
asUrgent:urgent];
// We don't check for success here for 2 reasons:
// 1) If we can't upload a crash for whatever reason, but we can upload analytics
// it's better for the customer to get accurate Crash Free Users.
// 2) In the past we did try to check for success, but it was a useless check because
// sendDataEvent is async (unless we're sending urgently).
if (isCrash) {
[FIRCLSAnalyticsManager logCrashWithTimeStamp:report.crashedOnDate.timeIntervalSince1970
toAnalytics:self->_analytics];
}
});
return success;
return;
}
- (BOOL)submitPackageMultipartMimeAtPath:(NSString *)multipartmimePath
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
synchronously:(BOOL)synchronous {
FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports", "Submitting %@ %@",
synchronous ? @"sync" : @"async", multipartmimePath);
if ([[[self fileManager] fileSizeAtPath:multipartmimePath] unsignedIntegerValue] == 0) {
FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"Already-submitted report being ignored");
return NO;
}
NSTimeInterval timeout = 10.0;
// If we are submitting synchronously, be more aggressive with the timeout. However,
// we only need this if the client does not support background requests.
if (synchronous && ![[self networkClient] supportsBackgroundRequests]) {
timeout = 2.0;
}
NSMutableURLRequest *request = [self mutableRequestWithURL:[self reportURL] timeout:timeout];
[request setHTTPMethod:@"POST"];
if (![self fillInRequest:request forMultipartMimeDataAtPath:multipartmimePath]) {
return NO;
}
[[self networkClient] startUploadRequest:request
filePath:multipartmimePath
dataCollectionToken:dataCollectionToken
immediately:synchronous];
return YES;
}
- (BOOL)uploadPackagedReportAtPath:(NSString *)path
/*
* This code path can be repeated any number of times for a prepared crash report if
* the report is failing to upload.
*
* Therefore, side effects (like logging to Analytics) should not go in this method or
* else they will re-trigger when failures happen.
*
* When a crash report fails to upload, it will stay in the "prepared" folder. Upon next
* run of the app, the ReportManager will attempt to re-upload prepared reports using this
* method.
*/
- (void)uploadPackagedReportAtPath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
asUrgent:(BOOL)urgent {
FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"Submitting report%@",
urgent ? @" as urgent" : @"");
FIRCLSDebugLog(@"Submitting report %@", urgent ? @"urgently" : @"async");
// Check with the legacy path as the new path will always be contained in the legacy path
BOOL isNewPreparedPath = ![path containsString:self.fileManager.legacyPreparedPath];
if (isNewPreparedPath && self.dataSource.settings.shouldUseNewReportEndpoint) {
if (![dataCollectionToken isValid]) {
FIRCLSErrorLog(@"A report upload was requested with an invalid data collection token.");
return NO;
}
FIRCLSReportAdapter *adapter =
[[FIRCLSReportAdapter alloc] initWithPath:path googleAppId:self.dataSource.googleAppID];
GDTCOREvent *event = [self.dataSource.googleTransport eventForTransport];
event.dataObject = adapter;
event.qosTier = GDTCOREventQoSFast; // Bypass batching, send immediately
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block BOOL success = YES;
[self.dataSource.googleTransport
sendDataEvent:event
onComplete:^(BOOL wasWritten, NSError *error) {
if (!wasWritten) {
FIRCLSDeveloperLog("Crashlytics:Crash:Reports",
@"Failed to send crash report due to gdt write failure.");
success = NO;
return;
}
if (error) {
FIRCLSDeveloperLog("Crashlytics:Crash:Reports",
@"Failed to send crash report due to gdt error: %@",
error.localizedDescription);
success = NO;
return;
}
FIRCLSDeveloperLog("Crashlytics:Crash:Reports",
@"Completed report submission with id: %@", path.lastPathComponent);
if (urgent) {
dispatch_semaphore_signal(semaphore);
}
[self cleanUpSubmittedReportAtPath:path];
}];
if (urgent) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
return success;
} else if (!isNewPreparedPath && !self.dataSource.settings.shouldUseNewReportEndpoint) {
return [self submitPackageMultipartMimeAtPath:path
dataCollectionToken:dataCollectionToken
synchronously:urgent];
if (![dataCollectionToken isValid]) {
FIRCLSErrorLog(@"A report upload was requested with an invalid data collection token.");
return;
}
// Unsupported state
return NO;
FIRCLSReportAdapter *adapter = [[FIRCLSReportAdapter alloc] initWithPath:path
googleAppId:self.googleAppID
installIDModel:self.installIDModel];
GDTCOREvent *event = [self.googleTransport eventForTransport];
event.dataObject = adapter;
event.qosTier = GDTCOREventQoSFast; // Bypass batching, send immediately
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.googleTransport
sendDataEvent:event
onComplete:^(BOOL wasWritten, NSError *error) {
if (!wasWritten) {
FIRCLSErrorLog(
@"Failed to send crash report due to failure writing GoogleDataTransport event");
return;
}
if (error) {
FIRCLSErrorLog(@"Failed to send crash report due to GoogleDataTransport error: %@",
error.localizedDescription);
return;
}
FIRCLSInfoLog(@"Completed report submission with id: %@", path.lastPathComponent);
if (urgent) {
dispatch_semaphore_signal(semaphore);
}
[self cleanUpSubmittedReportAtPath:path];
}];
if (urgent) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
}
- (BOOL)cleanUpSubmittedReportAtPath:(NSString *)path {
@ -278,79 +222,4 @@
return YES;
}
- (void)reportUploadAtPath:(NSString *)path
dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
completedWithError:(NSError *)error {
FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"completed submission of %@", path);
if (!error) {
[self cleanUpSubmittedReportAtPath:path];
}
[[self delegate] didCompletePackageSubmission:path
dataCollectionToken:dataCollectionToken
error:error];
}
#pragma mark - Properties (TODO: Can delete once the experiment is over)
- (NSURL *)reportURL {
FIRCLSURLBuilder *url = [FIRCLSURLBuilder URLWithBase:FIRCLSReportsEndpoint];
[url appendComponent:@"/sdk-api/v1/platforms/"];
[url appendComponent:FIRCLSApplicationGetPlatform()];
[url appendComponent:@"/apps/"];
[url appendComponent:self.dataSource.settings.fetchedBundleID];
[url appendComponent:@"/reports"];
return [url URL];
}
- (NSString *)localeIdentifier {
return [[NSLocale currentLocale] localeIdentifier];
}
#pragma mark - URL Requests
- (NSMutableURLRequest *)mutableRequestWithURL:(NSURL *)url timeout:(NSTimeInterval)timeout {
NSMutableURLRequest *request =
[NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:timeout];
NSString *localeId = [self localeIdentifier];
[request setValue:@CLS_SDK_GENERATOR_NAME forHTTPHeaderField:FIRCLSNetworkUserAgent];
[request setValue:FIRCLSNetworkApplicationJson forHTTPHeaderField:FIRCLSNetworkAccept];
[request setValue:FIRCLSNetworkUTF8 forHTTPHeaderField:FIRCLSNetworkAcceptCharset];
[request setValue:localeId forHTTPHeaderField:FIRCLSNetworkAcceptLanguage];
[request setValue:localeId forHTTPHeaderField:FIRCLSNetworkContentLanguage];
[request setValue:FIRCLSDeveloperToken forHTTPHeaderField:FIRCLSNetworkCrashlyticsDeveloperToken];
[request setValue:FIRCLSApplicationGetSDKBundleID()
forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientId];
[request setValue:@CLS_SDK_DISPLAY_VERSION
forHTTPHeaderField:FIRCLSNetworkCrashlyticsAPIClientDisplayVersion];
[request setValue:[[self dataSource] googleAppID]
forHTTPHeaderField:FIRCLSNetworkCrashlyticsGoogleAppId];
return request;
}
- (BOOL)fillInRequest:(NSMutableURLRequest *)request forMultipartMimeDataAtPath:(NSString *)path {
NSString *boundary = [[path lastPathComponent] stringByDeletingPathExtension];
[request setValue:[FIRCLSMultipartMimeStreamEncoder
contentTypeHTTPHeaderValueWithBoundary:boundary]
forHTTPHeaderField:@"Content-Type"];
NSNumber *fileSize = [[self fileManager] fileSizeAtPath:path];
if (fileSize == nil) {
FIRCLSErrorLog(@"Could not determine size of multipart mime file");
return NO;
}
[request setValue:[fileSize stringValue] forHTTPHeaderField:@"Content-Length"];
return YES;
}
@end

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#import "FIRCLSReportUploader.h"
#import "Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h"
@interface FIRCLSReportUploader (PrivateMethods)

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