diff --git a/.circleci/config.yml b/.circleci/config.yml
index bcb01f2a8..899f6808e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -135,6 +135,10 @@ commands:
name: Test
command: |
npx detox test << parameters.folder >> --configuration ios.sim.release --cleanup
+ when: always
+
+ - store_artifacts:
+ path: ./artifacts
# JOBS
jobs:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index c868686af..3cbbc7560 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,7 +1,34 @@
-
-@RocketChat/ReactNative
+
-
-Closes #ISSUE_NUMBER
+## Proposed changes
+
-
+## Issue(s)
+
+
+## How to test or reproduce
+
+
+## Screenshots
+
+## Types of changes
+
+
+
+- [ ] Bugfix (non-breaking change which fixes an issue)
+- [ ] Improvement (non-breaking change which improves a current function)
+- [ ] New feature (non-breaking change which adds functionality)
+- [ ] Documentation update (if none of the other choices apply)
+
+## Checklist
+
+
+- [ ] I have read the [CONTRIBUTING](https://github.com/RocketChat/Rocket.Chat/blob/develop/.github/CONTRIBUTING.md#contributing-to-rocketchat) doc
+- [ ] I have signed the [CLA](https://cla-assistant.io/RocketChat/Rocket.Chat.ReactNative)
+- [ ] Lint and unit tests pass locally with my changes
+- [ ] I have added tests that prove my fix is effective or that my feature works (if applicable)
+- [ ] I have added necessary documentation (if applicable)
+- [ ] Any dependent changes have been merged and published in downstream modules
+
+## Further comments
+
diff --git a/.gitignore b/.gitignore
index fee7bf865..18624dd0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,6 +58,7 @@ buck-out/
coverage
+artifacts
.vscode/
e2e/docker/rc_test_env/docker-compose.yml
e2e/docker/data/db
\ No newline at end of file
diff --git a/__mocks__/react-native-firebase.js b/__mocks__/@react-native-firebase/analytics.js
similarity index 100%
rename from __mocks__/react-native-firebase.js
rename to __mocks__/@react-native-firebase/analytics.js
diff --git a/__mocks__/@react-native-firebase/crashlytics.js b/__mocks__/@react-native-firebase/crashlytics.js
new file mode 100644
index 000000000..8e8b4e8db
--- /dev/null
+++ b/__mocks__/@react-native-firebase/crashlytics.js
@@ -0,0 +1,3 @@
+export default {
+ crashlytics: null
+};
diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap
index e881064aa..4b04cc4a8 100644
--- a/__tests__/__snapshots__/Storyshots.test.js.snap
+++ b/__tests__/__snapshots__/Storyshots.test.js.snap
@@ -4994,137 +4994,6 @@ exports[`Storyshots Message list message 1`] = `
`;
-exports[`Storyshots RoomItem list roomitem 1`] = `
-
-
-
- Basic
-
- View
-
- User
-
- View
- View
-
- Type
-
- View
- View
- View
- View
- View
-
- Alerts
-
- View
- View
- View
- View
- View
- View
- View
- View
- View
-
- Last Message
-
- View
- View
- View
- View
- View
- View
-
-
-`;
-
exports[`Storyshots UiKitMessage list uikitmessage 1`] = `
getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List packages = new PackageList(this).getPackages();
- if (BuildConfig.PLAY_BUILD) {
- packages.add(new RNFirebaseCrashlyticsPackage());
- packages.add(new RNFirebaseAnalyticsPackage());
- packages.add(new RNFirebasePerformancePackage());
+ if (!BuildConfig.FDROID_BUILD) {
packages.add(new RNNotificationsPackage(MainApplication.this));
}
packages.add(new KeyboardInputPackage(MainApplication.this));
diff --git a/android/build.gradle b/android/build.gradle
index 5ba4f467b..a1544ceaf 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,10 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
- buildToolsVersion = "28.0.3"
+ buildToolsVersion = "29.0.2"
minSdkVersion = 21
- compileSdkVersion = 28
- targetSdkVersion = 28
+ compileSdkVersion = 29
+ targetSdkVersion = 29
glideVersion = "4.9.0"
kotlin_version = "1.3.50"
supportLibVersion = "28.0.0"
@@ -24,11 +24,10 @@ buildscript {
dependencies {
if (!isFoss) {
classpath 'com.google.gms:google-services:4.2.0'
- classpath 'io.fabric.tools:gradle:1.28.1'
- classpath 'com.google.firebase:perf-plugin:1.2.1'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
classpath 'com.bugsnag:bugsnag-android-gradle-plugin:4.+'
}
- classpath 'com.android.tools.build:gradle:3.5.2'
+ classpath 'com.android.tools.build:gradle:3.5.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/android/gradle.properties b/android/gradle.properties
index a7df71591..6a47b0e29 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -27,7 +27,7 @@ android.useAndroidX=true
android.enableJetifier=true
# Version of flipper SDK to use with React Native
-FLIPPER_VERSION=0.33.1
+FLIPPER_VERSION=0.37.0
# App properties
VERSIONCODE=999999999
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index e8758b6d5..e603f2a51 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/android/gradlew b/android/gradlew
index 77564ddb2..645f6ca31 100755
--- a/android/gradlew
+++ b/android/gradlew
@@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
else
eval `echo args$i`="\"$arg\""
fi
- i=$((i+1))
+ i=`expr $i + 1`
done
case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
@@ -175,14 +175,9 @@ save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
-APP_ARGS=$(save "$@")
+APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
-
exec "$JAVACMD" "$@"
\ No newline at end of file
diff --git a/android/gradlew.bat b/android/gradlew.bat
index 7d3e0e477..8d8accd7c 100644
--- a/android/gradlew.bat
+++ b/android/gradlew.bat
@@ -5,7 +5,7 @@
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
-@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
diff --git a/app/constants/environment.js b/app/constants/environment.js
index 15f70a619..099d27d1c 100644
--- a/app/constants/environment.js
+++ b/app/constants/environment.js
@@ -1,3 +1,3 @@
import RNConfigReader from 'react-native-config-reader';
-export const isGooglePlayBuild = RNConfigReader.PLAY_BUILD;
+export const isFDroidBuild = RNConfigReader.FDROID_BUILD;
diff --git a/app/containers/LoginServices.js b/app/containers/LoginServices.js
index 11ffc6d97..31c8bbaf2 100644
--- a/app/containers/LoginServices.js
+++ b/app/containers/LoginServices.js
@@ -15,6 +15,7 @@ import OrSeparator from './OrSeparator';
import Touch from '../utils/touch';
import I18n from '../i18n';
import random from '../utils/random';
+import { logEvent, events } from '../utils/log';
import RocketChat from '../lib/rocketchat';
const BUTTON_HEIGHT = 48;
@@ -77,6 +78,7 @@ class LoginServices extends React.PureComponent {
}
onPressFacebook = () => {
+ logEvent(events.LOGIN_WITH_FACEBOOK);
const { services, server } = this.props;
const { clientId } = services.facebook;
const endpoint = 'https://m.facebook.com/v2.9/dialog/oauth';
@@ -88,6 +90,7 @@ class LoginServices extends React.PureComponent {
}
onPressGithub = () => {
+ logEvent(events.LOGIN_WITH_GITHUB);
const { services, server } = this.props;
const { clientId } = services.github;
const endpoint = `https://github.com/login?client_id=${ clientId }&return_to=${ encodeURIComponent('/login/oauth/authorize') }`;
@@ -99,6 +102,7 @@ class LoginServices extends React.PureComponent {
}
onPressGitlab = () => {
+ logEvent(events.LOGIN_WITH_GITLAB);
const { services, server, Gitlab_URL } = this.props;
const { clientId } = services.gitlab;
const baseURL = Gitlab_URL ? Gitlab_URL.trim().replace(/\/*$/, '') : 'https://gitlab.com';
@@ -111,6 +115,7 @@ class LoginServices extends React.PureComponent {
}
onPressGoogle = () => {
+ logEvent(events.LOGIN_WITH_GOOGLE);
const { services, server } = this.props;
const { clientId } = services.google;
const endpoint = 'https://accounts.google.com/o/oauth2/auth';
@@ -122,6 +127,7 @@ class LoginServices extends React.PureComponent {
}
onPressLinkedin = () => {
+ logEvent(events.LOGIN_WITH_LINKEDIN);
const { services, server } = this.props;
const { clientId } = services.linkedin;
const endpoint = 'https://www.linkedin.com/oauth/v2/authorization';
@@ -133,6 +139,7 @@ class LoginServices extends React.PureComponent {
}
onPressMeteor = () => {
+ logEvent(events.LOGIN_WITH_METEOR);
const { services, server } = this.props;
const { clientId } = services['meteor-developer'];
const endpoint = 'https://www.meteor.com/oauth2/authorize';
@@ -143,6 +150,7 @@ class LoginServices extends React.PureComponent {
}
onPressTwitter = () => {
+ logEvent(events.LOGIN_WITH_TWITTER);
const { server } = this.props;
const state = this.getOAuthState();
const url = `${ server }/_oauth/twitter/?requestTokenAndRedirect=true&state=${ state }`;
@@ -150,6 +158,7 @@ class LoginServices extends React.PureComponent {
}
onPressWordpress = () => {
+ logEvent(events.LOGIN_WITH_WORDPRESS);
const { services, server } = this.props;
const { clientId, serverURL } = services.wordpress;
const endpoint = `${ serverURL }/oauth/authorize`;
@@ -161,6 +170,7 @@ class LoginServices extends React.PureComponent {
}
onPressCustomOAuth = (loginService) => {
+ logEvent(events.LOGIN_WITH_CUSTOM_OAUTH);
const { server } = this.props;
const {
serverURL, authorizePath, clientId, scope, service
@@ -175,6 +185,7 @@ class LoginServices extends React.PureComponent {
}
onPressSaml = (loginService) => {
+ logEvent(events.LOGIN_WITH_SAML);
const { server } = this.props;
const { clientConfig } = loginService;
const { provider } = clientConfig;
@@ -184,6 +195,7 @@ class LoginServices extends React.PureComponent {
}
onPressCas = () => {
+ logEvent(events.LOGIN_WITH_CAS);
const { server, CAS_login_url } = this.props;
const ssoToken = random(17);
const url = `${ CAS_login_url }?service=${ server }/_cas/${ ssoToken }`;
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index 813d7d64c..92f74e5de 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -122,6 +122,7 @@ class MessageBox extends Component {
command: {}
};
this.text = '';
+ this.selection = { start: 0, end: 0 };
this.focused = false;
// MessageBox Actions
@@ -331,6 +332,10 @@ class MessageBox extends Component {
this.setInput(text);
}
+ onSelectionChange = (e) => {
+ this.selection = e.nativeEvent.selection;
+ }
+
// eslint-disable-next-line react/sort-comp
debouncedOnChangeText = debounce(async(text) => {
const { sharing } = this.props;
@@ -358,9 +363,9 @@ class MessageBox extends Component {
if (!isTextEmpty) {
try {
- const { start, end } = this.component?.lastNativeSelection;
+ const { start, end } = this.selection;
const cursor = Math.max(start, end);
- const lastNativeText = this.component?.lastNativeText || '';
+ const lastNativeText = this.text;
// matches if text either starts with '/' or have (@,#,:) then it groups whatever comes next of mention type
let regexp = /(#|@|:|^\/)([a-z0-9._-]+)$/im;
@@ -399,7 +404,7 @@ class MessageBox extends Component {
}
const { trackingType } = this.state;
const msg = this.text;
- const { start, end } = this.component?.lastNativeSelection;
+ const { start, end } = this.selection;
const cursor = Math.max(start, end);
const regexp = /([a-z0-9._-]+)$/im;
const result = msg.substr(0, cursor).replace(regexp, '');
@@ -410,7 +415,8 @@ class MessageBox extends Component {
if ((trackingType === MENTIONS_TRACKING_TYPE_COMMANDS) && item.providesPreview) {
this.setState({ showCommandPreview: true });
}
- this.setInput(text);
+ const newCursor = cursor + mentionName.length;
+ this.setInput(text, { start: newCursor, end: newCursor });
this.focus();
requestAnimationFrame(() => this.stopTrackingMention());
}
@@ -443,15 +449,11 @@ class MessageBox extends Component {
let newText = '';
// if messagebox has an active cursor
- if (this.component?.lastNativeSelection) {
- const { start, end } = this.component.lastNativeSelection;
- const cursor = Math.max(start, end);
- newText = `${ text.substr(0, cursor) }${ emoji }${ text.substr(cursor) }`;
- } else {
- // if messagebox doesn't have a cursor, just append selected emoji
- newText = `${ text }${ emoji }`;
- }
- this.setInput(newText);
+ const { start, end } = this.selection;
+ const cursor = Math.max(start, end);
+ newText = `${ text.substr(0, cursor) }${ emoji }${ text.substr(cursor) }`;
+ const newCursor = cursor + emoji.length;
+ this.setInput(newText, { start: newCursor, end: newCursor });
this.setShowSend(true);
}
@@ -551,11 +553,12 @@ class MessageBox extends Component {
this.setState({ commandPreview: [], showCommandPreview: true, command: {} });
}
- setInput = (text) => {
+ setInput = (text, selection) => {
this.text = text;
- if (this.component && this.component.setNativeProps) {
- this.component.setNativeProps({ text });
+ if (selection) {
+ return this.component.setTextAndSelection(text, selection);
}
+ this.component.setNativeProps({ text });
}
setShowSend = (showSend) => {
@@ -888,6 +891,7 @@ class MessageBox extends Component {
blurOnSubmit={false}
placeholder={I18n.t('New_Message')}
onChangeText={this.onChangeText}
+ onSelectionChange={this.onSelectionChange}
underlineColorAndroid='transparent'
defaultValue=''
multiline
diff --git a/app/containers/TwoFactor/index.js b/app/containers/TwoFactor/index.js
index e59ed0c1a..011b5e3de 100644
--- a/app/containers/TwoFactor/index.js
+++ b/app/containers/TwoFactor/index.js
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
-import { View, Text } from 'react-native';
+import { View, Text, InteractionManager } from 'react-native';
import _ from 'lodash';
import PropTypes from 'prop-types';
import { sha256 } from 'js-sha256';
@@ -99,6 +99,7 @@ const TwoFactor = React.memo(({ theme, isMasterDetail }) => {
InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
returnKeyType='send'
autoCapitalize='none'
onChangeText={setCode}
diff --git a/app/containers/TwoFactor/styles.js b/app/containers/TwoFactor/styles.js
index 36608408a..1c5c789cb 100644
--- a/app/containers/TwoFactor/styles.js
+++ b/app/containers/TwoFactor/styles.js
@@ -14,9 +14,10 @@ export default StyleSheet.create({
borderRadius: 4
},
title: {
- fontSize: 14,
+ fontSize: 16,
paddingBottom: 8,
- ...sharedStyles.textBold
+ ...sharedStyles.textBold,
+ ...sharedStyles.textAlignCenter
},
subtitle: {
fontSize: 14,
diff --git a/app/index.js b/app/index.js
index d93fa2185..8154208bb 100644
--- a/app/index.js
+++ b/app/index.js
@@ -36,7 +36,7 @@ import Toast from './containers/Toast';
import InAppNotification from './containers/InAppNotification';
import { ActionSheetProvider } from './containers/ActionSheet';
import debounce from './utils/debounce';
-import { isGooglePlayBuild } from './constants/environment';
+import { isFDroidBuild } from './constants/environment';
RNScreens.enableScreens();
@@ -59,7 +59,7 @@ export default class Root extends React.Component {
constructor(props) {
super(props);
this.init();
- if (isGooglePlayBuild) {
+ if (!isFDroidBuild) {
this.initCrashReport();
}
const { width, height, scale } = Dimensions.get('window');
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index cfa959919..1353b7364 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -845,6 +845,12 @@ const RocketChat = {
return other && other.length ? other[0] : me;
},
+ isRead(item) {
+ let isUnread = item.archived !== true && item.open === true; // item is not archived and not opened
+ isUnread = isUnread && (item.unread > 0 || item.alert === true); // either its unread count > 0 or its alert
+ return !isUnread;
+ },
+
isGroupChat(room) {
return (room.uids && room.uids.length > 2) || (room.usernames && room.usernames.length > 2);
},
diff --git a/app/notifications/push/index.js b/app/notifications/push/index.js
index fc232e06a..7e867801b 100644
--- a/app/notifications/push/index.js
+++ b/app/notifications/push/index.js
@@ -2,7 +2,7 @@ import EJSON from 'ejson';
import PushNotification from './push';
import store from '../../lib/createStore';
import { deepLinkingOpen } from '../../actions/deepLinking';
-import { isGooglePlayBuild } from '../../constants/environment';
+import { isFDroidBuild } from '../../constants/environment';
export const onNotification = (notification) => {
if (notification) {
@@ -37,7 +37,7 @@ export const onNotification = (notification) => {
export const getDeviceToken = () => PushNotification.getDeviceToken();
export const setBadgeCount = count => PushNotification.setBadgeCount(count);
export const initializePushNotifications = () => {
- if (isGooglePlayBuild) {
+ if (!isFDroidBuild) {
setBadgeCount();
return PushNotification.configure({
onNotification
diff --git a/app/presentation/KeyboardView.js b/app/presentation/KeyboardView.js
index 9bd25191e..628213825 100644
--- a/app/presentation/KeyboardView.js
+++ b/app/presentation/KeyboardView.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
+import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view';
import scrollPersistTaps from '../utils/scrollPersistTaps';
export default class KeyboardView extends React.PureComponent {
diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js
index 1b3ee93f2..487c22430 100644
--- a/app/presentation/RoomItem/index.js
+++ b/app/presentation/RoomItem/index.js
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
@@ -16,82 +16,79 @@ import { themes } from '../../constants/colors';
export { ROW_HEIGHT };
const attrs = [
- 'name',
- 'unread',
- 'userMentions',
- 'showLastMessage',
- 'useRealName',
- 'alert',
- 'type',
'width',
- 'isRead',
- 'favorite',
'status',
'connected',
'theme',
- 'isFocused'
+ 'isFocused',
+ 'forceUpdate',
+ 'showLastMessage'
];
-const arePropsEqual = (oldProps, newProps) => {
- const { _updatedAt: _updatedAtOld } = oldProps;
- const { _updatedAt: _updatedAtNew } = newProps;
- if (_updatedAtOld && _updatedAtNew && _updatedAtOld.toISOString() !== _updatedAtNew.toISOString()) {
- return false;
- }
- return attrs.every(key => oldProps[key] === newProps[key]);
-};
+const arePropsEqual = (oldProps, newProps) => attrs.every(key => oldProps[key] === newProps[key]);
const RoomItem = React.memo(({
+ item,
onPress,
width,
- favorite,
toggleFav,
- isRead,
- rid,
toggleRead,
hideChannel,
testID,
- unread,
- userMentions,
- name,
- _updatedAt,
- alert,
- type,
avatarSize,
baseUrl,
userId,
username,
token,
id,
- prid,
showLastMessage,
- hideUnreadStatus,
- lastMessage,
status,
- avatar,
useRealName,
getUserPresence,
- isGroupChat,
connected,
theme,
- isFocused
+ isFocused,
+ getRoomTitle,
+ getRoomAvatar,
+ getIsGroupChat,
+ getIsRead
}) => {
+ const [, setForceUpdate] = useState(1);
+
useEffect(() => {
- if (connected && type === 'd' && id) {
+ if (connected && item.t === 'd' && id) {
getUserPresence(id);
}
}, [connected]);
- const date = lastMessage && formatDate(lastMessage.ts);
+ useEffect(() => {
+ if (item?.observe) {
+ const observable = item.observe();
+ const subscription = observable?.subscribe?.(() => {
+ setForceUpdate(prevForceUpdate => prevForceUpdate + 1);
+ });
+
+ return () => {
+ subscription?.unsubscribe?.();
+ };
+ }
+ }, []);
+
+ const name = getRoomTitle(item);
+ const avatar = getRoomAvatar(item);
+ const isGroupChat = getIsGroupChat(item);
+ const isRead = getIsRead(item);
+ const _onPress = () => onPress(item);
+ const date = item.lastMessage?.ts && formatDate(item.lastMessage.ts);
let accessibilityLabel = name;
- if (unread === 1) {
- accessibilityLabel += `, ${ unread } ${ I18n.t('alert') }`;
- } else if (unread > 1) {
- accessibilityLabel += `, ${ unread } ${ I18n.t('alerts') }`;
+ if (item.unread === 1) {
+ accessibilityLabel += `, ${ item.unread } ${ I18n.t('alert') }`;
+ } else if (item.unread > 1) {
+ accessibilityLabel += `, ${ item.unread } ${ I18n.t('alerts') }`;
}
- if (userMentions > 0) {
+ if (item.userMentions > 0) {
accessibilityLabel += `, ${ I18n.t('you_were_mentioned') }`;
}
@@ -101,16 +98,16 @@ const RoomItem = React.memo(({
return (
@@ -121,7 +118,7 @@ const RoomItem = React.memo(({
{name}
- {_updatedAt ? (
+ {item.roomUpdatedAt ? (
@@ -203,17 +200,10 @@ const RoomItem = React.memo(({
}, arePropsEqual);
RoomItem.propTypes = {
- type: PropTypes.string.isRequired,
- name: PropTypes.string.isRequired,
+ item: PropTypes.object.isRequired,
baseUrl: PropTypes.string.isRequired,
showLastMessage: PropTypes.bool,
- _updatedAt: PropTypes.string,
- lastMessage: PropTypes.object,
- alert: PropTypes.bool,
- unread: PropTypes.number,
- userMentions: PropTypes.number,
id: PropTypes.string,
- prid: PropTypes.string,
onPress: PropTypes.func,
userId: PropTypes.string,
username: PropTypes.string,
@@ -221,27 +211,29 @@ RoomItem.propTypes = {
avatarSize: PropTypes.number,
testID: PropTypes.string,
width: PropTypes.number,
- favorite: PropTypes.bool,
- isRead: PropTypes.bool,
- rid: PropTypes.string,
status: PropTypes.string,
toggleFav: PropTypes.func,
toggleRead: PropTypes.func,
hideChannel: PropTypes.func,
- avatar: PropTypes.bool,
- hideUnreadStatus: PropTypes.bool,
useRealName: PropTypes.bool,
getUserPresence: PropTypes.func,
connected: PropTypes.bool,
- isGroupChat: PropTypes.bool,
theme: PropTypes.string,
- isFocused: PropTypes.bool
+ isFocused: PropTypes.bool,
+ getRoomTitle: PropTypes.func,
+ getRoomAvatar: PropTypes.func,
+ getIsGroupChat: PropTypes.func,
+ getIsRead: PropTypes.func
};
RoomItem.defaultProps = {
avatarSize: 48,
status: 'offline',
- getUserPresence: () => {}
+ getUserPresence: () => {},
+ getRoomTitle: () => 'title',
+ getRoomAvatar: () => '',
+ getIsGroupChat: () => false,
+ getIsRead: () => false
};
const mapStateToProps = (state, ownProps) => {
diff --git a/app/sagas/deepLinking.js b/app/sagas/deepLinking.js
index 2069cbd07..ffa04040f 100644
--- a/app/sagas/deepLinking.js
+++ b/app/sagas/deepLinking.js
@@ -5,12 +5,12 @@ import RNUserDefaults from 'rn-user-defaults';
import Navigation from '../lib/Navigation';
import * as types from '../actions/actionsTypes';
-import { selectServerRequest } from '../actions/server';
+import { selectServerRequest, serverInitAdd } from '../actions/server';
import { inviteLinksSetToken, inviteLinksRequest } from '../actions/inviteLinks';
import database from '../lib/database';
import RocketChat from '../lib/rocketchat';
import EventEmitter from '../utils/events';
-import { appStart, ROOT_INSIDE } from '../actions/app';
+import { appStart, ROOT_INSIDE, ROOT_NEW_SERVER } from '../actions/app';
import { localAuthenticate } from '../utils/localAuthentication';
import { goRoom } from '../utils/goRoom';
@@ -106,7 +106,8 @@ const handleOpen = function* handleOpen({ params }) {
if (!result.success) {
return;
}
- Navigation.navigate('NewServerView', { previousServer: server });
+ yield put(appStart({ root: ROOT_NEW_SERVER }));
+ yield put(serverInitAdd(server));
yield delay(1000);
EventEmitter.emit('NewServer', { server: host });
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 587b9b7ba..199952190 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -17,7 +17,7 @@ import {
import { roomsRequest } from '../actions/rooms';
import { toMomentLocale } from '../utils/moment';
import RocketChat from '../lib/rocketchat';
-import log from '../utils/log';
+import log, { logEvent, events } from '../utils/log';
import I18n from '../i18n';
import database from '../lib/database';
import EventEmitter from '../utils/events';
@@ -32,6 +32,7 @@ const loginCall = args => RocketChat.login(args);
const logoutCall = args => RocketChat.logout(args);
const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnError = false }) {
+ logEvent(events.DEFAULT_LOGIN);
try {
let result;
if (credentials.resume) {
@@ -52,6 +53,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials, logoutOnE
if (logoutOnError && (e.data && e.data.message && /you've been logged out by the server/i.test(e.data.message))) {
yield put(logout(true));
} else {
+ logEvent(events.DEFAULT_LOGIN_FAIL);
yield put(loginFailure(e));
}
}
diff --git a/app/stacks/InsideStack.js b/app/stacks/InsideStack.js
index e29c39dd9..8a0f2e919 100644
--- a/app/stacks/InsideStack.js
+++ b/app/stacks/InsideStack.js
@@ -112,7 +112,6 @@ const ChatsStackNavigator = () => {
{
{ }
-});
-
+export { analytics };
export const loggerConfig = bugsnag.config;
export const { leaveBreadcrumb } = bugsnag;
+export { events };
let metadata = {};
@@ -20,11 +20,18 @@ export const logServerVersion = (serverVersion) => {
};
};
-export const setCurrentScreen = (currentScreen) => {
- if (isGooglePlayBuild) {
- analytics().setCurrentScreen(currentScreen);
+export const logEvent = (eventName, payload) => {
+ if (!isFDroidBuild) {
+ analytics().logEvent(eventName, payload);
+ leaveBreadcrumb(eventName, payload);
+ }
+};
+
+export const setCurrentScreen = (currentScreen) => {
+ if (!isFDroidBuild) {
+ analytics().setCurrentScreen(currentScreen);
+ leaveBreadcrumb(currentScreen, { type: 'navigation' });
}
- leaveBreadcrumb(currentScreen, { type: 'navigation' });
};
export default (e) => {
@@ -36,6 +43,9 @@ export default (e) => {
}
};
});
+ if (!isFDroidBuild) {
+ crashlytics().recordError(e);
+ }
} else {
console.log(e);
}
diff --git a/app/utils/review.js b/app/utils/review.js
index 378cc65eb..76040c3e8 100644
--- a/app/utils/review.js
+++ b/app/utils/review.js
@@ -5,7 +5,7 @@ import { isIOS } from './deviceInfo';
import I18n from '../i18n';
import { showErrorAlert } from './info';
import { STORE_REVIEW_LINK } from '../constants/links';
-import { isGooglePlayBuild } from '../constants/environment';
+import { isFDroidBuild } from '../constants/environment';
const store = isIOS ? 'App Store' : 'Play Store';
@@ -76,7 +76,7 @@ const tryReview = async() => {
// if ask me later was pressed earlier, we can ask for review only after {{numberOfDays}} days
// if there's no review and it wasn't dismissed by the user
- if (daysBetween(lastReviewDate, new Date()) >= numberOfDays && !doneReview && isGooglePlayBuild) {
+ if (daysBetween(lastReviewDate, new Date()) >= numberOfDays && !doneReview) {
setTimeout(askReview, reviewDelay);
}
};
@@ -90,7 +90,9 @@ class ReviewApp {
}
this.positiveEventCount += 1;
if (this.positiveEventCount === numberOfPositiveEvent) {
- tryReview();
+ if (!isFDroidBuild) {
+ tryReview();
+ }
}
}
}
diff --git a/app/views/AdminPanelView/index.js b/app/views/AdminPanelView/index.js
index 283e2ef74..41005fc93 100644
--- a/app/views/AdminPanelView/index.js
+++ b/app/views/AdminPanelView/index.js
@@ -31,6 +31,8 @@ class AdminPanelView extends React.Component {
{}}
source={{ uri: `${ baseUrl }/admin/info?layout=embedded` }}
injectedJavaScript={`Meteor.loginWithToken('${ token }', function() { })`}
/>
diff --git a/app/views/DefaultBrowserView.js b/app/views/DefaultBrowserView.js
index 3148952ae..d0b9597ef 100644
--- a/app/views/DefaultBrowserView.js
+++ b/app/views/DefaultBrowserView.js
@@ -19,11 +19,11 @@ import SafeAreaView from '../containers/SafeAreaView';
const DEFAULT_BROWSERS = [
{
- title: I18n.t('In_app'),
+ title: 'In_app',
value: 'inApp'
},
{
- title: isIOS ? 'Safari' : I18n.t('Browser'),
+ title: isIOS ? 'Safari' : 'Browser',
value: 'systemDefault:'
}
];
@@ -137,7 +137,7 @@ class DefaultBrowserView extends React.Component {
const { title, value } = item;
return (
this.changeDefaultBrowser(value)}
testID={`default-browser-view-${ title }`}
right={this.isSelected(value) ? this.renderIcon : null}
diff --git a/app/views/InviteUsersEditView/index.js b/app/views/InviteUsersEditView/index.js
index ee9874298..08ff79040 100644
--- a/app/views/InviteUsersEditView/index.js
+++ b/app/views/InviteUsersEditView/index.js
@@ -21,9 +21,6 @@ import SafeAreaView from '../../containers/SafeAreaView';
const OPTIONS = {
days: [{
- label: I18n.t('Never'), value: 0
- },
- {
label: '1', value: 1
},
{
@@ -36,9 +33,6 @@ const OPTIONS = {
label: '30', value: 30
}],
maxUses: [{
- label: I18n.t('No_limit'), value: 0
- },
- {
label: '1', value: 1
},
{
@@ -91,9 +85,12 @@ class InviteUsersView extends React.Component {
navigation.pop();
}
- renderPicker = (key) => {
+ renderPicker = (key, first) => {
const { props } = this;
const { theme } = props;
+ const firstEl = [{
+ label: I18n.t(first), value: 0
+ }];
return (
this.onValueChangePicker(key, value)}
- items={OPTIONS[key]}
+ items={firstEl.concat(OPTIONS[key])}
/>
);
}
@@ -121,13 +118,13 @@ class InviteUsersView extends React.Component {
this.renderPicker('days')}
+ right={() => this.renderPicker('days', 'Never')}
theme={theme}
/>
this.renderPicker('maxUses')}
+ right={() => this.renderPicker('maxUses', 'No_limit')}
theme={theme}
/>
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index fe7587d9c..9e566b5be 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -6,7 +6,7 @@ import {
import { connect } from 'react-redux';
import equal from 'deep-equal';
-import { analytics } from '../utils/log';
+import { logEvent, events } from '../utils/log';
import sharedStyles from './Styles';
import Button from '../containers/Button';
import I18n from '../i18n';
@@ -17,7 +17,6 @@ import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import TextInput from '../containers/TextInput';
import { loginRequest as loginRequestAction } from '../actions/login';
import LoginServices from '../containers/LoginServices';
-import { isGooglePlayBuild } from '../constants/environment';
const styles = StyleSheet.create({
registerDisabled: {
@@ -104,6 +103,7 @@ class LoginView extends React.Component {
}
forgotPassword = () => {
+ logEvent(events.FORGOT_PASSWORD);
const { navigation, Site_Name } = this.props;
navigation.navigate('ForgotPasswordView', { title: Site_Name });
}
@@ -122,9 +122,6 @@ class LoginView extends React.Component {
const { loginRequest } = this.props;
Keyboard.dismiss();
loginRequest({ user, password });
- if (isGooglePlayBuild) {
- analytics().logEvent('login');
- }
}
renderUserForm = () => {
diff --git a/app/views/MessagesView/index.js b/app/views/MessagesView/index.js
index 1a55b72f3..4e826d3fc 100644
--- a/app/views/MessagesView/index.js
+++ b/app/views/MessagesView/index.js
@@ -18,10 +18,6 @@ import { withActionSheet } from '../../containers/ActionSheet';
import SafeAreaView from '../../containers/SafeAreaView';
class MessagesView extends React.Component {
- static navigationOptions = ({ route }) => ({
- title: I18n.t(route.params?.name)
- });
-
static propTypes = {
user: PropTypes.object,
baseUrl: PropTypes.string,
@@ -39,6 +35,7 @@ class MessagesView extends React.Component {
messages: [],
fileLoading: true
};
+ this.setHeader();
this.rid = props.route.params?.rid;
this.t = props.route.params?.t;
this.content = this.defineMessagesViewContent(props.route.params?.name);
@@ -65,10 +62,16 @@ class MessagesView extends React.Component {
if (fileLoading !== nextState.fileLoading) {
return true;
}
-
return false;
}
+ setHeader = () => {
+ const { route, navigation } = this.props;
+ navigation.setOptions({
+ title: I18n.t(route.params?.name)
+ });
+ }
+
navToRoomInfo = (navParam) => {
const { navigation, user } = this.props;
if (navParam.rid === user.id) {
diff --git a/app/views/ModalBlockView.js b/app/views/ModalBlockView.js
index 57b415fdc..e88d30885 100644
--- a/app/views/ModalBlockView.js
+++ b/app/views/ModalBlockView.js
@@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native';
import PropTypes from 'prop-types';
import isEqual from 'lodash/isEqual';
import { connect } from 'react-redux';
-import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
+import { KeyboardAwareScrollView } from '@codler/react-native-keyboard-aware-scroll-view';
import { withTheme } from '../theme';
import EventEmitter from '../utils/events';
diff --git a/app/views/NewServerView.js b/app/views/NewServerView.js
index b9429c05a..5d2726cdd 100644
--- a/app/views/NewServerView.js
+++ b/app/views/NewServerView.js
@@ -7,11 +7,12 @@ import { connect } from 'react-redux';
import * as FileSystem from 'expo-file-system';
import DocumentPicker from 'react-native-document-picker';
import RNUserDefaults from 'rn-user-defaults';
-import { encode } from 'base-64';
+import { Base64 } from 'js-base64';
import parse from 'url-parse';
import EventEmitter from '../utils/events';
import { selectServerRequest, serverRequest } from '../actions/server';
+import { inviteLinksClear as inviteLinksClearAction } from '../actions/inviteLinks';
import sharedStyles from './Styles';
import Button from '../containers/Button';
import TextInput from '../containers/TextInput';
@@ -20,7 +21,7 @@ import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import I18n from '../i18n';
import { isIOS } from '../utils/deviceInfo';
import { themes } from '../constants/colors';
-import log from '../utils/log';
+import log, { logEvent, events } from '../utils/log';
import { animateNextTransition } from '../utils/layoutAnimation';
import { withTheme } from '../theme';
import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch';
@@ -72,16 +73,13 @@ class NewServerView extends React.Component {
connectServer: PropTypes.func.isRequired,
selectServer: PropTypes.func.isRequired,
adding: PropTypes.bool,
- previousServer: PropTypes.string
+ previousServer: PropTypes.string,
+ inviteLinksClear: PropTypes.func
}
constructor(props) {
super(props);
- if (props.adding) {
- props.navigation.setOptions({
- headerLeft: () =>
- });
- }
+ this.setHeader();
this.state = {
text: '',
@@ -92,11 +90,27 @@ class NewServerView extends React.Component {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
+ componentDidUpdate(prevProps) {
+ const { adding } = this.props;
+ if (prevProps.adding !== adding) {
+ this.setHeader();
+ }
+ }
+
componentWillUnmount() {
EventEmitter.removeListener('NewServer', this.handleNewServerEvent);
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
+ setHeader = () => {
+ const { adding, navigation } = this.props;
+ if (adding) {
+ navigation.setOptions({
+ headerLeft: () =>
+ });
+ }
+ }
+
handleBackPress = () => {
const { navigation, previousServer } = this.props;
if (navigation.isFocused() && previousServer) {
@@ -111,7 +125,8 @@ class NewServerView extends React.Component {
}
close = () => {
- const { selectServer, previousServer } = this.props;
+ const { selectServer, previousServer, inviteLinksClear } = this.props;
+ inviteLinksClear();
selectServer(previousServer);
}
@@ -124,6 +139,7 @@ class NewServerView extends React.Component {
}
submit = async() => {
+ logEvent(events.CONNECT_TO_WORKSPACE);
const { text, certificate } = this.state;
const { connectServer } = this.props;
let cert = null;
@@ -135,6 +151,7 @@ class NewServerView extends React.Component {
try {
await FileSystem.copyAsync({ from: certificate.path, to: certificatePath });
} catch (e) {
+ logEvent(events.CONNECT_TO_WORKSPACE_FAIL);
log(e);
}
cert = {
@@ -152,6 +169,7 @@ class NewServerView extends React.Component {
}
connectOpen = () => {
+ logEvent(events.JOIN_OPEN_WORKSPACE);
this.setState({ connectingOpen: true });
const { connectServer } = this.props;
connectServer('https://open.rocket.chat');
@@ -161,7 +179,7 @@ class NewServerView extends React.Component {
try {
const parsedUrl = parse(text, true);
if (parsedUrl.auth.length) {
- const credentials = encode(parsedUrl.auth);
+ const credentials = Base64.encode(parsedUrl.auth);
await RNUserDefaults.set(`${ BASIC_AUTH_KEY }-${ server }`, credentials);
setBasicAuth(credentials);
}
@@ -321,7 +339,8 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
connectServer: (server, certificate) => dispatch(serverRequest(server, certificate)),
- selectServer: server => dispatch(selectServerRequest(server))
+ selectServer: server => dispatch(selectServerRequest(server)),
+ inviteLinksClear: () => dispatch(inviteLinksClearAction())
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewServerView));
diff --git a/app/views/NotificationPreferencesView/index.js b/app/views/NotificationPreferencesView/index.js
index 31a0fc679..7c97ec169 100644
--- a/app/views/NotificationPreferencesView/index.js
+++ b/app/views/NotificationPreferencesView/index.js
@@ -71,58 +71,58 @@ Info.propTypes = {
const OPTIONS = {
desktopNotifications: [{
- label: I18n.t('Default'), value: 'default'
+ label: 'Default', value: 'default'
}, {
- label: I18n.t('All_Messages'), value: 'all'
+ label: 'All_Messages', value: 'all'
}, {
- label: I18n.t('Mentions'), value: 'mentions'
+ label: 'Mentions', value: 'mentions'
}, {
- label: I18n.t('Nothing'), value: 'nothing'
+ label: 'Nothing', value: 'nothing'
}],
audioNotifications: [{
- label: I18n.t('Default'), value: 'default'
+ label: 'Default', value: 'default'
}, {
- label: I18n.t('All_Messages'), value: 'all'
+ label: 'All_Messages', value: 'all'
}, {
- label: I18n.t('Mentions'), value: 'mentions'
+ label: 'Mentions', value: 'mentions'
}, {
- label: I18n.t('Nothing'), value: 'nothing'
+ label: 'Nothing', value: 'nothing'
}],
mobilePushNotifications: [{
- label: I18n.t('Default'), value: 'default'
+ label: 'Default', value: 'default'
}, {
- label: I18n.t('All_Messages'), value: 'all'
+ label: 'All_Messages', value: 'all'
}, {
- label: I18n.t('Mentions'), value: 'mentions'
+ label: 'Mentions', value: 'mentions'
}, {
- label: I18n.t('Nothing'), value: 'nothing'
+ label: 'Nothing', value: 'nothing'
}],
emailNotifications: [{
- label: I18n.t('Default'), value: 'default'
+ label: 'Default', value: 'default'
}, {
- label: I18n.t('All_Messages'), value: 'all'
+ label: 'All_Messages', value: 'all'
}, {
- label: I18n.t('Mentions'), value: 'mentions'
+ label: 'Mentions', value: 'mentions'
}, {
- label: I18n.t('Nothing'), value: 'nothing'
+ label: 'Nothing', value: 'nothing'
}],
desktopNotificationDuration: [{
- label: I18n.t('Default'), value: 0
+ label: 'Default', value: 0
}, {
- label: I18n.t('Seconds', { second: 1 }), value: 1
+ label: 'Seconds', second: 1, value: 1
}, {
- label: I18n.t('Seconds', { second: 2 }), value: 2
+ label: 'Seconds', second: 2, value: 2
}, {
- label: I18n.t('Seconds', { second: 3 }), value: 3
+ label: 'Seconds', second: 3, value: 3
}, {
- label: I18n.t('Seconds', { second: 4 }), value: 4
+ label: 'Seconds', second: 4, value: 4
}, {
- label: I18n.t('Seconds', { second: 5 }), value: 5
+ label: 'Seconds', second: 5, value: 5
}],
audioNotificationValue: [{
label: 'None', value: 'none None'
}, {
- label: I18n.t('Default'), value: '0 Default'
+ label: 'Default', value: '0 Default'
}, {
label: 'Beep', value: 'beep Beep'
}, {
@@ -229,7 +229,7 @@ class NotificationPreferencesView extends React.Component {
const { room } = this.state;
const { theme } = this.props;
const text = room[key] ? OPTIONS[key].find(option => option.value === room[key]) : OPTIONS[key][0];
- return {text?.label};
+ return {I18n.t(text?.label, { defaultValue: text?.label, second: text?.second })};
}
renderSwitch = (key) => {
diff --git a/app/views/OnboardingView/index.js b/app/views/OnboardingView/index.js
index 0a1ac7383..0d509e0d4 100644
--- a/app/views/OnboardingView/index.js
+++ b/app/views/OnboardingView/index.js
@@ -14,6 +14,7 @@ import { isTablet } from '../../utils/deviceInfo';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import FormContainer, { FormContainerInner } from '../../containers/FormContainer';
+import { logEvent, events } from '../../utils/log';
class OnboardingView extends React.Component {
static navigationOptions = {
@@ -69,15 +70,17 @@ class OnboardingView extends React.Component {
}
connectServer = () => {
+ logEvent(events.JOIN_A_WORKSPACE);
const { navigation } = this.props;
navigation.navigate('NewServerView');
}
createWorkspace = async() => {
+ logEvent(events.CREATE_NEW_WORKSPACE);
try {
await Linking.openURL('https://cloud.rocket.chat/trial');
} catch {
- // do nothing
+ logEvent(events.CREATE_NEW_WORKSPACE_FAIL);
}
}
diff --git a/app/views/PickerView.js b/app/views/PickerView.js
index 489d8a42f..062e373ab 100644
--- a/app/views/PickerView.js
+++ b/app/views/PickerView.js
@@ -42,7 +42,7 @@ const Item = React.memo(({
theme
}) => (
)}
onPress={onItemPress}
theme={theme}
diff --git a/app/views/RegisterView.js b/app/views/RegisterView.js
index 1e0ce2654..d95fb7938 100644
--- a/app/views/RegisterView.js
+++ b/app/views/RegisterView.js
@@ -6,7 +6,7 @@ import {
import { connect } from 'react-redux';
import RNPickerSelect from 'react-native-picker-select';
-import log from '../utils/log';
+import log, { logEvent, events } from '../utils/log';
import sharedStyles from './Styles';
import Button from '../containers/Button';
import I18n from '../i18n';
@@ -114,6 +114,7 @@ class RegisterView extends React.Component {
}
submit = async() => {
+ logEvent(events.DEFAULT_SIGN_UP);
if (!this.valid()) {
return;
}
@@ -149,6 +150,7 @@ class RegisterView extends React.Component {
return loginRequest({ user: email, password });
}
if (e.data?.error) {
+ logEvent(events.DEFAULT_SIGN_UP_FAIL);
showErrorAlert(e.data.error, I18n.t('Oops'));
}
}
diff --git a/app/views/RoomView/List.js b/app/views/RoomView/List.js
index d0dc7c88d..209ec18bb 100644
--- a/app/views/RoomView/List.js
+++ b/app/views/RoomView/List.js
@@ -1,7 +1,6 @@
import React from 'react';
import { FlatList, RefreshControl } from 'react-native';
import PropTypes from 'prop-types';
-import orderBy from 'lodash/orderBy';
import { Q } from '@nozbe/watermelondb';
import moment from 'moment';
import isEqual from 'lodash/isEqual';
@@ -15,9 +14,10 @@ import EmptyRoom from './EmptyRoom';
import { isIOS } from '../../utils/deviceInfo';
import { animateNextTransition } from '../../utils/layoutAnimation';
import ActivityIndicator from '../../containers/ActivityIndicator';
-import debounce from '../../utils/debounce';
import { themes } from '../../constants/colors';
+const QUERY_SIZE = 50;
+
class List extends React.Component {
static propTypes = {
onEndReached: PropTypes.func,
@@ -47,7 +47,8 @@ class List extends React.Component {
super(props);
console.time(`${ this.constructor.name } init`);
console.time(`${ this.constructor.name } mount`);
-
+ this.count = 0;
+ this.needsFetch = false;
this.mounted = false;
this.state = {
loading: true,
@@ -56,7 +57,7 @@ class List extends React.Component {
refreshing: false,
animated: false
};
- this.init();
+ this.query();
this.unsubscribeFocus = props.navigation.addListener('focus', () => {
if (this.mounted) {
this.setState({ animated: true });
@@ -72,72 +73,6 @@ class List extends React.Component {
console.timeEnd(`${ this.constructor.name } mount`);
}
- // eslint-disable-next-line react/sort-comp
- async init() {
- const { rid, tmid } = this.props;
- const db = database.active;
-
- // handle servers with version < 3.0.0
- let { hideSystemMessages = [] } = this.props;
- if (!Array.isArray(hideSystemMessages)) {
- hideSystemMessages = [];
- }
-
- if (tmid) {
- try {
- this.thread = await db.collections
- .get('threads')
- .find(tmid);
- } catch (e) {
- console.log(e);
- }
- this.messagesObservable = db.collections
- .get('thread_messages')
- .query(Q.where('rid', tmid), Q.or(Q.where('t', Q.notIn(hideSystemMessages)), Q.where('t', Q.eq(null))))
- .observe();
- } else if (rid) {
- this.messagesObservable = db.collections
- .get('messages')
- .query(Q.where('rid', rid), Q.or(Q.where('t', Q.notIn(hideSystemMessages)), Q.where('t', Q.eq(null))))
- .observe();
- }
-
- if (rid) {
- this.unsubscribeMessages();
- this.messagesSubscription = this.messagesObservable
- .subscribe((data) => {
- if (tmid && this.thread) {
- data = [this.thread, ...data];
- }
- const messages = orderBy(data, ['ts'], ['desc']);
- if (this.mounted) {
- this.setState({ messages }, () => this.update());
- } else {
- this.state.messages = messages;
- }
- this.readThreads();
- });
- }
- }
-
- // eslint-disable-next-line react/sort-comp
- reload = () => {
- this.unsubscribeMessages();
- this.init();
- }
-
- readThreads = async() => {
- const { tmid } = this.props;
-
- if (tmid) {
- try {
- await RocketChat.readThreads(tmid);
- } catch {
- // Do nothing
- }
- }
- }
-
shouldComponentUpdate(nextProps, nextState) {
const { loading, end, refreshing } = this.state;
const { hideSystemMessages, theme } = this.props;
@@ -177,7 +112,7 @@ class List extends React.Component {
console.countReset(`${ this.constructor.name }.render calls`);
}
- onEndReached = debounce(async() => {
+ fetchData = async() => {
const {
loading, end, messages, latest = messages[messages.length - 1]?.ts
} = this.state;
@@ -196,12 +131,99 @@ class List extends React.Component {
result = await RocketChat.loadMessagesForRoom({ rid, t, latest });
}
- this.setState({ end: result.length < 50, loading: false, latest: result[result.length - 1]?.ts }, () => this.loadMoreMessages(result));
+ this.setState({ end: result.length < QUERY_SIZE, loading: false, latest: result[result.length - 1]?.ts }, () => this.loadMoreMessages(result));
} catch (e) {
this.setState({ loading: false });
log(e);
}
- }, 300)
+ }
+
+ query = async() => {
+ this.count += QUERY_SIZE;
+ const { rid, tmid } = this.props;
+ const db = database.active;
+
+ // handle servers with version < 3.0.0
+ let { hideSystemMessages = [] } = this.props;
+ if (!Array.isArray(hideSystemMessages)) {
+ hideSystemMessages = [];
+ }
+
+ if (tmid) {
+ try {
+ this.thread = await db.collections
+ .get('threads')
+ .find(tmid);
+ } catch (e) {
+ console.log(e);
+ }
+ this.messagesObservable = db.collections
+ .get('thread_messages')
+ .query(
+ Q.where('rid', tmid),
+ Q.experimentalSortBy('ts', Q.desc),
+ Q.experimentalSkip(0),
+ Q.experimentalTake(this.count)
+ )
+ .observe();
+ } else if (rid) {
+ this.messagesObservable = db.collections
+ .get('messages')
+ .query(
+ Q.where('rid', rid),
+ Q.experimentalSortBy('ts', Q.desc),
+ Q.experimentalSkip(0),
+ Q.experimentalTake(this.count)
+ )
+ .observe();
+ }
+
+ if (rid) {
+ this.unsubscribeMessages();
+ this.messagesSubscription = this.messagesObservable
+ .subscribe((messages) => {
+ if (messages.length <= this.count) {
+ this.needsFetch = true;
+ }
+ if (tmid && this.thread) {
+ messages = [...messages, this.thread];
+ }
+ messages = messages.filter(m => !m.t || !hideSystemMessages?.includes(m.t));
+
+ if (this.mounted) {
+ this.setState({ messages }, () => this.update());
+ } else {
+ this.state.messages = messages;
+ }
+ this.readThreads();
+ });
+ }
+ }
+
+ reload = () => {
+ this.count = 0;
+ this.query();
+ }
+
+ readThreads = async() => {
+ const { tmid } = this.props;
+
+ if (tmid) {
+ try {
+ await RocketChat.readThreads(tmid);
+ } catch {
+ // Do nothing
+ }
+ }
+ }
+
+ onEndReached = async() => {
+ if (this.needsFetch) {
+ this.needsFetch = false;
+ await this.fetchData();
+ }
+ this.query();
+ }
loadMoreMessages = (result) => {
const { end } = this.state;
@@ -305,7 +327,7 @@ class List extends React.Component {
removeClippedSubviews={isIOS}
initialNumToRender={7}
onEndReached={this.onEndReached}
- onEndReachedThreshold={5}
+ onEndReachedThreshold={0.5}
maxToRenderPerBatch={5}
windowSize={10}
ListFooterComponent={this.renderFooter}
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 0b6afd18f..4a54117dd 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -206,12 +206,10 @@ class RoomView extends React.Component {
const { appState, insets } = this.props;
if (appState === 'foreground' && appState !== prevProps.appState && this.rid) {
- this.onForegroundInteraction = InteractionManager.runAfterInteractions(() => {
- // Fire List.init() just to keep observables working
- if (this.list && this.list.current) {
- this.list.current.init();
- }
- });
+ // Fire List.query() just to keep observables working
+ if (this.list && this.list.current) {
+ this.list.current?.query?.();
+ }
}
// If it's not direct message
if (this.t !== 'd') {
@@ -267,9 +265,6 @@ class RoomView extends React.Component {
if (this.didMountInteraction && this.didMountInteraction.cancel) {
this.didMountInteraction.cancel();
}
- if (this.onForegroundInteraction && this.onForegroundInteraction.cancel) {
- this.onForegroundInteraction.cancel();
- }
if (this.willBlurListener && this.willBlurListener.remove) {
this.willBlurListener.remove();
}
diff --git a/app/views/RoomsListView/Header/Header.js b/app/views/RoomsListView/Header/Header.js
index 839048bc0..fb5d7eb9e 100644
--- a/app/views/RoomsListView/Header/Header.js
+++ b/app/views/RoomsListView/Header/Header.js
@@ -41,7 +41,7 @@ const Header = React.memo(({
const { isLandscape } = useOrientation();
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
const titleFontSize = 16 * scale;
- const subTitleFontSize = 12 * scale;
+ const subTitleFontSize = 14 * scale;
if (showSearchHeader) {
return (
@@ -78,11 +78,11 @@ const Header = React.memo(({
- {subtitle ? {subtitle} : null}
+ {subtitle ? {subtitle} : null}
);
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 232ea3228..d2bd6cced 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -9,7 +9,7 @@ import {
RefreshControl
} from 'react-native';
import { connect } from 'react-redux';
-import { isEqual, orderBy } from 'lodash';
+import isEqual from 'react-fast-compare';
import Orientation from 'react-native-orientation-locker';
import { Q } from '@nozbe/watermelondb';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
@@ -71,6 +71,7 @@ const DISCUSSIONS_HEADER = 'Discussions';
const CHANNELS_HEADER = 'Channels';
const DM_HEADER = 'Direct_Messages';
const GROUPS_HEADER = 'Private_Groups';
+const QUERY_SIZE = 20;
const filterIsUnread = s => (s.unread > 0 || s.alert) && !s.hideUnreadStatus;
const filterIsFavorite = s => s.f;
@@ -140,11 +141,12 @@ class RoomsListView extends React.Component {
this.gotSubscriptions = false;
this.animated = false;
+ this.count = 0;
this.state = {
searching: false,
search: [],
loading: true,
- allChats: [],
+ chatsOrder: [],
chats: [],
item: {}
};
@@ -211,7 +213,7 @@ class RoomsListView extends React.Component {
}
shouldComponentUpdate(nextProps, nextState) {
- const { allChats, searching, item } = this.state;
+ const { chatsOrder, searching, item } = this.state;
// eslint-disable-next-line react/destructuring-assignment
const propsUpdated = shouldUpdateProps.some(key => nextProps[key] !== this.props[key]);
if (propsUpdated) {
@@ -219,7 +221,7 @@ class RoomsListView extends React.Component {
}
// Compare changes only once
- const chatsNotEqual = !isEqual(nextState.allChats, allChats);
+ const chatsNotEqual = !isEqual(nextState.chatsOrder, chatsOrder);
// If they aren't equal, set to update if focused
if (chatsNotEqual) {
@@ -290,7 +292,7 @@ class RoomsListView extends React.Component {
&& prevProps.showUnread === showUnread
)
) {
- this.getSubscriptions(true);
+ this.getSubscriptions();
} else if (
appState === 'foreground'
&& appState !== prevProps.appState
@@ -309,9 +311,7 @@ class RoomsListView extends React.Component {
}
componentWillUnmount() {
- if (this.querySubscription && this.querySubscription.unsubscribe) {
- this.querySubscription.unsubscribe();
- }
+ this.unsubscribeQuery();
if (this.unsubscribeFocus) {
this.unsubscribeFocus();
}
@@ -396,17 +396,8 @@ class RoomsListView extends React.Component {
return allData;
}
- getSubscriptions = async(force = false) => {
- if (this.gotSubscriptions && !force) {
- return;
- }
- this.gotSubscriptions = true;
-
- if (this.querySubscription && this.querySubscription.unsubscribe) {
- this.querySubscription.unsubscribe();
- }
-
- this.setState({ loading: true });
+ getSubscriptions = async() => {
+ this.unsubscribeQuery();
const {
sortBy,
@@ -416,41 +407,49 @@ class RoomsListView extends React.Component {
} = this.props;
const db = database.active;
- const observable = await db.collections
- .get('subscriptions')
- .query(
- Q.where('archived', false),
- Q.where('open', true)
- )
- .observeWithColumns(['room_updated_at', 'unread', 'alert', 'user_mentions', 'f', 't']);
+ let observable;
+
+ const defaultWhereClause = [
+ Q.where('archived', false),
+ Q.where('open', true)
+ ];
+
+ if (sortBy === 'alphabetical') {
+ defaultWhereClause.push(Q.experimentalSortBy(`${ this.useRealName ? 'fname' : 'name' }`, Q.asc));
+ } else {
+ defaultWhereClause.push(Q.experimentalSortBy('room_updated_at', Q.desc));
+ }
+
+ // When we're grouping by something
+ if (this.isGrouping) {
+ observable = await db.collections
+ .get('subscriptions')
+ .query(...defaultWhereClause)
+ .observe();
+
+ // When we're NOT grouping
+ } else {
+ this.count += QUERY_SIZE;
+ observable = await db.collections
+ .get('subscriptions')
+ .query(
+ ...defaultWhereClause,
+ Q.experimentalSkip(0),
+ Q.experimentalTake(this.count)
+ )
+ .observe();
+ }
+
this.querySubscription = observable.subscribe((data) => {
let tempChats = [];
- let chats = [];
- if (sortBy === 'alphabetical') {
- chats = orderBy(data, [`${ this.useRealName ? 'fname' : 'name' }`], ['asc']);
- } else {
- chats = orderBy(data, ['roomUpdatedAt'], ['desc']);
- }
+ let chats = data;
- // it's better to map and test all subs altogether then testing them individually
- const allChats = data.map(item => ({
- alert: item.alert,
- unread: item.unread,
- userMentions: item.userMentions,
- isRead: this.getIsRead(item),
- favorite: item.f,
- lastMessage: item.lastMessage,
- name: this.getRoomTitle(item),
- _updatedAt: item.roomUpdatedAt,
- key: item._id,
- rid: item.rid,
- type: item.t,
- prid: item.prid,
- uids: item.uids,
- usernames: item.usernames,
- visitor: item.visitor
- }));
+ /**
+ * We trigger re-render only when chats order changes
+ * RoomItem handles its own re-render
+ */
+ const chatsOrder = data.map(item => item.rid);
// unread
if (showUnread) {
@@ -484,12 +483,18 @@ class RoomsListView extends React.Component {
this.internalSetState({
chats: tempChats,
- allChats,
+ chatsOrder,
loading: false
});
});
}
+ unsubscribeQuery = () => {
+ if (this.querySubscription && this.querySubscription.unsubscribe) {
+ this.querySubscription.unsubscribe();
+ }
+ }
+
initSearching = () => {
const { openSearchHeader } = this.props;
this.internalSetState({ searching: true }, () => {
@@ -548,10 +553,19 @@ class RoomsListView extends React.Component {
getRoomAvatar = item => RocketChat.getRoomAvatar(item)
+ isGroupChat = item => RocketChat.isGroupChat(item)
+
+ isRead = item => RocketChat.isRead(item)
+
getUserPresence = uid => RocketChat.getUserPresence(uid)
getUidDirectMessage = room => RocketChat.getUidDirectMessage(room);
+ get isGrouping() {
+ const { showUnread, showFavorites, groupByType } = this.props;
+ return showUnread || showFavorites || groupByType;
+ }
+
onPressItem = (item = {}) => {
const { navigation, isMasterDetail } = this.props;
if (!navigation.isFocused()) {
@@ -743,6 +757,13 @@ class RoomsListView extends React.Component {
roomsRequest({ allData: true });
}
+ onEndReached = () => {
+ // Run only when we're not grouping by anything
+ if (!this.isGrouping) {
+ this.getSubscriptions();
+ }
+ }
+
getScrollRef = ref => (this.scroll = ref);
renderListHeader = () => {
@@ -774,12 +795,6 @@ class RoomsListView extends React.Component {
);
}
- getIsRead = (item) => {
- let isUnread = item.archived !== true && item.open === true; // item is not archived and not opened
- isUnread = isUnread && (item.unread > 0 || item.alert === true); // either its unread count > 0 or its alert
- return !isUnread;
- };
-
renderItem = ({ item }) => {
if (item.separator) {
return this.renderSectionHeader(item.rid);
@@ -800,32 +815,19 @@ class RoomsListView extends React.Component {
width
} = this.props;
const id = this.getUidDirectMessage(item);
- const isGroupChat = RocketChat.isGroupChat(item);
return (
this.onPressItem(item)}
+ onPress={this.onPressItem}
testID={`rooms-list-view-item-${ item.name }`}
width={isMasterDetail ? MAX_SIDEBAR_WIDTH : width}
toggleFav={this.toggleFav}
@@ -833,7 +835,10 @@ class RoomsListView extends React.Component {
hideChannel={this.hideChannel}
useRealName={useRealName}
getUserPresence={this.getUserPresence}
- isGroupChat={isGroupChat}
+ getRoomTitle={this.getRoomTitle}
+ getRoomAvatar={this.getRoomAvatar}
+ getIsGroupChat={this.isGroupChat}
+ getIsRead={this.isRead}
visitor={item.visitor}
isFocused={currentItem?.rid === item.rid}
/>
@@ -880,6 +885,8 @@ class RoomsListView extends React.Component {
/>
)}
windowSize={9}
+ onEndReached={this.onEndReached}
+ onEndReachedThreshold={0.5}
/>
);
};
diff --git a/app/views/SettingsView/index.js b/app/views/SettingsView/index.js
index 335b1c7f5..17bddd4f8 100644
--- a/app/views/SettingsView/index.js
+++ b/app/views/SettingsView/index.js
@@ -38,7 +38,7 @@ import { appStart as appStartAction, ROOT_LOADING } from '../../actions/app';
import { onReviewPress } from '../../utils/review';
import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView';
-import { isGooglePlayBuild } from '../../constants/environment';
+import { isFDroidBuild } from '../../constants/environment';
const SectionSeparator = React.memo(({ theme }) => (
@@ -121,15 +121,14 @@ class SettingsView extends React.Component {
AsyncStorage.setItem(CRASH_REPORT_KEY, JSON.stringify(value));
const { toggleCrashReport } = this.props;
toggleCrashReport(value);
- loggerConfig.autoNotify = value;
- if (isGooglePlayBuild) {
+ if (!isFDroidBuild) {
+ loggerConfig.autoNotify = value;
analytics().setAnalyticsCollectionEnabled(value);
- }
-
- if (value) {
- loggerConfig.clearBeforeSendCallbacks();
- } else {
- loggerConfig.registerBeforeSendCallback(() => false);
+ if (value) {
+ loggerConfig.clearBeforeSendCallbacks();
+ } else {
+ loggerConfig.registerBeforeSendCallback(() => false);
+ }
}
}
@@ -162,7 +161,7 @@ class SettingsView extends React.Component {
shareApp = () => {
// eslint-disable-next-line no-nested-ternary
- Share.share({ message: isAndroid ? (isGooglePlayBuild ? PLAY_MARKET_LINK : FDROID_MARKET_LINK) : APP_STORE_LINK });
+ Share.share({ message: isAndroid ? (!isFDroidBuild ? PLAY_MARKET_LINK : FDROID_MARKET_LINK) : APP_STORE_LINK });
}
copyServerVersion = () => {
@@ -258,7 +257,7 @@ class SettingsView extends React.Component {
theme={theme}
/>
- {isGooglePlayBuild ? (
+ {!isFDroidBuild ? (
<>
) : null}
- {isGooglePlayBuild ? (
+ {!isFDroidBuild ? (
<>
({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
const keyExtractor = item => item.rid;
@@ -47,7 +46,7 @@ class ShareListView extends React.Component {
constructor(props) {
super(props);
- this.data = [];
+ this.chats = [];
this.state = {
searching: false,
searchText: '',
@@ -186,22 +185,36 @@ class ShareListView extends React.Component {
this.setState(...args);
}
- getSubscriptions = async(server) => {
+ query = (text) => {
const db = database.active;
+ const defaultWhereClause = [
+ Q.where('archived', false),
+ Q.where('open', true),
+ Q.experimentalSkip(0),
+ Q.experimentalTake(50),
+ Q.experimentalSortBy('room_updated_at', Q.desc)
+ ];
+ if (text) {
+ return db.collections
+ .get('subscriptions')
+ .query(
+ ...defaultWhereClause,
+ Q.or(
+ Q.where('name', Q.like(`%${ Q.sanitizeLikeString(text) }%`)),
+ Q.where('fname', Q.like(`%${ Q.sanitizeLikeString(text) }%`))
+ )
+ ).fetch();
+ }
+ return db.collections.get('subscriptions').query(...defaultWhereClause).fetch();
+ }
+
+ getSubscriptions = async(server) => {
const serversDB = database.servers;
if (server) {
- this.data = await db.collections
- .get('subscriptions')
- .query(
- Q.where('archived', false),
- Q.where('open', true)
- ).fetch();
- this.data = orderBy(this.data, ['roomUpdatedAt'], ['desc']);
-
+ this.chats = await this.query();
const serversCollection = serversDB.collections.get('servers');
this.servers = await serversCollection.query().fetch();
- this.chats = this.data.slice(0, LIMIT);
let serverInfo = {};
try {
serverInfo = await serversCollection.find(server);
@@ -210,8 +223,8 @@ class ShareListView extends React.Component {
}
this.internalSetState({
- chats: this.chats ? this.chats.slice() : [],
- servers: this.servers ? this.servers.slice() : [],
+ chats: this.chats ?? [],
+ servers: this.servers ?? [],
loading: false,
serverInfo
});
@@ -253,10 +266,10 @@ class ShareListView extends React.Component {
});
}
- search = (text) => {
- const result = this.data.filter(item => item.name.includes(text)) || [];
+ search = async(text) => {
+ const result = await this.query(text);
this.internalSetState({
- searchResults: result.slice(0, LIMIT),
+ searchResults: result,
searchText: text
});
}
@@ -297,9 +310,26 @@ class ShareListView extends React.Component {
}
renderItem = ({ item }) => {
+ const { serverInfo } = this.state;
+ const { useRealName } = serverInfo;
const {
userId, token, server, theme
} = this.props;
+ let description;
+ switch (item.t) {
+ case 'c':
+ description = item.topic || item.description;
+ break;
+ case 'p':
+ description = item.topic || item.description;
+ break;
+ case 'd':
+ description = useRealName ? item.name : item.fname;
+ break;
+ default:
+ description = item.fname;
+ break;
+ }
return (
this.shareMessage(item)}
testID={`share-extension-item-${ item.name }`}
diff --git a/app/views/StatusView.js b/app/views/StatusView.js
index 2c91fa294..5676e0bb0 100644
--- a/app/views/StatusView.js
+++ b/app/views/StatusView.js
@@ -24,16 +24,16 @@ import SafeAreaView from '../containers/SafeAreaView';
const STATUS = [{
id: 'online',
- name: I18n.t('Online')
+ name: 'Online'
}, {
id: 'busy',
- name: I18n.t('Busy')
+ name: 'Busy'
}, {
id: 'away',
- name: I18n.t('Away')
+ name: 'Away'
}, {
id: 'offline',
- name: I18n.t('Invisible')
+ name: 'Invisible'
}];
const styles = StyleSheet.create({
@@ -164,7 +164,7 @@ class StatusView extends React.Component {
const { id, name } = item;
return (
{
if (user.status !== item.id) {
try {
diff --git a/app/views/ThemeView.js b/app/views/ThemeView.js
index d8cd96902..f6c4f96cb 100644
--- a/app/views/ThemeView.js
+++ b/app/views/ThemeView.js
@@ -21,28 +21,28 @@ const THEME_GROUP = 'THEME_GROUP';
const DARK_GROUP = 'DARK_GROUP';
const SYSTEM_THEME = {
- label: I18n.t('Automatic'),
+ label: 'Automatic',
value: 'automatic',
group: THEME_GROUP
};
const THEMES = [
{
- label: I18n.t('Light'),
+ label: 'Light',
value: 'light',
group: THEME_GROUP
}, {
- label: I18n.t('Dark'),
+ label: 'Dark',
value: 'dark',
group: THEME_GROUP
}, {
- label: I18n.t('Dark'),
+ label: 'Dark',
value: 'dark',
separator: true,
- header: I18n.t('Dark_level'),
+ header: 'Dark_level',
group: DARK_GROUP
}, {
- label: I18n.t('Black'),
+ label: 'Black',
value: 'black',
group: DARK_GROUP
}
@@ -129,7 +129,7 @@ class ThemeView extends React.Component {
<>
{item.separator || isFirst ? this.renderSectionHeader(item.header) : null}
this.onClick(item)}
testID={`theme-view-${ value }`}
right={this.isSelected(item) ? this.renderIcon : null}
@@ -139,12 +139,12 @@ class ThemeView extends React.Component {
);
}
- renderSectionHeader = (header = I18n.t('Theme')) => {
+ renderSectionHeader = (header = 'Theme') => {
const { theme } = this.props;
return (
<>
- {header}
+ {I18n.t(header)}
{this.renderSeparator()}
>
@@ -169,7 +169,7 @@ class ThemeView extends React.Component {
item.value}
+ keyExtractor={item => item.value + item.group}
contentContainerStyle={[
styles.list,
{ borderColor: themes[theme].separatorColor }
diff --git a/e2e/data.js b/e2e/data.js
index 4c4c8ca16..d3f4a0df9 100644
--- a/e2e/data.js
+++ b/e2e/data.js
@@ -9,34 +9,39 @@ const data = {
regular: {
username: `userone${ value }`,
password: '123',
- email: `diego.mello+regular${ value }@rocket.chat`
+ email: `mobile+regular${ value }@rocket.chat`
},
alternate: {
username: `usertwo${ value }`,
password: '123',
- email: `diego.mello+alternate${ value }@rocket.chat`,
+ email: `mobile+alternate${ value }@rocket.chat`,
totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ'
},
profileChanges: {
username: `userthree${ value }`,
password: '123',
- email: `diego.mello+profileChanges${ value }@rocket.chat`
+ email: `mobile+profileChanges${ value }@rocket.chat`
},
existing: {
username: `existinguser${ value }`,
password: '123',
- email: `diego.mello+existing${ value }@rocket.chat`
+ email: `mobile+existing${ value }@rocket.chat`
}
},
channels: {
- public: {
+ detoxpublic: {
name: 'detox-public'
}
},
+ groups: {
+ private: {
+ name: `detox-private-${ value }`
+ }
+ },
registeringUser: {
username: `newuser${ value }`,
password: `password${ value }`,
- email: `diego.mello+registering${ value }@rocket.chat`
+ email: `mobile+registering${ value }@rocket.chat`
},
random: value
}
diff --git a/e2e/data/data.cloud.js b/e2e/data/data.cloud.js
index 4c4c8ca16..d3f4a0df9 100644
--- a/e2e/data/data.cloud.js
+++ b/e2e/data/data.cloud.js
@@ -9,34 +9,39 @@ const data = {
regular: {
username: `userone${ value }`,
password: '123',
- email: `diego.mello+regular${ value }@rocket.chat`
+ email: `mobile+regular${ value }@rocket.chat`
},
alternate: {
username: `usertwo${ value }`,
password: '123',
- email: `diego.mello+alternate${ value }@rocket.chat`,
+ email: `mobile+alternate${ value }@rocket.chat`,
totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ'
},
profileChanges: {
username: `userthree${ value }`,
password: '123',
- email: `diego.mello+profileChanges${ value }@rocket.chat`
+ email: `mobile+profileChanges${ value }@rocket.chat`
},
existing: {
username: `existinguser${ value }`,
password: '123',
- email: `diego.mello+existing${ value }@rocket.chat`
+ email: `mobile+existing${ value }@rocket.chat`
}
},
channels: {
- public: {
+ detoxpublic: {
name: 'detox-public'
}
},
+ groups: {
+ private: {
+ name: `detox-private-${ value }`
+ }
+ },
registeringUser: {
username: `newuser${ value }`,
password: `password${ value }`,
- email: `diego.mello+registering${ value }@rocket.chat`
+ email: `mobile+registering${ value }@rocket.chat`
},
random: value
}
diff --git a/e2e/data/data.docker.js b/e2e/data/data.docker.js
index 5a7ed505c..a22423a63 100644
--- a/e2e/data/data.docker.js
+++ b/e2e/data/data.docker.js
@@ -9,34 +9,39 @@ const data = {
regular: {
username: `userone${ value }`,
password: '123',
- email: `diego.mello+regular${ value }@rocket.chat`
+ email: `mobile+regular${ value }@rocket.chat`
},
alternate: {
username: `usertwo${ value }`,
password: '123',
- email: `diego.mello+alternate${ value }@rocket.chat`,
+ email: `mobile+alternate${ value }@rocket.chat`,
totpSecret: 'NA4GOMZGHBQSK6KEFRVT62DMGJJGSYZJFZIHO3ZOGVXWCYZ6MMZQ'
},
profileChanges: {
username: `userthree${ value }`,
password: '123',
- email: `diego.mello+profileChanges${ value }@rocket.chat`
+ email: `mobile+profileChanges${ value }@rocket.chat`
},
existing: {
username: `existinguser${ value }`,
password: '123',
- email: `diego.mello+existing${ value }@rocket.chat`
+ email: `mobile+existing${ value }@rocket.chat`
}
},
channels: {
- public: {
+ detoxpublic: {
name: 'detox-public'
}
},
+ groups: {
+ private: {
+ name: `detox-private-${ value }`
+ }
+ },
registeringUser: {
username: `newuser${ value }`,
password: `password${ value }`,
- email: `diego.mello+registering${ value }@rocket.chat`
+ email: `mobile+registering${ value }@rocket.chat`
},
random: value
}
diff --git a/e2e/docker/controlRCDemoEnv.sh b/e2e/docker/controlRCDemoEnv.sh
index 16dc49a7a..527d837e3 100755
--- a/e2e/docker/controlRCDemoEnv.sh
+++ b/e2e/docker/controlRCDemoEnv.sh
@@ -42,7 +42,7 @@ if [ "$COMMAND" == "start" ]; then
MAX_ATTEMPTS=60
while [ $ATTEMPT_NUMBER -lt $MAX_ATTEMPTS ]; do # https://stackoverflow.com/a/21189312/399007
ATTEMPT_NUMBER=$((ATTEMPT_NUMBER + 1 ))
- echo "Waiting for server to be up ($ATTEMPT_NUMBER of $MAX_ATTEMPTS)"
+ echo "Checking if servers are ready (attempt $ATTEMPT_NUMBER of $MAX_ATTEMPTS)"
LOGS=$(docker logs rc_test_env_rocketchat_1 2> /dev/null)
if grep -q 'SERVER RUNNING' <<< $LOGS ; then
echo "RocketChat is ready!"
diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js
index 88ef3d5ad..92feb61d3 100644
--- a/e2e/helpers/app.js
+++ b/e2e/helpers/app.js
@@ -31,7 +31,6 @@ async function login(username, password) {
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(2000);
await element(by.id('login-view-email')).replaceText(username);
await element(by.id('login-view-password')).replaceText(password);
- await sleep(300);
await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
}
@@ -61,6 +60,33 @@ async function mockMessage(message) {
await element(by.label(`${ data.random }${ message }`)).atIndex(0).tap();
};
+async function starMessage(message){
+ const messageLabel = `${ data.random }${ message }`
+ await waitFor(element(by.label(messageLabel))).toBeVisible().withTimeout(5000);
+ await element(by.label(messageLabel)).atIndex(0).longPress();
+ await expect(element(by.id('action-sheet'))).toExist();
+ await expect(element(by.id('action-sheet-handle'))).toBeVisible();
+ await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
+ await element(by.label('Star')).tap();
+ await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000);
+};
+
+async function pinMessage(message){
+ const messageLabel = `${ data.random }${ message }`
+ await waitFor(element(by.label(messageLabel)).atIndex(0)).toExist();
+ await element(by.label(messageLabel)).atIndex(0).longPress();
+ await expect(element(by.id('action-sheet'))).toExist();
+ await expect(element(by.id('action-sheet-handle'))).toBeVisible();
+ await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
+ await element(by.label('Pin')).tap();
+ await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000);
+}
+
+async function dismissReviewNag(){
+ await waitFor(element(by.text('Are you enjoying this app?'))).toExist().withTimeout(60000);
+ await element(by.label('No').and(by.type('_UIAlertControllerActionView'))).tap(); // Tap `no` on ask for review alert
+}
+
async function tapBack() {
await element(by.id('header-back')).atIndex(0).tap();
}
@@ -74,7 +100,22 @@ async function searchRoom(room) {
await expect(element(by.id('rooms-list-view-search-input'))).toExist();
await waitFor(element(by.id('rooms-list-view-search-input'))).toExist().withTimeout(5000);
await element(by.id('rooms-list-view-search-input')).typeText(room);
- await sleep(2000);
+}
+
+async function tryTapping(theElement, timeout, longtap = false){
+ try {
+ if(longtap){
+ await theElement.longPress()
+ } else {
+ await theElement.tap()
+ }
+ } catch(e) {
+ if(timeout <= 0){ //TODO: Maths. How closely has the timeout been honoured here?
+ throw e
+ }
+ await sleep(100)
+ await tryTapping(theElement, timeout - 100)
+ }
}
module.exports = {
@@ -84,7 +125,11 @@ module.exports = {
login,
logout,
mockMessage,
+ starMessage,
+ pinMessage,
+ dismissReviewNag,
tapBack,
sleep,
- searchRoom
+ searchRoom,
+ tryTapping
};
\ No newline at end of file
diff --git a/e2e/helpers/data_setup.js b/e2e/helpers/data_setup.js
index 84e5927ea..d16b41a55 100644
--- a/e2e/helpers/data_setup.js
+++ b/e2e/helpers/data_setup.js
@@ -38,7 +38,7 @@ const createUser = async (username, password, name, email) => {
}
const createChannelIfNotExists = async (channelname) => {
- console.log(`Creating channel ${channelname}`)
+ console.log(`Creating public channel ${channelname}`)
try {
await rocketchat.post('channels.create', {
"name": channelname
@@ -49,7 +49,24 @@ const createChannelIfNotExists = async (channelname) => {
} catch (infoError) {
console.log(JSON.stringify(createError))
console.log(JSON.stringify(infoError))
- throw "Failed to find or create channel"
+ throw "Failed to find or create public channel"
+ }
+ }
+}
+
+const createGroupIfNotExists = async (groupname) => {
+ console.log(`Creating private group ${groupname}`)
+ try {
+ await rocketchat.post('groups.create', {
+ "name": groupname
+ })
+ } catch (createError) {
+ try { //Maybe it exists already?
+ await rocketchat.get(`group.info?roomName=${groupname}`)
+ } catch (infoError) {
+ console.log(JSON.stringify(createError))
+ console.log(JSON.stringify(infoError))
+ throw "Failed to find or create private group"
}
}
}
@@ -71,6 +88,15 @@ const setup = async () => {
}
}
+ await login(data.users.regular.username, data.users.regular.password)
+
+ for (var groupKey in data.groups) {
+ if (data.groups.hasOwnProperty(groupKey)) {
+ const group = data.groups[groupKey]
+ await createGroupIfNotExists(group.name)
+ }
+ }
+
return
}
diff --git a/e2e/tests/assorted/01-changeserver.spec.js b/e2e/tests/assorted/01-changeserver.spec.js
index 0664970a7..7210d7060 100644
--- a/e2e/tests/assorted/01-changeserver.spec.js
+++ b/e2e/tests/assorted/01-changeserver.spec.js
@@ -9,12 +9,12 @@ const checkServer = async(server) => {
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.label(label))).toBeVisible().withTimeout(60000);
- await expect(element(by.label(label))).toBeVisible();
await element(by.id('sidebar-close-drawer')).tap();
}
describe('Change server', () => {
before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
@@ -28,8 +28,6 @@ describe('Change server', () => {
await sleep(5000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000);
- await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist();
- await sleep(1000);
await element(by.id('rooms-list-header-server-add')).tap();
// TODO: refactor
@@ -37,19 +35,16 @@ describe('Change server', () => {
await element(by.id('new-server-view-input')).replaceText(data.alternateServer);
await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('workspace-view'))).toBeVisible();
await element(by.id('workspace-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('register-view'))).toBeVisible();
+
// Register new user
await element(by.id('register-view-name')).replaceText(data.registeringUser.username);
await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
- await sleep(1000);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('rooms-list-view'))).toBeVisible();
// For a sanity test, to make sure roomslist is showing correct rooms
// app CANNOT show public room created on previous tests
@@ -59,11 +54,8 @@ describe('Change server', () => {
});
it('should change back', async() => {
- await sleep(5000);
await element(by.id('rooms-list-header-server-dropdown-button')).tap();
await waitFor(element(by.id('rooms-list-header-server-dropdown'))).toBeVisible().withTimeout(5000);
- await expect(element(by.id('rooms-list-header-server-dropdown'))).toExist();
- await sleep(1000);
await element(by.id(`rooms-list-header-server-${ data.server }`)).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await checkServer(data.server);
diff --git a/e2e/tests/assorted/02-broadcast.spec.js b/e2e/tests/assorted/02-broadcast.spec.js
index 1b2fcc1db..9e87c5e14 100644
--- a/e2e/tests/assorted/02-broadcast.spec.js
+++ b/e2e/tests/assorted/02-broadcast.spec.js
@@ -23,28 +23,16 @@ describe('Broadcast room', () => {
await waitFor(element(by.id('select-users-view'))).toBeVisible().withTimeout(2000);
await element(by.id('select-users-view-search')).replaceText(otheruser.username);
await waitFor(element(by.id(`select-users-view-item-${ otheruser.username }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.id(`select-users-view-item-${ otheruser.username }`))).toBeVisible();
await element(by.id(`select-users-view-item-${ otheruser.username }`)).tap();
await waitFor(element(by.id(`selected-user-${ otheruser.username }`))).toBeVisible().withTimeout(5000);
- await sleep(1000);
await element(by.id('selected-users-view-submit')).tap();
- await sleep(1000);
await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000);
await element(by.id('create-channel-name')).replaceText(`broadcast${ data.random }`);
- await sleep(2000);
- await element(by.id('create-channel-broadcast')).tap();
- if (device.getPlatform() === 'ios') { //Because this tap is FLAKY on iOS
- await expect(element(by.id('create-channel-broadcast'))).toHaveValue('1')
- }
- await sleep(500);
+ await element(by.id('create-channel-broadcast')).longPress(); //https://github.com/facebook/react-native/issues/28032
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('room-view'))).toBeVisible();
await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible();
- await sleep(1000);
await element(by.id('room-view-header-actions')).tap();
- await sleep(1000);
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);
await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view'))).toBeVisible().withTimeout(2000);
@@ -64,25 +52,19 @@ describe('Broadcast room', () => {
it('should login as user without write message authorization and enter room', async() => {
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
- await element(by.id('login-view-email')).replaceText(otheruser.username);
- await element(by.id('login-view-password')).replaceText(otheruser.password);
- await sleep(1000);
- await element(by.id('login-view-submit')).tap();
+ await login(otheruser.username, otheruser.password);
+
//await waitFor(element(by.id('two-factor'))).toBeVisible().withTimeout(5000);
//await expect(element(by.id('two-factor'))).toBeVisible();
//const code = GA.gen(data.alternateUserTOTPSecret);
//await element(by.id('two-factor-input')).replaceText(code);
- //await sleep(1000);
//await element(by.id('two-factor-send')).tap();
- await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
+
await searchRoom(`broadcast${ data.random }`);
await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000);
- await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist();
await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
- await expect(element(by.id(`room-view-title-broadcast${ data.random }`))).toBeVisible();
- await sleep(1000);
});
it('should not have messagebox', async() => {
@@ -95,7 +77,6 @@ describe('Broadcast room', () => {
it('should have the message created earlier', async() => {
await waitFor(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible().withTimeout(60000);
- await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toBeVisible();
});
it('should have reply button', async() => {
@@ -104,9 +85,7 @@ describe('Broadcast room', () => {
it('should tap on reply button and navigate to direct room', async() => {
await element(by.id('message-broadcast-reply')).tap();
- await sleep(1000);
await waitFor(element(by.id(`room-view-title-${ testuser.username }`))).toBeVisible().withTimeout(5000);
- await expect(element(by.id(`room-view-title-${ testuser.username }`))).toBeVisible();
});
it('should reply broadcasted message', async() => {
diff --git a/e2e/tests/assorted/03-profile.spec.js b/e2e/tests/assorted/03-profile.spec.js
index e5c321757..9700da1b2 100644
--- a/e2e/tests/assorted/03-profile.spec.js
+++ b/e2e/tests/assorted/03-profile.spec.js
@@ -13,7 +13,7 @@ async function waitForToast() {
// await expect(element(by.id('toast'))).toBeVisible();
// await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeNotVisible();
- await sleep(5000);
+ await sleep(1);
}
describe('Profile screen', () => {
@@ -24,7 +24,6 @@ describe('Profile screen', () => {
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('sidebar-profile'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('sidebar-profile'))).toBeVisible();
await element(by.id('sidebar-profile')).tap();
await waitFor(element(by.id('profile-view'))).toBeVisible().withTimeout(2000);
});
@@ -60,22 +59,18 @@ describe('Profile screen', () => {
it('should have reset avatar button', async() => {
await waitFor(element(by.id('profile-view-reset-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-reset-avatar'))).toExist();
});
it('should have upload avatar button', async() => {
await waitFor(element(by.id('profile-view-upload-avatar'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-upload-avatar'))).toExist();
});
it('should have avatar url button', async() => {
await waitFor(element(by.id('profile-view-avatar-url-button'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-avatar-url-button'))).toExist();
});
it('should have submit button', async() => {
await waitFor(element(by.id('profile-view-submit'))).toExist().whileElement(by.id('profile-view-list')).scroll(scrollDown, 'down');
- await expect(element(by.id('profile-view-submit'))).toExist();
});
});
@@ -84,15 +79,13 @@ describe('Profile screen', () => {
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
await element(by.id('profile-view-name')).replaceText(`${ profileChangeUser.username }new`);
await element(by.id('profile-view-username')).replaceText(`${ profileChangeUser.username }new`);
- await sleep(1000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
- await sleep(1000);
await element(by.id('profile-view-submit')).tap();
await waitForToast();
});
it('should change email and password', async() => {
- await element(by.id('profile-view-email')).replaceText(`diego.mello+profileChangesNew${ data.random }@rocket.chat`);
+ await element(by.id('profile-view-email')).replaceText(`mobile+profileChangesNew${ data.random }@rocket.chat`);
await element(by.id('profile-view-new-password')).replaceText(`${ profileChangeUser.password }new`);
await element(by.id('profile-view-submit')).tap();
await element(by.type('_UIAlertControllerTextField')).replaceText(`${ profileChangeUser.password }`)
@@ -103,7 +96,6 @@ describe('Profile screen', () => {
it('should reset avatar', async() => {
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
- await sleep(1000);
await element(by.id('profile-view-reset-avatar')).tap();
await waitForToast();
});
diff --git a/e2e/tests/assorted/04-setting.spec.js b/e2e/tests/assorted/04-setting.spec.js
index c2cfae6f1..951ccc70d 100644
--- a/e2e/tests/assorted/04-setting.spec.js
+++ b/e2e/tests/assorted/04-setting.spec.js
@@ -1,12 +1,18 @@
const {
device, expect, element, by, waitFor
} = require('detox');
+const { navigateToLogin, login } = require('../../helpers/app');
+
+const data = require('../../data');
+
+const testuser = data.users.regular
describe('Settings screen', () => {
before(async() => {
- await device.launchApp({ newInstance: true });
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToLogin();
+ await login(testuser.username, testuser.password);
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
- await expect(element(by.id('rooms-list-view'))).toBeVisible();
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('sidebar-settings'))).toBeVisible().withTimeout(2000);
diff --git a/e2e/tests/assorted/05-joinpublicroom.spec.js b/e2e/tests/assorted/05-joinpublicroom.spec.js
index d68d1a955..1c946f0c2 100644
--- a/e2e/tests/assorted/05-joinpublicroom.spec.js
+++ b/e2e/tests/assorted/05-joinpublicroom.spec.js
@@ -2,12 +2,12 @@ const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
-const { mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app');
+const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app');
-const room = 'detox-public';
+const testuser = data.users.regular
+const room = data.channels.detoxpublic.name;
async function navigateToRoom() {
- await sleep(2000);
await searchRoom(room);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
@@ -15,15 +15,15 @@ async function navigateToRoom() {
}
async function navigateToRoomActions() {
- await sleep(2000);
await element(by.id('room-view-header-actions')).tap();
- await sleep(2000);
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);
}
describe('Join public room', () => {
before(async() => {
- await device.launchApp({ newInstance: true });
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToLogin();
+ await login(testuser.username, testuser.password);
await navigateToRoom();
});
@@ -167,9 +167,7 @@ describe('Join public room', () => {
await element(by.text('Yes, leave it!')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
// await element(by.id('rooms-list-view-search')).typeText('');
- await sleep(2000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000);
- await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible();
});
});
});
diff --git a/e2e/tests/assorted/06-status.spec.js b/e2e/tests/assorted/06-status.spec.js
index ee592d41f..887dcb1e0 100644
--- a/e2e/tests/assorted/06-status.spec.js
+++ b/e2e/tests/assorted/06-status.spec.js
@@ -1,14 +1,21 @@
const {
expect, element, by, waitFor
} = require('detox');
-const { sleep } = require('../../helpers/app');
+const { navigateToLogin, login, sleep } = require('../../helpers/app');
+
+const data = require('../../data');
+const testuser = data.users.regular
async function waitForToast() {
- await sleep(5000);
+ await sleep(1);
}
describe('Status screen', () => {
- before(async() => {
+ before(async () => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToLogin();
+ await login(testuser.username, testuser.password);
+
await element(by.id('rooms-list-view-sidebar')).tap();
await waitFor(element(by.id('sidebar-view'))).toBeVisible().withTimeout(2000);
await waitFor(element(by.id('sidebar-custom-status'))).toBeVisible().withTimeout(2000);
@@ -17,30 +24,27 @@ describe('Status screen', () => {
await waitFor(element(by.id('status-view'))).toBeVisible().withTimeout(2000);
});
- describe('Render', async() => {
- it('should have status input', async() => {
+ describe('Render', async () => {
+ it('should have status input', async () => {
await expect(element(by.id('status-view-input'))).toBeVisible();
await expect(element(by.id('status-view-online'))).toExist();
await expect(element(by.id('status-view-busy'))).toExist();
await expect(element(by.id('status-view-away'))).toExist();
await expect(element(by.id('status-view-offline'))).toExist();
- });
- });
-
- describe('Usage', async() => {
- it('should change status', async() => {
- await sleep(1000);
- await element(by.id('status-view-busy')).tap();
- await sleep(1000);
- await expect(element(by.id('status-view-current-busy'))).toExist();
- });
+ });
+ });
- it('should change status text', async() => {
+ describe('Usage', async () => {
+ it('should change status', async () => {
+ await element(by.id('status-view-busy')).tap();
+ await expect(element(by.id('status-view-current-busy'))).toExist();
+ });
+
+ it('should change status text', async () => {
await element(by.id('status-view-input')).replaceText('status-text-new');
- await sleep(1000);
await element(by.id('status-view-submit')).tap();
await waitForToast();
await waitFor(element(by.label('status-text-new').withAncestor(by.id('sidebar-custom-status')))).toBeVisible().withTimeout(2000);
- });
- });
-});
+ });
+ });
+});
\ No newline at end of file
diff --git a/e2e/tests/init.js b/e2e/tests/init.js
index bef12f6d3..9eaa721b6 100644
--- a/e2e/tests/init.js
+++ b/e2e/tests/init.js
@@ -1,11 +1,21 @@
const detox = require('detox');
const config = require('../../package.json').detox;
const dataSetup = require('../helpers/data_setup')
+const adapter = require('detox/runners/mocha/adapter');
before(async() => {
- await dataSetup()
- await detox.init(config, { launchApp: false });
- await device.launchApp({ permissions: { notifications: 'YES' } });
+ await Promise.all([dataSetup(), detox.init(config, { launchApp: false })])
+ //await dataSetup()
+ //await detox.init(config, { launchApp: false });
+ //await device.launchApp({ permissions: { notifications: 'YES' } });
+});
+
+beforeEach(async function() {
+ await adapter.beforeEach(this);
+});
+
+afterEach(async function() {
+ await adapter.afterEach(this);
});
after(async() => {
diff --git a/e2e/tests/onboarding/01-onboarding.spec.js b/e2e/tests/onboarding/01-onboarding.spec.js
index c84928d60..9c049099e 100644
--- a/e2e/tests/onboarding/01-onboarding.spec.js
+++ b/e2e/tests/onboarding/01-onboarding.spec.js
@@ -31,7 +31,6 @@ describe('Onboarding', () => {
it('should navigate to join a workspace', async() => {
await element(by.id('join-workspace')).tap();
await waitFor(element(by.id('new-server-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('new-server-view'))).toBeVisible();
});
it('should enter an invalid server and get error', async() => {
@@ -39,14 +38,12 @@ describe('Onboarding', () => {
await element(by.id('new-server-view-button')).tap();
const errorText = 'Oops!';
await waitFor(element(by.text(errorText))).toBeVisible().withTimeout(60000);
- await expect(element(by.text(errorText))).toBeVisible();
await element(by.text('OK')).tap();
});
it('should tap on "Join our open workspace" and navigate', async() => {
await element(by.id('new-server-view-open')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('workspace-view'))).toBeVisible();
});
it('should enter a valid server without login services and navigate to login', async() => {
@@ -57,7 +54,6 @@ describe('Onboarding', () => {
await element(by.id('new-server-view-input')).replaceText(data.server);
await element(by.id('new-server-view-button')).tap();
await waitFor(element(by.id('workspace-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('workspace-view'))).toBeVisible();
});
});
});
diff --git a/e2e/tests/onboarding/02-legal.spec.js b/e2e/tests/onboarding/02-legal.spec.js
index e8109ece5..191e0647c 100644
--- a/e2e/tests/onboarding/02-legal.spec.js
+++ b/e2e/tests/onboarding/02-legal.spec.js
@@ -4,54 +4,62 @@ const {
const { navigateToRegister, navigateToLogin } = require('../../helpers/app');
describe('Legal screen', () => {
- it('should have legal button on login', async() => {
- await device.launchApp({ newInstance: true });
- await navigateToLogin();
- await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('login-view-more'))).toBeVisible();
- });
- it('should navigate to legal from login', async() => {
- await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000);
- await element(by.id('login-view-more')).tap();
- });
-
- it('should have legal button on register', async() => {
- await device.launchApp({ newInstance: true });
- await navigateToRegister();
- await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('register-view-more'))).toBeVisible();
- });
-
- it('should navigate to legal from register', async() => {
- await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000);
- await element(by.id('register-view-more')).tap();
- });
-
- it('should have legal screen', async() => {
- await expect(element(by.id('legal-view'))).toBeVisible();
- });
-
- it('should have terms of service button', async() => {
- await expect(element(by.id('legal-terms-button'))).toBeVisible();
- });
-
- it('should have privacy policy button', async() => {
- await expect(element(by.id('legal-privacy-button'))).toBeVisible();
- });
+ describe('From Login', () => {
+ before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToLogin();
+ });
-
- // We can't simulate how webview behaves, so I had to disable :(
- // it('should navigate to terms', async() => {
- // await element(by.id('legal-terms-button')).tap();
- // await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000);
- // await expect(element(by.id('terms-view'))).toBeVisible();
- // });
+ it('should have legal button on login', async() => {
+ await waitFor(element(by.id('login-view-more'))).toBeVisible().withTimeout(60000);
+ });
- // it('should navigate to privacy', async() => {
- // await tapBack();
- // await element(by.id('legal-privacy-button')).tap();
- // await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000);
- // await expect(element(by.id('privacy-view'))).toBeVisible();
- // });
+ it('should navigate to legal from login', async() => {
+ await expect(element(by.id('login-view-more'))).toBeVisible();
+ await element(by.id('login-view-more')).tap();
+ await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000)
+ });
+ });
+
+ describe('From Register', () => {
+ before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToRegister();
+ });
+
+ it('should have legal button on register', async() => {
+ await waitFor(element(by.id('register-view-more'))).toBeVisible().withTimeout(60000);
+ });
+
+ it('should navigate to legal from register', async() => {
+ await expect(element(by.id('register-view-more'))).toBeVisible();
+ await element(by.id('register-view-more')).tap();
+ await waitFor(element(by.id('legal-view'))).toBeVisible().withTimeout(4000);
+ });
+
+ it('should have terms of service button', async() => {
+ await expect(element(by.id('legal-terms-button'))).toBeVisible();
+ });
+
+ it('should have privacy policy button', async() => {
+ await expect(element(by.id('legal-privacy-button'))).toBeVisible();
+ });
+
+ // We can't simulate how webview behaves, so I had to disable :(
+ /*
+ it('should navigate to terms', async() => {
+ await element(by.id('legal-terms-button')).tap();
+ await waitFor(element(by.id('terms-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('terms-view'))).toBeVisible();
+ });
+
+ it('should navigate to privacy', async() => {
+ await tapBack();
+ await element(by.id('legal-privacy-button')).tap();
+ await waitFor(element(by.id('privacy-view'))).toBeVisible().withTimeout(2000);
+ await expect(element(by.id('privacy-view'))).toBeVisible();
+ });
+ */
+ });
});
diff --git a/e2e/tests/onboarding/03-forgotpassword.spec.js b/e2e/tests/onboarding/03-forgotpassword.spec.js
index 2fa710469..88d4c3e90 100644
--- a/e2e/tests/onboarding/03-forgotpassword.spec.js
+++ b/e2e/tests/onboarding/03-forgotpassword.spec.js
@@ -32,7 +32,6 @@ describe('Forgot password screen', () => {
await element(by.id('forgot-password-view-submit')).tap();
await element(by.text('OK')).tap();
await waitFor(element(by.id('login-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('login-view'))).toBeVisible();
});
});
});
diff --git a/e2e/tests/onboarding/04-createuser.spec.js b/e2e/tests/onboarding/04-createuser.spec.js
index 6abd8fc16..aa75807f2 100644
--- a/e2e/tests/onboarding/04-createuser.spec.js
+++ b/e2e/tests/onboarding/04-createuser.spec.js
@@ -53,10 +53,8 @@ describe('Create user screen', () => {
await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.users.existing.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
- await sleep(300);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.text('Email already exists. [403]')).atIndex(0)).toExist().withTimeout(10000);
- await expect(element(by.text('Email already exists. [403]')).atIndex(0)).toExist();
await element(by.text('OK')).tap();
});
@@ -65,10 +63,8 @@ describe('Create user screen', () => {
await element(by.id('register-view-username')).replaceText(data.users.existing.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
- await sleep(300);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.text('Username is already in use')).atIndex(0)).toExist().withTimeout(10000);
- await expect(element(by.text('Username is already in use')).atIndex(0)).toExist();
await element(by.text('OK')).tap();
});
@@ -77,10 +73,8 @@ describe('Create user screen', () => {
await element(by.id('register-view-username')).replaceText(data.registeringUser.username);
await element(by.id('register-view-email')).replaceText(data.registeringUser.email);
await element(by.id('register-view-password')).replaceText(data.registeringUser.password);
- await sleep(300);
await element(by.id('register-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
});
});
diff --git a/e2e/tests/onboarding/05-login.spec.js b/e2e/tests/onboarding/05-login.spec.js
index 3734b543d..7c6b4853c 100644
--- a/e2e/tests/onboarding/05-login.spec.js
+++ b/e2e/tests/onboarding/05-login.spec.js
@@ -44,33 +44,27 @@ describe('Login screen', () => {
it('should navigate to register', async() => {
await element(by.id('login-view-register')).tap();
await waitFor(element(by.id('register-view'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('register-view'))).toBeVisible();
await tapBack();
});
it('should navigate to forgot password', async() => {
await element(by.id('login-view-forgot-password')).tap();
await waitFor(element(by.id('forgot-password-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('forgot-password-view'))).toExist();
await tapBack();
});
it('should insert wrong password and get error', async() => {
await element(by.id('login-view-email')).replaceText(data.users.regular.username);
await element(by.id('login-view-password')).replaceText('NotMyActualPassword');
- await sleep(300);
await element(by.id('login-view-submit')).tap();
await waitFor(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible().withTimeout(10000);
- await expect(element(by.text('Your credentials were rejected! Please try again.'))).toBeVisible();
await element(by.text('OK')).tap();
});
it('should login with success', async() => {
await element(by.id('login-view-password')).replaceText(data.users.regular.password);
- await sleep(300);
await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('rooms-list-view'))).toBeVisible();
});
});
});
diff --git a/e2e/tests/onboarding/06-roomslist.spec.js b/e2e/tests/onboarding/06-roomslist.spec.js
index 5e7e1e8aa..bbe0b886b 100644
--- a/e2e/tests/onboarding/06-roomslist.spec.js
+++ b/e2e/tests/onboarding/06-roomslist.spec.js
@@ -1,9 +1,17 @@
const {
device, expect, element, by, waitFor
} = require('detox');
-const { logout, tapBack, sleep, searchRoom } = require('../../helpers/app');
+const { login, navigateToLogin, logout, tapBack, sleep, searchRoom } = require('../../helpers/app');
+const data = require('../../data');
describe('Rooms list screen', () => {
+
+ before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, newInstance: true, delete: true });
+ await navigateToLogin();
+ await login(data.users.regular.username, data.users.regular.password)
+ });
+
describe('Render', () => {
it('should have rooms list screen', async() => {
await expect(element(by.id('rooms-list-view'))).toBeVisible();
@@ -29,18 +37,12 @@ describe('Rooms list screen', () => {
it('should search room and navigate', async() => {
await searchRoom('rocket.cat');
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible();
await element(by.id('rooms-list-view-item-rocket.cat')).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(10000);
- await expect(element(by.id('room-view'))).toBeVisible();
await waitFor(element(by.id('room-view-title-rocket.cat'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('room-view-title-rocket.cat'))).toBeVisible();
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('rooms-list-view'))).toBeVisible();
- await sleep(2000);
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toExist().withTimeout(60000);
- await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist();
});
it('should logout', async() => {
diff --git a/e2e/tests/room/01-createroom.spec.js b/e2e/tests/room/01-createroom.spec.js
index 1cfbf16a7..35b007dba 100644
--- a/e2e/tests/room/01-createroom.spec.js
+++ b/e2e/tests/room/01-createroom.spec.js
@@ -2,48 +2,50 @@ const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
-const { tapBack, sleep, navigateToLogin, login } = require('../../helpers/app');
+const { tapBack, sleep, navigateToLogin, login, tryTapping } = require('../../helpers/app');
+
+
describe('Create room screen', () => {
before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
await navigateToLogin();
await login(data.users.regular.username, data.users.regular.password);
- await element(by.id('rooms-list-view-create-channel')).tap();
- await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000);
});
describe('New Message', async() => {
+ before(async() => {
+ await element(by.id('rooms-list-view-create-channel')).tap();
+ });
+
describe('Render', async() => {
it('should have new message screen', async() => {
- await expect(element(by.id('new-message-view'))).toExist();
+ await waitFor(element(by.id('new-message-view'))).toBeVisible().withTimeout(2000);
});
it('should have search input', async() => {
- await waitFor(element(by.id('new-message-view-search'))).toExist().withTimeout(2000);
- await expect(element(by.id('new-message-view-search'))).toExist();
+ await waitFor(element(by.id('new-message-view-search'))).toBeVisible().withTimeout(2000);
});
})
describe('Usage', async() => {
it('should back to rooms list', async() => {
- await sleep(1000);
+ await waitFor(element(by.id('new-message-view-close'))).toBeVisible().withTimeout(2000);
await element(by.id('new-message-view-close')).tap();
- await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('rooms-list-view'))).toExist();
- await element(by.id('rooms-list-view-create-channel')).tap();
+
+ await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
+
+ await tryTapping(element(by.id('rooms-list-view-create-channel')), 3000);
+ //await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('new-message-view'))).toExist();
});
it('should search user and navigate', async() => {
await element(by.id('new-message-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id('new-message-view-item-rocket.cat'))).toExist().withTimeout(60000);
- await expect(element(by.id('new-message-view-item-rocket.cat'))).toExist();
await element(by.id('new-message-view-item-rocket.cat')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(10000);
- await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id('room-view-title-rocket.cat'))).toExist().withTimeout(60000);
- await expect(element(by.id('room-view-title-rocket.cat'))).toExist();
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
});
@@ -51,11 +53,8 @@ describe('Create room screen', () => {
it('should navigate to select users', async() => {
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('new-message-view'))).toExist();
- await sleep(1000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('select-users-view'))).toExist();
});
})
});
@@ -108,7 +107,6 @@ describe('Create room screen', () => {
const room = `public${ data.random }`;
await element(by.id('create-channel-name')).replaceText(room);
await element(by.id('create-channel-type')).tap();
- await sleep(1000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000);
await expect(element(by.id('room-view'))).toExist();
@@ -123,20 +121,15 @@ describe('Create room screen', () => {
it('should create private room', async() => {
const room = `private${ data.random }`;
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
- // await device.launchApp({ newInstance: true });
- await sleep(1000);
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('select-users-view-item-rocket.cat')).tap();
await waitFor(element(by.id('selected-user-rocket.cat'))).toExist().withTimeout(5000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000);
await element(by.id('create-channel-name')).replaceText(room);
- await sleep(1000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000);
await expect(element(by.id('room-view'))).toExist();
@@ -152,17 +145,13 @@ describe('Create room screen', () => {
const room = `empty${ data.random }`;
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
// await device.launchApp({ newInstance: true });
- await sleep(1000);
await element(by.id('rooms-list-view-create-channel')).tap();
await waitFor(element(by.id('new-message-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('new-message-view-create-channel')).tap();
await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('create-channel-view'))).toExist().withTimeout(5000);
await element(by.id('create-channel-name')).replaceText(room);
- await sleep(1000);
await element(by.id('create-channel-submit')).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000);
await expect(element(by.id('room-view'))).toExist();
diff --git a/e2e/tests/room/02-room.spec.js b/e2e/tests/room/02-room.spec.js
index 86fdfc21f..00d5d434d 100644
--- a/e2e/tests/room/02-room.spec.js
+++ b/e2e/tests/room/02-room.spec.js
@@ -2,27 +2,29 @@ const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
-const { mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app');
+const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom, starMessage, pinMessage, dismissReviewNag, tryTapping } = require('../../helpers/app');
-async function navigateToRoom() {
- await searchRoom(`private${ data.random }`);
- await waitFor(element(by.id(`rooms-list-view-item-private${ data.random }`))).toExist().withTimeout(60000);
- await element(by.id(`rooms-list-view-item-private${ data.random }`)).tap();
+async function navigateToRoom(roomName) {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToLogin();
+ await login(data.users.regular.username, data.users.regular.password);
+ await searchRoom(`${ roomName }`);
+ await waitFor(element(by.id(`rooms-list-view-item-${ roomName }`))).toExist().withTimeout(60000);
+ await element(by.id(`rooms-list-view-item-${ roomName }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
}
describe('Room screen', () => {
- const mainRoom = `private${ data.random }`;
+ const mainRoom = data.groups.private.name;
before(async() => {
- await navigateToRoom();
+ await navigateToRoom(mainRoom);
});
describe('Render', async() => {
it('should have room screen', async() => {
await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${ mainRoom }`))).toExist().withTimeout(5000);
- await expect(element(by.id(`room-view-title-${ mainRoom }`))).toExist();
});
// Render - Header
@@ -69,22 +71,15 @@ describe('Room screen', () => {
await expect(element(by.label(`${ data.random }message`)).atIndex(0)).toExist();
});
- it('should ask for review', async() => {
- await waitFor(element(by.text('Are you enjoying this app?'))).toExist().withTimeout(60000);
- await expect(element(by.text('Are you enjoying this app?')).atIndex(0)).toExist();
- await element(by.label('No').and(by.type('_UIAlertControllerActionView'))).tap(); // Tap `no` on ask for review alert
- })
it('should show/hide emoji keyboard', async () => {
if (device.getPlatform() === 'android') {
await element(by.id('messagebox-open-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji'))).toExist().withTimeout(10000);
- await expect(element(by.id('messagebox-keyboard-emoji'))).toExist();
await expect(element(by.id('messagebox-close-emoji'))).toExist();
await expect(element(by.id('messagebox-open-emoji'))).toBeNotVisible();
await element(by.id('messagebox-close-emoji')).tap();
await waitFor(element(by.id('messagebox-keyboard-emoji'))).toBeNotVisible().withTimeout(10000);
- await expect(element(by.id('messagebox-keyboard-emoji'))).toBeNotVisible();
await expect(element(by.id('messagebox-close-emoji'))).toBeNotVisible();
await expect(element(by.id('messagebox-open-emoji'))).toExist();
}
@@ -94,10 +89,8 @@ describe('Room screen', () => {
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(':joy');
await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(10000);
- await expect(element(by.id('messagebox-container'))).toExist();
await element(by.id('messagebox-input')).clearText();
await waitFor(element(by.id('messagebox-container'))).toBeNotVisible().withTimeout(10000);
- await expect(element(by.id('messagebox-container'))).toBeNotVisible();
});
it('should show and tap on emoji autocomplete', async() => {
@@ -105,8 +98,6 @@ describe('Room screen', () => {
await element(by.id('messagebox-input')).replaceText(':');
await element(by.id('messagebox-input')).typeText('joy'); // workaround for number keyboard
await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(10000);
- await expect(element(by.id('messagebox-container'))).toExist();
- await sleep(1000);
await element(by.id('mention-item-joy')).tap();
await expect(element(by.id('messagebox-input'))).toHaveText(':joy: ');
await element(by.id('messagebox-input')).clearText();
@@ -116,25 +107,22 @@ describe('Room screen', () => {
const username = data.users.regular.username
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText(`@${ username }`);
- await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(60000);
- await expect(element(by.id('messagebox-container'))).toExist();
- await sleep(1000);
- await element(by.id(`mention-item-${ username }`)).tap();
+ await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000);
+ await waitFor(element(by.id(`mention-item-${ username }`))).toBeVisible().withTimeout(4000)
+ await tryTapping(element(by.id(`mention-item-${ username }`)), 2000, true);
await expect(element(by.id('messagebox-input'))).toHaveText(`@${ username } `);
- await element(by.id('messagebox-input')).tap();
+ await tryTapping(element(by.id('messagebox-input')), 2000)
await element(by.id('messagebox-input')).typeText(`${ data.random }mention`);
await element(by.id('messagebox-send-message')).tap();
// await waitFor(element(by.label(`@${ data.user } ${ data.random }mention`)).atIndex(0)).toExist().withTimeout(60000);
- await sleep(2000);
});
it('should show and tap on room autocomplete', async() => {
await element(by.id('messagebox-input')).tap();
await element(by.id('messagebox-input')).typeText('#general');
- await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(60000);
- await expect(element(by.id('messagebox-container'))).toExist();
- await sleep(1000);
- await element(by.id('mention-item-general')).tap();
+ //await waitFor(element(by.id('messagebox-container'))).toExist().withTimeout(4000);
+ await waitFor(element(by.id('mention-item-general'))).toBeVisible().withTimeout(4000);
+ await tryTapping(element(by.id('mention-item-general')), 2000, true)
await expect(element(by.id('messagebox-input'))).toHaveText('#general ');
await element(by.id('messagebox-input')).clearText();
});
@@ -147,7 +135,6 @@ describe('Room screen', () => {
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.label('Permalink')).tap();
- await sleep(1000);
// TODO: test clipboard
});
@@ -158,28 +145,20 @@ describe('Room screen', () => {
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.label('Copy')).tap();
- await sleep(1000);
// TODO: test clipboard
});
it('should star message', async() => {
- await element(by.label(`${ data.random }message`)).atIndex(0).longPress();
- await expect(element(by.id('action-sheet'))).toExist();
- await expect(element(by.id('action-sheet-handle'))).toBeVisible();
- await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
- await element(by.label('Star')).tap();
- await sleep(1000);
- await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000);
+ await starMessage('message')
+ await sleep(1000) //https://github.com/RocketChat/Rocket.Chat.ReactNative/issues/2324
await element(by.label(`${ data.random }message`)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await waitFor(element(by.label('Unstar'))).toBeVisible().withTimeout(2000);
- await expect(element(by.label('Unstar'))).toBeVisible();
await element(by.id('action-sheet-backdrop')).tap();
- await sleep(1000);
});
it('should react to message', async() => {
@@ -189,14 +168,10 @@ describe('Room screen', () => {
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.id('add-reaction')).tap();
await waitFor(element(by.id('reaction-picker'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('reaction-picker'))).toBeVisible();
await element(by.id('reaction-picker-😃')).tap();
await waitFor(element(by.id('reaction-picker-grinning'))).toExist().withTimeout(2000);
- await expect(element(by.id('reaction-picker-grinning'))).toExist();
await element(by.id('reaction-picker-grinning')).tap();
await waitFor(element(by.id('message-reaction-:grinning:'))).toExist().withTimeout(60000);
- await expect(element(by.id('message-reaction-:grinning:'))).toExist();
- await sleep(1000);
});
it('should react to message with frequently used emoji', async() => {
@@ -205,30 +180,27 @@ describe('Room screen', () => {
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await waitFor(element(by.id('message-actions-emoji-+1'))).toBeVisible().withTimeout(2000);
- await expect(element(by.id('message-actions-emoji-+1'))).toBeVisible();
await element(by.id('message-actions-emoji-+1')).tap();
await waitFor(element(by.id('message-reaction-:+1:'))).toBeVisible().withTimeout(60000);
- await expect(element(by.id('message-reaction-:+1:'))).toBeVisible();
- await sleep(1000);
});
it('should show reaction picker on add reaction button pressed and have frequently used emoji', async() => {
await element(by.id('message-add-reaction')).tap();
await waitFor(element(by.id('reaction-picker'))).toExist().withTimeout(2000);
- await expect(element(by.id('reaction-picker'))).toExist();
await waitFor(element(by.id('reaction-picker-grinning'))).toExist().withTimeout(2000);
- await expect(element(by.id('reaction-picker-grinning'))).toExist();
await element(by.id('reaction-picker-😃')).tap();
await waitFor(element(by.id('reaction-picker-grimacing'))).toExist().withTimeout(2000);
await element(by.id('reaction-picker-grimacing')).tap();
await waitFor(element(by.id('message-reaction-:grimacing:'))).toExist().withTimeout(60000);
- await sleep(1000);
});
+
+ it('should ask for review', async() => {
+ await dismissReviewNag() //TODO: Create a proper test for this elsewhere.
+ })
it('should remove reaction', async() => {
await element(by.id('message-reaction-:grinning:')).tap();
await waitFor(element(by.id('message-reaction-:grinning:'))).toBeNotVisible().withTimeout(60000);
- await expect(element(by.id('message-reaction-:grinning:'))).toBeNotVisible();
});
it('should edit message', async() => {
@@ -241,7 +213,6 @@ describe('Room screen', () => {
await element(by.id('messagebox-input')).typeText('ed');
await element(by.id('messagebox-send-message')).tap();
await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist().withTimeout(60000);
- await expect(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist();
});
it('should quote message', async() => {
@@ -253,46 +224,39 @@ describe('Room screen', () => {
await element(by.label('Quote')).tap();
await element(by.id('messagebox-input')).typeText(`${ data.random }quoted`);
await element(by.id('messagebox-send-message')).tap();
- await sleep(1000);
// TODO: test if quote was sent
});
it('should pin message', async() => {
- await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toExist();
- await element(by.label(`${ data.random }edited (edited)`)).atIndex(0).longPress();
- await expect(element(by.id('action-sheet'))).toExist();
- await expect(element(by.id('action-sheet-handle'))).toBeVisible();
- await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
- await element(by.label('Pin')).tap();
- await waitFor(element(by.id('action-sheet'))).toNotExist().withTimeout(5000);
- await sleep(1500);
-
- await waitFor(element(by.label(`${ data.random }edited (edited)`)).atIndex(0)).toBeVisible();
- await element(by.label(`${ data.random }edited (edited)`)).atIndex(0).longPress();
- await expect(element(by.id('action-sheet'))).toExist();
+ await mockMessage('pin')
+ await pinMessage('pin')
+
+ await waitFor(element(by.label(`${ data.random }pin`)).atIndex(0)).toBeVisible().withTimeout(2000);
+ await waitFor(element(by.label('Message pinned')).atIndex(0)).toBeVisible().withTimeout(2000);
+ await element(by.label(`${ data.random }pin`)).atIndex(0).longPress();
+ await waitFor(element(by.id('action-sheet'))).toExist().withTimeout(1000);
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await waitFor(element(by.label('Unpin'))).toBeVisible().withTimeout(2000);
- await expect(element(by.label('Unpin'))).toBeVisible();
await element(by.id('action-sheet-backdrop')).tap();
});
it('should delete message', async() => {
- await waitFor(element(by.label(`${ data.random }quoted`)).atIndex(0)).toBeVisible();
- await element(by.label(`${ data.random }quoted`)).atIndex(0).longPress();
+ await mockMessage('delete')
+
+ await waitFor(element(by.label(`${ data.random }delete`)).atIndex(0)).toBeVisible();
+ await element(by.label(`${ data.random }delete`)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.id('action-sheet-handle')).swipe('up', 'fast', 0.5);
await element(by.label('Delete')).tap();
const deleteAlertMessage = 'You will not be able to recover this message!';
- await waitFor(element(by.text(deleteAlertMessage)).atIndex(0)).toExist().withTimeout(10000);
- await expect(element(by.text(deleteAlertMessage)).atIndex(0)).toExist();
+ await waitFor(element(by.text(deleteAlertMessage)).atIndex(0)).toExist().withTimeout(10000);
await element(by.text('Delete')).tap();
- await sleep(1000);
- await expect(element(by.label(`${ data.random }quoted`)).atIndex(0)).toNotExist();
+ await waitFor(element(by.label(`${ data.random }delete`)).atIndex(0)).toNotExist().withTimeout(2000);
});
});
@@ -317,7 +281,6 @@ describe('Room screen', () => {
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id(`room-view-title-${ thread }`))).toExist();
await tapBack();
- await sleep(1000);
});
it('should toggle follow thread', async() => {
@@ -332,10 +295,13 @@ describe('Room screen', () => {
await waitFor(element(by.id('room-view-header-unfollow'))).toExist().withTimeout(60000);
await expect(element(by.id('room-view-header-unfollow'))).toExist();
await tapBack();
- await sleep(1000);
});
it('should navigate to thread from thread name', async() => {
+ await waitFor(element(by.id('room-view-header-actions').and(by.label(` ${ mainRoom }`)))).toBeVisible().withTimeout(2000);
+ await waitFor(element(by.id('room-view-header-actions').and(by.label(` ${ data.random }thread`)))).toBeNotVisible().withTimeout(2000);
+ await sleep(500) //TODO: Find a better way to wait for the animation to finish and the messagebox-input to be available and usable :(
+
await mockMessage('dummymessagebetweenthethread');
await element(by.label(thread)).atIndex(0).longPress();
await expect(element(by.id('action-sheet'))).toExist();
@@ -352,10 +318,10 @@ describe('Room screen', () => {
await waitFor(element(by.id(`room-view-title-${ thread }`))).toExist().withTimeout(5000);
await expect(element(by.id(`room-view-title-${ thread }`))).toExist();
await tapBack();
- await sleep(1000);
});
it('should navigate to thread from threads view', async() => {
+ await waitFor(element(by.id('room-view-header-threads'))).toExist().withTimeout(1000);
await element(by.id('room-view-header-threads')).tap();
await waitFor(element(by.id('thread-messages-view'))).toExist().withTimeout(5000);
await expect(element(by.id('thread-messages-view'))).toExist();
diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js
index dd5c8defd..bd5b7c6bc 100644
--- a/e2e/tests/room/03-roomactions.spec.js
+++ b/e2e/tests/room/03-roomactions.spec.js
@@ -2,7 +2,7 @@ const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
-const { tapBack, sleep, searchRoom } = require('../../helpers/app');
+const { navigateToLogin, login, tapBack, sleep, searchRoom, mockMessage, starMessage, pinMessage } = require('../../helpers/app');
const scrollDown = 200;
@@ -11,13 +11,12 @@ async function navigateToRoomActions(type) {
if (type === 'd') {
room = 'rocket.cat';
} else {
- room = `private${ data.random }`;
+ room = data.groups.private.name;
}
await searchRoom(room);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
}
@@ -25,7 +24,6 @@ async function navigateToRoomActions(type) {
async function backToActions() {
await tapBack();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('room-actions-view'))).toExist();
}
async function backToRoomsList() {
@@ -36,10 +34,16 @@ async function backToRoomsList() {
}
describe('Room actions screen', () => {
+
+ before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToLogin();
+ await login(data.users.regular.username, data.users.regular.password);
+ });
+
describe('Render', async() => {
describe('Direct', async() => {
before(async() => {
- await device.launchApp({ newInstance: true });
await navigateToRoomActions('d');
});
@@ -197,65 +201,89 @@ describe('Room actions screen', () => {
it('should show mentioned messages', async() => {
await element(by.id('room-actions-mentioned')).tap();
await waitFor(element(by.id('mentioned-messages-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('mentioned-messages-view'))).toExist();
// await waitFor(element(by.text(` ${ data.random }mention`))).toExist().withTimeout(60000);
- // await expect(element(by.text(` ${ data.random }mention`))).toExist();
await backToActions();
});
it('should show starred message and unstar it', async() => {
+
+ //Go back to room and send a message
+ await tapBack();
+ await mockMessage('messageToStar');
+
+ //Star the message
+ await starMessage('messageToStar')
+
+ //Back into Room Actions
+ await element(by.id('room-view-header-actions')).tap();
+ await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
+
+ //Go to starred messages
await element(by.id('room-actions-starred')).tap();
await waitFor(element(by.id('starred-messages-view'))).toExist().withTimeout(2000);
- await sleep(1000);
- await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeVisible().withTimeout(60000);
- await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeVisible();
- await element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view'))).longPress();
-
+ await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toBeVisible().withTimeout(60000);
+
+ //Unstar message
+ await element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view'))).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.label('Unstar')).tap();
- await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible().withTimeout(60000);
- await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible();
+ await waitFor(element(by.label(`${ data.random }messageToStar`).withAncestor(by.id('starred-messages-view')))).toBeNotVisible().withTimeout(60000);
await backToActions();
});
it('should show pinned message and unpin it', async() => {
+
+ //Go back to room and send a message
+ await tapBack();
+ await mockMessage('messageToPin');
+
+ //Pin the message
+ await pinMessage('messageToPin')
+
+ //Back into Room Actions
+ await element(by.id('room-view-header-actions')).tap();
+ await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
+
await waitFor(element(by.id('room-actions-pinned'))).toExist();
await element(by.id('room-actions-pinned')).tap();
await waitFor(element(by.id('pinned-messages-view'))).toExist().withTimeout(2000);
- await sleep(1000);
- await waitFor(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeVisible().withTimeout(60000);
- await expect(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeVisible();
- await element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view'))).longPress();
+ await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeVisible().withTimeout(60000);
+ await element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view'))).longPress();
await expect(element(by.id('action-sheet'))).toExist();
await expect(element(by.id('action-sheet-handle'))).toBeVisible();
await element(by.label('Unpin')).tap();
- await waitFor(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible().withTimeout(60000);
- await expect(element(by.label(`${ data.random }edited (edited)`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible();
+ await waitFor(element(by.label(`${ data.random }messageToPin`).withAncestor(by.id('pinned-messages-view')))).toBeNotVisible().withTimeout(60000);
await backToActions();
});
it('should search and find a message', async() => {
+
+ //Go back to room and send a message
+ await tapBack();
+ await mockMessage('messageToFind');
+
+ //Back into Room Actions
+ await element(by.id('room-view-header-actions')).tap();
+ await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
+
await element(by.id('room-actions-search')).tap();
await waitFor(element(by.id('search-messages-view'))).toExist().withTimeout(2000);
await expect(element(by.id('search-message-view-input'))).toExist();
- await element(by.id('search-message-view-input')).replaceText(`/${ data.random }message/`);
- await waitFor(element(by.label(`${ data.random }message`).withAncestor(by.id('search-messages-view')))).toExist().withTimeout(60000);
- await expect(element(by.label(`${ data.random }message`).withAncestor(by.id('search-messages-view')))).toExist();
+ await element(by.id('search-message-view-input')).replaceText(`/${ data.random }messageToFind/`);
+ await waitFor(element(by.label(`${ data.random }messageToFind`).withAncestor(by.id('search-messages-view')))).toExist().withTimeout(60000);
await backToActions();
});
});
describe('Notification', async() => {
it('should navigate to notification preference view', async() => {
- await waitFor(element(by.id('room-actions-notifications'))).toExist();
- await expect(element(by.id('room-actions-notifications'))).toExist();
+ await waitFor(element(by.id('room-actions-notifications'))).toExist().withTimeout(2000);
await element(by.id('room-actions-notifications')).tap();
await waitFor(element(by.id('notification-preference-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('notification-preference-view'))).toExist();
});
it('should have receive notification option', async() => {
@@ -271,30 +299,25 @@ describe('Room actions screen', () => {
});
it('should have push notification option', async() => {
- await waitFor(element(by.id('notification-preference-view-push-notification'))).toExist();
- await expect(element(by.id('notification-preference-view-push-notification'))).toExist();
+ await waitFor(element(by.id('notification-preference-view-push-notification'))).toExist().withTimeout(4000);
});
it('should have notification audio option', async() => {
- await waitFor(element(by.id('notification-preference-view-audio'))).toExist();
- await expect(element(by.id('notification-preference-view-audio'))).toExist();
+ await waitFor(element(by.id('notification-preference-view-audio'))).toExist().withTimeout(4000);
});
it('should have notification sound option', async() => {
// Ugly hack to scroll on detox
await element(by.type('UIScrollView')).atIndex(1).scrollTo('bottom');
- await waitFor(element(by.id('notification-preference-view-sound'))).toExist();
- await expect(element(by.id('notification-preference-view-sound'))).toExist();
+ await waitFor(element(by.id('notification-preference-view-sound'))).toExist().withTimeout(4000);
});
it('should have notification duration option', async() => {
- await waitFor(element(by.id('notification-preference-view-notification-duration'))).toExist();
- await expect(element(by.id('notification-preference-view-notification-duration'))).toExist();
+ await waitFor(element(by.id('notification-preference-view-notification-duration'))).toExist().withTimeout(4000);
});
it('should have email alert option', async() => {
- await waitFor(element(by.id('notification-preference-view-email-alert'))).toExist();
- await expect(element(by.id('notification-preference-view-email-alert'))).toExist();
+ await waitFor(element(by.id('notification-preference-view-email-alert'))).toExist().withTimeout(4000);
});
after(async() => {
@@ -309,34 +332,28 @@ describe('Room actions screen', () => {
const user = data.users.alternate
it('should tap on leave channel and raise alert', async() => {
- await waitFor(element(by.id('room-actions-leave-channel'))).toExist();
- await expect(element(by.id('room-actions-leave-channel'))).toExist();
+ await waitFor(element(by.id('room-actions-leave-channel'))).toExist().withTimeout(2000);
await element(by.id('room-actions-leave-channel')).tap();
await waitFor(element(by.text('Yes, leave it!'))).toExist().withTimeout(2000);
- await expect(element(by.text('Yes, leave it!'))).toExist();
await element(by.text('Yes, leave it!')).tap();
- await waitFor(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist().withTimeout(60000);
- await expect(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist();
+ await waitFor(element(by.text('You are the last owner. Please set new owner before leaving the room.'))).toExist().withTimeout(8000);
await element(by.text('OK')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000);
});
it('should add user to the room', async() => {
- await waitFor(element(by.id('room-actions-add-user'))).toExist();
+ await waitFor(element(by.id('room-actions-add-user'))).toExist().withTimeout(4000);
await element(by.id('room-actions-add-user')).tap();
await element(by.id('select-users-view-search')).tap();
await element(by.id('select-users-view-search')).replaceText(user.username);
- await waitFor(element(by.id(`select-users-view-item-${ user.username }`))).toExist().withTimeout(60000);
- await expect(element(by.id(`select-users-view-item-${ user.username }`))).toExist();
+ await waitFor(element(by.id(`select-users-view-item-${ user.username }`))).toExist().withTimeout(10000);
await element(by.id(`select-users-view-item-${ user.username }`)).tap();
await waitFor(element(by.id(`selected-user-${ user.username }`))).toExist().withTimeout(5000);
- await expect(element(by.id(`selected-user-${ user.username }`))).toExist();
await element(by.id('selected-users-view-submit')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(2000);
await element(by.id('room-actions-members')).tap();
await element(by.id('room-members-view-toggle-status')).tap();
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
- await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist();
await backToActions(1);
});
@@ -344,26 +361,20 @@ describe('Room actions screen', () => {
before(async() => {
await element(by.id('room-actions-members')).tap();
await waitFor(element(by.id('room-members-view'))).toExist().withTimeout(2000);
- await expect(element(by.id('room-members-view'))).toExist();
});
it('should show all users', async() => {
- await sleep(1000);
await element(by.id('room-members-view-toggle-status')).tap();
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
- await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist();
});
it('should filter user', async() => {
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
- await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist();
await element(by.id('room-members-view-search')).replaceText('rocket');
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toBeNotVisible().withTimeout(60000);
- await expect(element(by.id(`room-members-view-item-${ user.username }`))).toBeNotVisible();
await element(by.id('room-members-view-search')).tap();
await element(by.id('room-members-view-search')).clearText('');
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(60000);
- await expect(element(by.id(`room-members-view-item-${ user.username }`))).toExist();
});
// FIXME: mute/unmute isn't working
@@ -391,9 +402,7 @@ describe('Room actions screen', () => {
await waitFor(element(by.id(`room-members-view-item-${ user.username }`))).toExist().withTimeout(5000);
await element(by.id(`room-members-view-item-${ user.username }`)).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(60000);
- await expect(element(by.id('room-view'))).toExist();
await waitFor(element(by.id(`room-view-title-${ user.username }`))).toExist().withTimeout(60000);
- await expect(element(by.id(`room-view-title-${ user.username }`))).toExist();
await tapBack();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(2000);
});
@@ -407,13 +416,10 @@ describe('Room actions screen', () => {
it('should block/unblock user', async() => {
await waitFor(element(by.id('room-actions-block-user'))).toExist();
- await sleep(1000);
await element(by.id('room-actions-block-user')).tap();
await waitFor(element(by.label('Unblock user'))).toExist().withTimeout(60000);
- await expect(element(by.label('Unblock user'))).toExist();
await element(by.id('room-actions-block-user')).tap();
await waitFor(element(by.label('Block user'))).toExist().withTimeout(60000);
- await expect(element(by.label('Block user'))).toExist();
});
});
});
diff --git a/e2e/tests/room/04-roominfo.spec.js b/e2e/tests/room/04-roominfo.spec.js
index 35a45441b..9382f16cf 100644
--- a/e2e/tests/room/04-roominfo.spec.js
+++ b/e2e/tests/room/04-roominfo.spec.js
@@ -2,23 +2,23 @@ const {
device, expect, element, by, waitFor
} = require('detox');
const data = require('../../data');
-const { tapBack, sleep, searchRoom } = require('../../helpers/app');
+const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app');
+
+const privateRoomName = data.groups.private.name
async function navigateToRoomInfo(type) {
let room;
if (type === 'd') {
room = 'rocket.cat';
} else {
- room = `private${ data.random }`;
+ room = privateRoomName;
}
await searchRoom(room);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);
- await sleep(1000);
await element(by.id('room-actions-info')).tap();
await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000);
}
@@ -28,13 +28,19 @@ async function waitForToast() {
// await expect(element(by.id('toast'))).toExist();
// await waitFor(element(by.id('toast'))).toBeNotVisible().withTimeout(10000);
// await expect(element(by.id('toast'))).toBeNotVisible();
- await sleep(5000);
+ await sleep(1);
}
describe('Room info screen', () => {
+
+ before(async() => {
+ await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
+ await navigateToLogin();
+ await login(data.users.regular.username, data.users.regular.password);
+ });
+
describe('Direct', async() => {
before(async() => {
- await device.launchApp({ newInstance: true });
await navigateToRoomInfo('d');
});
@@ -42,11 +48,16 @@ describe('Room info screen', () => {
await expect(element(by.id('room-info-view'))).toExist();
await expect(element(by.id('room-info-view-name'))).toExist();
});
+
+ after(async() => {
+ await tapBack()
+ await tapBack()
+ await tapBack()
+ })
});
describe('Channel/Group', async() => {
before(async() => {
- await device.launchApp({ newInstance: true });
await navigateToRoomInfo('c');
});
@@ -78,7 +89,6 @@ describe('Room info screen', () => {
describe('Render Edit', async() => {
before(async() => {
- await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
@@ -141,7 +151,6 @@ describe('Room info screen', () => {
});
describe('Usage', async() => {
- const room = `private${ data.random }`;
// it('should enter "invalid name" and get error', async() => {
// await element(by.type('UIScrollView')).atIndex(1).swipe('down');
// await element(by.id('room-info-edit-view-name')).replaceText('invalid name');
@@ -155,22 +164,17 @@ describe('Room info screen', () => {
// });
it('should change room name', async() => {
- await element(by.id('room-info-edit-view-name')).replaceText(`${ room }new`);
+ await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }new`);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-submit')).tap();
- await sleep(5000);
await tapBack();
await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000);
- await sleep(1000);
- await expect(element(by.id('room-info-view-name'))).toHaveLabel(`${ room }new`);
+ await expect(element(by.id('room-info-view-name'))).toHaveLabel(`${ privateRoomName }new`);
// change name to original
await element(by.id('room-info-view-edit-button')).tap();
- await sleep(1000);
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
- await sleep(1000);
- await element(by.id('room-info-edit-view-name')).replaceText(`${ room }`);
+ await element(by.id('room-info-edit-view-name')).replaceText(`${ privateRoomName }`);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
- await sleep(1000);
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
@@ -184,14 +188,11 @@ describe('Room info screen', () => {
await element(by.id('room-info-edit-view-password')).replaceText('abc');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-t')).tap();
- await sleep(1000);
- await element(by.id('room-info-edit-view-ro')).tap();
- await sleep(1000);
+ await element(by.id('room-info-edit-view-ro')).longPress(); //https://github.com/facebook/react-native/issues/28032
await element(by.id('room-info-edit-view-react-when-ro')).tap();
- await sleep(1000);
await element(by.id('room-info-edit-view-reset')).tap();
// after reset
- await expect(element(by.id('room-info-edit-view-name'))).toHaveText(room);
+ await expect(element(by.id('room-info-edit-view-name'))).toHaveText(privateRoomName);
await expect(element(by.id('room-info-edit-view-description'))).toHaveText('');
await expect(element(by.id('room-info-edit-view-topic'))).toHaveText('');
await expect(element(by.id('room-info-edit-view-announcement'))).toHaveText('');
@@ -203,55 +204,45 @@ describe('Room info screen', () => {
});
it('should change room description', async() => {
- await sleep(1000);
await element(by.id('room-info-edit-view-description')).replaceText('new description');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await expect(element(by.label('new description').withAncestor(by.id('room-info-view-description')))).toExist();
});
it('should change room topic', async() => {
- await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('room-info-edit-view-topic')).replaceText('new topic');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await expect(element(by.label('new topic').withAncestor(by.id('room-info-view-topic')))).toExist();
});
it('should change room announcement', async() => {
- await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.id('room-info-edit-view-announcement')).replaceText('new announcement');
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-submit')).tap();
await waitForToast();
await tapBack();
await waitFor(element(by.id('room-info-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await expect(element(by.label('new announcement').withAncestor(by.id('room-info-view-announcement')))).toExist();
});
it('should change room password', async() => {
- await sleep(1000);
await waitFor(element(by.id('room-info-view-edit-button'))).toExist().withTimeout(10000);
await element(by.id('room-info-view-edit-button')).tap();
await waitFor(element(by.id('room-info-edit-view'))).toExist().withTimeout(2000);
- await sleep(1000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-password')).replaceText('password');
await element(by.id('room-info-edit-view-submit')).tap();
@@ -259,7 +250,6 @@ describe('Room info screen', () => {
});
it('should change room type', async() => {
- await sleep(1000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-t')).tap();
await element(by.id('room-info-edit-view-submit')).tap();
@@ -282,14 +272,11 @@ describe('Room info screen', () => {
// });
it('should archive room', async() => {
- await sleep(1000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-archive')).tap();
await waitFor(element(by.text('Yes, archive it!'))).toExist().withTimeout(5000);
- await expect(element(by.text('Yes, archive it!'))).toExist();
await element(by.text('Yes, archive it!')).tap();
await waitFor(element(by.id('room-info-edit-view-unarchive'))).toExist().withTimeout(60000);
- await expect(element(by.id('room-info-edit-view-unarchive'))).toExist();
await expect(element(by.id('room-info-edit-view-archive'))).toBeNotVisible();
// TODO: needs permission to unarchive
// await element(by.id('room-info-edit-view-archive')).tap();
@@ -301,16 +288,12 @@ describe('Room info screen', () => {
});
it('should delete room', async() => {
- await sleep(1000);
await element(by.type('UIScrollView')).atIndex(1).swipe('up');
await element(by.id('room-info-edit-view-delete')).tap();
await waitFor(element(by.text('Yes, delete it!'))).toExist().withTimeout(5000);
- await expect(element(by.text('Yes, delete it!'))).toExist();
await element(by.text('Yes, delete it!')).tap();
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000);
- await sleep(2000);
- await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000);
- await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible();
+ await waitFor(element(by.id(`rooms-list-view-item-${ privateRoomName }`))).toBeNotVisible().withTimeout(60000);
});
});
});
diff --git a/ios/Podfile b/ios/Podfile
index 35f65a777..c1eca515e 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,143 +1,23 @@
platform :ios, '11.0'
+require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
require_relative '../node_modules/react-native-unimodules/cocoapods.rb'
-def add_flipper_pods!(versions = {})
- versions['Flipper'] ||= '~> 0.33.1'
- versions['DoubleConversion'] ||= '1.1.7'
- versions['Flipper-Folly'] ||= '~> 2.1'
- versions['Flipper-Glog'] ||= '0.3.6'
- versions['Flipper-PeerTalk'] ||= '~> 0.0.4'
- versions['Flipper-RSocket'] ||= '~> 1.0'
-
- pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug'
-
- # List all transitive dependencies for FlipperKit pods
- # to avoid them being linked in Release builds
- pod 'Flipper', versions['Flipper'], :configuration => 'Debug'
- pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug'
- pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug'
- pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug'
- pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug'
- pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug'
- pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
-end
-
-# Post Install processing for Flipper
-def flipper_post_install(installer)
- installer.pods_project.targets.each do |target|
- if target.name == 'YogaKit'
- target.build_configurations.each do |config|
- config.build_settings['SWIFT_VERSION'] = '4.1'
- end
- end
- end
- file_name = Dir.glob("*.xcodeproj")[0]
- app_project = Xcodeproj::Project.open(file_name)
- app_project.native_targets.each do |target|
- target.build_configurations.each do |config|
- cflags = config.build_settings['OTHER_CFLAGS'] || '$(inherited) '
- unless cflags.include? '-DFB_SONARKIT_ENABLED=1'
- puts 'Adding -DFB_SONARKIT_ENABLED=1 in OTHER_CFLAGS...'
- cflags << '-DFB_SONARKIT_ENABLED=1'
- end
- config.build_settings['OTHER_CFLAGS'] = cflags
- end
- app_project.save
- end
- installer.pods_project.save
+def all_pods
+ config = use_native_modules!
+ use_unimodules!
+ use_react_native!(:path => config["reactNativePath"])
+ use_flipper!
end
target 'RocketChatRN' do
- pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
- pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
- pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
- pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
- pod 'React', :path => '../node_modules/react-native/'
- pod 'React-Core', :path => '../node_modules/react-native/'
- pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
- pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
- pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
- pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
- pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
- pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
- pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
- pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
- pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
- pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
- pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
- pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
-
- pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
- pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
- pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
- pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
- pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
- pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
- pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
-
- pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
- pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
- pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
-
- use_native_modules!
- use_unimodules!
+ all_pods
end
target 'ShareRocketChatRN' do
- pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
- pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
- pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
- pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
- pod 'React', :path => '../node_modules/react-native/'
- pod 'React-Core', :path => '../node_modules/react-native/'
- pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
- pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
- pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
- pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
- pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
- pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
- pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
- pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
- pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
- pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
- pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
- pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
-
- pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
- pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
- pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
- pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
- pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
- pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
- pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
-
- pod 'JitsiMeetSDK', :git => 'https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git'
-
- pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
- pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
- pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
-
- use_native_modules!
- use_unimodules!
+ all_pods
end
-# Enables Flipper.
-#
-# Note that if you have use_frameworks! enabled, Flipper will not work and
-# you should disable these next few lines.
-add_flipper_pods!
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
@@ -145,4 +25,4 @@ post_install do |installer|
end
end
flipper_post_install(installer)
-end
+end
\ No newline at end of file
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 0355ffe49..c72afcec8 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1,89 +1,95 @@
PODS:
- boost-for-react-native (1.63.0)
- - BugsnagReactNative (2.23.7):
- - BugsnagReactNative/Core (= 2.23.7)
+ - BugsnagReactNative (2.23.10):
+ - BugsnagReactNative/Core (= 2.23.10)
- React
- - BugsnagReactNative/Core (2.23.7):
+ - BugsnagReactNative/Core (2.23.10):
- React
- CocoaAsyncSocket (7.6.4)
- CocoaLibEvent (1.0.0)
- - Crashlytics (3.14.0):
- - Fabric (~> 1.10.2)
- DoubleConversion (1.1.6)
- EXAppleAuthentication (2.2.1):
- UMCore
- - EXAV (8.1.0):
+ - EXAV (8.2.1):
- UMCore
- UMFileSystemInterface
- UMPermissionsInterface
- - EXConstants (9.0.0):
+ - EXConstants (9.1.1):
- UMConstantsInterface
- UMCore
- - EXFileSystem (8.1.0):
+ - EXFileSystem (9.0.1):
- UMCore
- UMFileSystemInterface
- - EXHaptics (8.1.0):
+ - EXHaptics (8.2.1):
- UMCore
- - EXImageLoader (1.0.1):
+ - EXImageLoader (1.1.1):
- React-Core
- UMCore
- UMImageLoaderInterface
- - EXKeepAwake (8.1.0):
+ - EXKeepAwake (8.2.1):
- UMCore
- - EXLocalAuthentication (9.0.0):
+ - EXLocalAuthentication (9.2.0):
- UMConstantsInterface
- UMCore
- - EXPermissions (8.1.0):
+ - EXPermissions (9.0.1):
- UMCore
- UMPermissionsInterface
- - EXVideoThumbnails (4.1.1):
+ - EXVideoThumbnails (4.2.1):
- UMCore
- UMFileSystemInterface
- - EXWebBrowser (8.2.1):
+ - EXWebBrowser (8.3.1):
- UMCore
- - Fabric (1.10.2)
- - FBLazyVector (0.62.2)
- - FBReactNativeSpec (0.62.2):
- - Folly (= 2018.10.22.00)
- - RCTRequired (= 0.62.2)
- - RCTTypeSafety (= 0.62.2)
- - React-Core (= 0.62.2)
- - React-jsi (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - Firebase/Core (6.24.0):
+ - FBLazyVector (0.63.1)
+ - FBReactNativeSpec (0.63.1):
+ - Folly (= 2020.01.13.00)
+ - RCTRequired (= 0.63.1)
+ - RCTTypeSafety (= 0.63.1)
+ - React-Core (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - Firebase/Analytics (6.27.1):
+ - Firebase/Core
+ - Firebase/Core (6.27.1):
- Firebase/CoreOnly
- - FirebaseAnalytics (= 6.5.0)
- - Firebase/CoreOnly (6.24.0):
- - FirebaseCore (= 6.7.0)
- - FirebaseAnalytics (6.5.0):
- - FirebaseCore (~> 6.7)
- - FirebaseInstallations (~> 1.2)
- - GoogleAppMeasurement (= 6.5.0)
+ - FirebaseAnalytics (= 6.6.2)
+ - Firebase/CoreOnly (6.27.1):
+ - FirebaseCore (= 6.8.1)
+ - Firebase/Crashlytics (6.27.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.7.0):
+ - FirebaseCore (6.8.1):
- FirebaseCoreDiagnostics (~> 1.3)
- - FirebaseCoreDiagnosticsInterop (~> 1.2)
- GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5)
- - FirebaseCoreDiagnostics (1.3.0):
- - FirebaseCoreDiagnosticsInterop (~> 1.2)
+ - FirebaseCoreDiagnostics (1.4.0):
- GoogleDataTransportCCTSupport (~> 3.1)
- GoogleUtilities/Environment (~> 6.5)
- GoogleUtilities/Logger (~> 6.5)
- nanopb (~> 1.30905.0)
- - FirebaseCoreDiagnosticsInterop (1.2.0)
- - FirebaseInstallations (1.2.0):
- - FirebaseCore (~> 6.6)
- - GoogleUtilities/Environment (~> 6.6)
- - GoogleUtilities/UserDefaults (~> 6.6)
+ - FirebaseCrashlytics (4.2.0):
+ - FirebaseCore (~> 6.8)
+ - FirebaseInstallations (~> 1.1)
+ - GoogleDataTransport (~> 6.1)
+ - GoogleDataTransportCCTSupport (~> 3.1)
+ - nanopb (~> 1.30905.0)
- PromisesObjC (~> 1.2)
- - Flipper (0.33.1):
- - Flipper-Folly (~> 2.1)
- - Flipper-RSocket (~> 1.0)
+ - FirebaseInstallations (1.5.0):
+ - FirebaseCore (~> 6.8)
+ - GoogleUtilities/Environment (~> 6.7)
+ - GoogleUtilities/UserDefaults (~> 6.7)
+ - PromisesObjC (~> 1.2)
+ - Flipper (0.41.5):
+ - Flipper-Folly (~> 2.2)
+ - Flipper-RSocket (~> 1.1)
- Flipper-DoubleConversion (1.1.7)
- Flipper-Folly (2.2.0):
- boost-for-react-native
@@ -95,76 +101,76 @@ PODS:
- Flipper-PeerTalk (0.0.4)
- Flipper-RSocket (1.1.0):
- Flipper-Folly (~> 2.2)
- - FlipperKit (0.33.1):
- - FlipperKit/Core (= 0.33.1)
- - FlipperKit/Core (0.33.1):
- - Flipper (~> 0.33.1)
+ - FlipperKit (0.41.5):
+ - FlipperKit/Core (= 0.41.5)
+ - FlipperKit/Core (0.41.5):
+ - Flipper (~> 0.41.5)
- FlipperKit/CppBridge
- FlipperKit/FBCxxFollyDynamicConvert
- FlipperKit/FBDefines
- FlipperKit/FKPortForwarding
- - FlipperKit/CppBridge (0.33.1):
- - Flipper (~> 0.33.1)
- - FlipperKit/FBCxxFollyDynamicConvert (0.33.1):
- - Flipper-Folly (~> 2.1)
- - FlipperKit/FBDefines (0.33.1)
- - FlipperKit/FKPortForwarding (0.33.1):
+ - FlipperKit/CppBridge (0.41.5):
+ - Flipper (~> 0.41.5)
+ - FlipperKit/FBCxxFollyDynamicConvert (0.41.5):
+ - Flipper-Folly (~> 2.2)
+ - FlipperKit/FBDefines (0.41.5)
+ - FlipperKit/FKPortForwarding (0.41.5):
- CocoaAsyncSocket (~> 7.6)
- Flipper-PeerTalk (~> 0.0.4)
- - FlipperKit/FlipperKitHighlightOverlay (0.33.1)
- - FlipperKit/FlipperKitLayoutPlugin (0.33.1):
+ - FlipperKit/FlipperKitHighlightOverlay (0.41.5)
+ - FlipperKit/FlipperKitLayoutPlugin (0.41.5):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutTextSearchable
- YogaKit (~> 1.18)
- - FlipperKit/FlipperKitLayoutTextSearchable (0.33.1)
- - FlipperKit/FlipperKitNetworkPlugin (0.33.1):
+ - FlipperKit/FlipperKitLayoutTextSearchable (0.41.5)
+ - FlipperKit/FlipperKitNetworkPlugin (0.41.5):
- FlipperKit/Core
- - FlipperKit/FlipperKitReactPlugin (0.33.1):
+ - FlipperKit/FlipperKitReactPlugin (0.41.5):
- FlipperKit/Core
- - FlipperKit/FlipperKitUserDefaultsPlugin (0.33.1):
+ - FlipperKit/FlipperKitUserDefaultsPlugin (0.41.5):
- FlipperKit/Core
- - FlipperKit/SKIOSNetworkPlugin (0.33.1):
+ - FlipperKit/SKIOSNetworkPlugin (0.41.5):
- FlipperKit/Core
- FlipperKit/FlipperKitNetworkPlugin
- - Folly (2018.10.22.00):
+ - Folly (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
- - Folly/Default (= 2018.10.22.00)
+ - Folly/Default (= 2020.01.13.00)
- glog
- - Folly/Default (2018.10.22.00):
+ - Folly/Default (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
- glog
- glog (0.3.5)
- - GoogleAppMeasurement (6.5.0):
+ - 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.1.0)
- - GoogleDataTransportCCTSupport (3.1.0):
+ - GoogleDataTransport (6.2.1)
+ - GoogleDataTransportCCTSupport (3.2.0):
- GoogleDataTransport (~> 6.1)
- nanopb (~> 1.30905.0)
- - GoogleUtilities/AppDelegateSwizzler (6.6.0):
+ - GoogleUtilities/AppDelegateSwizzler (6.7.1):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- - GoogleUtilities/Environment (6.6.0):
+ - GoogleUtilities/Environment (6.7.1):
- PromisesObjC (~> 1.2)
- - GoogleUtilities/Logger (6.6.0):
+ - GoogleUtilities/Logger (6.7.1):
- GoogleUtilities/Environment
- - GoogleUtilities/MethodSwizzler (6.6.0):
+ - GoogleUtilities/MethodSwizzler (6.7.1):
- GoogleUtilities/Logger
- - GoogleUtilities/Network (6.6.0):
+ - GoogleUtilities/Network (6.7.1):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability
- - "GoogleUtilities/NSData+zlib (6.6.0)"
- - GoogleUtilities/Reachability (6.6.0):
+ - "GoogleUtilities/NSData+zlib (6.7.1)"
+ - GoogleUtilities/Reachability (6.7.1):
- GoogleUtilities/Logger
- - GoogleUtilities/UserDefaults (6.6.0):
+ - GoogleUtilities/UserDefaults (6.7.1):
- GoogleUtilities/Logger
- JitsiMeetSDK (2.8.1)
- KeyCommands (2.0.3):
@@ -186,177 +192,180 @@ PODS:
- OpenSSL-Universal (1.0.2.19):
- OpenSSL-Universal/Static (= 1.0.2.19)
- OpenSSL-Universal/Static (1.0.2.19)
- - PromisesObjC (1.2.8)
- - RCTRequired (0.62.2)
- - RCTTypeSafety (0.62.2):
- - FBLazyVector (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTRequired (= 0.62.2)
- - React-Core (= 0.62.2)
- - React (0.62.2):
- - React-Core (= 0.62.2)
- - React-Core/DevSupport (= 0.62.2)
- - React-Core/RCTWebSocket (= 0.62.2)
- - React-RCTActionSheet (= 0.62.2)
- - React-RCTAnimation (= 0.62.2)
- - React-RCTBlob (= 0.62.2)
- - React-RCTImage (= 0.62.2)
- - React-RCTLinking (= 0.62.2)
- - React-RCTNetwork (= 0.62.2)
- - React-RCTSettings (= 0.62.2)
- - React-RCTText (= 0.62.2)
- - React-RCTVibration (= 0.62.2)
- - React-Core (0.62.2):
- - Folly (= 2018.10.22.00)
+ - PromisesObjC (1.2.9)
+ - RCTRequired (0.63.1)
+ - RCTTypeSafety (0.63.1):
+ - FBLazyVector (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - RCTRequired (= 0.63.1)
+ - React-Core (= 0.63.1)
+ - React (0.63.1):
+ - React-Core (= 0.63.1)
+ - React-Core/DevSupport (= 0.63.1)
+ - React-Core/RCTWebSocket (= 0.63.1)
+ - React-RCTActionSheet (= 0.63.1)
+ - React-RCTAnimation (= 0.63.1)
+ - React-RCTBlob (= 0.63.1)
+ - React-RCTImage (= 0.63.1)
+ - React-RCTLinking (= 0.63.1)
+ - React-RCTNetwork (= 0.63.1)
+ - React-RCTSettings (= 0.63.1)
+ - React-RCTText (= 0.63.1)
+ - React-RCTVibration (= 0.63.1)
+ - React-callinvoker (0.63.1)
+ - React-Core (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- - React-Core/Default (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-Core/Default (= 0.63.1)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/CoreModulesHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/CoreModulesHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/Default (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/Default (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/DevSupport (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/DevSupport (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- - React-Core/Default (= 0.62.2)
- - React-Core/RCTWebSocket (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
- - React-jsinspector (= 0.62.2)
+ - React-Core/Default (= 0.63.1)
+ - React-Core/RCTWebSocket (= 0.63.1)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
+ - React-jsinspector (= 0.63.1)
- Yoga
- - React-Core/RCTActionSheetHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTActionSheetHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTAnimationHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTAnimationHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTBlobHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTBlobHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTImageHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTImageHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTLinkingHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTLinkingHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTNetworkHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTNetworkHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTSettingsHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTSettingsHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTTextHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTTextHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTVibrationHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTVibrationHeaders (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-Core/RCTWebSocket (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTWebSocket (0.63.1):
+ - Folly (= 2020.01.13.00)
- glog
- - React-Core/Default (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-Core/Default (= 0.63.1)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsiexecutor (= 0.63.1)
- Yoga
- - React-CoreModules (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/CoreModulesHeaders (= 0.62.2)
- - React-RCTImage (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-cxxreact (0.62.2):
+ - React-CoreModules (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.1)
+ - React-Core/CoreModulesHeaders (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-RCTImage (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - React-cxxreact (0.63.1):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-jsinspector (= 0.62.2)
- - React-jsi (0.62.2):
+ - React-callinvoker (= 0.63.1)
+ - React-jsinspector (= 0.63.1)
+ - React-jsi (0.63.1):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-jsi/Default (= 0.62.2)
- - React-jsi/Default (0.62.2):
+ - React-jsi/Default (= 0.63.1)
+ - React-jsi/Default (0.63.1):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-jsiexecutor (0.62.2):
+ - React-jsiexecutor (0.63.1):
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsinspector (0.62.2)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-jsinspector (0.63.1)
- react-native-appearance (0.3.4):
- React
- react-native-background-timer (2.2.0):
- React
- - react-native-cameraroll (1.6.0):
+ - react-native-cameraroll (4.0.0):
- React
- - react-native-document-picker (3.3.3):
+ - react-native-document-picker (3.5.3):
- React
- react-native-jitsi-meet (2.1.1):
- JitsiMeetSDK (= 2.8.1)
@@ -365,71 +374,72 @@ PODS:
- React
- react-native-orientation-locker (1.1.8):
- React
- - react-native-safe-area-context (3.0.2):
+ - react-native-safe-area-context (3.1.1):
- React
- - react-native-slider (2.0.9):
+ - react-native-slider (3.0.2):
- React
- - react-native-webview (9.4.0):
+ - react-native-webview (10.3.2):
- React
- - React-RCTActionSheet (0.62.2):
- - React-Core/RCTActionSheetHeaders (= 0.62.2)
- - React-RCTAnimation (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTAnimationHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTBlob (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - React-Core/RCTBlobHeaders (= 0.62.2)
- - React-Core/RCTWebSocket (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-RCTNetwork (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTImage (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTImageHeaders (= 0.62.2)
- - React-RCTNetwork (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTLinking (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - React-Core/RCTLinkingHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTNetwork (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTNetworkHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTSettings (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTSettingsHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTText (0.62.2):
- - React-Core/RCTTextHeaders (= 0.62.2)
- - React-RCTVibration (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - React-Core/RCTVibrationHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - ReactCommon/callinvoker (0.62.2):
+ - React-RCTActionSheet (0.63.1):
+ - React-Core/RCTActionSheetHeaders (= 0.63.1)
+ - React-RCTAnimation (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.1)
+ - React-Core/RCTAnimationHeaders (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - React-RCTBlob (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - React-Core/RCTBlobHeaders (= 0.63.1)
+ - React-Core/RCTWebSocket (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-RCTNetwork (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - React-RCTImage (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.1)
+ - React-Core/RCTImageHeaders (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - React-RCTNetwork (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - React-RCTLinking (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - React-Core/RCTLinkingHeaders (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - React-RCTNetwork (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.1)
+ - React-Core/RCTNetworkHeaders (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - React-RCTSettings (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.1)
+ - React-Core/RCTSettingsHeaders (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - React-RCTText (0.63.1):
+ - React-Core/RCTTextHeaders (= 0.63.1)
+ - React-RCTVibration (0.63.1):
+ - FBReactNativeSpec (= 0.63.1)
+ - Folly (= 2020.01.13.00)
+ - React-Core/RCTVibrationHeaders (= 0.63.1)
+ - React-jsi (= 0.63.1)
+ - ReactCommon/turbomodule/core (= 0.63.1)
+ - ReactCommon/turbomodule/core (0.63.1):
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-cxxreact (= 0.62.2)
- - ReactCommon/turbomodule/core (0.62.2):
- - DoubleConversion
- - Folly (= 2018.10.22.00)
- - glog
- - React-Core (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - ReactCommon/callinvoker (= 0.62.2)
+ - React-callinvoker (= 0.63.1)
+ - React-Core (= 0.63.1)
+ - React-cxxreact (= 0.63.1)
+ - React-jsi (= 0.63.1)
- ReactNativeART (1.2.0):
- React
- ReactNativeKeyboardInput (6.0.0):
@@ -442,27 +452,30 @@ PODS:
- React-Core
- RNBootSplash (2.2.5):
- React
- - RNCAsyncStorage (1.10.3):
+ - RNCAsyncStorage (1.11.0):
- React
- RNCMaskedView (0.1.10):
- React
- - RNDateTimePicker (2.3.2):
+ - RNDateTimePicker (2.6.0):
- React
- - RNDeviceInfo (5.5.7):
+ - RNDeviceInfo (5.6.2):
- React
- RNFastImage (8.1.5):
- React
- SDWebImage (~> 5.0)
- SDWebImageWebPCoder (~> 0.4.1)
- - RNFirebase (5.6.0):
- - Firebase/Core
+ - RNFBAnalytics (7.3.1):
+ - Firebase/Analytics (~> 6.27.0)
- React
- - RNFirebase/Crashlytics (= 5.6.0)
- - RNFirebase/Crashlytics (5.6.0):
- - Crashlytics
- - Fabric
- - Firebase/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
- RNGestureHandler (1.6.1):
- React
- RNImageCropPicker (0.31.1):
@@ -476,40 +489,40 @@ PODS:
- TOCropViewController
- RNLocalize (1.4.0):
- React
- - RNReanimated (1.8.0):
+ - RNReanimated (1.9.0):
- React
- RNRootView (1.0.3):
- React
- - RNScreens (2.7.0):
+ - RNScreens (2.9.0):
- React
- RNUserDefaults (1.8.1):
- React
- - RNVectorIcons (6.6.0):
+ - RNVectorIcons (7.0.0):
- React
- - SDWebImage (5.8.0):
- - SDWebImage/Core (= 5.8.0)
- - SDWebImage/Core (5.8.0)
+ - SDWebImage (5.8.4):
+ - SDWebImage/Core (= 5.8.4)
+ - SDWebImage/Core (5.8.4)
- SDWebImageWebPCoder (0.4.1):
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.5)
- - TOCropViewController (2.5.2)
- - UMAppLoader (1.0.2)
- - UMBarCodeScannerInterface (5.1.0)
- - UMCameraInterface (5.1.0)
- - UMConstantsInterface (5.1.0)
- - UMCore (5.1.2)
- - UMFaceDetectorInterface (5.1.0)
- - UMFileSystemInterface (5.1.0)
- - UMFontInterface (5.1.0)
- - UMImageLoaderInterface (5.1.0)
- - UMPermissionsInterface (5.1.0):
+ - TOCropViewController (2.5.3)
+ - UMAppLoader (1.2.0)
+ - UMBarCodeScannerInterface (5.2.1)
+ - UMCameraInterface (5.2.1)
+ - UMConstantsInterface (5.2.1)
+ - UMCore (5.3.0)
+ - UMFaceDetectorInterface (5.2.1)
+ - UMFileSystemInterface (5.2.1)
+ - UMFontInterface (5.2.1)
+ - UMImageLoaderInterface (5.2.1)
+ - UMPermissionsInterface (5.2.1):
- UMCore
- - UMReactNativeAdapter (5.2.0):
+ - UMReactNativeAdapter (5.4.0):
- React-Core
- UMCore
- UMFontInterface
- - UMSensorsInterface (5.1.0)
- - UMTaskManagerInterface (5.1.0)
+ - UMSensorsInterface (5.2.1)
+ - UMTaskManagerInterface (5.2.1)
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
@@ -530,32 +543,32 @@ DEPENDENCIES:
- EXWebBrowser (from `../node_modules/expo-web-browser/ios`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
- - Flipper (~> 0.33.1)
+ - Flipper (~> 0.41.1)
- Flipper-DoubleConversion (= 1.1.7)
- - Flipper-Folly (~> 2.1)
+ - Flipper-Folly (~> 2.2)
- Flipper-Glog (= 0.3.6)
- Flipper-PeerTalk (~> 0.0.4)
- - Flipper-RSocket (~> 1.0)
- - FlipperKit (~> 0.33.1)
- - FlipperKit/Core (~> 0.33.1)
- - FlipperKit/CppBridge (~> 0.33.1)
- - FlipperKit/FBCxxFollyDynamicConvert (~> 0.33.1)
- - FlipperKit/FBDefines (~> 0.33.1)
- - FlipperKit/FKPortForwarding (~> 0.33.1)
- - FlipperKit/FlipperKitHighlightOverlay (~> 0.33.1)
- - FlipperKit/FlipperKitLayoutPlugin (~> 0.33.1)
- - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.33.1)
- - FlipperKit/FlipperKitNetworkPlugin (~> 0.33.1)
- - FlipperKit/FlipperKitReactPlugin (~> 0.33.1)
- - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.33.1)
- - FlipperKit/SKIOSNetworkPlugin (~> 0.33.1)
+ - Flipper-RSocket (~> 1.1)
+ - FlipperKit (~> 0.41.1)
+ - FlipperKit/Core (~> 0.41.1)
+ - FlipperKit/CppBridge (~> 0.41.1)
+ - FlipperKit/FBCxxFollyDynamicConvert (~> 0.41.1)
+ - FlipperKit/FBDefines (~> 0.41.1)
+ - FlipperKit/FKPortForwarding (~> 0.41.1)
+ - FlipperKit/FlipperKitHighlightOverlay (~> 0.41.1)
+ - FlipperKit/FlipperKitLayoutPlugin (~> 0.41.1)
+ - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.41.1)
+ - FlipperKit/FlipperKitNetworkPlugin (~> 0.41.1)
+ - FlipperKit/FlipperKitReactPlugin (~> 0.41.1)
+ - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.41.1)
+ - FlipperKit/SKIOSNetworkPlugin (~> 0.41.1)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- - JitsiMeetSDK (from `https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git`)
- KeyCommands (from `../node_modules/react-native-keycommands`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
+ - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/DevSupport (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
@@ -583,7 +596,6 @@ DEPENDENCIES:
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- - ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "ReactNativeART (from `../node_modules/@react-native-community/art`)"
- ReactNativeKeyboardInput (from `../node_modules/react-native-keyboard-input`)
@@ -596,7 +608,9 @@ DEPENDENCIES:
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
- RNDeviceInfo (from `../node_modules/react-native-device-info`)
- "RNFastImage (from `../node_modules/@rocket.chat/react-native-fast-image`)"
- - RNFirebase (from `../node_modules/react-native-firebase/ios`)
+ - "RNFBAnalytics (from `../node_modules/@react-native-firebase/analytics`)"
+ - "RNFBApp (from `../node_modules/@react-native-firebase/app`)"
+ - "RNFBCrashlytics (from `../node_modules/@react-native-firebase/crashlytics`)"
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- RNLocalize (from `../node_modules/react-native-localize`)
@@ -625,13 +639,11 @@ SPEC REPOS:
- boost-for-react-native
- CocoaAsyncSocket
- CocoaLibEvent
- - Crashlytics
- - Fabric
- Firebase
- FirebaseAnalytics
- FirebaseCore
- FirebaseCoreDiagnostics
- - FirebaseCoreDiagnosticsInterop
+ - FirebaseCrashlytics
- FirebaseInstallations
- Flipper
- Flipper-DoubleConversion
@@ -644,6 +656,7 @@ SPEC REPOS:
- GoogleDataTransport
- GoogleDataTransportCCTSupport
- GoogleUtilities
+ - JitsiMeetSDK
- libwebp
- nanopb
- OpenSSL-Universal
@@ -688,8 +701,6 @@ EXTERNAL SOURCES:
:podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
glog:
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
- JitsiMeetSDK:
- :git: https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git
KeyCommands:
:path: "../node_modules/react-native-keycommands"
RCTRequired:
@@ -698,6 +709,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/TypeSafety"
React:
:path: "../node_modules/react-native/"
+ React-callinvoker:
+ :path: "../node_modules/react-native/ReactCommon/callinvoker"
React-Core:
:path: "../node_modules/react-native/"
React-CoreModules:
@@ -772,8 +785,12 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-device-info"
RNFastImage:
:path: "../node_modules/@rocket.chat/react-native-fast-image"
- RNFirebase:
- :path: "../node_modules/react-native-firebase/ios"
+ RNFBAnalytics:
+ :path: "../node_modules/@react-native-firebase/analytics"
+ RNFBApp:
+ :path: "../node_modules/@react-native-firebase/app"
+ RNFBCrashlytics:
+ :path: "../node_modules/@react-native-firebase/crashlytics"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNImageCropPicker:
@@ -819,125 +836,121 @@ EXTERNAL SOURCES:
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
-CHECKOUT OPTIONS:
- JitsiMeetSDK:
- :commit: 9177aaa3afb379e17cc687887485e91e5cd24a49
- :git: https://github.com/RocketChat/jitsi-meet-ios-sdk-releases.git
-
SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
- BugsnagReactNative: 14c1b59cfbf34fd5591b734bfec65a277b677ef8
+ BugsnagReactNative: 98fb350df4bb0c94cce903023531a1a5cc11fa51
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
- Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df
- DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
+ DoubleConversion: cde416483dac037923206447da6e1454df403714
EXAppleAuthentication: 5b3da71bada29e2423d8ea27e5538ef0d75aba62
- EXAV: 2edd9cd30fe98d04b55325303c7ff01db02d1d0f
- EXConstants: 5304709b1bea70a4828f48ba4c7fc3ec3b2d9b17
- EXFileSystem: cf4232ba7c62dc49b78c2d36005f97b6fddf0b01
- EXHaptics: 013b5065946d4dd7b46ea547b58072d45a206dbd
- EXImageLoader: 5ad6896fa1ef2ee814b551873cbf7a7baccc694a
- EXKeepAwake: d045bc2cf1ad5a04f0323cc7c894b95b414042e0
- EXLocalAuthentication: bbf1026cc289d729da4f29240dd7a8f6a14e4b20
- EXPermissions: 24b97f734ce9172d245a5be38ad9ccfcb6135964
- EXVideoThumbnails: be6984a3cda1e44c45b5c6278244e99855f99a0a
- EXWebBrowser: 5902f99ac5ac551e5c82ff46f13a337b323aa9ea
- Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74
- FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
- FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
- Firebase: b28e55c60efd98963cd9011fe2fac5a10c2ba124
- FirebaseAnalytics: 7386fc2176e3f93ad8ef34b5b1f2b33a891e4962
- FirebaseCore: e610482f64097b0e9f056cd97bc6b33dfabcbb6a
- FirebaseCoreDiagnostics: 4a773a47bd83bbd5a9b1ccf1ce7caa8b2d535e67
- FirebaseCoreDiagnosticsInterop: 296e2c5f5314500a850ad0b83e9e7c10b011a850
- FirebaseInstallations: 2119fb3e46b0a88bfdbf12562f855ee3252462fa
- Flipper: 6c1f484f9a88d30ab3e272800d53688439e50f69
+ EXAV: 86344030966e0da7e00556fbb97269d9ad16071d
+ EXConstants: f907b3b6ce16e20d1750f22af1e095e924574bcb
+ EXFileSystem: 76875135b61708b9afa7e6a89b72a60ba0fdfa20
+ EXHaptics: 5428b344a216ca5d9df6ca8f65720b2a1ad9f109
+ EXImageLoader: 02ca02c9cd5cc8a97b423207a73a791e0a86bea5
+ EXKeepAwake: 8b0f68242f036b971f9f8976341823cbe6f50812
+ EXLocalAuthentication: 985c65e08a6eb84f8f98b51f7435df138b18b9e8
+ EXPermissions: 80ac3acbdb145930079810fe5b08c022b3428aa8
+ EXVideoThumbnails: f70bdc5511749f3181028f5000bcb7be203c631d
+ EXWebBrowser: d37a5ffdea1b65947352bc001dd9f732463725d4
+ FBLazyVector: a50434c875bd42f2b1c99c712bda892a1dc659c7
+ FBReactNativeSpec: 393853a536428e05a9da00b6290042f09809b15b
+ Firebase: 919186c8e119dd9372a45fd1dd17a8a942bc1892
+ FirebaseAnalytics: 5fa308e1b13f838d0f6dc74719ac2a72e8c5afc4
+ FirebaseCore: 8cd4f8ea22075e0ee582849b1cf79d8816506085
+ FirebaseCoreDiagnostics: 4505e4d4009b1d93f605088ee7d7764d5f0d1c84
+ FirebaseCrashlytics: 7d0fa02ea8842cc4b2ab07b0735edafde1ad77d6
+ FirebaseInstallations: 3c520c951305cbf9ca54eb891ff9e6d1fd384881
+ Flipper: 33585e2d9810fe5528346be33bcf71b37bb7ae13
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
- FlipperKit: 6dc9b8f4ef60d9e5ded7f0264db299c91f18832e
- Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
- glog: 1f3da668190260b06b429bb211bfbee5cd790c28
- GoogleAppMeasurement: 4c644d86835d827bab30ab6aabb9ecaf1f500735
- GoogleDataTransport: f6f8eba931df03ebd2232ff4645aa85f8f47b5ab
- GoogleDataTransportCCTSupport: d70a561f7d236af529fee598835caad5e25f6d3d
- GoogleUtilities: 39530bc0ad980530298e9c4af8549e991fd033b1
+ FlipperKit: bc68102cd4952a258a23c9c1b316c7bec1fecf83
+ Folly: b73c3869541e86821df3c387eb0af5f65addfab4
+ glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
+ GoogleAppMeasurement: 8cd1f289d60e629cf16ab03363b9e89c776b9651
+ GoogleDataTransport: 9a8a16f79feffc7f42096743de2a7c4815e84020
+ GoogleDataTransportCCTSupport: 489c1265d2c85b68187a83a911913d190012158d
+ GoogleUtilities: e121a3867449ce16b0e35ddf1797ea7a389ffdf2
JitsiMeetSDK: 2984eac1343690bf1c0c72bde75b48b0148d0f79
KeyCommands: f66c535f698ed14b3d3a4e58859d79a827ea907e
libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3
nanopb: c43f40fadfe79e8b8db116583945847910cbabc9
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
- PromisesObjC: c119f3cd559f50b7ae681fa59dc1acd19173b7e6
- RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
- RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
- React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3
- React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05
- React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0
- React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103
- React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
- React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
- React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
+ PromisesObjC: b48e0338dbbac2207e611750777895f7a5811b75
+ RCTRequired: d9b1a9e6fa097744ca3ede59f86a35096df7202b
+ RCTTypeSafety: c227cd061983e9e964115afbc4e8730d6a6f1395
+ React: 86e972a20967ee4137aa19dc48319405927c2e94
+ React-callinvoker: 87ee376c25277d74e164ff036b27084e343f3e69
+ React-Core: f5ec03baf7ed58d9f3ee04a8f84e4c97ee8bf4c9
+ React-CoreModules: 958898aa8c069280e866e35a2f29480a81fcf335
+ React-cxxreact: 90de76b9b51575668ad7fd4e33a5a8c143beecc2
+ React-jsi: b32a31da32e030f30bbf9a8d3a9c8325df9e793f
+ React-jsiexecutor: 7ab9cdcdd18d57652fb041f8a147fe9658d4e00a
+ React-jsinspector: 2e28bb487e42dda6c94dbfa0c648d1343767a0fb
react-native-appearance: 0f0e5fc2fcef70e03d48c8fe6b00b9158c2ba8aa
react-native-background-timer: 1f7d560647b40e6a60b01c452ba29c54bf581fc4
- react-native-cameraroll: 02e60e9af9273a3cc3b641632bf651189830aaf8
- react-native-document-picker: dd96ce05bf1453b110d7a3912097bf6d298d2cb6
+ react-native-cameraroll: ae0a7c0cc8462508855707ff623b1e789b692865
+ react-native-document-picker: 825552b827012282baf4b7cbf119d3b37a280c90
react-native-jitsi-meet: f89bcb2cfbd5b15403b9c40738036c4f1af45d05
react-native-notifications: ee8fd739853e72694f3af8b374c8ccb106b7b227
react-native-orientation-locker: f0ca1a8e5031dab6b74bfb4ab33a17ed2c2fcb0d
- react-native-safe-area-context: b11a34881faac509cad5578726c98161ad4d275c
- react-native-slider: e51492f1264d882a8815b71c5870f8978e52887d
- react-native-webview: cf5527893252b3b036eea024a1da6996f7344c74
- React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
- React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
- React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
- React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3
- React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2
- React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44
- React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a
- React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
- React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
- ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
+ react-native-safe-area-context: 344b969c45af3d8464d36e8dea264942992ef033
+ react-native-slider: 0221b417686c5957f6e77cd9ac22c1478a165355
+ react-native-webview: 679b6f400176e2ea8a785acf7ae16cf282e7d1eb
+ React-RCTActionSheet: 1702a1a85e550b5c36e2e03cb2bd3adea053de95
+ React-RCTAnimation: ddda576010a878865a4eab83a78acd92176ef6a1
+ React-RCTBlob: 34334384284c81577409d5205bd2b9ff594d8ab6
+ React-RCTImage: e2a661266dca295cffb33909cc64675a2efedb26
+ React-RCTLinking: cd39b9b5e9cbb9e827854e30dfa92d7db074cea8
+ React-RCTNetwork: 16939b7e4058d6f662b304a1f61689e249a2bfcc
+ React-RCTSettings: 24726a62de0c326f9ebfc3838898a501b87ce711
+ React-RCTText: 4f95d322b7e6da72817284abf8a2cdcec18b9cd8
+ React-RCTVibration: f3a9123c244f35c40d3c9f3ec3f0b9e5717bb292
+ ReactCommon: 2905859f84a94a381bb0d8dd3921ccb1a0047cb8
ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab
ReactNativeKeyboardInput: c37e26821519869993b3b61844350feb9177ff37
ReactNativeKeyboardTrackingView: 02137fac3b2ebd330d74fa54ead48b14750a2306
rn-extensions-share: 8db79372089567cbc5aefe8444869bbc808578d3
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNBootSplash: b3836aa90c5bec690c6cd3c9ab355fcf98d0fe1d
- RNCAsyncStorage: cd7234ff15c010723ed7c499ae4eedc605eac1fd
+ RNCAsyncStorage: d059c3ee71738c39834a627476322a5a8cd5bf36
RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f
- RNDateTimePicker: 4bd49e09f91ca73d69119a9e1173b0d43b82f5e5
- RNDeviceInfo: e2102056bde3ad5d137fd029d8d431510a00486a
+ RNDateTimePicker: e386ff4ef3300964ed0cad97ce6f206e0effbfdb
+ RNDeviceInfo: ed8557a8bd6443cbc0ab5d375e6808a38a279744
RNFastImage: 35ae972d6727c84ee3f5c6897e07f84d0a3445e9
- RNFirebase: 37daa9a346d070f9f6ee1f3b4aaf4c8e3b1d5d1c
+ RNFBAnalytics: dae6d7b280ba61c96e1bbdd34aca3154388f025e
+ RNFBApp: 6fd8a7e757135d4168bf033a8812c241af7363a0
+ RNFBCrashlytics: 88de72c2476b5868a892d9523b89b86c527c540e
RNGestureHandler: 8f09cd560f8d533eb36da5a6c5a843af9f056b38
RNImageCropPicker: 38865ab4af1b0b2146ad66061196bc0184946855
RNLocalize: b6df30cc25ae736d37874f9bce13351db2f56796
- RNReanimated: 955cf4068714003d2f1a6e2bae3fb1118f359aff
+ RNReanimated: b5ccb50650ba06f6e749c7c329a1bc3ae0c88b43
RNRootView: 895a4813dedeaca82db2fa868ca1c333d790e494
- RNScreens: cf198f915f8a2bf163de94ca9f5bfc8d326c3706
+ RNScreens: c526239bbe0e957b988dacc8d75ac94ec9cb19da
RNUserDefaults: c421fd97ad06b35c16608c5d0fe675db353f632d
- RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
- SDWebImage: 84000f962cbfa70c07f19d2234cbfcf5d779b5dc
+ RNVectorIcons: da6fe858f5a65d7bbc3379540a889b0b12aa5976
+ SDWebImage: cf6922231e95550934da2ada0f20f2becf2ceba9
SDWebImageWebPCoder: 36f8f47bd9879a8aea6044765c1351120fd8e3a8
- TOCropViewController: e9da34f484aedd4e5d5a8ab230ba217cfe16c729
- UMAppLoader: ee77a072f9e15128f777ccd6d2d00f52ab4387e6
- UMBarCodeScannerInterface: 9dc692b87e5f20fe277fa57aa47f45d418c3cc6c
- UMCameraInterface: 625878bbf2ba188a8548675e1d1d2e438a653e6d
- UMConstantsInterface: 64060cf86587bcd90b1dbd804cceb6d377a308c1
- UMCore: eb200e882eadafcd31ead290770835fd648c0945
- UMFaceDetectorInterface: d6677d6ddc9ab95a0ca857aa7f8ba76656cc770f
- UMFileSystemInterface: c70ea7147198b9807080f3597f26236be49b0165
- UMFontInterface: d9d3b27af698c5389ae9e20b99ef56a083f491fb
- UMImageLoaderInterface: 14dd2c46c67167491effc9e91250e9510f12709e
- UMPermissionsInterface: 5e83a9167c177e4a0f0a3539345983cc749efb3e
- UMReactNativeAdapter: 126da3486c1a1f11945b649d557d6c2ebb9407b2
- UMSensorsInterface: 48941f70175e2975af1a9386c6d6cb16d8126805
- UMTaskManagerInterface: cb890c79c63885504ddc0efd7a7d01481760aca2
- Yoga: 3ebccbdd559724312790e7742142d062476b698e
+ TOCropViewController: 20a14b6a7a098308bf369e7c8d700dc983a974e6
+ UMAppLoader: 61049c8d55590b74e9ae1d5429bf68d96b4a2528
+ UMBarCodeScannerInterface: e5e4c87797d3d01214e25cd1618866caf5d4f17f
+ UMCameraInterface: 415ac060034edecacdbbaa739c223e3f276e0056
+ UMConstantsInterface: 1a52f2d884c95e8829439da13e36b7669a1a8fb4
+ UMCore: d98083b522b08c0a8ba3992bc263c624ae5d887c
+ UMFaceDetectorInterface: 67c6c82451338da01a4bc00ec46365a2a8ea9057
+ UMFileSystemInterface: 303d696ede28102a7e11d111808bd2ed2c5eb62f
+ UMFontInterface: 6edf1ee8bc55d2030766f8cf0a7b20a5d5a913b0
+ UMImageLoaderInterface: 9cdbf3bab6a513bddd88505cb2340fe02d6a11c0
+ UMPermissionsInterface: 019170ad655f464e3f8d23d2a8bcbda2e645cde4
+ UMReactNativeAdapter: 538efe92e781b5d7678cf95b34c46f2d0989a557
+ UMSensorsInterface: cb5bf31d52c4349f0ff9e3c049bbe4df0d80d383
+ UMTaskManagerInterface: 80653f25c55d9e6d79d6a0a65589fa213feaee11
+ Yoga: d5bd05a2b6b94c52323745c2c2b64557c8c66f64
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
-PODFILE CHECKSUM: 35d9478dd32cf502959b8efc14411ecf09c66c95
+PODFILE CHECKSUM: 55c04243097892160d63f79f3a23157165b7ac68
-COCOAPODS: 1.8.4
+COCOAPODS: 1.9.3
diff --git a/ios/Pods/Crashlytics/Crashlytics.framework/README b/ios/Pods/Crashlytics/Crashlytics.framework/README
deleted file mode 100644
index 3ebf76719..000000000
--- a/ios/Pods/Crashlytics/Crashlytics.framework/README
+++ /dev/null
@@ -1 +0,0 @@
-We've now combined all our supported platforms into a single podspec. As a result, we moved our submit script to a new location for Cocoapods projects: ${PODS_ROOT}/Crashlytics/submit. To avoid breaking functionality that references the old location of the submit, we've placed this dummy script that calls to the correct location, while providing a helpful warning if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning!
diff --git a/ios/Pods/Crashlytics/Crashlytics.framework/submit b/ios/Pods/Crashlytics/Crashlytics.framework/submit
deleted file mode 100755
index b7de4e377..000000000
--- a/ios/Pods/Crashlytics/Crashlytics.framework/submit
+++ /dev/null
@@ -1,6 +0,0 @@
-if [[ -z $PODS_ROOT ]]; then
-echo "error: The submit binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Crashlytics/submit'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the submit binary to fix this issue."
-else
-echo "warning: The submit script is now located at '$"{"PODS_ROOT"}"/Crashlytics/submit'. To remove this warning, update your path to point to this new location."
-sh "${PODS_ROOT}/Crashlytics/submit" "$@"
-fi
diff --git a/ios/Pods/Crashlytics/README.md b/ios/Pods/Crashlytics/README.md
deleted file mode 100644
index 996af5078..000000000
--- a/ios/Pods/Crashlytics/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-
-# Crashlytics
-
-## Overview
-
-[Crashlytics](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) offers the most powerful, yet lightest weight crash reporting solution for iOS.
-
-
-## Setup
-
-To start using Crashlytics, there are two options:
-
-1) The recommended way is to go to the [Firebase Crashlytics Docs](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) and follow the directions there.
-
-2) If you aren't using Firebase yet, go to [Fabric Kits](https://fabric.io/kits), and follow the directions for Crashlytics.
-
-
-## Resources
-
-* [API Reference](https://firebase.google.com/docs/reference/ios/crashlytics/api/reference/Classes)
-* [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
-* [Website](https://firebase.google.com/docs/crashlytics)
-* Follow us on Twitter: [@crashlytics](https://twitter.com/crashlytics)
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics
deleted file mode 100755
index 214fb9051..000000000
Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics and /dev/null differ
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h
deleted file mode 100644
index 6ec011d93..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// ANSCompatibility.h
-// AnswersKit
-//
-// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
-//
-
-#pragma once
-
-#if !__has_feature(nullability)
-#define nonnull
-#define nullable
-#define _Nullable
-#define _Nonnull
-#endif
-
-#ifndef NS_ASSUME_NONNULL_BEGIN
-#define NS_ASSUME_NONNULL_BEGIN
-#endif
-
-#ifndef NS_ASSUME_NONNULL_END
-#define NS_ASSUME_NONNULL_END
-#endif
-
-#if __has_feature(objc_generics)
-#define ANS_GENERIC_NSARRAY(type) NSArray
-#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
-#else
-#define ANS_GENERIC_NSARRAY(type) NSArray
-#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
-#endif
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h
deleted file mode 100644
index 8deacbee5..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h
+++ /dev/null
@@ -1,210 +0,0 @@
-//
-// Answers.h
-// Crashlytics
-//
-// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
-//
-
-#import
-#import "ANSCompatibility.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * This class exposes the Answers Events API, allowing you to track key
- * user user actions and metrics in your app.
- */
-@interface Answers : NSObject
-
-/**
- * Log a Sign Up event to see users signing up for your app in real-time, understand how
- * many users are signing up with different methods and their success rate signing up.
- *
- * @param signUpMethodOrNil The method by which a user logged in, e.g. Twitter or Digits.
- * @param signUpSucceededOrNil The ultimate success or failure of the login
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logSignUpWithMethod:(nullable NSString *)signUpMethodOrNil
- success:(nullable NSNumber *)signUpSucceededOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log an Log In event to see users logging into your app in real-time, understand how many
- * users are logging in with different methods and their success rate logging into your app.
- *
- * @param loginMethodOrNil The method by which a user logged in, e.g. email, Twitter or Digits.
- * @param loginSucceededOrNil The ultimate success or failure of the login
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logLoginWithMethod:(nullable NSString *)loginMethodOrNil
- success:(nullable NSNumber *)loginSucceededOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Share event to see users sharing from your app in real-time, letting you
- * understand what content they're sharing from the type or genre down to the specific id.
- *
- * @param shareMethodOrNil The method by which a user shared, e.g. email, Twitter, SMS.
- * @param contentNameOrNil The human readable name for this piece of content.
- * @param contentTypeOrNil The type of content shared.
- * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logShareWithMethod:(nullable NSString *)shareMethodOrNil
- contentName:(nullable NSString *)contentNameOrNil
- contentType:(nullable NSString *)contentTypeOrNil
- contentId:(nullable NSString *)contentIdOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log an Invite Event to track how users are inviting other users into
- * your application.
- *
- * @param inviteMethodOrNil The method of invitation, e.g. GameCenter, Twitter, email.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logInviteWithMethod:(nullable NSString *)inviteMethodOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Purchase event to see your revenue in real-time, understand how many users are making purchases, see which
- * items are most popular, and track plenty of other important purchase-related metrics.
- *
- * @param itemPriceOrNil The purchased item's price.
- * @param currencyOrNil The ISO4217 currency code. Example: USD
- * @param purchaseSucceededOrNil Was the purchase successful or unsuccessful
- * @param itemNameOrNil The human-readable form of the item's name. Example:
- * @param itemTypeOrNil The type, or genre of the item. Example: Song
- * @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this purchase.
- */
-+ (void)logPurchaseWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
- currency:(nullable NSString *)currencyOrNil
- success:(nullable NSNumber *)purchaseSucceededOrNil
- itemName:(nullable NSString *)itemNameOrNil
- itemType:(nullable NSString *)itemTypeOrNil
- itemId:(nullable NSString *)itemIdOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Level Start Event to track where users are in your game.
- *
- * @param levelNameOrNil The level name
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this level start event.
- */
-+ (void)logLevelStart:(nullable NSString *)levelNameOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Level End event to track how users are completing levels in your game.
- *
- * @param levelNameOrNil The name of the level completed, E.G. "1" or "Training"
- * @param scoreOrNil The score the user completed the level with.
- * @param levelCompletedSuccesfullyOrNil A boolean representing whether or not the level was completed successfully.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logLevelEnd:(nullable NSString *)levelNameOrNil
- score:(nullable NSNumber *)scoreOrNil
- success:(nullable NSNumber *)levelCompletedSuccesfullyOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log an Add to Cart event to see users adding items to a shopping cart in real-time, understand how
- * many users start the purchase flow, see which items are most popular, and track plenty of other important
- * purchase-related metrics.
- *
- * @param itemPriceOrNil The purchased item's price.
- * @param currencyOrNil The ISO4217 currency code. Example: USD
- * @param itemNameOrNil The human-readable form of the item's name. Example:
- * @param itemTypeOrNil The type, or genre of the item. Example: Song
- * @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logAddToCartWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
- currency:(nullable NSString *)currencyOrNil
- itemName:(nullable NSString *)itemNameOrNil
- itemType:(nullable NSString *)itemTypeOrNil
- itemId:(nullable NSString *)itemIdOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Start Checkout event to see users moving through the purchase funnel in real-time, understand how many
- * users are doing this and how much they're spending per checkout, and see how it related to other important
- * purchase-related metrics.
- *
- * @param totalPriceOrNil The total price of the cart.
- * @param currencyOrNil The ISO4217 currency code. Example: USD
- * @param itemCountOrNil The number of items in the cart.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logStartCheckoutWithPrice:(nullable NSDecimalNumber *)totalPriceOrNil
- currency:(nullable NSString *)currencyOrNil
- itemCount:(nullable NSNumber *)itemCountOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Rating event to see users rating content within your app in real-time and understand what
- * content is most engaging, from the type or genre down to the specific id.
- *
- * @param ratingOrNil The integer rating given by the user.
- * @param contentNameOrNil The human readable name for this piece of content.
- * @param contentTypeOrNil The type of content shared.
- * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logRating:(nullable NSNumber *)ratingOrNil
- contentName:(nullable NSString *)contentNameOrNil
- contentType:(nullable NSString *)contentTypeOrNil
- contentId:(nullable NSString *)contentIdOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Content View event to see users viewing content within your app in real-time and
- * understand what content is most engaging, from the type or genre down to the specific id.
- *
- * @param contentNameOrNil The human readable name for this piece of content.
- * @param contentTypeOrNil The type of content shared.
- * @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logContentViewWithName:(nullable NSString *)contentNameOrNil
- contentType:(nullable NSString *)contentTypeOrNil
- contentId:(nullable NSString *)contentIdOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Search event allows you to see users searching within your app in real-time and understand
- * exactly what they're searching for.
- *
- * @param queryOrNil The user's query.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
- */
-+ (void)logSearchWithQuery:(nullable NSString *)queryOrNil
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-/**
- * Log a Custom Event to see user actions that are uniquely important for your app in real-time, to see how often
- * they're performing these actions with breakdowns by different categories you add. Use a human-readable name for
- * the name of the event, since this is how the event will appear in Answers.
- *
- * @param eventName The human-readable name for the event.
- * @param customAttributesOrNil A dictionary of custom attributes to associate with this event. Attribute keys
- * must be NSString
and values must be NSNumber
or NSString
.
- * @discussion How we treat NSNumbers
:
- * We will provide information about the distribution of values over time.
- *
- * How we treat NSStrings
:
- * NSStrings are used as categorical data, allowing comparison across different category values.
- * Strings are limited to a maximum length of 100 characters, attributes over this length will be
- * truncated.
- *
- * When tracking the Tweet views to better understand user engagement, sending the tweet's length
- * and the type of media present in the tweet allows you to track how tweet length and the type of media influence
- * engagement.
- */
-+ (void)logCustomEventWithName:(NSString *)eventName
- customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h
deleted file mode 100644
index 1526b0dca..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// CLSAttributes.h
-// Crashlytics
-//
-// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
-//
-
-#pragma once
-
-#define CLS_DEPRECATED(x) __attribute__ ((deprecated(x)))
-
-#if !__has_feature(nullability)
- #define nonnull
- #define nullable
- #define _Nullable
- #define _Nonnull
-#endif
-
-#ifndef NS_ASSUME_NONNULL_BEGIN
- #define NS_ASSUME_NONNULL_BEGIN
-#endif
-
-#ifndef NS_ASSUME_NONNULL_END
- #define NS_ASSUME_NONNULL_END
-#endif
-
-#if __has_feature(objc_generics)
- #define CLS_GENERIC_NSARRAY(type) NSArray
- #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
-#else
- #define CLS_GENERIC_NSARRAY(type) NSArray
- #define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
-#endif
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h
deleted file mode 100644
index 59590d546..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// CLSLogging.h
-// Crashlytics
-//
-// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
-//
-#ifdef __OBJC__
-#import "CLSAttributes.h"
-#import
-
-NS_ASSUME_NONNULL_BEGIN
-#endif
-
-
-
-/**
- *
- * The CLS_LOG macro provides as easy way to gather more information in your log messages that are
- * sent with your crash data. CLS_LOG prepends your custom log message with the function name and
- * line number where the macro was used. If your app was built with the DEBUG preprocessor macro
- * defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
- * If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
- *
- * Example output:
- * -[AppDelegate login:] line 134 $ login start
- *
- * If you would like to change this macro, create a new header file, unset our define and then define
- * your own version. Make sure this new header file is imported after the Crashlytics header file.
- *
- * #undef CLS_LOG
- * #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
- *
- **/
-#ifdef __OBJC__
-#ifdef DEBUG
-#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
-#else
-#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
-#endif
-#endif
-
-/**
- *
- * Add logging that will be sent with your crash data. This logging will not show up in the system.log
- * and will only be visible in your Crashlytics dashboard.
- *
- **/
-
-#ifdef __OBJC__
-OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
-OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
-
-/**
- *
- * Add logging that will be sent with your crash data. This logging will show up in the system.log
- * and your Crashlytics dashboard. It is not recommended for Release builds.
- *
- **/
-OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
-OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
-
-
-NS_ASSUME_NONNULL_END
-#endif
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h
deleted file mode 100644
index a8ff3b0b9..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h
+++ /dev/null
@@ -1,103 +0,0 @@
-//
-// CLSReport.h
-// Crashlytics
-//
-// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
-//
-
-#import
-#import "CLSAttributes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- * The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details.
- **/
-@protocol CLSCrashReport
-
-@property (nonatomic, copy, readonly) NSString *identifier;
-@property (nonatomic, copy, readonly) NSDictionary *customKeys;
-@property (nonatomic, copy, readonly) NSString *bundleVersion;
-@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
-@property (nonatomic, readonly, nullable) NSDate *crashedOnDate;
-@property (nonatomic, copy, readonly) NSString *OSVersion;
-@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
-
-@end
-
-/**
- * The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can
- * use this class to get information about the event, and can also set some values after the
- * event has occurred.
- **/
-@interface CLSReport : NSObject
-
-- (instancetype)init NS_UNAVAILABLE;
-+ (instancetype)new NS_UNAVAILABLE;
-
-/**
- * Returns the session identifier for the report.
- **/
-@property (nonatomic, copy, readonly) NSString *identifier;
-
-/**
- * Returns the custom key value data for the report.
- **/
-@property (nonatomic, copy, readonly) NSDictionary *customKeys;
-
-/**
- * Returns the CFBundleVersion of the application that generated the report.
- **/
-@property (nonatomic, copy, readonly) NSString *bundleVersion;
-
-/**
- * Returns the CFBundleShortVersionString of the application that generated the report.
- **/
-@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
-
-/**
- * Returns the date that the report was created.
- **/
-@property (nonatomic, copy, readonly) NSDate *dateCreated;
-
-/**
- * Returns the os version that the application crashed on.
- **/
-@property (nonatomic, copy, readonly) NSString *OSVersion;
-
-/**
- * Returns the os build version that the application crashed on.
- **/
-@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
-
-/**
- * Returns YES if the report contains any crash information, otherwise returns NO.
- **/
-@property (nonatomic, assign, readonly) BOOL isCrash;
-
-/**
- * You can use this method to set, after the event, additional custom keys. The rules
- * and semantics for this method are the same as those documented in Crashlytics.h. Be aware
- * that the maximum size and count of custom keys is still enforced, and you can overwrite keys
- * and/or cause excess keys to be deleted by using this method.
- **/
-- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
-
-/**
- * Record an application-specific user identifier. See Crashlytics.h for details.
- **/
-@property (nonatomic, copy, nullable) NSString * userIdentifier;
-
-/**
- * Record a user name. See Crashlytics.h for details.
- **/
-@property (nonatomic, copy, nullable) NSString * userName;
-
-/**
- * Record a user email. See Crashlytics.h for details.
- **/
-@property (nonatomic, copy, nullable) NSString * userEmail;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h
deleted file mode 100644
index cdb5596cc..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSStackFrame.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// CLSStackFrame.h
-// Crashlytics
-//
-// Copyright 2015 Crashlytics, Inc. All rights reserved.
-//
-
-#import
-#import "CLSAttributes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-/**
- *
- * This class is used in conjunction with -[Crashlytics recordCustomExceptionName:reason:frameArray:] to
- * record information about non-ObjC/C++ exceptions. All information included here will be displayed
- * in the Crashlytics UI, and can influence crash grouping. Be particularly careful with the use of the
- * address property. If set, Crashlytics will attempt symbolication and could overwrite other properities
- * in the process.
- *
- **/
-@interface CLSStackFrame : NSObject
-
-+ (instancetype)stackFrame;
-+ (instancetype)stackFrameWithAddress:(NSUInteger)address;
-+ (instancetype)stackFrameWithSymbol:(NSString *)symbol;
-
-@property (nonatomic, copy, nullable) NSString *symbol;
-@property (nonatomic, copy, nullable) NSString *rawSymbol;
-@property (nonatomic, copy, nullable) NSString *library;
-@property (nonatomic, copy, nullable) NSString *fileName;
-@property (nonatomic, assign) uint32_t lineNumber;
-@property (nonatomic, assign) uint64_t offset;
-@property (nonatomic, assign) uint64_t address;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h
deleted file mode 100644
index 7104ca812..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Crashlytics.h
+++ /dev/null
@@ -1,288 +0,0 @@
-//
-// Crashlytics.h
-// Crashlytics
-//
-// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
-//
-
-#import
-
-#import "CLSAttributes.h"
-#import "CLSLogging.h"
-#import "CLSReport.h"
-#import "CLSStackFrame.h"
-#import "Answers.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@protocol CrashlyticsDelegate;
-
-/**
- * Crashlytics. Handles configuration and initialization of Crashlytics.
- *
- * Note: The Crashlytics class cannot be subclassed. If this is causing you pain for
- * testing, we suggest using either a wrapper class or a protocol extension.
- */
-@interface Crashlytics : NSObject
-
-@property (nonatomic, readonly, copy) NSString *APIKey;
-@property (nonatomic, readonly, copy) NSString *version;
-@property (nonatomic, assign) BOOL debugMode;
-
-/**
- *
- * The delegate can be used to influence decisions on reporting and behavior, as well as reacting
- * to previous crashes.
- *
- * Make certain that the delegate is setup before starting Crashlytics with startWithAPIKey:... or
- * via +[Fabric with:...]. Failure to do will result in missing any delegate callbacks that occur
- * synchronously during start.
- *
- **/
-@property (nonatomic, assign, nullable) id delegate;
-
-/**
- * The recommended way to install Crashlytics into your application is to place a call to +startWithAPIKey:
- * in your -application:didFinishLaunchingWithOptions: or -applicationDidFinishLaunching:
- * method.
- *
- * Note: Starting with 3.0, the submission process has been significantly improved. The delay parameter
- * is no longer required to throttle submissions on launch, performance will be great without it.
- *
- * @param apiKey The Crashlytics API Key for this app
- *
- * @return The singleton Crashlytics instance
- */
-+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey;
-+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey: instead.");
-
-/**
- * If you need the functionality provided by the CrashlyticsDelegate protocol, you can use
- * these convenience methods to activate the framework and set the delegate in one call.
- *
- * @param apiKey The Crashlytics API Key for this app
- * @param delegate A delegate object which conforms to CrashlyticsDelegate.
- *
- * @return The singleton Crashlytics instance
- */
-+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id)delegate;
-+ (Crashlytics *)startWithAPIKey:(NSString *)apiKey delegate:(nullable id)delegate afterDelay:(NSTimeInterval)delay CLS_DEPRECATED("Crashlytics no longer needs or uses the delay parameter. Please use +startWithAPIKey:delegate: instead.");
-
-/**
- * Access the singleton Crashlytics instance.
- *
- * @return The singleton Crashlytics instance
- */
-+ (Crashlytics *)sharedInstance;
-
-/**
- * The easiest way to cause a crash - great for testing!
- */
-- (void)crash;
-
-/**
- * The easiest way to cause a crash with an exception - great for testing.
- */
-- (void)throwException;
-
-/**
- * Specify a user identifier which will be visible in the Crashlytics UI.
- *
- * Many of our customers have requested the ability to tie crashes to specific end-users of their
- * application in order to facilitate responses to support requests or permit the ability to reach
- * out for more information. We allow you to specify up to three separate values for display within
- * the Crashlytics UI - but please be mindful of your end-user's privacy.
- *
- * We recommend specifying a user identifier - an arbitrary string that ties an end-user to a record
- * in your system. This could be a database id, hash, or other value that is meaningless to a
- * third-party observer but can be indexed and queried by you.
- *
- * Optionally, you may also specify the end-user's name or username, as well as email address if you
- * do not have a system that works well with obscured identifiers.
- *
- * Pursuant to our EULA, this data is transferred securely throughout our system and we will not
- * disseminate end-user data unless required to by law. That said, if you choose to provide end-user
- * contact information, we strongly recommend that you disclose this in your application's privacy
- * policy. Data privacy is of our utmost concern.
- *
- * @param identifier An arbitrary user identifier string which ties an end-user to a record in your system.
- */
-- (void)setUserIdentifier:(nullable NSString *)identifier;
-
-/**
- * Specify a user name which will be visible in the Crashlytics UI.
- * Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
- * @see setUserIdentifier:
- *
- * @param name An end user's name.
- */
-- (void)setUserName:(nullable NSString *)name;
-
-/**
- * Specify a user email which will be visible in the Crashlytics UI.
- * Please be mindful of your end-user's privacy and see if setUserIdentifier: can fulfil your needs.
- *
- * @see setUserIdentifier:
- *
- * @param email An end user's email address.
- */
-- (void)setUserEmail:(nullable NSString *)email;
-
-+ (void)setUserIdentifier:(nullable NSString *)identifier CLS_DEPRECATED("Please access this method via +sharedInstance");
-+ (void)setUserName:(nullable NSString *)name CLS_DEPRECATED("Please access this method via +sharedInstance");
-+ (void)setUserEmail:(nullable NSString *)email CLS_DEPRECATED("Please access this method via +sharedInstance");
-
-/**
- * Set a value for a for a key to be associated with your crash data which will be visible in the Crashlytics UI.
- * When setting an object value, the object is converted to a string. This is typically done by calling
- * -[NSObject description].
- *
- * @param value The object to be associated with the key
- * @param key The key with which to associate the value
- */
-- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
-
-/**
- * Set an int value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
- *
- * @param value The integer value to be set
- * @param key The key with which to associate the value
- */
-- (void)setIntValue:(int)value forKey:(NSString *)key;
-
-/**
- * Set an BOOL value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
- *
- * @param value The BOOL value to be set
- * @param key The key with which to associate the value
- */
-- (void)setBoolValue:(BOOL)value forKey:(NSString *)key;
-
-/**
- * Set an float value for a key to be associated with your crash data which will be visible in the Crashlytics UI.
- *
- * @param value The float value to be set
- * @param key The key with which to associate the value
- */
-- (void)setFloatValue:(float)value forKey:(NSString *)key;
-
-+ (void)setObjectValue:(nullable id)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
-+ (void)setIntValue:(int)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
-+ (void)setBoolValue:(BOOL)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
-+ (void)setFloatValue:(float)value forKey:(NSString *)key CLS_DEPRECATED("Please access this method via +sharedInstance");
-
-/**
- * This method can be used to record a single exception structure in a report. This is particularly useful
- * when your code interacts with non-native languages like Lua, C#, or Javascript. This call can be
- * expensive and should only be used shortly before process termination. This API is not intended be to used
- * to log NSException objects. All safely-reportable NSExceptions are automatically captured by
- * Crashlytics.
- *
- * @param name The name of the custom exception
- * @param reason The reason this exception occurred
- * @param frameArray An array of CLSStackFrame objects
- */
-- (void)recordCustomExceptionName:(NSString *)name reason:(nullable NSString *)reason frameArray:(CLS_GENERIC_NSARRAY(CLSStackFrame *) *)frameArray;
-
-/**
- *
- * This allows you to record a non-fatal event, described by an NSError object. These events will be grouped and
- * displayed similarly to crashes. Keep in mind that this method can be expensive. Also, the total number of
- * NSErrors that can be recorded during your app's life-cycle is limited by a fixed-size circular buffer. If the
- * buffer is overrun, the oldest data is dropped. Errors are relayed to Crashlytics on a subsequent launch
- * of your application.
- *
- * You can also use the -recordError:withAdditionalUserInfo: to include additional context not represented
- * by the NSError instance itself.
- *
- **/
-- (void)recordError:(NSError *)error;
-- (void)recordError:(NSError *)error withAdditionalUserInfo:(nullable CLS_GENERIC_NSDICTIONARY(NSString *, id) *)userInfo;
-
-- (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
-- (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
-+ (void)logEvent:(NSString *)eventName CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
-+ (void)logEvent:(NSString *)eventName attributes:(nullable NSDictionary *) attributes CLS_DEPRECATED("Please refer to Answers +logCustomEventWithName:");
-
-@end
-
-/**
- *
- * The CrashlyticsDelegate protocol provides a mechanism for your application to take
- * action on events that occur in the Crashlytics crash reporting system. You can make
- * use of these calls by assigning an object to the Crashlytics' delegate property directly,
- * or through the convenience +startWithAPIKey:delegate: method.
- *
- */
-@protocol CrashlyticsDelegate
-@optional
-
-
-- (void)crashlyticsDidDetectCrashDuringPreviousExecution:(Crashlytics *)crashlytics CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
-- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id )crash CLS_DEPRECATED("Please refer to -crashlyticsDidDetectReportForLastExecution:");
-
-/**
- *
- * Called when a Crashlytics instance has determined that the last execution of the
- * application resulted in a saved report. This is called synchronously on Crashlytics
- * initialization. Your delegate must invoke the completionHandler, but does not need to do so
- * synchronously, or even on the main thread. Invoking completionHandler with NO will cause the
- * detected report to be deleted and not submitted to Crashlytics. This is useful for
- * implementing permission prompts, or other more-complex forms of logic around submitting crashes.
- *
- * Instead of using this method, you should try to make use of -crashlyticsDidDetectReportForLastExecution:
- * if you can.
- *
- * @warning Failure to invoke the completionHandler will prevent submissions from being reported. Watch out.
- *
- * @warning Just implementing this delegate method will disable all forms of synchronous report submission. This can
- * impact the reliability of reporting crashes very early in application launch.
- *
- * @param report The CLSReport object representing the last detected report
- * @param completionHandler The completion handler to call when your logic has completed.
- *
- */
-- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report completionHandler:(void (^)(BOOL submit))completionHandler;
-
-/**
- *
- * Called when a Crashlytics instance has determined that the last execution of the
- * application resulted in a saved report. This method differs from
- * -crashlyticsDidDetectReportForLastExecution:completionHandler: in three important ways:
- *
- * - it is not called synchronously during initialization
- * - it does not give you the ability to prevent the report from being submitted
- * - the report object itself is immutable
- *
- * Thanks to these limitations, making use of this method does not impact reporting
- * reliabilty in any way.
- *
- * @param report The read-only CLSReport object representing the last detected report
- *
- */
-
-- (void)crashlyticsDidDetectReportForLastExecution:(CLSReport *)report;
-
-/**
- * If your app is running on an OS that supports it (OS X 10.9+, iOS 7.0+), Crashlytics will submit
- * most reports using out-of-process background networking operations. This results in a significant
- * improvement in reliability of reporting, as well as power and performance wins for your users.
- * If you don't want this functionality, you can disable by returning NO from this method.
- *
- * @warning Background submission is not supported for extensions on iOS or OS X.
- *
- * @param crashlytics The Crashlytics singleton instance
- *
- * @return Return NO if you don't want out-of-process background network operations.
- *
- */
-- (BOOL)crashlyticsCanUseBackgroundSessions:(Crashlytics *)crashlytics;
-
-@end
-
-/**
- * `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, use Crashlytics.sharedInstance()
- */
-#define CrashlyticsKit [Crashlytics sharedInstance]
-
-NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist
deleted file mode 100644
index d86059be5..000000000
Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Info.plist and /dev/null differ
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap
deleted file mode 100644
index da0845e39..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/Modules/module.modulemap
+++ /dev/null
@@ -1,14 +0,0 @@
-framework module Crashlytics {
- header "Crashlytics.h"
- header "Answers.h"
- header "ANSCompatibility.h"
- header "CLSLogging.h"
- header "CLSReport.h"
- header "CLSStackFrame.h"
- header "CLSAttributes.h"
-
- export *
-
- link "z"
- link "c++"
-}
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/run b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/run
deleted file mode 100755
index 736cd2ff5..000000000
--- a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/run
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/sh
-
-# run
-#
-# Copyright (c) 2015 Crashlytics. All rights reserved.
-#
-#
-# This script is meant to be run as a Run Script in the "Build Phases" section
-# of your Xcode project. It sends debug symbols to symbolicate stacktraces,
-# sends build events to track versions, and onboard apps for Crashlytics.
-#
-# This script calls upload-symbols twice:
-#
-# 1) First it calls upload-symbols synchronously in "validation" mode. If the
-# script finds issues with the build environment, it will report errors to Xcode.
-# In validation mode it exits before doing any time consuming work.
-#
-# 2) Then it calls upload-symbols in the background to actually send the build
-# event and upload symbols. It does this in the background so that it doesn't
-# slow down your builds. If an error happens here, you won't see it in Xcode.
-#
-# You can find the output for the background execution in Console.app, by
-# searching for "upload-symbols".
-#
-# If you want verbose output, you can pass the --debug flag to this script
-#
-
-# Figure out where we're being called from
-DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-
-# If the first argument is specified without a dash, treat it as the Fabric API
-# Key and add it as an argument
-if [ -z "$1" ] || [[ $1 == -* ]]; then
- API_KEY_ARG=""
-else
- API_KEY_ARG="-a $1"; shift
-fi
-
-# If a second argument is specified without a dash, treat it as the Build Secret
-# and add it as an argument
-if [ -z "$1" ] || [[ $1 == -* ]]; then
- BUILD_SECRET_ARG=""
-else
- BUILD_SECRET_ARG="-bs $1"; shift
-fi
-
-# Build up the arguments list, passing through any flags added after the
-# API Key and Build Secret
-ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@"
-VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate"
-UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase"
-
-# Quote the path to handle folders with special characters
-COMMAND_PATH="\"$DIR/upload-symbols\" "
-
-# Ensure params are as expected, run in sync mode to validate,
-# and cause a build error if validation fails
-eval $COMMAND_PATH$VALIDATE_ARGUMENTS
-return_code=$?
-
-if [[ $return_code != 0 ]]; then
- exit $return_code
-fi
-
-# Verification passed, convert and upload cSYMs in the background to prevent
-# build delays
-#
-# Note: Validation is performed again at this step before upload
-#
-# Note: Output can still be found in Console.app, by searching for
-# "upload-symbols"
-#
-eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 &
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/submit b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/submit
deleted file mode 100755
index 3fda5cff1..000000000
Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/submit and /dev/null differ
diff --git a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols b/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols
deleted file mode 100755
index 5af65decc..000000000
Binary files a/ios/Pods/Crashlytics/iOS/Crashlytics.framework/upload-symbols and /dev/null differ
diff --git a/ios/Pods/Crashlytics/submit b/ios/Pods/Crashlytics/submit
deleted file mode 100755
index 3fda5cff1..000000000
Binary files a/ios/Pods/Crashlytics/submit and /dev/null differ
diff --git a/ios/Pods/Fabric/Fabric.framework/README b/ios/Pods/Fabric/Fabric.framework/README
deleted file mode 100644
index 3b1fbe24e..000000000
--- a/ios/Pods/Fabric/Fabric.framework/README
+++ /dev/null
@@ -1 +0,0 @@
-We've now combined all our supported platforms into a single podspec. As a result, we moved our run script to a new location for Cocoapods projects: ${PODS_ROOT}/Fabric/run. To avoid breaking builds that reference the old location of the run script, we've placed this dummy script that calls to the correct location, while providing a helpful warning in Xcode if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning!
diff --git a/ios/Pods/Fabric/Fabric.framework/run b/ios/Pods/Fabric/Fabric.framework/run
deleted file mode 100755
index b9edd17f9..000000000
--- a/ios/Pods/Fabric/Fabric.framework/run
+++ /dev/null
@@ -1,6 +0,0 @@
-if [[ -z $PODS_ROOT ]]; then
- echo "error: The run binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Fabric/run'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the run binary to fix this issue."
-else
- echo "warning: The run script is now located at '$"{"PODS_ROOT"}"/Fabric/run'. To remove this warning, update your Run Script Build Phase to point to this new location."
- sh "${PODS_ROOT}/Fabric/run" "$@"
-fi
diff --git a/ios/Pods/Fabric/README.md b/ios/Pods/Fabric/README.md
deleted file mode 100644
index e99ce8344..000000000
--- a/ios/Pods/Fabric/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-
-# Fabric
-
-## Overview
-
-[Fabric](https://get.fabric.io) provides developers with the tools they need to build the best apps. Developed and maintained by Google and the team that built Crashlytics.
-
-For a full list of SDKs provided through Fabric visit [https://fabric.io/kits](https://fabric.io/kits).
-
-To follow the migration to Firebase, check out the [Fabric Roadmap](https://get.fabric.io/roadmap).
-
-
-## Setup
-
-Fabric is a dependency for the Crashlytics SDK. To start using Crashlytics, there are two options:
-
-1) The recommended way is to go to the [Firebase Crashlytics Docs](https://firebase.google.com/docs/crashlytics/get-started?platform=ios) and follow the directions there.
-
-2) If you aren't using Firebase yet, go to [Fabric Kits](https://fabric.io/kits), and follow the directions for Crashlytics.
-
-
-## Resources
-
-* [Documentation](https://docs.fabric.io/)
-* [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
-* [Website](https://get.fabric.io)
-* Follow us on Twitter: [@fabric](https://twitter.com/fabric)
diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Fabric b/ios/Pods/Fabric/iOS/Fabric.framework/Fabric
deleted file mode 100755
index aa394a3de..000000000
Binary files a/ios/Pods/Fabric/iOS/Fabric.framework/Fabric and /dev/null differ
diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h b/ios/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h
deleted file mode 100644
index 3a9355a7c..000000000
--- a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/FABAttributes.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-// FABAttributes.h
-// Fabric
-//
-// Copyright (C) 2015 Twitter, Inc.
-//
-// 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.
-//
-
-#pragma once
-
-#define FAB_UNAVAILABLE(x) __attribute__((unavailable(x)))
-
-#if !__has_feature(nullability)
- #define nonnull
- #define nullable
- #define _Nullable
- #define _Nonnull
-#endif
-
-#ifndef NS_ASSUME_NONNULL_BEGIN
- #define NS_ASSUME_NONNULL_BEGIN
-#endif
-
-#ifndef NS_ASSUME_NONNULL_END
- #define NS_ASSUME_NONNULL_END
-#endif
-
-
-/**
- * The following macros are defined here to provide
- * backwards compatability. If you are still using
- * them you should migrate to the native nullability
- * macros.
- */
-#define fab_nullable nullable
-#define fab_nonnull nonnull
-#define FAB_NONNULL __fab_nonnull
-#define FAB_NULLABLE __fab_nullable
-#define FAB_START_NONNULL NS_ASSUME_NONNULL_BEGIN
-#define FAB_END_NONNULL NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h b/ios/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h
deleted file mode 100644
index ecbdb53b8..000000000
--- a/ios/Pods/Fabric/iOS/Fabric.framework/Headers/Fabric.h
+++ /dev/null
@@ -1,82 +0,0 @@
-//
-// Fabric.h
-// Fabric
-//
-// Copyright (C) 2015 Twitter, Inc.
-//
-// 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
-#import "FABAttributes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-#if TARGET_OS_IPHONE
-#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
- #error "Fabric's minimum iOS version is 6.0"
-#endif
-#else
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070
- #error "Fabric's minimum OS X version is 10.7"
-#endif
-#endif
-
-/**
- * Fabric Base. Coordinates configuration and starts all provided kits.
- */
-@interface Fabric : NSObject
-
-/**
- * Initialize Fabric and all provided kits. Call this method within your App Delegate's `application:didFinishLaunchingWithOptions:` and provide the kits you wish to use.
- *
- * For example, in Objective-C:
- *
- * `[Fabric with:@[[Crashlytics class], [Twitter class], [Digits class], [MoPub class]]];`
- *
- * Swift:
- *
- * `Fabric.with([Crashlytics.self(), Twitter.self(), Digits.self(), MoPub.self()])`
- *
- * Only the first call to this method is honored. Subsequent calls are no-ops.
- *
- * @param kitClasses An array of kit Class objects
- *
- * @return Returns the shared Fabric instance. In most cases this can be ignored.
- */
-+ (instancetype)with:(NSArray *)kitClasses;
-
-/**
- * Returns the Fabric singleton object.
- */
-+ (instancetype)sharedSDK;
-
-/**
- * This BOOL enables or disables debug logging, such as kit version information. The default value is NO.
- */
-@property (nonatomic, assign) BOOL debug;
-
-/**
- * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance.
- */
-- (id)init FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance.");
-
-/**
- * Unavailable. Use `+sharedSDK` to retrieve the shared Fabric instance.
- */
-+ (instancetype)new FAB_UNAVAILABLE("Use +sharedSDK to retrieve the shared Fabric instance.");
-
-@end
-
-NS_ASSUME_NONNULL_END
-
diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Info.plist b/ios/Pods/Fabric/iOS/Fabric.framework/Info.plist
deleted file mode 100644
index a617b035f..000000000
Binary files a/ios/Pods/Fabric/iOS/Fabric.framework/Info.plist and /dev/null differ
diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap b/ios/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap
deleted file mode 100644
index 2a312239d..000000000
--- a/ios/Pods/Fabric/iOS/Fabric.framework/Modules/module.modulemap
+++ /dev/null
@@ -1,6 +0,0 @@
-framework module Fabric {
- umbrella header "Fabric.h"
-
- export *
- module * { export * }
-}
\ No newline at end of file
diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/run b/ios/Pods/Fabric/iOS/Fabric.framework/run
deleted file mode 100755
index 736cd2ff5..000000000
--- a/ios/Pods/Fabric/iOS/Fabric.framework/run
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/sh
-
-# run
-#
-# Copyright (c) 2015 Crashlytics. All rights reserved.
-#
-#
-# This script is meant to be run as a Run Script in the "Build Phases" section
-# of your Xcode project. It sends debug symbols to symbolicate stacktraces,
-# sends build events to track versions, and onboard apps for Crashlytics.
-#
-# This script calls upload-symbols twice:
-#
-# 1) First it calls upload-symbols synchronously in "validation" mode. If the
-# script finds issues with the build environment, it will report errors to Xcode.
-# In validation mode it exits before doing any time consuming work.
-#
-# 2) Then it calls upload-symbols in the background to actually send the build
-# event and upload symbols. It does this in the background so that it doesn't
-# slow down your builds. If an error happens here, you won't see it in Xcode.
-#
-# You can find the output for the background execution in Console.app, by
-# searching for "upload-symbols".
-#
-# If you want verbose output, you can pass the --debug flag to this script
-#
-
-# Figure out where we're being called from
-DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-
-# If the first argument is specified without a dash, treat it as the Fabric API
-# Key and add it as an argument
-if [ -z "$1" ] || [[ $1 == -* ]]; then
- API_KEY_ARG=""
-else
- API_KEY_ARG="-a $1"; shift
-fi
-
-# If a second argument is specified without a dash, treat it as the Build Secret
-# and add it as an argument
-if [ -z "$1" ] || [[ $1 == -* ]]; then
- BUILD_SECRET_ARG=""
-else
- BUILD_SECRET_ARG="-bs $1"; shift
-fi
-
-# Build up the arguments list, passing through any flags added after the
-# API Key and Build Secret
-ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@"
-VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate"
-UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase"
-
-# Quote the path to handle folders with special characters
-COMMAND_PATH="\"$DIR/upload-symbols\" "
-
-# Ensure params are as expected, run in sync mode to validate,
-# and cause a build error if validation fails
-eval $COMMAND_PATH$VALIDATE_ARGUMENTS
-return_code=$?
-
-if [[ $return_code != 0 ]]; then
- exit $return_code
-fi
-
-# Verification passed, convert and upload cSYMs in the background to prevent
-# build delays
-#
-# Note: Validation is performed again at this step before upload
-#
-# Note: Output can still be found in Console.app, by searching for
-# "upload-symbols"
-#
-eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 &
diff --git a/ios/Pods/Fabric/iOS/Fabric.framework/upload-symbols b/ios/Pods/Fabric/iOS/Fabric.framework/upload-symbols
deleted file mode 100755
index a18d0b68f..000000000
Binary files a/ios/Pods/Fabric/iOS/Fabric.framework/upload-symbols and /dev/null differ
diff --git a/ios/Pods/Fabric/run b/ios/Pods/Fabric/run
deleted file mode 100755
index 736cd2ff5..000000000
--- a/ios/Pods/Fabric/run
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/sh
-
-# run
-#
-# Copyright (c) 2015 Crashlytics. All rights reserved.
-#
-#
-# This script is meant to be run as a Run Script in the "Build Phases" section
-# of your Xcode project. It sends debug symbols to symbolicate stacktraces,
-# sends build events to track versions, and onboard apps for Crashlytics.
-#
-# This script calls upload-symbols twice:
-#
-# 1) First it calls upload-symbols synchronously in "validation" mode. If the
-# script finds issues with the build environment, it will report errors to Xcode.
-# In validation mode it exits before doing any time consuming work.
-#
-# 2) Then it calls upload-symbols in the background to actually send the build
-# event and upload symbols. It does this in the background so that it doesn't
-# slow down your builds. If an error happens here, you won't see it in Xcode.
-#
-# You can find the output for the background execution in Console.app, by
-# searching for "upload-symbols".
-#
-# If you want verbose output, you can pass the --debug flag to this script
-#
-
-# Figure out where we're being called from
-DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
-
-# If the first argument is specified without a dash, treat it as the Fabric API
-# Key and add it as an argument
-if [ -z "$1" ] || [[ $1 == -* ]]; then
- API_KEY_ARG=""
-else
- API_KEY_ARG="-a $1"; shift
-fi
-
-# If a second argument is specified without a dash, treat it as the Build Secret
-# and add it as an argument
-if [ -z "$1" ] || [[ $1 == -* ]]; then
- BUILD_SECRET_ARG=""
-else
- BUILD_SECRET_ARG="-bs $1"; shift
-fi
-
-# Build up the arguments list, passing through any flags added after the
-# API Key and Build Secret
-ARGUMENTS="$API_KEY_ARG $BUILD_SECRET_ARG $@"
-VALIDATE_ARGUMENTS="$ARGUMENTS --build-phase --validate"
-UPLOAD_ARGUMENTS="$ARGUMENTS --build-phase"
-
-# Quote the path to handle folders with special characters
-COMMAND_PATH="\"$DIR/upload-symbols\" "
-
-# Ensure params are as expected, run in sync mode to validate,
-# and cause a build error if validation fails
-eval $COMMAND_PATH$VALIDATE_ARGUMENTS
-return_code=$?
-
-if [[ $return_code != 0 ]]; then
- exit $return_code
-fi
-
-# Verification passed, convert and upload cSYMs in the background to prevent
-# build delays
-#
-# Note: Validation is performed again at this step before upload
-#
-# Note: Output can still be found in Console.app, by searching for
-# "upload-symbols"
-#
-eval $COMMAND_PATH$UPLOAD_ARGUMENTS > /dev/null 2>&1 &
diff --git a/ios/Pods/Fabric/upload-symbols b/ios/Pods/Fabric/upload-symbols
deleted file mode 100755
index a18d0b68f..000000000
Binary files a/ios/Pods/Fabric/upload-symbols and /dev/null differ
diff --git a/ios/Pods/Firebase/README.md b/ios/Pods/Firebase/README.md
index 1747cca7a..d778205ff 100644
--- a/ios/Pods/Firebase/README.md
+++ b/ios/Pods/Firebase/README.md
@@ -2,14 +2,20 @@
[](https://cocoapods.org/pods/Firebase)
[](https://cocoapods.org/pods/Firebase)
+[![Actions Status][gh-abtesting-badge]][gh-actions]
[![Actions Status][gh-auth-badge]][gh-actions]
[![Actions Status][gh-core-badge]][gh-actions]
+[![Actions Status][gh-crashlytics-badge]][gh-actions]
+[![Actions Status][gh-database-badge]][gh-actions]
[![Actions Status][gh-datatransport-badge]][gh-actions]
[![Actions Status][gh-dynamiclinks-badge]][gh-actions]
[![Actions Status][gh-firebasepod-badge]][gh-actions]
[![Actions Status][gh-firestore-badge]][gh-actions]
+[![Actions Status][gh-functions-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-remoteconfig-badge]][gh-actions]
[![Actions Status][gh-storage-badge]][gh-actions]
[![Actions Status][gh-symbolcollision-badge]][gh-actions]
[![Actions Status][gh-zip-badge]][gh-actions]
@@ -92,7 +98,7 @@ Instructions for installing binary frameworks via
To develop Firebase software in this repository, ensure that you have at least
the following software:
- * Xcode 10.1 (or later)
+ * Xcode 10.3 (or later)
* CocoaPods 1.7.2 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
@@ -123,6 +129,10 @@ Firestore has a self contained Xcode project. See
See [AddNewPod.md](AddNewPod.md).
+### Managing Headers and Imports
+
+See [HeadersImports.md](HeadersImports.md).
+
### Code Formatting
To ensure that the code is formatted consistently, run the script
@@ -185,8 +195,16 @@ building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
-To run the Database Integration tests, make your database authentication rules
-[public](https://firebase.google.com/docs/database/security/quickstart).
+The Firebase Database Integration tests can be run against a locally running Database Emulator
+or against a production instance.
+
+To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
+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
+[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
+running.
### Firebase Storage
@@ -274,14 +292,20 @@ Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
[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-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg
[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/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
[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg
[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-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-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
[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg
diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector
index bb3a7cae8..8746d38f6 100755
Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector and b/ios/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector differ
diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics
index 46030f0c4..e00c279a8 100755
Binary files a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics and b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/FirebaseAnalytics differ
diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h
index be0b1faee..15b693c90 100755
--- a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h
+++ b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIRAnalytics.h
@@ -90,12 +90,10 @@ NS_SWIFT_NAME(Analytics)
/// 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. If you implement
-/// viewDidAppear in your UIViewController but do not call [super viewDidAppear:], that screen class
-/// will not be automatically tracked. 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:].
+/// 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.
@@ -103,6 +101,9 @@ NS_SWIFT_NAME(Analytics)
/// 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
diff --git a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h
index 674f8b121..807b6159e 100755
--- a/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h
+++ b/ios/Pods/FirebaseAnalytics/Frameworks/FirebaseAnalytics.framework/Headers/FIREventNames.h
@@ -366,7 +366,6 @@ static NSString *const kFIREventViewItemList NS_SWIFT_NAME(AnalyticsEventViewIte
///
/// - @c kFIRParameterSearchTerm (NSString)
///
-/// This constant has been deprecated.
static NSString *const kFIREventViewSearchResults NS_SWIFT_NAME(AnalyticsEventViewSearchResults) =
@"view_search_results";
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.h
similarity index 100%
rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h
rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.h
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m
index 3a7d6de0e..07c786cb7 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAnalyticsConfiguration.m
@@ -14,7 +14,7 @@
#import
-#import "FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h"
+#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m
index 002f93c51..4dfbfa838 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRApp.m
@@ -22,14 +22,14 @@
#import
#endif
-#import
+#import "FirebaseCore/Sources/Public/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/Private/FIRAnalyticsConfiguration.h"
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
-#import "FirebaseCore/Sources/Private/FIRComponentContainerInternal.h"
-#import "FirebaseCore/Sources/Private/FIRConfigurationInternal.h"
#import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h"
#import "FirebaseCore/Sources/Private/FIRLibrary.h"
#import "FirebaseCore/Sources/Private/FIRLogger.h"
@@ -166,7 +166,7 @@ static dispatch_once_t sFirebaseUserAgentOnceToken;
if ([name isEqualToString:kFIRDefaultAppName]) {
if (sDefaultApp) {
- // The default app already exixts. Handle duplicate `configure` calls and return.
+ // The default app already exists. Handle duplicate `configure` calls and return.
[self appWasConfiguredTwice:sDefaultApp usingOptions:options];
return;
}
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppAssociationRegistration.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.h
similarity index 100%
rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppAssociationRegistration.h
rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.h
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m
index e4125cd25..f3f812c74 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRAppAssociationRegistration.m
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#import "FirebaseCore/Sources/Private/FIRAppAssociationRegistration.h"
+#import "FirebaseCore/Sources/FIRAppAssociationRegistration.h"
#import
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m
index b858f14c4..de2c29542 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRBundleUtil.m
@@ -49,15 +49,19 @@
+ (BOOL)hasBundleIdentifierPrefix:(NSString *)bundleIdentifier inBundles:(NSArray *)bundles {
for (NSBundle *bundle in bundles) {
- // This allows app extensions that have the app's bundle as their prefix to pass this test.
- NSString *applicationBundleIdentifier =
- [GULAppEnvironmentUtil isAppExtension]
- ? [self bundleIdentifierByRemovingLastPartFrom:bundle.bundleIdentifier]
- : bundle.bundleIdentifier;
-
- if ([applicationBundleIdentifier isEqualToString:bundleIdentifier]) {
+ if ([bundle.bundleIdentifier isEqualToString:bundleIdentifier]) {
return YES;
}
+
+ if ([GULAppEnvironmentUtil isAppExtension]) {
+ // A developer could be using the same `FIROptions` for both their app and extension. Since
+ // extensions have a suffix added to the bundleID, we consider a matching prefix as valid.
+ NSString *appBundleIDFromExtension =
+ [self bundleIdentifierByRemovingLastPartFrom:bundle.bundleIdentifier];
+ if ([appBundleIDFromExtension isEqualToString:bundleIdentifier]) {
+ return YES;
+ }
+ }
}
return NO;
}
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainerInternal.h
similarity index 93%
rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h
rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainerInternal.h
index bf39bc6ce..82356060d 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainerInternal.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentContainerInternal.h
@@ -15,8 +15,8 @@
*/
#import
-#import
-#import
+#import "FirebaseCore/Sources/Private/FIRComponent.h"
+#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
@class FIRApp;
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m
index 6410f2ea1..9051336a6 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRComponentType.m
@@ -16,7 +16,7 @@
#import "FirebaseCore/Sources/Private/FIRComponentType.h"
-#import "FirebaseCore/Sources/Private/FIRComponentContainerInternal.h"
+#import "FirebaseCore/Sources/FIRComponentContainerInternal.h"
@implementation FIRComponentType
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m
index a1c9f4a2e..83b3248c3 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfiguration.m
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#import "FirebaseCore/Sources/Private/FIRConfigurationInternal.h"
+#import "FirebaseCore/Sources/FIRConfigurationInternal.h"
-#import "FirebaseCore/Sources/Private/FIRAnalyticsConfiguration.h"
+#import "FirebaseCore/Sources/FIRAnalyticsConfiguration.h"
extern void FIRSetLoggerLevel(FIRLoggerLevel loggerLevel);
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfigurationInternal.h
similarity index 95%
rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h
rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfigurationInternal.h
index 0d1a36f66..ee1688670 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRConfigurationInternal.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRConfigurationInternal.h
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#import
+#import "FIRConfiguration.h"
@class FIRAnalyticsConfiguration;
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m
index 4981ca1b0..730dd6ef9 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRCoreDiagnosticsConnector.m
@@ -16,12 +16,12 @@
#import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h"
-#import
+#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h"
-#import
+#import "FirebaseCore/Sources/Public/FIROptions.h"
+#import "FirebaseCore/Sources/FIRDiagnosticsData.h"
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
-#import "FirebaseCore/Sources/Private/FIRDiagnosticsData.h"
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
// Define the interop class symbol declared as an extern in FIRCoreDiagnosticsInterop.
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.h
similarity index 94%
rename from ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h
rename to ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.h
index ac5ef2c4f..5b5ff8ad9 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRDiagnosticsData.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.h
@@ -16,7 +16,7 @@
#import
-#import
+#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h"
NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m
index bbe0561d9..da9489d99 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRDiagnosticsData.m
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#import "FirebaseCore/Sources/Private/FIRDiagnosticsData.h"
+#import "FirebaseCore/Sources/FIRDiagnosticsData.h"
-#import
+#import "FirebaseCore/Sources/Public/FIRApp.h"
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m
index ba2ee1f58..a749cbd94 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/FIRLogger.m
@@ -14,9 +14,9 @@
#import "FirebaseCore/Sources/Private/FIRLogger.h"
-#import
#import
#import
+#import "FirebaseCore/Sources/Public/FIRLoggerLevel.h"
#import "FirebaseCore/Sources/FIRVersion.h"
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h
index ad1a186b9..9a0c943db 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRAppInternal.h
@@ -14,8 +14,19 @@
* 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
+#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
+#endif
@class FIRComponentContainer;
@protocol FIRLibrary;
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h
index 8dfab9c10..db2bafef8 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRComponentContainer.h
@@ -15,8 +15,14 @@
*/
#import
+// 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
#import
+#endif
NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h
index f77b3d002..c90d9eecf 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRErrorCode.h
@@ -32,7 +32,8 @@ typedef NS_ENUM(NSInteger, FIRErrorCode) {
FIRErrorCodeInvalidAppID = -101,
/**
- * Error code for failing to configure a specific service.
+ * Error code for failing to configure a specific service. It's deprecated, but
+ * still used after copybara.
*/
FIRErrorCodeConfigFailed = -114,
};
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h
index af9d9685d..e7a9e077c 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLibrary.h
@@ -19,7 +19,12 @@
#import
+// 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
+#endif
@class FIRApp;
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h
index 548e389a4..6fd778441 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIRLogger.h
@@ -16,7 +16,12 @@
#import
+#if SWIFT_PACKAGE
+// TODO(paulb777): Investigate if there's a common strategy for both Cocoapods and Swift PM.
+#import "FIRLoggerLevel.h"
+#else
#import
+#endif
NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h
index 0660a3cd8..acaf45868 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FIROptionsInternal.h
@@ -14,7 +14,12 @@
* 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
+#endif
/**
* Keys for the strings in the plist file.
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FirebaseCoreInternal.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FirebaseCoreInternal.h
new file mode 100644
index 000000000..93af6cb8d
--- /dev/null
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Private/FirebaseCoreInternal.h
@@ -0,0 +1,31 @@
+// 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.
+
+// 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
+#endif
+
+#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
+#import "FirebaseCore/Sources/Private/FIRComponent.h"
+#import "FirebaseCore/Sources/Private/FIRComponentContainer.h"
+#import "FirebaseCore/Sources/Private/FIRDependency.h"
+#import "FirebaseCore/Sources/Private/FIRHeartbeatInfo.h"
+#import "FirebaseCore/Sources/Private/FIRLibrary.h"
+#import "FirebaseCore/Sources/Private/FIRLogger.h"
+#import "FirebaseCore/Sources/Private/FIROptionsInternal.h"
diff --git a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h
index 8de3b076a..2b8e678ce 100644
--- a/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h
+++ b/ios/Pods/FirebaseCore/FirebaseCore/Sources/Public/FIRConfiguration.h
@@ -16,7 +16,7 @@
#import
-#import
+#import "FIRLoggerLevel.h"
NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h b/ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h
similarity index 100%
rename from ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h
rename to ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h
diff --git a/ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h b/ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
similarity index 100%
rename from ios/Pods/FirebaseCoreDiagnosticsInterop/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
rename to ios/Pods/FirebaseCore/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
diff --git a/ios/Pods/FirebaseCore/README.md b/ios/Pods/FirebaseCore/README.md
index 1747cca7a..d778205ff 100644
--- a/ios/Pods/FirebaseCore/README.md
+++ b/ios/Pods/FirebaseCore/README.md
@@ -2,14 +2,20 @@
[](https://cocoapods.org/pods/Firebase)
[](https://cocoapods.org/pods/Firebase)
+[![Actions Status][gh-abtesting-badge]][gh-actions]
[![Actions Status][gh-auth-badge]][gh-actions]
[![Actions Status][gh-core-badge]][gh-actions]
+[![Actions Status][gh-crashlytics-badge]][gh-actions]
+[![Actions Status][gh-database-badge]][gh-actions]
[![Actions Status][gh-datatransport-badge]][gh-actions]
[![Actions Status][gh-dynamiclinks-badge]][gh-actions]
[![Actions Status][gh-firebasepod-badge]][gh-actions]
[![Actions Status][gh-firestore-badge]][gh-actions]
+[![Actions Status][gh-functions-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-remoteconfig-badge]][gh-actions]
[![Actions Status][gh-storage-badge]][gh-actions]
[![Actions Status][gh-symbolcollision-badge]][gh-actions]
[![Actions Status][gh-zip-badge]][gh-actions]
@@ -92,7 +98,7 @@ Instructions for installing binary frameworks via
To develop Firebase software in this repository, ensure that you have at least
the following software:
- * Xcode 10.1 (or later)
+ * Xcode 10.3 (or later)
* CocoaPods 1.7.2 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
@@ -123,6 +129,10 @@ Firestore has a self contained Xcode project. See
See [AddNewPod.md](AddNewPod.md).
+### Managing Headers and Imports
+
+See [HeadersImports.md](HeadersImports.md).
+
### Code Formatting
To ensure that the code is formatted consistently, run the script
@@ -185,8 +195,16 @@ building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
-To run the Database Integration tests, make your database authentication rules
-[public](https://firebase.google.com/docs/database/security/quickstart).
+The Firebase Database Integration tests can be run against a locally running Database Emulator
+or against a production instance.
+
+To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
+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
+[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
+running.
### Firebase Storage
@@ -274,14 +292,20 @@ Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
[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-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg
[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/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
[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg
[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-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-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
[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg
diff --git a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m
index 87d57c4e8..2db7db839 100644
--- a/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m
+++ b/ios/Pods/FirebaseCoreDiagnostics/Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m
@@ -26,8 +26,8 @@
#import
#import
-#import
-#import
+#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h"
+#import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h"
#import
#import
diff --git a/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h
new file mode 100644
index 000000000..69c40721a
--- /dev/null
+++ b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** If present, is a BOOL wrapped in an NSNumber. */
+#define kFIRCDIsDataCollectionDefaultEnabledKey @"FIRCDIsDataCollectionDefaultEnabledKey"
+
+/** If present, is an int32_t wrapped in an NSNumber. */
+#define kFIRCDConfigurationTypeKey @"FIRCDConfigurationTypeKey"
+
+/** If present, is an NSString. */
+#define kFIRCDSdkNameKey @"FIRCDSdkNameKey"
+
+/** If present, is an NSString. */
+#define kFIRCDSdkVersionKey @"FIRCDSdkVersionKey"
+
+/** If present, is an int32_t wrapped in an NSNumber. */
+#define kFIRCDllAppsCountKey @"FIRCDllAppsCountKey"
+
+/** If present, is an NSString. */
+#define kFIRCDGoogleAppIDKey @"FIRCDGoogleAppIDKey"
+
+/** If present, is an NSString. */
+#define kFIRCDBundleIDKey @"FIRCDBundleID"
+
+/** If present, is a BOOL wrapped in an NSNumber. */
+#define kFIRCDUsingOptionsFromDefaultPlistKey @"FIRCDUsingOptionsFromDefaultPlistKey"
+
+/** If present, is an NSString. */
+#define kFIRCDLibraryVersionIDKey @"FIRCDLibraryVersionIDKey"
+
+/** If present, is an NSString. */
+#define kFIRCDFirebaseUserAgentKey @"FIRCDFirebaseUserAgentKey"
+
+/** Defines the interface of a data object needed to log diagnostics data. */
+@protocol FIRCoreDiagnosticsData
+
+@required
+
+/** A dictionary containing data (non-exhaustive) to be logged in diagnostics. */
+@property(nonatomic) NSDictionary *diagnosticObjects;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
new file mode 100644
index 000000000..2b0eb710c
--- /dev/null
+++ b/ios/Pods/FirebaseCoreDiagnostics/Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import
+
+#import "FIRCoreDiagnosticsData.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/** Allows the interoperation of FirebaseCore and FirebaseCoreDiagnostics. */
+@protocol FIRCoreDiagnosticsInterop
+
+/** Sends the given diagnostics data.
+ *
+ * @param diagnosticsData The diagnostics data object to send.
+ */
++ (void)sendDiagnosticsData:(id)diagnosticsData;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCoreDiagnostics/README.md b/ios/Pods/FirebaseCoreDiagnostics/README.md
index 1747cca7a..d778205ff 100644
--- a/ios/Pods/FirebaseCoreDiagnostics/README.md
+++ b/ios/Pods/FirebaseCoreDiagnostics/README.md
@@ -2,14 +2,20 @@
[](https://cocoapods.org/pods/Firebase)
[](https://cocoapods.org/pods/Firebase)
+[![Actions Status][gh-abtesting-badge]][gh-actions]
[![Actions Status][gh-auth-badge]][gh-actions]
[![Actions Status][gh-core-badge]][gh-actions]
+[![Actions Status][gh-crashlytics-badge]][gh-actions]
+[![Actions Status][gh-database-badge]][gh-actions]
[![Actions Status][gh-datatransport-badge]][gh-actions]
[![Actions Status][gh-dynamiclinks-badge]][gh-actions]
[![Actions Status][gh-firebasepod-badge]][gh-actions]
[![Actions Status][gh-firestore-badge]][gh-actions]
+[![Actions Status][gh-functions-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-remoteconfig-badge]][gh-actions]
[![Actions Status][gh-storage-badge]][gh-actions]
[![Actions Status][gh-symbolcollision-badge]][gh-actions]
[![Actions Status][gh-zip-badge]][gh-actions]
@@ -92,7 +98,7 @@ Instructions for installing binary frameworks via
To develop Firebase software in this repository, ensure that you have at least
the following software:
- * Xcode 10.1 (or later)
+ * Xcode 10.3 (or later)
* CocoaPods 1.7.2 (or later)
* [CocoaPods generate](https://github.com/square/cocoapods-generate)
@@ -123,6 +129,10 @@ Firestore has a self contained Xcode project. See
See [AddNewPod.md](AddNewPod.md).
+### Managing Headers and Imports
+
+See [HeadersImports.md](HeadersImports.md).
+
### Code Formatting
To ensure that the code is formatted consistently, run the script
@@ -185,8 +195,16 @@ building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
-To run the Database Integration tests, make your database authentication rules
-[public](https://firebase.google.com/docs/database/security/quickstart).
+The Firebase Database Integration tests can be run against a locally running Database Emulator
+or against a production instance.
+
+To run against a local emulator instance, invoke `./scripts/run_database_emulator.sh start` before
+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
+[public](https://firebase.google.com/docs/database/security/quickstart) while your tests are
+running.
### Firebase Storage
@@ -274,14 +292,20 @@ Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).
[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-auth-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/auth/badge.svg
[gh-core-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/core/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
[gh-dynamiclinks-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/dynamiclinks/badge.svg
[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-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-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
[gh-zip-badge]: https://github.com/firebase/firebase-ios-sdk/workflows/zip/badge.svg
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h
new file mode 100644
index 000000000..75536f7cd
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.h
@@ -0,0 +1,88 @@
+// 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
+#if CLS_TARGET_OS_HAS_UIKIT
+#import
+#endif
+
+__BEGIN_DECLS
+
+#define FIRCLSApplicationActivityDefault \
+ (NSActivitySuddenTerminationDisabled | NSActivityAutomaticTerminationDisabled)
+
+/**
+ * Type to indicate application installation source
+ */
+typedef NS_ENUM(NSInteger, FIRCLSApplicationInstallationSourceType) {
+ FIRCLSApplicationInstallationSourceTypeDeveloperInstall = 1,
+ // 2 and 3 are reserved for legacy values.
+ FIRCLSApplicationInstallationSourceTypeAppStore = 4
+};
+
+/**
+ * Returns the application bundle identifier with occurences of "/" replaced by "_"
+ */
+NSString* FIRCLSApplicationGetBundleIdentifier(void);
+
+/**
+ * Returns the SDK's bundle ID
+ */
+NSString* FIRCLSApplicationGetSDKBundleID(void);
+
+/**
+ * Returns the platform identifier, either: ios, mac, or tvos.
+ * Catalyst apps are treated as mac.
+ */
+NSString* FIRCLSApplicationGetPlatform(void);
+
+/**
+ * Returns the user-facing app name
+ */
+NSString* FIRCLSApplicationGetName(void);
+
+/**
+ * Returns the build number
+ */
+NSString* FIRCLSApplicationGetBundleVersion(void);
+
+/**
+ * Returns the human-readable build version
+ */
+NSString* FIRCLSApplicationGetShortBundleVersion(void);
+
+/**
+ * Returns a number to indicate how the app has been installed: Developer / App Store
+ */
+FIRCLSApplicationInstallationSourceType FIRCLSApplicationInstallationSource(void);
+
+BOOL FIRCLSApplicationIsExtension(void);
+NSString* FIRCLSApplicationExtensionPointIdentifier(void);
+
+#if CLS_TARGET_OS_HAS_UIKIT
+UIApplication* FIRCLSApplicationSharedInstance(void);
+#else
+id FIRCLSApplicationSharedInstance(void);
+#endif
+
+void FIRCLSApplicationOpenURL(NSURL* url,
+ NSExtensionContext* extensionContext,
+ void (^completionBlock)(BOOL success));
+
+id FIRCLSApplicationBeginActivity(NSActivityOptions options, NSString* reason);
+void FIRCLSApplicationEndActivity(id activity);
+
+void FIRCLSApplicationActivity(NSActivityOptions options, NSString* reason, void (^block)(void));
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m
new file mode 100644
index 000000000..219b4bb7a
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSApplication.m
@@ -0,0 +1,211 @@
+// 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 "FIRCLSApplication.h"
+
+#import "FIRCLSHost.h"
+#import "FIRCLSUtility.h"
+
+#if CLS_TARGET_OS_OSX
+#import
+#endif
+
+#if CLS_TARGET_OS_HAS_UIKIT
+#import
+#endif
+
+NSString* FIRCLSApplicationGetBundleIdentifier(void) {
+ return [[[NSBundle mainBundle] bundleIdentifier] stringByReplacingOccurrencesOfString:@"/"
+ withString:@"_"];
+}
+
+NSString* FIRCLSApplicationGetSDKBundleID(void) {
+ return
+ [@"com.google.firebase.crashlytics." stringByAppendingString:FIRCLSApplicationGetPlatform()];
+}
+
+NSString* FIRCLSApplicationGetPlatform(void) {
+#if defined(TARGET_OS_MACCATALYST) && TARGET_OS_MACCATALYST
+ return @"mac";
+#elif TARGET_OS_IOS
+ return @"ios";
+#elif TARGET_OS_OSX
+ return @"mac";
+#elif TARGET_OS_TV
+ return @"tvos";
+#endif
+}
+
+// these defaults match the FIRCLSInfoPlist helper in FIRCLSIDEFoundation
+NSString* FIRCLSApplicationGetBundleVersion(void) {
+ return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
+}
+
+NSString* FIRCLSApplicationGetShortBundleVersion(void) {
+ return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
+}
+
+NSString* FIRCLSApplicationGetName(void) {
+ NSString* name;
+ NSBundle* mainBundle;
+
+ mainBundle = [NSBundle mainBundle];
+
+ name = [mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
+ if (name) {
+ return name;
+ }
+
+ name = [mainBundle objectForInfoDictionaryKey:@"CFBundleName"];
+ if (name) {
+ return name;
+ }
+
+ return FIRCLSApplicationGetBundleVersion();
+}
+
+BOOL FIRCLSApplicationHasAppStoreReceipt(void) {
+ NSURL* url = NSBundle.mainBundle.appStoreReceiptURL;
+ return [NSFileManager.defaultManager fileExistsAtPath:[url path]];
+}
+
+FIRCLSApplicationInstallationSourceType FIRCLSApplicationInstallationSource(void) {
+ if (FIRCLSApplicationHasAppStoreReceipt()) {
+ return FIRCLSApplicationInstallationSourceTypeAppStore;
+ }
+
+ return FIRCLSApplicationInstallationSourceTypeDeveloperInstall;
+}
+
+BOOL FIRCLSApplicationIsExtension(void) {
+ return FIRCLSApplicationExtensionPointIdentifier() != nil;
+}
+
+NSString* FIRCLSApplicationExtensionPointIdentifier(void) {
+ id extensionDict = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"NSExtension"];
+
+ if (!extensionDict) {
+ return nil;
+ }
+
+ if (![extensionDict isKindOfClass:[NSDictionary class]]) {
+ FIRCLSSDKLog("Error: NSExtension Info.plist entry is mal-formed\n");
+ return nil;
+ }
+
+ id typeValue = [(NSDictionary*)extensionDict objectForKey:@"NSExtensionPointIdentifier"];
+
+ if (![typeValue isKindOfClass:[NSString class]]) {
+ FIRCLSSDKLog("Error: NSExtensionPointIdentifier Info.plist entry is mal-formed\n");
+ return nil;
+ }
+
+ return typeValue;
+}
+
+#if CLS_TARGET_OS_HAS_UIKIT
+UIApplication* FIRCLSApplicationSharedInstance(void) {
+ if (FIRCLSApplicationIsExtension()) {
+ return nil;
+ }
+
+ return [[UIApplication class] performSelector:@selector(sharedApplication)];
+}
+#elif CLS_TARGET_OS_OSX
+id FIRCLSApplicationSharedInstance(void) {
+ return [NSClassFromString(@"NSApplication") sharedApplication];
+}
+#else
+id FIRCLSApplicationSharedInstance(void) {
+ return nil; // FIXME: what do we actually return for watch?
+}
+#endif
+
+void FIRCLSApplicationOpenURL(NSURL* url,
+ NSExtensionContext* extensionContext,
+ void (^completionBlock)(BOOL success)) {
+ if (extensionContext) {
+ [extensionContext openURL:url completionHandler:completionBlock];
+ return;
+ }
+
+ BOOL result = NO;
+
+#if TARGET_OS_IOS
+ // What's going on here is the value returned is a scalar, but we really need an object to
+ // call this dynamically. Hoops must be jumped.
+ NSInvocationOperation* op =
+ [[NSInvocationOperation alloc] initWithTarget:FIRCLSApplicationSharedInstance()
+ selector:@selector(openURL:)
+ object:url];
+ [op start];
+ [op.result getValue:&result];
+#elif CLS_TARGET_OS_OSX
+ result = [[NSClassFromString(@"NSWorkspace") sharedWorkspace] openURL:url];
+#endif
+
+ completionBlock(result);
+}
+
+id FIRCLSApplicationBeginActivity(NSActivityOptions options, NSString* reason) {
+ if ([[NSProcessInfo processInfo] respondsToSelector:@selector(beginActivityWithOptions:
+ reason:)]) {
+ return [[NSProcessInfo processInfo] beginActivityWithOptions:options reason:reason];
+ }
+
+#if CLS_TARGET_OS_OSX
+ if (options & NSActivitySuddenTerminationDisabled) {
+ [[NSProcessInfo processInfo] disableSuddenTermination];
+ }
+
+ if (options & NSActivityAutomaticTerminationDisabled) {
+ [[NSProcessInfo processInfo] disableAutomaticTermination:reason];
+ }
+#endif
+
+ // encode the options, so we can undo our work later
+ return @{@"options" : @(options), @"reason" : reason};
+}
+
+void FIRCLSApplicationEndActivity(id activity) {
+ if (!activity) {
+ return;
+ }
+
+ if ([[NSProcessInfo processInfo] respondsToSelector:@selector(endActivity:)]) {
+ [[NSProcessInfo processInfo] endActivity:activity];
+ return;
+ }
+
+#if CLS_TARGET_OS_OSX
+ NSInteger options = [[(NSDictionary*)activity objectForKey:@"options"] integerValue];
+
+ if (options & NSActivitySuddenTerminationDisabled) {
+ [[NSProcessInfo processInfo] enableSuddenTermination];
+ }
+
+ if (options & NSActivityAutomaticTerminationDisabled) {
+ [[NSProcessInfo processInfo]
+ enableAutomaticTermination:[(NSDictionary*)activity objectForKey:@"reason"]];
+ }
+#endif
+}
+
+void FIRCLSApplicationActivity(NSActivityOptions options, NSString* reason, void (^block)(void)) {
+ id activity = FIRCLSApplicationBeginActivity(options, reason);
+
+ block();
+
+ FIRCLSApplicationEndActivity(activity);
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h
new file mode 100644
index 000000000..f3c0bd718
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.h
@@ -0,0 +1,81 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include
+#include
+
+#include "FIRCLSFeatures.h"
+#include "FIRCLSFile.h"
+#include "FIRCLSMachO.h"
+
+__BEGIN_DECLS
+
+// Typically, apps seem to have ~300 binary images loaded
+#define CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT (512)
+#define CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE (32)
+#define CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME 0
+
+#define FIRCLSUUIDStringLength (33)
+
+typedef struct {
+ _Atomic(void*) volatile baseAddress;
+ uint64_t size;
+#if CLS_DWARF_UNWINDING_SUPPORTED
+ const void* ehFrame;
+#endif
+#if CLS_COMPACT_UNWINDING_SUPPORTED
+ const void* unwindInfo;
+#endif
+ const void* crashInfo;
+#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
+ char name[CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE];
+#endif
+} FIRCLSBinaryImageRuntimeNode;
+
+typedef struct {
+ char uuidString[FIRCLSUUIDStringLength];
+ bool encrypted;
+ FIRCLSMachOVersion builtSDK;
+ FIRCLSMachOVersion minSDK;
+ FIRCLSBinaryImageRuntimeNode node;
+ struct FIRCLSMachOSlice slice;
+ intptr_t vmaddr_slide;
+} FIRCLSBinaryImageDetails;
+
+typedef struct {
+ const char* path;
+} FIRCLSBinaryImageReadOnlyContext;
+
+typedef struct {
+ FIRCLSFile file;
+ FIRCLSBinaryImageRuntimeNode nodes[CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT];
+} FIRCLSBinaryImageReadWriteContext;
+
+void FIRCLSBinaryImageInit(FIRCLSBinaryImageReadOnlyContext* roContext,
+ FIRCLSBinaryImageReadWriteContext* rwContext);
+
+#if CLS_COMPACT_UNWINDING_SUPPORTED
+bool FIRCLSBinaryImageSafeFindImageForAddress(uintptr_t address,
+ FIRCLSBinaryImageRuntimeNode* image);
+bool FIRCLSBinaryImageSafeHasUnwindInfo(FIRCLSBinaryImageRuntimeNode* image);
+#endif
+
+bool FIRCLSBinaryImageFindImageForUUID(const char* uuidString,
+ FIRCLSBinaryImageDetails* imageDetails);
+
+bool FIRCLSBinaryImageRecordMainExecutable(FIRCLSFile* file);
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m
new file mode 100644
index 000000000..8b707bb44
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSBinaryImage.m
@@ -0,0 +1,571 @@
+// 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.
+
+#include "FIRCLSBinaryImage.h"
+
+#include
+#include
+
+#include
+
+#include
+
+#include "FIRCLSByteUtility.h"
+#include "FIRCLSFeatures.h"
+#include "FIRCLSFile.h"
+#include "FIRCLSGlobals.h"
+#include "FIRCLSHost.h"
+#include "FIRCLSMachO.h"
+#include "FIRCLSUtility.h"
+
+#include
+
+// this is defined only if __OPEN_SOURCE__ is *not* defined in the TVOS SDK's mach-o/loader.h
+// also, it has not yet made it back to the OSX SDKs, for example
+#ifndef LC_VERSION_MIN_TVOS
+#define LC_VERSION_MIN_TVOS 0x2F
+#endif
+
+#pragma mark Prototypes
+static bool FIRCLSBinaryImageOpenIfNeeded(bool* needsClosing);
+
+static void FIRCLSBinaryImageAddedCallback(const struct mach_header* mh, intptr_t vmaddr_slide);
+static void FIRCLSBinaryImageRemovedCallback(const struct mach_header* mh, intptr_t vmaddr_slide);
+static void FIRCLSBinaryImageChanged(bool added,
+ const struct mach_header* mh,
+ intptr_t vmaddr_slide);
+static bool FIRCLSBinaryImageFillInImageDetails(FIRCLSBinaryImageDetails* details);
+
+static void FIRCLSBinaryImageStoreNode(bool added, FIRCLSBinaryImageDetails imageDetails);
+static void FIRCLSBinaryImageRecordSlice(bool added, const FIRCLSBinaryImageDetails imageDetails);
+
+#pragma mark - Core API
+void FIRCLSBinaryImageInit(FIRCLSBinaryImageReadOnlyContext* roContext,
+ FIRCLSBinaryImageReadWriteContext* rwContext) {
+ // initialize our node array to all zeros
+ memset(&_firclsContext.writable->binaryImage, 0, sizeof(_firclsContext.writable->binaryImage));
+ _firclsContext.writable->binaryImage.file.fd = -1;
+
+ dispatch_async(FIRCLSGetBinaryImageQueue(), ^{
+ if (!FIRCLSUnlinkIfExists(_firclsContext.readonly->binaryimage.path)) {
+ FIRCLSSDKLog("Unable to reset the binary image log file %s\n", strerror(errno));
+ }
+
+ bool needsClosing; // unneeded
+ if (!FIRCLSBinaryImageOpenIfNeeded(&needsClosing)) {
+ FIRCLSSDKLog("Error: Unable to open the binary image log file during init\n");
+ }
+ });
+
+ _dyld_register_func_for_add_image(FIRCLSBinaryImageAddedCallback);
+ _dyld_register_func_for_remove_image(FIRCLSBinaryImageRemovedCallback);
+
+ dispatch_async(FIRCLSGetBinaryImageQueue(), ^{
+ FIRCLSFileClose(&_firclsContext.writable->binaryImage.file);
+ });
+}
+
+static bool FIRCLSBinaryImageOpenIfNeeded(bool* needsClosing) {
+ if (!FIRCLSIsValidPointer(_firclsContext.writable)) {
+ return false;
+ }
+
+ if (!FIRCLSIsValidPointer(_firclsContext.readonly)) {
+ return false;
+ }
+
+ if (!FIRCLSIsValidPointer(needsClosing)) {
+ return false;
+ }
+
+ *needsClosing = false;
+
+ if (FIRCLSFileIsOpen(&_firclsContext.writable->binaryImage.file)) {
+ return true;
+ }
+
+ if (!FIRCLSFileInitWithPath(&_firclsContext.writable->binaryImage.file,
+ _firclsContext.readonly->binaryimage.path, false)) {
+ FIRCLSSDKLog("Error: unable to open binary image log file\n");
+ return false;
+ }
+
+ *needsClosing = true;
+
+ return true;
+}
+
+#if CLS_COMPACT_UNWINDING_SUPPORTED
+bool FIRCLSBinaryImageSafeFindImageForAddress(uintptr_t address,
+ FIRCLSBinaryImageRuntimeNode* image) {
+ if (!FIRCLSContextIsInitialized()) {
+ return false;
+ }
+
+ if (address == 0) {
+ return false;
+ }
+
+ if (!FIRCLSIsValidPointer(image)) {
+ return false;
+ }
+
+ FIRCLSBinaryImageRuntimeNode* nodes = _firclsContext.writable->binaryImage.nodes;
+ if (!nodes) {
+ FIRCLSSDKLogError("The node structure is NULL\n");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) {
+ FIRCLSBinaryImageRuntimeNode* node = &nodes[i];
+ if (!FIRCLSIsValidPointer(node)) {
+ FIRCLSSDKLog(
+ "Invalid node pointer encountered in context's writable binary image at index %i", i);
+ continue;
+ }
+
+ if ((address >= (uintptr_t)node->baseAddress) &&
+ (address < (uintptr_t)node->baseAddress + node->size)) {
+ *image = *node; // copy the image
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool FIRCLSBinaryImageSafeHasUnwindInfo(FIRCLSBinaryImageRuntimeNode* image) {
+ return FIRCLSIsValidPointer(image->unwindInfo);
+}
+#endif
+
+bool FIRCLSBinaryImageFindImageForUUID(const char* uuidString,
+ FIRCLSBinaryImageDetails* imageDetails) {
+ if (!imageDetails || !uuidString) {
+ FIRCLSSDKLog("null input\n");
+ return false;
+ }
+
+ uint32_t imageCount = _dyld_image_count();
+
+ for (uint32_t i = 0; i < imageCount; ++i) {
+ const struct mach_header* mh = _dyld_get_image_header(i);
+
+ FIRCLSBinaryImageDetails image;
+
+ image.slice = FIRCLSMachOSliceWithHeader((void*)mh);
+ FIRCLSBinaryImageFillInImageDetails(&image);
+
+ if (strncmp(uuidString, image.uuidString, FIRCLSUUIDStringLength) == 0) {
+ *imageDetails = image;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#pragma mark - DYLD callback handlers
+static void FIRCLSBinaryImageAddedCallback(const struct mach_header* mh, intptr_t vmaddr_slide) {
+ FIRCLSBinaryImageChanged(true, mh, vmaddr_slide);
+}
+
+static void FIRCLSBinaryImageRemovedCallback(const struct mach_header* mh, intptr_t vmaddr_slide) {
+ FIRCLSBinaryImageChanged(false, mh, vmaddr_slide);
+}
+
+#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
+static bool FIRCLSBinaryImagePopulateRuntimeNodeName(FIRCLSBinaryImageDetails* details) {
+ if (!FIRCLSIsValidPointer(details)) {
+ return false;
+ }
+
+ memset(details->node.name, 0, CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE);
+
+ // We have limited storage space for the name. And, we really want to store
+ // "CoreFoundation", not "/System/Library/Fram", so we have to play tricks
+ // to make sure we get the right side of the string.
+ const char* imageName = FIRCLSMachOSliceGetExecutablePath(&details->slice);
+ if (!imageName) {
+ return false;
+ }
+
+ const size_t imageNameLength = strlen(imageName);
+
+ // Remember to leave one character for null-termination.
+ if (imageNameLength > CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1) {
+ imageName = imageName + (imageNameLength - (CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1));
+ }
+
+ // subtract one to make sure the string is always null-terminated
+ strncpy(details->node.name, imageName, CLS_BINARY_IMAGE_RUNTIME_NODE_NAME_SIZE - 1);
+
+ return true;
+}
+#endif
+
+// There were plans later to replace this with FIRCLSMachO
+static FIRCLSMachOSegmentCommand FIRCLSBinaryImageMachOGetSegmentCommand(
+ const struct load_command* cmd) {
+ FIRCLSMachOSegmentCommand segmentCommand;
+
+ memset(&segmentCommand, 0, sizeof(FIRCLSMachOSegmentCommand));
+
+ if (!cmd) {
+ return segmentCommand;
+ }
+
+ if (cmd->cmd == LC_SEGMENT) {
+ struct segment_command* segCmd = (struct segment_command*)cmd;
+
+ memcpy(segmentCommand.segname, segCmd->segname, 16);
+ segmentCommand.vmaddr = segCmd->vmaddr;
+ segmentCommand.vmsize = segCmd->vmsize;
+ } else if (cmd->cmd == LC_SEGMENT_64) {
+ struct segment_command_64* segCmd = (struct segment_command_64*)cmd;
+
+ memcpy(segmentCommand.segname, segCmd->segname, 16);
+ segmentCommand.vmaddr = segCmd->vmaddr;
+ segmentCommand.vmsize = segCmd->vmsize;
+ }
+
+ return segmentCommand;
+}
+
+static bool FIRCLSBinaryImageMachOSliceInitSectionByName(FIRCLSMachOSliceRef slice,
+ const char* segName,
+ const char* sectionName,
+ FIRCLSMachOSection* section) {
+ if (!FIRCLSIsValidPointer(slice)) {
+ return false;
+ }
+
+ if (!section) {
+ return false;
+ }
+
+ memset(section, 0, sizeof(FIRCLSMachOSection));
+
+ if (FIRCLSMachOSliceIs64Bit(slice)) {
+ const struct section_64* sect =
+ getsectbynamefromheader_64(slice->startAddress, segName, sectionName);
+ if (!sect) {
+ return false;
+ }
+
+ section->addr = sect->addr;
+ section->size = sect->size;
+ section->offset = sect->offset;
+ } else {
+ const struct section* sect = getsectbynamefromheader(slice->startAddress, segName, sectionName);
+ if (!sect) {
+ return false;
+ }
+
+ section->addr = sect->addr;
+ section->size = sect->size;
+ section->offset = sect->offset;
+ }
+
+ return true;
+}
+
+static bool FIRCLSBinaryImageFillInImageDetails(FIRCLSBinaryImageDetails* details) {
+ if (!FIRCLSIsValidPointer(details)) {
+ return false;
+ }
+
+ if (!FIRCLSIsValidPointer(details->slice.startAddress)) {
+ return false;
+ }
+
+#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
+ // this is done for debugging purposes, so if it fails, its ok to continue
+ FIRCLSBinaryImagePopulateRuntimeNodeName(details);
+#endif
+
+ // This cast might look a little dubious, but its just because we're using the same
+ // struct types in a few different places.
+ details->node.baseAddress = (void* volatile)details->slice.startAddress;
+
+ FIRCLSMachOSliceEnumerateLoadCommands(
+ &details->slice, ^(uint32_t type, uint32_t size, const struct load_command* cmd) {
+ switch (type) {
+ case LC_UUID: {
+ const uint8_t* uuid = FIRCLSMachOGetUUID(cmd);
+ FIRCLSSafeHexToString(uuid, 16, details->uuidString);
+ } break;
+ case LC_ENCRYPTION_INFO:
+ details->encrypted = FIRCLSMachOGetEncrypted(cmd);
+ break;
+ case LC_SEGMENT:
+ case LC_SEGMENT_64: {
+ FIRCLSMachOSegmentCommand segmentCommand = FIRCLSBinaryImageMachOGetSegmentCommand(cmd);
+
+ if (strncmp(segmentCommand.segname, SEG_TEXT, sizeof(SEG_TEXT)) == 0) {
+ details->node.size = segmentCommand.vmsize;
+ }
+ } break;
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ case LC_VERSION_MIN_TVOS:
+ case LC_VERSION_MIN_WATCHOS:
+ details->minSDK = FIRCLSMachOGetMinimumOSVersion(cmd);
+ details->builtSDK = FIRCLSMachOGetLinkedSDKVersion(cmd);
+ break;
+ }
+ });
+
+ // We look up the section we want, and we *should* be able to use:
+ //
+ // address of data we want = start address + section.offset
+ //
+ // However, the offset value is coming back funky in iOS 9. So, instead we look up the address
+ // the section should be loaded at, and compute the offset by looking up the address of the
+ // segment itself.
+
+ FIRCLSMachOSection section;
+
+#if CLS_COMPACT_UNWINDING_SUPPORTED
+ if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_TEXT, "__unwind_info",
+ §ion)) {
+ details->node.unwindInfo = (void*)(section.addr + details->vmaddr_slide);
+ }
+#endif
+
+#if CLS_DWARF_UNWINDING_SUPPORTED
+ if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_TEXT, "__eh_frame",
+ §ion)) {
+ details->node.ehFrame = (void*)(section.addr + details->vmaddr_slide);
+ }
+#endif
+
+ if (FIRCLSBinaryImageMachOSliceInitSectionByName(&details->slice, SEG_DATA, "__crash_info",
+ §ion)) {
+ details->node.crashInfo = (void*)(section.addr + details->vmaddr_slide);
+ }
+
+ return true;
+}
+
+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
+ dispatch_async(FIRCLSGetBinaryImageQueue(), ^{
+ FIRCLSBinaryImageRecordSlice(added, imageDetails);
+ });
+}
+
+#pragma mark - In-Memory Storage
+static void FIRCLSBinaryImageStoreNode(bool added, FIRCLSBinaryImageDetails imageDetails) {
+ // This function is mutating a structure that needs to be accessed at crash time. We
+ // need to make sure the structure is always in as valid a state as possible.
+ // FIRCLSSDKLog("Storing %s node %p\n", added ? "loaded" : "unloaded",
+ // (void*)imageDetails.node.baseAddress);
+
+ if (!_firclsContext.writable) {
+ FIRCLSSDKLog("Error: Writable context is NULL\n");
+ return;
+ }
+
+ void* searchAddress = NULL;
+ bool success = false;
+ FIRCLSBinaryImageRuntimeNode* nodes = _firclsContext.writable->binaryImage.nodes;
+
+ if (!added) {
+ // capture the search address first
+ searchAddress = imageDetails.node.baseAddress;
+
+ // If we are removing a node, we need to set its entries to zero. By clearing all of
+ // these values, we can just copy in imageDetails.node. Using memset here is slightly
+ // weird, since we have to restore one field. But, this way, if/when the structure changes,
+ // we still do the right thing.
+ memset(&imageDetails.node, 0, sizeof(FIRCLSBinaryImageRuntimeNode));
+
+ // restore the baseAddress, which just got zeroed, and is used for indexing
+ imageDetails.node.baseAddress = searchAddress;
+ }
+
+ for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) {
+ FIRCLSBinaryImageRuntimeNode* node = &nodes[i];
+
+ if (!node) {
+ FIRCLSSDKLog("Error: Binary image storage is NULL\n");
+ break;
+ }
+
+ // navigate through the array, looking for our matching address
+ if (node->baseAddress != searchAddress) {
+ continue;
+ }
+
+ // Attempt to swap the base address with whatever we are searching for. Success means that
+ // entry has been claims/cleared. Failure means some other thread beat us to it.
+ if (atomic_compare_exchange_strong(&node->baseAddress, &searchAddress,
+ imageDetails.node.baseAddress)) {
+ *node = imageDetails.node;
+ success = true;
+
+ break;
+ }
+
+ // If this is an unload, getting here means two threads unloaded at the same time. I think
+ // that's highly unlikely, and possibly even impossible. So, I'm choosing to abort the process
+ // at this point.
+ if (!added) {
+ FIRCLSSDKLog("Error: Failed to swap during image unload\n");
+ break;
+ }
+ }
+
+ if (!success) {
+ FIRCLSSDKLog("Error: Unable to track a %s node %p\n", added ? "loaded" : "unloaded",
+ (void*)imageDetails.node.baseAddress);
+ }
+}
+
+#pragma mark - On-Disk Storage
+static void FIRCLSBinaryImageRecordDetails(FIRCLSFile* file,
+ const FIRCLSBinaryImageDetails imageDetails) {
+ if (!file) {
+ FIRCLSSDKLog("Error: file is invalid\n");
+ return;
+ }
+
+ FIRCLSFileWriteHashEntryString(file, "uuid", imageDetails.uuidString);
+ FIRCLSFileWriteHashEntryUint64(file, "base", (uintptr_t)imageDetails.slice.startAddress);
+ FIRCLSFileWriteHashEntryUint64(file, "size", imageDetails.node.size);
+}
+
+static void FIRCLSBinaryImageRecordLibraryFrameworkInfo(FIRCLSFile* file, const char* path) {
+ if (!file) {
+ FIRCLSSDKLog("Error: file is invalid\n");
+ return;
+ }
+
+ if (!path) {
+ return;
+ }
+
+ // Because this function is so expensive, we've decided to omit this info for all Apple-supplied
+ // frameworks. This really isn't that bad, because we can know their info ahead of time (within a
+ // small margin of error). With this implemenation, we will still record this info for any
+ // user-built framework, which in the end is the most important thing.
+ if (strncmp(path, "/System", 7) == 0) {
+ return;
+ }
+
+ // check to see if this is a potential framework bundle
+ if (!strstr(path, ".framework")) {
+ return;
+ }
+
+ // My.framework/Versions/A/My for OS X
+ // My.framework/My for iOS
+
+ NSString* frameworkPath = [NSString stringWithUTF8String:path];
+#if TARGET_OS_IPHONE
+ frameworkPath = [frameworkPath stringByDeletingLastPathComponent];
+#else
+ frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework/Versions/A
+ frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework/Versions
+ frameworkPath = [frameworkPath stringByDeletingLastPathComponent]; // My.framework
+#endif
+
+ NSBundle* const bundle = [NSBundle bundleWithPath:frameworkPath];
+
+ if (!bundle) {
+ return;
+ }
+
+ FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(file, "bundle_id", [bundle bundleIdentifier]);
+ FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(
+ file, "build_version", [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]);
+ FIRCLSFileWriteHashEntryNSStringUnlessNilOrEmpty(
+ file, "display_version", [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]);
+}
+
+static void FIRCLSBinaryImageRecordSlice(bool added, const FIRCLSBinaryImageDetails imageDetails) {
+ bool needsClosing = false;
+ if (!FIRCLSBinaryImageOpenIfNeeded(&needsClosing)) {
+ FIRCLSSDKLog("Error: unable to open binary image log file\n");
+ return;
+ }
+
+ FIRCLSFile* file = &_firclsContext.writable->binaryImage.file;
+
+ FIRCLSFileWriteSectionStart(file, added ? "load" : "unload");
+
+ FIRCLSFileWriteHashStart(file);
+
+ const char* path = FIRCLSMachOSliceGetExecutablePath((FIRCLSMachOSliceRef)&imageDetails.slice);
+
+ FIRCLSFileWriteHashEntryString(file, "path", path);
+
+ if (added) {
+ // this won't work if the binary has been unloaded
+ FIRCLSBinaryImageRecordLibraryFrameworkInfo(file, path);
+ }
+
+ FIRCLSBinaryImageRecordDetails(file, imageDetails);
+
+ FIRCLSFileWriteHashEnd(file);
+
+ FIRCLSFileWriteSectionEnd(file);
+
+ if (needsClosing) {
+ FIRCLSFileClose(file);
+ }
+}
+
+bool FIRCLSBinaryImageRecordMainExecutable(FIRCLSFile* file) {
+ FIRCLSBinaryImageDetails imageDetails;
+
+ memset(&imageDetails, 0, sizeof(FIRCLSBinaryImageDetails));
+
+ imageDetails.slice = FIRCLSMachOSliceGetCurrent();
+ FIRCLSBinaryImageFillInImageDetails(&imageDetails);
+
+ FIRCLSFileWriteSectionStart(file, "executable");
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSFileWriteHashEntryString(file, "architecture",
+ FIRCLSMachOSliceGetArchitectureName(&imageDetails.slice));
+
+ FIRCLSBinaryImageRecordDetails(file, imageDetails);
+ FIRCLSFileWriteHashEntryBoolean(file, "encrypted", imageDetails.encrypted);
+ FIRCLSFileWriteHashEntryString(file, "minimum_sdk_version",
+ [FIRCLSMachOFormatVersion(&imageDetails.minSDK) UTF8String]);
+ FIRCLSFileWriteHashEntryString(file, "built_sdk_version",
+ [FIRCLSMachOFormatVersion(&imageDetails.builtSDK) UTF8String]);
+
+ FIRCLSFileWriteHashEnd(file);
+ FIRCLSFileWriteSectionEnd(file);
+
+ return true;
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h
new file mode 100644
index 000000000..bdb43418c
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.h
@@ -0,0 +1,121 @@
+// 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.
+
+#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
+#include
+
+// The purpose of the crash context is to hold values that absolutely must be read and/or written at
+// crash time. For robustness against memory corruption, they are protected with guard pages.
+// Further, the context is seperated into read-only and read-write sections.
+
+__BEGIN_DECLS
+
+#ifdef __OBJC__
+@class FIRCLSInternalReport;
+@class FIRCLSSettings;
+@class FIRCLSInstallIdentifierModel;
+@class FIRCLSFileManager;
+#endif
+
+typedef struct {
+ volatile bool initialized;
+ volatile bool debuggerAttached;
+ const char* previouslyCrashedFileFullPath;
+ const char* logPath;
+#if CLS_USE_SIGALTSTACK
+ void* signalStack;
+#endif
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ void* machStack;
+#endif
+ void* delegate;
+ void* callbackDelegate;
+
+ FIRCLSBinaryImageReadOnlyContext binaryimage;
+ FIRCLSExceptionReadOnlyContext exception;
+ FIRCLSHostReadOnlyContext host;
+ FIRCLSSignalReadContext signal;
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ FIRCLSMachExceptionReadContext machException;
+#endif
+ FIRCLSUserLoggingReadOnlyContext logging;
+} FIRCLSReadOnlyContext;
+
+typedef struct {
+ FIRCLSInternalLoggingWritableContext internalLogging;
+ volatile bool crashOccurred;
+ FIRCLSBinaryImageReadWriteContext binaryImage;
+ FIRCLSUserLoggingWritableContext logging;
+ FIRCLSExceptionWritableContext exception;
+} FIRCLSReadWriteContext;
+
+typedef struct {
+ FIRCLSReadOnlyContext* readonly;
+ FIRCLSReadWriteContext* writable;
+ FIRCLSAllocatorRef allocator;
+} FIRCLSContext;
+
+typedef struct {
+ void* delegate;
+ const char* customBundleId;
+ const char* rootPath;
+ const char* previouslyCrashedFileRootPath;
+ const char* sessionId;
+ const char* installId;
+ const char* betaToken;
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ exception_mask_t machExceptionMask;
+#endif
+ bool errorsEnabled;
+ bool customExceptionsEnabled;
+ uint32_t maxCustomExceptions;
+ uint32_t maxErrorLogSize;
+ uint32_t maxLogSize;
+ uint32_t maxKeyValues;
+} FIRCLSContextInitData;
+
+#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);
+void FIRCLSContextBaseDeinit(void);
+
+bool FIRCLSContextIsInitialized(void);
+bool FIRCLSContextHasCrashed(void);
+void FIRCLSContextMarkHasCrashed(void);
+bool FIRCLSContextMarkAndCheckIfCrashed(void);
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m
new file mode 100644
index 000000000..26d46c72e
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSContext.m
@@ -0,0 +1,468 @@
+// 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.
+
+#include "FIRCLSContext.h"
+
+#include
+#include
+
+#import "FIRCLSFileManager.h"
+#import "FIRCLSInstallIdentifierModel.h"
+#import "FIRCLSInternalReport.h"
+#import "FIRCLSSettings.h"
+
+#include "FIRCLSApplication.h"
+#include "FIRCLSCrashedMarkerFile.h"
+#include "FIRCLSDefines.h"
+#include "FIRCLSFeatures.h"
+#include "FIRCLSGlobals.h"
+#include "FIRCLSProcess.h"
+#include "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
+// first allocation is gauranteed to be page-aligned.
+//
+// CLS_SIGNAL_HANDLER_STACK_SIZE and CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE are platform dependant,
+// defined as 0 for tv/watch.
+#define CLS_MINIMUM_READWRITE_SIZE \
+ (CLS_SIGNAL_HANDLER_STACK_SIZE + CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE + \
+ sizeof(FIRCLSReadWriteContext))
+
+// We need enough space here for the context, plus storage for strings.
+#define CLS_MINIMUM_READABLE_SIZE (sizeof(FIRCLSReadOnlyContext) + 4096 * 4)
+
+static const int64_t FIRCLSContextInitWaitTime = 5LL * NSEC_PER_SEC;
+
+static bool FIRCLSContextRecordMetadata(const char* path, const FIRCLSContextInitData* initData);
+static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component);
+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
+ // from the last time they were fetched
+
+ FIRCLSContextInitData initData;
+
+ 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];
+ initData.errorsEnabled = [settings errorReportingEnabled];
+ initData.customExceptionsEnabled = [settings customExceptionsEnabled];
+ initData.maxCustomExceptions = [settings maxCustomExceptions];
+ initData.maxErrorLogSize = [settings errorLogBufferSize];
+ initData.maxLogSize = [settings logBufferSize];
+ initData.maxKeyValues = [settings maxCustomKeys];
+
+ // 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,
+ // we're going to leave this disabled. It does work in the exception case, but will ultimtely
+ // result in the following crash to be discared. Usually that crash isn't interesting. But, if it
+ // was, we'd never have a chance to see it.
+ initData.delegate = nil;
+
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ __block exception_mask_t mask = 0;
+
+ // TODO(b/141241224) This if statement was hardcoded to no, so this block was never run
+ // FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
+ // if ([self.delegate ensureDeliveryOfUnixSignal:signal]) {
+ // mask |= FIRCLSMachExceptionMaskForSignal(signal);
+ // }
+ // });
+
+ initData.machExceptionMask = mask;
+#endif
+
+ return initData;
+}
+
+bool FIRCLSContextInitialize(FIRCLSInternalReport* report,
+ FIRCLSSettings* settings,
+ FIRCLSInstallIdentifierModel* installIDModel,
+ FIRCLSFileManager* fileManager) {
+ FIRCLSContextInitData initDataObj =
+ FIRCLSContextBuildInitData(report, settings, installIDModel, fileManager);
+ FIRCLSContextInitData* initData = &initDataObj;
+
+ if (!initData) {
+ return false;
+ }
+
+ FIRCLSContextBaseInit();
+
+ dispatch_group_t group = dispatch_group_create();
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+
+ if (!FIRCLSIsValidPointer(initData->rootPath)) {
+ return false;
+ }
+
+ NSString* rootPath = [NSString stringWithUTF8String:initData->rootPath];
+
+ // setup our SDK log file synchronously, because other calls may depend on it
+ _firclsContext.readonly->logPath = FIRCLSContextAppendToRoot(rootPath, @"sdk.log");
+ if (!FIRCLSUnlinkIfExists(_firclsContext.readonly->logPath)) {
+ FIRCLSErrorLog(@"Unable to write initialize SDK write paths %s", strerror(errno));
+ }
+
+ // some values that aren't tied to particular subsystem
+ _firclsContext.readonly->debuggerAttached = FIRCLSProcessDebuggerAttached();
+ _firclsContext.readonly->delegate = initData->delegate;
+
+ dispatch_group_async(group, queue, ^{
+ FIRCLSHostInitialize(&_firclsContext.readonly->host);
+ });
+
+ dispatch_group_async(group, queue, ^{
+ _firclsContext.readonly->logging.errorStorage.maxSize = 0;
+ _firclsContext.readonly->logging.errorStorage.maxEntries =
+ initData->errorsEnabled ? initData->maxCustomExceptions : 0;
+ _firclsContext.readonly->logging.errorStorage.restrictBySize = false;
+ _firclsContext.readonly->logging.errorStorage.entryCount =
+ &_firclsContext.writable->logging.errorsCount;
+ _firclsContext.readonly->logging.errorStorage.aPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportErrorAFile);
+ _firclsContext.readonly->logging.errorStorage.bPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportErrorBFile);
+
+ _firclsContext.readonly->logging.logStorage.maxSize = initData->maxLogSize;
+ _firclsContext.readonly->logging.logStorage.maxEntries = 0;
+ _firclsContext.readonly->logging.logStorage.restrictBySize = true;
+ _firclsContext.readonly->logging.logStorage.entryCount = NULL;
+ _firclsContext.readonly->logging.logStorage.aPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportLogAFile);
+ _firclsContext.readonly->logging.logStorage.bPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportLogBFile);
+ _firclsContext.readonly->logging.customExceptionStorage.aPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportCustomExceptionAFile);
+ _firclsContext.readonly->logging.customExceptionStorage.bPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportCustomExceptionBFile);
+ _firclsContext.readonly->logging.customExceptionStorage.maxSize = 0;
+ _firclsContext.readonly->logging.customExceptionStorage.restrictBySize = false;
+ _firclsContext.readonly->logging.customExceptionStorage.maxEntries =
+ initData->maxCustomExceptions;
+ _firclsContext.readonly->logging.customExceptionStorage.entryCount =
+ &_firclsContext.writable->exception.customExceptionCount;
+
+ _firclsContext.readonly->logging.userKVStorage.maxCount = initData->maxKeyValues;
+ _firclsContext.readonly->logging.userKVStorage.incrementalPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportUserIncrementalKVFile);
+ _firclsContext.readonly->logging.userKVStorage.compactedPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportUserCompactedKVFile);
+
+ _firclsContext.readonly->logging.internalKVStorage.maxCount = 32; // Hardcode = bad
+ _firclsContext.readonly->logging.internalKVStorage.incrementalPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportInternalIncrementalKVFile);
+ _firclsContext.readonly->logging.internalKVStorage.compactedPath =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportInternalCompactedKVFile);
+
+ FIRCLSUserLoggingInit(&_firclsContext.readonly->logging, &_firclsContext.writable->logging);
+ });
+
+ dispatch_group_async(group, queue, ^{
+ _firclsContext.readonly->binaryimage.path =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportBinaryImageFile);
+
+ FIRCLSBinaryImageInit(&_firclsContext.readonly->binaryimage,
+ &_firclsContext.writable->binaryImage);
+ });
+
+ dispatch_group_async(group, queue, ^{
+ NSString* rootPath = [NSString stringWithUTF8String:initData->previouslyCrashedFileRootPath];
+ NSString* fileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName];
+ _firclsContext.readonly->previouslyCrashedFileFullPath =
+ FIRCLSContextAppendToRoot(rootPath, fileName);
+ });
+
+ if (!_firclsContext.readonly->debuggerAttached) {
+ dispatch_group_async(group, queue, ^{
+ _firclsContext.readonly->signal.path =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportSignalFile);
+
+ FIRCLSSignalInitialize(&_firclsContext.readonly->signal);
+ });
+
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ dispatch_group_async(group, queue, ^{
+ _firclsContext.readonly->machException.path =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportMachExceptionFile);
+
+ FIRCLSMachExceptionInit(&_firclsContext.readonly->machException, initData->machExceptionMask);
+ });
+#endif
+
+ dispatch_group_async(group, queue, ^{
+ _firclsContext.readonly->exception.path =
+ FIRCLSContextAppendToRoot(rootPath, FIRCLSReportExceptionFile);
+ _firclsContext.readonly->exception.maxCustomExceptions =
+ initData->customExceptionsEnabled ? initData->maxCustomExceptions : 0;
+
+ FIRCLSExceptionInitialize(&_firclsContext.readonly->exception,
+ &_firclsContext.writable->exception, initData->delegate);
+ });
+ } else {
+ FIRCLSSDKLog("Debugger present - not installing handlers\n");
+ }
+
+ dispatch_group_async(group, queue, ^{
+ const char* metaDataPath = [[rootPath stringByAppendingPathComponent:FIRCLSReportMetadataFile]
+ fileSystemRepresentation];
+ if (!FIRCLSContextRecordMetadata(metaDataPath, initData)) {
+ FIRCLSSDKLog("Unable to record context metadata\n");
+ }
+ });
+
+ // At this point we need to do two things. First, we need to do our memory protection *only* after
+ // all of these initialization steps are really done. But, we also want to wait as long as
+ // possible for these to be complete. If we do not, there's a chance that we will not be able to
+ // correctly report a crash shortly after start.
+
+ // Note at this will retain the group, so its totally fine to release the group here.
+ dispatch_group_notify(group, queue, ^{
+ _firclsContext.readonly->initialized = true;
+ __sync_synchronize();
+
+ if (!FIRCLSAllocatorProtect(_firclsContext.allocator)) {
+ FIRCLSSDKLog("Error: Memory protection failed\n");
+ }
+ });
+
+ if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, FIRCLSContextInitWaitTime)) !=
+ 0) {
+ FIRCLSSDKLog("Error: Delayed initialization\n");
+ }
+
+ 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();
+
+ NSString* loggingQueueName = [sdkBundleID stringByAppendingString:@".logging"];
+ NSString* binaryImagesQueueName = [sdkBundleID stringByAppendingString:@".binary-images"];
+ NSString* exceptionQueueName = [sdkBundleID stringByAppendingString:@".exception"];
+
+ _firclsLoggingQueue = dispatch_queue_create([loggingQueueName UTF8String], DISPATCH_QUEUE_SERIAL);
+ _firclsBinaryImageQueue =
+ dispatch_queue_create([binaryImagesQueueName UTF8String], DISPATCH_QUEUE_SERIAL);
+ _firclsExceptionQueue =
+ dispatch_queue_create([exceptionQueueName UTF8String], DISPATCH_QUEUE_SERIAL);
+
+ FIRCLSContextAllocate(&_firclsContext);
+
+ _firclsContext.writable->internalLogging.logFd = -1;
+ _firclsContext.writable->internalLogging.logLevel = FIRCLSInternalLogLevelDebug;
+ _firclsContext.writable->crashOccurred = false;
+
+ _firclsContext.readonly->initialized = false;
+
+ __sync_synchronize();
+}
+
+static void FIRCLSContextAllocate(FIRCLSContext* context) {
+ // create the allocator, and the contexts
+ // The ordering here is really important, because the "stack" variable must be
+ // page-aligned. There's no mechanism to ask the allocator to do alignment, but we
+ // do know the very first allocation in a region is aligned to a page boundary.
+
+ context->allocator = FIRCLSAllocatorCreate(CLS_MINIMUM_READWRITE_SIZE, CLS_MINIMUM_READABLE_SIZE);
+
+ context->readonly =
+ FIRCLSAllocatorSafeAllocate(context->allocator, sizeof(FIRCLSReadOnlyContext), CLS_READONLY);
+ memset(context->readonly, 0, sizeof(FIRCLSReadOnlyContext));
+
+#if CLS_MEMORY_PROTECTION_ENABLED
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ context->readonly->machStack = FIRCLSAllocatorSafeAllocate(
+ context->allocator, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE, CLS_READWRITE);
+#endif
+#if CLS_USE_SIGALTSTACK
+ context->readonly->signalStack =
+ FIRCLSAllocatorSafeAllocate(context->allocator, CLS_SIGNAL_HANDLER_STACK_SIZE, CLS_READWRITE);
+#endif
+#else
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ context->readonly->machStack = valloc(CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE);
+#endif
+#if CLS_USE_SIGALTSTACK
+ context->readonly->signalStack = valloc(CLS_SIGNAL_HANDLER_STACK_SIZE);
+#endif
+#endif
+
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ memset(_firclsContext.readonly->machStack, 0, CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE);
+#endif
+#if CLS_USE_SIGALTSTACK
+ memset(_firclsContext.readonly->signalStack, 0, CLS_SIGNAL_HANDLER_STACK_SIZE);
+#endif
+
+ context->writable = FIRCLSAllocatorSafeAllocate(context->allocator,
+ sizeof(FIRCLSReadWriteContext), CLS_READWRITE);
+ memset(context->writable, 0, sizeof(FIRCLSReadWriteContext));
+}
+
+void FIRCLSContextBaseDeinit(void) {
+ _firclsContext.readonly->initialized = false;
+
+ FIRCLSAllocatorDestroy(_firclsContext.allocator);
+}
+
+bool FIRCLSContextIsInitialized(void) {
+ __sync_synchronize();
+ if (!FIRCLSIsValidPointer(_firclsContext.readonly)) {
+ return false;
+ }
+
+ return _firclsContext.readonly->initialized;
+}
+
+bool FIRCLSContextHasCrashed(void) {
+ if (!FIRCLSContextIsInitialized()) {
+ return false;
+ }
+
+ // we've already run a full barrier above, so this read is ok
+ return _firclsContext.writable->crashOccurred;
+}
+
+void FIRCLSContextMarkHasCrashed(void) {
+ if (!FIRCLSContextIsInitialized()) {
+ return;
+ }
+
+ _firclsContext.writable->crashOccurred = true;
+ __sync_synchronize();
+}
+
+bool FIRCLSContextMarkAndCheckIfCrashed(void) {
+ if (!FIRCLSContextIsInitialized()) {
+ return false;
+ }
+
+ if (_firclsContext.writable->crashOccurred) {
+ return true;
+ }
+
+ _firclsContext.writable->crashOccurred = true;
+ __sync_synchronize();
+
+ return false;
+}
+
+static const char* FIRCLSContextAppendToRoot(NSString* root, NSString* component) {
+ return FIRCLSDupString(
+ [[root stringByAppendingPathComponent:component] fileSystemRepresentation]);
+}
+
+static bool FIRCLSContextRecordIdentity(FIRCLSFile* file, const FIRCLSContextInitData* initData) {
+ FIRCLSFileWriteSectionStart(file, "identity");
+
+ 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);
+ FIRCLSFileWriteHashEntryUint64(file, "started_at", time(NULL));
+
+ FIRCLSFileWriteHashEntryString(file, "session_id", initData->sessionId);
+ FIRCLSFileWriteHashEntryString(file, "install_id", initData->installId);
+ FIRCLSFileWriteHashEntryString(file, "beta_token", initData->betaToken);
+ FIRCLSFileWriteHashEntryBoolean(file, "absolute_log_timestamps", true);
+
+ FIRCLSFileWriteHashEnd(file);
+ FIRCLSFileWriteSectionEnd(file);
+
+ return true;
+}
+
+static bool FIRCLSContextRecordApplication(FIRCLSFile* file, const char* customBundleId) {
+ FIRCLSFileWriteSectionStart(file, "application");
+
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSFileWriteHashEntryString(file, "bundle_id",
+ [FIRCLSApplicationGetBundleIdentifier() UTF8String]);
+ FIRCLSFileWriteHashEntryString(file, "custom_bundle_id", customBundleId);
+ FIRCLSFileWriteHashEntryString(file, "build_version",
+ [FIRCLSApplicationGetBundleVersion() UTF8String]);
+ FIRCLSFileWriteHashEntryString(file, "display_version",
+ [FIRCLSApplicationGetShortBundleVersion() UTF8String]);
+ FIRCLSFileWriteHashEntryString(file, "extension_id",
+ [FIRCLSApplicationExtensionPointIdentifier() UTF8String]);
+
+ FIRCLSFileWriteHashEnd(file);
+ FIRCLSFileWriteSectionEnd(file);
+
+ return true;
+}
+
+static bool FIRCLSContextRecordMetadata(const char* path, const FIRCLSContextInitData* initData) {
+ if (!FIRCLSUnlinkIfExists(path)) {
+ FIRCLSSDKLog("Unable to unlink existing metadata file %s\n", strerror(errno));
+ }
+
+ FIRCLSFile file;
+
+ if (!FIRCLSFileInitWithPath(&file, path, false)) {
+ FIRCLSSDKLog("Unable to open metadata file %s\n", strerror(errno));
+ return false;
+ }
+
+ if (!FIRCLSContextRecordIdentity(&file, initData)) {
+ FIRCLSSDKLog("Unable to write out identity metadata\n");
+ }
+
+ if (!FIRCLSHostRecord(&file)) {
+ FIRCLSSDKLog("Unable to write out host metadata\n");
+ }
+
+ if (!FIRCLSContextRecordApplication(&file, initData->customBundleId)) {
+ FIRCLSSDKLog("Unable to write out application metadata\n");
+ }
+
+ if (!FIRCLSBinaryImageRecordMainExecutable(&file)) {
+ FIRCLSSDKLog("Unable to write out executable metadata\n");
+ }
+
+ FIRCLSFileClose(&file);
+
+ return true;
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c
new file mode 100644
index 000000000..5dd67fdec
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.c
@@ -0,0 +1,31 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "FIRCLSCrashedMarkerFile.h"
+#include "FIRCLSFile.h"
+#include "FIRCLSUtility.h"
+
+const char *FIRCLSCrashedMarkerFileName = "previously-crashed";
+
+void FIRCLSCreateCrashedMarkerFile() {
+ FIRCLSFile file;
+
+ if (!FIRCLSFileInitWithPath(&file, _firclsContext.readonly->previouslyCrashedFileFullPath, false)) {
+ FIRCLSSDKLog("Unable to create the crashed marker file\n");
+ return;
+ }
+
+ FIRCLSFileClose(&file);
+ FIRCLSSDKLog("Created the crashed marker file\n");
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h
new file mode 100644
index 000000000..ccf427673
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSCrashedMarkerFile.h
@@ -0,0 +1,19 @@
+// 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.
+
+#include
+
+extern const char *FIRCLSCrashedMarkerFileName;
+
+void FIRCLSCreateCrashedMarkerFile(void);
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h
new file mode 100644
index 000000000..4f125cf08
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSGlobals.h
@@ -0,0 +1,28 @@
+// 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.
+
+#include "FIRCLSContext.h"
+
+__BEGIN_DECLS
+
+extern FIRCLSContext _firclsContext;
+extern dispatch_queue_t _firclsLoggingQueue;
+extern dispatch_queue_t _firclsBinaryImageQueue;
+extern dispatch_queue_t _firclsExceptionQueue;
+
+#define FIRCLSGetLoggingQueue() (_firclsLoggingQueue)
+#define FIRCLSGetBinaryImageQueue() (_firclsBinaryImageQueue)
+#define FIRCLSGetExceptionQueue() (_firclsExceptionQueue)
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h
new file mode 100644
index 000000000..24b1acd13
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.h
@@ -0,0 +1,37 @@
+// 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.
+
+#pragma once
+
+#include
+#include
+
+#include "FIRCLSFile.h"
+
+typedef struct {
+ const char* documentDirectoryPath;
+ vm_size_t pageSize;
+} FIRCLSHostReadOnlyContext;
+
+__BEGIN_DECLS
+
+void FIRCLSHostInitialize(FIRCLSHostReadOnlyContext* roContext);
+
+vm_size_t FIRCLSHostGetPageSize(void);
+
+bool FIRCLSHostRecord(FIRCLSFile* file);
+
+void FIRCLSHostWriteDiskUsage(FIRCLSFile* file);
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m
new file mode 100644
index 000000000..87ff0f7ae
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSHost.m
@@ -0,0 +1,161 @@
+// 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.
+
+#include "FIRCLSHost.h"
+
+#include
+#include
+#include
+
+#import "FIRCLSApplication.h"
+#include "FIRCLSDefines.h"
+#import "FIRCLSFABHost.h"
+#include "FIRCLSFile.h"
+#include "FIRCLSGlobals.h"
+#include "FIRCLSUtility.h"
+
+#if TARGET_OS_IPHONE
+#import
+#else
+#import
+#endif
+
+#define CLS_HOST_SYSCTL_BUFFER_SIZE (128)
+
+#if CLS_CPU_ARM64
+#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 16)
+#else
+// return 4K, which is correct for all platforms except arm64, currently
+#define CLS_MAX_NATIVE_PAGE_SIZE (1024 * 4)
+#endif
+#define CLS_MIN_NATIVE_PAGE_SIZE (1024 * 4)
+
+#pragma mark Prototypes
+static void FIRCLSHostWriteSysctlEntry(
+ FIRCLSFile* file, const char* key, const char* sysctlKey, void* buffer, size_t bufferSize);
+static void FIRCLSHostWriteModelInfo(FIRCLSFile* file);
+static void FIRCLSHostWriteOSVersionInfo(FIRCLSFile* file);
+
+#pragma mark - API
+void FIRCLSHostInitialize(FIRCLSHostReadOnlyContext* roContext) {
+ _firclsContext.readonly->host.pageSize = FIRCLSHostGetPageSize();
+ _firclsContext.readonly->host.documentDirectoryPath = NULL;
+
+ // determine where the document directory is mounted, so we can get file system statistics later
+ NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ if ([paths count]) {
+ _firclsContext.readonly->host.documentDirectoryPath =
+ FIRCLSDupString([[paths objectAtIndex:0] fileSystemRepresentation]);
+ }
+}
+
+vm_size_t FIRCLSHostGetPageSize(void) {
+ size_t size;
+ int pageSize;
+
+ // hw.pagesize is defined as HW_PAGESIZE, which is an int. It's important to match
+ // 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.
+
+ 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;
+ }
+
+ // if the returned size is not the expected value, abort
+ if (size != sizeof(pageSize)) {
+ return CLS_MAX_NATIVE_PAGE_SIZE;
+ }
+
+ // 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 < CLS_MIN_NATIVE_PAGE_SIZE) {
+ return CLS_MIN_NATIVE_PAGE_SIZE;
+ }
+
+ return pageSize;
+}
+
+static void FIRCLSHostWriteSysctlEntry(
+ FIRCLSFile* file, const char* key, const char* sysctlKey, void* buffer, size_t bufferSize) {
+ if (sysctlbyname(sysctlKey, buffer, &bufferSize, NULL, 0) != 0) {
+ FIRCLSFileWriteHashEntryString(file, key, "(failed)");
+ return;
+ }
+
+ FIRCLSFileWriteHashEntryString(file, key, buffer);
+}
+
+static void FIRCLSHostWriteModelInfo(FIRCLSFile* file) {
+ FIRCLSFileWriteHashEntryString(file, "model", [FIRCLSHostModelInfo() UTF8String]);
+
+ // allocate a static buffer for the sysctl values, which are typically
+ // quite short
+ char buffer[CLS_HOST_SYSCTL_BUFFER_SIZE];
+
+#if TARGET_OS_EMBEDDED
+ FIRCLSHostWriteSysctlEntry(file, "machine", "hw.model", buffer, CLS_HOST_SYSCTL_BUFFER_SIZE);
+#else
+ FIRCLSHostWriteSysctlEntry(file, "machine", "hw.machine", buffer, CLS_HOST_SYSCTL_BUFFER_SIZE);
+ FIRCLSHostWriteSysctlEntry(file, "cpu", "machdep.cpu.brand_string", buffer,
+ CLS_HOST_SYSCTL_BUFFER_SIZE);
+#endif
+}
+
+static void FIRCLSHostWriteOSVersionInfo(FIRCLSFile* file) {
+ FIRCLSFileWriteHashEntryString(file, "os_build_version", [FIRCLSHostOSBuildVersion() UTF8String]);
+ FIRCLSFileWriteHashEntryString(file, "os_display_version",
+ [FIRCLSHostOSDisplayVersion() UTF8String]);
+ FIRCLSFileWriteHashEntryString(file, "platform", [FIRCLSApplicationGetPlatform() UTF8String]);
+}
+
+bool FIRCLSHostRecord(FIRCLSFile* file) {
+ FIRCLSFileWriteSectionStart(file, "host");
+
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSHostWriteModelInfo(file);
+ FIRCLSHostWriteOSVersionInfo(file);
+ FIRCLSFileWriteHashEntryString(file, "locale",
+ [[[NSLocale currentLocale] localeIdentifier] UTF8String]);
+
+ FIRCLSFileWriteHashEnd(file);
+
+ FIRCLSFileWriteSectionEnd(file);
+
+ return true;
+}
+
+void FIRCLSHostWriteDiskUsage(FIRCLSFile* file) {
+ struct statfs tStats;
+
+ FIRCLSFileWriteSectionStart(file, "storage");
+
+ FIRCLSFileWriteHashStart(file);
+
+ if (statfs(_firclsContext.readonly->host.documentDirectoryPath, &tStats) == 0) {
+ FIRCLSFileWriteHashEntryUint64(file, "free", tStats.f_bavail * tStats.f_bsize);
+ FIRCLSFileWriteHashEntryUint64(file, "total", tStats.f_blocks * tStats.f_bsize);
+ }
+
+ FIRCLSFileWriteHashEnd(file);
+
+ FIRCLSFileWriteSectionEnd(file);
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c
new file mode 100644
index 000000000..415466ec4
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.c
@@ -0,0 +1,824 @@
+// 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.
+
+#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
+#include
+#include
+#include
+
+#define THREAD_NAME_BUFFER_SIZE (64)
+
+#pragma mark Prototypes
+static bool FIRCLSProcessGetThreadName(FIRCLSProcess *process,
+ thread_t thread,
+ char *buffer,
+ size_t length);
+static const char *FIRCLSProcessGetThreadDispatchQueueName(FIRCLSProcess *process, thread_t thread);
+
+#pragma mark - API
+bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uapVoid) {
+ if (!process) {
+ return false;
+ }
+
+ process->task = mach_task_self();
+ process->thisThread = mach_thread_self();
+ process->crashedThread = crashedThread;
+ process->uapVoid = uapVoid;
+
+ if (task_threads(process->task, &process->threads, &process->threadCount) != KERN_SUCCESS) {
+ // failed to get all threads
+ process->threadCount = 0;
+ FIRCLSSDKLog("Error: unable to get task threads\n");
+
+ return false;
+ }
+
+ return true;
+}
+
+bool FIRCLSProcessDestroy(FIRCLSProcess *process) {
+ return false;
+}
+
+// https://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+bool FIRCLSProcessDebuggerAttached(void) {
+ int junk;
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+ size = sizeof(info);
+ junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
+ if (junk != 0) {
+ FIRCLSSDKLog("sysctl failed while trying to get kinfo_proc\n");
+ return false;
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+ return (info.kp_proc.p_flag & P_TRACED) != 0;
+}
+
+#pragma mark - Thread Support
+static bool FIRCLSProcessIsCurrentThread(FIRCLSProcess *process, thread_t thread) {
+ return MACH_PORT_INDEX(process->thisThread) == MACH_PORT_INDEX(thread);
+}
+
+static bool FIRCLSProcessIsCrashedThread(FIRCLSProcess *process, thread_t thread) {
+ return MACH_PORT_INDEX(process->crashedThread) == MACH_PORT_INDEX(thread);
+}
+
+static uint32_t FIRCLSProcessGetThreadCount(FIRCLSProcess *process) {
+ return process->threadCount;
+}
+
+static thread_t FIRCLSProcessGetThread(FIRCLSProcess *process, uint32_t index) {
+ if (index >= process->threadCount) {
+ return MACH_PORT_NULL;
+ }
+
+ return process->threads[index];
+}
+
+bool FIRCLSProcessSuspendAllOtherThreads(FIRCLSProcess *process) {
+ mach_msg_type_number_t i;
+ bool success;
+
+ success = true;
+ for (i = 0; i < process->threadCount; ++i) {
+ thread_t thread;
+
+ thread = FIRCLSProcessGetThread(process, i);
+
+ if (FIRCLSProcessIsCurrentThread(process, thread)) {
+ continue;
+ }
+
+ // FIXME: workaround to get this building on watch, but we need to suspend/resume threads!
+#if CLS_CAN_SUSPEND_THREADS
+ success = success && (thread_suspend(thread) == KERN_SUCCESS);
+#endif
+ }
+
+ return success;
+}
+
+bool FIRCLSProcessResumeAllOtherThreads(FIRCLSProcess *process) {
+ mach_msg_type_number_t i;
+ bool success;
+
+ success = true;
+ for (i = 0; i < process->threadCount; ++i) {
+ thread_t thread;
+
+ thread = FIRCLSProcessGetThread(process, i);
+
+ if (FIRCLSProcessIsCurrentThread(process, thread)) {
+ continue;
+ }
+
+ // FIXME: workaround to get this building on watch, but we need to suspend/resume threads!
+#if CLS_CAN_SUSPEND_THREADS
+ success = success && (thread_resume(thread) == KERN_SUCCESS);
+#endif
+ }
+
+ return success;
+}
+
+#pragma mark - Thread Properties
+void *FIRCLSThreadGetCurrentPC(void) {
+ return __builtin_return_address(0);
+}
+
+static bool FIRCLSProcessGetThreadState(FIRCLSProcess *process,
+ thread_t thread,
+ FIRCLSThreadContext *context) {
+ if (!FIRCLSIsValidPointer(context)) {
+ FIRCLSSDKLogError("invalid context supplied");
+ return false;
+ }
+
+ // If the thread context we should use is non-NULL, then just assign it here. Otherwise,
+ // query the thread state
+ if (FIRCLSProcessIsCrashedThread(process, thread) && FIRCLSIsValidPointer(process->uapVoid)) {
+ *context = *((_STRUCT_UCONTEXT *)process->uapVoid)->uc_mcontext;
+ return true;
+ }
+
+ // Here's a wild trick: emulate what thread_get_state would do. It apppears that
+ // we cannot reliably unwind out of thread_get_state. So, instead of trying, setup
+ // a thread context that resembles what the real thing would look like
+ if (FIRCLSProcessIsCurrentThread(process, thread)) {
+ FIRCLSSDKLog("Faking current thread\n");
+ memset(context, 0, sizeof(FIRCLSThreadContext));
+
+ // Compute the frame address, and then base the stack value off of that. A frame pushes
+ // two pointers onto the stack, so we have to offset.
+ const uintptr_t frameAddress = (uintptr_t)__builtin_frame_address(0);
+ const uintptr_t stackAddress = FIRCLSUnwindStackPointerFromFramePointer(frameAddress);
+
+#if CLS_CPU_X86_64
+ context->__ss.__rip = (uintptr_t)FIRCLSThreadGetCurrentPC();
+ context->__ss.__rbp = frameAddress;
+ context->__ss.__rsp = stackAddress;
+#elif CLS_CPU_I386
+ context->__ss.__eip = (uintptr_t)FIRCLSThreadGetCurrentPC();
+ context->__ss.__ebp = frameAddress;
+ context->__ss.__esp = stackAddress;
+#elif CLS_CPU_ARM64
+ FIRCLSThreadContextSetPC(context, (uintptr_t)FIRCLSThreadGetCurrentPC());
+ FIRCLSThreadContextSetFramePointer(context, frameAddress);
+ FIRCLSThreadContextSetLinkRegister(context, (uintptr_t)__builtin_return_address(0));
+ FIRCLSThreadContextSetStackPointer(context, stackAddress);
+#elif CLS_CPU_ARM
+ context->__ss.__pc = (uintptr_t)FIRCLSThreadGetCurrentPC();
+ context->__ss.__r[7] = frameAddress;
+ context->__ss.__lr = (uintptr_t)__builtin_return_address(0);
+ context->__ss.__sp = stackAddress;
+#endif
+
+ return true;
+ }
+
+#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");
+ return false;
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+static bool FIRCLSProcessGetThreadName(FIRCLSProcess *process,
+ thread_t thread,
+ char *buffer,
+ size_t length) {
+ pthread_t pthread;
+
+ if (!buffer || length <= 0) {
+ return false;
+ }
+
+ pthread = pthread_from_mach_thread_np(thread);
+
+ return pthread_getname_np(pthread, buffer, length) == 0;
+}
+
+static const char *FIRCLSProcessGetThreadDispatchQueueName(FIRCLSProcess *process,
+ thread_t thread) {
+ thread_identifier_info_data_t info;
+ mach_msg_type_number_t infoCount;
+ dispatch_queue_t *queueAddress;
+ dispatch_queue_t queue;
+ const char *string;
+
+ 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");
+ return NULL;
+ }
+
+ queueAddress = (dispatch_queue_t *)info.dispatch_qaddr;
+ if (queueAddress == NULL) {
+ return "";
+ }
+
+ // Sometimes a queue address is invalid. I cannot explain why this is, but
+ // it can cause a crash.
+ if (!FIRCLSReadMemory((vm_address_t)queueAddress, &queue, sizeof(void *))) {
+ return "";
+ }
+
+ // here, we know it is safe to de-reference this address, so attempt to get the queue name
+ if (!queue) {
+ return "";
+ }
+
+ string = dispatch_queue_get_label(queue);
+
+ // but, we still don't if the entire string is valid, so check that too
+ if (!FIRCLSReadString((vm_address_t)string, (char **)&string, 128)) {
+ return "";
+ }
+
+ return string;
+}
+
+#pragma mark - Data Recording
+static bool FIRCLSProcessRecordThreadRegisters(FIRCLSThreadContext context, FIRCLSFile *file) {
+#if CLS_CPU_ARM
+ FIRCLSFileWriteHashEntryUint64(file, "r0", context.__ss.__r[0]);
+ FIRCLSFileWriteHashEntryUint64(file, "r1", context.__ss.__r[1]);
+ FIRCLSFileWriteHashEntryUint64(file, "r2", context.__ss.__r[2]);
+ FIRCLSFileWriteHashEntryUint64(file, "r3", context.__ss.__r[3]);
+ FIRCLSFileWriteHashEntryUint64(file, "r4", context.__ss.__r[4]);
+ FIRCLSFileWriteHashEntryUint64(file, "r5", context.__ss.__r[5]);
+ FIRCLSFileWriteHashEntryUint64(file, "r6", context.__ss.__r[6]);
+ FIRCLSFileWriteHashEntryUint64(file, "r7", context.__ss.__r[7]);
+ FIRCLSFileWriteHashEntryUint64(file, "r8", context.__ss.__r[8]);
+ FIRCLSFileWriteHashEntryUint64(file, "r9", context.__ss.__r[9]);
+ FIRCLSFileWriteHashEntryUint64(file, "r10", context.__ss.__r[10]);
+ FIRCLSFileWriteHashEntryUint64(file, "r11", context.__ss.__r[11]);
+ FIRCLSFileWriteHashEntryUint64(file, "ip", context.__ss.__r[12]);
+ FIRCLSFileWriteHashEntryUint64(file, "sp", context.__ss.__sp);
+ FIRCLSFileWriteHashEntryUint64(file, "lr", context.__ss.__lr);
+ FIRCLSFileWriteHashEntryUint64(file, "pc", context.__ss.__pc);
+ FIRCLSFileWriteHashEntryUint64(file, "cpsr", context.__ss.__cpsr);
+#elif CLS_CPU_ARM64
+ FIRCLSFileWriteHashEntryUint64(file, "x0", context.__ss.__x[0]);
+ FIRCLSFileWriteHashEntryUint64(file, "x1", context.__ss.__x[1]);
+ FIRCLSFileWriteHashEntryUint64(file, "x2", context.__ss.__x[2]);
+ FIRCLSFileWriteHashEntryUint64(file, "x3", context.__ss.__x[3]);
+ FIRCLSFileWriteHashEntryUint64(file, "x4", context.__ss.__x[4]);
+ FIRCLSFileWriteHashEntryUint64(file, "x5", context.__ss.__x[5]);
+ FIRCLSFileWriteHashEntryUint64(file, "x6", context.__ss.__x[6]);
+ FIRCLSFileWriteHashEntryUint64(file, "x7", context.__ss.__x[7]);
+ FIRCLSFileWriteHashEntryUint64(file, "x8", context.__ss.__x[8]);
+ FIRCLSFileWriteHashEntryUint64(file, "x9", context.__ss.__x[9]);
+ FIRCLSFileWriteHashEntryUint64(file, "x10", context.__ss.__x[10]);
+ FIRCLSFileWriteHashEntryUint64(file, "x11", context.__ss.__x[11]);
+ FIRCLSFileWriteHashEntryUint64(file, "x12", context.__ss.__x[12]);
+ FIRCLSFileWriteHashEntryUint64(file, "x13", context.__ss.__x[13]);
+ FIRCLSFileWriteHashEntryUint64(file, "x14", context.__ss.__x[14]);
+ FIRCLSFileWriteHashEntryUint64(file, "x15", context.__ss.__x[15]);
+ FIRCLSFileWriteHashEntryUint64(file, "x16", context.__ss.__x[16]);
+ FIRCLSFileWriteHashEntryUint64(file, "x17", context.__ss.__x[17]);
+ FIRCLSFileWriteHashEntryUint64(file, "x18", context.__ss.__x[18]);
+ FIRCLSFileWriteHashEntryUint64(file, "x19", context.__ss.__x[19]);
+ FIRCLSFileWriteHashEntryUint64(file, "x20", context.__ss.__x[20]);
+ FIRCLSFileWriteHashEntryUint64(file, "x21", context.__ss.__x[21]);
+ FIRCLSFileWriteHashEntryUint64(file, "x22", context.__ss.__x[22]);
+ FIRCLSFileWriteHashEntryUint64(file, "x23", context.__ss.__x[23]);
+ FIRCLSFileWriteHashEntryUint64(file, "x24", context.__ss.__x[24]);
+ FIRCLSFileWriteHashEntryUint64(file, "x25", context.__ss.__x[25]);
+ FIRCLSFileWriteHashEntryUint64(file, "x26", context.__ss.__x[26]);
+ FIRCLSFileWriteHashEntryUint64(file, "x27", context.__ss.__x[27]);
+ FIRCLSFileWriteHashEntryUint64(file, "x28", context.__ss.__x[28]);
+ FIRCLSFileWriteHashEntryUint64(file, "fp", FIRCLSThreadContextGetFramePointer(&context));
+ FIRCLSFileWriteHashEntryUint64(file, "sp", FIRCLSThreadContextGetStackPointer(&context));
+ FIRCLSFileWriteHashEntryUint64(file, "lr", FIRCLSThreadContextGetLinkRegister(&context));
+ FIRCLSFileWriteHashEntryUint64(file, "pc", FIRCLSThreadContextGetPC(&context));
+ FIRCLSFileWriteHashEntryUint64(file, "cpsr", context.__ss.__cpsr);
+#elif CLS_CPU_I386
+ FIRCLSFileWriteHashEntryUint64(file, "eax", context.__ss.__eax);
+ FIRCLSFileWriteHashEntryUint64(file, "ebx", context.__ss.__ebx);
+ FIRCLSFileWriteHashEntryUint64(file, "ecx", context.__ss.__ecx);
+ FIRCLSFileWriteHashEntryUint64(file, "edx", context.__ss.__edx);
+ FIRCLSFileWriteHashEntryUint64(file, "edi", context.__ss.__edi);
+ FIRCLSFileWriteHashEntryUint64(file, "esi", context.__ss.__esi);
+ FIRCLSFileWriteHashEntryUint64(file, "ebp", context.__ss.__ebp);
+ FIRCLSFileWriteHashEntryUint64(file, "esp", context.__ss.__esp);
+ FIRCLSFileWriteHashEntryUint64(file, "ss", context.__ss.__ss);
+ FIRCLSFileWriteHashEntryUint64(file, "eflags", context.__ss.__eflags);
+ FIRCLSFileWriteHashEntryUint64(file, "eip", context.__ss.__eip);
+ FIRCLSFileWriteHashEntryUint64(file, "cs", context.__ss.__cs);
+ FIRCLSFileWriteHashEntryUint64(file, "ds", context.__ss.__ds);
+ FIRCLSFileWriteHashEntryUint64(file, "es", context.__ss.__es);
+ FIRCLSFileWriteHashEntryUint64(file, "fs", context.__ss.__fs);
+ FIRCLSFileWriteHashEntryUint64(file, "gs", context.__ss.__gs);
+
+ // how do we get the cr2 register?
+#elif CLS_CPU_X86_64
+ FIRCLSFileWriteHashEntryUint64(file, "rax", context.__ss.__rax);
+ FIRCLSFileWriteHashEntryUint64(file, "rbx", context.__ss.__rbx);
+ FIRCLSFileWriteHashEntryUint64(file, "rcx", context.__ss.__rcx);
+ FIRCLSFileWriteHashEntryUint64(file, "rdx", context.__ss.__rdx);
+ FIRCLSFileWriteHashEntryUint64(file, "rdi", context.__ss.__rdi);
+ FIRCLSFileWriteHashEntryUint64(file, "rsi", context.__ss.__rsi);
+ FIRCLSFileWriteHashEntryUint64(file, "rbp", context.__ss.__rbp);
+ FIRCLSFileWriteHashEntryUint64(file, "rsp", context.__ss.__rsp);
+ FIRCLSFileWriteHashEntryUint64(file, "r8", context.__ss.__r8);
+ FIRCLSFileWriteHashEntryUint64(file, "r9", context.__ss.__r9);
+ FIRCLSFileWriteHashEntryUint64(file, "r10", context.__ss.__r10);
+ FIRCLSFileWriteHashEntryUint64(file, "r11", context.__ss.__r11);
+ FIRCLSFileWriteHashEntryUint64(file, "r12", context.__ss.__r12);
+ FIRCLSFileWriteHashEntryUint64(file, "r13", context.__ss.__r13);
+ FIRCLSFileWriteHashEntryUint64(file, "r14", context.__ss.__r14);
+ FIRCLSFileWriteHashEntryUint64(file, "r15", context.__ss.__r15);
+ FIRCLSFileWriteHashEntryUint64(file, "rip", context.__ss.__rip);
+ FIRCLSFileWriteHashEntryUint64(file, "rflags", context.__ss.__rflags);
+ FIRCLSFileWriteHashEntryUint64(file, "cs", context.__ss.__cs);
+ FIRCLSFileWriteHashEntryUint64(file, "fs", context.__ss.__fs);
+ FIRCLSFileWriteHashEntryUint64(file, "gs", context.__ss.__gs);
+#endif
+
+ return true;
+}
+
+static bool FIRCLSProcessRecordThread(FIRCLSProcess *process, thread_t thread, FIRCLSFile *file) {
+ FIRCLSUnwindContext unwindContext;
+ FIRCLSThreadContext context;
+
+ if (!FIRCLSProcessGetThreadState(process, thread, &context)) {
+ FIRCLSSDKLogError("unable to get thread state");
+ return false;
+ }
+
+ if (!FIRCLSUnwindInit(&unwindContext, context)) {
+ FIRCLSSDKLog("unable to init unwind context\n");
+
+ return false;
+ }
+
+ FIRCLSFileWriteHashStart(file);
+
+ // registers
+ FIRCLSFileWriteHashKey(file, "registers");
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSProcessRecordThreadRegisters(context, file);
+
+ FIRCLSFileWriteHashEnd(file);
+
+ // stacktrace
+ FIRCLSFileWriteHashKey(file, "stacktrace");
+
+ // stacktrace is an array of integers
+ FIRCLSFileWriteArrayStart(file);
+
+ uint32_t repeatedPCCount = 0;
+ uint64_t repeatedPC = 0;
+ const FIRCLSInternalLogLevel level = _firclsContext.writable->internalLogging.logLevel;
+
+ while (FIRCLSUnwindNextFrame(&unwindContext)) {
+ const uintptr_t pc = FIRCLSUnwindGetPC(&unwindContext);
+ const uint32_t frameCount = FIRCLSUnwindGetFrameRepeatCount(&unwindContext);
+
+ if (repeatedPC == pc && repeatedPC != 0) {
+ // actively counting a recursion
+ repeatedPCCount = frameCount;
+ continue;
+ }
+
+ if (frameCount >= FIRCLSUnwindInfiniteRecursionCountThreshold && repeatedPC == 0) {
+ repeatedPC = pc;
+ FIRCLSSDKLogWarn("Possible infinite recursion - suppressing logging\n");
+ _firclsContext.writable->internalLogging.logLevel = FIRCLSInternalLogLevelWarn;
+ continue;
+ }
+
+ if (repeatedPC != 0) {
+ // at this point, we've recorded a repeated PC, but it is now no longer
+ // repeating, so we can restore the logging
+ _firclsContext.writable->internalLogging.logLevel = level;
+ }
+
+ FIRCLSFileWriteArrayEntryUint64(file, pc);
+ }
+
+ FIRCLSFileWriteArrayEnd(file);
+
+ // crashed?
+ if (FIRCLSProcessIsCrashedThread(process, thread)) {
+ FIRCLSFileWriteHashEntryBoolean(file, "crashed", true);
+ }
+
+ if (repeatedPC != 0) {
+ FIRCLSFileWriteHashEntryUint64(file, "repeated_pc", repeatedPC);
+ FIRCLSFileWriteHashEntryUint64(file, "repeat_count", repeatedPCCount);
+ }
+
+ // Just for extra safety, restore the logging level again. The logic
+ // above is fairly tricky, this is cheap, and no logging is a real pain.
+ _firclsContext.writable->internalLogging.logLevel = level;
+
+ // end thread info
+ FIRCLSFileWriteHashEnd(file);
+
+ return true;
+}
+
+bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file) {
+ uint32_t threadCount;
+ uint32_t i;
+
+ threadCount = FIRCLSProcessGetThreadCount(process);
+
+ FIRCLSFileWriteSectionStart(file, "threads");
+
+ FIRCLSFileWriteArrayStart(file);
+
+ for (i = 0; i < threadCount; ++i) {
+ thread_t thread;
+
+ thread = FIRCLSProcessGetThread(process, i);
+
+ FIRCLSSDKLogInfo("recording thread %d data\n", i);
+ if (!FIRCLSProcessRecordThread(process, thread, file)) {
+ return false;
+ }
+ }
+
+ FIRCLSFileWriteArrayEnd(file);
+
+ FIRCLSFileWriteSectionEnd(file);
+
+ FIRCLSSDKLogInfo("completed recording all thread data\n");
+
+ return true;
+}
+
+void FIRCLSProcessRecordThreadNames(FIRCLSProcess *process, FIRCLSFile *file) {
+ uint32_t threadCount;
+ uint32_t i;
+
+ FIRCLSFileWriteSectionStart(file, "thread_names");
+
+ FIRCLSFileWriteArrayStart(file);
+
+ threadCount = FIRCLSProcessGetThreadCount(process);
+ for (i = 0; i < threadCount; ++i) {
+ thread_t thread;
+ char name[THREAD_NAME_BUFFER_SIZE];
+
+ thread = FIRCLSProcessGetThread(process, i);
+
+ name[0] = 0; // null-terminate, just in case nothing is written
+
+ FIRCLSProcessGetThreadName(process, thread, name, THREAD_NAME_BUFFER_SIZE);
+
+ FIRCLSFileWriteArrayEntryString(file, name);
+ }
+
+ FIRCLSFileWriteArrayEnd(file);
+ FIRCLSFileWriteSectionEnd(file);
+}
+
+void FIRCLSProcessRecordDispatchQueueNames(FIRCLSProcess *process, FIRCLSFile *file) {
+ uint32_t threadCount;
+ uint32_t i;
+
+ FIRCLSFileWriteSectionStart(file, "dispatch_queue_names");
+
+ FIRCLSFileWriteArrayStart(file);
+
+ threadCount = FIRCLSProcessGetThreadCount(process);
+ for (i = 0; i < threadCount; ++i) {
+ thread_t thread;
+ const char *name;
+
+ thread = FIRCLSProcessGetThread(process, i);
+
+ name = FIRCLSProcessGetThreadDispatchQueueName(process, thread);
+
+ FIRCLSFileWriteArrayEntryString(file, name);
+ }
+
+ FIRCLSFileWriteArrayEnd(file);
+ FIRCLSFileWriteSectionEnd(file);
+}
+
+#pragma mark - Othe Process Info
+bool FIRCLSProcessGetMemoryUsage(uint64_t *active,
+ uint64_t *inactive,
+ uint64_t *wired,
+ uint64_t *freeMem) {
+ mach_port_t hostPort;
+ mach_msg_type_number_t hostSize;
+ vm_size_t pageSize;
+ vm_statistics_data_t vmStat;
+
+ hostPort = mach_host_self();
+
+ hostSize = sizeof(vm_statistics_data_t) / sizeof(integer_t);
+
+ pageSize = _firclsContext.readonly->host.pageSize;
+
+ if (host_statistics(hostPort, HOST_VM_INFO, (host_info_t)&vmStat, &hostSize) != KERN_SUCCESS) {
+ FIRCLSSDKLog("Failed to get vm statistics\n");
+ return false;
+ }
+
+ if (!(active && inactive && wired && freeMem)) {
+ FIRCLSSDKLog("Invalid pointers\n");
+ return false;
+ }
+
+ // compute the sizes in bytes and return the values
+ *active = vmStat.active_count * pageSize;
+ *inactive = vmStat.inactive_count * pageSize;
+ *wired = vmStat.wire_count * pageSize;
+ *freeMem = vmStat.free_count * pageSize;
+
+ return true;
+}
+
+bool FIRCLSProcessGetInfo(FIRCLSProcess *process,
+ uint64_t *virtualSize,
+ uint64_t *residentSize,
+ time_value_t *userTime,
+ time_value_t *systemTime) {
+ struct task_basic_info_64 taskInfo;
+ mach_msg_type_number_t count;
+
+ count = TASK_BASIC_INFO_64_COUNT;
+ if (task_info(process->task, TASK_BASIC_INFO_64, (task_info_t)&taskInfo, &count) !=
+ KERN_SUCCESS) {
+ FIRCLSSDKLog("Failed to get task info\n");
+ return false;
+ }
+
+ if (!(virtualSize && residentSize && userTime && systemTime)) {
+ FIRCLSSDKLog("Invalid pointers\n");
+ return false;
+ }
+
+ *virtualSize = taskInfo.virtual_size;
+ *residentSize = taskInfo.resident_size;
+ *userTime = taskInfo.user_time;
+ *systemTime = taskInfo.system_time;
+
+ return true;
+}
+
+void FIRCLSProcessRecordStats(FIRCLSProcess *process, FIRCLSFile *file) {
+ uint64_t active;
+ uint64_t inactive;
+ uint64_t virtualSize;
+ uint64_t residentSize;
+ uint64_t wired;
+ uint64_t freeMem;
+ time_value_t userTime;
+ time_value_t systemTime;
+
+ if (!FIRCLSProcessGetMemoryUsage(&active, &inactive, &wired, &freeMem)) {
+ FIRCLSSDKLog("Unable to get process memory usage\n");
+ return;
+ }
+
+ if (!FIRCLSProcessGetInfo(process, &virtualSize, &residentSize, &userTime, &systemTime)) {
+ FIRCLSSDKLog("Unable to get process stats\n");
+ return;
+ }
+
+ FIRCLSFileWriteSectionStart(file, "process_stats");
+
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSFileWriteHashEntryUint64(file, "active", active);
+ FIRCLSFileWriteHashEntryUint64(file, "inactive", inactive);
+ FIRCLSFileWriteHashEntryUint64(file, "wired", wired);
+ FIRCLSFileWriteHashEntryUint64(file, "freeMem", freeMem); // Intentionally left in, for now. Arg.
+ FIRCLSFileWriteHashEntryUint64(file, "free_mem", freeMem);
+ FIRCLSFileWriteHashEntryUint64(file, "virtual", virtualSize);
+ FIRCLSFileWriteHashEntryUint64(file, "resident", active);
+ FIRCLSFileWriteHashEntryUint64(file, "user_time",
+ (userTime.seconds * 1000 * 1000) + userTime.microseconds);
+ FIRCLSFileWriteHashEntryUint64(file, "sys_time",
+ (systemTime.seconds * 1000 * 1000) + systemTime.microseconds);
+
+ FIRCLSFileWriteHashEnd(file);
+
+ FIRCLSFileWriteSectionEnd(file);
+}
+
+#pragma mark - Runtime Info
+#define OBJC_MSG_SEND_START ((vm_address_t)objc_msgSend)
+#define OBJC_MSG_SEND_SUPER_START ((vm_address_t)objc_msgSendSuper)
+#define OBJC_MSG_SEND_END (OBJC_MSG_SEND_START + 66)
+#define OBJC_MSG_SEND_SUPER_END (OBJC_MSG_SEND_SUPER_START + 66)
+
+#if !CLS_CPU_ARM64
+#define OBJC_MSG_SEND_STRET_START ((vm_address_t)objc_msgSend_stret)
+#define OBJC_MSG_SEND_SUPER_STRET_START ((vm_address_t)objc_msgSendSuper_stret)
+#define OBJC_MSG_SEND_STRET_END (OBJC_MSG_SEND_STRET_START + 66)
+#define OBJC_MSG_SEND_SUPER_STRET_END (OBJC_MSG_SEND_SUPER_STRET_START + 66)
+#endif
+
+#if CLS_CPU_X86
+#define OBJC_MSG_SEND_FPRET_START ((vm_address_t)objc_msgSend_fpret)
+#define OBJC_MSG_SEND_FPRET_END (OBJC_MSG_SEND_FPRET_START + 66)
+#endif
+
+static const char *FIRCLSProcessGetObjCSelectorName(FIRCLSThreadContext registers) {
+ void *selectorAddress;
+ void *selRegister;
+#if !CLS_CPU_ARM64
+ void *stretSelRegister;
+#endif
+ vm_address_t pc;
+
+ // First, did we crash in objc_msgSend? The two ways I can think
+ // of doing this are to use dladdr, and then comparing the strings to
+ // objc_msg*, or looking up the symbols, and guessing if we are "close enough".
+
+ selectorAddress = NULL;
+
+#if CLS_CPU_ARM
+ pc = registers.__ss.__pc;
+ selRegister = (void *)registers.__ss.__r[1];
+ stretSelRegister = (void *)registers.__ss.__r[2];
+#elif CLS_CPU_ARM64
+ pc = FIRCLSThreadContextGetPC(®isters);
+ selRegister = (void *)registers.__ss.__x[1];
+#elif CLS_CPU_I386
+ pc = registers.__ss.__eip;
+ selRegister = (void *)registers.__ss.__ecx;
+ stretSelRegister = (void *)registers.__ss.__ecx;
+#elif CLS_CPU_X86_64
+ pc = registers.__ss.__rip;
+ selRegister = (void *)registers.__ss.__rsi;
+ stretSelRegister = (void *)registers.__ss.__rdx;
+#endif
+
+ if ((pc >= OBJC_MSG_SEND_START) && (pc <= OBJC_MSG_SEND_END)) {
+ selectorAddress = selRegister;
+ }
+
+#if !CLS_CPU_ARM64
+ if ((pc >= OBJC_MSG_SEND_SUPER_START) && (pc <= OBJC_MSG_SEND_SUPER_END)) {
+ selectorAddress = selRegister;
+ }
+
+ if ((pc >= OBJC_MSG_SEND_STRET_START) && (pc <= OBJC_MSG_SEND_STRET_END)) {
+ selectorAddress = stretSelRegister;
+ }
+
+ if ((pc >= OBJC_MSG_SEND_SUPER_STRET_START) && (pc <= OBJC_MSG_SEND_SUPER_STRET_END)) {
+ selectorAddress = stretSelRegister;
+ }
+
+#if CLS_CPU_X86
+ if ((pc >= OBJC_MSG_SEND_FPRET_START) && (pc <= OBJC_MSG_SEND_FPRET_END)) {
+ selectorAddress = selRegister;
+ }
+#endif
+#endif
+
+ if (!selectorAddress) {
+ return "";
+ }
+
+ if (!FIRCLSReadString((vm_address_t)selectorAddress, (char **)&selectorAddress, 128)) {
+ FIRCLSSDKLog("Unable to read the selector string\n");
+ return "";
+ }
+
+ return selectorAddress;
+}
+
+#define CRASH_ALIGN __attribute__((aligned(8)))
+typedef struct {
+ unsigned version CRASH_ALIGN;
+ const char *message CRASH_ALIGN;
+ const char *signature CRASH_ALIGN;
+ const char *backtrace CRASH_ALIGN;
+ const char *message2 CRASH_ALIGN;
+ void *reserved CRASH_ALIGN;
+ void *reserved2 CRASH_ALIGN;
+} crash_info_t;
+
+static void FIRCLSProcessRecordCrashInfo(FIRCLSFile *file) {
+ // TODO: this should be abstracted into binary images, if possible
+ FIRCLSBinaryImageRuntimeNode *nodes = _firclsContext.writable->binaryImage.nodes;
+ if (!nodes) {
+ FIRCLSSDKLogError("The node structure is NULL\n");
+ return;
+ }
+
+ for (uint32_t i = 0; i < CLS_BINARY_IMAGE_RUNTIME_NODE_COUNT; ++i) {
+ FIRCLSBinaryImageRuntimeNode *node = &nodes[i];
+
+ if (!node->crashInfo) {
+ continue;
+ }
+
+ crash_info_t info;
+
+ if (!FIRCLSReadMemory((vm_address_t)node->crashInfo, &info, sizeof(crash_info_t))) {
+ continue;
+ }
+
+ FIRCLSSDKLogDebug("Found crash info with version %d\n", info.version);
+
+ // Currently support versions 0 through 5.
+ // 4 was in use for a long time, but it appears that with iOS 9 / swift 2.0, the verison has
+ // been bumped.
+ if (info.version > 5) {
+ continue;
+ }
+
+ if (!info.message) {
+ continue;
+ }
+
+#if CLS_BINARY_IMAGE_RUNTIME_NODE_RECORD_NAME
+ FIRCLSSDKLogInfo("Found crash info for %s\n", node->name);
+#endif
+
+ FIRCLSSDKLogDebug("attempting to read crash info string\n");
+
+ char *string = NULL;
+
+ if (!FIRCLSReadString((vm_address_t)info.message, &string, 256)) {
+ FIRCLSSDKLogError("Failed to copy crash info string\n");
+ continue;
+ }
+
+ FIRCLSFileWriteArrayEntryHexEncodedString(file, string);
+ }
+}
+
+void FIRCLSProcessRecordRuntimeInfo(FIRCLSProcess *process, FIRCLSFile *file) {
+ FIRCLSThreadContext mcontext;
+
+ if (!FIRCLSProcessGetThreadState(process, process->crashedThread, &mcontext)) {
+ FIRCLSSDKLogError("unable to get crashed thread state");
+ }
+
+ FIRCLSFileWriteSectionStart(file, "runtime");
+
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSFileWriteHashEntryString(file, "objc_selector", FIRCLSProcessGetObjCSelectorName(mcontext));
+
+ FIRCLSFileWriteHashKey(file, "crash_info_entries");
+
+ FIRCLSFileWriteArrayStart(file);
+ FIRCLSProcessRecordCrashInfo(file);
+ FIRCLSFileWriteArrayEnd(file);
+
+ FIRCLSFileWriteHashEnd(file);
+
+ FIRCLSFileWriteSectionEnd(file);
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h
new file mode 100644
index 000000000..dbe758ffd
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSProcess.h
@@ -0,0 +1,45 @@
+// 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.
+
+#pragma once
+
+#include
+#include
+
+#include "FIRCLSFile.h"
+
+typedef struct {
+ // task info
+ mach_port_t task;
+
+ // thread stuff
+ thread_t thisThread;
+ thread_t crashedThread;
+ thread_act_array_t threads;
+ mach_msg_type_number_t threadCount;
+ void *uapVoid; // current thread state
+} FIRCLSProcess;
+
+bool FIRCLSProcessInit(FIRCLSProcess *process, thread_t crashedThread, void *uapVoid);
+bool FIRCLSProcessDestroy(FIRCLSProcess *process);
+bool FIRCLSProcessDebuggerAttached(void);
+
+bool FIRCLSProcessSuspendAllOtherThreads(FIRCLSProcess *process);
+bool FIRCLSProcessResumeAllOtherThreads(FIRCLSProcess *process);
+
+void FIRCLSProcessRecordThreadNames(FIRCLSProcess *process, FIRCLSFile *file);
+void FIRCLSProcessRecordDispatchQueueNames(FIRCLSProcess *process, FIRCLSFile *file);
+bool FIRCLSProcessRecordAllThreads(FIRCLSProcess *process, FIRCLSFile *file);
+void FIRCLSProcessRecordStats(FIRCLSProcess *process, FIRCLSFile *file);
+void FIRCLSProcessRecordRuntimeInfo(FIRCLSProcess *process, FIRCLSFile *file);
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h
new file mode 100644
index 000000000..e1ee897d6
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.h
@@ -0,0 +1,101 @@
+// 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.
+
+#pragma once
+
+#include "FIRCLSFile.h"
+
+__BEGIN_DECLS
+
+#ifdef __OBJC__
+extern NSString* const FIRCLSStartTimeKey;
+extern NSString* const FIRCLSFirstRunloopTurnTimeKey;
+extern NSString* const FIRCLSInBackgroundKey;
+#if TARGET_OS_IPHONE
+extern NSString* const FIRCLSDeviceOrientationKey;
+extern NSString* const FIRCLSUIOrientationKey;
+#endif
+extern NSString* const FIRCLSUserIdentifierKey;
+extern NSString* const FIRCLSUserNameKey;
+extern NSString* const FIRCLSUserEmailKey;
+extern NSString* const FIRCLSDevelopmentPlatformNameKey;
+extern NSString* const FIRCLSDevelopmentPlatformVersionKey;
+#endif
+
+extern const uint32_t FIRCLSUserLoggingMaxKVEntries;
+
+typedef struct {
+ const char* incrementalPath;
+ const char* compactedPath;
+
+ uint32_t maxIncrementalCount;
+ uint32_t maxCount;
+} FIRCLSUserLoggingKVStorage;
+
+typedef struct {
+ const char* aPath;
+ const char* bPath;
+ uint32_t maxSize;
+ uint32_t maxEntries;
+ bool restrictBySize;
+ uint32_t* entryCount;
+} FIRCLSUserLoggingABStorage;
+
+typedef struct {
+ FIRCLSUserLoggingKVStorage userKVStorage;
+ FIRCLSUserLoggingKVStorage internalKVStorage;
+
+ FIRCLSUserLoggingABStorage logStorage;
+ FIRCLSUserLoggingABStorage errorStorage;
+ FIRCLSUserLoggingABStorage customExceptionStorage;
+} FIRCLSUserLoggingReadOnlyContext;
+
+typedef struct {
+ const char* activeUserLogPath;
+ const char* activeErrorLogPath;
+ const char* activeCustomExceptionPath;
+ uint32_t userKVCount;
+ uint32_t internalKVCount;
+ uint32_t errorsCount;
+} FIRCLSUserLoggingWritableContext;
+
+void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext* roContext,
+ FIRCLSUserLoggingWritableContext* rwContext);
+
+#ifdef __OBJC__
+void FIRCLSUserLoggingRecordUserKeyValue(NSString* key, id value);
+void FIRCLSUserLoggingRecordInternalKeyValue(NSString* key, id value);
+void FIRCLSUserLoggingWriteInternalKeyValue(NSString* key, NSString* value);
+
+void FIRCLSUserLoggingRecordError(NSError* error, NSDictionary* additionalUserInfo);
+
+NSDictionary* FIRCLSUserLoggingGetCompactedKVEntries(FIRCLSUserLoggingKVStorage* storage,
+ bool decodeHex);
+void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage* storage);
+
+void FIRCLSUserLoggingRecordKeyValue(NSString* key,
+ id value,
+ FIRCLSUserLoggingKVStorage* storage,
+ uint32_t* counter);
+
+void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage* storage,
+ const char** activePath,
+ void (^openedFileBlock)(FIRCLSFile* file));
+
+NSArray* FIRCLSUserLoggingStoredKeyValues(const char* path);
+
+OBJC_EXTERN void FIRCLSLog(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
+#endif
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m
new file mode 100644
index 000000000..0bb768cfe
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Components/FIRCLSUserLogging.m
@@ -0,0 +1,523 @@
+// 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.
+
+#include "FIRCLSUserLogging.h"
+
+#include
+
+#include "FIRCLSGlobals.h"
+#include "FIRCLSUtility.h"
+
+#import "FIRCLSReportManager_Private.h"
+
+NSString *const FIRCLSStartTimeKey = @"com.crashlytics.kit-start-time";
+NSString *const FIRCLSFirstRunloopTurnTimeKey = @"com.crashlytics.first-run-loop-time";
+NSString *const FIRCLSInBackgroundKey = @"com.crashlytics.in-background";
+#if TARGET_OS_IPHONE
+NSString *const FIRCLSDeviceOrientationKey = @"com.crashlytics.device-orientation";
+NSString *const FIRCLSUIOrientationKey = @"com.crashlytics.ui-orientation";
+#endif
+NSString *const FIRCLSUserIdentifierKey = @"com.crashlytics.user-id";
+NSString *const FIRCLSDevelopmentPlatformNameKey = @"com.crashlytics.development-platform-name";
+NSString *const FIRCLSDevelopmentPlatformVersionKey =
+ @"com.crashlytics.development-platform-version";
+
+const uint32_t FIRCLSUserLoggingMaxKVEntries = 64;
+
+#pragma mark - Prototypes
+static void FIRCLSUserLoggingWriteKeyValue(NSString *key,
+ NSString *value,
+ FIRCLSUserLoggingKVStorage *storage,
+ uint32_t *counter);
+static void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage,
+ const char **activePath,
+ off_t fileSize);
+void FIRCLSLogInternal(NSString *message);
+
+#pragma mark - Setup
+void FIRCLSUserLoggingInit(FIRCLSUserLoggingReadOnlyContext *roContext,
+ FIRCLSUserLoggingWritableContext *rwContext) {
+ rwContext->activeUserLogPath = roContext->logStorage.aPath;
+ rwContext->activeErrorLogPath = roContext->errorStorage.aPath;
+ rwContext->activeCustomExceptionPath = roContext->customExceptionStorage.aPath;
+
+ rwContext->userKVCount = 0;
+ rwContext->internalKVCount = 0;
+ rwContext->errorsCount = 0;
+
+ roContext->userKVStorage.maxIncrementalCount = FIRCLSUserLoggingMaxKVEntries;
+ roContext->internalKVStorage.maxIncrementalCount = roContext->userKVStorage.maxIncrementalCount;
+}
+
+#pragma mark - KV Logging
+void FIRCLSUserLoggingRecordInternalKeyValue(NSString *key, id value) {
+ FIRCLSUserLoggingRecordKeyValue(key, value, &_firclsContext.readonly->logging.internalKVStorage,
+ &_firclsContext.writable->logging.internalKVCount);
+}
+
+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);
+}
+
+void FIRCLSUserLoggingRecordUserKeyValue(NSString *key, id value) {
+ FIRCLSUserLoggingRecordKeyValue(key, value, &_firclsContext.readonly->logging.userKVStorage,
+ &_firclsContext.writable->logging.userKVCount);
+}
+
+static id FIRCLSUserLoggingGetComponent(NSDictionary *entry,
+ NSString *componentName,
+ bool decodeHex) {
+ id value = [entry objectForKey:componentName];
+
+ return (decodeHex && value != [NSNull null]) ? FIRCLSFileHexDecodeString([value UTF8String])
+ : value;
+}
+
+static NSString *FIRCLSUserLoggingGetKey(NSDictionary *entry, bool decodeHex) {
+ return FIRCLSUserLoggingGetComponent(entry, @"key", decodeHex);
+}
+
+static id FIRCLSUserLoggingGetValue(NSDictionary *entry, bool decodeHex) {
+ return FIRCLSUserLoggingGetComponent(entry, @"value", decodeHex);
+}
+
+NSDictionary *FIRCLSUserLoggingGetCompactedKVEntries(FIRCLSUserLoggingKVStorage *storage,
+ bool decodeHex) {
+ if (!FIRCLSIsValidPointer(storage)) {
+ FIRCLSSDKLogError("storage invalid\n");
+ return nil;
+ }
+
+ NSArray *incrementalKVs = FIRCLSUserLoggingStoredKeyValues(storage->incrementalPath);
+ NSArray *compactedKVs = FIRCLSUserLoggingStoredKeyValues(storage->compactedPath);
+
+ NSMutableDictionary *finalKVSet = [NSMutableDictionary new];
+
+ // These should all be unique, so there might be a more efficient way to
+ // do this
+ for (NSDictionary *entry in compactedKVs) {
+ NSString *key = FIRCLSUserLoggingGetKey(entry, decodeHex);
+ NSString *value = FIRCLSUserLoggingGetValue(entry, decodeHex);
+
+ if (!key || !value) {
+ FIRCLSSDKLogError("compacted key/value contains a nil and must be dropped\n");
+ continue;
+ }
+
+ [finalKVSet setObject:value forKey:key];
+ }
+
+ // Now, assign the incremental values, in file order, so we overwrite any older values.
+ for (NSDictionary *entry in incrementalKVs) {
+ NSString *key = FIRCLSUserLoggingGetKey(entry, decodeHex);
+ NSString *value = FIRCLSUserLoggingGetValue(entry, decodeHex);
+
+ if (!key || !value) {
+ FIRCLSSDKLogError("incremental key/value contains a nil and must be dropped\n");
+ continue;
+ }
+
+ if ([value isEqual:[NSNull null]]) {
+ [finalKVSet removeObjectForKey:key];
+ } else {
+ [finalKVSet setObject:value forKey:key];
+ }
+ }
+
+ return finalKVSet;
+}
+
+void FIRCLSUserLoggingCompactKVEntries(FIRCLSUserLoggingKVStorage *storage) {
+ if (!FIRCLSIsValidPointer(storage)) {
+ FIRCLSSDKLogError("Error: storage invalid\n");
+ return;
+ }
+
+ NSDictionary *finalKVs = FIRCLSUserLoggingGetCompactedKVEntries(storage, false);
+
+ if (unlink(storage->compactedPath) != 0) {
+ FIRCLSSDKLog("Error: Unable to remove compacted KV store before compaction %s\n",
+ strerror(errno));
+ }
+
+ FIRCLSFile file;
+
+ if (!FIRCLSFileInitWithPath(&file, storage->compactedPath, true)) {
+ FIRCLSSDKLog("Error: Unable to open compacted k-v file\n");
+ return;
+ }
+
+ uint32_t maxCount = storage->maxCount;
+ if ([finalKVs count] > maxCount) {
+ // We need to remove keys, to avoid going over the max.
+ // This is just about the worst way to go about doing this. There are lots of smarter ways,
+ // 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);
+
+ 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);
+ }
+
+ FIRCLSFileClose(&file);
+
+ if (unlink(storage->incrementalPath) != 0) {
+ FIRCLSSDKLog("Error: Unable to remove incremental KV store after compaction %s\n",
+ strerror(errno));
+ }
+}
+
+void FIRCLSUserLoggingRecordKeyValue(NSString *key,
+ id value,
+ FIRCLSUserLoggingKVStorage *storage,
+ uint32_t *counter) {
+ if (!FIRCLSIsValidPointer(key)) {
+ FIRCLSSDKLogWarn("User provided bad key\n");
+ 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;
+ }
+
+ 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;
+ }
+
+ dispatch_sync(FIRCLSGetLoggingQueue(), ^{
+ FIRCLSUserLoggingWriteKeyValue(key, value, storage, counter);
+ });
+}
+
+static void FIRCLSUserLoggingWriteKeyValue(NSString *key,
+ NSString *value,
+ FIRCLSUserLoggingKVStorage *storage,
+ uint32_t *counter) {
+ FIRCLSFile file;
+
+ if (!FIRCLSIsValidPointer(storage) || !FIRCLSIsValidPointer(counter)) {
+ FIRCLSSDKLogError("Bad parameters\n");
+ return;
+ }
+
+ if (!FIRCLSFileInitWithPath(&file, storage->incrementalPath, true)) {
+ FIRCLSSDKLogError("Unable to open k-v file\n");
+ return;
+ }
+
+ FIRCLSFileWriteSectionStart(&file, "kv");
+ FIRCLSFileWriteHashStart(&file);
+ FIRCLSFileWriteHashEntryHexEncodedString(&file, "key", [key UTF8String]);
+ FIRCLSFileWriteHashEntryHexEncodedString(&file, "value", [value UTF8String]);
+ FIRCLSFileWriteHashEnd(&file);
+ FIRCLSFileWriteSectionEnd(&file);
+
+ FIRCLSFileClose(&file);
+
+ *counter += 1;
+ if (*counter >= storage->maxIncrementalCount) {
+ dispatch_async(FIRCLSGetLoggingQueue(), ^{
+ FIRCLSUserLoggingCompactKVEntries(storage);
+ *counter = 0;
+ });
+ }
+}
+
+NSArray *FIRCLSUserLoggingStoredKeyValues(const char *path) {
+ if (!FIRCLSContextIsInitialized()) {
+ return nil;
+ }
+
+ return FIRCLSFileReadSections(path, true, ^NSObject *(id obj) {
+ return [obj objectForKey:@"kv"];
+ });
+}
+
+#pragma mark - NSError Logging
+static void FIRCLSUserLoggingRecordErrorUserInfo(FIRCLSFile *file,
+ const char *fileKey,
+ NSDictionary *userInfo) {
+ if ([userInfo count] == 0) {
+ return;
+ }
+
+ FIRCLSFileWriteHashKey(file, fileKey);
+ FIRCLSFileWriteArrayStart(file);
+
+ for (id key in userInfo) {
+ id value = [userInfo objectForKey:key];
+ if (![value respondsToSelector:@selector(description)]) {
+ continue;
+ }
+
+ FIRCLSFileWriteArrayStart(file);
+ FIRCLSFileWriteArrayEntryHexEncodedString(file, [key UTF8String]);
+ FIRCLSFileWriteArrayEntryHexEncodedString(file, [[value description] UTF8String]);
+ FIRCLSFileWriteArrayEnd(file);
+ }
+
+ FIRCLSFileWriteArrayEnd(file);
+}
+
+static void FIRCLSUserLoggingWriteError(FIRCLSFile *file,
+ NSError *error,
+ NSDictionary *additionalUserInfo,
+ NSArray *addresses,
+ uint64_t timestamp) {
+ FIRCLSFileWriteSectionStart(file, "error");
+ FIRCLSFileWriteHashStart(file);
+ FIRCLSFileWriteHashEntryHexEncodedString(file, "domain", [[error domain] UTF8String]);
+ FIRCLSFileWriteHashEntryInt64(file, "code", [error code]);
+ FIRCLSFileWriteHashEntryUint64(file, "time", timestamp);
+
+ // addresses
+ FIRCLSFileWriteHashKey(file, "stacktrace");
+ FIRCLSFileWriteArrayStart(file);
+ for (NSNumber *address in addresses) {
+ FIRCLSFileWriteArrayEntryUint64(file, [address unsignedLongLongValue]);
+ }
+ FIRCLSFileWriteArrayEnd(file);
+
+ // user-info
+ FIRCLSUserLoggingRecordErrorUserInfo(file, "info", [error userInfo]);
+ FIRCLSUserLoggingRecordErrorUserInfo(file, "extra_info", additionalUserInfo);
+
+ FIRCLSFileWriteHashEnd(file);
+ FIRCLSFileWriteSectionEnd(file);
+}
+
+void FIRCLSUserLoggingRecordError(NSError *error,
+ NSDictionary *additionalUserInfo) {
+ if (!error) {
+ return;
+ }
+
+ if (!FIRCLSContextIsInitialized()) {
+ return;
+ }
+
+ // record the stacktrace and timestamp here, so we
+ // are as close as possible to the user's log statement
+ NSArray *addresses = [NSThread callStackReturnAddresses];
+ uint64_t timestamp = time(NULL);
+
+ FIRCLSUserLoggingWriteAndCheckABFiles(
+ &_firclsContext.readonly->logging.errorStorage,
+ &_firclsContext.writable->logging.activeErrorLogPath, ^(FIRCLSFile *file) {
+ FIRCLSUserLoggingWriteError(file, error, additionalUserInfo, addresses, timestamp);
+ });
+}
+
+#pragma mark - CLSLog Support
+void FIRCLSLog(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(msg);
+}
+
+#pragma mark - Properties
+uint32_t FIRCLSUserLoggingMaxLogSize(void) {
+ // don't forget that the message encoding overhead is 2x, and we
+ // wrap everything in a json structure with time. So, there is
+ // quite a penalty
+
+ uint32_t size = 1024 * 64;
+
+ return size * 2;
+}
+
+uint32_t FIRCLSUserLoggingMaxErrorSize(void) {
+ return FIRCLSUserLoggingMaxLogSize();
+}
+
+#pragma mark - AB Logging
+void FIRCLSUserLoggingCheckAndSwapABFiles(FIRCLSUserLoggingABStorage *storage,
+ const char **activePath,
+ off_t fileSize) {
+ if (!activePath || !storage) {
+ return;
+ }
+
+ if (!*activePath) {
+ return;
+ }
+
+ if (storage->restrictBySize) {
+ if (fileSize <= storage->maxSize) {
+ return;
+ }
+ } else {
+ if (!FIRCLSIsValidPointer(storage->entryCount)) {
+ FIRCLSSDKLogError("Error: storage has invalid pointer, but is restricted by entry count\n");
+ return;
+ }
+
+ if (*storage->entryCount < storage->maxEntries) {
+ return;
+ }
+
+ // Here we have rolled over, so we have to reset our counter.
+ *storage->entryCount = 0;
+ }
+
+ // if it is too big:
+ // - reset the other log
+ // - make it active
+ const char *otherPath = NULL;
+
+ if (*activePath == storage->aPath) {
+ otherPath = storage->bPath;
+ } else {
+ // take this path if the pointer is invalid as well, to reset
+ otherPath = storage->aPath;
+ }
+
+ // guard here against path being nil or empty
+ NSString *pathString = [NSString stringWithUTF8String:otherPath];
+
+ if ([pathString length] > 0) {
+ // ignore the error, because there is nothing we can do to recover here, and its likely
+ // any failures would be intermittent
+
+ [[NSFileManager defaultManager] removeItemAtPath:pathString error:nil];
+ }
+
+ *activePath = otherPath;
+}
+
+void FIRCLSUserLoggingWriteAndCheckABFiles(FIRCLSUserLoggingABStorage *storage,
+ const char **activePath,
+ void (^openedFileBlock)(FIRCLSFile *file)) {
+ if (!storage || !activePath || !openedFileBlock) {
+ return;
+ }
+
+ if (!*activePath) {
+ return;
+ }
+
+ if (storage->restrictBySize) {
+ if (storage->maxSize == 0) {
+ return;
+ }
+ } else {
+ if (storage->maxEntries == 0) {
+ return;
+ }
+ }
+
+ dispatch_sync(FIRCLSGetLoggingQueue(), ^{
+ FIRCLSFile file;
+
+ if (!FIRCLSFileInitWithPath(&file, *activePath, true)) {
+ FIRCLSSDKLog("Unable to open log file\n");
+ return;
+ }
+
+ openedFileBlock(&file);
+
+ off_t fileSize = 0;
+ FIRCLSFileCloseWithOffset(&file, &fileSize);
+
+ // increment the count before calling FIRCLSUserLoggingCheckAndSwapABFiles, so the value
+ // reflects the actual amount of stuff written
+ if (!storage->restrictBySize && FIRCLSIsValidPointer(storage->entryCount)) {
+ *storage->entryCount += 1;
+ }
+
+ dispatch_async(FIRCLSGetLoggingQueue(), ^{
+ FIRCLSUserLoggingCheckAndSwapABFiles(storage, activePath, fileSize);
+ });
+ });
+}
+
+void FIRCLSLogInternalWrite(FIRCLSFile *file, NSString *message, uint64_t time) {
+ FIRCLSFileWriteSectionStart(file, "log");
+ FIRCLSFileWriteHashStart(file);
+ FIRCLSFileWriteHashEntryHexEncodedString(file, "msg", [message UTF8String]);
+ FIRCLSFileWriteHashEntryUint64(file, "time", time);
+ FIRCLSFileWriteHashEnd(file);
+ FIRCLSFileWriteSectionEnd(file);
+}
+
+void FIRCLSLogInternal(NSString *message) {
+ if (!message) {
+ return;
+ }
+
+ if (!FIRCLSContextIsInitialized()) {
+ FIRCLSWarningLog(@"WARNING: FIRCLSLog has been used before (or concurrently with) "
+ @"Crashlytics initialization and cannot be recorded. The message was: \n%@",
+ message);
+ return;
+ }
+ struct timeval te;
+
+ NSUInteger messageLength = [message length];
+ int maxLogSize = _firclsContext.readonly->logging.logStorage.maxSize;
+
+ if (messageLength > maxLogSize) {
+ FIRCLSWarningLog(
+ @"WARNING: Attempted to write %zd bytes, but %d is the maximum size of the log. "
+ @"Truncating to %d bytes.\n",
+ messageLength, maxLogSize, maxLogSize);
+ message = [message substringToIndex:maxLogSize];
+ }
+
+ // unable to get time - abort
+ if (gettimeofday(&te, NULL) != 0) {
+ return;
+ }
+
+ 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);
+ });
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h
new file mode 100644
index 000000000..2c259c296
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.h
@@ -0,0 +1,61 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+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)delegate;
+
+@property(nonatomic, weak) id 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
+@required
+- (BOOL)networkClientCanUseBackgroundSessions:(FIRCLSNetworkClient *)client;
+
+@optional
+- (void)networkClient:(FIRCLSNetworkClient *)client
+ didFinishUploadWithPath:(NSString *)path
+ error:(NSError *)error;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m
new file mode 100644
index 000000000..5fee0ca9f
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSNetworkClient.m
@@ -0,0 +1,366 @@
+// 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 () {
+ 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)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
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h
new file mode 100644
index 000000000..02b7031a5
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.h
@@ -0,0 +1,57 @@
+// 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
+
+#include "FIRCLSApplicationIdentifierModel.h"
+#include "FIRCLSProfiling.h"
+#include "FIRCrashlytics.h"
+
+@class FBLPromise;
+
+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)analytics
+ googleAppID:(NSString *)googleAppID
+ dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
+ googleTransport:(GDTCORTransport *)googleTransport
+ appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
+ settings:(FIRCLSSettings *)settings NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
++ (instancetype)new NS_UNAVAILABLE;
+
+- (FBLPromise *)startWithProfilingMark:(FIRCLSProfileMark)mark;
+
+- (FBLPromise *)checkForUnsentReports;
+- (FBLPromise *)sendUnsentReports;
+- (FBLPromise *)deleteUnsentReports;
+
+@end
+
+extern NSString *const FIRCLSConfigSubmitReportsKey;
+extern NSString *const FIRCLSConfigPackageReportsKey;
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m
new file mode 100644
index 000000000..cf04b3f1f
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager.m
@@ -0,0 +1,909 @@
+// 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.
+
+//
+// The report manager has the ability to send to two different endpoints.
+//
+// The old legacy flow for a report goes through the following states/folders:
+// 1. active - .clsrecords optimized for crash time persistence
+// 2. processing - .clsrecords with attempted symbolication
+// 3. prepared-legacy - .multipartmime of compressed .clsrecords
+//
+// The new flow for a report goes through the following states/folders:
+// 1. active - .clsrecords optimized for crash time persistence
+// 2. processing - .clsrecords with attempted symbolication
+// 3. prepared - .clsrecords moved from processing with no changes
+//
+// The code was designed so the report processing workflows are not dramatically different from one
+// another. The design will help avoid having a lot of conditional code blocks throughout the
+// codebase.
+//
+
+#include
+
+#if __has_include()
+#import
+#else
+#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"
+
+#include "FIRCLSGlobals.h"
+#include "FIRCLSUtility.h"
+
+#import "FIRCLSConstants.h"
+#import "FIRCLSExecutionIdentifierModel.h"
+#import "FIRCLSInstallIdentifierModel.h"
+#import "FIRCLSSettingsOnboardingManager.h"
+
+#import "FIRCLSReportManager_Private.h"
+
+#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
+#import "Interop/Analytics/Public/FIRAnalyticsInteropListener.h"
+
+#include "FIRAEvent+Internal.h"
+#include "FIRCLSFCRAnalytics.h"
+
+#if TARGET_OS_IPHONE
+#import
+#else
+#import
+#endif
+
+static NSTimeInterval const CLSReportRetryInterval = 10 * 60;
+
+static NSString *FIRCLSFirebaseAnalyticsEventLogFormat = @"$A$:%@";
+
+@interface FIRCLSAnalyticsInteropListener : NSObject {
+}
+@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.
+ */
+typedef NS_ENUM(NSInteger, FIRCLSReportAction) {
+ /** Upload the reports to Crashlytics. */
+ FIRCLSReportActionSend,
+ /** Delete the reports without uploading them. */
+ FIRCLSReportActionDelete,
+};
+
+/**
+ * This is just a helper to make code using FIRReportAction more readable.
+ */
+typedef NSNumber FIRCLSWrappedReportAction;
+@implementation NSNumber (FIRCLSWrappedReportAction)
+- (FIRCLSReportAction)reportActionValue {
+ return [self intValue];
+}
+@end
+
+/**
+ * This is a helper to make code using NSNumber for bools more readable.
+ */
+typedef NSNumber FIRCLSWrappedBool;
+
+@interface FIRCLSReportManager () {
+ FIRCLSFileManager *_fileManager;
+ FIRCLSNetworkClient *_networkClient;
+ FIRCLSReportUploader *_uploader;
+ dispatch_queue_t _dispatchQueue;
+ NSOperationQueue *_operationQueue;
+ id _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 *_unsentReportsAvailable;
+
+ // A promise that will be resolved when the user has provided an action that they want to perform
+ // for all the unsent reports.
+ FBLPromise *_reportActionProvided;
+
+ // A promise that will be resolved when all unsent reports have been "handled". They won't
+ // necessarily have been uploaded, but we will know whether they should be sent or deleted, and
+ // the initial work to make that happen will have been processed on the work queue.
+ //
+ // Currently only used for testing
+ FBLPromise *_unsentReportsHandled;
+
+ // A token to make sure that checkForUnsentReports only gets called once.
+ atomic_bool _checkForUnsentReportsCalled;
+
+ BOOL _registeredAnalyticsEventListener;
+}
+
+@property(nonatomic, readonly) NSString *googleAppID;
+
+@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;
+
+// Runs the operations that fetch settings and call onboarding endpoints
+@property(nonatomic, strong) FIRCLSSettingsOnboardingManager *settingsAndOnboardingManager;
+
+@property(nonatomic, strong) GDTCORTransport *googleTransport;
+
+@end
+
+@implementation FIRCLSReportManager
+
+// Used only for internal data collection E2E testing
+static void (^reportSentCallback)(void);
+
+- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
+ installations:(FIRInstallations *)installations
+ analytics:(id)analytics
+ googleAppID:(NSString *)googleAppID
+ dataArbiter:(FIRCLSDataCollectionArbiter *)dataArbiter
+ googleTransport:(GDTCORTransport *)googleTransport
+ appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
+ settings:(FIRCLSSettings *)settings {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _fileManager = fileManager;
+ _analytics = analytics;
+ _googleAppID = [googleAppID copy];
+ _dataArbiter = dataArbiter;
+
+ _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];
+
+ _unsentReportsAvailable = [FBLPromise pendingPromise];
+ _reportActionProvided = [FBLPromise pendingPromise];
+ _unsentReportsHandled = [FBLPromise pendingPromise];
+
+ _checkForUnsentReportsCalled = NO;
+
+ _installIDModel = [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:installations];
+ _executionIDModel = [[FIRCLSExecutionIdentifierModel alloc] init];
+
+ _settings = settings;
+ _appIDModel = appIDModel;
+
+ _settingsAndOnboardingManager =
+ [[FIRCLSSettingsOnboardingManager alloc] initWithAppIDModel:appIDModel
+ installIDModel:self.installIDModel
+ settings:self.settings
+ fileManager:self.fileManager
+ googleAppID:self.googleAppID];
+
+ 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 *)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.
+// This method waits until either:
+// 1. Data collection becomes enabled, in which case, the promise will be resolved with Send.
+// 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 *)waitForReportAction {
+ FIRCLSDebugLog(@"[Crashlytics:Crash] Notifying that unsent reports are available.");
+ [_unsentReportsAvailable fulfill:@YES];
+
+ // 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.
+ FBLPromise *collectionEnabled =
+ [[self.dataArbiter waitForCrashlyticsCollectionEnabled]
+ then:^id _Nullable(NSNumber *_Nullable value) {
+ 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 *)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];
+ }
+ return _unsentReportsAvailable;
+}
+
+- (FBLPromise *)sendUnsentReports {
+ [_reportActionProvided fulfill:@(FIRCLSReportActionSend)];
+ return _unsentReportsHandled;
+}
+
+- (FBLPromise *)deleteUnsentReports {
+ [_reportActionProvided fulfill:@(FIRCLSReportActionDelete)];
+ return _unsentReportsHandled;
+}
+
+- (FBLPromise *)startWithProfilingMark:(FIRCLSProfileMark)mark {
+ NSString *executionIdentifier = self.executionIDModel.executionID;
+
+ // This needs to be called before any values are read from settings
+ NSTimeInterval currentTimestamp = [NSDate timeIntervalSinceReferenceDate];
+ [self.settings reloadFromCacheWithGoogleAppID:self.googleAppID currentTimestamp:currentTimestamp];
+
+ if (![self validateAppIdentifiers]) {
+ return [FBLPromise resolvedWith:@NO];
+ }
+
+#if DEBUG
+ 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:]");
+
+ return [FBLPromise resolvedWith:@NO];
+ }
+
+ if (![_fileManager createReportDirectories]) {
+ return [FBLPromise resolvedWith:@NO];
+ }
+
+ // Grab existing reports
+ BOOL launchFailure = [self checkForAndCreateLaunchMarker];
+ NSArray *preexistingReportPaths = _fileManager.activePathContents;
+
+ FIRCLSInternalReport *report = [self setupCurrentReport:executionIdentifier];
+ if (!report) {
+ FIRCLSErrorLog(@"Unable to setup a new report");
+ }
+
+ if (![self startCrashReporterWithProfilingMark:mark report:report]) {
+ FIRCLSErrorLog(@"Unable to start crash reporter");
+ 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 *promise = [FBLPromise resolvedWith:@(report != nil)];
+
+ if ([self.dataArbiter isCrashlyticsCollectionEnabled]) {
+ FIRCLSDebugLog(@"Automatic data collection is enabled.");
+ FIRCLSDebugLog(@"Unsent reports will be uploaded at startup");
+ FIRCLSDataCollectionToken *dataCollectionToken = [FIRCLSDataCollectionToken validToken];
+
+ [self beginSettingsAndOnboardingWithToken:dataCollectionToken waitForSettingsRequest:NO];
+
+ [self beginReportUploadsWithToken:dataCollectionToken
+ preexistingReportPaths:preexistingReportPaths
+ blockingSend:launchFailure
+ report:report];
+
+ // If data collection is enabled, the SDK will not notify the user
+ // when unsent reports are available, or respect Send / DeleteUnsentReports
+ [_unsentReportsAvailable fulfill:@NO];
+
+ } else {
+ FIRCLSDebugLog(@"Automatic data collection is disabled.");
+
+ // 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];
+
+ // 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 beginSettingsAndOnboardingWithToken:dataCollectionToken
+ waitForSettingsRequest:waitForSetting];
+
+ [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];
+ }
+ }
+
+ if (report != nil) {
+ // capture the start-up time here, but record it asynchronously
+ double endMark = FIRCLSProfileEnd(mark);
+
+ dispatch_async(FIRCLSGetLoggingQueue(), ^{
+ FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSStartTimeKey, [@(endMark) description]);
+ });
+ }
+
+ // To make the code more predictable and therefore testable, don't resolve the startup promise
+ // until the operations that got queued up for processing reports have been processed through the
+ // work queue.
+ 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;
+ }];
+
+ 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 {
+ 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];
+ });
+ }
+}
+
+- (void)beginReportUploadsWithToken:(FIRCLSDataCollectionToken *)token
+ preexistingReportPaths:(NSArray *)preexistingReportPaths
+ blockingSend:(BOOL)blockingSend
+ report:(FIRCLSInternalReport *)report {
+ if (self.settings.collectReportsEnabled) {
+ [self processExistingReportPaths:preexistingReportPaths
+ dataCollectionToken:token
+ asUrgent:blockingSend];
+ [self handleContentsInOtherReportingDirectoriesWithToken:token];
+
+ } else {
+ FIRCLSInfoLog(@"Collect crash reports is disabled");
+ [self deleteUnsentReportsWithPreexisting:preexistingReportPaths];
+ }
+}
+
+- (BOOL)startCrashReporterWithProfilingMark:(FIRCLSProfileMark)mark
+ report:(FIRCLSInternalReport *)report {
+ if (!report) {
+ return NO;
+ }
+
+ if (!FIRCLSContextInitialize(report, self.settings, self.installIDModel, _fileManager)) {
+ return NO;
+ }
+
+ [self setupStateNotifications];
+
+ [self registerAnalyticsEventListener];
+
+ [self crashReportingSetupCompleted:mark];
+
+ return YES;
+}
+
+- (void)crashReportingSetupCompleted:(FIRCLSProfileMark)mark {
+ // check our handlers
+ FIRCLSDispatchAfter(2.0, dispatch_get_main_queue(), ^{
+ FIRCLSExceptionCheckHandlers((__bridge void *)(self));
+ FIRCLSSignalCheckHandlers();
+#if CLS_MACH_EXCEPTION_SUPPORTED
+ FIRCLSMachExceptionCheckHandlers();
+#endif
+ });
+
+ // remove the launch failure marker and record the startup time
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self removeLaunchFailureMarker];
+ dispatch_async(FIRCLSGetLoggingQueue(), ^{
+ FIRCLSUserLoggingWriteInternalKeyValue(FIRCLSFirstRunloopTurnTimeKey,
+ [@(FIRCLSProfileEnd(mark)) description]);
+ });
+ });
+}
+
+- (BOOL)validateAppIdentifiers {
+ // When the ApplicationIdentifierModel fails to initialize, it is usually due to
+ // failing computeExecutableInfo. This can happen if the user sets the
+ // Exported Symbols File in Build Settings, and leaves off the one symbol
+ // that Crashlytics needs, "__mh_execute_header" (wich is defined in mach-o/ldsyms.h as
+ // _MH_EXECUTE_SYM). From https://github.com/firebase/firebase-ios-sdk/issues/5020
+ if (!self.appIDModel) {
+ FIRCLSErrorLog(
+ @"Crashlytics could not find the symbol for the app's main function and cannot "
+ @"start up. This can happen when Exported Symbols File is set in Build Settings. To "
+ @"resolve this, add \"__mh_execute_header\" as a newline to your Exported Symbols File.");
+ return NO;
+ }
+
+ if (self.appIDModel.bundleID.length == 0) {
+ FIRCLSErrorLog(@"An application must have a valid bundle identifier in its Info.plist");
+ 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];
+
+ NSString *reportPath = [_fileManager setupNewPathForExecutionIdentifier:executionIdentifier];
+
+ return [[FIRCLSInternalReport alloc] initWithPath:reportPath
+ 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
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h
new file mode 100644
index 000000000..f7f139740
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportManager_Private.h
@@ -0,0 +1,36 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRCLSReportManager.h"
+#import "FIRCLSReportUploader.h"
+
+@class FIRCLSInstallIdentifierModel;
+
+@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;
+
+- (BOOL)potentiallySubmittableCrashOccurred;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h
new file mode 100644
index 000000000..c1cf8b4a1
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h
@@ -0,0 +1,80 @@
+// 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
+
+#import
+
+@class FIRCLSDataCollectionToken;
+@class FIRCLSInternalReport;
+@class FIRCLSSettings;
+@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)delegate
+ dataSource:(id)dataSource
+ client:(FIRCLSNetworkClient *)client
+ fileManager:(FIRCLSFileManager *)fileManager
+ analytics:(id)analytics NS_DESIGNATED_INITIALIZER;
+
+@property(nonatomic, weak) id delegate;
+@property(nonatomic, weak) id dataSource;
+
+@property(nonatomic, readonly) NSOperationQueue *operationQueue;
+@property(nonatomic, readonly) FIRCLSNetworkClient *networkClient;
+@property(nonatomic, readonly) FIRCLSFileManager *fileManager;
+
+- (BOOL)prepareAndSubmitReport:(FIRCLSInternalReport *)report
+ dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
+ asUrgent:(BOOL)urgent
+ withProcessing:(BOOL)shouldProcess;
+
+- (BOOL)uploadPackagedReportAtPath:(NSString *)path
+ dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
+ asUrgent:(BOOL)urgent;
+
+- (void)reportUploadAtPath:(NSString *)path
+ dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
+ completedWithError:(NSError *)error;
+
+@end
+
+@protocol FIRCLSReportUploaderDelegate
+@required
+
+- (void)didCompletePackageSubmission:(NSString *)path
+ dataCollectionToken:(FIRCLSDataCollectionToken *)token
+ error:(NSError *)error;
+- (void)didCompleteAllSubmissions;
+
+@end
+
+@protocol FIRCLSReportUploaderDataSource
+@required
+
+- (NSString *)googleAppID;
+- (FIRCLSSettings *)settings;
+- (GDTCORTransport *)googleTransport;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m
new file mode 100644
index 000000000..6b8263534
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m
@@ -0,0 +1,356 @@
+// 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 "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"
+
+#include "FIRCLSUtility.h"
+
+#import "FIRCLSConstants.h"
+#import "FIRCLSMultipartMimeStreamEncoder.h"
+#import "FIRCLSURLBuilder.h"
+
+#import
+#import
+
+@interface FIRCLSReportUploader () {
+ id _analytics;
+}
+@end
+
+@implementation FIRCLSReportUploader
+
+- (instancetype)initWithQueue:(NSOperationQueue *)queue
+ delegate:(id)delegate
+ dataSource:(id)dataSource
+ client:(FIRCLSNetworkClient *)client
+ fileManager:(FIRCLSFileManager *)fileManager
+ analytics:(id)analytics {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _operationQueue = queue;
+ _delegate = delegate;
+ _dataSource = dataSource;
+ _networkClient = client;
+ _fileManager = fileManager;
+ _analytics = analytics;
+
+ return self;
+}
+
+#pragma mark - Packaging and Submission
+- (BOOL)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;
+ }
+
+ FIRCLSApplicationActivity(
+ FIRCLSApplicationActivityDefault, @"Crashlytics Crash Report Processing", ^{
+ if (shouldProcess) {
+ if (![self.fileManager moveItemAtPath:report.path
+ toDirectory:self.fileManager.processingPath]) {
+ FIRCLSErrorLog(@"Unable to move report for processing");
+ return;
+ }
+
+ // adjust the report's path, and process it
+ [report setPath:[self.fileManager.processingPath
+ stringByAppendingPathComponent:report.directoryName]];
+
+ FIRCLSSymbolResolver *resolver = [[FIRCLSSymbolResolver alloc] init];
+
+ FIRCLSProcessReportOperation *processOperation =
+ [[FIRCLSProcessReportOperation alloc] initWithReport:report resolver:resolver];
+
+ [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");
+ }
+ }
+
+ NSLog(@"[Firebase/Crashlytics] Packaged report with id '%@' for submission",
+ report.identifier);
+
+ success = [self uploadPackagedReportAtPath:packagedPath
+ dataCollectionToken:dataCollectionToken
+ asUrgent:urgent];
+
+ // 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];
+ }
+ });
+
+ return success;
+}
+
+- (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
+ dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken
+ asUrgent:(BOOL)urgent {
+ FIRCLSDeveloperLog("Crashlytics:Crash:Reports", @"Submitting report%@",
+ urgent ? @" as urgent" : @"");
+
+ // 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];
+ }
+
+ // Unsupported state
+ return NO;
+}
+
+- (BOOL)cleanUpSubmittedReportAtPath:(NSString *)path {
+ if (![[self fileManager] removeItemAtPath:path]) {
+ FIRCLSErrorLog(@"Unable to remove packaged submission");
+ return NO;
+ }
+
+ 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
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h
new file mode 100644
index 000000000..f54dc1673
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader_Private.h
@@ -0,0 +1,23 @@
+// 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 "FIRCLSReportUploader.h"
+
+@interface FIRCLSReportUploader (PrivateMethods)
+
+@property(nonatomic, readonly) NSURL *reportURL;
+
+- (NSMutableURLRequest *)mutableRequestWithURL:(NSURL *)url timeout:(NSTimeInterval)timeout;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h
new file mode 100644
index 000000000..147bc7bbc
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.h
@@ -0,0 +1,39 @@
+// 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
+
+@class FIRApp;
+@class FBLPromise;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRCLSDataCollectionArbiter : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict;
+
+- (BOOL)isLegacyDataCollectionKeyInPlist;
+
+- (BOOL)isCrashlyticsCollectionEnabled;
+
+- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled;
+
+// Returns a promise that is fulfilled once data collection is enabled.
+- (FBLPromise *)waitForCrashlyticsCollectionEnabled;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m
new file mode 100644
index 000000000..e7d5996cf
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionArbiter.m
@@ -0,0 +1,148 @@
+// 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 "FIRCLSDataCollectionArbiter.h"
+
+#if __has_include()
+#import
+#else
+#import "FBLPromises.h"
+#endif
+
+#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
+
+#import "FIRCLSUserDefaults.h"
+
+// The legacy data collection setting allows Fabric customers to turn off auto-
+// initialization, but can be overridden by calling [Fabric with:].
+//
+// While we support Fabric, we must have two different versions, because
+// they require these slightly different semantics.
+NSString *const FIRCLSLegacyCrashlyticsCollectionKey = @"firebase_crashlytics_collection_enabled";
+
+// The new data collection setting can be set by an API that is stored in FIRCLSUserDefaults
+NSString *const FIRCLSDataCollectionEnabledKey = @"com.crashlytics.data_collection";
+
+// The new data collection setting also allows Firebase customers to turn off data
+// collection in their Info.plist, and can be overridden by setting it to true using
+// the setCrashlyticsCollectionEnabled API.
+NSString *const FIRCLSCrashlyticsCollectionKey = @"FirebaseCrashlyticsCollectionEnabled";
+
+typedef NS_ENUM(NSInteger, FIRCLSDataCollectionSetting) {
+ FIRCLSDataCollectionSettingNotSet = 0,
+ FIRCLSDataCollectionSettingEnabled = 1,
+ FIRCLSDataCollectionSettingDisabled = 2,
+};
+
+@interface FIRCLSDataCollectionArbiter () {
+ NSLock *_mutex;
+ FBLPromise *_dataCollectionEnabled;
+ BOOL _promiseResolved;
+ FIRApp *_app;
+ NSDictionary *_appInfo;
+}
+@end
+
+@implementation FIRCLSDataCollectionArbiter
+
+- (instancetype)initWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict {
+ self = [super init];
+ if (self) {
+ _mutex = [[NSLock alloc] init];
+ _appInfo = dict;
+ _app = app;
+ if ([FIRCLSDataCollectionArbiter isCrashlyticsCollectionEnabledWithApp:app withAppInfo:dict]) {
+ _dataCollectionEnabled = [FBLPromise resolvedWith:nil];
+ _promiseResolved = YES;
+ } else {
+ _dataCollectionEnabled = [FBLPromise pendingPromise];
+ _promiseResolved = NO;
+ }
+ }
+
+ return self;
+}
+
+/*
+ * Legacy collection key that we provide for customers to disable Crash reporting.
+ * Customers can later turn on Crashlytics using Fabric.with if they choose to do so.
+ *
+ * This flag is unsupported for the "New SDK"
+ */
+- (BOOL)isLegacyDataCollectionKeyInPlist {
+ if ([_appInfo objectForKey:FIRCLSLegacyCrashlyticsCollectionKey]) {
+ return true;
+ }
+
+ return false;
+}
+
+// This functionality is called in the initializer before self is fully initialized,
+// so a class method is used. The instance method below allows for a consistent clean API.
++ (BOOL)isCrashlyticsCollectionEnabledWithApp:(FIRApp *)app withAppInfo:(NSDictionary *)dict {
+ FIRCLSDataCollectionSetting stickySetting = [FIRCLSDataCollectionArbiter stickySetting];
+ if (stickySetting != FIRCLSDataCollectionSettingNotSet) {
+ return stickySetting == FIRCLSDataCollectionSettingEnabled;
+ }
+
+ id firebaseCrashlyticsCollectionEnabled = [dict objectForKey:FIRCLSCrashlyticsCollectionKey];
+ if ([firebaseCrashlyticsCollectionEnabled isKindOfClass:[NSString class]] ||
+ [firebaseCrashlyticsCollectionEnabled isKindOfClass:[NSNumber class]]) {
+ return [firebaseCrashlyticsCollectionEnabled boolValue];
+ }
+
+ return [app isDataCollectionDefaultEnabled];
+}
+
+- (BOOL)isCrashlyticsCollectionEnabled {
+ return [FIRCLSDataCollectionArbiter isCrashlyticsCollectionEnabledWithApp:_app
+ withAppInfo:_appInfo];
+}
+
+- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled {
+ FIRCLSUserDefaults *userDefaults = [FIRCLSUserDefaults standardUserDefaults];
+ FIRCLSDataCollectionSetting setting =
+ enabled ? FIRCLSDataCollectionSettingEnabled : FIRCLSDataCollectionSettingDisabled;
+ [userDefaults setInteger:setting forKey:FIRCLSDataCollectionEnabledKey];
+ [userDefaults synchronize];
+
+ [_mutex lock];
+ if (enabled) {
+ if (!_promiseResolved) {
+ [_dataCollectionEnabled fulfill:nil];
+ _promiseResolved = YES;
+ }
+ } else {
+ if (_promiseResolved) {
+ _dataCollectionEnabled = [FBLPromise pendingPromise];
+ _promiseResolved = NO;
+ }
+ }
+ [_mutex unlock];
+}
+
++ (FIRCLSDataCollectionSetting)stickySetting {
+ FIRCLSUserDefaults *userDefaults = [FIRCLSUserDefaults standardUserDefaults];
+ return [userDefaults integerForKey:FIRCLSDataCollectionEnabledKey];
+}
+
+- (FBLPromise *)waitForCrashlyticsCollectionEnabled {
+ FBLPromise *result = nil;
+ [_mutex lock];
+ result = _dataCollectionEnabled;
+ [_mutex unlock];
+ return result;
+}
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h
new file mode 100644
index 000000000..4ab2bb661
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.h
@@ -0,0 +1,45 @@
+// 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
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * A FIRCLSDataCollectionToken represents having permission to upload data. A data collection token
+ * is either valid or nil. Every function that directly initiates a network operation that will
+ * result in data collection must check to make sure it has been passed a valid token. Tokens should
+ * only be created when either (1) automatic data collection is enabled, or (2) the user has
+ * explicitly given permission to collect data for a particular purpose, using the API. For all the
+ * functions in between, the data collection token getting passed as an argument helps to document
+ * and enforce the flow of data collection permission through the SDK.
+ */
+@interface FIRCLSDataCollectionToken : NSObject
+
+/**
+ * Creates a valid token. Only call this method when either (1) automatic data collection is
+ * enabled, or (2) the user has explicitly given permission to collect data for a particular
+ * purpose, using the API.
+ */
++ (instancetype)validToken;
+
+/**
+ * Use this to verify that a token is valid. If this is called on a nil instance, it will return NO.
+ * @return YES.
+ */
+- (BOOL)isValid;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m
new file mode 100644
index 000000000..1a41ee1df
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/DataCollection/FIRCLSDataCollectionToken.m
@@ -0,0 +1,27 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRCLSDataCollectionToken.h"
+
+@implementation FIRCLSDataCollectionToken
+
++ (instancetype)validToken {
+ return [[FIRCLSDataCollectionToken alloc] init];
+}
+
+- (BOOL)isValid {
+ return YES;
+}
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h
new file mode 100644
index 000000000..fa53468c1
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.h
@@ -0,0 +1,84 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import "FIRCLSURLSessionConfiguration.h"
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface FIRCLSURLSession : NSObject {
+ id _delegate;
+ NSOperationQueue *_delegateQueue;
+ NSURLSessionConfiguration *_configuration;
+ NSMutableSet *_taskSet;
+ dispatch_queue_t _queue;
+
+ NSString *_sessionDescription;
+}
+
++ (BOOL)NSURLSessionShouldBeUsed;
+
++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
+ delegate:(nullable id)delegate
+ delegateQueue:(nullable NSOperationQueue *)queue;
+
+@property(nonatomic, readonly, retain) NSOperationQueue *delegateQueue;
+@property(nonatomic, readonly, retain) id delegate;
+@property(nonatomic, readonly, copy) NSURLSessionConfiguration *configuration;
+
+@property(nonatomic, copy) NSString *sessionDescription;
+
+- (void)getTasksWithCompletionHandler:
+ (void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler;
+
+// task creation - suitable for background operations
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
+
+- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
+- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
+
+// convenience methods (that are not available for background sessions
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
+ completionHandler:(nullable void (^)(NSData *data,
+ NSURLResponse *response,
+ NSError *error))completionHandler;
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
+
+- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
+ completionHandler:
+ (nullable void (^)(NSURL *targetPath,
+ NSURLResponse *response,
+ NSError *error))completionHandler;
+
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
+ fromFile:(NSURL *)fileURL
+ completionHandler:
+ (nullable void (^)(NSData *data,
+ NSURLResponse *response,
+ NSError *error))completionHandler;
+
+- (void)invalidateAndCancel;
+- (void)finishTasksAndInvalidate;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m
new file mode 100644
index 000000000..981542368
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession.m
@@ -0,0 +1,346 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+#import "FIRCLSURLSession.h"
+
+#import "FIRCLSURLSessionDataTask.h"
+#import "FIRCLSURLSessionDataTask_PrivateMethods.h"
+#import "FIRCLSURLSessionDownloadTask.h"
+#import "FIRCLSURLSessionDownloadTask_PrivateMethods.h"
+#import "FIRCLSURLSessionTask_PrivateMethods.h"
+#import "FIRCLSURLSessionUploadTask.h"
+
+#define DELEGATE ((id)self->_delegate)
+
+@interface FIRCLSURLSession ()
+
+@property(nonatomic, retain) NSOperationQueue *delegateQueue;
+@property(nonatomic, retain) id delegate;
+@property(nonatomic, copy) NSURLSessionConfiguration *configuration;
+
+@end
+
+@implementation FIRCLSURLSession
+
+@synthesize delegate = _delegate;
+@synthesize delegateQueue = _delegateQueue;
+@synthesize configuration = _configuration;
+@synthesize sessionDescription = _sessionDescription;
+
++ (BOOL)NSURLSessionShouldBeUsed {
+ if (!NSClassFromString(@"NSURLSession")) {
+ return NO;
+ }
+
+ // We use this as a proxy to verify that we are on at least iOS 8 or 10.10. The first OSes that
+ // has NSURLSession were fairly unstable.
+ return [[NSURLSessionConfiguration class]
+ respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)];
+}
+
++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration {
+ return [self sessionWithConfiguration:configuration delegate:nil delegateQueue:nil];
+}
+
++ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
+ delegate:(nullable id)delegate
+ delegateQueue:(nullable NSOperationQueue *)queue {
+ if ([self NSURLSessionShouldBeUsed]) {
+ return [NSURLSession sessionWithConfiguration:configuration
+ delegate:delegate
+ delegateQueue:queue];
+ }
+
+ if (!configuration) {
+ return nil;
+ }
+
+#if __has_feature(objc_arc)
+ FIRCLSURLSession *session = [self new];
+#else
+ FIRCLSURLSession *session = [[self new] autorelease];
+#endif
+ [session setDelegate:delegate];
+ // When delegate exists, but delegateQueue is nil, create a serial queue like NSURLSession
+ // documents.
+ if (delegate && !queue) {
+ queue = [self newDefaultDelegateQueue];
+ }
+ session.delegateQueue = queue;
+ session.configuration = configuration;
+ return (NSURLSession *)session;
+}
+
++ (NSOperationQueue *)newDefaultDelegateQueue {
+ NSOperationQueue *delegateQueue = [[NSOperationQueue alloc] init];
+ delegateQueue.name = [NSString stringWithFormat:@"%@ %p", NSStringFromClass(self), self];
+ delegateQueue.maxConcurrentOperationCount = 1;
+ return delegateQueue;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _queue = dispatch_queue_create("com.crashlytics.URLSession", 0);
+
+ return self;
+}
+
+#if !__has_feature(objc_arc)
+- (void)dealloc {
+ [_taskSet release];
+ [_delegate release];
+ [_delegateQueue release];
+ [_configuration release];
+
+#if !OS_OBJECT_USE_OBJC
+ dispatch_release(_queue);
+#endif
+
+ [super dealloc];
+}
+#endif
+
+#pragma mark - Managing the Session
+
+- (void)invalidateAndCancel {
+ dispatch_sync(_queue, ^{
+ for (FIRCLSURLSessionTask *task in self->_taskSet) {
+ [task cancel];
+ }
+ });
+
+ self.delegate = nil;
+}
+
+- (void)finishTasksAndInvalidate {
+ self.delegate = nil;
+}
+
+#pragma mark -
+
+- (void)getTasksWithCompletionHandler:
+ (void (^)(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks))completionHandler {
+ [[self delegateQueue] addOperationWithBlock:^{
+ // TODO - this is totally wrong, but better than not calling back at all
+ completionHandler(@[], @[], @[]);
+ }];
+}
+
+- (void)removeTaskFromSet:(FIRCLSURLSessionTask *)task {
+ dispatch_async(_queue, ^{
+ [self->_taskSet removeObject:task];
+ });
+}
+
+- (void)configureTask:(FIRCLSURLSessionTask *)task
+ withRequest:(NSURLRequest *)request
+ block:(void (^)(NSMutableURLRequest *mutableRequest))block {
+ NSMutableURLRequest *modifiedRequest = [request mutableCopy];
+
+ dispatch_sync(_queue, ^{
+ [self->_taskSet addObject:task];
+
+ // TODO: this isn't allowed to overwrite existing headers
+ for (NSString *key in [self->_configuration HTTPAdditionalHeaders]) {
+ [modifiedRequest addValue:[[self->_configuration HTTPAdditionalHeaders] objectForKey:key]
+ forHTTPHeaderField:key];
+ }
+ });
+
+ if (block) {
+ block(modifiedRequest);
+ }
+
+ [task setOriginalRequest:modifiedRequest];
+ [task setDelegate:self];
+
+#if !__has_feature(objc_arc)
+ [modifiedRequest release];
+#endif
+}
+
+- (BOOL)shouldInvokeDelegateSelector:(SEL)selector forTask:(FIRCLSURLSessionTask *)task {
+ return [task invokesDelegate] && [_delegate respondsToSelector:selector];
+}
+
+#pragma mark Task Creation
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
+ fromFile:(NSURL *)fileURL {
+ return [self uploadTaskWithRequest:request fromFile:fileURL completionHandler:nil];
+}
+
+- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request {
+ return [self downloadTaskWithRequest:request completionHandler:nil];
+}
+
+- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url {
+ return [self downloadTaskWithRequest:[NSURLRequest requestWithURL:url]];
+}
+
+#pragma mark Async Convenience Methods
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
+ completionHandler:(nullable void (^)(NSData *data,
+ NSURLResponse *response,
+ NSError *error))completionHandler {
+ FIRCLSURLSessionDataTask *task = [FIRCLSURLSessionDataTask task];
+
+ if (completionHandler) {
+ [task setCompletionHandler:completionHandler];
+ [task setInvokesDelegate:NO];
+ }
+
+ [self configureTask:task withRequest:request block:nil];
+
+ return (NSURLSessionDataTask *)task;
+}
+
+- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request {
+ return [self dataTaskWithRequest:request completionHandler:nil];
+}
+
+- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
+ fromFile:(NSURL *)fileURL
+ completionHandler:
+ (nullable void (^)(NSData *data,
+ NSURLResponse *response,
+ NSError *error))completionHandler {
+ FIRCLSURLSessionUploadTask *task = [FIRCLSURLSessionUploadTask task];
+
+ if (completionHandler) {
+ [task setCompletionHandler:completionHandler];
+ [task setInvokesDelegate:NO];
+ }
+
+ [self configureTask:task
+ withRequest:request
+ block:^(NSMutableURLRequest *mutableRequest) {
+ // you cannot set up both of these, and we'll be using the stream here
+ [mutableRequest setHTTPBody:nil];
+ [mutableRequest setHTTPBodyStream:[NSInputStream inputStreamWithURL:fileURL]];
+ }];
+
+ return (NSURLSessionUploadTask *)task;
+}
+
+- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
+ completionHandler:
+ (nullable void (^)(NSURL *targetPath,
+ NSURLResponse *response,
+ NSError *error))completionHandler {
+ FIRCLSURLSessionDownloadTask *task = [FIRCLSURLSessionDownloadTask task];
+
+ if (completionHandler) {
+ [task setDownloadCompletionHandler:completionHandler];
+ [task setInvokesDelegate:NO];
+ }
+
+ [self configureTask:task withRequest:request block:nil];
+
+ return (NSURLSessionDownloadTask *)task;
+}
+
+#pragma mark FIRCLSURLSessionTaskDelegate
+- (NSURLRequest *)task:(FIRCLSURLSessionTask *)task
+ willPerformHTTPRedirection:(NSHTTPURLResponse *)response
+ newRequest:(NSURLRequest *)request {
+ // just accept the proposed redirection
+ return request;
+}
+
+- (void)task:(FIRCLSURLSessionTask *)task didCompleteWithError:(NSError *)error {
+ if (![self shouldInvokeDelegateSelector:@selector(URLSession:task:didCompleteWithError:)
+ forTask:task]) {
+ [self removeTaskFromSet:task];
+ return;
+ }
+
+ [_delegateQueue addOperationWithBlock:^{
+ [DELEGATE URLSession:(NSURLSession *)self
+ task:(NSURLSessionTask *)task
+ didCompleteWithError:error];
+
+ // Note that you *cannot* clean up here, because this method could be run asynchronously with
+ // the delegate methods that care about the state of the task
+ [self removeTaskFromSet:task];
+ }];
+}
+
+#pragma mark FIRCLSURLSessionDataTask
+- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response {
+ if (![self shouldInvokeDelegateSelector:@selector
+ (URLSession:dataTask:didReceiveResponse:completionHandler:)
+ forTask:task]) {
+ return;
+ }
+
+ [_delegateQueue addOperationWithBlock:^{
+ [DELEGATE URLSession:(NSURLSession *)self
+ dataTask:(NSURLSessionDataTask *)task
+ didReceiveResponse:response
+ completionHandler:^(NSURLSessionResponseDisposition disposition){
+ // nothing to do here
+ }];
+ }];
+}
+
+- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveData:(NSData *)data {
+ if (![self shouldInvokeDelegateSelector:@selector(URLSession:dataTask:didReceiveData:)
+ forTask:task]) {
+ return;
+ }
+
+ [_delegateQueue addOperationWithBlock:^{
+ [DELEGATE URLSession:(NSURLSession *)self
+ dataTask:(NSURLSessionDataTask *)task
+ didReceiveData:data];
+ }];
+}
+
+#pragma mark FIRCLSURLSessionDownloadDelegate
+- (void)downloadTask:(FIRCLSURLSessionDownloadTask *)task didFinishDownloadingToURL:(NSURL *)url {
+ if (![self shouldInvokeDelegateSelector:@selector(URLSession:
+ downloadTask:didFinishDownloadingToURL:)
+ forTask:task]) {
+ // We have to be certain that we cleanup only once the delegate no longer cares about the state
+ // of the task being changed. In the case of download, this is either after the delegate method
+ // has been invoked, or here, if the delegate doesn't care.
+ [task cleanup];
+ return;
+ }
+
+ [_delegateQueue addOperationWithBlock:^{
+ [DELEGATE URLSession:(NSURLSession *)self
+ downloadTask:(NSURLSessionDownloadTask *)task
+ didFinishDownloadingToURL:url];
+
+ // Cleanup for the download tasks is a little complex. As long as we do it only after
+ // the delegate has been informed of the completed download, we are ok.
+ [task cleanup];
+ }];
+}
+
+@end
+
+#else
+
+INJECT_STRIP_SYMBOL(clsurlsession)
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h
new file mode 100644
index 000000000..9f3ead01b
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionAvailability.h
@@ -0,0 +1,28 @@
+// 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.
+
+#pragma once
+
+#include
+
+#define FIRCLSURLSESSION_REQUIRED (!TARGET_OS_WATCH && !TARGET_OS_TV)
+
+// These macros generate a function to force a symbol for the containing .o, to work around an issue
+// where strip will not strip debug information without a symbol to strip.
+#define CONCAT_EXPANDED(a, b) a##b
+#define CONCAT(a, b) CONCAT_EXPANDED(a, b)
+#define DUMMY_FUNCTION_NAME(x) CONCAT(fircls_strip_this_, x)
+#define INJECT_STRIP_SYMBOL(x) \
+ void DUMMY_FUNCTION_NAME(x)(void) { \
+ }
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.h
new file mode 100644
index 000000000..bda0f7083
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.h
@@ -0,0 +1,42 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import
+
+@interface FIRCLSURLSessionConfiguration : NSObject {
+ NSDictionary *_additionalHeaders;
+ NSURLCache *_URLCache;
+ NSHTTPCookieAcceptPolicy _cookiePolicy;
+}
+
++ (NSURLSessionConfiguration *)defaultSessionConfiguration;
++ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
++ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
++ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier;
+
+@property(nonatomic, copy) NSDictionary *HTTPAdditionalHeaders;
+@property(nonatomic, retain) NSURLCache *URLCache;
+@property(nonatomic, assign) NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;
+@property(nonatomic, assign) BOOL sessionSendsLaunchEvents;
+@property(nonatomic, assign) NSTimeInterval timeoutIntervalForRequest;
+@property(nonatomic, assign) NSTimeInterval timeoutIntervalForResource;
+@property(nonatomic, assign) BOOL allowsCellularAccess;
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.m
new file mode 100644
index 000000000..177e7a6bf
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSessionConfiguration.m
@@ -0,0 +1,92 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#import "FIRCLSURLSession.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+#import "FIRCLSURLSessionConfiguration.h"
+
+@implementation FIRCLSURLSessionConfiguration
+
+@synthesize URLCache = _URLCache;
+@synthesize HTTPAdditionalHeaders = _additionalHeaders;
+@synthesize HTTPCookieAcceptPolicy = _cookiePolicy;
+
++ (NSURLSessionConfiguration *)defaultSessionConfiguration {
+ if ([FIRCLSURLSession NSURLSessionShouldBeUsed]) {
+ return [NSURLSessionConfiguration defaultSessionConfiguration];
+ }
+
+#if __has_feature(objc_arc)
+ return [self new];
+#else
+ return [[self new] autorelease];
+#endif
+}
+
++ (NSURLSessionConfiguration *)ephemeralSessionConfiguration {
+ if ([FIRCLSURLSession NSURLSessionShouldBeUsed]) {
+ return [NSURLSessionConfiguration ephemeralSessionConfiguration];
+ }
+
+#if __has_feature(objc_arc)
+ return [self new];
+#else
+ return [[self new] autorelease];
+#endif
+}
+
++ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier {
+ return [self backgroundSessionConfigurationWithIdentifier:identifier];
+}
+
++ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier {
+ if (![FIRCLSURLSession NSURLSessionShouldBeUsed]) {
+ return nil;
+ }
+
+ if ([[NSURLSessionConfiguration class]
+ respondsToSelector:@selector(backgroundSessionConfigurationWithIdentifier:)]) {
+ return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
+ }
+
+ return [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ FIRCLSURLSessionConfiguration *configuration;
+
+ configuration = [FIRCLSURLSessionConfiguration new];
+ [configuration setHTTPAdditionalHeaders:[self HTTPAdditionalHeaders]];
+
+ return configuration;
+}
+
+// This functionality is not supported by the wrapper, so we just stub it out
+- (BOOL)sessionSendsLaunchEvents {
+ return NO;
+}
+
+- (void)setSessionSendsLaunchEvents:(BOOL)sessionSendsLaunchEvents {
+}
+
+@end
+
+#else
+
+INJECT_STRIP_SYMBOL(clsurlsessionconfiguration)
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession_PrivateMethods.h
new file mode 100644
index 000000000..84885e1d6
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/FIRCLSURLSession_PrivateMethods.h
@@ -0,0 +1,27 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import
+
+@interface FIRCLSURLSession (PrivateMethods)
+
+- (void)runOnDelegateQueue:(void (^)(void))block;
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.h
new file mode 100644
index 000000000..22099584e
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.h
@@ -0,0 +1,32 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import "FIRCLSURLSessionTask.h"
+
+@interface FIRCLSURLSessionDataTask : FIRCLSURLSessionTask {
+ void (^_completionHandler)(NSData *data, NSURLResponse *response, NSError *error);
+ NSURLConnection *_connection;
+ NSMutableData *_data;
+ NSString *_taskDescription;
+}
+
+@property(nonatomic, copy) NSString *taskDescription;
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m
new file mode 100644
index 000000000..be40ab753
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask.m
@@ -0,0 +1,124 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+#import "FIRCLSURLSessionDataTask.h"
+
+#import "FIRCLSURLSessionDataTask_PrivateMethods.h"
+
+#define DELEGATE ((id)[self delegate])
+
+@interface FIRCLSURLSessionDataTask ()
+@end
+
+@implementation FIRCLSURLSessionDataTask
+
+@synthesize connection = _connection;
+@synthesize completionHandler = _completionHandler;
+@synthesize taskDescription = _taskDescription;
+
+#if !__has_feature(objc_arc)
+- (void)dealloc {
+ [_connection release];
+ [_completionHandler release];
+ [_taskDescription release];
+ [_data release];
+
+ [super dealloc];
+}
+#endif
+
+- (void)resume {
+ dispatch_async([self queue], ^{
+ NSURLConnection *connection;
+
+ if ([self connection]) {
+ return;
+ }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ connection = [[NSURLConnection alloc] initWithRequest:[self originalRequest]
+ delegate:self
+ startImmediately:NO];
+#pragma clang diagnostic pop
+
+ [self setConnection:connection];
+
+ // bummer we have to do this on a runloop, but other mechanisms require iOS 5 or 10.7
+ [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
+#if !__has_feature(objc_arc)
+ [connection release];
+#endif
+ [connection start];
+ });
+}
+
+- (void)complete {
+ // call completion handler first
+ if (_completionHandler) {
+ // this should go to another queue
+ _completionHandler(_data, [self response], [self error]);
+ }
+
+ // and then finally, call the session delegate completion
+ [DELEGATE task:self didCompleteWithError:[self error]];
+}
+
+- (void)cancel {
+ [self.connection cancel];
+}
+
+#pragma mark NSURLConnectionDelegate
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
+ dispatch_async([self queue], ^{
+ [DELEGATE task:self didReceiveResponse:response];
+
+ [self setResponse:response];
+ });
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ dispatch_async([self queue], ^{
+ if (!self->_data) {
+ self->_data = [NSMutableData new];
+ }
+
+ [self->_data appendData:data];
+ [DELEGATE task:self didReceiveData:data];
+ });
+}
+
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
+ dispatch_async([self queue], ^{
+ [self setError:error];
+ [self complete];
+ });
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
+ dispatch_async([self queue], ^{
+ [self complete];
+ });
+}
+
+@end
+
+#else
+
+INJECT_STRIP_SYMBOL(clsurlsessiondatatask)
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask_PrivateMethods.h
new file mode 100644
index 000000000..f85a3776f
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDataTask_PrivateMethods.h
@@ -0,0 +1,43 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import
+
+#import "FIRCLSURLSessionTask_PrivateMethods.h"
+
+@protocol FIRCLSURLSessionDataDelegate;
+
+@interface FIRCLSURLSessionDataTask ()
+
+@property(nonatomic, retain) NSURLConnection *connection;
+@property(nonatomic, copy) void (^completionHandler)
+ (NSData *data, NSURLResponse *response, NSError *error);
+
+- (void)complete;
+
+@end
+
+@protocol FIRCLSURLSessionDataDelegate
+@required
+
+- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response;
+- (void)task:(FIRCLSURLSessionDataTask *)task didReceiveData:(NSData *)data;
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.h
new file mode 100644
index 000000000..314180f7d
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.h
@@ -0,0 +1,31 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import "FIRCLSURLSessionDataTask.h"
+
+@protocol FIRCLSURLSessionDownloadDelegate;
+
+@interface FIRCLSURLSessionDownloadTask : FIRCLSURLSessionDataTask {
+ void (^_downloadCompletionHandler)(NSURL *targetPath, NSURLResponse *response, NSError *error);
+ NSOutputStream *_outputStream;
+ NSURL *_targetURL;
+}
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.m
new file mode 100644
index 000000000..a0c9b2a84
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask.m
@@ -0,0 +1,157 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+#import "FIRCLSURLSessionDownloadTask.h"
+
+#import "FIRCLSURLSessionDownloadTask_PrivateMethods.h"
+
+#define DELEGATE ((id)[self delegate])
+
+@interface FIRCLSURLSessionDownloadTask ()
+@end
+
+@implementation FIRCLSURLSessionDownloadTask
+
+@synthesize downloadCompletionHandler = _downloadCompletionHandler;
+
+- (id)init {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+#if __has_feature(objc_arc)
+ _targetURL = [self temporaryFileURL];
+ _outputStream = [NSOutputStream outputStreamWithURL:_targetURL append:NO];
+#else
+ _targetURL = [[self temporaryFileURL] retain];
+ _outputStream = [[NSOutputStream outputStreamWithURL:_targetURL append:NO] retain];
+#endif
+
+ [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
+ [_outputStream setDelegate:self];
+
+ return self;
+}
+
+#if !__has_feature(objc_arc)
+- (void)dealloc {
+ [_downloadCompletionHandler release];
+ [_targetURL release];
+ [_outputStream release];
+
+ [super dealloc];
+}
+#else
+- (void)dealloc {
+ [_outputStream close];
+ _outputStream.delegate = nil;
+}
+#endif
+
+- (NSURL *)temporaryFileURL {
+ NSString *tmpPath;
+
+ tmpPath = [NSTemporaryDirectory()
+ stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
+
+ // TODO: make this actually unique
+ return [NSURL fileURLWithPath:tmpPath isDirectory:NO];
+}
+
+- (void)cleanup {
+ // now, remove the temporary file
+ [[NSFileManager defaultManager] removeItemAtURL:_targetURL error:nil];
+}
+
+- (void)complete {
+ // This is an override of FIRCLSURLSessionDataTask's cleanup method
+
+ // call completion handler first
+ if (_downloadCompletionHandler) {
+ _downloadCompletionHandler(_targetURL, [self response], [self error]);
+ }
+
+ // followed by the session delegate, if there was no error
+ if (![self error]) {
+ [DELEGATE downloadTask:self didFinishDownloadingToURL:_targetURL];
+ }
+
+ // and then finally, call the session delegate completion
+ [DELEGATE task:self didCompleteWithError:[self error]];
+}
+
+- (void)writeDataToStream:(NSData *)data {
+ // open the stream first
+ if ([_outputStream streamStatus] == NSStreamStatusNotOpen) {
+ [_outputStream open];
+ }
+
+ if ([data respondsToSelector:@selector(enumerateByteRangesUsingBlock:)]) {
+ [data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
+ [self->_outputStream write:bytes maxLength:byteRange.length];
+ }];
+
+ return;
+ }
+
+ // fall back to the less-efficient mechanism for older OSes
+ [_outputStream write:[data bytes] maxLength:[data length]];
+}
+
+#pragma mark NSURLConnectionDelegate
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
+ dispatch_async([self queue], ^{
+ [self writeDataToStream:data];
+ });
+}
+
+- (void)completeForError {
+ dispatch_async([self queue], ^{
+ [self->_outputStream close];
+ [self->_connection cancel];
+ if (![self error]) {
+ [self setError:[NSError errorWithDomain:@"FIRCLSURLSessionDownloadTaskError"
+ code:-1
+ userInfo:nil]];
+ }
+ [self complete];
+ });
+}
+
+#pragma mark NSStreamDelegate
+- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
+ switch (eventCode) {
+ case NSStreamEventHasSpaceAvailable:
+ break;
+ case NSStreamEventErrorOccurred:
+ [self completeForError];
+ break;
+ case NSStreamEventEndEncountered:
+ break;
+ default:
+ break;
+ }
+}
+
+@end
+
+#else
+
+INJECT_STRIP_SYMBOL(clsurlsessiondownloadtask)
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask_PrivateMethods.h
new file mode 100644
index 000000000..7e8ee9d9f
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionDownloadTask_PrivateMethods.h
@@ -0,0 +1,39 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import
+
+#import "FIRCLSURLSessionDataTask_PrivateMethods.h"
+
+@protocol FIRCLSURLSessionDownloadDelegate;
+
+@interface FIRCLSURLSessionDownloadTask ()
+
+@property(nonatomic, copy) void (^downloadCompletionHandler)
+ (NSURL *targetPath, NSURLResponse *response, NSError *error);
+
+@end
+
+@protocol FIRCLSURLSessionDownloadDelegate
+@required
+
+- (void)downloadTask:(FIRCLSURLSessionDownloadTask *)task didFinishDownloadingToURL:(NSURL *)url;
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h
new file mode 100644
index 000000000..92316464d
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.h
@@ -0,0 +1,38 @@
+// 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
+
+@protocol FIRCLSURLSessionTaskDelegate;
+
+@interface FIRCLSURLSessionTask : NSObject {
+ __unsafe_unretained id _delegate;
+
+ NSURLRequest* _originalRequest;
+ NSURLRequest* _currentRequest;
+ NSURLResponse* _response;
+ NSError* _error;
+ dispatch_queue_t _queue;
+ BOOL _invokesDelegate;
+}
+
+@property(nonatomic, readonly, copy) NSURLRequest* originalRequest;
+@property(nonatomic, readonly, copy) NSURLRequest* currentRequest;
+@property(nonatomic, readonly, copy) NSURLResponse* response;
+
+@property(nonatomic, readonly, copy) NSError* error;
+
+- (void)resume;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m
new file mode 100644
index 000000000..8eba2c185
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask.m
@@ -0,0 +1,95 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+#import "FIRCLSURLSession.h"
+
+#import "FIRCLSURLSessionTask.h"
+
+#import "FIRCLSURLSessionTask_PrivateMethods.h"
+#import "FIRCLSURLSession_PrivateMethods.h"
+
+@implementation FIRCLSURLSessionTask
+
++ (instancetype)task {
+#if __has_feature(objc_arc)
+ return [[self class] new];
+
+#else
+ return [[[self class] new] autorelease];
+#endif
+}
+
+@synthesize currentRequest = _currentRequest;
+@synthesize originalRequest = _originalRequest;
+@synthesize response = _response;
+@synthesize error = _error;
+@synthesize queue = _queue;
+@synthesize invokesDelegate = _invokesDelegate;
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) {
+ return self;
+ }
+
+ _queue = dispatch_queue_create("com.crashlytics.URLSessionTask", 0);
+
+ _invokesDelegate = YES;
+
+ return self;
+}
+
+#if !__has_feature(objc_arc)
+- (void)dealloc {
+ [_originalRequest release];
+ [_currentRequest release];
+ [_response release];
+ [_error release];
+
+#if !OS_OBJECT_USE_OBJC
+ dispatch_release(_queue);
+#endif
+
+ [super dealloc];
+}
+#endif
+
+- (void)start {
+#if DEBUG
+ assert(0 && "Must be implemented by FIRCLSURLSessionTask subclasses");
+#endif
+}
+
+- (void)cancel {
+#if DEBUG
+ assert(0 && "Must be implemented by FIRCLSURLSessionTask subclasses");
+#endif
+}
+
+- (void)resume {
+}
+
+- (void)cleanup {
+}
+
+@end
+
+#else
+
+INJECT_STRIP_SYMBOL(clsurlsessiontask)
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask_PrivateMethods.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask_PrivateMethods.h
new file mode 100644
index 000000000..784b3a3ce
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionTask_PrivateMethods.h
@@ -0,0 +1,55 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import
+
+@protocol FIRCLSURLSessionTaskDelegate;
+
+@interface FIRCLSURLSessionTask ()
+
++ (instancetype)task;
+
+@property(nonatomic, assign) id delegate;
+
+@property(nonatomic, copy) NSURLRequest *originalRequest;
+@property(nonatomic, copy) NSURLRequest *currentRequest;
+@property(nonatomic, copy) NSURLResponse *response;
+
+@property(nonatomic, readonly) dispatch_queue_t queue;
+@property(nonatomic, assign) BOOL invokesDelegate;
+
+- (void)cancel;
+
+@property(nonatomic, copy) NSError *error;
+
+- (void)cleanup;
+
+@end
+
+@protocol FIRCLSURLSessionTaskDelegate
+@required
+
+- (NSURLRequest *)task:(FIRCLSURLSessionTask *)task
+ willPerformHTTPRedirection:(NSHTTPURLResponse *)response
+ newRequest:(NSURLRequest *)request;
+
+- (void)task:(FIRCLSURLSessionTask *)task didCompleteWithError:(NSError *)error;
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.h
new file mode 100644
index 000000000..6c5ed8b4b
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.h
@@ -0,0 +1,25 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+
+#import "FIRCLSURLSessionDataTask.h"
+
+@interface FIRCLSURLSessionUploadTask : FIRCLSURLSessionDataTask
+
+@end
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.m
new file mode 100644
index 000000000..df3df8382
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSURLSession/Tasks/FIRCLSURLSessionUploadTask.m
@@ -0,0 +1,28 @@
+// 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 "FIRCLSURLSessionAvailability.h"
+
+#if FIRCLSURLSESSION_REQUIRED
+#import "FIRCLSURLSessionUploadTask.h"
+
+@implementation FIRCLSURLSessionUploadTask
+
+@end
+
+#else
+
+INJECT_STRIP_SYMBOL(clsurlsessionuploadtask)
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.h
new file mode 100644
index 000000000..6df153268
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.h
@@ -0,0 +1,42 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+extern NSString *const FIRCLSUserDefaultsPathComponent;
+
+@interface FIRCLSUserDefaults : NSObject
+
++ (instancetype)standardUserDefaults;
+
+- (id)objectForKey:(NSString *)key;
+- (NSString *)stringForKey:(NSString *)key;
+- (BOOL)boolForKey:(NSString *)key;
+- (NSInteger)integerForKey:(NSString *)key;
+
+- (void)setObject:(id)object forKey:(NSString *)key;
+- (void)setString:(NSString *)string forKey:(NSString *)key;
+- (void)setBool:(BOOL)boolean forKey:(NSString *)key;
+- (void)setInteger:(NSInteger)integer forKey:(NSString *)key;
+
+- (void)removeObjectForKey:(NSString *)key;
+- (void)removeAllObjects;
+
+- (NSDictionary *)dictionaryRepresentation;
+
+- (void)migrateFromNSUserDefaults:(NSArray *)keysToMigrate;
+- (id)objectForKeyByMigratingFromNSUserDefaults:(NSString *)keyToMigrateOrNil;
+- (void)synchronize;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.m
new file mode 100644
index 000000000..244dbe169
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults.m
@@ -0,0 +1,372 @@
+// 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 "FIRCLSUserDefaults.h"
+
+#import "FIRCLSApplication.h"
+#import "FIRCLSLogger.h"
+
+#define CLS_USER_DEFAULTS_SERIAL_DISPATCH_QUEUE "com.crashlytics.CLSUserDefaults.access"
+#define CLS_USER_DEFAULTS_SYNC_QUEUE "com.crashlytics.CLSUserDefaults.io"
+
+#define CLS_TARGET_CAN_WRITE_TO_DISK !TARGET_OS_TV
+
+// These values are required to stay the same between versions of the SDK so
+// that when end users upgrade, their crashlytics data is still saved on disk.
+#if !CLS_TARGET_CAN_WRITE_TO_DISK
+static NSString *const FIRCLSNSUserDefaultsDataDictionaryKey =
+ @"com.crashlytics.CLSUserDefaults.user-default-key.data-dictionary";
+#endif
+
+NSString *const FIRCLSUserDefaultsPathComponent = @"CLSUserDefaults";
+
+/**
+ * This class is an isolated re-implementation of NSUserDefaults which isolates our storage
+ * from that of our customers. This solves a number of issues we have seen in production, firstly
+ * that customers often delete or clear NSUserDefaults, unintentionally deleting our data.
+ * Further, we have seen thread safety issues in production with NSUserDefaults, as well as a number
+ * of bugs related to accessing NSUserDefaults before the device has been unlocked due to the
+ * NSFileProtection of NSUserDefaults.
+ */
+@interface FIRCLSUserDefaults ()
+@property(nonatomic, readwrite) BOOL synchronizeWroteToDisk;
+#if CLS_TARGET_CAN_WRITE_TO_DISK
+@property(nonatomic, copy, readonly) NSURL *directoryURL;
+@property(nonatomic, copy, readonly) NSURL *fileURL;
+#endif
+@property(nonatomic, copy, readonly)
+ NSDictionary *persistedDataDictionary; // May only be safely accessed on the DictionaryQueue
+@property(nonatomic, copy, readonly)
+ NSMutableDictionary *dataDictionary; // May only be safely accessed on the DictionaryQueue
+@property(nonatomic, readonly) dispatch_queue_t
+ serialDictionaryQueue; // The queue on which all access to the dataDictionary occurs.
+@property(nonatomic, readonly)
+ dispatch_queue_t synchronizationQueue; // The queue on which all disk access occurs.
+
+@end
+
+@implementation FIRCLSUserDefaults
+
+#pragma mark - singleton
+
++ (instancetype)standardUserDefaults {
+ static FIRCLSUserDefaults *standardUserDefaults = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ standardUserDefaults = [[super allocWithZone:NULL] init];
+ });
+
+ return standardUserDefaults;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ return self;
+}
+
+- (id)init {
+ if (self = [super init]) {
+ _serialDictionaryQueue =
+ dispatch_queue_create(CLS_USER_DEFAULTS_SERIAL_DISPATCH_QUEUE, DISPATCH_QUEUE_SERIAL);
+ _synchronizationQueue =
+ dispatch_queue_create(CLS_USER_DEFAULTS_SYNC_QUEUE, DISPATCH_QUEUE_SERIAL);
+
+ dispatch_sync(self.serialDictionaryQueue, ^{
+#if CLS_TARGET_CAN_WRITE_TO_DISK
+ self->_directoryURL = [self generateDirectoryURL];
+ self->_fileURL = [[self->_directoryURL
+ URLByAppendingPathComponent:FIRCLSUserDefaultsPathComponent
+ isDirectory:NO] URLByAppendingPathExtension:@"plist"];
+#endif
+ self->_persistedDataDictionary = [self loadDefaults];
+ if (!self->_persistedDataDictionary) {
+ self->_persistedDataDictionary = [NSDictionary dictionary];
+ }
+ self->_dataDictionary = [self->_persistedDataDictionary mutableCopy];
+ });
+ }
+ return self;
+}
+
+- (NSURL *)generateDirectoryURL {
+ NSURL *directoryBaseURL =
+ [[[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory
+ inDomains:NSUserDomainMask] lastObject];
+ NSString *hostAppBundleIdentifier = [self getEscapedAppBundleIdentifier];
+ return [self generateDirectoryURLForBaseURL:directoryBaseURL
+ hostAppBundleIdentifier:hostAppBundleIdentifier];
+}
+
+- (NSURL *)generateDirectoryURLForBaseURL:(NSURL *)directoryBaseURL
+ hostAppBundleIdentifier:(NSString *)hostAppBundleIdentifier {
+ NSURL *directoryURL = directoryBaseURL;
+ // On iOS NSApplicationSupportDirectory is contained in the app's bundle. On OSX, it is not (it is
+ // ~/Library/Application Support/). On OSX we create a directory
+ // ~/Library/Application Support//com.crashlytics/ for storing files.
+ // Mac App Store review process requires files to be written to
+ // ~/Library/Application Support//,
+ // so ~/Library/Application Support/com.crashlytics// cannot be used.
+#if !TARGET_OS_SIMULATOR && !TARGET_OS_EMBEDDED
+ if (hostAppBundleIdentifier) {
+ directoryURL = [directoryURL URLByAppendingPathComponent:hostAppBundleIdentifier];
+ }
+#endif
+ directoryURL = [directoryURL URLByAppendingPathComponent:@"com.crashlytics"];
+ return directoryURL;
+}
+
+- (NSString *)getEscapedAppBundleIdentifier {
+ return FIRCLSApplicationGetBundleIdentifier();
+}
+
+#pragma mark - fetch object
+
+- (id)objectForKey:(NSString *)key {
+ __block id result;
+
+ dispatch_sync(self.serialDictionaryQueue, ^{
+ result = [self->_dataDictionary objectForKey:key];
+ });
+
+ return result;
+}
+
+- (NSString *)stringForKey:(NSString *)key {
+ id result = [self objectForKey:key];
+
+ if (result != nil && [result isKindOfClass:[NSString class]]) {
+ return (NSString *)result;
+ } else {
+ return nil;
+ }
+}
+
+- (BOOL)boolForKey:(NSString *)key {
+ id result = [self objectForKey:key];
+ if (result != nil && [result isKindOfClass:[NSNumber class]]) {
+ return [(NSNumber *)result boolValue];
+ } else {
+ return NO;
+ }
+}
+
+// Defaults to 0
+- (NSInteger)integerForKey:(NSString *)key {
+ id result = [self objectForKey:key];
+ if (result && [result isKindOfClass:[NSNumber class]]) {
+ return [(NSNumber *)result integerValue];
+ } else {
+ return 0;
+ }
+}
+
+#pragma mark - set object
+
+- (void)setObject:(id)object forKey:(NSString *)key {
+ dispatch_sync(self.serialDictionaryQueue, ^{
+ [self->_dataDictionary setValue:object forKey:key];
+ });
+}
+
+- (void)setString:(NSString *)string forKey:(NSString *)key {
+ [self setObject:string forKey:key];
+}
+
+- (void)setBool:(BOOL)boolean forKey:(NSString *)key {
+ [self setObject:[NSNumber numberWithBool:boolean] forKey:key];
+}
+
+- (void)setInteger:(NSInteger)integer forKey:(NSString *)key {
+ [self setObject:[NSNumber numberWithInteger:integer] forKey:key];
+}
+
+#pragma mark - removing objects
+
+- (void)removeObjectForKey:(NSString *)key {
+ dispatch_sync(self.serialDictionaryQueue, ^{
+ [self->_dataDictionary removeObjectForKey:key];
+ });
+}
+
+- (void)removeAllObjects {
+ dispatch_sync(self.serialDictionaryQueue, ^{
+ [self->_dataDictionary removeAllObjects];
+ });
+}
+
+#pragma mark - dictionary representation
+
+- (NSDictionary *)dictionaryRepresentation {
+ __block NSDictionary *result;
+
+ dispatch_sync(self.serialDictionaryQueue, ^{
+ result = [self->_dataDictionary copy];
+ });
+
+ return result;
+}
+
+#pragma mark - synchronization
+
+- (void)synchronize {
+ __block BOOL dirty = NO;
+
+ // only write to the disk if the dictionaries have changed
+ dispatch_sync(self.serialDictionaryQueue, ^{
+ dirty = ![self->_persistedDataDictionary isEqualToDictionary:self->_dataDictionary];
+ });
+
+ _synchronizeWroteToDisk = dirty;
+ if (!dirty) {
+ return;
+ }
+
+ NSDictionary *state = [self dictionaryRepresentation];
+ dispatch_sync(self.synchronizationQueue, ^{
+#if CLS_TARGET_CAN_WRITE_TO_DISK
+ BOOL isDirectory = NO;
+ BOOL pathExists = [[NSFileManager defaultManager] fileExistsAtPath:[self->_directoryURL path]
+ isDirectory:&isDirectory];
+
+ if (!pathExists) {
+ NSError *error;
+ if (![[NSFileManager defaultManager] createDirectoryAtURL:self->_directoryURL
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error]) {
+ FIRCLSErrorLog(@"Failed to create directory with error: %@", error);
+ }
+ }
+
+ if (![state writeToURL:self->_fileURL atomically:YES]) {
+ FIRCLSErrorLog(@"Unable to open file for writing at path %@", [self->_fileURL path]);
+ } else {
+#if TARGET_OS_IOS
+ // We disable NSFileProtection on our file in order to allow us to access it even if the
+ // device is locked.
+ NSError *error;
+ if (![[NSFileManager defaultManager]
+ setAttributes:@{NSFileProtectionKey : NSFileProtectionNone}
+ ofItemAtPath:[self->_fileURL path]
+ error:&error]) {
+ FIRCLSErrorLog(@"Error setting NSFileProtection: %@", error);
+ }
+#endif
+ }
+#else
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ [defaults setObject:state forKey:FIRCLSNSUserDefaultsDataDictionaryKey];
+ [defaults synchronize];
+#endif
+ });
+
+ dispatch_sync(self.serialDictionaryQueue, ^{
+ self->_persistedDataDictionary = [self->_dataDictionary copy];
+ });
+}
+
+- (NSDictionary *)loadDefaults {
+ __block NSDictionary *state = nil;
+ dispatch_sync(self.synchronizationQueue, ^{
+#if CLS_TARGET_CAN_WRITE_TO_DISK
+ BOOL isDirectory = NO;
+ BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[self->_fileURL path]
+ isDirectory:&isDirectory];
+
+ if (fileExists && !isDirectory) {
+ state = [NSDictionary dictionaryWithContentsOfURL:self->_fileURL];
+ if (nil == state) {
+ FIRCLSErrorLog(@"Failed to read existing UserDefaults file");
+ }
+ } else if (!fileExists) {
+ // No file found. This is expected on first launch.
+ } else if (fileExists && isDirectory) {
+ FIRCLSErrorLog(@"Found directory where file expected. Removing conflicting directory");
+
+ NSError *error;
+ if (![[NSFileManager defaultManager] removeItemAtURL:self->_fileURL error:&error]) {
+ FIRCLSErrorLog(@"Error removing conflicting directory: %@", error);
+ }
+ }
+#else
+ state = [[NSUserDefaults standardUserDefaults] dictionaryForKey:FIRCLSNSUserDefaultsDataDictionaryKey];
+#endif
+ });
+ return state;
+}
+
+#pragma mark - migration
+
+// This method migrates all keys specified from NSUserDefaults to FIRCLSUserDefaults
+// To do so, we copy all known key-value pairs into FIRCLSUserDefaults, synchronize it, then
+// remove the keys from NSUserDefaults and synchronize it.
+- (void)migrateFromNSUserDefaults:(NSArray *)keysToMigrate {
+ BOOL didFindKeys = NO;
+
+ // First, copy all of the keysToMigrate which are stored NSUserDefaults
+ for (NSString *key in keysToMigrate) {
+ id oldValue = [[NSUserDefaults standardUserDefaults] objectForKey:(NSString *)key];
+ if (nil != oldValue) {
+ didFindKeys = YES;
+ [self setObject:oldValue forKey:key];
+ }
+ }
+
+ if (didFindKeys) {
+ // First synchronize FIRCLSUserDefaults such that all keysToMigrate in NSUserDefaults are stored
+ // in FIRCLSUserDefaults. At this point, data is duplicated.
+ [[FIRCLSUserDefaults standardUserDefaults] synchronize];
+
+ for (NSString *key in keysToMigrate) {
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:(NSString *)key];
+ }
+
+ // This should be our last interaction with NSUserDefaults. All data is migrated into
+ // FIRCLSUserDefaults
+ [[NSUserDefaults standardUserDefaults] synchronize];
+ }
+}
+
+// This method first queries FIRCLSUserDefaults to see if the key exist, and upon failure,
+// searches for the key in NSUserDefaults, and migrates it if found.
+- (id)objectForKeyByMigratingFromNSUserDefaults:(NSString *)keyToMigrateOrNil {
+ if (!keyToMigrateOrNil) {
+ return nil;
+ }
+
+ id clsUserDefaultsValue = [self objectForKey:keyToMigrateOrNil];
+ if (clsUserDefaultsValue != nil) {
+ return clsUserDefaultsValue; // if the value exists in FIRCLSUserDefaults, return it.
+ }
+
+ id oldNSUserDefaultsValue =
+ [[NSUserDefaults standardUserDefaults] objectForKey:keyToMigrateOrNil];
+ if (!oldNSUserDefaultsValue) {
+ return nil; // if the value also does not exist in NSUserDefaults, return nil.
+ }
+
+ // Otherwise, the key exists in NSUserDefaults. Migrate it to FIRCLSUserDefaults
+ // and then return the associated value.
+
+ // First store it in FIRCLSUserDefaults so in the event of a crash, data is not lost.
+ [self setObject:oldNSUserDefaultsValue forKey:keyToMigrateOrNil];
+ [[FIRCLSUserDefaults standardUserDefaults] synchronize];
+
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:keyToMigrateOrNil];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+
+ return oldNSUserDefaultsValue;
+}
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults_private.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults_private.h
new file mode 100644
index 000000000..775bca24a
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCLSUserDefaults/FIRCLSUserDefaults_private.h
@@ -0,0 +1,23 @@
+// 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
+#import "FIRCLSUserDefaults.h"
+
+@interface FIRCLSUserDefaults (Private)
+- (BOOL)synchronizeWroteToDisk;
+- (NSDictionary *)loadDefaults;
+- (NSURL *)generateDirectoryURLForBaseURL:(NSURL *)directoryBaseURL
+ hostAppBundleIdentifier:(NSString *)hostAppBundleIdentifer;
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCrashlytics.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCrashlytics.m
new file mode 100644
index 000000000..16e7a2f44
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRCrashlytics.m
@@ -0,0 +1,318 @@
+// 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.
+
+#include
+
+#if __has_include()
+#import
+#else
+#import "FBLPromises.h"
+#endif
+
+#import "FIRCLSApplicationIdentifierModel.h"
+#include "FIRCLSCrashedMarkerFile.h"
+#import "FIRCLSDataCollectionArbiter.h"
+#import "FIRCLSDefines.h"
+#include "FIRCLSException.h"
+#import "FIRCLSFileManager.h"
+#include "FIRCLSGlobals.h"
+#import "FIRCLSHost.h"
+#include "FIRCLSProfiling.h"
+#import "FIRCLSReport_Private.h"
+#import "FIRCLSSettings.h"
+#import "FIRCLSUserDefaults.h"
+#include "FIRCLSUserLogging.h"
+#include "FIRCLSUtility.h"
+
+#import "FIRCLSByteUtility.h"
+#import "FIRCLSFABHost.h"
+#import "FIRCLSLogger.h"
+
+#import "FIRCLSReportManager.h"
+
+#import
+#import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
+#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
+
+#import
+#import
+
+#if TARGET_OS_IPHONE
+#import
+#endif
+
+FIRCLSContext _firclsContext;
+dispatch_queue_t _firclsLoggingQueue;
+dispatch_queue_t _firclsBinaryImageQueue;
+dispatch_queue_t _firclsExceptionQueue;
+
+static atomic_bool _hasInitializedInstance;
+
+NSString *const FIRCLSGoogleTransportMappingID = @"1206";
+
+/// Empty protocol to register with FirebaseCore's component system.
+@protocol FIRCrashlyticsInstanceProvider
+@end
+
+@interface FIRCrashlytics ()
+
+@property(nonatomic) BOOL didPreviouslyCrash;
+@property(nonatomic, copy) NSString *googleAppID;
+@property(nonatomic) FIRCLSDataCollectionArbiter *dataArbiter;
+@property(nonatomic) FIRCLSFileManager *fileManager;
+@property(nonatomic) FIRCLSReportManager *reportManager;
+@property(nonatomic) GDTCORTransport *googleTransport;
+
+@end
+
+@implementation FIRCrashlytics
+
+#pragma mark - Singleton Support
+
+- (instancetype)initWithApp:(FIRApp *)app
+ appInfo:(NSDictionary *)appInfo
+ installations:(FIRInstallations *)installations
+ analytics:(id)analytics {
+ self = [super init];
+
+ if (self) {
+ bool expectedCalled = NO;
+ if (!atomic_compare_exchange_strong(&_hasInitializedInstance, &expectedCalled, YES)) {
+ FIRCLSErrorLog(@"Cannot instantiate more than one instance of Crashlytics.");
+ return nil;
+ }
+
+ FIRCLSProfileMark mark = FIRCLSProfilingStart();
+
+ NSLog(@"[Firebase/Crashlytics] Version %@", @CLS_SDK_DISPLAY_VERSION);
+
+ FIRCLSDeveloperLog("Crashlytics", @"Running on %@, %@ (%@)", FIRCLSHostModelInfo(),
+ FIRCLSHostOSDisplayVersion(), FIRCLSHostOSBuildVersion());
+
+ _googleTransport = [[GDTCORTransport alloc] initWithMappingID:FIRCLSGoogleTransportMappingID
+ transformers:nil
+ target:kGDTCORTargetCSH];
+
+ _fileManager = [[FIRCLSFileManager alloc] init];
+ _googleAppID = app.options.googleAppID;
+ _dataArbiter = [[FIRCLSDataCollectionArbiter alloc] initWithApp:app withAppInfo:appInfo];
+
+ FIRCLSApplicationIdentifierModel *appModel = [[FIRCLSApplicationIdentifierModel alloc] init];
+ FIRCLSSettings *settings = [[FIRCLSSettings alloc] initWithFileManager:_fileManager
+ appIDModel:appModel];
+
+ _reportManager = [[FIRCLSReportManager alloc] initWithFileManager:_fileManager
+ installations:installations
+ analytics:analytics
+ googleAppID:_googleAppID
+ dataArbiter:_dataArbiter
+ googleTransport:_googleTransport
+ appIDModel:appModel
+ settings:settings];
+
+ // Process did crash during previous execution
+ NSString *crashedMarkerFileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName];
+ NSString *crashedMarkerFileFullPath =
+ [[_fileManager rootPath] stringByAppendingPathComponent:crashedMarkerFileName];
+ _didPreviouslyCrash = [_fileManager fileExistsAtPath:crashedMarkerFileFullPath];
+
+ if (_didPreviouslyCrash) {
+ // Delete the crash file marker in the background ensure start up is as fast as possible
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ [self.fileManager removeItemAtPath:crashedMarkerFileFullPath];
+ });
+ }
+
+ [[[_reportManager startWithProfilingMark:mark] then:^id _Nullable(NSNumber *_Nullable value) {
+ if (![value boolValue]) {
+ FIRCLSErrorLog(@"Crash reporting could not be initialized");
+ }
+ return value;
+ }] catch:^void(NSError *error) {
+ FIRCLSErrorLog(@"Crash reporting failed to initialize with error: %@", error);
+ }];
+ }
+
+ return self;
+}
+
++ (void)load {
+ [FIRApp registerInternalLibrary:(Class)self
+ withName:@"firebase-crashlytics"
+ withVersion:@CLS_SDK_DISPLAY_VERSION];
+}
+
++ (NSArray *)componentsToRegister {
+ FIRDependency *analyticsDep =
+ [FIRDependency dependencyWithProtocol:@protocol(FIRAnalyticsInterop)];
+
+ FIRComponentCreationBlock creationBlock =
+ ^id _Nullable(FIRComponentContainer *container, BOOL *isCacheable) {
+ if (!container.app.isDefaultApp) {
+ FIRCLSErrorLog(@"Crashlytics must be used with the default Firebase app.");
+ return nil;
+ }
+
+ id analytics = FIR_COMPONENT(FIRAnalyticsInterop, container);
+
+ FIRInstallations *installations = [FIRInstallations installationsWithApp:container.app];
+
+ *isCacheable = YES;
+
+ return [[FIRCrashlytics alloc] initWithApp:container.app
+ appInfo:NSBundle.mainBundle.infoDictionary
+ installations:installations
+ analytics:analytics];
+ };
+
+ FIRComponent *component =
+ [FIRComponent componentWithProtocol:@protocol(FIRCrashlyticsInstanceProvider)
+ instantiationTiming:FIRInstantiationTimingEagerInDefaultApp
+ dependencies:@[ analyticsDep ]
+ creationBlock:creationBlock];
+ return @[ component ];
+}
+
++ (instancetype)crashlytics {
+ // The container will return the same instance since isCacheable is set
+
+ FIRApp *defaultApp = [FIRApp defaultApp]; // Missing configure will be logged here.
+
+ // Get the instance from the `FIRApp`'s container. This will create a new instance the
+ // first time it is called, and since `isCacheable` is set in the component creation
+ // block, it will return the existing instance on subsequent calls.
+ id instance =
+ FIR_COMPONENT(FIRCrashlyticsInstanceProvider, defaultApp.container);
+
+ // In the component creation block, we return an instance of `FIRCrashlytics`. Cast it and
+ // return it.
+ return (FIRCrashlytics *)instance;
+}
+
+- (void)setCrashlyticsCollectionEnabled:(BOOL)enabled {
+ [self.dataArbiter setCrashlyticsCollectionEnabled:enabled];
+}
+
+- (BOOL)isCrashlyticsCollectionEnabled {
+ return [self.dataArbiter isCrashlyticsCollectionEnabled];
+}
+
+#pragma mark - API: didCrashDuringPreviousExecution
+
+- (BOOL)didCrashDuringPreviousExecution {
+ return self.didPreviouslyCrash;
+}
+
+- (void)processDidCrashDuringPreviousExecution {
+ NSString *crashedMarkerFileName = [NSString stringWithUTF8String:FIRCLSCrashedMarkerFileName];
+ NSString *crashedMarkerFileFullPath =
+ [[self.fileManager rootPath] stringByAppendingPathComponent:crashedMarkerFileName];
+ self.didPreviouslyCrash = [self.fileManager fileExistsAtPath:crashedMarkerFileFullPath];
+
+ if (self.didPreviouslyCrash) {
+ // Delete the crash file marker in the background ensure start up is as fast as possible
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+ [self.fileManager removeItemAtPath:crashedMarkerFileFullPath];
+ });
+ }
+}
+
+#pragma mark - API: Logging
+- (void)log:(NSString *)msg {
+ FIRCLSLog(@"%@", msg);
+}
+
+- (void)logWithFormat:(NSString *)format, ... {
+ va_list args;
+ va_start(args, format);
+ [self logWithFormat:format arguments:args];
+ va_end(args);
+}
+
+- (void)logWithFormat:(NSString *)format arguments:(va_list)args {
+ [self log:[[NSString alloc] initWithFormat:format arguments:args]];
+}
+
+#pragma mark - API: Accessors
+
+- (void)checkForUnsentReportsWithCompletion:(void (^)(BOOL))completion {
+ [[self.reportManager checkForUnsentReports] then:^id _Nullable(NSNumber *_Nullable value) {
+ completion([value boolValue]);
+ return nil;
+ }];
+}
+
+- (void)sendUnsentReports {
+ [self.reportManager sendUnsentReports];
+}
+
+- (void)deleteUnsentReports {
+ [self.reportManager deleteUnsentReports];
+}
+
+#pragma mark - API: setUserID
+- (void)setUserID:(NSString *)userID {
+ FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSUserIdentifierKey, userID);
+}
+
+#pragma mark - API: setCustomValue
+
+- (void)setCustomValue:(id)value forKey:(NSString *)key {
+ FIRCLSUserLoggingRecordUserKeyValue(key, value);
+}
+
+#pragma mark - API: Development Platform
+// These two methods are depercated by our own API, so
+// its ok to implement them
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
++ (void)setDevelopmentPlatformName:(NSString *)name {
+ [[self crashlytics] setDevelopmentPlatformName:name];
+}
+
++ (void)setDevelopmentPlatformVersion:(NSString *)version {
+ [[self crashlytics] setDevelopmentPlatformVersion:version];
+}
+#pragma clang diagnostic pop
+
+- (NSString *)developmentPlatformName {
+ FIRCLSErrorLog(@"developmentPlatformName is write-only");
+ return nil;
+}
+
+- (void)setDevelopmentPlatformName:(NSString *)developmentPlatformName {
+ FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDevelopmentPlatformNameKey,
+ developmentPlatformName);
+}
+
+- (NSString *)developmentPlatformVersion {
+ FIRCLSErrorLog(@"developmentPlatformVersion is write-only");
+ return nil;
+}
+
+- (void)setDevelopmentPlatformVersion:(NSString *)developmentPlatformVersion {
+ FIRCLSUserLoggingRecordInternalKeyValue(FIRCLSDevelopmentPlatformVersionKey,
+ developmentPlatformVersion);
+}
+
+#pragma mark - API: Errors and Exceptions
+- (void)recordError:(NSError *)error {
+ FIRCLSUserLoggingRecordError(error, nil);
+}
+
+- (void)recordExceptionModel:(FIRExceptionModel *)exceptionModel {
+ FIRCLSExceptionRecordModel(exceptionModel);
+}
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRExceptionModel.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRExceptionModel.m
new file mode 100644
index 000000000..f0c4697a8
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRExceptionModel.m
@@ -0,0 +1,42 @@
+// Copyright 2020 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 "FIRExceptionModel.h"
+
+@interface FIRExceptionModel ()
+
+@property(nonatomic, copy) NSString *name;
+@property(nonatomic, copy) NSString *reason;
+
+@end
+
+@implementation FIRExceptionModel
+
+- (instancetype)initWithName:(NSString *)name reason:(NSString *)reason {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _name = [name copy];
+ _reason = [reason copy];
+
+ return self;
+}
+
++ (instancetype)exceptionModelWithName:(NSString *)name reason:(NSString *)reason {
+ return [[FIRExceptionModel alloc] initWithName:name reason:reason];
+}
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRStackFrame.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRStackFrame.m
new file mode 100644
index 000000000..304491465
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/FIRStackFrame.m
@@ -0,0 +1,94 @@
+// Copyright 2020 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 "FIRStackFrame_Private.h"
+
+@interface FIRStackFrame ()
+
+@property(nonatomic, copy, nullable) NSString *symbol;
+@property(nonatomic, copy, nullable) NSString *rawSymbol;
+@property(nonatomic, copy, nullable) NSString *library;
+@property(nonatomic, copy, nullable) NSString *fileName;
+@property(nonatomic, assign) uint32_t lineNumber;
+@property(nonatomic, assign) uint64_t offset;
+@property(nonatomic, assign) uint64_t address;
+
+@property(nonatomic, assign) BOOL isSymbolicated;
+
+@end
+
+@implementation FIRStackFrame
+
+#pragma mark - Public Methods
+
+- (instancetype)initWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line {
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+
+ _symbol = [symbol copy];
+ _fileName = [file copy];
+ _lineNumber = (uint32_t)line;
+
+ _isSymbolicated = true;
+
+ return self;
+}
+
++ (instancetype)stackFrameWithSymbol:(NSString *)symbol file:(NSString *)file line:(NSInteger)line {
+ return [[FIRStackFrame alloc] initWithSymbol:symbol file:file line:line];
+}
+
+#pragma mark - Internal Methods
+
++ (instancetype)stackFrame {
+ return [[self alloc] init];
+}
+
++ (instancetype)stackFrameWithAddress:(NSUInteger)address {
+ FIRStackFrame *frame = [self stackFrame];
+
+ [frame setAddress:address];
+
+ return frame;
+}
+
++ (instancetype)stackFrameWithSymbol:(NSString *)symbol {
+ FIRStackFrame *frame = [self stackFrame];
+
+ frame.symbol = symbol;
+ frame.rawSymbol = symbol;
+
+ return frame;
+}
+
+#pragma mark - Overrides
+
+- (NSString *)description {
+ if (self.isSymbolicated) {
+ return [NSString
+ stringWithFormat:@"{%@ - %@:%u}", [self fileName], [self symbol], [self lineNumber]];
+ }
+
+ if (self.fileName) {
+ return [NSString stringWithFormat:@"{[0x%llx] %@ - %@:%u}", [self address], [self fileName],
+ [self symbol], [self lineNumber]];
+ }
+
+ return [NSString
+ stringWithFormat:@"{[0x%llx + %u] %@}", [self address], [self lineNumber], [self symbol]];
+}
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.h
new file mode 100644
index 000000000..61dac77c3
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.h
@@ -0,0 +1,73 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include
+#include
+
+#ifdef __OBJC__
+#import
+@class FIRStackFrame;
+@class FIRExceptionModel;
+#endif
+
+#define CLS_EXCEPTION_STRING_LENGTH_MAX (1024 * 16)
+
+typedef enum {
+ FIRCLSExceptionTypeUnknown = 0,
+ FIRCLSExceptionTypeObjectiveC = 1,
+ FIRCLSExceptionTypeCpp = 2,
+ // 3 was FIRCLSExceptionTypeJavascript
+ // Keeping these numbers the same just to be safe
+ FIRCLSExceptionTypeCustom = 4
+} FIRCLSExceptionType;
+
+typedef struct {
+ const char* path;
+
+ void (*originalTerminateHandler)(void);
+
+#if !TARGET_OS_IPHONE
+ void* originalNSApplicationReportException;
+#endif
+
+ uint32_t maxCustomExceptions;
+} FIRCLSExceptionReadOnlyContext;
+
+typedef struct {
+ uint32_t customExceptionCount;
+} FIRCLSExceptionWritableContext;
+
+__BEGIN_DECLS
+
+void FIRCLSExceptionInitialize(FIRCLSExceptionReadOnlyContext* roContext,
+ FIRCLSExceptionWritableContext* rwContext,
+ void* delegate);
+void FIRCLSExceptionCheckHandlers(void* delegate);
+
+void FIRCLSExceptionRaiseTestObjCException(void) __attribute((noreturn));
+void FIRCLSExceptionRaiseTestCppException(void) __attribute((noreturn));
+
+#ifdef __OBJC__
+void FIRCLSExceptionRecordModel(FIRExceptionModel* exceptionModel);
+void FIRCLSExceptionRecordNSException(NSException* exception);
+void FIRCLSExceptionRecord(FIRCLSExceptionType type,
+ const char* name,
+ const char* reason,
+ NSArray* frames,
+ BOOL attemptDelivery);
+#endif
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm
new file mode 100644
index 000000000..6f2099045
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSException.mm
@@ -0,0 +1,410 @@
+// 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
+
+#include "FIRCLSException.h"
+
+#import "FIRExceptionModel_Private.h"
+#import "FIRStackFrame_Private.h"
+
+#include "FIRCLSApplication.h"
+#include "FIRCLSFile.h"
+#include "FIRCLSGlobals.h"
+#include "FIRCLSHandler.h"
+#import "FIRCLSLogger.h"
+#include "FIRCLSProcess.h"
+#import "FIRCLSUserLogging.h"
+#import "FIRCLSUtility.h"
+
+#include "FIRCLSDemangleOperation.h"
+#import "FIRCLSReportManager_Private.h"
+
+// C++/Objective-C exception handling
+#include
+#include
+#include
+#include
+
+#if !TARGET_OS_IPHONE
+#import
+#import
+#endif
+
+#pragma mark Prototypes
+static void FIRCLSTerminateHandler(void);
+#if !TARGET_OS_IPHONE
+void FIRCLSNSApplicationReportException(id self, SEL cmd, NSException *exception);
+
+typedef void (*NSApplicationReportExceptionFunction)(id, SEL, NSException *);
+
+static BOOL FIRCLSIsNSApplicationCrashOnExceptionsEnabled(void);
+static NSApplicationReportExceptionFunction FIRCLSOriginalNSExceptionReportExceptionFunction(void);
+static Method FIRCLSGetNSApplicationReportExceptionMethod(void);
+
+#endif
+
+#pragma mark - API
+void FIRCLSExceptionInitialize(FIRCLSExceptionReadOnlyContext *roContext,
+ FIRCLSExceptionWritableContext *rwContext,
+ void *delegate) {
+ if (!FIRCLSUnlinkIfExists(roContext->path)) {
+ FIRCLSSDKLog("Unable to reset the exception file %s\n", strerror(errno));
+ }
+
+ roContext->originalTerminateHandler = std::set_terminate(FIRCLSTerminateHandler);
+
+#if !TARGET_OS_IPHONE
+ // If FIRCLSApplicationSharedInstance is null, we don't need this
+ if (FIRCLSIsNSApplicationCrashOnExceptionsEnabled() && FIRCLSApplicationSharedInstance()) {
+ Method m = FIRCLSGetNSApplicationReportExceptionMethod();
+
+ roContext->originalNSApplicationReportException =
+ (void *)method_setImplementation(m, (IMP)FIRCLSNSApplicationReportException);
+ }
+#endif
+
+ rwContext->customExceptionCount = 0;
+}
+
+void FIRCLSExceptionRecordModel(FIRExceptionModel *exceptionModel) {
+ const char *name = [[exceptionModel.name copy] UTF8String];
+ const char *reason = [[exceptionModel.reason copy] UTF8String];
+
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCustom, name, reason, [exceptionModel.stackTrace copy],
+ NO);
+}
+
+void FIRCLSExceptionRecordNSException(NSException *exception) {
+ FIRCLSSDKLog("Recording an NSException\n");
+
+ NSArray *returnAddresses = [exception callStackReturnAddresses];
+
+ NSString *name = [exception name];
+ NSString *reason = [exception reason];
+
+ // It's tempting to try to make use of callStackSymbols here. But, the output
+ // of that function is not intended to be machine-readible. We could parse it,
+ // but that isn't really worthwhile, considering that address-based symbolication
+ // needs to work anyways.
+
+ // package our frames up into the appropriate format
+ NSMutableArray *frames = [NSMutableArray new];
+
+ for (NSNumber *address in returnAddresses) {
+ [frames addObject:[FIRStackFrame stackFrameWithAddress:[address unsignedIntegerValue]]];
+ }
+
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeObjectiveC, [name UTF8String], [reason UTF8String],
+ frames, YES);
+}
+
+static void FIRCLSExceptionRecordFrame(FIRCLSFile *file, FIRStackFrame *frame) {
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSFileWriteHashEntryUint64(file, "pc", [frame address]);
+
+ NSString *string = [frame symbol];
+ if (string) {
+ FIRCLSFileWriteHashEntryHexEncodedString(file, "symbol", [string UTF8String]);
+ }
+
+ FIRCLSFileWriteHashEntryUint64(file, "offset", [frame offset]);
+
+ string = [frame library];
+ if (string) {
+ FIRCLSFileWriteHashEntryHexEncodedString(file, "library", [string UTF8String]);
+ }
+
+ string = [frame fileName];
+ if (string) {
+ FIRCLSFileWriteHashEntryHexEncodedString(file, "file", [string UTF8String]);
+ }
+
+ FIRCLSFileWriteHashEntryUint64(file, "line", [frame lineNumber]);
+
+ FIRCLSFileWriteHashEnd(file);
+}
+
+static bool FIRCLSExceptionIsNative(FIRCLSExceptionType type) {
+ return type == FIRCLSExceptionTypeObjectiveC || type == FIRCLSExceptionTypeCpp;
+}
+
+static const char *FIRCLSExceptionNameForType(FIRCLSExceptionType type) {
+ switch (type) {
+ case FIRCLSExceptionTypeObjectiveC:
+ return "objective-c";
+ case FIRCLSExceptionTypeCpp:
+ return "c++";
+ case FIRCLSExceptionTypeCustom:
+ return "custom";
+ default:
+ break;
+ }
+
+ return "unknown";
+}
+
+void FIRCLSExceptionWrite(FIRCLSFile *file,
+ FIRCLSExceptionType type,
+ const char *name,
+ const char *reason,
+ NSArray *frames) {
+ FIRCLSFileWriteSectionStart(file, "exception");
+
+ FIRCLSFileWriteHashStart(file);
+
+ FIRCLSFileWriteHashEntryString(file, "type", FIRCLSExceptionNameForType(type));
+ FIRCLSFileWriteHashEntryHexEncodedString(file, "name", name);
+ FIRCLSFileWriteHashEntryHexEncodedString(file, "reason", reason);
+ FIRCLSFileWriteHashEntryUint64(file, "time", time(NULL));
+
+ if ([frames count]) {
+ FIRCLSFileWriteHashKey(file, "frames");
+ FIRCLSFileWriteArrayStart(file);
+
+ for (FIRStackFrame *frame in frames) {
+ FIRCLSExceptionRecordFrame(file, frame);
+ }
+
+ FIRCLSFileWriteArrayEnd(file);
+ }
+
+ FIRCLSFileWriteHashEnd(file);
+
+ FIRCLSFileWriteSectionEnd(file);
+}
+
+void FIRCLSExceptionRecord(FIRCLSExceptionType type,
+ const char *name,
+ const char *reason,
+ NSArray *frames,
+ BOOL attemptDelivery) {
+ if (!FIRCLSContextIsInitialized()) {
+ return;
+ }
+
+ bool native = FIRCLSExceptionIsNative(type);
+
+ FIRCLSSDKLog("Recording an exception structure (%d, %d)\n", attemptDelivery, native);
+
+ // exceptions can happen on multiple threads at the same time
+ if (native) {
+ dispatch_sync(_firclsExceptionQueue, ^{
+ const char *path = _firclsContext.readonly->exception.path;
+ FIRCLSFile file;
+
+ if (!FIRCLSFileInitWithPath(&file, path, false)) {
+ FIRCLSSDKLog("Unable to open exception file\n");
+ return;
+ }
+
+ FIRCLSExceptionWrite(&file, type, name, reason, frames);
+
+ // We only want to do this work if we have the expectation that we'll actually crash
+ FIRCLSHandler(&file, mach_thread_self(), NULL);
+
+ FIRCLSFileClose(&file);
+
+ // disallow immediate delivery for non-native exceptions
+ if (attemptDelivery) {
+ FIRCLSHandlerAttemptImmediateDelivery();
+ }
+ });
+ } else {
+ FIRCLSUserLoggingWriteAndCheckABFiles(
+ &_firclsContext.readonly->logging.customExceptionStorage,
+ &_firclsContext.writable->logging.activeCustomExceptionPath, ^(FIRCLSFile *file) {
+ FIRCLSExceptionWrite(file, type, name, reason, frames);
+ });
+ }
+
+ FIRCLSSDKLog("Finished recording an exception structure\n");
+}
+
+// Ignore this message here, because we know that this call will not leak.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winvalid-noreturn"
+void FIRCLSExceptionRaiseTestObjCException(void) {
+ [NSException raise:@"CrashlyticsTestException"
+ format:@"This is an Objective-C exception using for testing."];
+}
+
+void FIRCLSExceptionRaiseTestCppException(void) {
+ throw "Crashlytics C++ Test Exception";
+}
+#pragma clang diagnostic pop
+
+static const char *FIRCLSExceptionDemangle(const char *symbol) {
+ return [[FIRCLSDemangleOperation demangleCppSymbol:symbol] UTF8String];
+}
+
+static void FIRCLSCatchAndRecordActiveException(std::type_info *typeInfo) {
+ if (!FIRCLSIsValidPointer(typeInfo)) {
+ FIRCLSSDKLog("Error: invalid parameter\n");
+ return;
+ }
+
+ const char *name = typeInfo->name();
+ FIRCLSSDKLog("Recording exception of type '%s'\n", name);
+
+ // This is a funny technique to get the exception object. The inner @try
+ // has the ability to capture NSException-derived objects. It seems that
+ // c++ trys can do that in some cases, but I was warned by the WWDC labs
+ // that there are cases where that will not work (like for NSException subclasses).
+ try {
+ @try {
+ // This could potentially cause a call to std::terminate() if there is actually no active
+ // exception.
+ throw;
+ } @catch (NSException *exception) {
+#if TARGET_OS_IPHONE
+ FIRCLSExceptionRecordNSException(exception);
+#else
+ // There's no need to record this here, because we're going to get
+ // the value forward to us by AppKit
+ FIRCLSSDKLog("Skipping ObjC exception at this point\n");
+#endif
+ }
+ } catch (const char *exc) {
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, "const char *", exc, nil, YES);
+ } catch (const std::string &exc) {
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, "std::string", exc.c_str(), nil, YES);
+ } catch (const std::exception &exc) {
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, FIRCLSExceptionDemangle(name), exc.what(), nil,
+ YES);
+ } catch (const std::exception *exc) {
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, FIRCLSExceptionDemangle(name), exc->what(), nil,
+ YES);
+ } catch (const std::bad_alloc &exc) {
+ // it is especially important to avoid demangling in this case, because the expetation at this
+ // point is that all allocations could fail
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, "std::bad_alloc", exc.what(), nil, YES);
+ } catch (...) {
+ FIRCLSExceptionRecord(FIRCLSExceptionTypeCpp, FIRCLSExceptionDemangle(name), "", nil, YES);
+ }
+}
+
+#pragma mark - Handlers
+static void FIRCLSTerminateHandler(void) {
+ FIRCLSSDKLog("C++ terminate handler invoked\n");
+
+ void (*handler)(void) = _firclsContext.readonly->exception.originalTerminateHandler;
+ if (handler == FIRCLSTerminateHandler) {
+ FIRCLSSDKLog("Error: original handler was set recursively\n");
+ handler = NULL;
+ }
+
+ // Restore pre-existing handler, if any. Do this early, so that
+ // if std::terminate is called while we are executing here, we do not recurse.
+ if (handler) {
+ FIRCLSSDKLog("restoring pre-existing handler\n");
+
+ // To prevent infinite recursion in this function, check that we aren't resetting the terminate
+ // handler to the same function again, which would be this function in the event that we can't
+ // actually change the handler during a terminate.
+ if (std::set_terminate(handler) == handler) {
+ FIRCLSSDKLog("handler has already been restored, aborting\n");
+ abort();
+ }
+ }
+
+ // we can use typeInfo to record the type of the exception,
+ // but we must use a catch to get the value
+ std::type_info *typeInfo = __cxxabiv1::__cxa_current_exception_type();
+ if (typeInfo) {
+ FIRCLSCatchAndRecordActiveException(typeInfo);
+ } else {
+ FIRCLSSDKLog("no active exception\n");
+ }
+
+ // only do this if there was a pre-existing handler
+ if (handler) {
+ FIRCLSSDKLog("invoking pre-existing handler\n");
+ handler();
+ }
+
+ FIRCLSSDKLog("aborting\n");
+ abort();
+}
+
+void FIRCLSExceptionCheckHandlers(void *delegate) {
+#if !TARGET_OS_IPHONE
+ // Check this on OS X all the time, even if the debugger is attached. This is a common
+ // source of errors, so we want to be extra verbose in this case.
+ if (FIRCLSApplicationSharedInstance()) {
+ if (!FIRCLSIsNSApplicationCrashOnExceptionsEnabled()) {
+ FIRCLSWarningLog(@"Warning: NSApplicationCrashOnExceptions is not set. This will "
+ @"result in poor top-level uncaught exception reporting.");
+ }
+ }
+#endif
+
+ if (_firclsContext.readonly->debuggerAttached) {
+ return;
+ }
+
+ void *ptr = NULL;
+
+ ptr = (void *)std::get_terminate();
+ if (ptr != FIRCLSTerminateHandler) {
+ FIRCLSLookupFunctionPointer(ptr, ^(const char *name, const char *lib) {
+ FIRCLSWarningLog(@"Warning: std::get_terminate is '%s' in '%s'", name, lib);
+ });
+ }
+
+#if TARGET_OS_IPHONE
+ ptr = (void *)NSGetUncaughtExceptionHandler();
+ if (ptr) {
+ FIRCLSLookupFunctionPointer(ptr, ^(const char *name, const char *lib) {
+ FIRCLSWarningLog(@"Warning: NSUncaughtExceptionHandler is '%s' in '%s'", name, lib);
+ });
+ }
+#else
+ if (FIRCLSApplicationSharedInstance() && FIRCLSIsNSApplicationCrashOnExceptionsEnabled()) {
+ // In this case, we *might* be able to intercept exceptions. But, verify we've still
+ // swizzled the method.
+ Method m = FIRCLSGetNSApplicationReportExceptionMethod();
+
+ if (method_getImplementation(m) != (IMP)FIRCLSNSApplicationReportException) {
+ FIRCLSWarningLog(
+ @"Warning: top-level NSApplication-reported exceptions cannot be intercepted");
+ }
+ }
+#endif
+}
+
+#pragma mark - AppKit Handling
+#if !TARGET_OS_IPHONE
+static BOOL FIRCLSIsNSApplicationCrashOnExceptionsEnabled(void) {
+ return [[NSUserDefaults standardUserDefaults] boolForKey:@"NSApplicationCrashOnExceptions"];
+}
+
+static Method FIRCLSGetNSApplicationReportExceptionMethod(void) {
+ return class_getInstanceMethod(NSClassFromString(@"NSApplication"), @selector(reportException:));
+}
+
+static NSApplicationReportExceptionFunction FIRCLSOriginalNSExceptionReportExceptionFunction(void) {
+ return (NSApplicationReportExceptionFunction)
+ _firclsContext.readonly->exception.originalNSApplicationReportException;
+}
+
+void FIRCLSNSApplicationReportException(id self, SEL cmd, NSException *exception) {
+ FIRCLSExceptionRecordNSException(exception);
+
+ // Call the original implementation
+ FIRCLSOriginalNSExceptionReportExceptionFunction()(self, cmd, exception);
+}
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.h
new file mode 100644
index 000000000..7ce03e416
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.h
@@ -0,0 +1,26 @@
+// 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.
+
+#pragma once
+
+#include
+
+#include "FIRCLSFile.h"
+
+__BEGIN_DECLS
+
+void FIRCLSHandler(FIRCLSFile* file, thread_t crashedThread, void* uapVoid);
+void FIRCLSHandlerAttemptImmediateDelivery(void);
+
+__END_DECLS
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m
new file mode 100644
index 000000000..918e4265e
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSHandler.m
@@ -0,0 +1,61 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "FIRCLSHandler.h"
+
+#include "FIRCLSCrashedMarkerFile.h"
+#include "FIRCLSGlobals.h"
+#include "FIRCLSHost.h"
+#include "FIRCLSProcess.h"
+#include "FIRCLSUtility.h"
+
+#import "FIRCLSReportManager_Private.h"
+
+void FIRCLSHandler(FIRCLSFile* file, thread_t crashedThread, void* uapVoid) {
+ FIRCLSProcess process;
+
+ FIRCLSProcessInit(&process, crashedThread, uapVoid);
+
+ FIRCLSProcessSuspendAllOtherThreads(&process);
+
+ FIRCLSProcessRecordAllThreads(&process, file);
+
+ FIRCLSProcessRecordRuntimeInfo(&process, file);
+ // Get dispatch queue and thread names. Note that getting the thread names
+ // can hang, so let's do that last
+ FIRCLSProcessRecordDispatchQueueNames(&process, file);
+ FIRCLSProcessRecordThreadNames(&process, file);
+
+ // this stuff isn't super important, but we can try
+ FIRCLSProcessRecordStats(&process, file);
+ FIRCLSHostWriteDiskUsage(file);
+
+ // This is the first common point where various crash handlers call into
+ // Store a crash file marker to indicate that a crash has occured
+ FIRCLSCreateCrashedMarkerFile();
+
+ FIRCLSProcessResumeAllOtherThreads(&process);
+
+ // clean up after ourselves
+ FIRCLSProcessDestroy(&process);
+}
+
+void FIRCLSHandlerAttemptImmediateDelivery(void) {
+ // now, attempt to relay the event to the delegate
+ FIRCLSReportManager* manager = (__bridge id)_firclsContext.readonly->delegate;
+
+ if ([manager respondsToSelector:@selector(potentiallySubmittableCrashOccurred)]) {
+ [manager potentiallySubmittableCrashOccurred];
+ }
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c
new file mode 100644
index 000000000..3ec4e9790
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c
@@ -0,0 +1,533 @@
+// 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.
+
+#include "FIRCLSDefines.h"
+#include "FIRCLSFeatures.h"
+
+#if CLS_MACH_EXCEPTION_SUPPORTED
+
+#include "FIRCLSGlobals.h"
+#include "FIRCLSHandler.h"
+#include "FIRCLSMachException.h"
+#include "FIRCLSProcess.h"
+#include "FIRCLSSignal.h"
+#include "FIRCLSUtility.h"
+
+#include
+#include
+#include
+#include
+
+#pragma mark Prototypes
+static exception_mask_t FIRCLSMachExceptionMask(void);
+static void* FIRCLSMachExceptionServer(void* argument);
+static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context);
+static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message);
+static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message);
+static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message,
+ kern_return_t result);
+static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context,
+ exception_mask_t ignoreMask);
+static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts,
+ exception_mask_t mask);
+static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message);
+
+#pragma mark - Initialization
+void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context, exception_mask_t ignoreMask) {
+ if (!FIRCLSUnlinkIfExists(context->path)) {
+ FIRCLSSDKLog("Unable to reset the mach exception file %s\n", strerror(errno));
+ }
+
+ if (!FIRCLSMachExceptionRegister(context, ignoreMask)) {
+ FIRCLSSDKLog("Unable to register mach exception handler\n");
+ return;
+ }
+
+ if (!FIRCLSMachExceptionThreadStart(context)) {
+ FIRCLSSDKLog("Unable to start thread\n");
+ FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask);
+ }
+}
+
+void FIRCLSMachExceptionCheckHandlers(void) {
+ if (_firclsContext.readonly->debuggerAttached) {
+ return;
+ }
+
+ // It isn't really critical that this be done, as its extremely uncommon to run into
+ // preexisting handlers.
+ // Can use task_get_exception_ports for this.
+}
+
+static exception_mask_t FIRCLSMachExceptionMask(void) {
+ exception_mask_t mask;
+
+ // EXC_BAD_ACCESS
+ // EXC_BAD_INSTRUCTION
+ // EXC_ARITHMETIC
+ // EXC_EMULATION - non-failure
+ // EXC_SOFTWARE - non-failure
+ // EXC_BREAKPOINT - trap instructions, from the debugger and code. Needs special treatment.
+ // EXC_SYSCALL - non-failure
+ // EXC_MACH_SYSCALL - non-failure
+ // EXC_RPC_ALERT - non-failure
+ // EXC_CRASH - see below
+ // EXC_RESOURCE - non-failure, happens when a process exceeds a resource limit
+ // EXC_GUARD - see below
+ //
+ // EXC_CRASH is a special kind of exception. It is handled by launchd, and treated special by
+ // the kernel. Seems that we cannot safely catch it - our handler will never be called. This
+ // is a confirmed kernel bug. Lacking access to EXC_CRASH means we must use signal handlers to
+ // cover all types of crashes.
+ // EXC_GUARD is relatively new, and isn't available on all OS versions. You have to be careful,
+ // becuase you cannot succesfully register hanlders if there are any unrecognized masks. We've
+ // dropped support for old OS versions that didn't have EXC_GUARD (iOS 5 and below, macOS 10.6 and
+ // below) so we always add it now
+
+ mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC |
+ EXC_MASK_BREAKPOINT | EXC_MASK_GUARD;
+
+ return mask;
+}
+
+static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context) {
+ pthread_attr_t attr;
+
+ if (pthread_attr_init(&attr) != 0) {
+ FIRCLSSDKLog("pthread_attr_init %s\n", strerror(errno));
+ return false;
+ }
+
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {
+ FIRCLSSDKLog("pthread_attr_setdetachstate %s\n", strerror(errno));
+ return false;
+ }
+
+ // Use to pre-allocate a stack for this thread
+ // The stack must be page-aligned
+ if (pthread_attr_setstack(&attr, _firclsContext.readonly->machStack,
+ CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE) != 0) {
+ FIRCLSSDKLog("pthread_attr_setstack %s\n", strerror(errno));
+ return false;
+ }
+
+ if (pthread_create(&context->thread, &attr, FIRCLSMachExceptionServer, context) != 0) {
+ FIRCLSSDKLog("pthread_create %s\n", strerror(errno));
+ return false;
+ }
+
+ pthread_attr_destroy(&attr);
+
+ return true;
+}
+
+exception_mask_t FIRCLSMachExceptionMaskForSignal(int signal) {
+ switch (signal) {
+ case SIGTRAP:
+ return EXC_MASK_BREAKPOINT;
+ case SIGSEGV:
+ return EXC_MASK_BAD_ACCESS;
+ case SIGBUS:
+ return EXC_MASK_BAD_ACCESS;
+ case SIGILL:
+ return EXC_MASK_BAD_INSTRUCTION;
+ case SIGABRT:
+ return EXC_MASK_CRASH;
+ case SIGSYS:
+ return EXC_MASK_CRASH;
+ case SIGFPE:
+ return EXC_MASK_ARITHMETIC;
+ }
+
+ return 0;
+}
+
+#pragma mark - Message Handling
+static void* FIRCLSMachExceptionServer(void* argument) {
+ FIRCLSMachExceptionReadContext* context = argument;
+
+ pthread_setname_np("com.google.firebase.crashlytics.MachExceptionServer");
+
+ while (1) {
+ MachExceptionMessage message;
+
+ // read the exception message
+ if (!FIRCLSMachExceptionReadMessage(context, &message)) {
+ break;
+ }
+
+ // handle it, and possibly forward
+ kern_return_t result = FIRCLSMachExceptionDispatchMessage(context, &message);
+
+ // and now, reply
+ if (!FIRCLSMachExceptionReply(context, &message, result)) {
+ break;
+ }
+ }
+
+ FIRCLSSDKLog("Mach exception server thread exiting\n");
+
+ return NULL;
+}
+
+static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message) {
+ mach_msg_return_t r;
+
+ memset(message, 0, sizeof(MachExceptionMessage));
+
+ r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionMessage),
+ context->port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (r != MACH_MSG_SUCCESS) {
+ FIRCLSSDKLog("Error receving mach_msg (%d)\n", r);
+ return false;
+ }
+
+ FIRCLSSDKLog("Accepted mach exception message\n");
+
+ return true;
+}
+
+static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message) {
+ FIRCLSSDKLog("Mach exception: 0x%x, count: %d, code: 0x%llx 0x%llx\n", message->exception,
+ message->codeCnt, message->codeCnt > 0 ? message->code[0] : -1,
+ message->codeCnt > 1 ? message->code[1] : -1);
+
+ // This will happen if a child process raises an exception, as the exception ports are
+ // inherited.
+ if (message->task.name != mach_task_self()) {
+ FIRCLSSDKLog("Mach exception task mis-match, returning failure\n");
+ return KERN_FAILURE;
+ }
+
+ FIRCLSSDKLog("Unregistering handler\n");
+ if (!FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask)) {
+ FIRCLSSDKLog("Failed to unregister\n");
+ return KERN_FAILURE;
+ }
+
+ FIRCLSSDKLog("Restoring original signal handlers\n");
+ if (!FIRCLSSignalSafeInstallPreexistingHandlers(&_firclsContext.readonly->signal)) {
+ FIRCLSSDKLog("Failed to restore signal handlers\n");
+ return KERN_FAILURE;
+ }
+
+ FIRCLSSDKLog("Recording mach exception\n");
+ if (!FIRCLSMachExceptionRecord(context, message)) {
+ FIRCLSSDKLog("Failed to record mach exception\n");
+ return KERN_FAILURE;
+ }
+
+ return KERN_SUCCESS;
+}
+
+static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message,
+ kern_return_t result) {
+ MachExceptionReply reply;
+ mach_msg_return_t r;
+
+ // prepare the reply
+ reply.head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(message->head.msgh_bits), 0);
+ reply.head.msgh_remote_port = message->head.msgh_remote_port;
+ reply.head.msgh_size = (mach_msg_size_t)sizeof(MachExceptionReply);
+ reply.head.msgh_local_port = MACH_PORT_NULL;
+ reply.head.msgh_id = message->head.msgh_id + 100;
+
+ reply.NDR = NDR_record;
+
+ reply.retCode = result;
+
+ FIRCLSSDKLog("Sending exception reply\n");
+
+ // send it
+ r = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (r != MACH_MSG_SUCCESS) {
+ FIRCLSSDKLog("mach_msg reply failed (%d)\n", r);
+ return false;
+ }
+
+ FIRCLSSDKLog("Exception reply delivered\n");
+
+ return true;
+}
+
+#pragma mark - Registration
+static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context,
+ exception_mask_t ignoreMask) {
+ mach_port_t task = mach_task_self();
+
+ kern_return_t kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &context->port);
+ if (kr != KERN_SUCCESS) {
+ FIRCLSSDKLog("Error: mach_port_allocate failed %d\n", kr);
+ return false;
+ }
+
+ kr = mach_port_insert_right(task, context->port, context->port, MACH_MSG_TYPE_MAKE_SEND);
+ if (kr != KERN_SUCCESS) {
+ FIRCLSSDKLog("Error: mach_port_insert_right failed %d\n", kr);
+ mach_port_deallocate(task, context->port);
+ return false;
+ }
+
+ // Get the desired mask, which covers all the mach exceptions we are capable of handling,
+ // but clear out any that are in our ignore list. We do this by ANDing with the bitwise
+ // negation. Because we are only clearing bits, there's no way to set an incorrect mask
+ // using ignoreMask.
+ context->mask = FIRCLSMachExceptionMask() & ~ignoreMask;
+
+ // ORing with MACH_EXCEPTION_CODES will produce 64-bit exception data
+ kr = task_swap_exception_ports(task, context->mask, context->port,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
+ context->originalPorts.masks, &context->originalPorts.count,
+ context->originalPorts.ports, context->originalPorts.behaviors,
+ context->originalPorts.flavors);
+ if (kr != KERN_SUCCESS) {
+ FIRCLSSDKLog("Error: task_swap_exception_ports %d\n", kr);
+ return false;
+ }
+
+ for (int i = 0; i < context->originalPorts.count; ++i) {
+ FIRCLSSDKLog("original 0x%x 0x%x 0x%x 0x%x\n", context->originalPorts.ports[i],
+ context->originalPorts.masks[i], context->originalPorts.behaviors[i],
+ context->originalPorts.flavors[i]);
+ }
+
+ return true;
+}
+
+static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts,
+ exception_mask_t mask) {
+ kern_return_t kr;
+
+ // Re-register all the old ports.
+ for (mach_msg_type_number_t i = 0; i < originalPorts->count; ++i) {
+ // clear the bits from this original mask
+ mask &= ~originalPorts->masks[i];
+
+ kr =
+ task_set_exception_ports(mach_task_self(), originalPorts->masks[i], originalPorts->ports[i],
+ originalPorts->behaviors[i], originalPorts->flavors[i]);
+ if (kr != KERN_SUCCESS) {
+ FIRCLSSDKLog("unable to restore original port: %d", originalPorts->ports[i]);
+ }
+ }
+
+ // Finally, mark any masks we registered for that do not have an original port as unused.
+ kr = task_set_exception_ports(mach_task_self(), mask, MACH_PORT_NULL,
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
+ if (kr != KERN_SUCCESS) {
+ FIRCLSSDKLog("unable to unset unregistered mask: 0x%x", mask);
+ return false;
+ }
+
+ return true;
+}
+
+#pragma mark - Recording
+static void FIRCLSMachExceptionNameLookup(exception_type_t number,
+ mach_exception_data_type_t code,
+ const char** name,
+ const char** codeName) {
+ if (!name || !codeName) {
+ return;
+ }
+
+ *name = NULL;
+ *codeName = NULL;
+
+ switch (number) {
+ case EXC_BAD_ACCESS:
+ *name = "EXC_BAD_ACCESS";
+ switch (code) {
+ case KERN_INVALID_ADDRESS:
+ *codeName = "KERN_INVALID_ADDRESS";
+ break;
+ case KERN_PROTECTION_FAILURE:
+ *codeName = "KERN_PROTECTION_FAILURE";
+ break;
+ }
+
+ break;
+ case EXC_BAD_INSTRUCTION:
+ *name = "EXC_BAD_INSTRUCTION";
+#if CLS_CPU_X86
+ *codeName = "EXC_I386_INVOP";
+#endif
+ break;
+ case EXC_ARITHMETIC:
+ *name = "EXC_ARITHMETIC";
+#if CLS_CPU_X86
+ switch (code) {
+ case EXC_I386_DIV:
+ *codeName = "EXC_I386_DIV";
+ break;
+ case EXC_I386_INTO:
+ *codeName = "EXC_I386_INTO";
+ break;
+ case EXC_I386_NOEXT:
+ *codeName = "EXC_I386_NOEXT";
+ break;
+ case EXC_I386_EXTOVR:
+ *codeName = "EXC_I386_EXTOVR";
+ break;
+ case EXC_I386_EXTERR:
+ *codeName = "EXC_I386_EXTERR";
+ break;
+ case EXC_I386_EMERR:
+ *codeName = "EXC_I386_EMERR";
+ break;
+ case EXC_I386_BOUND:
+ *codeName = "EXC_I386_BOUND";
+ break;
+ case EXC_I386_SSEEXTERR:
+ *codeName = "EXC_I386_SSEEXTERR";
+ break;
+ }
+#endif
+ break;
+ case EXC_BREAKPOINT:
+ *name = "EXC_BREAKPOINT";
+#if CLS_CPU_X86
+ switch (code) {
+ case EXC_I386_DIVERR:
+ *codeName = "EXC_I386_DIVERR";
+ break;
+ case EXC_I386_SGLSTP:
+ *codeName = "EXC_I386_SGLSTP";
+ break;
+ case EXC_I386_NMIFLT:
+ *codeName = "EXC_I386_NMIFLT";
+ break;
+ case EXC_I386_BPTFLT:
+ *codeName = "EXC_I386_BPTFLT";
+ break;
+ case EXC_I386_INTOFLT:
+ *codeName = "EXC_I386_INTOFLT";
+ break;
+ case EXC_I386_BOUNDFLT:
+ *codeName = "EXC_I386_BOUNDFLT";
+ break;
+ case EXC_I386_INVOPFLT:
+ *codeName = "EXC_I386_INVOPFLT";
+ break;
+ case EXC_I386_NOEXTFLT:
+ *codeName = "EXC_I386_NOEXTFLT";
+ break;
+ case EXC_I386_EXTOVRFLT:
+ *codeName = "EXC_I386_EXTOVRFLT";
+ break;
+ case EXC_I386_INVTSSFLT:
+ *codeName = "EXC_I386_INVTSSFLT";
+ break;
+ case EXC_I386_SEGNPFLT:
+ *codeName = "EXC_I386_SEGNPFLT";
+ break;
+ case EXC_I386_STKFLT:
+ *codeName = "EXC_I386_STKFLT";
+ break;
+ case EXC_I386_GPFLT:
+ *codeName = "EXC_I386_GPFLT";
+ break;
+ case EXC_I386_PGFLT:
+ *codeName = "EXC_I386_PGFLT";
+ break;
+ case EXC_I386_EXTERRFLT:
+ *codeName = "EXC_I386_EXTERRFLT";
+ break;
+ case EXC_I386_ALIGNFLT:
+ *codeName = "EXC_I386_ALIGNFLT";
+ break;
+ case EXC_I386_ENDPERR:
+ *codeName = "EXC_I386_ENDPERR";
+ break;
+ case EXC_I386_ENOEXTFLT:
+ *codeName = "EXC_I386_ENOEXTFLT";
+ break;
+ }
+#endif
+ break;
+ case EXC_GUARD:
+ *name = "EXC_GUARD";
+ break;
+ }
+}
+
+static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
+ MachExceptionMessage* message) {
+ if (!context || !message) {
+ return false;
+ }
+
+ if (FIRCLSContextMarkAndCheckIfCrashed()) {
+ FIRCLSSDKLog("Error: aborting mach exception handler because crash has already occurred\n");
+ exit(1);
+ return false;
+ }
+
+ FIRCLSFile file;
+
+ if (!FIRCLSFileInitWithPath(&file, context->path, false)) {
+ FIRCLSSDKLog("Unable to open mach exception file\n");
+ return false;
+ }
+
+ FIRCLSFileWriteSectionStart(&file, "mach_exception");
+
+ FIRCLSFileWriteHashStart(&file);
+
+ FIRCLSFileWriteHashEntryUint64(&file, "exception", message->exception);
+
+ // record the codes
+ FIRCLSFileWriteHashKey(&file, "codes");
+ FIRCLSFileWriteArrayStart(&file);
+ for (mach_msg_type_number_t i = 0; i < message->codeCnt; ++i) {
+ FIRCLSFileWriteArrayEntryUint64(&file, message->code[i]);
+ }
+ FIRCLSFileWriteArrayEnd(&file);
+
+ const char* name = NULL;
+ const char* codeName = NULL;
+
+ FIRCLSMachExceptionNameLookup(message->exception, message->codeCnt > 0 ? message->code[0] : 0,
+ &name, &codeName);
+
+ FIRCLSFileWriteHashEntryString(&file, "name", name);
+ FIRCLSFileWriteHashEntryString(&file, "code_name", codeName);
+
+ FIRCLSFileWriteHashEntryUint64(&file, "original_ports", context->originalPorts.count);
+ FIRCLSFileWriteHashEntryUint64(&file, "time", time(NULL));
+
+ FIRCLSFileWriteHashEnd(&file);
+
+ FIRCLSFileWriteSectionEnd(&file);
+
+ FIRCLSHandler(&file, message->thread.name, NULL);
+
+ FIRCLSFileClose(&file);
+
+ return true;
+}
+
+#else
+
+INJECT_STRIP_SYMBOL(cls_mach_exception)
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h
new file mode 100644
index 000000000..b19881a7a
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h
@@ -0,0 +1,78 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "FIRCLSFeatures.h"
+
+#pragma once
+
+#if CLS_MACH_EXCEPTION_SUPPORTED
+
+#include
+#include
+#include
+
+// must be at least PTHREAD_STACK_MIN size
+#define CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE (256 * 1024)
+
+#pragma mark Structures
+#pragma pack(push, 4)
+typedef struct {
+ mach_msg_header_t head;
+ /* start of the kernel processed data */
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ /* end of the kernel processed data */
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
+ mach_msg_trailer_t trailer;
+} MachExceptionMessage;
+
+typedef struct {
+ mach_msg_header_t head;
+ NDR_record_t NDR;
+ kern_return_t retCode;
+} MachExceptionReply;
+#pragma pack(pop)
+
+typedef struct {
+ mach_msg_type_number_t count;
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ exception_handler_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+} FIRCLSMachExceptionOriginalPorts;
+
+typedef struct {
+ mach_port_t port;
+ pthread_t thread;
+ const char* path;
+
+ exception_mask_t mask;
+ FIRCLSMachExceptionOriginalPorts originalPorts;
+} FIRCLSMachExceptionReadContext;
+
+#pragma mark - API
+void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context, exception_mask_t ignoreMask);
+exception_mask_t FIRCLSMachExceptionMaskForSignal(int signal);
+
+void FIRCLSMachExceptionCheckHandlers(void);
+
+#else
+
+#define CLS_MACH_EXCEPTION_HANDLER_STACK_SIZE 0
+
+#endif
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.c
new file mode 100644
index 000000000..5fa302d7c
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.c
@@ -0,0 +1,318 @@
+// 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.
+
+#include "FIRCLSSignal.h"
+#include "FIRCLSGlobals.h"
+#include "FIRCLSHandler.h"
+#include "FIRCLSUtility.h"
+
+#include
+#include
+
+static const int FIRCLSFatalSignals[FIRCLSSignalCount] = {SIGABRT, SIGBUS, SIGFPE, SIGILL,
+ SIGSEGV, SIGSYS, SIGTRAP};
+
+#if CLS_USE_SIGALTSTACK
+static void FIRCLSSignalInstallAltStack(FIRCLSSignalReadContext *roContext);
+#endif
+static void FIRCLSSignalInstallHandlers(FIRCLSSignalReadContext *roContext);
+static void FIRCLSSignalHandler(int signal, siginfo_t *info, void *uapVoid);
+
+void FIRCLSSignalInitialize(FIRCLSSignalReadContext *roContext) {
+ if (!FIRCLSUnlinkIfExists(roContext->path)) {
+ FIRCLSSDKLog("Unable to reset the signal log file %s\n", strerror(errno));
+ }
+
+#if CLS_USE_SIGALTSTACK
+ FIRCLSSignalInstallAltStack(roContext);
+#endif
+ FIRCLSSignalInstallHandlers(roContext);
+#if TARGET_IPHONE_SIMULATOR
+ // prevent the OpenGL stack (by way of OpenGLES.framework/libLLVMContainer.dylib) from installing
+ // signal handlers that do not chain back
+ // TODO: I don't believe this is necessary as of recent iOS releases
+ bool *ptr = dlsym(RTLD_DEFAULT, "_ZN4llvm23DisablePrettyStackTraceE");
+ if (ptr) {
+ *ptr = true;
+ }
+#endif
+}
+
+void FIRCLSSignalEnumerateHandledSignals(void (^block)(int idx, int signal)) {
+ for (int i = 0; i < FIRCLSSignalCount; ++i) {
+ block(i, FIRCLSFatalSignals[i]);
+ }
+}
+
+#if CLS_USE_SIGALTSTACK
+
+static void FIRCLSSignalInstallAltStack(FIRCLSSignalReadContext *roContext) {
+ stack_t signalStack;
+ stack_t originalStack;
+
+ signalStack.ss_sp = _firclsContext.readonly->signalStack;
+ signalStack.ss_size = CLS_SIGNAL_HANDLER_STACK_SIZE;
+ signalStack.ss_flags = 0;
+
+ if (sigaltstack(&signalStack, &originalStack) != 0) {
+ FIRCLSSDKLog("Unable to setup stack %s\n", strerror(errno));
+
+ return;
+ }
+
+ roContext->originalStack.ss_sp = NULL;
+ roContext->originalStack = originalStack;
+}
+
+#endif
+
+static void FIRCLSSignalInstallHandlers(FIRCLSSignalReadContext *roContext) {
+ FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
+ struct sigaction action;
+ struct sigaction previousAction;
+
+ action.sa_sigaction = FIRCLSSignalHandler;
+ // SA_RESETHAND seems like it would be great, but it doesn't appear to
+ // work correctly. After taking a signal, causing another identical signal in
+ // the handler will *not* cause the default handler to be invokved (which should
+ // terminate the process). I've found some evidence that others have seen this
+ // behavior on MAC OS X.
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+
+ sigemptyset(&action.sa_mask);
+
+ previousAction.sa_sigaction = NULL;
+ if (sigaction(signal, &action, &previousAction) != 0) {
+ FIRCLSSDKLog("Unable to install handler for %d (%s)\n", signal, strerror(errno));
+ }
+
+ // store the last action, so it can be recalled
+ roContext->originalActions[idx].sa_sigaction = NULL;
+
+ if (previousAction.sa_sigaction) {
+ roContext->originalActions[idx] = previousAction;
+ }
+ });
+}
+
+void FIRCLSSignalCheckHandlers(void) {
+ if (_firclsContext.readonly->debuggerAttached) {
+ return;
+ }
+
+ FIRCLSSignalEnumerateHandledSignals(^(int idx, int signal) {
+ struct sigaction previousAction;
+ Dl_info info;
+ void *ptr;
+
+ if (sigaction(signal, 0, &previousAction) != 0) {
+ fprintf(stderr, "Unable to read signal handler\n");
+ return;
+ }
+
+ ptr = previousAction.__sigaction_u.__sa_handler;
+ const char *signalName = NULL;
+ const char *codeName = NULL;
+
+ FIRCLSSignalNameLookup(signal, 0, &signalName, &codeName);
+
+ if (ptr == FIRCLSSignalHandler) {
+ return;
+ }
+
+ const char *name = NULL;
+ if (dladdr(ptr, &info) != 0) {
+ name = info.dli_sname;
+ }
+
+ fprintf(stderr,
+ "[Crashlytics] The signal %s has a non-Crashlytics handler (%s). This will interfere "
+ "with reporting.\n",
+ signalName, name);
+ });
+}
+
+void FIRCLSSignalSafeRemoveHandlers(bool includingAbort) {
+ for (int i = 0; i < FIRCLSSignalCount; ++i) {
+ struct sigaction sa;
+
+ if (!includingAbort && (FIRCLSFatalSignals[i] == SIGABRT)) {
+ continue;
+ }
+
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+
+ if (sigaction(FIRCLSFatalSignals[i], &sa, NULL) != 0)
+ FIRCLSSDKLog("Unable to set default handler for %d (%s)\n", i, strerror(errno));
+ }
+}
+
+bool FIRCLSSignalSafeInstallPreexistingHandlers(FIRCLSSignalReadContext *roContext) {
+ bool success;
+
+ FIRCLSSignalSafeRemoveHandlers(true);
+
+#if CLS_USE_SIGALTSTACK
+
+ // re-install the original stack, if needed
+ if (roContext->originalStack.ss_sp) {
+ if (sigaltstack(&roContext->originalStack, 0) != 0) {
+ FIRCLSSDKLog("Unable to setup stack %s\n", strerror(errno));
+
+ return false;
+ }
+ }
+
+#endif
+
+ // re-install the original handlers, if any
+ success = true;
+ for (int i = 0; i < FIRCLSSignalCount; ++i) {
+ if (roContext->originalActions[i].sa_sigaction == NULL) {
+ continue;
+ }
+
+ if (sigaction(FIRCLSFatalSignals[i], &roContext->originalActions[i], 0) != 0) {
+ FIRCLSSDKLog("Unable to install handler for %d (%s)\n", i, strerror(errno));
+ success = false;
+ }
+ }
+
+ return success;
+}
+
+void FIRCLSSignalNameLookup(int number, int code, const char **name, const char **codeName) {
+ if (!name || !codeName) {
+ return;
+ }
+
+ *codeName = NULL;
+
+ switch (number) {
+ case SIGABRT:
+ *name = "SIGABRT";
+ *codeName = "ABORT";
+ break;
+ case SIGBUS:
+ *name = "SIGBUS";
+ break;
+ case SIGFPE:
+ *name = "SIGFPE";
+ break;
+ case SIGILL:
+ *name = "SIGILL";
+ break;
+ case SIGSEGV:
+ *name = "SIGSEGV";
+ break;
+ case SIGSYS:
+ *name = "SIGSYS";
+ break;
+ case SIGTRAP:
+ *name = "SIGTRAP";
+ break;
+ default:
+ *name = "UNKNOWN";
+ break;
+ }
+}
+
+static void FIRCLSSignalRecordSignal(int savedErrno, siginfo_t *info, void *uapVoid) {
+ if (!_firclsContext.readonly) {
+ return;
+ }
+
+ if (FIRCLSContextMarkAndCheckIfCrashed()) {
+ FIRCLSSDKLog("Error: aborting signal handler because crash has already occurred");
+ exit(1);
+ return;
+ }
+
+ FIRCLSFile file;
+
+ if (!FIRCLSFileInitWithPath(&file, _firclsContext.readonly->signal.path, false)) {
+ FIRCLSSDKLog("Unable to open signal file\n");
+ return;
+ }
+
+ FIRCLSFileWriteSectionStart(&file, "signal");
+
+ FIRCLSFileWriteHashStart(&file);
+
+ if (FIRCLSIsValidPointer(info)) {
+ FIRCLSFileWriteHashEntryUint64(&file, "number", info->si_signo);
+ FIRCLSFileWriteHashEntryUint64(&file, "code", info->si_code);
+ FIRCLSFileWriteHashEntryUint64(&file, "address", (uint64_t)info->si_addr);
+
+ const char *name = NULL;
+ const char *codeName = NULL;
+
+ FIRCLSSignalNameLookup(info->si_signo, info->si_code, &name, &codeName);
+
+ FIRCLSFileWriteHashEntryString(&file, "name", name);
+ FIRCLSFileWriteHashEntryString(&file, "code_name", codeName);
+ }
+
+ FIRCLSFileWriteHashEntryUint64(&file, "errno", savedErrno);
+ FIRCLSFileWriteHashEntryUint64(&file, "time", time(NULL));
+
+ FIRCLSFileWriteHashEnd(&file);
+
+ FIRCLSFileWriteSectionEnd(&file);
+
+ FIRCLSHandler(&file, mach_thread_self(), uapVoid);
+
+ FIRCLSFileClose(&file);
+}
+
+static void FIRCLSSignalHandler(int signal, siginfo_t *info, void *uapVoid) {
+ int savedErrno;
+ sigset_t set;
+
+ // save errno, both because it is interesting, and so we can restore it afterwards
+ savedErrno = errno;
+ errno = 0;
+
+ FIRCLSSDKLog("Signal: %d\n", signal);
+
+ // it is important to do this before unmasking signals, otherwise we can get
+ // called in a loop
+ FIRCLSSignalSafeRemoveHandlers(true);
+
+ sigfillset(&set);
+ if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
+ FIRCLSSDKLog("Unable to unmask signals - we risk infinite recursion here\n");
+ }
+
+ // check info and uapVoid, and set them to appropriate values if invalid. This can happen
+ // if we have been called without the SA_SIGINFO flag set
+ if (!FIRCLSIsValidPointer(info)) {
+ info = NULL;
+ }
+
+ if (!FIRCLSIsValidPointer(uapVoid)) {
+ uapVoid = NULL;
+ }
+
+ FIRCLSSignalRecordSignal(savedErrno, info, uapVoid);
+
+ // re-install original handlers
+ if (_firclsContext.readonly) {
+ FIRCLSSignalSafeInstallPreexistingHandlers(&_firclsContext.readonly->signal);
+ }
+
+ // restore errno
+ errno = savedErrno;
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h
new file mode 100644
index 000000000..3b6b1b4e2
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Handlers/FIRCLSSignal.h
@@ -0,0 +1,51 @@
+// 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.
+
+#pragma once
+
+#include "FIRCLSFeatures.h"
+#include "FIRCLSFile.h"
+
+#include
+#include
+
+#define FIRCLSSignalCount (7)
+
+// per man sigaltstack, MINSIGSTKSZ is the minimum *overhead* needed to support
+// a signal stack. The actual stack size must be larger. Let's pick the recommended
+// size.
+#if CLS_USE_SIGALTSTACK
+#define CLS_SIGNAL_HANDLER_STACK_SIZE (SIGSTKSZ * 2)
+#else
+#define CLS_SIGNAL_HANDLER_STACK_SIZE 0
+#endif
+
+typedef struct {
+ const char* path;
+ struct sigaction originalActions[FIRCLSSignalCount];
+
+#if CLS_USE_SIGALTSTACK
+ stack_t originalStack;
+#endif
+} FIRCLSSignalReadContext;
+
+void FIRCLSSignalInitialize(FIRCLSSignalReadContext* roContext);
+void FIRCLSSignalCheckHandlers(void);
+
+void FIRCLSSignalSafeRemoveHandlers(bool includingAbort);
+bool FIRCLSSignalSafeInstallPreexistingHandlers(FIRCLSSignalReadContext* roContext);
+
+void FIRCLSSignalNameLookup(int number, int code, const char** name, const char** codeName);
+
+void FIRCLSSignalEnumerateHandledSignals(void (^block)(int idx, int signal));
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.h
new file mode 100644
index 000000000..f0de9fed9
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.h
@@ -0,0 +1,23 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FIRAEvent_Internal_h
+#define FIRAEvent_Internal_h
+
+#import "FIRAEvent.h"
+#import "FIRAValue.h"
+
+NSString* FIRCLSFIRAEventDictionaryToJSON(NSDictionary* eventAsDictionary);
+
+#endif /* FIRAEvent_Internal_h */
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.m
new file mode 100644
index 000000000..4591e29f1
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent+Internal.m
@@ -0,0 +1,42 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRAEvent+Internal.h"
+
+#import "FIRCLSUtility.h"
+
+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;
+ }
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent.h
new file mode 100644
index 000000000..9fcbd6602
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAEvent.h
@@ -0,0 +1,79 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import
+
+@class FIRAPBEvent;
+
+/// An application event.
+@interface FIRAEvent : NSObject
+
+/// Origin of this event (eg "app" or "auto").
+@property(nonatomic, readonly) NSString *origin;
+
+/// Name of this event.
+@property(nonatomic, readonly) NSString *name;
+
+/// Timestamp of when this event was fired.
+@property(nonatomic, readonly) NSTimeInterval timestamp;
+
+/// Timestamp of the previous time an event with this name was fired, if any.
+@property(nonatomic, readonly) NSTimeInterval previousTimestamp;
+
+/// The event's parameters as {NSString : NSString} or {NSString : NSNumber}.
+@property(nonatomic, readonly) NSDictionary *parameters;
+
+/// Indicates whether the event has the conversion parameter. Setting to YES adds the conversion
+/// parameter if not already present. Setting to NO removes the conversion parameter and adds an
+/// error.
+@property(nonatomic, getter=isConversion) BOOL conversion;
+
+/// Indicates whether the event has the real-time parameter. Setting to YES adds the real-time
+/// parameter if not already present. Setting to NO removes the real-time parameter.
+@property(nonatomic, getter=isRealtime) BOOL realtime;
+
+/// Indicates whether the event has debug parameter. Setting to YES adds the debug parameter if
+/// not already present. Setting to NO removes the debug parameter.
+@property(nonatomic, getter=isDebug) BOOL debug;
+
+/// The populated FIRAPBEvent for proto.
+@property(nonatomic, readonly) FIRAPBEvent *protoEvent;
+
+/// Creates an event with the given parameters. Parameters will be copied and normalized. Returns
+/// nil if the name does not meet length requirements.
+/// If |parameters| contains the "_o" parameter, its value will be overwritten with the value of
+/// |origin|.
+- (instancetype)initWithOrigin:(NSString *)origin
+ isPublic:(BOOL)isPublic
+ name:(NSString *)name
+ timestamp:(NSTimeInterval)timestamp
+ previousTimestamp:(NSTimeInterval)previousTimestamp
+ parameters:(NSDictionary *)parameters NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/// Returns a new event object with the given previousTimestamp.
+- (instancetype)copyWithPreviousTimestamp:(NSTimeInterval)previousTimestamp;
+
+/// Returns a new event object with the new parameters.
+- (instancetype)copyWithParameters:(NSDictionary *)parameters;
+
+/// Returns YES if all parameters in screenParameters were added to the event object. Returns NO if
+/// screenParameters is nil/empty or the event already contains any of the screen parameter keys.
+/// Performs internal validation on the screen parameter values and converts them to FIRAValue
+/// objects if they aren't already. screenParameters should be a dictionary of
+/// { NSString : NSString | NSNumber } or { NSString : FIRAValue }.
+- (BOOL)addScreenParameters:(NSDictionary *)screenParameters;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAValue.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAValue.h
new file mode 100644
index 000000000..7d10ec308
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRAValue.h
@@ -0,0 +1,69 @@
+// 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
+
+typedef NS_ENUM(NSInteger, FIRAValueType) {
+ kFIRAValueTypeDouble = 0,
+ kFIRAValueTypeInteger,
+ kFIRAValueTypeString,
+};
+
+@interface FIRAValue : NSObject
+
+/// The type of the value.
+@property(nonatomic, readonly) FIRAValueType valueType;
+
+#pragma mark - Double type.
+
+/// Indicates whether the FIRAValue instance is a floating point.
+@property(nonatomic, readonly) BOOL isDouble;
+
+/// Float value. Check valueType to see if this attribute has float value.
+@property(nonatomic, readonly) double doubleValue;
+
+#pragma mark - Integer type.
+
+/// Indicates whether the FIRAValue instance is an integer.
+@property(nonatomic, readonly) BOOL isInt64;
+
+/// Int64 value. Check valueType to see if this attribute has int64 value.
+@property(nonatomic, readonly) int64_t int64Value;
+
+#pragma mark - String type.
+
+/// Indicates whether the FIRAValue instance is a string.
+@property(nonatomic, readonly) BOOL isString;
+
+/// String value. Check valueType to see if this attribute has string value.
+@property(nonatomic, readonly) NSString *stringValue;
+
+#pragma mark - Initializers.
+
+/// Creates a @c FIRAValue if |object| is of type NSString or NSNumber. Returns |object| if it's
+/// already a FIRAValue. Returns nil otherwise.
++ (instancetype)valueFromObject:(id)object;
+
+/// Creates a @c FIRAValue with double value.
+- (instancetype)initWithDouble:(double)value;
+
+/// Creates a @c FIRAValue with int64 value.
+- (instancetype)initWithInt64:(int64_t)value;
+
+/// Creates a @c FIRAValue with string value.
+- (instancetype)initWithString:(NSString *)stringValue;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.c b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.c
new file mode 100644
index 000000000..febafd2ab
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.c
@@ -0,0 +1,238 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+
+#include "FIRCLSAllocate.h"
+#include "FIRCLSHost.h"
+#include "FIRCLSUtility.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+void* FIRCLSAllocatorSafeAllocateFromRegion(FIRCLSAllocationRegion* region, size_t size);
+
+FIRCLSAllocatorRef FIRCLSAllocatorCreate(size_t writableSpace, size_t readableSpace) {
+ FIRCLSAllocatorRef allocator;
+ FIRCLSAllocationRegion writableRegion;
+ FIRCLSAllocationRegion readableRegion;
+ size_t allocationSize;
+ vm_size_t pageSize;
+ void* buffer;
+
+ // | GUARD | WRITABLE_REGION | GUARD | READABLE_REGION | GUARD |
+
+ pageSize = FIRCLSHostGetPageSize();
+
+ readableSpace += sizeof(FIRCLSAllocator); // add the space for our allocator itself
+
+ // we can only protect at the page level, so we need all of our regions to be
+ // exact multples of pages. But, we don't need anything in the special-case of zero.
+
+ writableRegion.size = 0;
+ if (writableSpace > 0) {
+ writableRegion.size = ((writableSpace / pageSize) + 1) * pageSize;
+ }
+
+ readableRegion.size = 0;
+ if (readableSpace > 0) {
+ readableRegion.size = ((readableSpace / pageSize) + 1) * pageSize;
+ }
+
+ // Make one big, continous allocation, adding additional pages for our guards. Note
+ // that we cannot use malloc (or valloc) in this case, because we need to assert full
+ // ownership over these allocations. mmap is a much better choice. We also mark these
+ // pages as MAP_NOCACHE.
+ allocationSize = writableRegion.size + readableRegion.size + pageSize * 3;
+ buffer =
+ mmap(0, allocationSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_NOCACHE, -1, 0);
+ if (buffer == MAP_FAILED) {
+ FIRCLSSDKLogError("Mapping failed %s\n", strerror(errno));
+ return NULL;
+ }
+
+ // move our cursors into position
+ writableRegion.cursor = (void*)((uintptr_t)buffer + pageSize);
+ readableRegion.cursor = (void*)((uintptr_t)buffer + pageSize + writableRegion.size + pageSize);
+ writableRegion.start = writableRegion.cursor;
+ readableRegion.start = readableRegion.cursor;
+
+ FIRCLSSDKLogInfo("Mapping: %p %p %p, total: %zu K\n", buffer, writableRegion.start,
+ readableRegion.start, allocationSize / 1024);
+
+ // protect first guard page
+ if (mprotect(buffer, pageSize, PROT_NONE) != 0) {
+ FIRCLSSDKLogError("First guard protection failed %s\n", strerror(errno));
+ return NULL;
+ }
+
+ // middle guard
+ if (mprotect((void*)((uintptr_t)buffer + pageSize + writableRegion.size), pageSize, PROT_NONE) !=
+ 0) {
+ FIRCLSSDKLogError("Middle guard protection failed %s\n", strerror(errno));
+ return NULL;
+ }
+
+ // end guard
+ if (mprotect((void*)((uintptr_t)buffer + pageSize + writableRegion.size + pageSize +
+ readableRegion.size),
+ pageSize, PROT_NONE) != 0) {
+ FIRCLSSDKLogError("Last guard protection failed %s\n", strerror(errno));
+ return NULL;
+ }
+
+ // now, perform our first "allocation", which is to place our allocator into the read-only region
+ allocator = FIRCLSAllocatorSafeAllocateFromRegion(&readableRegion, sizeof(FIRCLSAllocator));
+
+ // set up its data structure
+ allocator->buffer = buffer;
+ allocator->protectionEnabled = false;
+ allocator->readableRegion = readableRegion;
+ allocator->writeableRegion = writableRegion;
+
+ FIRCLSSDKLogDebug("Allocator successfully created %p", allocator);
+
+ return allocator;
+}
+
+void FIRCLSAllocatorDestroy(FIRCLSAllocatorRef allocator) {
+ if (allocator) {
+ }
+}
+
+bool FIRCLSAllocatorProtect(FIRCLSAllocatorRef allocator) {
+ void* address;
+
+ if (!FIRCLSIsValidPointer(allocator)) {
+ FIRCLSSDKLogError("Invalid allocator");
+ return false;
+ }
+
+ if (allocator->protectionEnabled) {
+ FIRCLSSDKLogWarn("Write protection already enabled");
+ return true;
+ }
+
+ // This has to be done first
+ allocator->protectionEnabled = true;
+
+ vm_size_t pageSize = FIRCLSHostGetPageSize();
+
+ // readable region
+ address =
+ (void*)((uintptr_t)allocator->buffer + pageSize + allocator->writeableRegion.size + pageSize);
+
+ return mprotect(address, allocator->readableRegion.size, PROT_READ) == 0;
+}
+
+bool FIRCLSAllocatorUnprotect(FIRCLSAllocatorRef allocator) {
+ size_t bufferSize;
+
+ if (!allocator) {
+ return false;
+ }
+
+ vm_size_t pageSize = FIRCLSHostGetPageSize();
+
+ bufferSize = (uintptr_t)allocator->buffer + pageSize + allocator->writeableRegion.size +
+ pageSize + allocator->readableRegion.size + pageSize;
+
+ allocator->protectionEnabled =
+ !(mprotect(allocator->buffer, bufferSize, PROT_READ | PROT_WRITE) == 0);
+
+ return allocator->protectionEnabled;
+}
+
+void* FIRCLSAllocatorSafeAllocateFromRegion(FIRCLSAllocationRegion* region, size_t size) {
+ void* newCursor;
+ void* originalCursor;
+
+ // Here's the idea
+ // - read the current cursor
+ // - compute what our new cursor should be
+ // - attempt a swap
+ // if the swap fails, some other thread has modified stuff, and we have to start again
+ // if the swap works, everything has been updated correctly and we are done
+ do {
+ originalCursor = region->cursor;
+
+ // this shouldn't happen unless we make a mistake with our size pre-computations
+ if ((uintptr_t)originalCursor - (uintptr_t)region->start + size > region->size) {
+ FIRCLSSDKLog("Unable to allocate sufficient memory, falling back to malloc\n");
+ void* ptr = malloc(size);
+ if (!ptr) {
+ FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocateFromRegion\n");
+ return NULL;
+ }
+ return ptr;
+ }
+
+ newCursor = (void*)((uintptr_t)originalCursor + size);
+ } while (!atomic_compare_exchange_strong(®ion->cursor, &originalCursor, newCursor));
+
+ return originalCursor;
+}
+
+void* FIRCLSAllocatorSafeAllocate(FIRCLSAllocatorRef allocator,
+ size_t size,
+ FIRCLSAllocationType type) {
+ FIRCLSAllocationRegion* region;
+
+ if (!allocator) {
+ // fall back to malloc in this case
+ FIRCLSSDKLog("Allocator invalid, falling back to malloc\n");
+ void* ptr = malloc(size);
+ if (!ptr) {
+ FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocate\n");
+ return NULL;
+ }
+ return ptr;
+ }
+
+ if (allocator->protectionEnabled) {
+ FIRCLSSDKLog("Allocator already protected, falling back to malloc\n");
+ void* ptr = malloc(size);
+ if (!ptr) {
+ FIRCLSSDKLog("Unable to malloc in FIRCLSAllocatorSafeAllocate\n");
+ return NULL;
+ }
+ return ptr;
+ }
+
+ switch (type) {
+ case CLS_READONLY:
+ region = &allocator->readableRegion;
+ break;
+ case CLS_READWRITE:
+ region = &allocator->writeableRegion;
+ break;
+ default:
+ return NULL;
+ }
+
+ return FIRCLSAllocatorSafeAllocateFromRegion(region, size);
+}
+
+void FIRCLSAllocatorFree(FIRCLSAllocatorRef allocator, void* ptr) {
+ if (!allocator) {
+ free(ptr);
+ }
+
+ // how do we do deallocations?
+}
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h
new file mode 100644
index 000000000..02b526a87
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSAllocate.h
@@ -0,0 +1,48 @@
+// 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.
+
+#include "FIRCLSFeatures.h"
+
+#pragma once
+
+#include
+#include
+
+typedef enum { CLS_READONLY = 0, CLS_READWRITE = 1 } FIRCLSAllocationType;
+
+typedef struct {
+ size_t size;
+ void* start;
+ _Atomic(void*) volatile cursor;
+} FIRCLSAllocationRegion;
+
+typedef struct {
+ void* buffer;
+ bool protectionEnabled;
+ FIRCLSAllocationRegion writeableRegion;
+ FIRCLSAllocationRegion readableRegion;
+} FIRCLSAllocator;
+typedef FIRCLSAllocator* FIRCLSAllocatorRef;
+
+FIRCLSAllocatorRef FIRCLSAllocatorCreate(size_t writableSpace, size_t readableSpace);
+void FIRCLSAllocatorDestroy(FIRCLSAllocatorRef allocator);
+
+bool FIRCLSAllocatorProtect(FIRCLSAllocatorRef allocator);
+bool FIRCLSAllocatorUnprotect(FIRCLSAllocatorRef allocator);
+
+void* FIRCLSAllocatorSafeAllocate(FIRCLSAllocatorRef allocator,
+ size_t size,
+ FIRCLSAllocationType type);
+const char* FIRCLSAllocatorSafeStrdup(FIRCLSAllocatorRef allocator, const char* string);
+void FIRCLSAllocatorFree(FIRCLSAllocatorRef allocator, void* ptr);
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h
new file mode 100644
index 000000000..f309ae07a
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSDefines.h
@@ -0,0 +1,81 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "TargetConditionals.h"
+
+// macro trickiness
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+#define CONCAT_EXPANDED(a, b) a##b
+#define CONCAT(a, b) CONCAT_EXPANDED(a, b)
+
+// These macros generate a function to force a symbol for the containing .o, to work around an issue
+// where strip will not strip debug information without a symbol to strip.
+#define DUMMY_FUNCTION_NAME(x) CONCAT(fircls_strip_this_, x)
+#define INJECT_STRIP_SYMBOL(x) \
+ void DUMMY_FUNCTION_NAME(x)(void) { \
+ }
+
+// These make some target os types available to previous versions of xcode that do not yet have them
+// in their SDKs
+#ifndef TARGET_OS_IOS
+#define TARGET_OS_IOS TARGET_OS_IPHONE
+#endif
+
+#ifndef TARGET_OS_WATCH
+#define TARGET_OS_WATCH 0
+#endif
+
+#ifndef TARGET_OS_TV
+#define TARGET_OS_TV 0
+#endif
+
+// These help compile based on availability of technologies/frameworks.
+#define CLS_TARGET_OS_OSX (TARGET_OS_MAC && !TARGET_OS_IPHONE)
+#define CLS_TARGET_OS_HAS_UIKIT (TARGET_OS_IOS || TARGET_OS_TV)
+
+#define CLS_SDK_DISPLAY_VERSION STR(DISPLAY_VERSION)
+
+#define CLS_SDK_GENERATOR_NAME (STR(CLS_SDK_NAME) "/" CLS_SDK_DISPLAY_VERSION)
+
+// arch definitions
+#if defined(__arm__) || defined(__arm64__) || defined(__arm64e__)
+#include
+#endif
+
+#if defined(__arm__)
+#define CLS_CPU_ARM 1
+#endif
+#if defined(__arm64__) || defined(__arm64e__)
+#define CLS_CPU_ARM64 1
+#endif
+#if defined(__ARM_ARCH_7S__)
+#define CLS_CPU_ARMV7S 1
+#endif
+#if defined(_ARM_ARCH_7)
+#define CLS_CPU_ARMV7 1
+#endif
+#if defined(_ARM_ARCH_6)
+#define CLS_CPU_ARMV6 1
+#endif
+#if defined(__i386__)
+#define CLS_CPU_I386 1
+#endif
+#if defined(__x86_64__)
+#define CLS_CPU_X86_64 1
+#endif
+#define CLS_CPU_X86 (CLS_CPU_I386 || CLS_CPU_X86_64)
+#define CLS_CPU_64BIT (CLS_CPU_X86_64 || CLS_CPU_ARM64)
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.h
new file mode 100644
index 000000000..098833f61
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.h
@@ -0,0 +1,32 @@
+// 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
+
+@class FIRCLSSettings;
+@protocol FIRAnalyticsInterop;
+@protocol FIRAnalyticsInteropListener;
+
+@interface FIRCLSFCRAnalytics : NSObject
+
+/** Logs a Crashlytics crash session in Firebase Analytics.
+ * @param crashTimeStamp The time stamp of the crash to be logged.
+ */
++ (void)logCrashWithTimeStamp:(NSTimeInterval)crashTimeStamp
+ toAnalytics:(id)analytics;
+
++ (void)registerEventListener:(id)eventListener
+ toAnalytics:(id)analytics;
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.m b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.m
new file mode 100644
index 000000000..56dd33b98
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFCRAnalytics.m
@@ -0,0 +1,78 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#import "FIRCLSFCRAnalytics.h"
+
+#import "FIRCLSInternalLogging.h"
+#import "FIRCLSSettings.h"
+
+#import "Interop/Analytics/Public/FIRAnalyticsInterop.h"
+
+// 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));
+}
+
+@implementation FIRCLSFCRAnalytics
+
++ (void)logCrashWithTimeStamp:(NSTimeInterval)crashTimeStamp
+ toAnalytics:(id)analytics {
+ if (analytics == nil) {
+ return;
+ }
+
+ FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports:Event", "Sending event.");
+ NSDictionary *params = [self buildLogParamsFromCrash:crashTimeStamp];
+ [analytics logEventWithOrigin:kFIREventOriginCrash name:kFIREventAppException parameters:params];
+}
+
++ (void)registerEventListener:(id)eventListener
+ toAnalytics:(id)analytics {
+ if (analytics == nil) {
+ return;
+ }
+
+ [analytics registerAnalyticsListener:eventListener withOrigin:kFIREventOriginCrash];
+
+ FIRCLSDeveloperLog(@"Crashlytics:Crash:Reports:Event",
+ "Registered Firebase Analytics event listener");
+}
+
+/**
+ * Builds a dictionary of params to be sent to Analytics using the crash object.
+ *
+ * @param crashTimeStamp The time stamp of the crash to be logged.
+ *
+ * @return An NSDictionary containing the time the crash occured and the fatal
+ * flag to be fed into Firebase Analytics.
+ */
++ (NSDictionary *)buildLogParamsFromCrash:(NSTimeInterval)crashTimeStamp {
+ return @{
+ kFIRParameterTimestamp : timeIntervalInMillis(crashTimeStamp),
+ kFIRParameterFatal : @(INT64_C(1))
+ };
+}
+
+@end
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h
new file mode 100644
index 000000000..4810a5d9b
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFeatures.h
@@ -0,0 +1,31 @@
+// Copyright 2019 Google
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include "FIRCLSDefines.h"
+
+#define CLS_MEMORY_PROTECTION_ENABLED 1
+#define CLS_COMPACT_UNWINDED_ENABLED 1
+#define CLS_DWARF_UNWINDING_ENABLED 1
+
+#define CLS_USE_SIGALTSTACK (!TARGET_OS_WATCH && !TARGET_OS_TV)
+#define CLS_CAN_SUSPEND_THREADS !TARGET_OS_WATCH
+#define CLS_MACH_EXCEPTION_SUPPORTED (!TARGET_OS_WATCH && !TARGET_OS_TV)
+
+#define CLS_COMPACT_UNWINDING_SUPPORTED \
+ ((CLS_CPU_I386 || CLS_CPU_X86_64 || CLS_CPU_ARM64) && CLS_COMPACT_UNWINDED_ENABLED)
+
+#define CLS_DWARF_UNWINDING_SUPPORTED \
+ (CLS_COMPACT_UNWINDING_SUPPORTED && CLS_DWARF_UNWINDING_ENABLED)
diff --git a/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.h b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.h
new file mode 100644
index 000000000..1714ac12e
--- /dev/null
+++ b/ios/Pods/FirebaseCrashlytics/Crashlytics/Crashlytics/Helpers/FIRCLSFile.h
@@ -0,0 +1,109 @@
+// 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.
+
+#pragma once
+
+#include
+#include
+#include
+
+// Required for 1P builds
+#include
+#include