diff --git a/.circleci/config.yml b/.circleci/config.yml index ab9141960..a75a64473 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -84,6 +84,10 @@ jobs: echo -e "VERSIONCODE=$CIRCLE_BUILD_NUM" >> ./gradle.properties + echo -e "" > ./app/fabric.properties + echo -e "apiKey=$FABRIC_KEY" >> ./app/fabric.properties + echo -e "apiSecret=$FABRIC_SECRET" >> ./app/fabric.properties + - run: name: Install Android Depedencies command: | @@ -165,6 +169,8 @@ jobs: command: | cd ios agvtool new-version -all $CIRCLE_BUILD_NUM + /usr/libexec/PlistBuddy -c "Set Fabric:APIKey $FABRIC_KEY" ./RocketChatRN/Info.plist + echo -e "./Fabric.framework/run $FABRIC_KEY $FABRIC_SECRET" >> ./RocketChatRN/Fabric.sh if [[ $MATCH_KEYCHAIN_NAME ]]; then fastlane ios release diff --git a/.eslintrc.js b/.eslintrc.js index 933fb9646..94d3a23e1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -124,7 +124,8 @@ module.exports = { "prefer-const": 2, "object-shorthand": 2, "consistent-return": 0, - "global-require": "off" + "global-require": "off", + "react/prop-types": [0, { skipUndeclared: true }] }, "globals": { "__DEV__": true diff --git a/.gitignore b/.gitignore index 3762b2466..1a68faf71 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ build/ .idea .gradle local.properties +fabric.properties *.iml # node.js diff --git a/README.md b/README.md index 1272fab42..321649b79 100644 --- a/README.md +++ b/README.md @@ -12,75 +12,32 @@ **Supported Server Versions:** 0.58.0+ (We are working to support earlier versions) -# Installing Dependencies +# Installing dependencies Follow the [React Native Getting Started Guide](https://facebook.github.io/react-native/docs/getting-started.html) for detailed instructions on setting up your local machine for development. -# Detailed configuration: - -## Mac - -- General requirements - - - XCode 8.3 - - Install required packages using homebrew: - ```bash - $ brew install watchman - $ brew install yarn - ``` - -- Clone repository and configure: +# How to run +- Clone repository and install dependencies: ```bash $ git clone git@github.com:RocketChat/Rocket.Chat.ReactNative.git $ cd Rocket.Chat.ReactNative - $ npm install $ npm install -g react-native-cli + $ yarn + ``` +- Configuration + ```bash + $ yarn fabric-ios --key="YOUR_API_KEY" --secret="YOUR_API_SECRET" + $ yarn fabric-android --key="YOUR_API_KEY" --secret="YOUR_API_SECRET" ``` - Run application ```bash - $ react-native run-ios + $ yarn ios ``` ```bash - $ react-native run-android + $ yarn android ``` -## Linux: - -- General requirements: - - - JDK 7 or greater - - Android SDK - - Virtualbox - - An Android emulator: Genymotion or Android emulator. If using genymotion ensure that it uses existing adb tools (Settings: "Use custom Android SDK Tools") - - Install watchman (do this globally): - ```bash - $ git clone https://github.com/facebook/watchman.git - $ cd watchman - $ git checkout master - $ ./autogen.sh - $ ./configure - $ make - $ sudo make install - ``` - Configure your kernel to accept a lot of file watches, using a command like: - ```bash - $ sudo sysctl -w fs.inotify.max_user_watches=1048576 - ``` - -- Clone repository and configure: - ```bash - $ git clone git@github.com:RocketChat/Rocket.Chat.ReactNative.git - $ cd Rocket.Chat.ReactNative - $ npm install - $ npm install -g react-native-cli - ``` - -- Run application - - Start emulator - - Start react packager: `$ react-native start` - - Run in emulator: `$ react-native run-android` - # Storybook - General requirements - Install storybook diff --git a/android/app/build.gradle b/android/app/build.gradle index fc2f40982..1183adc62 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -143,7 +143,29 @@ android { } } +buildscript { + repositories { + maven { url 'https://maven.fabric.io/public' } + } + + dependencies { + // These docs use an open ended version so that our plugin + // can be updated quickly in response to Android tooling updates + + // We recommend changing it to the latest version from our changelog: + // https://docs.fabric.io/android/changelog.html#fabric-gradle-plugin + classpath 'io.fabric.tools:gradle:1.+' + } +} + +apply plugin: 'io.fabric' + +repositories { + maven { url 'https://maven.fabric.io/public' } +} + dependencies { + compile project(':react-native-fabric') compile project(':react-native-audio') compile project(":reactnativekeyboardinput") compile project(':react-native-splash-screen') @@ -162,6 +184,9 @@ dependencies { compile "com.facebook.react:react-native:+" // From node_modules compile 'com.facebook.fresco:fresco:1.7.1' compile 'com.facebook.fresco:animated-gif:1.7.1' + compile('com.crashlytics.sdk.android:crashlytics:2.9.1@aar') { + transitive = true; + } } // Run this once to be able to run the application with BUCK diff --git a/android/app/src/main/java/com/rocketchatrn/MainActivity.java b/android/app/src/main/java/com/rocketchatrn/MainActivity.java index 2dcc53324..a539ec30f 100644 --- a/android/app/src/main/java/com/rocketchatrn/MainActivity.java +++ b/android/app/src/main/java/com/rocketchatrn/MainActivity.java @@ -3,6 +3,8 @@ package chat.rocket.reactnative; import android.os.Bundle; import com.facebook.react.ReactActivity; import org.devio.rn.splashscreen.SplashScreen; +import com.crashlytics.android.Crashlytics; +import io.fabric.sdk.android.Fabric; public class MainActivity extends ReactActivity { @@ -19,5 +21,6 @@ public class MainActivity extends ReactActivity { protected void onCreate(Bundle savedInstanceState) { SplashScreen.show(this); super.onCreate(savedInstanceState); + Fabric.with(this, new Crashlytics()); } } diff --git a/android/app/src/main/java/com/rocketchatrn/MainApplication.java b/android/app/src/main/java/com/rocketchatrn/MainApplication.java index 051a8fdc7..689cee99f 100644 --- a/android/app/src/main/java/com/rocketchatrn/MainApplication.java +++ b/android/app/src/main/java/com/rocketchatrn/MainApplication.java @@ -18,6 +18,7 @@ import com.brentvatne.react.ReactVideoPackage; import com.remobile.toast.RCTToastPackage; import com.wix.reactnativekeyboardinput.KeyboardInputPackage; import com.rnim.rn.audio.ReactNativeAudioPackage; +import com.smixx.fabric.FabricPackage; import java.util.Arrays; import java.util.List; @@ -47,7 +48,8 @@ public class MainApplication extends Application implements ReactApplication { new RCTToastPackage(), new ReactNativeAudioPackage(), new KeyboardInputPackage(MainApplication.this), - new RocketChatNativePackage() + new RocketChatNativePackage(), + new FabricPackage() ); } }; diff --git a/android/gradle.properties b/android/gradle.properties index 55422fd77..d94d12020 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -17,4 +17,4 @@ # org.gradle.parallel=true android.useDeprecatedNdk=true -# VERSIONCODE=999999999 +VERSIONCODE=999999999 diff --git a/android/settings.gradle b/android/settings.gradle index 5c628bbf4..bba1bd27f 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'RocketChatRN' +include ':react-native-fabric' +project(':react-native-fabric').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fabric/android') include ':react-native-audio' project(':react-native-audio').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-audio/android') include ':reactnativekeyboardinput' diff --git a/app/containers/TextInput.js b/app/containers/TextInput.js index c6b289942..3abf75459 100644 --- a/app/containers/TextInput.js +++ b/app/containers/TextInput.js @@ -52,7 +52,6 @@ export default class RCTextInput extends React.PureComponent { secureTextEntry: PropTypes.bool } static defaultProps = { - showPassword: false, error: {} } state = { diff --git a/app/sagas/login.js b/app/sagas/login.js index 9cb590794..9bf8a89f6 100644 --- a/app/sagas/login.js +++ b/app/sagas/login.js @@ -1,5 +1,6 @@ import { AsyncStorage } from 'react-native'; import { put, call, takeLatest, select, all, take } from 'redux-saga/effects'; +import { Answers } from 'react-native-fabric'; import * as types from '../actions/actionsTypes'; import { loginRequest, @@ -63,6 +64,7 @@ const saveToken = function* saveToken() { yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user)); const token = yield AsyncStorage.getItem('pushId'); yield token && RocketChat.registerPushToken(user.user.id, token); + Answers.logLogin('Email', true, { server }); }; const handleLoginRequest = function* handleLoginRequest({ credentials }) { diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js index 6b7a0c425..da46c1b59 100644 --- a/app/views/CreateChannelView.js +++ b/app/views/CreateChannelView.js @@ -2,6 +2,8 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { TextInput, View, Text, Switch, TouchableOpacity, SafeAreaView } from 'react-native'; + +import LoggedView from './View'; import { createChannelRequest } from '../actions/createChannel'; import styles from './Styles'; import KeyboardView from '../presentation/KeyboardView'; @@ -15,7 +17,7 @@ import KeyboardView from '../presentation/KeyboardView'; createChannel: data => dispatch(createChannelRequest(data)) }) ) -export default class CreateChannelView extends React.Component { +export default class CreateChannelView extends LoggedView { static navigationOptions = () => ({ title: 'Create a New Channel' }); @@ -27,7 +29,7 @@ export default class CreateChannelView extends React.Component { }; constructor(props) { - super(props); + super('CreateChannelView', props); this.default = { channelName: '', type: true diff --git a/app/views/ForgotPasswordView.js b/app/views/ForgotPasswordView.js index fd7cc0277..caf2aa6b5 100644 --- a/app/views/ForgotPasswordView.js +++ b/app/views/ForgotPasswordView.js @@ -4,13 +4,14 @@ import PropTypes from 'prop-types'; import { Text, TextInput, View, TouchableOpacity, SafeAreaView } from 'react-native'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; + +import LoggedView from './View'; import * as loginActions from '../actions/login'; import KeyboardView from '../presentation/KeyboardView'; - import styles from './Styles'; import { showErrorAlert } from '../utils/info'; -class ForgotPasswordView extends React.Component { +class ForgotPasswordView extends LoggedView { static propTypes = { forgotPasswordInit: PropTypes.func.isRequired, forgotPasswordRequest: PropTypes.func.isRequired, @@ -19,7 +20,7 @@ class ForgotPasswordView extends React.Component { } constructor(props) { - super(props); + super('ForgotPasswordView', props); this.state = { email: '', diff --git a/app/views/ListServerView.js b/app/views/ListServerView.js index 7b4c2905d..c8c3177ea 100644 --- a/app/views/ListServerView.js +++ b/app/views/ListServerView.js @@ -5,6 +5,8 @@ import PropTypes from 'prop-types'; import Zeroconf from 'react-native-zeroconf'; import { View, Text, SectionList, StyleSheet, SafeAreaView } from 'react-native'; import { connect } from 'react-redux'; + +import LoggedView from './View'; import { setServer } from '../actions/server'; import database from '../lib/realm'; import Fade from '../animations/fade'; @@ -69,7 +71,7 @@ const zeroconf = new Zeroconf(); }), dispatch => ({ selectServer: server => dispatch(setServer(server)) })) -export default class ListServerView extends React.Component { +export default class ListServerView extends LoggedView { static propTypes = { navigation: PropTypes.object.isRequired, login: PropTypes.object.isRequired, @@ -79,7 +81,7 @@ export default class ListServerView extends React.Component { } constructor(props) { - super(props); + super('ListServerView', props); this.state = { sections: [] }; diff --git a/app/views/MentionedMessagesView/index.js b/app/views/MentionedMessagesView/index.js index 92ff90283..22d2322bd 100644 --- a/app/views/MentionedMessagesView/index.js +++ b/app/views/MentionedMessagesView/index.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { FlatList, Text, View } from 'react-native'; import { connect } from 'react-redux'; +import LoggedView from '../View'; import { openMentionedMessages, closeMentionedMessages } from '../../actions/mentionedMessages'; import styles from './styles'; import Message from '../../containers/message'; @@ -18,7 +19,7 @@ import Message from '../../containers/message'; closeMentionedMessages: () => dispatch(closeMentionedMessages()) }) ) -export default class MentionedMessagesView extends React.PureComponent { +export default class MentionedMessagesView extends LoggedView { static propTypes = { navigation: PropTypes.object, messages: PropTypes.array, @@ -28,6 +29,10 @@ export default class MentionedMessagesView extends React.PureComponent { closeMentionedMessages: PropTypes.func } + constructor(props) { + super('MentionedMessagesView', props); + } + componentDidMount() { this.props.openMentionedMessages(this.props.navigation.state.params.rid); } diff --git a/app/views/PinnedMessagesView/index.js b/app/views/PinnedMessagesView/index.js index eab5bff61..6d04157d4 100644 --- a/app/views/PinnedMessagesView/index.js +++ b/app/views/PinnedMessagesView/index.js @@ -4,6 +4,7 @@ import { FlatList, Text, View } from 'react-native'; import { connect } from 'react-redux'; import ActionSheet from 'react-native-actionsheet'; +import LoggedView from '../View'; import { openPinnedMessages, closePinnedMessages } from '../../actions/pinnedMessages'; import styles from './styles'; import Message from '../../containers/message'; @@ -25,7 +26,7 @@ const options = ['Unpin', 'Cancel']; togglePinRequest: message => dispatch(togglePinRequest(message)) }) ) -export default class PinnedMessagesView extends React.PureComponent { +export default class PinnedMessagesView extends LoggedView { static propTypes = { navigation: PropTypes.object, messages: PropTypes.array, @@ -37,7 +38,7 @@ export default class PinnedMessagesView extends React.PureComponent { } constructor(props) { - super(props); + super('PinnedMessagesView', props); this.state = { message: {} }; diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 0b2632e40..48aa2238c 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -5,6 +5,7 @@ import Icon from 'react-native-vector-icons/Ionicons'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import { connect } from 'react-redux'; +import LoggedView from '../View'; import styles from './styles'; import sharedStyles from '../Styles'; import Avatar from '../../containers/Avatar'; @@ -20,7 +21,7 @@ import { leaveRoom } from '../../actions/room'; }), dispatch => ({ leaveRoom: rid => dispatch(leaveRoom(rid)) })) -export default class RoomActionsView extends React.PureComponent { +export default class RoomActionsView extends LoggedView { static propTypes = { baseUrl: PropTypes.string, user: PropTypes.object, @@ -29,7 +30,7 @@ export default class RoomActionsView extends React.PureComponent { } constructor(props) { - super(props); + super('RoomActionsView', props); const { rid } = props.navigation.state.params; this.rooms = database.objects('subscriptions').filtered('rid = $0', rid); this.state = { diff --git a/app/views/RoomFilesView/index.js b/app/views/RoomFilesView/index.js index 6fca8129c..720598bd7 100644 --- a/app/views/RoomFilesView/index.js +++ b/app/views/RoomFilesView/index.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { FlatList, Text, View } from 'react-native'; import { connect } from 'react-redux'; +import LoggedView from '../View'; import { openRoomFiles, closeRoomFiles } from '../../actions/roomFiles'; import styles from './styles'; import Message from '../../containers/message'; @@ -18,7 +19,7 @@ import Message from '../../containers/message'; closeRoomFiles: () => dispatch(closeRoomFiles()) }) ) -export default class RoomFilesView extends React.PureComponent { +export default class RoomFilesView extends LoggedView { static propTypes = { navigation: PropTypes.object, messages: PropTypes.array, @@ -28,6 +29,10 @@ export default class RoomFilesView extends React.PureComponent { closeRoomFiles: PropTypes.func } + constructor(props) { + super('RoomFilesView', props); + } + componentDidMount() { this.props.openRoomFiles(this.props.navigation.state.params.rid); } diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js index b8b30037a..af296af66 100644 --- a/app/views/RoomInfoEditView/index.js +++ b/app/views/RoomInfoEditView/index.js @@ -4,6 +4,8 @@ import { Text, View, ScrollView, TouchableOpacity, SafeAreaView, Keyboard, Alert import Spinner from 'react-native-loading-spinner-overlay'; import { connect } from 'react-redux'; + +import LoggedView from '../View'; import KeyboardView from '../../presentation/KeyboardView'; import sharedStyles from '../Styles'; import styles from './styles'; @@ -34,14 +36,14 @@ const PERMISSIONS_ARRAY = [ @connect(null, dispatch => ({ eraseRoom: rid => dispatch(eraseRoom(rid)) })) -export default class RoomInfoEditView extends React.Component { +export default class RoomInfoEditView extends LoggedView { static propTypes = { navigation: PropTypes.object, eraseRoom: PropTypes.func }; constructor(props) { - super(props); + super('RoomInfoEditView', props); const { rid } = props.navigation.state.params; this.rooms = database.objects('subscriptions').filtered('rid = $0', rid); this.permissions = {}; @@ -60,6 +62,7 @@ export default class RoomInfoEditView extends React.Component { }; } + async componentDidMount() { await this.updateRoom(); this.init(); diff --git a/app/views/RoomInfoView/index.js b/app/views/RoomInfoView/index.js index bbcf14b5e..ec81c66bd 100644 --- a/app/views/RoomInfoView/index.js +++ b/app/views/RoomInfoView/index.js @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import moment from 'moment'; +import LoggedView from '../View'; import Status from '../../containers/status'; import Avatar from '../../containers/Avatar'; import styles from './styles'; @@ -25,7 +26,7 @@ const camelize = str => str.replace(/^(.)/, (match, chr) => chr.toUpperCase()); Message_TimeFormat: state.settings.Message_TimeFormat, roles: state.roles })) -export default class RoomInfoView extends React.Component { +export default class RoomInfoView extends LoggedView { static propTypes = { baseUrl: PropTypes.string, user: PropTypes.object, @@ -58,7 +59,7 @@ export default class RoomInfoView extends React.Component { }; constructor(props) { - super(props); + super('RoomInfoView', props); const { rid } = props.navigation.state.params; this.rooms = database.objects('subscriptions').filtered('rid = $0', rid); this.sub = { diff --git a/app/views/RoomMembersView/index.js b/app/views/RoomMembersView/index.js index 89beee3c1..ac143fc36 100644 --- a/app/views/RoomMembersView/index.js +++ b/app/views/RoomMembersView/index.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { FlatList, Text, View, TextInput } from 'react-native'; import { connect } from 'react-redux'; +import LoggedView from '../View'; import styles from './styles'; import sharedStyles from '../Styles'; import Avatar from '../../containers/Avatar'; @@ -17,7 +18,7 @@ import database from '../../lib/realm'; user: state.login.user, baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' })) -export default class MentionedMessagesView extends React.PureComponent { +export default class MentionedMessagesView extends LoggedView { static propTypes = { navigation: PropTypes.object } @@ -47,7 +48,7 @@ export default class MentionedMessagesView extends React.PureComponent { }; constructor(props) { - super(props); + super('MentionedMessagesView', props); const { rid, members } = props.navigation.state.params; this.state = { allUsers: false, diff --git a/app/views/RoomView/ListView.js b/app/views/RoomView/ListView.js index cf67514e1..24195d497 100644 --- a/app/views/RoomView/ListView.js +++ b/app/views/RoomView/ListView.js @@ -54,12 +54,15 @@ export class List extends React.Component { componentWillUpdate() { LayoutAnimation.easeInEaseOut(); } + componentWillUnmount() { + this.updateState.stop(); + } updateState = debounce(() => { // this.setState({ this.dataSource = this.dataSource.cloneWithRows(this.data); this.forceUpdate(); // }); - }, 100); + }, 300); render() { return ( dispatch(actionsShow(actionMessage)) }) ) -export default class RoomView extends React.Component { +export default class RoomView extends LoggedView { static propTypes = { navigation: PropTypes.object.isRequired, openRoom: PropTypes.func.isRequired, @@ -61,7 +62,7 @@ export default class RoomView extends React.Component { }); constructor(props) { - super(props); + super('RoomView', props); this.rid = props.rid || props.navigation.state.params.room.rid; diff --git a/app/views/SnippetedMessagesView/index.js b/app/views/SnippetedMessagesView/index.js index a659d20b3..7cc216589 100644 --- a/app/views/SnippetedMessagesView/index.js +++ b/app/views/SnippetedMessagesView/index.js @@ -3,10 +3,23 @@ import PropTypes from 'prop-types'; import { FlatList, Text, View } from 'react-native'; import { connect } from 'react-redux'; +import LoggedView from '../View'; import { openSnippetedMessages, closeSnippetedMessages } from '../../actions/snippetedMessages'; import styles from './styles'; import Message from '../../containers/message'; +const renderItem = ({ item }) => ( + {}} + /> +); + @connect( state => ({ messages: state.snippetedMessages.messages, @@ -18,7 +31,7 @@ import Message from '../../containers/message'; closeSnippetedMessages: () => dispatch(closeSnippetedMessages()) }) ) -export default class SnippetedMessagesView extends React.PureComponent { +export default class SnippetedMessagesView extends LoggedView { static propTypes = { navigation: PropTypes.object, messages: PropTypes.array, @@ -28,6 +41,10 @@ export default class SnippetedMessagesView extends React.PureComponent { closeSnippetedMessages: PropTypes.func } + constructor(props) { + super('SnippetedMessagesView', props); + } + componentDidMount() { this.props.openSnippetedMessages(this.props.navigation.state.params.rid); } @@ -42,18 +59,6 @@ export default class SnippetedMessagesView extends React.PureComponent { ) - renderItem = ({ item }) => ( - {}} - /> - ) - render() { if (this.props.messages.length === 0) { return this.renderEmpty(); @@ -62,7 +67,7 @@ export default class SnippetedMessagesView extends React.PureComponent { item._id} /> diff --git a/app/views/StarredMessagesView/index.js b/app/views/StarredMessagesView/index.js index 2c4a2066d..dad001ef6 100644 --- a/app/views/StarredMessagesView/index.js +++ b/app/views/StarredMessagesView/index.js @@ -4,6 +4,7 @@ import { FlatList, Text, View } from 'react-native'; import { connect } from 'react-redux'; import ActionSheet from 'react-native-actionsheet'; +import LoggedView from '../View'; import { openStarredMessages, closeStarredMessages } from '../../actions/starredMessages'; import styles from './styles'; import Message from '../../containers/message'; @@ -25,7 +26,7 @@ const options = ['Unstar', 'Cancel']; toggleStarRequest: message => dispatch(toggleStarRequest(message)) }) ) -export default class StarredMessagesView extends React.PureComponent { +export default class StarredMessagesView extends LoggedView { static propTypes = { navigation: PropTypes.object, messages: PropTypes.array, @@ -37,7 +38,7 @@ export default class StarredMessagesView extends React.PureComponent { } constructor(props) { - super(props); + super('StarredMessagesView', props); this.state = { message: {} }; diff --git a/app/views/View.js b/app/views/View.js new file mode 100644 index 000000000..cb1646d16 --- /dev/null +++ b/app/views/View.js @@ -0,0 +1,9 @@ +import React from 'react'; +import { Answers } from 'react-native-fabric'; + +export default class extends React.Component { + constructor(name, props) { + super(props); + Answers.logContentView(name); + } +} diff --git a/ios/Crashlytics.framework/Crashlytics b/ios/Crashlytics.framework/Crashlytics new file mode 100755 index 000000000..426b69910 Binary files /dev/null and b/ios/Crashlytics.framework/Crashlytics differ diff --git a/ios/Crashlytics.framework/Headers/ANSCompatibility.h b/ios/Crashlytics.framework/Headers/ANSCompatibility.h new file mode 100644 index 000000000..6ec011d93 --- /dev/null +++ b/ios/Crashlytics.framework/Headers/ANSCompatibility.h @@ -0,0 +1,31 @@ +// +// 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/Crashlytics.framework/Headers/Answers.h b/ios/Crashlytics.framework/Headers/Answers.h new file mode 100644 index 000000000..8deacbee5 --- /dev/null +++ b/ios/Crashlytics.framework/Headers/Answers.h @@ -0,0 +1,210 @@ +// +// 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/Crashlytics.framework/Headers/CLSAttributes.h b/ios/Crashlytics.framework/Headers/CLSAttributes.h new file mode 100644 index 000000000..1526b0dca --- /dev/null +++ b/ios/Crashlytics.framework/Headers/CLSAttributes.h @@ -0,0 +1,33 @@ +// +// 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/Crashlytics.framework/Headers/CLSLogging.h b/ios/Crashlytics.framework/Headers/CLSLogging.h new file mode 100644 index 000000000..59590d546 --- /dev/null +++ b/ios/Crashlytics.framework/Headers/CLSLogging.h @@ -0,0 +1,64 @@ +// +// 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/Crashlytics.framework/Headers/CLSReport.h b/ios/Crashlytics.framework/Headers/CLSReport.h new file mode 100644 index 000000000..a8ff3b0b9 --- /dev/null +++ b/ios/Crashlytics.framework/Headers/CLSReport.h @@ -0,0 +1,103 @@ +// +// 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/Crashlytics.framework/Headers/CLSStackFrame.h b/ios/Crashlytics.framework/Headers/CLSStackFrame.h new file mode 100644 index 000000000..cdb5596cc --- /dev/null +++ b/ios/Crashlytics.framework/Headers/CLSStackFrame.h @@ -0,0 +1,38 @@ +// +// 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/Crashlytics.framework/Headers/Crashlytics.h b/ios/Crashlytics.framework/Headers/Crashlytics.h new file mode 100644 index 000000000..7104ca812 --- /dev/null +++ b/ios/Crashlytics.framework/Headers/Crashlytics.h @@ -0,0 +1,288 @@ +// +// 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/Crashlytics.framework/Info.plist b/ios/Crashlytics.framework/Info.plist new file mode 100644 index 000000000..dad66d98e Binary files /dev/null and b/ios/Crashlytics.framework/Info.plist differ diff --git a/ios/Crashlytics.framework/Modules/module.modulemap b/ios/Crashlytics.framework/Modules/module.modulemap new file mode 100644 index 000000000..da0845e39 --- /dev/null +++ b/ios/Crashlytics.framework/Modules/module.modulemap @@ -0,0 +1,14 @@ +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/Crashlytics.framework/run b/ios/Crashlytics.framework/run new file mode 100755 index 000000000..9058ea62c --- /dev/null +++ b/ios/Crashlytics.framework/run @@ -0,0 +1,28 @@ +#!/bin/sh + +# run +# +# Copyright (c) 2015 Crashlytics. All rights reserved. + +# Figure out where we're being called from +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# Quote path in case of spaces or special chars +DIR="\"${DIR}" + +PATH_SEP="/" +VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script" +UPLOAD_COMMAND="uploadDSYM\" $@ run-script" + +# Ensure params are as expected, run in sync mode to validate +eval $DIR$PATH_SEP$VALIDATE_COMMAND +return_code=$? + +if [[ $return_code != 0 ]]; then + exit $return_code +fi + +# Verification passed, upload dSYM in background to prevent Xcode from waiting +# Note: Validation is performed again before upload. +# Output can still be found in Console.app +eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 & diff --git a/ios/Crashlytics.framework/submit b/ios/Crashlytics.framework/submit new file mode 100755 index 000000000..b7e84f3b8 Binary files /dev/null and b/ios/Crashlytics.framework/submit differ diff --git a/ios/Crashlytics.framework/uploadDSYM b/ios/Crashlytics.framework/uploadDSYM new file mode 100755 index 000000000..971940b3c Binary files /dev/null and b/ios/Crashlytics.framework/uploadDSYM differ diff --git a/ios/Fabric.framework/Fabric b/ios/Fabric.framework/Fabric new file mode 100755 index 000000000..290ee1ca9 Binary files /dev/null and b/ios/Fabric.framework/Fabric differ diff --git a/ios/Fabric.framework/Headers/FABAttributes.h b/ios/Fabric.framework/Headers/FABAttributes.h new file mode 100644 index 000000000..3a9355a7c --- /dev/null +++ b/ios/Fabric.framework/Headers/FABAttributes.h @@ -0,0 +1,51 @@ +// +// 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/Fabric.framework/Headers/Fabric.h b/ios/Fabric.framework/Headers/Fabric.h new file mode 100644 index 000000000..ecbdb53b8 --- /dev/null +++ b/ios/Fabric.framework/Headers/Fabric.h @@ -0,0 +1,82 @@ +// +// 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/Fabric.framework/Info.plist b/ios/Fabric.framework/Info.plist new file mode 100644 index 000000000..4b6894e77 Binary files /dev/null and b/ios/Fabric.framework/Info.plist differ diff --git a/ios/Fabric.framework/Modules/module.modulemap b/ios/Fabric.framework/Modules/module.modulemap new file mode 100644 index 000000000..2a312239d --- /dev/null +++ b/ios/Fabric.framework/Modules/module.modulemap @@ -0,0 +1,6 @@ +framework module Fabric { + umbrella header "Fabric.h" + + export * + module * { export * } +} \ No newline at end of file diff --git a/ios/Fabric.framework/run b/ios/Fabric.framework/run new file mode 100755 index 000000000..9058ea62c --- /dev/null +++ b/ios/Fabric.framework/run @@ -0,0 +1,28 @@ +#!/bin/sh + +# run +# +# Copyright (c) 2015 Crashlytics. All rights reserved. + +# Figure out where we're being called from +DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +# Quote path in case of spaces or special chars +DIR="\"${DIR}" + +PATH_SEP="/" +VALIDATE_COMMAND="uploadDSYM\" $@ validate run-script" +UPLOAD_COMMAND="uploadDSYM\" $@ run-script" + +# Ensure params are as expected, run in sync mode to validate +eval $DIR$PATH_SEP$VALIDATE_COMMAND +return_code=$? + +if [[ $return_code != 0 ]]; then + exit $return_code +fi + +# Verification passed, upload dSYM in background to prevent Xcode from waiting +# Note: Validation is performed again before upload. +# Output can still be found in Console.app +eval $DIR$PATH_SEP$UPLOAD_COMMAND > /dev/null 2>&1 & diff --git a/ios/Fabric.framework/uploadDSYM b/ios/Fabric.framework/uploadDSYM new file mode 100755 index 000000000..0b78a89b3 Binary files /dev/null and b/ios/Fabric.framework/uploadDSYM differ diff --git a/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj index 8e899c110..0bf4d04e1 100644 --- a/ios/RocketChatRN.xcodeproj/project.pbxproj +++ b/ios/RocketChatRN.xcodeproj/project.pbxproj @@ -49,6 +49,10 @@ 70A8D9B456894EFFAF027CAB /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7A30DA4B2D474348824CD05B /* FontAwesome.ttf */; }; 74815BBCB91147C08C8F7B3D /* libRNAudio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1142E3442BA94B19BCF52814 /* libRNAudio.a */; }; 77C35F50C01C43668188886C /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A0EEFAF8AB14F5B9E796CDD /* libRNVectorIcons.a */; }; + 7A2D202320726F1400D0AA04 /* libSMXCrashlytics.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A2D201F20726EF600D0AA04 /* libSMXCrashlytics.a */; }; + 7A309C9C20724870000C6B13 /* Fabric.sh in Resources */ = {isa = PBXBuildFile; fileRef = 7A309C9B20724870000C6B13 /* Fabric.sh */; }; + 7A32C246206D791D001C80E9 /* Fabric.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A32C20F206D791D001C80E9 /* Fabric.framework */; }; + 7A32C247206D791D001C80E9 /* Crashlytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A32C245206D791D001C80E9 /* Crashlytics.framework */; }; 7A430E4F20238C46008F55BC /* libRCTCustomInputController.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A430E1E20238C02008F55BC /* libRCTCustomInputController.a */; }; 7AFB806E205AE65700D004E7 /* libRCTToast.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AFB804C205AE63100D004E7 /* libRCTToast.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; @@ -301,6 +305,20 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTLinking; }; + 7A2D201E20726EF600D0AA04 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7A2D1FE620726EF600D0AA04 /* SMXCrashlytics.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D8D9DB831C6D03DB009FBC0E; + remoteInfo = SMXCrashlytics; + }; + 7A2D202020726EF600D0AA04 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7A2D1FE620726EF600D0AA04 /* SMXCrashlytics.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 6463C84C1EBA12A60095B8CD; + remoteInfo = "SMXCrashlytics-tvOS"; + }; 7A430E1D20238C02008F55BC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */; @@ -492,7 +510,11 @@ 60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = ""; }; 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 7A2D1FE620726EF600D0AA04 /* SMXCrashlytics.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SMXCrashlytics.xcodeproj; path = "../node_modules/react-native-fabric/ios/SMXCrashlytics.xcodeproj"; sourceTree = ""; }; + 7A309C9B20724870000C6B13 /* Fabric.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = Fabric.sh; path = RocketChatRN/Fabric.sh; sourceTree = ""; }; 7A30DA4B2D474348824CD05B /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; }; + 7A32C20F206D791D001C80E9 /* Fabric.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Fabric.framework; path = "../../../../Downloads/com.crashlytics.ios-manual/Fabric.framework"; sourceTree = ""; }; + 7A32C245206D791D001C80E9 /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Crashlytics.framework; path = "../../../../Downloads/com.crashlytics.ios-manual/Crashlytics.framework"; sourceTree = ""; }; 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCustomInputController.xcodeproj; path = "../node_modules/react-native-keyboard-input/lib/ios/RCTCustomInputController.xcodeproj"; sourceTree = ""; }; 7AFB8035205AE63000D004E7 /* RCTToast.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTToast.xcodeproj; path = "../node_modules/@remobile/react-native-toast/ios/RCTToast.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; @@ -528,6 +550,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 7A2D202320726F1400D0AA04 /* libSMXCrashlytics.a in Frameworks */, 7AFB806E205AE65700D004E7 /* libRCTToast.a in Frameworks */, B8971BB2202A093B0000D245 /* libKeyboardTrackingView.a in Frameworks */, 7A430E4F20238C46008F55BC /* libRCTCustomInputController.a in Frameworks */, @@ -535,6 +558,7 @@ B88F586F1FBF57F600B352B8 /* libRCTPushNotification.a in Frameworks */, 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, + 7A32C246206D791D001C80E9 /* Fabric.framework in Frameworks */, 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, @@ -552,6 +576,7 @@ 8A159EDB97C44E52AF62D69C /* libRNSVG.a in Frameworks */, C758F0BD5C3244E2BA073E61 /* libRNImagePicker.a in Frameworks */, 8ECBD927DDAC4987B98E102E /* libRCTVideo.a in Frameworks */, + 7A32C247206D791D001C80E9 /* Crashlytics.framework in Frameworks */, 0F026E58B8A6427D9A204D89 /* libSplashScreen.a in Frameworks */, 2C800DF680F8451599E80AF1 /* libSafariViewManager.a in Frameworks */, 74815BBCB91147C08C8F7B3D /* libRNAudio.a in Frameworks */, @@ -673,6 +698,7 @@ 13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 13B07FB71A68108700A75B9A /* main.m */, + 7A309C9B20724870000C6B13 /* Fabric.sh */, ); name = RocketChatRN; sourceTree = ""; @@ -742,6 +768,15 @@ name = Products; sourceTree = ""; }; + 7A2D1FE720726EF600D0AA04 /* Products */ = { + isa = PBXGroup; + children = ( + 7A2D201F20726EF600D0AA04 /* libSMXCrashlytics.a */, + 7A2D202120726EF600D0AA04 /* libSMXCrashlytics.a */, + ); + name = Products; + sourceTree = ""; + }; 7A430E1720238C01008F55BC /* Products */ = { isa = PBXGroup; children = ( @@ -778,6 +813,7 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 7A2D1FE620726EF600D0AA04 /* SMXCrashlytics.xcodeproj */, 7AFB8035205AE63000D004E7 /* RCTToast.xcodeproj */, B8971BAC202A091D0000D245 /* KeyboardTrackingView.xcodeproj */, 7A430E1620238C01008F55BC /* RCTCustomInputController.xcodeproj */, @@ -819,6 +855,8 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 7A32C245206D791D001C80E9 /* Crashlytics.framework */, + 7A32C20F206D791D001C80E9 /* Fabric.framework */, 13B07FAE1A68108700A75B9A /* RocketChatRN */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 00E356EF1AD99517003FC87E /* RocketChatRNTests */, @@ -1163,6 +1201,10 @@ ProductGroup = B810DF8D203B10480010C331 /* Products */; ProjectRef = 4019A5E1911B4C61944FBCEC /* SafariViewManager.xcodeproj */; }, + { + ProductGroup = 7A2D1FE720726EF600D0AA04 /* Products */; + ProjectRef = 7A2D1FE620726EF600D0AA04 /* SMXCrashlytics.xcodeproj */; + }, { ProductGroup = 7ADCFEBC1FEA8A7900763ED8 /* Products */; ProjectRef = 30FCE1B6376C423E94C9FBB0 /* SplashScreen.xcodeproj */; @@ -1396,6 +1438,20 @@ remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A2D201F20726EF600D0AA04 /* libSMXCrashlytics.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libSMXCrashlytics.a; + remoteRef = 7A2D201E20726EF600D0AA04 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 7A2D202120726EF600D0AA04 /* libSMXCrashlytics.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libSMXCrashlytics.a; + remoteRef = 7A2D202020726EF600D0AA04 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 7A430E1E20238C02008F55BC /* libRCTCustomInputController.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -1559,6 +1615,7 @@ buildActionMask = 2147483647; files = ( B8C682A81FD850F4003A12C8 /* icomoon.ttf in Resources */, + 7A309C9C20724870000C6B13 /* Fabric.sh in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, AE5D35882AE04CC29630FB3D /* Entypo.ttf in Resources */, @@ -1597,7 +1654,7 @@ /* Begin PBXShellScriptBuildPhase section */ 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 12; files = ( ); inputPaths = ( @@ -1607,7 +1664,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n/bin/sh \"${PROJECT_DIR}/RocketChatRN/Fabric.sh\""; }; 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { isa = PBXShellScriptBuildPhase; @@ -1787,6 +1844,10 @@ CURRENT_PROJECT_VERSION = 100; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = S6UPZG7ZR3; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/realm/src/**", @@ -1828,6 +1889,10 @@ CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 100; DEVELOPMENT_TEAM = S6UPZG7ZR3; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SRCROOT)/../node_modules/realm/src/**", diff --git a/ios/RocketChatRN/AppDelegate.m b/ios/RocketChatRN/AppDelegate.m index 15d6ace56..d95a78284 100644 --- a/ios/RocketChatRN/AppDelegate.m +++ b/ios/RocketChatRN/AppDelegate.m @@ -13,6 +13,8 @@ #import #import #import "SplashScreen.h" +#import +#import @implementation AppDelegate @@ -38,6 +40,7 @@ self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; [SplashScreen show]; + [Fabric with:@[[Crashlytics class]]]; return YES; } diff --git a/ios/RocketChatRN/Fabric.sh b/ios/RocketChatRN/Fabric.sh new file mode 100644 index 000000000..5f0aa8dbf --- /dev/null +++ b/ios/RocketChatRN/Fabric.sh @@ -0,0 +1 @@ +./Fabric.framework/run YOUR_API_KEY YOUR_API_SECRET diff --git a/ios/RocketChatRN/Info.plist b/ios/RocketChatRN/Info.plist index 0fca665fc..9a7c650b0 100644 --- a/ios/RocketChatRN/Info.plist +++ b/ios/RocketChatRN/Info.plist @@ -22,8 +22,20 @@ ???? CFBundleVersion 100 - NSMicrophoneUsageDescription - This app uses the microphone to record audio message. + Fabric + + APIKey + YOUR_API_KEY + Kits + + + KitInfo + + KitName + Crashlytics + + + ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS @@ -42,7 +54,9 @@ NSCameraUsageDescription Upload images from camera NSLocationWhenInUseUsageDescription - + + NSMicrophoneUsageDescription + This app uses the microphone to record audio message. NSPhotoLibraryUsageDescription Upload images from library UIAppFonts diff --git a/package.json b/package.json index da87961ac..ebe3c6c90 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "storybook": "storybook start -p 7007", "snyk-protect": "snyk protect", "prepare": "exit 0", - "yarn": "yarn" + "yarn": "yarn", + "fabric-ios": "./scripts/fabric-ios.sh", + "fabric-android": "./scripts/fabric-android.sh" }, "rnpm": { "assets": [ @@ -44,6 +46,7 @@ "react-native-animatable": "^1.2.4", "react-native-audio": "^4.0.0", "react-native-easy-markdown": "git+https://github.com/diegolmello/react-native-easy-markdown.git", + "react-native-fabric": "^0.5.1", "react-native-fetch-blob": "^0.10.8", "react-native-image-picker": "^0.26.7", "react-native-img-cache": "^1.5.2", diff --git a/scripts/fabric-android.sh b/scripts/fabric-android.sh new file mode 100755 index 000000000..a4d23d12d --- /dev/null +++ b/scripts/fabric-android.sh @@ -0,0 +1,19 @@ +# #!/bin/bash +while [ "$#" -gt 0 ]; do + case "$1" in + --key=*) key="${1#*=}"; shift 1;; + --secret=*) secret="${1#*=}"; shift 1;; + --key|--secret) echo "$1 requires an argument" >&2; exit 1;; + + -*) echo "unknown option: $1" >&2; exit 1;; + esac +done + +if [[ -z ${key} || -z ${secret} ]] + then + echo 'Usage: yarn fabric-android --key="YOUR_API_KEY" --secret="YOUR_API_SECRET"' + exit 1 +fi + +echo "apiKey=${key}" > ./android/app/fabric.properties +echo "apiSecret=${secret}" >> ./android/app/fabric.properties \ No newline at end of file diff --git a/scripts/fabric-ios.sh b/scripts/fabric-ios.sh new file mode 100755 index 000000000..4a3566f15 --- /dev/null +++ b/scripts/fabric-ios.sh @@ -0,0 +1,19 @@ +# #!/bin/bash +while [ "$#" -gt 0 ]; do + case "$1" in + --key=*) key="${1#*=}"; shift 1;; + --secret=*) secret="${1#*=}"; shift 1;; + --key|--secret) echo "$1 requires an argument" >&2; exit 1;; + + -*) echo "unknown option: $1" >&2; exit 1;; + esac +done + +if [[ -z ${key} || -z ${secret} ]] + then + echo 'Usage: yarn fabric-ios --key="YOUR_API_KEY" --secret="YOUR_API_SECRET"' + exit 1 +fi + +/usr/libexec/PlistBuddy -c "Set Fabric:APIKey ${key}" ./ios/RocketChatRN/Info.plist +echo "./Fabric.framework/run ${key} ${secret}" > ./ios/RocketChatRN/Fabric.sh \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 95c937ead..ea7f7722a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7678,6 +7678,10 @@ react-native-drawer-layout@1.3.2: dependencies: simple-markdown "^0.1.1" +react-native-fabric@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/react-native-fabric/-/react-native-fabric-0.5.1.tgz#e5eb65c56196355b41f230c14adcc9c4bcf60dda" + react-native-fetch-blob@^0.10.8: version "0.10.8" resolved "https://registry.yarnpkg.com/react-native-fetch-blob/-/react-native-fetch-blob-0.10.8.tgz#4fc256abae0cb5f10e7c41f28c11b3ff330d72a9"