Merge branch 'develop' into new/android-migration

This commit is contained in:
Filipe Brito 2019-09-23 18:09:13 -03:00 committed by GitHub
commit 53496efa6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
558 changed files with 27705 additions and 14672 deletions

View File

@ -81,6 +81,7 @@ import com.android.build.OutputFile
project.ext.react = [ project.ext.react = [
entryFile: "index.js", entryFile: "index.js",
bundleAssetName: "app.bundle",
iconFontNames: [ 'custom.ttf' ], iconFontNames: [ 'custom.ttf' ],
enableHermes: false, // clean and rebuild if changing enableHermes: false, // clean and rebuild if changing
] ]
@ -225,6 +226,12 @@ dependencies {
implementation('com.crashlytics.sdk.android:crashlytics:2.9.9@aar') { implementation('com.crashlytics.sdk.android:crashlytics:2.9.9@aar') {
transitive = true transitive = true
} }
implementation(project(':react-native-jitsi-meet')) {
exclude group: 'com.facebook.react', module:'react-native-fast-image'
exclude group: 'com.facebook.react', module:'react-native-vector-icons'
exclude group: 'com.facebook.react', module:'react-native-webview'
exclude group: 'com.facebook.react', module:'react-native-background-timer'
}
if (enableHermes) { if (enableHermes) {
def hermesPath = "../../node_modules/hermesvm/android/"; def hermesPath = "../../node_modules/hermesvm/android/";

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
ÖŠ<EFBFBD>tùþ춥Z'ŸFöà.â°'

View File

@ -6,6 +6,8 @@ import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import androidx.annotation.Nullable;
import com.facebook.react.PackageList; import com.facebook.react.PackageList;
import com.facebook.hermes.reactexecutor.HermesExecutorFactory; import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
import com.facebook.react.bridge.JavaScriptExecutorFactory; import com.facebook.react.bridge.JavaScriptExecutorFactory;
@ -33,6 +35,7 @@ import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage;
import io.invertase.firebase.perf.RNFirebasePerformancePackage; import io.invertase.firebase.perf.RNFirebasePerformancePackage;
import com.nozbe.watermelondb.WatermelonDBPackage; import com.nozbe.watermelondb.WatermelonDBPackage;
import com.reactnativejitsimeet.JitsiMeetPackage;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -57,6 +60,7 @@ public class MainApplication extends Application implements ReactApplication, IN
packages.add(new KeyboardInputPackage(MainApplication.this)); packages.add(new KeyboardInputPackage(MainApplication.this));
packages.add(new RNNotificationsPackage(MainApplication.this)); packages.add(new RNNotificationsPackage(MainApplication.this));
packages.add(new WatermelonDBPackage()); packages.add(new WatermelonDBPackage());
packages.add(new JitsiMeetPackage());
packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider)); packages.add(new ModuleRegistryAdapter(mModuleRegistryProvider));
return packages; return packages;
} }
@ -65,6 +69,11 @@ public class MainApplication extends Application implements ReactApplication, IN
protected String getJSMainModuleName() { protected String getJSMainModuleName() {
return "index"; return "index";
} }
@Override
protected @Nullable String getBundleAssetName() {
return "app.bundle";
}
}; };
@Override @Override

View File

@ -41,6 +41,9 @@ allprojects {
// Android JSC is installed from npm // Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist") url("$rootDir/../node_modules/jsc-android/dist")
} }
maven {
url "https://github.com/jitsi/jitsi-maven-repository/raw/master/releases"
}
google() google()
jcenter() jcenter()

View File

@ -8,5 +8,7 @@ include ':reactnativenotifications'
project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app') project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app')
include ':reactnativekeyboardinput' include ':reactnativekeyboardinput'
project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyboard-input/lib/android') project(':reactnativekeyboardinput').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keyboard-input/lib/android')
include ':react-native-jitsi-meet'
project(':react-native-jitsi-meet').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-jitsi-meet/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app' include ':app'

View File

@ -1,77 +0,0 @@
import { View, Animated } from 'react-native';
import PropTypes from 'prop-types';
import React from 'react';
export default class Panel extends React.Component {
static propTypes = {
open: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
style: PropTypes.object
}
constructor(props) {
super(props);
this.state = {
animation: new Animated.Value()
};
this.first = true;
this.open = false;
this.opacity = 0;
}
componentDidMount() {
const { animation } = this.state;
const { open } = this.props;
const initialValue = !open ? this.height : 0;
animation.setValue(initialValue);
}
componentWillReceiveProps(nextProps) {
const { animation } = this.state;
const { open } = this.props;
if (this.first) {
this.first = false;
if (!open) {
animation.setValue(0);
return;
}
}
if (this.open === nextProps.open) {
return;
}
this.open = nextProps.open;
const initialValue = !nextProps.open ? this.height : 0;
const finalValue = !nextProps.open ? 0 : this.height;
animation.setValue(initialValue);
Animated.timing(
animation,
{
toValue: finalValue,
duration: 150,
useNativeDriver: true
}
).start();
}
set _height(h) {
this.height = h || this.height;
}
render() {
const { animation } = this.state;
const { style, children } = this.props;
return (
<Animated.View
style={[{ height: animation }, style]}
>
<View onLayout={({ nativeEvent }) => this._height = nativeEvent.layout.height} style={{ position: !this.first ? 'relative' : 'absolute' }}>
{children}
</View>
</Animated.View>
);
}
}

View File

@ -1,63 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Animated, Text } from 'react-native';
export default class Fade extends React.Component {
static propTypes = {
visible: PropTypes.bool.isRequired,
style: Animated.View.propTypes.style,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
])
}
constructor(props) {
super(props);
const { visible } = this.props;
this.state = {
visible
};
this._visibility = new Animated.Value(visible ? 1 : 0);
}
componentWillReceiveProps(nextProps) {
if (nextProps.visible) {
this.setState({ visible: true });
}
Animated.timing(this._visibility, {
toValue: nextProps.visible ? 1 : 0,
duration: 300,
useNativeDriver: true
}).start(() => {
this.setState({ visible: nextProps.visible });
});
}
render() {
const { visible } = this.state;
const { style, children, ...rest } = this.props;
const containerStyle = {
opacity: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [0, 1]
}),
transform: [
{
scale: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [1.1, 1]
})
}
]
};
const combinedStyle = [containerStyle, style];
return (
<Animated.View style={visible ? combinedStyle : containerStyle} {...rest}>
<Text>{visible ? children : null}</Text>
</Animated.View>
);
}
}

View File

@ -23,6 +23,18 @@ export default {
LDAP_Enable: { LDAP_Enable: {
type: 'valueAsBoolean' type: 'valueAsBoolean'
}, },
Jitsi_Enabled: {
type: 'valueAsBoolean'
},
Jitsi_SSL: {
type: 'valueAsBoolean'
},
Jitsi_Domain: {
type: 'valueAsString'
},
Jitsi_URL_Room_Prefix: {
type: 'valueAsString'
},
Message_AllowDeleting: { Message_AllowDeleting: {
type: 'valueAsBoolean' type: 'valueAsBoolean'
}, },
@ -56,6 +68,9 @@ export default {
Store_Last_Message: { Store_Last_Message: {
type: 'valueAsBoolean' type: 'valueAsBoolean'
}, },
uniqueID: {
type: 'valueAsString'
},
UI_Use_Real_Name: { UI_Use_Real_Name: {
type: 'valueAsBoolean' type: 'valueAsBoolean'
}, },

View File

@ -0,0 +1,39 @@
import React from 'react';
import { View, Text } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import PropTypes from 'prop-types';
import { formatLastMessage, BUTTON_HIT_SLOP } from './utils';
import styles from './styles';
import I18n from '../../i18n';
import { CustomIcon } from '../../lib/Icons';
const CallButton = React.memo(({
dlm, callJitsi
}) => {
const time = formatLastMessage(dlm);
return (
<View style={styles.buttonContainer}>
<Touchable
onPress={callJitsi}
background={Touchable.Ripple('#fff')}
style={[styles.button, styles.smallButton]}
hitSlop={BUTTON_HIT_SLOP}
>
<>
<CustomIcon name='video' size={20} style={styles.buttonIcon} />
<Text style={styles.buttonText}>{I18n.t('Click_to_join')}</Text>
</>
</Touchable>
<Text style={styles.time}>{time}</Text>
</View>
);
});
CallButton.propTypes = {
dlm: PropTypes.string,
callJitsi: PropTypes.func
};
CallButton.displayName = 'CallButton';
export default CallButton;

View File

@ -16,6 +16,7 @@ import Broadcast from './Broadcast';
import Discussion from './Discussion'; import Discussion from './Discussion';
import Content from './Content'; import Content from './Content';
import ReadReceipt from './ReadReceipt'; import ReadReceipt from './ReadReceipt';
import CallButton from './CallButton';
const MessageInner = React.memo((props) => { const MessageInner = React.memo((props) => {
if (props.type === 'discussion-created') { if (props.type === 'discussion-created') {
@ -26,6 +27,15 @@ const MessageInner = React.memo((props) => {
</React.Fragment> </React.Fragment>
); );
} }
if (props.type === 'jitsi_call_started') {
return (
<React.Fragment>
<User {...props} />
<Content {...props} isInfo />
<CallButton {...props} />
</React.Fragment>
);
}
return ( return (
<React.Fragment> <React.Fragment>
<User {...props} /> <User {...props} />

View File

@ -41,7 +41,8 @@ export default class MessageContainer extends React.Component {
fetchThreadName: PropTypes.func, fetchThreadName: PropTypes.func,
onOpenFileModal: PropTypes.func, onOpenFileModal: PropTypes.func,
onReactionLongPress: PropTypes.func, onReactionLongPress: PropTypes.func,
navToRoomInfo: PropTypes.func navToRoomInfo: PropTypes.func,
callJitsi: PropTypes.func
} }
static defaultProps = { static defaultProps = {
@ -204,7 +205,7 @@ export default class MessageContainer extends React.Component {
render() { render() {
const { const {
item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage, navToRoomInfo, getCustomEmoji, isThreadRoom item, user, style, archived, baseUrl, useRealName, broadcast, fetchThreadName, customThreadTimeFormat, onOpenFileModal, timeFormat, useMarkdown, isReadReceiptEnabled, autoTranslateRoom, autoTranslateLanguage, navToRoomInfo, getCustomEmoji, isThreadRoom, callJitsi
} = this.props; } = this.props;
const { const {
id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, autoTranslate: autoTranslateMessage id, msg, ts, attachments, urls, reactions, t, avatar, u, alias, editedBy, role, drid, dcount, dlm, tmid, tcount, tlm, tmsg, mentions, channels, unread, autoTranslate: autoTranslateMessage
@ -270,6 +271,7 @@ export default class MessageContainer extends React.Component {
onOpenFileModal={onOpenFileModal} onOpenFileModal={onOpenFileModal}
getCustomEmoji={getCustomEmoji} getCustomEmoji={getCustomEmoji}
navToRoomInfo={navToRoomInfo} navToRoomInfo={navToRoomInfo}
callJitsi={callJitsi}
/> />
); );
} }

View File

@ -67,6 +67,8 @@ export const getInfoMessage = ({
return I18n.t('Room_name_changed', { name: msg, userBy: username }); return I18n.t('Room_name_changed', { name: msg, userBy: username });
} else if (type === 'message_pinned') { } else if (type === 'message_pinned') {
return I18n.t('Message_pinned'); return I18n.t('Message_pinned');
} else if (type === 'jitsi_call_started') {
return I18n.t('Started_call', { userBy: username });
} else if (type === 'ul') { } else if (type === 'ul') {
return I18n.t('Has_left_the_channel'); return I18n.t('Has_left_the_channel');
} else if (type === 'ru') { } else if (type === 'ru') {

View File

@ -120,6 +120,8 @@ export default {
Channel_Name: 'Channel Name', Channel_Name: 'Channel Name',
Channels: 'Channels', Channels: 'Channels',
Chats: 'Chats', Chats: 'Chats',
Call_already_ended: 'Call already ended!',
Click_to_join: 'Click to Join!',
Close: 'Close', Close: 'Close',
Close_emoji_selector: 'Close emoji selector', Close_emoji_selector: 'Close emoji selector',
Choose: 'Choose', Choose: 'Choose',
@ -364,6 +366,7 @@ export default {
Starred: 'Starred', Starred: 'Starred',
Start_of_conversation: 'Start of conversation', Start_of_conversation: 'Start of conversation',
Started_discussion: 'Started a discussion:', Started_discussion: 'Started a discussion:',
Started_call: 'Call started by {{userBy}}',
Submit: 'Submit', Submit: 'Submit',
Table: 'Table', Table: 'Table',
Take_a_photo: 'Take a photo', Take_a_photo: 'Take a photo',

View File

@ -122,6 +122,8 @@ export default {
Channel_Name: 'Nome do Canal', Channel_Name: 'Nome do Canal',
Channels: 'Canais', Channels: 'Canais',
Chats: 'Conversas', Chats: 'Conversas',
Call_already_ended: 'A chamada já terminou!',
Click_to_join: 'Clique para participar!',
Close: 'Fechar', Close: 'Fechar',
Close_emoji_selector: 'Fechar seletor de emojis', Close_emoji_selector: 'Fechar seletor de emojis',
Choose: 'Escolher', Choose: 'Escolher',
@ -325,6 +327,7 @@ export default {
starred: 'favoritou', starred: 'favoritou',
Starred: 'Mensagens Favoritas', Starred: 'Mensagens Favoritas',
Start_of_conversation: 'Início da conversa', Start_of_conversation: 'Início da conversa',
Started_call: 'Chamada iniciada por {{userBy}}',
Started_discussion: 'Iniciou uma discussão:', Started_discussion: 'Iniciou uma discussão:',
Submit: 'Enviar', Submit: 'Enviar',
Table: 'Tabela', Table: 'Tabela',

View File

@ -19,6 +19,7 @@ import { defaultHeader, onNavigationStateChange } from './utils/navigation';
import { loggerConfig, analytics } from './utils/log'; import { loggerConfig, analytics } from './utils/log';
import Toast from './containers/Toast'; import Toast from './containers/Toast';
import RocketChat from './lib/rocketchat'; import RocketChat from './lib/rocketchat';
import LayoutAnimation from './utils/layoutAnimation';
useScreens(); useScreens();
@ -308,12 +309,14 @@ export default class Root extends React.Component {
render() { render() {
return ( return (
<Provider store={store}> <Provider store={store}>
<App <LayoutAnimation>
ref={(navigatorRef) => { <App
Navigation.setTopLevelNavigator(navigatorRef); ref={(navigatorRef) => {
}} Navigation.setTopLevelNavigator(navigatorRef);
onNavigationStateChange={onNavigationStateChange} }}
/> onNavigationStateChange={onNavigationStateChange}
/>
</LayoutAnimation>
</Provider> </Provider>
); );
} }

View File

@ -20,6 +20,8 @@ import Server from './model/Server';
import serversSchema from './schema/servers'; import serversSchema from './schema/servers';
import appSchema from './schema/app'; import appSchema from './schema/app';
import migrations from './model/migrations';
import { isIOS } from '../../utils/deviceInfo'; import { isIOS } from '../../utils/deviceInfo';
const appGroupPath = isIOS ? `${ RNFetchBlob.fs.syncPathAppGroup('group.ios.chat.rocket') }/` : ''; const appGroupPath = isIOS ? `${ RNFetchBlob.fs.syncPathAppGroup('group.ios.chat.rocket') }/` : '';
@ -54,7 +56,8 @@ class DB {
const adapter = new SQLiteAdapter({ const adapter = new SQLiteAdapter({
dbName, dbName,
schema: appSchema schema: appSchema,
migrations
}); });
this.databases.activeDB = new Database({ this.databases.activeDB = new Database({

View File

@ -74,6 +74,8 @@ export default class Subscription extends Model {
@date('last_thread_sync') lastThreadSync; @date('last_thread_sync') lastThreadSync;
@date('jitsi_timeout') jitsiTimeout;
@field('auto_translate') autoTranslate; @field('auto_translate') autoTranslate;
@field('auto_translate_language') autoTranslateLanguage; @field('auto_translate_language') autoTranslateLanguage;

View File

@ -0,0 +1,17 @@
import { schemaMigrations, addColumns } from '@nozbe/watermelondb/Schema/migrations';
export default schemaMigrations({
migrations: [
{
toVersion: 2,
steps: [
addColumns({
table: 'subscriptions',
columns: [
{ name: 'jitsi_timeout', type: 'number', isOptional: true }
]
})
]
}
]
});

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb'; import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({ export default appSchema({
version: 1, version: 2,
tables: [ tables: [
tableSchema({ tableSchema({
name: 'subscriptions', name: 'subscriptions',
@ -36,6 +36,7 @@ export default appSchema({
{ name: 'prid', type: 'string', isOptional: true }, { name: 'prid', type: 'string', isOptional: true },
{ name: 'draft_message', type: 'string', isOptional: true }, { name: 'draft_message', type: 'string', isOptional: true },
{ name: 'last_thread_sync', type: 'number', isOptional: true }, { name: 'last_thread_sync', type: 'number', isOptional: true },
{ name: 'jitsi_timeout', type: 'number', isOptional: true },
{ name: 'auto_translate', type: 'boolean', isOptional: true }, { name: 'auto_translate', type: 'boolean', isOptional: true },
{ name: 'auto_translate_language', type: 'string' } { name: 'auto_translate_language', type: 'string' }
] ]

View File

@ -0,0 +1,53 @@
import JitsiMeet, { JitsiMeetEvents } from 'react-native-jitsi-meet';
import BackgroundTimer from 'react-native-background-timer';
import reduxStore from '../createStore';
let jitsiTimeout = null;
const jitsiBaseUrl = ({
Jitsi_Enabled, Jitsi_SSL, Jitsi_Domain, Jitsi_URL_Room_Prefix, uniqueID
}) => {
if (!Jitsi_Enabled) {
return '';
}
const uniqueIdentifier = uniqueID || 'undefined';
const domain = Jitsi_Domain;
const prefix = Jitsi_URL_Room_Prefix;
const urlProtocol = Jitsi_SSL ? 'https://' : 'http://';
const urlDomain = `${ domain }/`;
return `${ urlProtocol }${ urlDomain }${ prefix }${ uniqueIdentifier }`;
};
function callJitsi(rid, options = {}) {
const { settings } = reduxStore.getState();
// Jitsi Update Timeout needs to be called every 10 seconds to make sure
// call is not ended and is available to web users.
JitsiMeet.initialize();
const conferenceJoined = JitsiMeetEvents.addListener('CONFERENCE_JOINED', () => {
this.updateJitsiTimeout(rid).catch(e => console.log(e));
if (jitsiTimeout) {
BackgroundTimer.clearInterval(jitsiTimeout);
}
jitsiTimeout = BackgroundTimer.setInterval(() => {
this.updateJitsiTimeout(rid).catch(e => console.log(e));
}, 10000);
});
const conferenceLeft = JitsiMeetEvents.addListener('CONFERENCE_LEFT', () => {
if (jitsiTimeout) {
BackgroundTimer.clearInterval(jitsiTimeout);
}
if (conferenceJoined && conferenceJoined.remove) {
conferenceJoined.remove();
}
if (conferenceLeft && conferenceLeft.remove) {
conferenceLeft.remove();
}
});
JitsiMeet.call(`${ jitsiBaseUrl(settings) }${ rid }`, options);
}
export default callJitsi;

View File

@ -20,6 +20,7 @@ export const merge = (subscription, room) => {
subscription.reactWhenReadOnly = room.reactWhenReadOnly; subscription.reactWhenReadOnly = room.reactWhenReadOnly;
subscription.archived = room.archived || false; subscription.archived = room.archived || false;
subscription.joinCodeRequired = room.joinCodeRequired; subscription.joinCodeRequired = room.joinCodeRequired;
subscription.jitsiTimeout = room.jitsiTimeout;
} }
subscription.ro = room.ro; subscription.ro = room.ro;
subscription.broadcast = room.broadcast; subscription.broadcast = room.broadcast;

View File

@ -58,6 +58,7 @@ const createOrUpdateSubscription = async(subscription, room) => {
prid: s.prid, prid: s.prid,
draftMessage: s.draftMessage, draftMessage: s.draftMessage,
lastThreadSync: s.lastThreadSync, lastThreadSync: s.lastThreadSync,
jitsiTimeout: s.jitsiTimeout,
autoTranslate: s.autoTranslate, autoTranslate: s.autoTranslate,
autoTranslateLanguage: s.autoTranslateLanguage, autoTranslateLanguage: s.autoTranslateLanguage,
lastMessage: s.lastMessage lastMessage: s.lastMessage

View File

@ -42,6 +42,8 @@ import loadThreadMessages from './methods/loadThreadMessages';
import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage'; import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage';
import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage'; import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
import callJitsi from './methods/callJitsi';
import { getDeviceToken } from '../notifications/push'; import { getDeviceToken } from '../notifications/push';
import { SERVERS, SERVER_URL } from '../constants/credentials'; import { SERVERS, SERVER_URL } from '../constants/credentials';
import { setActiveUsers } from '../actions/activeUsers'; import { setActiveUsers } from '../actions/activeUsers';
@ -57,6 +59,7 @@ const STATUSES = ['offline', 'online', 'away', 'busy'];
const RocketChat = { const RocketChat = {
TOKEN_KEY, TOKEN_KEY,
callJitsi,
async subscribeRooms() { async subscribeRooms() {
if (this.roomsSub) { if (this.roomsSub) {
this.roomsSub.stop(); this.roomsSub.stop();
@ -246,6 +249,10 @@ const RocketChat = {
} }
}, },
updateJitsiTimeout(rid) {
return this.sdk.methodCall('jitsi:updateTimeout', rid);
},
register(credentials) { register(credentials) {
// RC 0.50.0 // RC 0.50.0
return this.sdk.post('users.register', credentials, false); return this.sdk.post('users.register', credentials, false);

View File

@ -16,6 +16,10 @@ const formatMsg = ({
if (!lastMessage || lastMessage.pinned) { if (!lastMessage || lastMessage.pinned) {
return I18n.t('No_Message'); return I18n.t('No_Message');
} }
if (lastMessage.t === 'jitsi_call_started') {
const { u } = lastMessage;
return I18n.t('Started_call', { userBy: u.username });
}
let prefix = ''; let prefix = '';
const isLastMessageSentByMe = lastMessage.u.username === username; const isLastMessageSentByMe = lastMessage.u.username === username;

View File

@ -11,6 +11,7 @@ import sharedStyles from './views/Styles';
import { isNotch } from './utils/deviceInfo'; import { isNotch } from './utils/deviceInfo';
import { defaultHeader, onNavigationStateChange } from './utils/navigation'; import { defaultHeader, onNavigationStateChange } from './utils/navigation';
import RocketChat from './lib/rocketchat'; import RocketChat from './lib/rocketchat';
import LayoutAnimation from './utils/layoutAnimation';
import { IDENTIFIER } from './constants/credentials'; import { IDENTIFIER } from './constants/credentials';
@ -84,12 +85,14 @@ class Root extends React.Component {
onLayout={this.handleLayout} onLayout={this.handleLayout}
> >
<Provider store={store}> <Provider store={store}>
<AppContainer <LayoutAnimation>
ref={(navigatorRef) => { <AppContainer
Navigation.setTopLevelNavigator(navigatorRef); ref={(navigatorRef) => {
}} Navigation.setTopLevelNavigator(navigatorRef);
onNavigationStateChange={onNavigationStateChange} }}
/> onNavigationStateChange={onNavigationStateChange}
/>
</LayoutAnimation>
</Provider> </Provider>
</View> </View>
); );

View File

@ -0,0 +1,44 @@
import React from 'react';
import { Transition, Transitioning } from 'react-native-reanimated';
import PropTypes from 'prop-types';
import debounce from './debounce';
import { isIOS } from './deviceInfo';
import sharedStyles from '../views/Styles';
const transition = (
<Transition.Together>
<Transition.In type='fade' />
<Transition.Out type='fade' />
<Transition.Change interpolation='easeInOut' />
</Transition.Together>
);
const TRANSITION_REF = React.createRef();
export const animateNextTransition = debounce(() => {
if (isIOS) {
TRANSITION_REF.current.animateNextTransition();
}
}, 200, true);
const LayoutAnimation = ({ children }) => {
if (isIOS) {
return (
<Transitioning.View
style={sharedStyles.root}
transition={transition}
ref={TRANSITION_REF}
>
{children}
</Transitioning.View>
);
}
return children;
};
LayoutAnimation.propTypes = {
children: PropTypes.node
};
export default LayoutAnimation;

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Keyboard, Text, ScrollView, View, StyleSheet, Alert, LayoutAnimation Keyboard, Text, ScrollView, View, StyleSheet, Alert
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
@ -18,6 +18,7 @@ import { loginRequest as loginRequestAction } from '../actions/login';
import { LegalButton } from '../containers/HeaderButton'; import { LegalButton } from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { COLOR_PRIMARY } from '../constants/colors'; import { COLOR_PRIMARY } from '../constants/colors';
import { animateNextTransition } from '../utils/layoutAnimation';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
bottomContainer: { bottomContainer: {
@ -84,7 +85,7 @@ class LoginView extends React.Component {
this.setTitle(nextProps.Site_Name); this.setTitle(nextProps.Site_Name);
} else if (nextProps.failure && !equal(error, nextProps.error)) { } else if (nextProps.failure && !equal(error, nextProps.error)) {
if (nextProps.error && nextProps.error.error === 'totp-required') { if (nextProps.error && nextProps.error.error === 'totp-required') {
LayoutAnimation.easeInEaseOut(); animateNextTransition();
this.setState({ showTOTP: true }); this.setState({ showTOTP: true });
return; return;
} }

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Text, ScrollView, Keyboard, Image, StyleSheet, TouchableOpacity, View, Alert, LayoutAnimation Text, ScrollView, Keyboard, Image, StyleSheet, TouchableOpacity, View, Alert
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
@ -23,6 +23,7 @@ import { CustomIcon } from '../lib/Icons';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { COLOR_PRIMARY } from '../constants/colors'; import { COLOR_PRIMARY } from '../constants/colors';
import log from '../utils/log'; import log from '../utils/log';
import { animateNextTransition } from '../utils/layoutAnimation';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
image: { image: {
@ -195,7 +196,7 @@ class NewServerView extends React.Component {
uriToPath = uri => uri.replace('file://', ''); uriToPath = uri => uri.replace('file://', '');
saveCertificate = (certificate) => { saveCertificate = (certificate) => {
LayoutAnimation.easeInEaseOut(); animateNextTransition();
this.setState({ certificate }); this.setState({ certificate });
} }

View File

@ -36,7 +36,8 @@ class RoomActionsView extends React.Component {
id: PropTypes.string, id: PropTypes.string,
token: PropTypes.string token: PropTypes.string
}), }),
leaveRoom: PropTypes.func leaveRoom: PropTypes.func,
jitsiEnabled: PropTypes.bool
} }
constructor(props) { constructor(props) {
@ -164,6 +165,7 @@ class RoomActionsView extends React.Component {
const { const {
room, membersCount, canViewMembers, canAddUser, joined, canAutoTranslate room, membersCount, canViewMembers, canAddUser, joined, canAutoTranslate
} = this.state; } = this.state;
const { jitsiEnabled } = this.props;
const { const {
rid, t, blocker rid, t, blocker
} = room; } = room;
@ -176,6 +178,21 @@ class RoomActionsView extends React.Component {
testID: 'room-actions-notifications' testID: 'room-actions-notifications'
}; };
const jitsiActions = jitsiEnabled ? [
{
icon: 'livechat',
name: I18n.t('Voice_call'),
event: () => RocketChat.callJitsi(rid, { videoMuted: true }),
testID: 'room-actions-voice'
},
{
icon: 'video',
name: I18n.t('Video_call'),
event: () => RocketChat.callJitsi(rid),
testID: 'room-actions-video'
}
] : [];
const sections = [{ const sections = [{
data: [{ data: [{
icon: 'star', icon: 'star',
@ -187,20 +204,7 @@ class RoomActionsView extends React.Component {
}], }],
renderItem: this.renderRoomInfo renderItem: this.renderRoomInfo
}, { }, {
data: [ data: jitsiActions,
{
icon: 'livechat',
name: I18n.t('Voice_call'),
disabled: true,
testID: 'room-actions-voice'
},
{
icon: 'video',
name: I18n.t('Video_call'),
disabled: true,
testID: 'room-actions-video'
}
],
renderItem: this.renderItem renderItem: this.renderItem
}, { }, {
data: [ data: [
@ -477,7 +481,8 @@ const mapStateToProps = state => ({
id: state.login.user && state.login.user.id, id: state.login.user && state.login.user.id,
token: state.login.user && state.login.user.token token: state.login.user && state.login.user.token
}, },
baseUrl: state.settings.Site_Url || state.server ? state.server.server : '' baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
jitsiEnabled: state.settings.Jitsi_Enabled || false
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { import {
ActivityIndicator, FlatList, InteractionManager, LayoutAnimation ActivityIndicator, FlatList, InteractionManager
} from 'react-native'; } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
@ -15,6 +15,7 @@ import RocketChat from '../../lib/rocketchat';
import log from '../../utils/log'; import log from '../../utils/log';
import EmptyRoom from './EmptyRoom'; import EmptyRoom from './EmptyRoom';
import { isIOS } from '../../utils/deviceInfo'; import { isIOS } from '../../utils/deviceInfo';
import { animateNextTransition } from '../../utils/layoutAnimation';
export class List extends React.Component { export class List extends React.Component {
static propTypes = { static propTypes = {
@ -83,7 +84,7 @@ export class List extends React.Component {
} }
const messages = orderBy(data, ['ts'], ['desc']); const messages = orderBy(data, ['ts'], ['desc']);
if (this.mounted) { if (this.mounted) {
LayoutAnimation.easeInEaseOut(); animateNextTransition();
this.setState({ messages }); this.setState({ messages });
} else { } else {
this.state.messages = messages; this.state.messages = messages;
@ -186,11 +187,11 @@ export class List extends React.Component {
style={styles.list} style={styles.list}
inverted inverted
removeClippedSubviews={isIOS} removeClippedSubviews={isIOS}
initialNumToRender={7} // initialNumToRender={7}
onEndReached={this.onEndReached} onEndReached={this.onEndReached}
onEndReachedThreshold={5} // onEndReachedThreshold={5}
maxToRenderPerBatch={5} // maxToRenderPerBatch={5}
windowSize={10} // windowSize={10}
ListFooterComponent={this.renderFooter} ListFooterComponent={this.renderFooter}
{...scrollPersistTaps} {...scrollPersistTaps}
/> />

View File

@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { Text, View, InteractionManager } from 'react-native';
Text, View, InteractionManager, LayoutAnimation
} from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { RectButton } from 'react-native-gesture-handler'; import { RectButton } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
@ -39,6 +37,7 @@ import ReactionsModal from '../../containers/ReactionsModal';
import { LISTENER } from '../../containers/Toast'; import { LISTENER } from '../../containers/Toast';
import { isReadOnly, isBlocked } from '../../utils/room'; import { isReadOnly, isBlocked } from '../../utils/room';
import { isIOS } from '../../utils/deviceInfo'; import { isIOS } from '../../utils/deviceInfo';
import { showErrorAlert } from '../../utils/info';
const stateAttrsUpdate = [ const stateAttrsUpdate = [
'joined', 'joined',
@ -53,7 +52,7 @@ const stateAttrsUpdate = [
'replying', 'replying',
'reacting' 'reacting'
]; ];
const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted']; const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted', 'jitsiTimeout'];
class RoomView extends React.Component { class RoomView extends React.Component {
static navigationOptions = ({ navigation }) => { static navigationOptions = ({ navigation }) => {
@ -483,15 +482,11 @@ class RoomView extends React.Component {
if (!this.mounted) { if (!this.mounted) {
return; return;
} }
if (isIOS && this.beginAnimating) {
LayoutAnimation.easeInEaseOut();
}
this.setState(...args); this.setState(...args);
} }
sendMessage = (message, tmid) => { sendMessage = (message, tmid) => {
const { user } = this.props; const { user } = this.props;
LayoutAnimation.easeInEaseOut();
RocketChat.sendMessage(this.rid, message, this.tmid || tmid, user).then(() => { RocketChat.sendMessage(this.rid, message, this.tmid || tmid, user).then(() => {
this.setLastOpen(null); this.setLastOpen(null);
}); });
@ -603,6 +598,16 @@ class RoomView extends React.Component {
navigation.navigate('RoomInfoView', navParam); navigation.navigate('RoomInfoView', navParam);
} }
callJitsi = () => {
const { room } = this.state;
const { jitsiTimeout } = room;
if (jitsiTimeout < Date.now()) {
showErrorAlert(I18n.t('Call_already_ended'));
} else {
RocketChat.callJitsi(this.rid, {});
}
};
get isReadOnly() { get isReadOnly() {
const { room } = this.state; const { room } = this.state;
const { user } = this.props; const { user } = this.props;
@ -658,6 +663,7 @@ class RoomView extends React.Component {
autoTranslateLanguage={room.autoTranslateLanguage} autoTranslateLanguage={room.autoTranslateLanguage}
navToRoomInfo={this.navToRoomInfo} navToRoomInfo={this.navToRoomInfo}
getCustomEmoji={this.getCustomEmoji} getCustomEmoji={this.getCustomEmoji}
callJitsi={this.callJitsi}
/> />
); );

View File

@ -8,7 +8,6 @@ import {
Text, Text,
ScrollView, ScrollView,
Keyboard, Keyboard,
LayoutAnimation,
Dimensions Dimensions
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -43,6 +42,7 @@ import {
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import ListHeader from './ListHeader'; import ListHeader from './ListHeader';
import { selectServerRequest as selectServerRequestAction } from '../../actions/server'; import { selectServerRequest as selectServerRequestAction } from '../../actions/server';
import { animateNextTransition } from '../../utils/layoutAnimation';
const SCROLL_OFFSET = 56; const SCROLL_OFFSET = 56;
@ -290,8 +290,8 @@ class RoomsListView extends React.Component {
// eslint-disable-next-line react/sort-comp // eslint-disable-next-line react/sort-comp
internalSetState = (...args) => { internalSetState = (...args) => {
const { navigation } = this.props; const { navigation } = this.props;
if (isIOS && navigation.isFocused()) { if (navigation.isFocused()) {
LayoutAnimation.easeInEaseOut(); animateNextTransition();
} }
this.setState(...args); this.setState(...args);
}; };

View File

@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { View, StyleSheet, FlatList } from 'react-native';
View, StyleSheet, FlatList, LayoutAnimation
} from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal'; import equal from 'deep-equal';
@ -25,6 +23,7 @@ import sharedStyles from './Styles';
import { Item, CustomHeaderButtons } from '../containers/HeaderButton'; import { Item, CustomHeaderButtons } from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar'; import StatusBar from '../containers/StatusBar';
import { COLOR_WHITE } from '../constants/colors'; import { COLOR_WHITE } from '../constants/colors';
import { animateNextTransition } from '../utils/layoutAnimation';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
safeAreaView: { safeAreaView: {
@ -169,7 +168,7 @@ class SelectedUsersView extends React.Component {
toggleUser = (user) => { toggleUser = (user) => {
const { addUser, removeUser } = this.props; const { addUser, removeUser } = this.props;
LayoutAnimation.easeInEaseOut(); animateNextTransition();
if (!this.isChecked(user.name)) { if (!this.isChecked(user.name)) {
addUser(user); addUser(user);
} else { } else {

View File

@ -1,8 +1,6 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { Keyboard, View, StyleSheet } from 'react-native';
Keyboard, LayoutAnimation, View, StyleSheet
} from 'react-native';
import ShareExtension from 'rn-extensions-share'; import ShareExtension from 'rn-extensions-share';
import SearchBox from '../../../containers/SearchBox'; import SearchBox from '../../../containers/SearchBox';
@ -10,6 +8,7 @@ import { CloseShareExtensionButton } from '../../../containers/HeaderButton';
import { HEADER_BACKGROUND } from '../../../constants/colors'; import { HEADER_BACKGROUND } from '../../../constants/colors';
import sharedStyles from '../../Styles'; import sharedStyles from '../../Styles';
import { animateNextTransition } from '../../../utils/layoutAnimation';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -33,12 +32,12 @@ const Header = React.memo(({
Keyboard.dismiss(); Keyboard.dismiss();
onChangeText(''); onChangeText('');
cancelSearch(); cancelSearch();
LayoutAnimation.easeInEaseOut(); animateNextTransition();
}; };
const onFocus = () => { const onFocus = () => {
initSearch(); initSearch();
LayoutAnimation.easeInEaseOut(); animateNextTransition();
}; };
return ( return (

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
View, Text, LayoutAnimation, FlatList, ActivityIndicator, Keyboard, BackHandler View, Text, FlatList, ActivityIndicator, Keyboard, BackHandler
} from 'react-native'; } from 'react-native';
import { SafeAreaView } from 'react-navigation'; import { SafeAreaView } from 'react-navigation';
import ShareExtension from 'rn-extensions-share'; import ShareExtension from 'rn-extensions-share';
@ -25,6 +25,7 @@ import ShareListHeader from './Header';
import styles from './styles'; import styles from './styles';
import StatusBar from '../../containers/StatusBar'; import StatusBar from '../../containers/StatusBar';
import { animateNextTransition } from '../../utils/layoutAnimation';
const LIMIT = 50; const LIMIT = 50;
const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index }); const getItemLayout = (data, index) => ({ length: ROW_HEIGHT, offset: ROW_HEIGHT * index, index });
@ -174,8 +175,8 @@ class ShareListView extends React.Component {
// eslint-disable-next-line react/sort-comp // eslint-disable-next-line react/sort-comp
internalSetState = (...args) => { internalSetState = (...args) => {
const { navigation } = this.props; const { navigation } = this.props;
if (isIOS && navigation.isFocused()) { if (navigation.isFocused()) {
LayoutAnimation.easeInEaseOut(); animateNextTransition();
} }
this.setState(...args); this.setState(...args);
} }

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
ScrollView, Text, View, FlatList, LayoutAnimation, SafeAreaView ScrollView, Text, View, FlatList, SafeAreaView
} from 'react-native'; } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import equal from 'deep-equal'; import equal from 'deep-equal';
@ -20,6 +20,7 @@ import styles from './styles';
import SidebarItem from './SidebarItem'; import SidebarItem from './SidebarItem';
import { COLOR_TEXT } from '../../constants/colors'; import { COLOR_TEXT } from '../../constants/colors';
import database from '../../lib/database'; import database from '../../lib/database';
import { animateNextTransition } from '../../utils/layoutAnimation';
const keyExtractor = item => item.id; const keyExtractor = item => item.id;
@ -147,7 +148,7 @@ class Sidebar extends Component {
} }
toggleStatus = () => { toggleStatus = () => {
LayoutAnimation.easeInEaseOut(); animateNextTransition();
this.setState(prevState => ({ showStatus: !prevState.showStatus })); this.setState(prevState => ({ showStatus: !prevState.showStatus }));
} }

View File

@ -5,6 +5,9 @@ import {
} from '../constants/colors'; } from '../constants/colors';
export default StyleSheet.create({ export default StyleSheet.create({
root: {
flex: 1
},
container: { container: {
backgroundColor: 'white', backgroundColor: 'white',
flex: 1 flex: 1

View File

@ -174,18 +174,20 @@ describe('Room actions screen', () => {
describe('Usage', async() => { describe('Usage', async() => {
describe('TDB', async() => { describe('TDB', async() => {
it('should NOT navigate to voice call', async() => { // TODO: test into a jitsi call
await waitFor(element(by.id('room-actions-voice'))).toBeVisible().whileElement(by.id('room-actions-list')).scroll(scrollDown, 'up'); // it('should NOT navigate to voice call', async() => {
await element(by.id('room-actions-voice')).tap(); // await waitFor(element(by.id('room-actions-voice'))).toBeVisible().whileElement(by.id('room-actions-list')).scroll(scrollDown, 'up');
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000); // await element(by.id('room-actions-voice')).tap();
await expect(element(by.id('room-actions-view'))).toBeVisible(); // await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000);
}); // await expect(element(by.id('room-actions-view'))).toBeVisible();
// });
it('should NOT navigate to video call', async() => { // TODO: test into a jitsi call
await element(by.id('room-actions-video')).tap(); // it('should NOT navigate to video call', async() => {
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000); // await element(by.id('room-actions-video')).tap();
await expect(element(by.id('room-actions-view'))).toBeVisible(); // await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(2000);
}); // await expect(element(by.id('room-actions-view'))).toBeVisible();
// });
// TODO: test share room link // TODO: test share room link
// it('should NOT navigate to share room', async() => { // it('should NOT navigate to share room', async() => {

View File

@ -5,7 +5,7 @@ PODS:
- React - React
- BugsnagReactNative/Core (2.22.4): - BugsnagReactNative/Core (2.22.4):
- React - React
- Crashlytics (3.13.4): - Crashlytics (3.14.0):
- Fabric (~> 1.10.2) - Fabric (~> 1.10.2)
- DoubleConversion (1.1.6) - DoubleConversion (1.1.6)
- EXAppLoaderProvider (6.0.0) - EXAppLoaderProvider (6.0.0)
@ -26,24 +26,32 @@ PODS:
- EXWebBrowser (6.0.0): - EXWebBrowser (6.0.0):
- UMCore - UMCore
- Fabric (1.10.2) - Fabric (1.10.2)
- Firebase/Core (6.5.0): - Firebase/Core (6.8.1):
- Firebase/CoreOnly - Firebase/CoreOnly
- FirebaseAnalytics (= 6.0.4) - FirebaseAnalytics (= 6.1.1)
- Firebase/CoreOnly (6.5.0): - Firebase/CoreOnly (6.8.1):
- FirebaseCore (= 6.1.0) - FirebaseCore (= 6.2.3)
- FirebaseAnalytics (6.0.4): - FirebaseAnalytics (6.1.1):
- FirebaseCore (~> 6.1) - FirebaseCore (~> 6.2)
- FirebaseInstanceID (~> 4.2) - FirebaseInstanceID (~> 4.2)
- GoogleAppMeasurement (= 6.0.4) - GoogleAppMeasurement (= 6.1.1)
- GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0) - GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)" - "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (~> 0.3) - nanopb (~> 0.3)
- FirebaseCore (6.1.0): - FirebaseCore (6.2.3):
- GoogleUtilities/Environment (~> 6.0) - FirebaseCoreDiagnostics (~> 1.0)
- GoogleUtilities/Logger (~> 6.0) - FirebaseCoreDiagnosticsInterop (~> 1.0)
- FirebaseInstanceID (4.2.2): - GoogleUtilities/Environment (~> 6.2)
- GoogleUtilities/Logger (~> 6.2)
- FirebaseCoreDiagnostics (1.0.1):
- FirebaseCoreDiagnosticsInterop (~> 1.0)
- GoogleDataTransportCCTSupport (~> 1.0)
- GoogleUtilities/Environment (~> 6.2)
- GoogleUtilities/Logger (~> 6.2)
- FirebaseCoreDiagnosticsInterop (1.0.0)
- FirebaseInstanceID (4.2.5):
- FirebaseCore (~> 6.0) - FirebaseCore (~> 6.0)
- GoogleUtilities/Environment (~> 6.0) - GoogleUtilities/Environment (~> 6.0)
- GoogleUtilities/UserDefaults (~> 6.0) - GoogleUtilities/UserDefaults (~> 6.0)
@ -57,54 +65,43 @@ PODS:
- DoubleConversion - DoubleConversion
- glog - glog
- glog (0.3.5) - glog (0.3.5)
- GoogleAppMeasurement (6.0.4): - GoogleAppMeasurement (6.1.1):
- GoogleUtilities/AppDelegateSwizzler (~> 6.0) - GoogleUtilities/AppDelegateSwizzler (~> 6.0)
- GoogleUtilities/MethodSwizzler (~> 6.0) - GoogleUtilities/MethodSwizzler (~> 6.0)
- GoogleUtilities/Network (~> 6.0) - GoogleUtilities/Network (~> 6.0)
- "GoogleUtilities/NSData+zlib (~> 6.0)" - "GoogleUtilities/NSData+zlib (~> 6.0)"
- nanopb (~> 0.3) - nanopb (~> 0.3)
- GoogleUtilities/AppDelegateSwizzler (6.2.3): - GoogleDataTransport (1.2.0)
- GoogleDataTransportCCTSupport (1.0.4):
- GoogleDataTransport (~> 1.2)
- nanopb
- GoogleUtilities/AppDelegateSwizzler (6.3.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Network - GoogleUtilities/Network
- GoogleUtilities/Environment (6.2.3) - GoogleUtilities/Environment (6.3.0)
- GoogleUtilities/Logger (6.2.3): - GoogleUtilities/Logger (6.3.0):
- GoogleUtilities/Environment - GoogleUtilities/Environment
- GoogleUtilities/MethodSwizzler (6.2.3): - GoogleUtilities/MethodSwizzler (6.3.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/Network (6.2.3): - GoogleUtilities/Network (6.3.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib" - "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Reachability - GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (6.2.3)" - "GoogleUtilities/NSData+zlib (6.3.0)"
- GoogleUtilities/Reachability (6.2.3): - GoogleUtilities/Reachability (6.3.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- GoogleUtilities/UserDefaults (6.2.3): - GoogleUtilities/UserDefaults (6.3.0):
- GoogleUtilities/Logger - GoogleUtilities/Logger
- libwebp (1.0.2): - libwebp (1.0.3):
- libwebp/core (= 1.0.2) - libwebp/demux (= 1.0.3)
- libwebp/dec (= 1.0.2) - libwebp/mux (= 1.0.3)
- libwebp/demux (= 1.0.2) - libwebp/webp (= 1.0.3)
- libwebp/dsp (= 1.0.2) - libwebp/demux (1.0.3):
- libwebp/enc (= 1.0.2)
- libwebp/mux (= 1.0.2)
- libwebp/utils (= 1.0.2)
- libwebp/webp (= 1.0.2)
- libwebp/core (1.0.2):
- libwebp/webp - libwebp/webp
- libwebp/dec (1.0.2): - libwebp/mux (1.0.3):
- libwebp/core - libwebp/demux
- libwebp/demux (1.0.2): - libwebp/webp (1.0.3)
- libwebp/core
- libwebp/dsp (1.0.2):
- libwebp/core
- libwebp/enc (1.0.2):
- libwebp/core
- libwebp/mux (1.0.2):
- libwebp/core
- libwebp/utils (1.0.2):
- libwebp/core
- libwebp/webp (1.0.2)
- nanopb (0.3.901): - nanopb (0.3.901):
- nanopb/decode (= 0.3.901) - nanopb/decode (= 0.3.901)
- nanopb/encode (= 0.3.901) - nanopb/encode (= 0.3.901)
@ -157,6 +154,8 @@ PODS:
- React-cxxreact (= 0.60.4) - React-cxxreact (= 0.60.4)
- React-jsi (= 0.60.4) - React-jsi (= 0.60.4)
- React-jsinspector (0.60.4) - React-jsinspector (0.60.4)
- react-native-background-timer (2.1.1):
- React
- react-native-document-picker (3.2.4): - react-native-document-picker (3.2.4):
- React - React
- react-native-keyboard-input (5.3.1): - react-native-keyboard-input (5.3.1):
@ -239,10 +238,10 @@ PODS:
- RNVectorIcons (6.6.0): - RNVectorIcons (6.6.0):
- React - React
- RSKImageCropper (2.2.3) - RSKImageCropper (2.2.3)
- SDWebImage (5.0.6): - SDWebImage (5.1.1):
- SDWebImage/Core (= 5.0.6) - SDWebImage/Core (= 5.1.1)
- SDWebImage/Core (5.0.6) - SDWebImage/Core (5.1.1)
- SDWebImageWebPCoder (0.2.3): - SDWebImageWebPCoder (0.2.4):
- libwebp (~> 1.0) - libwebp (~> 1.0)
- SDWebImage/Core (~> 5.0) - SDWebImage/Core (~> 5.0)
- UMBarCodeScannerInterface (3.0.0) - UMBarCodeScannerInterface (3.0.0)
@ -282,6 +281,7 @@ DEPENDENCIES:
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`) - react-native-document-picker (from `../node_modules/react-native-document-picker`)
- react-native-keyboard-input (from `../node_modules/react-native-keyboard-input`) - react-native-keyboard-input (from `../node_modules/react-native-keyboard-input`)
- react-native-keyboard-tracking-view (from `../node_modules/react-native-keyboard-tracking-view`) - react-native-keyboard-tracking-view (from `../node_modules/react-native-keyboard-tracking-view`)
@ -335,8 +335,12 @@ SPEC REPOS:
- Firebase - Firebase
- FirebaseAnalytics - FirebaseAnalytics
- FirebaseCore - FirebaseCore
- FirebaseCoreDiagnostics
- FirebaseCoreDiagnosticsInterop
- FirebaseInstanceID - FirebaseInstanceID
- GoogleAppMeasurement - GoogleAppMeasurement
- GoogleDataTransport
- GoogleDataTransportCCTSupport
- GoogleUtilities - GoogleUtilities
- libwebp - libwebp
- nanopb - nanopb
@ -391,6 +395,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsiexecutor" :path: "../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector: React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector" :path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-background-timer:
:path: "../node_modules/react-native-background-timer"
react-native-document-picker: react-native-document-picker:
:path: "../node_modules/react-native-document-picker" :path: "../node_modules/react-native-document-picker"
react-native-keyboard-input: react-native-keyboard-input:
@ -495,7 +501,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS: SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
BugsnagReactNative: 2114356c3acac0a71fb4b8962d3d1afdeb35f4d9 BugsnagReactNative: 2114356c3acac0a71fb4b8962d3d1afdeb35f4d9
Crashlytics: 2dfd686bcb918dc10ee0e76f7f853fe42c7bd552 Crashlytics: 540b7e5f5da5a042647227a5e3ac51d85eed06df
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2 DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
EXAppLoaderProvider: 7a8185228d8ba9e689a0e2d6d957fe9bdd49c8a0 EXAppLoaderProvider: 7a8185228d8ba9e689a0e2d6d957fe9bdd49c8a0
EXAV: 7228890721d1d74779bc3154fb678a44249b1c71 EXAV: 7228890721d1d74779bc3154fb678a44249b1c71
@ -505,15 +511,19 @@ SPEC CHECKSUMS:
EXPermissions: 99e52dc3e5f8e55153f1958004f6df2a30a1f2f5 EXPermissions: 99e52dc3e5f8e55153f1958004f6df2a30a1f2f5
EXWebBrowser: def838b95aa9d396f9ce71ace4e614ee16e7ee30 EXWebBrowser: def838b95aa9d396f9ce71ace4e614ee16e7ee30
Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74 Fabric: 706c8b8098fff96c33c0db69cbf81f9c551d0d74
Firebase: dedc9e48ea3f3649ad5f6b982f8a0c73508a14b5 Firebase: 9cbe4e5b5eaafa05dc932be58b7c8c3820d71e88
FirebaseAnalytics: 3fb375bc9d13779add4039716f868d233a473fad FirebaseAnalytics: 843c7f64a8f9c79f0d03281197ebe7bb1d58d477
FirebaseCore: aecf02fb2274ec361b9bebeac112f5daa18273bd FirebaseCore: e9d9bd1dae61c1e82bc1e0e617a9d832392086a0
FirebaseInstanceID: 662b8108a09fe9ed01aafdedba100fde8536b0f6 FirebaseCoreDiagnostics: 4c04ae09d0ab027c30179828c6bb47764df1bd13
FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb
FirebaseInstanceID: 550df9be1f99f751d8fcde3ac342a1e21a0e6c42
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51 Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28 glog: 1f3da668190260b06b429bb211bfbee5cd790c28
GoogleAppMeasurement: 183bd916af7f80deb67c01888368f1108d641832 GoogleAppMeasurement: 86a82f0e1f20b8eedf8e20326530138fd71409de
GoogleUtilities: d2b0e277a95962e09bb27f5cd42f5f0b6a506c7d GoogleDataTransport: 8f9897b8e073687f24ca8d3c3a8013dec7d2d1cc
libwebp: b068a3bd7c45f7460f6715be7bed1a18fd5d6b48 GoogleDataTransportCCTSupport: 7455d07b98851aa63e4c05a34dad356ca588479e
GoogleUtilities: 9c2c544202301110b29f7974a82e77fdcf12bf51
libwebp: 057912d6d0abfb6357d8bb05c0ea470301f5d61e
nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48 nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48
QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022 QBImagePickerController: d54cf93db6decf26baf6ed3472f336ef35cae022
React: ff7ee2ae5ee1c1d9ae2183b4111045b25294bb01 React: ff7ee2ae5ee1c1d9ae2183b4111045b25294bb01
@ -524,6 +534,7 @@ SPEC CHECKSUMS:
React-jsi: 21d3153b1153fbf6510a92b6b11e33e725cb7432 React-jsi: 21d3153b1153fbf6510a92b6b11e33e725cb7432
React-jsiexecutor: 7549641e48bafae7bfee3f3ea19bf4901639c5de React-jsiexecutor: 7549641e48bafae7bfee3f3ea19bf4901639c5de
React-jsinspector: 73f24a02fa684ed6a2b828ba116874a2191ded88 React-jsinspector: 73f24a02fa684ed6a2b828ba116874a2191ded88
react-native-background-timer: 1b6e6b4e10f1b74c367a1fdc3c72b67c619b222b
react-native-document-picker: c36bf5f067a581657ecaf7124dcd921a8be19061 react-native-document-picker: c36bf5f067a581657ecaf7124dcd921a8be19061
react-native-keyboard-input: 2a01e0aceac330592bbe9b3101761bb9d8e6d1fb react-native-keyboard-input: 2a01e0aceac330592bbe9b3101761bb9d8e6d1fb
react-native-keyboard-tracking-view: 1ebd24a2b6ca2314549aa51775995678094bffa1 react-native-keyboard-tracking-view: 1ebd24a2b6ca2314549aa51775995678094bffa1
@ -556,8 +567,8 @@ SPEC CHECKSUMS:
RNUserDefaults: 8a4928443510aa99e4ccb3b53f1bf186593d690b RNUserDefaults: 8a4928443510aa99e4ccb3b53f1bf186593d690b
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4 RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
RSKImageCropper: a446db0e8444a036b34f3c43db01b2373baa4b2a RSKImageCropper: a446db0e8444a036b34f3c43db01b2373baa4b2a
SDWebImage: 920f1a2ff1ca8296ad34f6e0510a1ef1d70ac965 SDWebImage: 96d7f03415ccb28d299d765f93557ff8a617abd8
SDWebImageWebPCoder: 7568737603c50f6237850afedd7e9e28e5917e6b SDWebImageWebPCoder: cc72085bb20368b2f8dbb21b7e355c667e1309b7
UMBarCodeScannerInterface: 84ea2d6b58ff0dc27ef9b68bab71286be18ee020 UMBarCodeScannerInterface: 84ea2d6b58ff0dc27ef9b68bab71286be18ee020
UMCameraInterface: 26b26005d1756a0d5f4f04f1e168e39ea9154535 UMCameraInterface: 26b26005d1756a0d5f4f04f1e168e39ea9154535
UMConstantsInterface: 038bacb19de12b6fd328c589122c8dc977cccf61 UMConstantsInterface: 038bacb19de12b6fd328c589122c8dc977cccf61

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,9 +0,0 @@
framework module FirebaseCoreDiagnostics {
export *
module * { export * }
link "z"
link framework "Foundation"
link framework "Security"
link framework "SystemConfiguration"
link framework "UIKit"
}

View File

@ -14,33 +14,45 @@
#include <sys/utsname.h> #include <sys/utsname.h>
#if __has_include(<UIKit/UIKit.h>)
#import <UIKit/UIKit.h>
#endif
#if __has_include(<AppKit/AppKit.h>)
#import <AppKit/AppKit.h>
#endif
#import "FIRApp.h" #import "FIRApp.h"
#import "Private/FIRAnalyticsConfiguration.h" #import "Private/FIRAnalyticsConfiguration.h"
#import "Private/FIRAppInternal.h" #import "Private/FIRAppInternal.h"
#import "Private/FIRBundleUtil.h" #import "Private/FIRBundleUtil.h"
#import "Private/FIRComponentContainerInternal.h" #import "Private/FIRComponentContainerInternal.h"
#import "Private/FIRConfigurationInternal.h" #import "Private/FIRConfigurationInternal.h"
#import "Private/FIRCoreDiagnosticsConnector.h"
#import "Private/FIRLibrary.h" #import "Private/FIRLibrary.h"
#import "Private/FIRLogger.h" #import "Private/FIRLogger.h"
#import "Private/FIROptionsInternal.h" #import "Private/FIROptionsInternal.h"
NSString *const kFIRServiceAdMob = @"AdMob"; // The kFIRService strings are only here while transitioning CoreDiagnostics from the Analytics
NSString *const kFIRServiceAuth = @"Auth"; // pod to a Core dependency. These symbols are not used and should be deleted after the transition.
NSString *const kFIRServiceAuthUI = @"AuthUI"; NSString *const kFIRServiceAdMob;
NSString *const kFIRServiceCrash = @"Crash"; NSString *const kFIRServiceAuth;
NSString *const kFIRServiceDatabase = @"Database"; NSString *const kFIRServiceAuthUI;
NSString *const kFIRServiceDynamicLinks = @"DynamicLinks"; NSString *const kFIRServiceCrash;
NSString *const kFIRServiceFirestore = @"Firestore"; NSString *const kFIRServiceDatabase;
NSString *const kFIRServiceFunctions = @"Functions"; NSString *const kFIRServiceDynamicLinks;
NSString *const kFIRServiceInstanceID = @"InstanceID"; NSString *const kFIRServiceFirestore;
NSString *const kFIRServiceInvites = @"Invites"; NSString *const kFIRServiceFunctions;
NSString *const kFIRServiceMessaging = @"Messaging"; NSString *const kFIRServiceInstanceID;
NSString *const kFIRServiceMeasurement = @"Measurement"; NSString *const kFIRServiceInvites;
NSString *const kFIRServicePerformance = @"Performance"; NSString *const kFIRServiceMessaging;
NSString *const kFIRServiceRemoteConfig = @"RemoteConfig"; NSString *const kFIRServiceMeasurement;
NSString *const kFIRServiceStorage = @"Storage"; NSString *const kFIRServicePerformance;
NSString *const kGGLServiceAnalytics = @"Analytics"; NSString *const kFIRServiceRemoteConfig;
NSString *const kGGLServiceSignIn = @"SignIn"; NSString *const kFIRServiceStorage;
NSString *const kGGLServiceAnalytics;
NSString *const kGGLServiceSignIn;
NSString *const kFIRDefaultAppName = @"__FIRAPP_DEFAULT"; NSString *const kFIRDefaultAppName = @"__FIRAPP_DEFAULT";
NSString *const kFIRAppReadyToConfigureSDKNotification = @"FIRAppReadyToConfigureSDKNotification"; NSString *const kFIRAppReadyToConfigureSDKNotification = @"FIRAppReadyToConfigureSDKNotification";
@ -103,19 +115,6 @@ static NSMutableDictionary *sLibraryVersions;
+ (void)configure { + (void)configure {
FIROptions *options = [FIROptions defaultOptions]; FIROptions *options = [FIROptions defaultOptions];
if (!options) { if (!options) {
// Read the Info.plist to see if the flag is set. At this point we can't check any user defaults
// since the app isn't configured at all, so only rely on the Info.plist value.
NSNumber *collectionEnabledPlistValue = [[self class] readDataCollectionSwitchFromPlist];
if (collectionEnabledPlistValue == nil || [collectionEnabledPlistValue boolValue]) {
[[NSNotificationCenter defaultCenter]
postNotificationName:kFIRAppDiagnosticsNotification
object:nil
userInfo:@{
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
kFIRAppDiagnosticsErrorKey : [FIRApp errorForMissingOptions]
}];
}
[NSException raise:kFirebaseCoreErrorDomain [NSException raise:kFirebaseCoreErrorDomain
format:@"`[FIRApp configure];` (`FirebaseApp.configure()` in Swift) could not find " format:@"`[FIRApp configure];` (`FirebaseApp.configure()` in Swift) could not find "
@"a valid GoogleService-Info.plist in your project. Please download one " @"a valid GoogleService-Info.plist in your project. Please download one "
@ -290,30 +289,17 @@ static NSMutableDictionary *sLibraryVersions;
return self; return self;
} }
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (BOOL)configureCore { - (BOOL)configureCore {
[self checkExpectedBundleID]; [self checkExpectedBundleID];
if (![self isAppIDValid]) { if (![self isAppIDValid]) {
if (_options.usingOptionsFromDefaultPlist && [self isDataCollectionDefaultEnabled]) {
[[NSNotificationCenter defaultCenter]
postNotificationName:kFIRAppDiagnosticsNotification
object:nil
userInfo:@{
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
kFIRAppDiagnosticsErrorKey : [FIRApp errorForInvalidAppID],
}];
}
return NO; return NO;
} }
if ([self isDataCollectionDefaultEnabled]) { [self logCoreTelemetryIfEnabled];
[[NSNotificationCenter defaultCenter]
postNotificationName:kFIRAppDiagnosticsNotification
object:nil
userInfo:@{
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeCore),
kFIRAppDiagnosticsFIRAppKey : self
}];
}
#if TARGET_OS_IOS #if TARGET_OS_IOS
// Initialize the Analytics once there is a valid options under default app. Analytics should // Initialize the Analytics once there is a valid options under default app. Analytics should
@ -338,6 +324,8 @@ static NSMutableDictionary *sLibraryVersions;
} }
#endif #endif
[self subscribeForAppDidBecomeActiveNotifications];
return YES; return YES;
} }
@ -809,26 +797,40 @@ static NSMutableDictionary *sLibraryVersions;
#pragma mark - Sending Logs #pragma mark - Sending Logs
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
- (void)sendLogsWithServiceName:(NSString *)serviceName - (void)sendLogsWithServiceName:(NSString *)serviceName
version:(NSString *)version version:(NSString *)version
error:(NSError *)error { error:(NSError *)error {
// If the user has manually turned off data collection, return and don't send logs. // Do nothing. Please remove calls to this method.
if (![self isDataCollectionDefaultEnabled]) { }
return; #pragma clang diagnostic pop
}
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] initWithDictionary:@{ #pragma mark - App Life Cycle
kFIRAppDiagnosticsConfigurationTypeKey : @(FIRConfigTypeSDK),
kFIRAppDiagnosticsSDKNameKey : serviceName, - (void)subscribeForAppDidBecomeActiveNotifications {
kFIRAppDiagnosticsSDKVersionKey : version, #if TARGET_OS_IOS || TARGET_OS_TV
kFIRAppDiagnosticsFIRAppKey : self NSNotificationName notificationName = UIApplicationDidBecomeActiveNotification;
}]; #endif
if (error) {
userInfo[kFIRAppDiagnosticsErrorKey] = error; #if TARGET_OS_OSX
NSNotificationName notificationName = NSApplicationDidBecomeActiveNotification;
#endif
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appDidBecomeActive:)
name:notificationName
object:nil];
}
- (void)appDidBecomeActive:(NSNotification *)notification {
[self logCoreTelemetryIfEnabled];
}
- (void)logCoreTelemetryIfEnabled {
if ([self isDataCollectionDefaultEnabled]) {
[FIRCoreDiagnosticsConnector logCoreTelemetryWithOptions:_options];
} }
[[NSNotificationCenter defaultCenter] postNotificationName:kFIRAppDiagnosticsNotification
object:nil
userInfo:userInfo];
} }
@end @end

View File

@ -23,7 +23,9 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@interface FIRComponentContainer () @interface FIRComponentContainer () {
dispatch_queue_t _containerQueue;
}
/// The dictionary of components that are registered for a particular app. The key is an NSString /// The dictionary of components that are registered for a particular app. The key is an NSString
/// of the protocol. /// of the protocol.
@ -67,6 +69,8 @@ static NSMutableSet<Class> *sFIRComponentRegistrants;
_app = app; _app = app;
_cachedInstances = [NSMutableDictionary<NSString *, id> dictionary]; _cachedInstances = [NSMutableDictionary<NSString *, id> dictionary];
_components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary]; _components = [NSMutableDictionary<NSString *, FIRComponentCreationBlock> dictionary];
_containerQueue =
dispatch_queue_create("com.google.FirebaseComponentContainer", DISPATCH_QUEUE_SERIAL);
[self populateComponentsFromRegisteredClasses:allRegistrants forApp:app]; [self populateComponentsFromRegisteredClasses:allRegistrants forApp:app];
} }
@ -92,7 +96,7 @@ static NSMutableSet<Class> *sFIRComponentRegistrants;
// Store the creation block for later usage. // Store the creation block for later usage.
self.components[protocolName] = component.creationBlock; self.components[protocolName] = component.creationBlock;
// Instantiate the // Instantiate the instance if it has requested to be instantiated.
BOOL shouldInstantiateEager = BOOL shouldInstantiateEager =
(component.instantiationTiming == FIRInstantiationTimingAlwaysEager); (component.instantiationTiming == FIRInstantiationTimingAlwaysEager);
BOOL shouldInstantiateDefaultEager = BOOL shouldInstantiateDefaultEager =
@ -136,7 +140,9 @@ static NSMutableSet<Class> *sFIRComponentRegistrants;
// The instance is ready to be returned, but check if it should be cached first before returning. // The instance is ready to be returned, but check if it should be cached first before returning.
if (shouldCache) { if (shouldCache) {
self.cachedInstances[protocolName] = instance; dispatch_sync(_containerQueue, ^{
self.cachedInstances[protocolName] = instance;
});
} }
return instance; return instance;
@ -147,7 +153,11 @@ static NSMutableSet<Class> *sFIRComponentRegistrants;
- (nullable id)instanceForProtocol:(Protocol *)protocol { - (nullable id)instanceForProtocol:(Protocol *)protocol {
// Check if there is a cached instance, and return it if so. // Check if there is a cached instance, and return it if so.
NSString *protocolName = NSStringFromProtocol(protocol); NSString *protocolName = NSStringFromProtocol(protocol);
id cachedInstance = self.cachedInstances[protocolName]; __block id cachedInstance;
dispatch_sync(_containerQueue, ^{
cachedInstance = self.cachedInstances[protocolName];
});
if (cachedInstance) { if (cachedInstance) {
return cachedInstance; return cachedInstance;
} }
@ -161,14 +171,29 @@ static NSMutableSet<Class> *sFIRComponentRegistrants;
- (void)removeAllCachedInstances { - (void)removeAllCachedInstances {
// Loop through the cache and notify each instance that is a maintainer to clean up after itself. // Loop through the cache and notify each instance that is a maintainer to clean up after itself.
for (id instance in self.cachedInstances.allValues) { // Design note: we're getting a copy here, unlocking the cached instances, iterating over the
// copy, then locking and removing all cached instances. A race condition *could* exist where a
// new cached instance is created between the copy and the removal, but the chances are slim and
// side-effects are significantly smaller than including the entire loop in the `dispatch_sync`
// block (access to the cache from inside the block would deadlock and crash).
__block NSDictionary<NSString *, id> *instancesCopy;
dispatch_sync(_containerQueue, ^{
instancesCopy = [self.cachedInstances copy];
});
for (id instance in instancesCopy.allValues) {
if ([instance conformsToProtocol:@protocol(FIRComponentLifecycleMaintainer)] && if ([instance conformsToProtocol:@protocol(FIRComponentLifecycleMaintainer)] &&
[instance respondsToSelector:@selector(appWillBeDeleted:)]) { [instance respondsToSelector:@selector(appWillBeDeleted:)]) {
[instance appWillBeDeleted:self.app]; [instance appWillBeDeleted:self.app];
} }
} }
[self.cachedInstances removeAllObjects]; instancesCopy = nil;
// Empty the cache.
dispatch_sync(_containerQueue, ^{
[self.cachedInstances removeAllObjects];
});
} }
@end @end

View File

@ -0,0 +1,61 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRCoreDiagnosticsConnector.h"
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsInterop.h>
#import <FirebaseCore/FIROptions.h>
#import "Private/FIRAppInternal.h"
#import "Private/FIRDiagnosticsData.h"
#import "Private/FIROptionsInternal.h"
// Define the interop class symbol declared as an extern in FIRCoreDiagnosticsInterop.
Class<FIRCoreDiagnosticsInterop> FIRCoreDiagnosticsImplementation;
@implementation FIRCoreDiagnosticsConnector
+ (void)initialize {
if (!FIRCoreDiagnosticsImplementation) {
FIRCoreDiagnosticsImplementation = NSClassFromString(@"FIRCoreDiagnostics");
if (FIRCoreDiagnosticsImplementation) {
NSAssert([FIRCoreDiagnosticsImplementation
conformsToProtocol:@protocol(FIRCoreDiagnosticsInterop)],
@"If FIRCoreDiagnostics is implemented, it must conform to the interop protocol.");
NSAssert(
[FIRCoreDiagnosticsImplementation respondsToSelector:@selector(sendDiagnosticsData:)],
@"If FIRCoreDiagnostics is implemented, it must implement +sendDiagnosticsData.");
}
}
}
+ (void)logCoreTelemetryWithOptions:(FIROptions *)options {
if (FIRCoreDiagnosticsImplementation) {
FIRDiagnosticsData *diagnosticsData = [[FIRDiagnosticsData alloc] init];
[diagnosticsData insertValue:@(YES) forKey:kFIRCDIsDataCollectionDefaultEnabledKey];
[diagnosticsData insertValue:[FIRApp firebaseUserAgent] forKey:kFIRCDFirebaseUserAgentKey];
[diagnosticsData insertValue:@(FIRConfigTypeCore) forKey:kFIRCDConfigurationTypeKey];
[diagnosticsData insertValue:options.googleAppID forKey:kFIRCDGoogleAppIDKey];
[diagnosticsData insertValue:options.bundleID forKey:kFIRCDBundleIDKey];
[diagnosticsData insertValue:@(options.usingOptionsFromDefaultPlist)
forKey:kFIRCDUsingOptionsFromDefaultPlistKey];
[diagnosticsData insertValue:options.libraryVersionID forKey:kFIRCDLibraryVersionIDKey];
[FIRCoreDiagnosticsImplementation sendDiagnosticsData:diagnosticsData];
}
}
@end

View File

@ -0,0 +1,66 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "Private/FIRDiagnosticsData.h"
#import <FirebaseCore/FIRApp.h>
#import "Private/FIRAppInternal.h"
#import "Private/FIROptionsInternal.h"
@implementation FIRDiagnosticsData {
/** Backing ivar for the diagnosticObjects property. */
NSMutableDictionary<NSString *, id> *_diagnosticObjects;
}
- (instancetype)init {
self = [super init];
if (self) {
_diagnosticObjects = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)insertValue:(nullable id)value forKey:(NSString *)key {
if (key) {
_diagnosticObjects[key] = value;
}
}
#pragma mark - FIRCoreDiagnosticsData
- (NSDictionary<NSString *, id> *)diagnosticObjects {
if (!_diagnosticObjects[kFIRCDllAppsCountKey]) {
_diagnosticObjects[kFIRCDllAppsCountKey] = @([FIRApp allApps].count);
}
if (!_diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey]) {
_diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey] =
@([[FIRApp defaultApp] isDataCollectionDefaultEnabled]);
}
if (!_diagnosticObjects[kFIRCDFirebaseUserAgentKey]) {
_diagnosticObjects[kFIRCDFirebaseUserAgentKey] = [FIRApp firebaseUserAgent];
}
return _diagnosticObjects;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
- (void)setDiagnosticObjects:(NSDictionary<NSString *, id> *)diagnosticObjects {
NSAssert(NO, @"Please use -insertValue:forKey:");
}
#pragma clang diagnostic pop
@end

View File

@ -34,26 +34,6 @@ typedef NS_ENUM(NSInteger, FIRConfigType) {
FIRConfigTypeSDK = 2, FIRConfigTypeSDK = 2,
}; };
/**
* Names of services provided by Firebase.
*/
extern NSString *const kFIRServiceAdMob;
extern NSString *const kFIRServiceAuth;
extern NSString *const kFIRServiceAuthUI;
extern NSString *const kFIRServiceDatabase;
extern NSString *const kFIRServiceDynamicLinks;
extern NSString *const kFIRServiceInstanceID;
extern NSString *const kFIRServiceMessaging;
extern NSString *const kFIRServiceMeasurement;
extern NSString *const kFIRServiceRemoteConfig;
extern NSString *const kFIRServiceStorage;
/**
* Names of services provided by the Google pod, but logged by the Firebase pod.
*/
extern NSString *const kGGLServiceAnalytics;
extern NSString *const kGGLServiceSignIn;
extern NSString *const kFIRDefaultAppName; extern NSString *const kFIRDefaultAppName;
extern NSString *const kFIRAppReadyToConfigureSDKNotification; extern NSString *const kFIRAppReadyToConfigureSDKNotification;
extern NSString *const kFIRAppDeleteNotification; extern NSString *const kFIRAppDeleteNotification;
@ -160,6 +140,8 @@ extern NSString *const FIRAuthStateDidChangeInternalNotificationUIDKey;
/** /**
* Used by each SDK to send logs about SDK configuration status to Clearcut. * Used by each SDK to send logs about SDK configuration status to Clearcut.
*
* @note This API is a no-op, please remove calls to it.
*/ */
- (void)sendLogsWithServiceName:(NSString *)serviceName - (void)sendLogsWithServiceName:(NSString *)serviceName
version:(NSString *)version version:(NSString *)version

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
@class FIRDiagnosticsData;
@class FIROptions;
NS_ASSUME_NONNULL_BEGIN
/** Connects FIRCore with the CoreDiagnostics library. */
@interface FIRCoreDiagnosticsConnector : NSObject
/** Logs FirebaseCore related data.
*
* @param options The options object containing data to log.
*/
+ (void)logCoreTelemetryWithOptions:(FIROptions *)options;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,35 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsData.h>
NS_ASSUME_NONNULL_BEGIN
/** Implements the FIRCoreDiagnosticsData protocol to log diagnostics data. */
@interface FIRDiagnosticsData : NSObject <FIRCoreDiagnosticsData>
/** Inserts values into the diagnosticObjects dictionary if the value isn't nil.
*
* @param value The value to insert if it's not nil.
* @param key The key to associate it with.
*/
- (void)insertValue:(nullable id)value forKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,8 +1,8 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) # Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore, includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage. FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The The repository also includes GoogleUtilities source. The
@ -80,9 +80,8 @@ For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open` `pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See Firestore has a self contained Xcode project. See
[Firestore/README.md](Firestore/README.md) and [Firestore/README.md](Firestore/README.md).
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod ### Adding a New Firebase Pod
@ -179,7 +178,8 @@ very grateful! We'd like to empower as many developers as we can to be able to
participate in the Firebase community. participate in the Firebase community.
### macOS and tvOS ### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging, Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore,
FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS. macOS and tvOS.
@ -195,6 +195,7 @@ Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile: To install, add a subset of the following to the Podfile:
``` ```
pod 'FirebaseABTesting'
pod 'FirebaseAuth' pod 'FirebaseAuth'
pod 'FirebaseCore' pod 'FirebaseCore'
pod 'FirebaseDatabase' pod 'FirebaseDatabase'

View File

@ -0,0 +1,674 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <objc/runtime.h>
#include <sys/utsname.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <GoogleDataTransport/GDTEvent.h>
#import <GoogleDataTransport/GDTTargets.h>
#import <GoogleDataTransport/GDTTransport.h>
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
#import <GoogleUtilities/GULLogger.h>
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsData.h>
#import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsInterop.h>
#import <nanopb/pb.h>
#import <nanopb/pb_decode.h>
#import <nanopb/pb_encode.h>
#import "FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h"
#import "FIRCDLibrary/FIRCoreDiagnosticsDateFileStorage.h"
/** The logger service string to use when printing to the console. */
static GULLoggerService kFIRCoreDiagnostics = @"[FirebaseCoreDiagnostics/FIRCoreDiagnostics]";
#ifdef FIREBASE_BUILD_ZIP_FILE
static BOOL kUsingZipFile = YES;
#else // FIREBASE_BUILD_ZIP_FILE
static BOOL kUsingZipFile = NO;
#endif // FIREBASE_BUILD_ZIP_FILE
#ifdef FIREBASE_BUILD_CARTHAGE
#define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_CARTHAGE
#elif FIREBASE_BUILD_ZIP_FILE
#define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_ZIP_FILE
#else
#define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_COCOAPODS
#endif
static NSString *const kFIRServiceMLVisionOnDeviceAutoML = @"MLVisionOnDeviceAutoML";
static NSString *const kFIRServiceMLVisionOnDeviceFace = @"MLVisionOnDeviceFace";
static NSString *const kFIRServiceMLVisionOnDeviceBarcode = @"MLVisionOnDeviceBarcode";
static NSString *const kFIRServiceMLVisionOnDeviceText = @"MLVisionOnDeviceText";
static NSString *const kFIRServiceMLVisionOnDeviceLabel = @"MLVisionOnDeviceLabel";
static NSString *const kFIRServiceMLVisionOnDeviceObjectDetection =
@"MLVisionOnDeviceObjectDetection";
static NSString *const kFIRServiceMLModelInterpreter = @"MLModelInterpreter";
static NSString *const kFIRServiceAdMob = @"AdMob";
static NSString *const kFIRServiceAuth = @"Auth";
static NSString *const kFIRServiceAuthUI = @"AuthUI";
static NSString *const kFIRServiceCrash = @"Crash";
static NSString *const kFIRServiceDatabase = @"Database";
static NSString *const kFIRServiceDynamicLinks = @"DynamicLinks";
static NSString *const kFIRServiceFirestore = @"Firestore";
static NSString *const kFIRServiceFunctions = @"Functions";
static NSString *const kFIRServiceIAM = @"InAppMessaging";
static NSString *const kFIRServiceInstanceID = @"InstanceID";
static NSString *const kFIRServiceInvites = @"Invites";
static NSString *const kFIRServiceMessaging = @"Messaging";
static NSString *const kFIRServiceMeasurement = @"Measurement";
static NSString *const kFIRServicePerformance = @"Performance";
static NSString *const kFIRServiceRemoteConfig = @"RemoteConfig";
static NSString *const kFIRServiceStorage = @"Storage";
static NSString *const kGGLServiceAnalytics = @"Analytics";
static NSString *const kGGLServiceSignIn = @"SignIn";
static NSString *const kFIRAppDiagnosticsConfigurationTypeKey =
@"FIRAppDiagnosticsConfigurationTypeKey";
static NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRAppDiagnosticsFIRAppKey";
static NSString *const kFIRAppDiagnosticsSDKNameKey = @"FIRAppDiagnosticsSDKNameKey";
static NSString *const kFIRAppDiagnosticsSDKVersionKey = @"FIRAppDiagnosticsSDKVersionKey";
/**
* The file name to the recent heartbeat date.
*/
NSString *const kFIRCoreDiagnosticsHeartbeatDateFileName = @"FIREBASE_DIAGNOSTICS_HEARTBEAT_DATE";
/**
* @note This should implement the GDTEventDataObject protocol, but can't because of weak-linking.
*/
@interface FIRCoreDiagnosticsLog : NSObject
/** The config that will be converted to proto bytes. */
@property(nonatomic) logs_proto_mobilesdk_ios_ICoreConfiguration config;
@end
@implementation FIRCoreDiagnosticsLog
- (instancetype)initWithConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration)config {
self = [super init];
if (self) {
_config = config;
}
return self;
}
// Provided and required by the GDTEventDataObject protocol.
- (NSData *)transportBytes {
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
// Encode 1 time to determine the size.
if (!pb_encode(&sizestream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
GDTLogError(GDTMCETransportBytesError, @"Error in nanopb encoding for size: %s",
PB_GET_ERROR(&sizestream));
}
// Encode a 2nd time to actually get the bytes from it.
size_t bufferSize = sizestream.bytes_written;
CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
if (!pb_encode(&ostream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
GDTLogError(GDTMCETransportBytesError, @"Error in nanopb encoding for bytes: %s",
PB_GET_ERROR(&ostream));
}
CFDataSetLength(dataRef, ostream.bytes_written);
return CFBridgingRelease(dataRef);
}
- (void)dealloc {
pb_release(logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config);
}
@end
NS_ASSUME_NONNULL_BEGIN
/** This class produces a protobuf containing diagnostics and usage data to be logged. */
@interface FIRCoreDiagnostics : NSObject <FIRCoreDiagnosticsInterop>
/** The queue on which all diagnostics collection will occur. */
@property(nonatomic, readonly) dispatch_queue_t diagnosticsQueue;
/** The transport object used to send data. */
@property(nonatomic, readonly) GDTTransport *transport;
/** The storage to store the date of the last sent heartbeat. */
@property(nonatomic, readonly) FIRCoreDiagnosticsDateFileStorage *heartbeatDateStorage;
@end
NS_ASSUME_NONNULL_END
@implementation FIRCoreDiagnostics
+ (instancetype)sharedInstance {
static FIRCoreDiagnostics *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[FIRCoreDiagnostics alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
GDTTransport *transport = [[GDTTransport alloc] initWithMappingID:@"137"
transformers:nil
target:kGDTTargetCCT];
FIRCoreDiagnosticsDateFileStorage *dateStorage = [[FIRCoreDiagnosticsDateFileStorage alloc]
initWithFileURL:[[self class] filePathURLWithName:kFIRCoreDiagnosticsHeartbeatDateFileName]];
return [self initWithTransport:transport heartbeatDateStorage:dateStorage];
}
/** Initializer for unit tests.
*
* @param transport A `GDTTransport` instance which that be used to send event.
* @param heartbeatDateStorage An instanse of date storage to track heartbeat sending.
* @return Returns the initialized `FIRCoreDiagnostics` instance.
*/
- (instancetype)initWithTransport:(GDTTransport *)transport
heartbeatDateStorage:(FIRCoreDiagnosticsDateFileStorage *)heartbeatDateStorage {
self = [super init];
if (self) {
_diagnosticsQueue =
dispatch_queue_create("com.google.FIRCoreDiagnostics", DISPATCH_QUEUE_SERIAL);
_transport = transport;
_heartbeatDateStorage = heartbeatDateStorage;
}
return self;
}
#pragma mark - File path helpers
/** Returns the URL path of the file with name fileName under the Application Support folder for
* local logging. Creates the Application Support folder if the folder doesn't exist.
*
* @return the URL path of the file with the name fileName in Application Support.
*/
+ (NSURL *)filePathURLWithName:(NSString *)fileName {
@synchronized(self) {
NSArray<NSString *> *paths =
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSArray<NSString *> *components = @[ paths.lastObject, @"Google/FIRApp" ];
NSString *directoryString = [NSString pathWithComponents:components];
NSURL *directoryURL = [NSURL fileURLWithPath:directoryString];
NSError *error;
if (![directoryURL checkResourceIsReachableAndReturnError:&error]) {
// If fail creating the Application Support directory, return nil.
if (![[NSFileManager defaultManager] createDirectoryAtURL:directoryURL
withIntermediateDirectories:YES
attributes:nil
error:&error]) {
GULLogWarning(kFIRCoreDiagnostics, YES, @"I-COR100001",
@"Unable to create internal state storage: %@", error);
return nil;
}
}
return [directoryURL URLByAppendingPathComponent:fileName];
}
}
#pragma mark - Metadata helpers
/** Returns the model of iOS device. Sample platform strings are @"iPhone7,1" for iPhone 6 Plus,
* @"iPhone7,2" for iPhone 6, etc. Refer to the Hardware strings at
* https://en.wikipedia.org/wiki/List_of_iOS_devices
*
* @return The device model as an NSString.
*/
+ (NSString *)deviceModel {
static NSString *deviceModel = nil;
if (deviceModel == nil) {
struct utsname systemInfo;
uname(&systemInfo);
deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
}
return deviceModel;
}
#pragma mark - nanopb helper functions
/** Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
*
* @note Memory needs to be free manually, through pb_free or pb_release.
* @param string The string to encode as pb_bytes.
*/
pb_bytes_array_t *FIREncodeString(NSString *string) {
NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding];
return FIREncodeData(stringBytes);
}
/** Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
*
* @note Memory needs to be free manually, through pb_free or pb_release.
* @param data The data to copy into the new bytes array.
*/
pb_bytes_array_t *FIREncodeData(NSData *data) {
pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
memcpy(pbBytes->bytes, [data bytes], data.length);
pbBytes->size = (pb_size_t)data.length;
return pbBytes;
}
/** Maps a service string to the representative nanopb enum.
*
* @param serviceString The SDK service string to convert.
* @return The representative nanopb enum.
*/
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType FIRMapFromServiceStringToTypeEnum(
NSString *serviceString) {
static NSDictionary<NSString *, NSNumber *> *serviceStringToTypeEnum;
if (serviceStringToTypeEnum == nil) {
serviceStringToTypeEnum = @{
kFIRServiceAdMob : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ADMOB),
kFIRServiceMessaging : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MESSAGING),
kFIRServiceMeasurement :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MEASUREMENT),
kFIRServiceRemoteConfig :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_REMOTE_CONFIG),
kFIRServiceDatabase : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DATABASE),
kFIRServiceDynamicLinks :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DYNAMIC_LINKS),
kFIRServiceAuth : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH),
kFIRServiceAuthUI : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH_UI),
kFIRServiceFirestore : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FIRESTORE),
kFIRServiceFunctions : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FUNCTIONS),
kFIRServicePerformance :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_PERFORMANCE),
kFIRServiceStorage : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_STORAGE),
kFIRServiceMLVisionOnDeviceAutoML :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_AUTOML),
kFIRServiceMLVisionOnDeviceFace :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_FACE),
kFIRServiceMLVisionOnDeviceBarcode :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_BARCODE),
kFIRServiceMLVisionOnDeviceText :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_TEXT),
kFIRServiceMLVisionOnDeviceLabel :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_LABEL),
kFIRServiceMLVisionOnDeviceObjectDetection : @(
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION),
kFIRServiceMLModelInterpreter :
@(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_MODEL_INTERPRETER),
kGGLServiceAnalytics : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ANALYTICS),
kGGLServiceSignIn : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SIGN_IN),
kFIRServiceIAM : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_IN_APP_MESSAGING),
};
}
if (serviceStringToTypeEnum[serviceString] != nil) {
return (int32_t)serviceStringToTypeEnum[serviceString].longLongValue;
}
return logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE;
}
#pragma mark - Proto population functions
/** Populates the given proto with data related to an SDK logDiagnostics call from the
* diagnosticObjects dictionary.
*
* @param config The proto to populate
* @param diagnosticObjects The dictionary of diagnostics objects.
*/
void FIRPopulateProtoWithInfoFromUserInfoParams(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
NSDictionary<NSString *, id> *diagnosticObjects) {
NSNumber *configurationType = diagnosticObjects[kFIRCDConfigurationTypeKey];
if (configurationType != nil) {
switch (configurationType.integerValue) {
case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE:
config->configuration_type =
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE;
config->has_configuration_type = 1;
break;
case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK:
config->configuration_type =
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK;
config->has_configuration_type = 1;
break;
default:
break;
}
}
NSString *sdkName = diagnosticObjects[kFIRCDSdkNameKey];
if (sdkName) {
config->sdk_name = FIRMapFromServiceStringToTypeEnum(sdkName);
config->has_sdk_name = 1;
}
NSString *version = diagnosticObjects[kFIRCDSdkVersionKey];
if (version) {
config->sdk_version = FIREncodeString(version);
}
}
/** Populates the given proto with data from the calling FIRApp using the given
* diagnosticObjects dictionary.
*
* @param config The proto to populate
* @param diagnosticObjects The dictionary of diagnostics objects.
*/
void FIRPopulateProtoWithCommonInfoFromApp(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
NSDictionary<NSString *, id> *diagnosticObjects) {
config->pod_name = logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE;
config->has_pod_name = 1;
if (!diagnosticObjects[kFIRCDllAppsCountKey]) {
GDTLogError(GDTMCEGeneralError, @"%@", @"App count is a required value in the data dict.");
}
config->app_count = (int32_t)[diagnosticObjects[kFIRCDllAppsCountKey] integerValue];
config->has_app_count = 1;
NSString *googleAppID = diagnosticObjects[kFIRCDGoogleAppIDKey];
if (googleAppID.length) {
config->app_id = FIREncodeString(googleAppID);
}
NSString *bundleID = diagnosticObjects[kFIRCDBundleIDKey];
if (bundleID.length) {
config->bundle_id = FIREncodeString(bundleID);
}
NSString *firebaseUserAgent = diagnosticObjects[kFIRCDFirebaseUserAgentKey];
if (firebaseUserAgent.length) {
config->platform_info = FIREncodeString(firebaseUserAgent);
}
NSNumber *usingOptionsFromDefaultPlist = diagnosticObjects[kFIRCDUsingOptionsFromDefaultPlistKey];
if (usingOptionsFromDefaultPlist != nil) {
config->use_default_app = [usingOptionsFromDefaultPlist boolValue];
config->has_use_default_app = 1;
}
NSString *libraryVersionID = diagnosticObjects[kFIRCDLibraryVersionIDKey];
if (libraryVersionID) {
config->icore_version = FIREncodeString(libraryVersionID);
}
NSString *deviceModel = [FIRCoreDiagnostics deviceModel];
if (deviceModel.length) {
config->device_model = FIREncodeString(deviceModel);
}
NSString *osVersion = [GULAppEnvironmentUtil systemVersion];
if (osVersion.length) {
config->os_version = FIREncodeString(osVersion);
}
config->using_zip_file = kUsingZipFile;
config->has_using_zip_file = 1;
config->deployment_type = kDeploymentType;
config->has_deployment_type = 1;
config->deployed_in_app_store = [GULAppEnvironmentUtil isFromAppStore];
config->has_deployed_in_app_store = 1;
}
/** Populates the given proto with installed services data.
*
* @param config The proto to populate
*/
void FIRPopulateProtoWithInstalledServices(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
NSMutableArray<NSNumber *> *sdkServiceInstalledArray = [NSMutableArray array];
// AdMob
if (NSClassFromString(@"GADBannerView") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAdMob))];
}
// CloudMessaging
if (NSClassFromString(@"FIRMessaging") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMessaging))];
}
// RemoteConfig
if (NSClassFromString(@"FIRRemoteConfig") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceRemoteConfig))];
}
// Measurement/Analtyics
if (NSClassFromString(@"FIRAnalytics") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMeasurement))];
}
// ML Vision On Device AutoML.
if (NSClassFromString(@"FIRVisionOnDeviceAutoMLImageLabelerOptions") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceAutoML))];
}
// ML Vision On Device Face.
if (NSClassFromString(@"FIRVisionFaceDetector") != nil &&
NSClassFromString(@"GMVFaceDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceFace))];
}
// ML Vision On Device Barcode.
if (NSClassFromString(@"FIRVisionBarcodeDetector") != nil &&
NSClassFromString(@"GMVBarcodeDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceBarcode))];
}
// ML Vision On Device Text.
if (NSClassFromString(@"FIRVisionTextDetector") != nil &&
NSClassFromString(@"GMVTextDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceText))];
}
// ML Vision On Device Image Label.
if (NSClassFromString(@"FIRVisionLabelDetector") != nil &&
NSClassFromString(@"GMVLabelDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceLabel))];
}
// ML Vision On Device Object.
if (NSClassFromString(@"FIRVisionObjectDetector") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceObjectDetection))];
}
// ML Model Interpreter
if (NSClassFromString(@"FIRCustomModelInterpreter") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLModelInterpreter))];
}
// Database
if (NSClassFromString(@"FIRDatabase") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDatabase))];
}
// DynamicDeepLink
if (NSClassFromString(@"FIRDynamicLinks") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDynamicLinks))];
}
// Auth
if (NSClassFromString(@"FIRAuth") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuth))];
}
// AuthUI
if (NSClassFromString(@"FUIAuth") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuthUI))];
}
// Firestore
if (NSClassFromString(@"FIRFirestore") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFirestore))];
}
// Functions
if (NSClassFromString(@"FIRFunctions") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFunctions))];
}
// Performance
if (NSClassFromString(@"FIRPerformance") != nil) {
[sdkServiceInstalledArray
addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServicePerformance))];
}
// Storage
if (NSClassFromString(@"FIRStorage") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceStorage))];
}
// SignIn via Google pod
if (NSClassFromString(@"GIDSignIn") != nil && NSClassFromString(@"GGLContext") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceSignIn))];
}
// Analytics via Google pod
if (NSClassFromString(@"GAI") != nil && NSClassFromString(@"GGLContext") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceAnalytics))];
}
// In-App Messaging
if (NSClassFromString(@"FIRInAppMessaging") != nil) {
[sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceIAM))];
}
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *servicesInstalled =
malloc(sizeof(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType) *
sdkServiceInstalledArray.count);
for (NSUInteger i = 0; i < sdkServiceInstalledArray.count; i++) {
NSNumber *typeEnum = sdkServiceInstalledArray[i];
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType serviceType =
(int32_t)typeEnum.integerValue;
servicesInstalled[i] = serviceType;
}
config->sdk_service_installed = servicesInstalled;
config->sdk_service_installed_count = (int32_t)sdkServiceInstalledArray.count;
}
/** Populates the proto with the number of linked frameworks.
*
* @param config The proto to populate.
*/
void FIRPopulateProtoWithNumberOfLinkedFrameworks(
logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
int numFrameworks = -1; // Subtract the app binary itself.
unsigned int numImages;
const char **imageNames = objc_copyImageNames(&numImages);
for (unsigned int i = 0; i < numImages; i++) {
NSString *imageName = [NSString stringWithUTF8String:imageNames[i]];
if ([imageName rangeOfString:@"System/Library"].length != 0 // Apple .frameworks
|| [imageName rangeOfString:@"Developer/Library"].length != 0 // Xcode debug .frameworks
|| [imageName rangeOfString:@"usr/lib"].length != 0) { // Public .dylibs
continue;
}
numFrameworks++;
}
free(imageNames);
config->dynamic_framework_count = numFrameworks;
config->has_dynamic_framework_count = 1;
}
/** Populates the proto with Info.plist values.
*
* @param config The proto to populate.
*/
void FIRPopulateProtoWithInfoPlistValues(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
NSString *xcodeVersion = info[@"DTXcodeBuild"] ?: @"";
NSString *sdkVersion = info[@"DTSDKBuild"] ?: @"";
NSString *combinedVersions = [NSString stringWithFormat:@"%@-%@", xcodeVersion, sdkVersion];
config->apple_framework_version = FIREncodeString(combinedVersions);
NSString *minVersion = info[@"MinimumOSVersion"];
if (minVersion) {
config->min_supported_ios_version = FIREncodeString(minVersion);
}
// Apps can turn off swizzling in the Info.plist, check if they've explicitly set the value and
// report it. It's enabled by default.
NSNumber *appDelegateSwizzledNum = info[@"FirebaseAppDelegateProxyEnabled"];
BOOL appDelegateSwizzled = YES;
if ([appDelegateSwizzledNum isKindOfClass:[NSNumber class]]) {
appDelegateSwizzled = [appDelegateSwizzledNum boolValue];
}
config->swizzling_enabled = appDelegateSwizzled;
config->has_swizzling_enabled = 1;
}
#pragma mark - FIRCoreDiagnosticsInterop
+ (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
FIRCoreDiagnostics *diagnostics = [FIRCoreDiagnostics sharedInstance];
[diagnostics sendDiagnosticsData:diagnosticsData];
}
- (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
dispatch_async(self.diagnosticsQueue, ^{
NSDictionary<NSString *, id> *diagnosticObjects = diagnosticsData.diagnosticObjects;
NSNumber *isDataCollectionDefaultEnabled =
diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey];
if (isDataCollectionDefaultEnabled && ![isDataCollectionDefaultEnabled boolValue]) {
return;
}
// Create the proto.
logs_proto_mobilesdk_ios_ICoreConfiguration icore_config =
logs_proto_mobilesdk_ios_ICoreConfiguration_init_default;
icore_config.using_gdt = 1;
icore_config.has_using_gdt = 1;
// Populate the proto with information.
FIRPopulateProtoWithInfoFromUserInfoParams(&icore_config, diagnosticObjects);
FIRPopulateProtoWithCommonInfoFromApp(&icore_config, diagnosticObjects);
FIRPopulateProtoWithInstalledServices(&icore_config);
FIRPopulateProtoWithNumberOfLinkedFrameworks(&icore_config);
FIRPopulateProtoWithInfoPlistValues(&icore_config);
[self setHeartbeatFlagIfNeededToConfig:&icore_config];
// This log object is capable of converting the proto to bytes.
FIRCoreDiagnosticsLog *log = [[FIRCoreDiagnosticsLog alloc] initWithConfig:icore_config];
// Send the log as a telemetry event.
GDTEvent *event = [self.transport eventForTransport];
event.dataObject = (id<GDTEventDataObject>)log;
[self.transport sendTelemetryEvent:event];
});
}
#pragma mark - Heartbeat
- (void)setHeartbeatFlagIfNeededToConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration *)config {
// Check if need to send a heartbeat.
NSDate *currentDate = [NSDate date];
NSDate *lastCheckin = [self.heartbeatDateStorage date];
if (lastCheckin) {
// Ensure the previous checkin was on a different date in the past.
if ([self isDate:currentDate inSameDayOrBeforeThan:lastCheckin]) {
return;
}
}
// Update heartbeat sent date.
NSError *error;
if (![self.heartbeatDateStorage setDate:currentDate error:&error]) {
GULLogError(kFIRCoreDiagnostics, NO, @"I-COR100004", @"Unable to persist internal state: %@",
error);
}
// Set the flag.
config->sdk_name = logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ICORE;
config->has_sdk_name = 1;
}
- (BOOL)isDate:(NSDate *)date1 inSameDayOrBeforeThan:(NSDate *)date2 {
return [[NSCalendar currentCalendar] isDate:date1 inSameDayAsDate:date2] ||
[date1 compare:date2] == NSOrderedAscending;
}
@end

View File

@ -0,0 +1,47 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// Stores a date to a specified file.
@interface FIRCoreDiagnosticsDateFileStorage : NSObject
- (instancetype)init NS_UNAVAILABLE;
/**
* Default initializer.
* @param fileURL The URL of the file to store the date. The directory must exist, the file may not
* exist, it will be created if needed.
*/
- (instancetype)initWithFileURL:(NSURL *)fileURL;
/**
* Saves the date to the specified file.
* @return YES on success, NO otherwise.
*/
- (BOOL)setDate:(nullable NSDate *)date error:(NSError **)outError;
/**
* Reads the date to the specified file.
* @return Returns date if exists, otherwise `nil`.
*/
- (nullable NSDate *)date;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,64 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "FIRCDLibrary/FIRCoreDiagnosticsDateFileStorage.h"
@interface FIRCoreDiagnosticsDateFileStorage ()
@property(nonatomic, readonly) NSURL *fileURL;
@end
@implementation FIRCoreDiagnosticsDateFileStorage
- (instancetype)initWithFileURL:(NSURL *)fileURL {
if (fileURL == nil) {
return nil;
}
self = [super init];
if (self) {
_fileURL = fileURL;
}
return self;
}
- (BOOL)setDate:(nullable NSDate *)date error:(NSError **)outError {
NSString *stringToSave = @"";
if (date != nil) {
NSTimeInterval timestamp = [date timeIntervalSinceReferenceDate];
stringToSave = [NSString stringWithFormat:@"%f", timestamp];
}
return [stringToSave writeToURL:self.fileURL
atomically:YES
encoding:NSUTF8StringEncoding
error:outError];
}
- (nullable NSDate *)date {
NSString *timestampString = [NSString stringWithContentsOfURL:self.fileURL
encoding:NSUTF8StringEncoding
error:nil];
if (timestampString.length == 0) {
return nil;
}
NSTimeInterval timestamp = timestampString.doubleValue;
return [NSDate dateWithTimeIntervalSinceReferenceDate:timestamp];
}
@end

View File

@ -0,0 +1,72 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.3.9.2 */
#include "firebasecore.nanopb.h"
/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
const pb_field_t logs_proto_mobilesdk_ios_ICoreConfiguration_fields[34] = {
PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, logs_proto_mobilesdk_ios_ICoreConfiguration, configuration_type, configuration_type, 0),
PB_FIELD( 2, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, version_name, configuration_type, 0),
PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, build_number, version_name, 0),
PB_FIELD( 4, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, build_type, build_number, 0),
PB_FIELD( 5, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, plist_version, build_type, 0),
PB_FIELD( 6, UENUM , REPEATED, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_service_enabled, plist_version, 0),
PB_FIELD( 7, UENUM , REPEATED, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_service_installed, sdk_service_enabled, 0),
PB_FIELD( 9, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, device_model, sdk_service_installed, 0),
PB_FIELD( 10, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, app_id, device_model, 0),
PB_FIELD( 11, INT64 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, project_number, app_id, 0),
PB_FIELD( 12, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, bundle_id, project_number, 0),
PB_FIELD( 13, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, client_id, bundle_id, 0),
PB_FIELD( 14, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, install, client_id, 0),
PB_FIELD( 16, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, pod_name, install, 0),
PB_FIELD( 18, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, icore_version, pod_name, 0),
PB_FIELD( 19, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_version, icore_version, 0),
PB_FIELD( 20, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, sdk_name, sdk_version, 0),
PB_FIELD( 21, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, app_count, sdk_name, 0),
PB_FIELD( 22, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, os_version, app_count, 0),
PB_FIELD( 23, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, itunes_id, os_version, 0),
PB_FIELD( 24, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, min_supported_ios_version, itunes_id, 0),
PB_FIELD( 25, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, use_default_app, min_supported_ios_version, 0),
PB_FIELD( 26, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, deployed_in_app_store, use_default_app, 0),
PB_FIELD( 27, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, dynamic_framework_count, deployed_in_app_store, 0),
PB_FIELD( 28, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, apple_framework_version, dynamic_framework_count, 0),
PB_FIELD( 29, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, using_zip_file, apple_framework_version, 0),
PB_FIELD( 30, UENUM , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, deployment_type, using_zip_file, 0),
PB_FIELD( 31, BYTES , OPTIONAL, POINTER , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, platform_info, deployment_type, 0),
PB_FIELD( 32, INT64 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, app_extensions, platform_info, 0),
PB_FIELD( 33, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, swizzling_enabled, app_extensions, 0),
PB_FIELD( 34, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, log_error_count, swizzling_enabled, 0),
PB_FIELD( 35, INT32 , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, log_warning_count, log_error_count, 0),
PB_FIELD( 36, BOOL , OPTIONAL, STATIC , OTHER, logs_proto_mobilesdk_ios_ICoreConfiguration, using_gdt, log_warning_count, 0),
PB_LAST_FIELD
};
/* @@protoc_insertion_point(eof) */

View File

@ -0,0 +1,224 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.9.2 */
#ifndef PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED
#define PB_LOGS_PROTO_MOBILESDK_IOS_FIREBASECORE_NANOPB_H_INCLUDED
#include <nanopb/pb.h>
/* @@protoc_insertion_point(includes) */
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType {
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_UNKNOWN_CONFIGURATION_TYPE = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK = 2
} logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_UNKNOWN_CONFIGURATION_TYPE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType)(logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType {
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_UNKNOWN_BUILD_TYPE = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_INTERNAL = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_EAP = 2,
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_PROD = 3
} logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_UNKNOWN_BUILD_TYPE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_PROD
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType)(logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_PROD+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType {
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ICORE = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ADMOB = 2,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_APP_INVITE = 3,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SIGN_IN = 5,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_GCM = 6,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MAPS = 7,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SCION = 8,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ANALYTICS = 9,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_APP_INDEXING = 10,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_CONFIG = 11,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DURABLE_DEEP_LINKS = 12,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_CRASH = 13,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH = 14,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DATABASE = 15,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_STORAGE = 16,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MESSAGING = 17,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MEASUREMENT = 18,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_REMOTE_CONFIG = 19,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DYNAMIC_LINKS = 20,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_INVITES = 21,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH_UI = 22,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FIRESTORE = 23,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_PERFORMANCE = 24,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_FACE = 26,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_BARCODE = 27,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_TEXT = 28,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_LABEL = 29,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_MODEL_INTERPRETER = 30,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_IN_APP_MESSAGING = 31,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FUNCTIONS = 32,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_NATURAL_LANGUAGE = 33,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_AUTOML = 34,
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION = 35
} logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType)(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName {
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_UNKNOWN_POD_NAME = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_GOOGLE = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE = 2
} logs_proto_mobilesdk_ios_ICoreConfiguration_PodName;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_UNKNOWN_POD_NAME
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_PodName)(logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE+1))
typedef enum _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType {
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_UNKNOWN = 0,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_COCOAPODS = 1,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_ZIP_FILE = 2,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_CARTHAGE = 3,
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM = 4
} logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType;
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MIN logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_UNKNOWN
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MAX logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM
#define _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_ARRAYSIZE ((logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType)(logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM+1))
/* Struct definitions */
typedef struct _logs_proto_mobilesdk_ios_ICoreConfiguration {
bool has_configuration_type;
logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType configuration_type;
pb_bytes_array_t *version_name;
bool has_build_number;
int64_t build_number;
bool has_build_type;
logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType build_type;
pb_bytes_array_t *plist_version;
pb_size_t sdk_service_enabled_count;
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *sdk_service_enabled;
pb_size_t sdk_service_installed_count;
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *sdk_service_installed;
pb_bytes_array_t *device_model;
pb_bytes_array_t *app_id;
bool has_project_number;
int64_t project_number;
pb_bytes_array_t *bundle_id;
pb_bytes_array_t *client_id;
pb_bytes_array_t *install;
bool has_pod_name;
logs_proto_mobilesdk_ios_ICoreConfiguration_PodName pod_name;
pb_bytes_array_t *icore_version;
pb_bytes_array_t *sdk_version;
bool has_sdk_name;
logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType sdk_name;
bool has_app_count;
int32_t app_count;
pb_bytes_array_t *os_version;
pb_bytes_array_t *itunes_id;
pb_bytes_array_t *min_supported_ios_version;
bool has_use_default_app;
bool use_default_app;
bool has_deployed_in_app_store;
bool deployed_in_app_store;
bool has_dynamic_framework_count;
int32_t dynamic_framework_count;
pb_bytes_array_t *apple_framework_version;
bool has_using_zip_file;
bool using_zip_file;
bool has_deployment_type;
logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType deployment_type;
pb_bytes_array_t *platform_info;
bool has_app_extensions;
int64_t app_extensions;
bool has_swizzling_enabled;
bool swizzling_enabled;
bool has_log_error_count;
int32_t log_error_count;
bool has_log_warning_count;
int32_t log_warning_count;
bool has_using_gdt;
bool using_gdt;
/* @@protoc_insertion_point(struct:logs_proto_mobilesdk_ios_ICoreConfiguration) */
} logs_proto_mobilesdk_ios_ICoreConfiguration;
/* Default values for struct fields */
/* Initializer values for message structs */
#define logs_proto_mobilesdk_ios_ICoreConfiguration_init_default {false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MIN, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MIN, NULL, 0, NULL, 0, NULL, NULL, NULL, false, 0, NULL, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MIN, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MIN, false, 0, NULL, NULL, NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MIN, NULL, false, 0, false, 0, false, 0, false, 0, false, 0}
#define logs_proto_mobilesdk_ios_ICoreConfiguration_init_zero {false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_MIN, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_BuildType_MIN, NULL, 0, NULL, 0, NULL, NULL, NULL, false, 0, NULL, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_MIN, NULL, NULL, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MIN, false, 0, NULL, NULL, NULL, false, 0, false, 0, false, 0, NULL, false, 0, false, _logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_MIN, NULL, false, 0, false, 0, false, 0, false, 0, false, 0}
/* Field tags (for use in manual encoding/decoding) */
#define logs_proto_mobilesdk_ios_ICoreConfiguration_pod_name_tag 16
#define logs_proto_mobilesdk_ios_ICoreConfiguration_configuration_type_tag 1
#define logs_proto_mobilesdk_ios_ICoreConfiguration_version_name_tag 2
#define logs_proto_mobilesdk_ios_ICoreConfiguration_icore_version_tag 18
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_version_tag 19
#define logs_proto_mobilesdk_ios_ICoreConfiguration_build_number_tag 3
#define logs_proto_mobilesdk_ios_ICoreConfiguration_build_type_tag 4
#define logs_proto_mobilesdk_ios_ICoreConfiguration_plist_version_tag 5
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_service_enabled_tag 6
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_service_installed_tag 7
#define logs_proto_mobilesdk_ios_ICoreConfiguration_sdk_name_tag 20
#define logs_proto_mobilesdk_ios_ICoreConfiguration_device_model_tag 9
#define logs_proto_mobilesdk_ios_ICoreConfiguration_os_version_tag 22
#define logs_proto_mobilesdk_ios_ICoreConfiguration_app_id_tag 10
#define logs_proto_mobilesdk_ios_ICoreConfiguration_project_number_tag 11
#define logs_proto_mobilesdk_ios_ICoreConfiguration_bundle_id_tag 12
#define logs_proto_mobilesdk_ios_ICoreConfiguration_client_id_tag 13
#define logs_proto_mobilesdk_ios_ICoreConfiguration_itunes_id_tag 23
#define logs_proto_mobilesdk_ios_ICoreConfiguration_min_supported_ios_version_tag 24
#define logs_proto_mobilesdk_ios_ICoreConfiguration_install_tag 14
#define logs_proto_mobilesdk_ios_ICoreConfiguration_use_default_app_tag 25
#define logs_proto_mobilesdk_ios_ICoreConfiguration_app_count_tag 21
#define logs_proto_mobilesdk_ios_ICoreConfiguration_deployed_in_app_store_tag 26
#define logs_proto_mobilesdk_ios_ICoreConfiguration_dynamic_framework_count_tag 27
#define logs_proto_mobilesdk_ios_ICoreConfiguration_apple_framework_version_tag 28
#define logs_proto_mobilesdk_ios_ICoreConfiguration_using_zip_file_tag 29
#define logs_proto_mobilesdk_ios_ICoreConfiguration_deployment_type_tag 30
#define logs_proto_mobilesdk_ios_ICoreConfiguration_platform_info_tag 31
#define logs_proto_mobilesdk_ios_ICoreConfiguration_app_extensions_tag 32
#define logs_proto_mobilesdk_ios_ICoreConfiguration_swizzling_enabled_tag 33
#define logs_proto_mobilesdk_ios_ICoreConfiguration_log_error_count_tag 34
#define logs_proto_mobilesdk_ios_ICoreConfiguration_log_warning_count_tag 35
#define logs_proto_mobilesdk_ios_ICoreConfiguration_using_gdt_tag 36
/* Struct field encoding specification for nanopb */
extern const pb_field_t logs_proto_mobilesdk_ios_ICoreConfiguration_fields[34];
/* Maximum encoded size of messages (where known) */
/* logs_proto_mobilesdk_ios_ICoreConfiguration_size depends on runtime parameters */
/* Message IDs (where set with "msgid" option) */
#ifdef PB_MSGID
#define FIREBASECORE_MESSAGES \
#endif
/* @@protoc_insertion_point(eof) */
#endif

202
ios/Pods/FirebaseCoreDiagnostics/LICENSE generated Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

223
ios/Pods/FirebaseCoreDiagnostics/README.md generated Normal file
View File

@ -0,0 +1,223 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
[https://firebase.google.com](https://firebase.google.com).
## Installation
See the three subsections for details about three different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
### Standard pod install
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
For releases starting with 5.0.0, the source for each release is also deployed
to CocoaPods master and available via standard
[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See
[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All of the official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
```
To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
```
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution are at
[Carthage](Carthage.md).
### Rome
Instructions for installing binary frameworks via
[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.1 (or later)
* CocoaPods 1.7.2 (or later)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See
[Firestore/README.md](Firestore/README.md) and
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
before creating a PR.
Travis will verify that any code changes are done in a style compliant way. Install
`clang-format` and `swiftformat`.
These commands will get the right versions:
```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage
First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
After running the `AllUnitTests_iOS` scheme in Xcode, execute
`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
### Running Sample Apps
In order to run the sample apps and integration tests, you'll need valid
`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
files without real values, but can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g. `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
(e.g. in [Example/Database/App/](Example/Database/App/));
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
special Apple capabilities, and you will have to change the sample app to use a unique bundle
identifier that you can control in your own Apple Developer account.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
If you're doing specific Firebase Auth development, see
[the Auth Sample README](Example/Auth/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
To run the Database Integration tests, make your database authentication rules
[public](https://firebase.google.com/docs/database/security/quickstart).
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to actually test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account, and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
In order to receive push notifications, you'll have to follow the steps above and run the app on a
physical device.
## Community Supported Efforts
We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
participate in the Firebase community.
### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS.
For tvOS, checkout the [Sample](Example/tvOSSample).
Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile:
```
pod 'FirebaseAuth'
pod 'FirebaseCore'
pod 'FirebaseDatabase'
pod 'FirebaseFirestore'
pod 'FirebaseFunctions'
pod 'FirebaseMessaging'
pod 'FirebaseStorage'
```
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
iOS SDK.
## License
The contents of this repository is licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).

View File

@ -0,0 +1,63 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** If present, is a BOOL wrapped in an NSNumber. */
static NSString *const kFIRCDIsDataCollectionDefaultEnabledKey =
@"FIRCDIsDataCollectionDefaultEnabledKey";
/** If present, is an int32_t wrapped in an NSNumber. */
static NSString *const kFIRCDConfigurationTypeKey = @"FIRCDConfigurationTypeKey";
/** If present, is an NSString. */
static NSString *const kFIRCDSdkNameKey = @"FIRCDSdkNameKey";
/** If present, is an NSString. */
static NSString *const kFIRCDSdkVersionKey = @"FIRCDSdkVersionKey";
/** If present, is an int32_t wrapped in an NSNumber. */
static NSString *const kFIRCDllAppsCountKey = @"FIRCDllAppsCountKey";
/** If present, is an NSString. */
static NSString *const kFIRCDGoogleAppIDKey = @"FIRCDGoogleAppIDKey";
/** If present, is an NSString. */
static NSString *const kFIRCDBundleIDKey = @"FIRCDBundleID";
/** If present, is a BOOL wrapped in an NSNumber. */
static NSString *const kFIRCDUsingOptionsFromDefaultPlistKey =
@"FIRCDUsingOptionsFromDefaultPlistKey";
/** If present, is an NSString. */
static NSString *const kFIRCDLibraryVersionIDKey = @"FIRCDLibraryVersionIDKey";
/** If present, is an NSString. */
static NSString *const kFIRCDFirebaseUserAgentKey = @"FIRCDFirebaseUserAgentKey";
/** Defines the interface of a data object needed to log diagnostics data. */
@protocol FIRCoreDiagnosticsData <NSObject>
@required
/** A dictionary containing data (non-exhaustive) to be logged in diagnostics. */
@property(nonatomic) NSDictionary<NSString *, id> *diagnosticObjects;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,34 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "FIRCoreDiagnosticsData.h"
NS_ASSUME_NONNULL_BEGIN
/** Allows the interoperation of FirebaseCore and FirebaseCoreDiagnostics. */
@protocol FIRCoreDiagnosticsInterop <NSObject>
/** Sends the given diagnostics data.
*
* @param diagnosticsData The diagnostics data object to send.
*/
+ (void)sendDiagnosticsData:(id<FIRCoreDiagnosticsData>)diagnosticsData;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,223 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The
[GoogleUtilities](GoogleUtilities/README.md) pod is
a set of utilities used by Firebase and other Google products.
Firebase is an app development platform with tools to help you build, grow and
monetize your app. More information about Firebase can be found at
[https://firebase.google.com](https://firebase.google.com).
## Installation
See the three subsections for details about three different installation methods.
1. [Standard pod install](README.md#standard-pod-install)
1. [Installing from the GitHub repo](README.md#installing-from-github)
1. [Experimental Carthage](README.md#carthage-ios-only)
### Standard pod install
Go to
[https://firebase.google.com/docs/ios/setup](https://firebase.google.com/docs/ios/setup).
### Installing from GitHub
For releases starting with 5.0.0, the source for each release is also deployed
to CocoaPods master and available via standard
[CocoaPods Podfile syntax](https://guides.cocoapods.org/syntax/podfile.html#pod).
These instructions can be used to access the Firebase repo at other branches,
tags, or commits.
#### Background
See
[the Podfile Syntax Reference](https://guides.cocoapods.org/syntax/podfile.html#pod)
for instructions and options about overriding pod source locations.
#### Accessing Firebase Source Snapshots
All of the official releases are tagged in this repo and available via CocoaPods. To access a local
source snapshot or unreleased branch, use Podfile directives like the following:
To access FirebaseFirestore via a branch:
```
pod 'FirebaseCore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
pod 'FirebaseFirestore', :git => 'https://github.com/firebase/firebase-ios-sdk.git', :branch => 'master'
```
To access FirebaseMessaging via a checked out version of the firebase-ios-sdk repo do:
```
pod 'FirebaseCore', :path => '/path/to/firebase-ios-sdk'
pod 'FirebaseMessaging', :path => '/path/to/firebase-ios-sdk'
```
### Carthage (iOS only)
Instructions for the experimental Carthage distribution are at
[Carthage](Carthage.md).
### Rome
Instructions for installing binary frameworks via
[Rome](https://github.com/CocoaPods/Rome) are at [Rome](Rome.md).
## Development
To develop Firebase software in this repository, ensure that you have at least
the following software:
* Xcode 10.1 (or later)
* CocoaPods 1.7.2 (or later)
For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See
[Firestore/README.md](Firestore/README.md) and
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod
See [AddNewPod.md](AddNewPod.md).
### Code Formatting
To ensure that the code is formatted consistently, run the script
[./scripts/style.sh](https://github.com/firebase/firebase-ios-sdk/blob/master/scripts/style.sh)
before creating a PR.
Travis will verify that any code changes are done in a style compliant way. Install
`clang-format` and `swiftformat`.
These commands will get the right versions:
```
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/773cb75d360b58f32048f5964038d09825a507c8/Formula/clang-format.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/3dfea1004e0736754bbf49673cca8aaed8a94089/Formula/swiftformat.rb
```
Note: if you already have a newer version of these installed you may need to
`brew switch` to this version.
### Running Unit Tests
Select a scheme and press Command-u to build a component and run its unit tests.
#### Viewing Code Coverage
First, make sure that [xcov](https://github.com/nakiostudio/xcov) is installed with `gem install xcov`.
After running the `AllUnitTests_iOS` scheme in Xcode, execute
`xcov --workspace Firebase.xcworkspace --scheme AllUnitTests_iOS --output_directory xcov_output`
at Example/ in the terminal. This will aggregate the coverage, and you can run `open xcov_output/index.html` to see the results.
### Running Sample Apps
In order to run the sample apps and integration tests, you'll need valid
`GoogleService-Info.plist` files for those samples. The Firebase Xcode project contains dummy plist
files without real values, but can be replaced with real plist files. To get your own
`GoogleService-Info.plist` files:
1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Create a new Firebase project, if you don't already have one
3. For each sample app you want to test, create a new Firebase app with the sample app's bundle
identifier (e.g. `com.google.Database-Example`)
4. Download the resulting `GoogleService-Info.plist` and replace the appropriate dummy plist file
(e.g. in [Example/Database/App/](Example/Database/App/));
Some sample apps like Firebase Messaging ([Example/Messaging/App](Example/Messaging/App)) require
special Apple capabilities, and you will have to change the sample app to use a unique bundle
identifier that you can control in your own Apple Developer account.
## Specific Component Instructions
See the sections below for any special instructions for those components.
### Firebase Auth
If you're doing specific Firebase Auth development, see
[the Auth Sample README](Example/Auth/README.md) for instructions about
building and running the FirebaseAuth pod along with various samples and tests.
### Firebase Database
To run the Database Integration tests, make your database authentication rules
[public](https://firebase.google.com/docs/database/security/quickstart).
### Firebase Storage
To run the Storage Integration tests, follow the instructions in
[FIRStorageIntegrationTests.m](Example/Storage/Tests/Integration/FIRStorageIntegrationTests.m).
#### Push Notifications
Push notifications can only be delivered to specially provisioned App IDs in the developer portal.
In order to actually test receiving push notifications, you will need to:
1. Change the bundle identifier of the sample app to something you own in your Apple Developer
account, and enable that App ID for push notifications.
2. You'll also need to
[upload your APNs Provider Authentication Key or certificate to the Firebase Console](https://firebase.google.com/docs/cloud-messaging/ios/certs)
at **Project Settings > Cloud Messaging > [Your Firebase App]**.
3. Ensure your iOS device is added to your Apple Developer portal as a test device.
#### iOS Simulator
The iOS Simulator cannot register for remote notifications, and will not receive push notifications.
In order to receive push notifications, you'll have to follow the steps above and run the app on a
physical device.
## Community Supported Efforts
We've seen an amazing amount of interest and contributions to improve the Firebase SDKs, and we are
very grateful! We'd like to empower as many developers as we can to be able to use Firebase and
participate in the Firebase community.
### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS.
For tvOS, checkout the [Sample](Example/tvOSSample).
Keep in mind that macOS and tvOS are not officially supported by Firebase, and this repository is
actively developed primarily for iOS. While we can catch basic unit test issues with Travis, there
may be some changes where the SDK no longer works as expected on macOS or tvOS. If you encounter
this, please [file an issue](https://github.com/firebase/firebase-ios-sdk/issues).
Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile:
```
pod 'FirebaseAuth'
pod 'FirebaseCore'
pod 'FirebaseDatabase'
pod 'FirebaseFirestore'
pod 'FirebaseFunctions'
pod 'FirebaseMessaging'
pod 'FirebaseStorage'
```
## Roadmap
See [Roadmap](ROADMAP.md) for more about the Firebase iOS SDK Open Source
plans and directions.
## Contributing
See [Contributing](CONTRIBUTING.md) for more information on contributing to the Firebase
iOS SDK.
## License
The contents of this repository is licensed under the
[Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0).
Your use of Firebase is governed by the
[Terms of Service for Firebase Services](https://firebase.google.com/terms/).

View File

@ -44,6 +44,8 @@ typedef NS_ENUM(NSInteger, FIRInstanceIDMessageCode) {
kFIRInstanceIDMessageCodeInstanceID014 = 3014, kFIRInstanceIDMessageCodeInstanceID014 = 3014,
kFIRInstanceIDMessageCodeInstanceID015 = 3015, kFIRInstanceIDMessageCodeInstanceID015 = 3015,
kFIRInstanceIDMessageCodeRefetchingTokenForAPNS = 3016, kFIRInstanceIDMessageCodeRefetchingTokenForAPNS = 3016,
kFIRInstanceIDMessageCodeInstanceID017 = 3017,
kFIRInstanceIDMessageCodeInstanceID018 = 3018,
// FIRInstanceIDAuthService.m // FIRInstanceIDAuthService.m
kFIRInstanceIDMessageCodeAuthService000 = 5000, kFIRInstanceIDMessageCodeAuthService000 = 5000,
kFIRInstanceIDMessageCodeAuthService001 = 5001, kFIRInstanceIDMessageCodeAuthService001 = 5001,

View File

@ -1012,6 +1012,16 @@ static FIRInstanceID *gInstanceID;
FIRInstanceID_WEAKIFY(self); FIRInstanceID_WEAKIFY(self);
[self asyncLoadKeyPairWithHandler:^(FIRInstanceIDKeyPair *keyPair, NSError *error) { [self asyncLoadKeyPairWithHandler:^(FIRInstanceIDKeyPair *keyPair, NSError *error) {
FIRInstanceID_STRONGIFY(self); FIRInstanceID_STRONGIFY(self);
if (self == nil) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID017,
@"Instance ID shut down during token reset. Aborting");
return;
}
if (self.apnsTokenData == nil) {
FIRInstanceIDLoggerError(kFIRInstanceIDMessageCodeInstanceID018,
@"apnsTokenData was set to nil during token reset. Aborting");
return;
}
NSMutableDictionary *tokenOptions = [@{ NSMutableDictionary *tokenOptions = [@{
kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData, kFIRInstanceIDTokenOptionsAPNSKey : self.apnsTokenData,

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#import "FIRInstanceIDCheckinPreferences.h" #import <FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h>
@interface FIRInstanceIDCheckinPreferences (Internal) @interface FIRInstanceIDCheckinPreferences (Internal)

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#import "FIRInstanceIDCheckinPreferences.h" #import <FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h>
/** Checkin refresh interval. **/ /** Checkin refresh interval. **/
FOUNDATION_EXPORT const NSTimeInterval kFIRInstanceIDDefaultCheckinInterval; FOUNDATION_EXPORT const NSTimeInterval kFIRInstanceIDDefaultCheckinInterval;

View File

@ -16,6 +16,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <FirebaseInstanceID/FIRInstanceID+Private.h>
#import "FIRInstanceIDUtilities.h" #import "FIRInstanceIDUtilities.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -31,20 +32,6 @@ FOUNDATION_EXPORT NSString *const kFIRInstanceIDDeviceDataVersionKey;
@class FIRInstanceIDCheckinPreferences; @class FIRInstanceIDCheckinPreferences;
/**
* @related FIRInstanceIDCheckinService
*
* The completion handler invoked once the fetch from Checkin server finishes.
* For successful fetches we returned checkin information by the checkin service
* and `nil` error, else we return the appropriate error object as reported by the
* Checkin Service.
*
* @param checkinPreferences The checkin preferences as fetched from the server.
* @param error The error object which fetching GServices data.
*/
typedef void (^FIRInstanceIDDeviceCheckinCompletion)(
FIRInstanceIDCheckinPreferences *_Nullable checkinPreferences, NSError *_Nullable error);
/** /**
* Register the device with Checkin Service and get back the `authID`, `secret * Register the device with Checkin Service and get back the `authID`, `secret
* token` etc. for the client. Checkin results are cached in the * token` etc. for the client. Checkin results are cached in the

View File

@ -14,14 +14,26 @@
* limitations under the License. * limitations under the License.
*/ */
#import "FIRInstanceID.h" #import <FirebaseInstanceID/FIRInstanceID.h>
#import <FirebaseInstanceID/FIRInstanceIDCheckinPreferences.h>
#import "FIRInstanceIDCheckinService.h"
/** /**
* Internal API used by Firebase SDK teams by calling in reflection or internal teams. * @related FIRInstanceIDCheckinService
*
* The completion handler invoked once the fetch from Checkin server finishes.
* For successful fetches we returned checkin information by the checkin service
* and `nil` error, else we return the appropriate error object as reported by the
* Checkin Service.
*
* @param checkinPreferences The checkin preferences as fetched from the server.
* @param error The error object which fetching GServices data.
*/
typedef void (^FIRInstanceIDDeviceCheckinCompletion)(
FIRInstanceIDCheckinPreferences *_Nullable checkinPreferences, NSError *_Nullable error);
/**
* Private API used by Firebase SDK teams by calling in reflection or internal teams.
*/ */
// TODO(chliangGoogle) Rename this to Internal.
@interface FIRInstanceID (Private) @interface FIRInstanceID (Private)
/** /**

View File

@ -1,8 +1,8 @@
# Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk) # Firebase iOS Open Source Development [![Build Status](https://travis-ci.org/firebase/firebase-ios-sdk.svg?branch=master)](https://travis-ci.org/firebase/firebase-ios-sdk)
This repository contains a subset of the Firebase iOS SDK source. It currently This repository contains a subset of the Firebase iOS SDK source. It currently
includes FirebaseCore, FirebaseAuth, FirebaseDatabase, FirebaseFirestore, includes FirebaseCore, FirebaseABTesting, FirebaseAuth, FirebaseDatabase,
FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging, FirebaseFirestore, FirebaseFunctions, FirebaseInstanceID, FirebaseInAppMessaging,
FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage. FirebaseInAppMessagingDisplay, FirebaseMessaging and FirebaseStorage.
The repository also includes GoogleUtilities source. The The repository also includes GoogleUtilities source. The
@ -80,9 +80,8 @@ For the pod that you want to develop:
`pod gen Firebase{name here}.podspec --local-sources=./ --auto-open` `pod gen Firebase{name here}.podspec --local-sources=./ --auto-open`
Firestore and Functions have self contained Xcode projects. See Firestore has a self contained Xcode project. See
[Firestore/README.md](Firestore/README.md) and [Firestore/README.md](Firestore/README.md).
[Functions/README.md](Functions/README.md).
### Adding a New Firebase Pod ### Adding a New Firebase Pod
@ -179,7 +178,8 @@ very grateful! We'd like to empower as many developers as we can to be able to
participate in the Firebase community. participate in the Firebase community.
### macOS and tvOS ### macOS and tvOS
Thanks to contributions from the community, FirebaseAuth, FirebaseCore, FirebaseDatabase, FirebaseMessaging, Thanks to contributions from the community, FirebaseABTesting, FirebaseAuth, FirebaseCore,
FirebaseDatabase, FirebaseMessaging,
FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on FirebaseFirestore, FirebaseFunctions and FirebaseStorage now compile, run unit tests, and work on
macOS and tvOS. macOS and tvOS.
@ -195,6 +195,7 @@ Note that the Firebase pod is not available for macOS and tvOS.
To install, add a subset of the following to the Podfile: To install, add a subset of the following to the Podfile:
``` ```
pod 'FirebaseABTesting'
pod 'FirebaseAuth' pod 'FirebaseAuth'
pod 'FirebaseCore' pod 'FirebaseCore'
pod 'FirebaseDatabase' pod 'FirebaseDatabase'

View File

@ -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 "GDTLibrary/Public/GDTAssert.h"
GDTAssertionBlock GDTAssertionBlockToRunInstead(void) {
// This class is only compiled in by unit tests, and this should fail quickly in optimized builds.
Class GDTAssertClass = NSClassFromString(@"GDTAssertHelper");
if (__builtin_expect(!!GDTAssertClass, 0)) {
SEL assertionBlockSEL = NSSelectorFromString(@"assertionBlock");
if (assertionBlockSEL) {
IMP assertionBlockIMP = [GDTAssertClass methodForSelector:assertionBlockSEL];
if (assertionBlockIMP) {
GDTAssertionBlock assertionBlock =
((GDTAssertionBlock(*)(id, SEL))assertionBlockIMP)(GDTAssertClass, assertionBlockSEL);
if (assertionBlock) {
return assertionBlock;
}
}
}
}
return NULL;
}

View File

@ -0,0 +1,164 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Public/GDTClock.h"
#import <sys/sysctl.h>
// Using a monotonic clock is necessary because CFAbsoluteTimeGetCurrent(), NSDate, and related all
// are subject to drift. That it to say, multiple consecutive calls do not always result in a
// time that is in the future. Clocks may be adjusted by the user, NTP, or any number of external
// factors. This class attempts to determine the wall-clock time at the time of the event by
// capturing the kernel start and time since boot to determine a wallclock time in UTC.
//
// Timezone offsets at the time of a snapshot are also captured in order to provide local-time
// details. Other classes in this library depend on comparing times at some time in the future to
// a time captured in the past, and this class needs to provide a mechanism to do that.
//
// TL;DR: This class attempts to accomplish two things: 1. Provide accurate event times. 2. Provide
// a monotonic clock mechanism to accurately check if some clock snapshot was before or after
// by using a shared reference point (kernel boot time).
//
// Note: Much of the mach time stuff doesn't work properly in the simulator. So this class can be
// difficult to unit test.
/** Returns the kernel boottime property from sysctl.
*
* Inspired by https://stackoverflow.com/a/40497811
*
* @return The KERN_BOOTTIME property from sysctl, in nanoseconds.
*/
static int64_t KernelBootTimeInNanoseconds() {
// Caching the result is not possible because clock drift would not be accounted for.
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
int rc = sysctl(mib, 2, &boottime, &size, NULL, 0);
if (rc != 0) {
return 0;
}
return (int64_t)boottime.tv_sec * NSEC_PER_MSEC + (int64_t)boottime.tv_usec;
}
/** Returns value of gettimeofday, in nanoseconds.
*
* Inspired by https://stackoverflow.com/a/40497811
*
* @return The value of gettimeofday, in nanoseconds.
*/
static int64_t UptimeInNanoseconds() {
int64_t before_now;
int64_t after_now;
struct timeval now;
before_now = KernelBootTimeInNanoseconds();
// Addresses a race condition in which the system time has updated, but the boottime has not.
do {
gettimeofday(&now, NULL);
after_now = KernelBootTimeInNanoseconds();
} while (after_now != before_now);
return (int64_t)now.tv_sec * NSEC_PER_MSEC + (int64_t)now.tv_usec - before_now;
}
// TODO: Consider adding a 'trustedTime' property that can be populated by the response from a BE.
@implementation GDTClock
- (instancetype)init {
self = [super init];
if (self) {
_kernelBootTime = KernelBootTimeInNanoseconds();
_uptime = UptimeInNanoseconds();
_timeMillis =
(int64_t)((CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) * NSEC_PER_USEC);
CFTimeZoneRef timeZoneRef = CFTimeZoneCopySystem();
_timezoneOffsetSeconds = CFTimeZoneGetSecondsFromGMT(timeZoneRef, 0);
CFRelease(timeZoneRef);
}
return self;
}
+ (GDTClock *)snapshot {
return [[GDTClock alloc] init];
}
+ (instancetype)clockSnapshotInTheFuture:(uint64_t)millisInTheFuture {
GDTClock *snapshot = [self snapshot];
snapshot->_timeMillis += millisInTheFuture;
return snapshot;
}
- (BOOL)isAfter:(GDTClock *)otherClock {
// These clocks are trivially comparable when they share a kernel boot time.
if (_kernelBootTime == otherClock->_kernelBootTime) {
int64_t timeDiff = (_timeMillis + _timezoneOffsetSeconds) -
(otherClock->_timeMillis + otherClock->_timezoneOffsetSeconds);
return timeDiff > 0;
} else {
int64_t kernelBootTimeDiff = otherClock->_kernelBootTime - _kernelBootTime;
// This isn't a great solution, but essentially, if the other clock's boot time is 'later', NO
// is returned. This can be altered by changing the system time and rebooting.
return kernelBootTimeDiff < 0 ? YES : NO;
}
}
- (NSUInteger)hash {
return [@(_kernelBootTime) hash] ^ [@(_uptime) hash] ^ [@(_timeMillis) hash] ^
[@(_timezoneOffsetSeconds) hash];
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
#pragma mark - NSSecureCoding
/** NSKeyedCoder key for timeMillis property. */
static NSString *const kGDTClockTimeMillisKey = @"GDTClockTimeMillis";
/** NSKeyedCoder key for timezoneOffsetMillis property. */
static NSString *const kGDTClockTimezoneOffsetSeconds = @"GDTClockTimezoneOffsetSeconds";
/** NSKeyedCoder key for _kernelBootTime ivar. */
static NSString *const kGDTClockKernelBootTime = @"GDTClockKernelBootTime";
/** NSKeyedCoder key for _uptime ivar. */
static NSString *const kGDTClockUptime = @"GDTClockUptime";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
// TODO: If the kernelBootTime is more recent, we need to change the kernel boot time and
// uptimeMillis ivars
_timeMillis = [aDecoder decodeInt64ForKey:kGDTClockTimeMillisKey];
_timezoneOffsetSeconds = [aDecoder decodeInt64ForKey:kGDTClockTimezoneOffsetSeconds];
_kernelBootTime = [aDecoder decodeInt64ForKey:kGDTClockKernelBootTime];
_uptime = [aDecoder decodeInt64ForKey:kGDTClockUptime];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInt64:_timeMillis forKey:kGDTClockTimeMillisKey];
[aCoder encodeInt64:_timezoneOffsetSeconds forKey:kGDTClockTimezoneOffsetSeconds];
[aCoder encodeInt64:_kernelBootTime forKey:kGDTClockKernelBootTime];
[aCoder encodeInt64:_uptime forKey:kGDTClockUptime];
}
@end

View File

@ -0,0 +1,36 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Public/GDTConsoleLogger.h"
/** The console logger prefix. */
static NSString *kGDTConsoleLogger = @"[GoogleDataTransport]";
NSString *GDTMessageCodeEnumToString(GDTMessageCode code) {
return [[NSString alloc] initWithFormat:@"I-GDT%06ld", (long)code];
}
void GDTLog(GDTMessageCode code, NSString *format, ...) {
// Don't log anything in not debug builds.
#ifndef NDEBUG
NSString *logFormat = [NSString
stringWithFormat:@"%@[%@] %@", kGDTConsoleLogger, GDTMessageCodeEnumToString(code), format];
va_list args;
va_start(args, format);
NSLogv(logFormat, args);
va_end(args);
#endif // NDEBUG
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <GoogleDataTransport/GDTDataFuture.h>
@implementation GDTDataFuture
- (instancetype)initWithFileURL:(NSURL *)fileURL {
self = [super init];
if (self) {
_fileURL = fileURL;
}
return self;
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
- (NSUInteger)hash {
// In reality, only one of these should be populated.
return [_fileURL hash] ^ [_originalData hash];
}
#pragma mark - NSSecureCoding
/** Coding key for _fileURL ivar. */
static NSString *kGDTDataFutureFileURLKey = @"GDTDataFutureFileURLKey";
/** Coding key for _data ivar. */
static NSString *kGDTDataFutureDataKey = @"GDTDataFutureDataKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:_fileURL forKey:kGDTDataFutureFileURLKey];
[aCoder encodeObject:_originalData forKey:kGDTDataFutureDataKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
self = [self init];
if (self) {
_fileURL = [aDecoder decodeObjectOfClass:[NSURL class] forKey:kGDTDataFutureFileURLKey];
_originalData = [aDecoder decodeObjectOfClass:[NSData class] forKey:kGDTDataFutureDataKey];
}
return self;
}
@end

View File

@ -0,0 +1,119 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <GoogleDataTransport/GDTEvent.h>
#import <GoogleDataTransport/GDTAssert.h>
#import <GoogleDataTransport/GDTStoredEvent.h>
#import "GDTLibrary/Private/GDTEvent_Private.h"
@implementation GDTEvent
- (instancetype)initWithMappingID:(NSString *)mappingID target:(NSInteger)target {
GDTAssert(mappingID.length > 0, @"Please give a valid mapping ID");
GDTAssert(target > 0, @"A target cannot be negative or 0");
if (mappingID == nil || mappingID.length == 0 || target <= 0) {
return nil;
}
self = [super init];
if (self) {
_mappingID = mappingID;
_target = target;
_qosTier = GDTEventQosDefault;
}
return self;
}
- (instancetype)copy {
GDTEvent *copy = [[GDTEvent alloc] initWithMappingID:_mappingID target:_target];
copy.dataObject = _dataObject;
copy.dataObjectTransportBytes = _dataObjectTransportBytes;
copy.qosTier = _qosTier;
copy.clockSnapshot = _clockSnapshot;
copy.customPrioritizationParams = _customPrioritizationParams;
return copy;
}
- (NSUInteger)hash {
// This loses some precision, but it's probably fine.
NSUInteger mappingIDHash = [_mappingID hash];
NSUInteger timeHash = [_clockSnapshot hash];
NSUInteger dataObjectTransportBytesHash = [_dataObjectTransportBytes hash];
return mappingIDHash ^ _target ^ dataObjectTransportBytesHash ^ _qosTier ^ timeHash;
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
- (void)setDataObject:(id<GDTEventDataObject>)dataObject {
// If you're looking here because of a performance issue in -transportBytes slowing the assignment
// of -dataObject, one way to address this is to add a queue to this class,
// dispatch_(barrier_ if concurrent)async here, and implement the getter with a dispatch_sync.
if (dataObject != _dataObject) {
_dataObject = dataObject;
_dataObjectTransportBytes = [dataObject transportBytes];
}
}
- (GDTStoredEvent *)storedEventWithDataFuture:(GDTDataFuture *)dataFuture {
return [[GDTStoredEvent alloc] initWithEvent:self dataFuture:dataFuture];
}
#pragma mark - NSSecureCoding and NSCoding Protocols
/** NSCoding key for mappingID property. */
static NSString *mappingIDKey = @"_mappingID";
/** NSCoding key for target property. */
static NSString *targetKey = @"_target";
/** NSCoding key for dataObjectTransportBytes property. */
static NSString *dataObjectTransportBytesKey = @"_dataObjectTransportBytesKey";
/** NSCoding key for qosTier property. */
static NSString *qosTierKey = @"_qosTier";
/** NSCoding key for clockSnapshot property. */
static NSString *clockSnapshotKey = @"_clockSnapshot";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
NSString *mappingID = [aDecoder decodeObjectOfClass:[NSObject class] forKey:mappingIDKey];
NSInteger target = [aDecoder decodeIntegerForKey:targetKey];
self = [self initWithMappingID:mappingID target:target];
if (self) {
_dataObjectTransportBytes = [aDecoder decodeObjectOfClass:[NSData class]
forKey:dataObjectTransportBytesKey];
_qosTier = [aDecoder decodeIntegerForKey:qosTierKey];
_clockSnapshot = [aDecoder decodeObjectOfClass:[GDTClock class] forKey:clockSnapshotKey];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_mappingID forKey:mappingIDKey];
[aCoder encodeInteger:_target forKey:targetKey];
[aCoder encodeObject:_dataObjectTransportBytes forKey:dataObjectTransportBytesKey];
[aCoder encodeInteger:_qosTier forKey:qosTierKey];
[aCoder encodeObject:_clockSnapshot forKey:clockSnapshotKey];
}
@end

View File

@ -0,0 +1,119 @@
/*
* 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 "GDTLibrary/Public/GDTLifecycle.h"
#import <GoogleDataTransport/GDTEvent.h>
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
#import "GDTLibrary/Private/GDTStorage_Private.h"
#import "GDTLibrary/Private/GDTTransformer_Private.h"
#import "GDTLibrary/Private/GDTUploadCoordinator.h"
@implementation GDTLifecycle
+ (void)load {
[self sharedInstance];
}
/** Creates/returns the singleton instance of this class.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance {
static GDTLifecycle *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTLifecycle alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(applicationDidEnterBackground:)
name:kGDTApplicationDidEnterBackgroundNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(applicationWillEnterForeground:)
name:kGDTApplicationWillEnterForegroundNotification
object:nil];
NSString *name = kGDTApplicationWillTerminateNotification;
[notificationCenter addObserver:self
selector:@selector(applicationWillTerminate:)
name:name
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)applicationDidEnterBackground:(NSNotification *)notification {
GDTApplication *application = [GDTApplication sharedApplication];
if ([[GDTTransformer sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTTransformer sharedInstance] appWillBackground:application];
}
if ([[GDTStorage sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTStorage sharedInstance] appWillBackground:application];
}
if ([[GDTUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTUploadCoordinator sharedInstance] appWillBackground:application];
}
if ([[GDTRegistrar sharedInstance] respondsToSelector:@selector(appWillBackground:)]) {
[[GDTRegistrar sharedInstance] appWillBackground:application];
}
}
- (void)applicationWillEnterForeground:(NSNotification *)notification {
GDTApplication *application = [GDTApplication sharedApplication];
if ([[GDTTransformer sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTTransformer sharedInstance] appWillForeground:application];
}
if ([[GDTStorage sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTStorage sharedInstance] appWillForeground:application];
}
if ([[GDTUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTUploadCoordinator sharedInstance] appWillForeground:application];
}
if ([[GDTRegistrar sharedInstance] respondsToSelector:@selector(appWillForeground:)]) {
[[GDTRegistrar sharedInstance] appWillForeground:application];
}
}
- (void)applicationWillTerminate:(NSNotification *)notification {
GDTApplication *application = [GDTApplication sharedApplication];
if ([[GDTTransformer sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTTransformer sharedInstance] appWillTerminate:application];
}
if ([[GDTStorage sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTStorage sharedInstance] appWillTerminate:application];
}
if ([[GDTUploadCoordinator sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTUploadCoordinator sharedInstance] appWillTerminate:application];
}
if ([[GDTRegistrar sharedInstance] respondsToSelector:@selector(appWillTerminate:)]) {
[[GDTRegistrar sharedInstance] appWillTerminate:application];
}
}
@end

View File

@ -0,0 +1,175 @@
/*
* 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 <GoogleDataTransport/GDTPlatform.h>
#import <GoogleDataTransport/GDTAssert.h>
const GDTBackgroundIdentifier GDTBackgroundIdentifierInvalid = 0;
NSString *const kGDTApplicationDidEnterBackgroundNotification =
@"GDTApplicationDidEnterBackgroundNotification";
NSString *const kGDTApplicationWillEnterForegroundNotification =
@"GDTApplicationWillEnterForegroundNotification";
NSString *const kGDTApplicationWillTerminateNotification =
@"GDTApplicationWillTerminateNotification";
BOOL GDTReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) {
#if TARGET_OS_IOS
return (flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN;
#else
return NO;
#endif // TARGET_OS_IOS
}
@implementation GDTApplication
+ (void)load {
#if TARGET_OS_IOS || TARGET_OS_TV
// If this asserts, please file a bug at https://github.com/firebase/firebase-ios-sdk/issues.
GDTFatalAssert(GDTBackgroundIdentifierInvalid == UIBackgroundTaskInvalid,
@"GDTBackgroundIdentifierInvalid and UIBackgroundTaskInvalid should be the same.");
#endif
[self sharedApplication];
}
+ (nullable GDTApplication *)sharedApplication {
static GDTApplication *application;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
application = [[GDTApplication alloc] init];
});
return application;
}
- (instancetype)init {
self = [super init];
if (self) {
#if TARGET_OS_IOS || TARGET_OS_TV
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(iOSApplicationDidEnterBackground:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(iOSApplicationWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
NSString *name = UIApplicationWillTerminateNotification;
[notificationCenter addObserver:self
selector:@selector(iOSApplicationWillTerminate:)
name:name
object:nil];
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13, tvOS 13.0, *)) {
[notificationCenter addObserver:self
selector:@selector(iOSApplicationWillEnterForeground:)
name:UISceneWillEnterForegroundNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(iOSApplicationDidEnterBackground:)
name:UISceneWillDeactivateNotification
object:nil];
}
#endif // defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
#elif TARGET_OS_OSX
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(macOSApplicationWillTerminate:)
name:NSApplicationWillTerminateNotification
object:nil];
#endif // TARGET_OS_IOS || TARGET_OS_TV
}
return self;
}
- (GDTBackgroundIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler {
return
[[self sharedApplicationForBackgroundTask] beginBackgroundTaskWithExpirationHandler:handler];
}
- (void)endBackgroundTask:(GDTBackgroundIdentifier)bgID {
if (bgID != GDTBackgroundIdentifierInvalid) {
[[self sharedApplicationForBackgroundTask] endBackgroundTask:bgID];
}
}
#pragma mark - App environment helpers
- (BOOL)isAppExtension {
#if TARGET_OS_IOS || TARGET_OS_TV
BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"];
return appExtension;
#elif TARGET_OS_OSX
return NO;
#endif
}
/** Returns a UIApplication instance if on the appropriate platform.
*
* @return The shared UIApplication if on the appropriate platform.
*/
#if TARGET_OS_IOS || TARGET_OS_TV
- (nullable UIApplication *)sharedApplicationForBackgroundTask {
#else
- (nullable id)sharedApplicationForBackgroundTask {
#endif
if ([self isAppExtension]) {
return nil;
}
id sharedApplication = nil;
Class uiApplicationClass = NSClassFromString(@"UIApplication");
if (uiApplicationClass &&
[uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) {
sharedApplication = [uiApplicationClass sharedApplication];
}
return sharedApplication;
}
#pragma mark - UIApplicationDelegate
#if TARGET_OS_IOS || TARGET_OS_TV
- (void)iOSApplicationDidEnterBackground:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTApplicationDidEnterBackgroundNotification object:nil];
}
- (void)iOSApplicationWillEnterForeground:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTApplicationWillEnterForegroundNotification object:nil];
}
- (void)iOSApplicationWillTerminate:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTApplicationWillTerminateNotification object:nil];
}
#endif // TARGET_OS_IOS || TARGET_OS_TV
#pragma mark - NSApplicationDelegate
#if TARGET_OS_OSX
- (void)macOSApplicationWillTerminate:(NSNotification *)notif {
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter postNotificationName:kGDTApplicationWillTerminateNotification object:nil];
}
#endif // TARGET_OS_OSX
@end

View File

@ -0,0 +1,110 @@
/*
* 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 "GDTLibrary/Private/GDTReachability.h"
#import "GDTLibrary/Private/GDTReachability_Private.h"
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <netinet/in.h>
/** Sets the _callbackFlag ivar whenever the network changes.
*
* @param reachability The reachability object calling back.
* @param flags The new flag values.
* @param info Any data that might be passed in by the callback.
*/
static void GDTReachabilityCallback(SCNetworkReachabilityRef reachability,
SCNetworkReachabilityFlags flags,
void *info);
@implementation GDTReachability {
/** The reachability object. */
SCNetworkReachabilityRef _reachabilityRef;
/** The queue on which callbacks and all work will occur. */
dispatch_queue_t _reachabilityQueue;
/** Flags specified by reachability callbacks. */
SCNetworkConnectionFlags _callbackFlags;
}
+ (void)load {
[self sharedInstance];
}
+ (instancetype)sharedInstance {
static GDTReachability *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTReachability alloc] init];
});
return sharedInstance;
}
+ (SCNetworkReachabilityFlags)currentFlags {
__block SCNetworkReachabilityFlags currentFlags;
dispatch_sync([GDTReachability sharedInstance] -> _reachabilityQueue, ^{
GDTReachability *reachability = [GDTReachability sharedInstance];
currentFlags = reachability->_flags ? reachability->_flags : reachability->_callbackFlags;
});
return currentFlags;
}
- (instancetype)init {
self = [super init];
if (self) {
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
_reachabilityQueue = dispatch_queue_create("com.google.GDTReachability", DISPATCH_QUEUE_SERIAL);
_reachabilityRef = SCNetworkReachabilityCreateWithAddress(
kCFAllocatorDefault, (const struct sockaddr *)&zeroAddress);
Boolean success = SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _reachabilityQueue);
if (!success) {
GDTLogWarning(GDTMCWReachabilityFailed, @"%@", @"The reachability queue wasn't set.");
}
success = SCNetworkReachabilitySetCallback(_reachabilityRef, GDTReachabilityCallback, NULL);
if (!success) {
GDTLogWarning(GDTMCWReachabilityFailed, @"%@", @"The reachability callback wasn't set.");
}
// Get the initial set of flags.
dispatch_async(_reachabilityQueue, ^{
Boolean valid = SCNetworkReachabilityGetFlags(self->_reachabilityRef, &self->_flags);
if (!valid) {
self->_flags = 0;
}
});
}
return self;
}
- (void)setCallbackFlags:(SCNetworkReachabilityFlags)flags {
if (_callbackFlags != flags) {
self->_callbackFlags = flags;
}
}
@end
static void GDTReachabilityCallback(SCNetworkReachabilityRef reachability,
SCNetworkReachabilityFlags flags,
void *info) {
[[GDTReachability sharedInstance] setCallbackFlags:flags];
}

View File

@ -0,0 +1,139 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Public/GDTRegistrar.h"
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
@implementation GDTRegistrar {
/** Backing ivar for targetToUploader property. */
NSMutableDictionary<NSNumber *, id<GDTUploader>> *_targetToUploader;
/** Backing ivar for targetToPrioritizer property. */
NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *_targetToPrioritizer;
}
+ (instancetype)sharedInstance {
static GDTRegistrar *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GDTRegistrar alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
_registrarQueue = dispatch_queue_create("com.google.GDTRegistrar", DISPATCH_QUEUE_CONCURRENT);
_targetToPrioritizer = [[NSMutableDictionary alloc] init];
_targetToUploader = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)registerUploader:(id<GDTUploader>)backend target:(GDTTarget)target {
__weak GDTRegistrar *weakSelf = self;
dispatch_barrier_async(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
strongSelf->_targetToUploader[@(target)] = backend;
}
});
}
- (void)registerPrioritizer:(id<GDTPrioritizer>)prioritizer target:(GDTTarget)target {
__weak GDTRegistrar *weakSelf = self;
dispatch_barrier_async(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
strongSelf->_targetToPrioritizer[@(target)] = prioritizer;
}
});
}
- (NSMutableDictionary<NSNumber *, id<GDTUploader>> *)targetToUploader {
__block NSMutableDictionary<NSNumber *, id<GDTUploader>> *targetToUploader;
__weak GDTRegistrar *weakSelf = self;
dispatch_sync(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
targetToUploader = strongSelf->_targetToUploader;
}
});
return targetToUploader;
}
- (NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *)targetToPrioritizer {
__block NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *targetToPrioritizer;
__weak GDTRegistrar *weakSelf = self;
dispatch_sync(_registrarQueue, ^{
GDTRegistrar *strongSelf = weakSelf;
if (strongSelf) {
targetToPrioritizer = strongSelf->_targetToPrioritizer;
}
});
return targetToPrioritizer;
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillBackground:(nonnull GDTApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillBackground:)]) {
[uploader appWillBackground:app];
}
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillBackground:)]) {
[prioritizer appWillBackground:app];
}
}
});
}
- (void)appWillForeground:(nonnull GDTApplication *)app {
dispatch_async(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillForeground:)]) {
[uploader appWillForeground:app];
}
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillForeground:)]) {
[prioritizer appWillForeground:app];
}
}
});
}
- (void)appWillTerminate:(nonnull GDTApplication *)app {
dispatch_sync(_registrarQueue, ^{
for (id<GDTUploader> uploader in [self->_targetToUploader allValues]) {
if ([uploader respondsToSelector:@selector(appWillTerminate:)]) {
[uploader appWillTerminate:app];
}
}
for (id<GDTPrioritizer> prioritizer in [self->_targetToPrioritizer allValues]) {
if ([prioritizer respondsToSelector:@selector(appWillTerminate:)]) {
[prioritizer appWillTerminate:app];
}
}
});
}
@end

View File

@ -0,0 +1,321 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Private/GDTStorage.h"
#import "GDTLibrary/Private/GDTStorage_Private.h"
#import <GoogleDataTransport/GDTAssert.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <GoogleDataTransport/GDTLifecycle.h>
#import <GoogleDataTransport/GDTPrioritizer.h>
#import <GoogleDataTransport/GDTStoredEvent.h>
#import "GDTLibrary/Private/GDTEvent_Private.h"
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
#import "GDTLibrary/Private/GDTUploadCoordinator.h"
/** Creates and/or returns a singleton NSString that is the shared storage path.
*
* @return The SDK event storage path.
*/
static NSString *GDTStoragePath() {
static NSString *storagePath;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *cachePath =
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
storagePath = [NSString stringWithFormat:@"%@/google-sdks-events", cachePath];
});
return storagePath;
}
@implementation GDTStorage
+ (NSString *)archivePath {
static NSString *archivePath;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
archivePath = [GDTStoragePath() stringByAppendingPathComponent:@"GDTStorageArchive"];
});
return archivePath;
}
+ (instancetype)sharedInstance {
static GDTStorage *sharedStorage;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedStorage = [[GDTStorage alloc] init];
});
return sharedStorage;
}
- (instancetype)init {
self = [super init];
if (self) {
_storageQueue = dispatch_queue_create("com.google.GDTStorage", DISPATCH_QUEUE_SERIAL);
_targetToEventSet = [[NSMutableDictionary alloc] init];
_storedEvents = [[NSMutableOrderedSet alloc] init];
_uploadCoordinator = [GDTUploadCoordinator sharedInstance];
}
return self;
}
- (void)storeEvent:(GDTEvent *)event {
if (event == nil) {
return;
}
[self createEventDirectoryIfNotExists];
__block GDTBackgroundIdentifier bgID = GDTBackgroundIdentifierInvalid;
if (_runningInBackground) {
bgID = [[GDTApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
}];
}
dispatch_async(_storageQueue, ^{
// Check that a backend implementation is available for this target.
NSInteger target = event.target;
// Check that a prioritizer is available for this target.
id<GDTPrioritizer> prioritizer = [GDTRegistrar sharedInstance].targetToPrioritizer[@(target)];
GDTAssert(prioritizer, @"There's no prioritizer registered for the given target.");
// Write the transport bytes to disk, get a filename.
GDTAssert(event.dataObjectTransportBytes, @"The event should have been serialized to bytes");
NSURL *eventFile = [self saveEventBytesToDisk:event.dataObjectTransportBytes
eventHash:event.hash];
GDTDataFuture *dataFuture = [[GDTDataFuture alloc] initWithFileURL:eventFile];
GDTStoredEvent *storedEvent = [event storedEventWithDataFuture:dataFuture];
// Add event to tracking collections.
[self addEventToTrackingCollections:storedEvent];
// Have the prioritizer prioritize the event.
[prioritizer prioritizeEvent:storedEvent];
// Check the QoS, if it's high priority, notify the target that it has a high priority event.
if (event.qosTier == GDTEventQoSFast) {
[self.uploadCoordinator forceUploadForTarget:target];
}
// Write state to disk.
if (self->_runningInBackground) {
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
requiringSecureCoding:YES
error:nil];
[data writeToFile:[GDTStorage archivePath] atomically:YES];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
#endif
}
}
// If running in the background, save state to disk and end the associated background task.
if (bgID != GDTBackgroundIdentifierInvalid) {
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
});
}
- (void)removeEvents:(NSSet<GDTStoredEvent *> *)events {
NSSet<GDTStoredEvent *> *eventsToRemove = [events copy];
dispatch_async(_storageQueue, ^{
for (GDTStoredEvent *event in eventsToRemove) {
// Remove from disk, first and foremost.
NSError *error;
if (event.dataFuture.fileURL) {
NSURL *fileURL = event.dataFuture.fileURL;
[[NSFileManager defaultManager] removeItemAtURL:fileURL error:&error];
GDTAssert(error == nil, @"There was an error removing an event file: %@", error);
}
// Remove from the tracking collections.
[self.storedEvents removeObject:event];
[self.targetToEventSet[event.target] removeObject:event];
}
});
}
#pragma mark - Private helper methods
/** Creates the storage directory if it does not exist. */
- (void)createEventDirectoryIfNotExists {
NSError *error;
BOOL result = [[NSFileManager defaultManager] createDirectoryAtPath:GDTStoragePath()
withIntermediateDirectories:YES
attributes:0
error:&error];
if (!result || error) {
GDTLogError(GDTMCEDirectoryCreationError, @"Error creating the directory: %@", error);
}
}
/** Saves the event's dataObjectTransportBytes to a file using NSData mechanisms.
*
* @note This method should only be called from a method within a block on _storageQueue to maintain
* thread safety.
*
* @param transportBytes The transport bytes of the event.
* @param eventHash The hash value of the event.
* @return The filename
*/
- (NSURL *)saveEventBytesToDisk:(NSData *)transportBytes eventHash:(NSUInteger)eventHash {
NSString *storagePath = GDTStoragePath();
NSString *event = [NSString stringWithFormat:@"event-%lu", (unsigned long)eventHash];
NSURL *eventFilePath = [NSURL fileURLWithPath:[storagePath stringByAppendingPathComponent:event]];
GDTAssert(![[NSFileManager defaultManager] fileExistsAtPath:eventFilePath.path],
@"An event shouldn't already exist at this path: %@", eventFilePath.path);
BOOL writingSuccess = [transportBytes writeToURL:eventFilePath atomically:YES];
if (!writingSuccess) {
GDTLogError(GDTMCEFileWriteError, @"An event file could not be written: %@", eventFilePath);
}
return eventFilePath;
}
/** Adds the event to internal tracking collections.
*
* @note This method should only be called from a method within a block on _storageQueue to maintain
* thread safety.
*
* @param event The event to track.
*/
- (void)addEventToTrackingCollections:(GDTStoredEvent *)event {
[_storedEvents addObject:event];
NSMutableSet<GDTStoredEvent *> *events = self.targetToEventSet[event.target];
events = events ? events : [[NSMutableSet alloc] init];
[events addObject:event];
_targetToEventSet[event.target] = events;
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillForeground:(GDTApplication *)app {
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSData dataWithContentsOfFile:[GDTStorage archivePath]];
[NSKeyedUnarchiver unarchivedObjectOfClass:[GDTStorage class] fromData:data error:nil];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedUnarchiver unarchiveObjectWithFile:[GDTStorage archivePath]];
#endif
}
}
- (void)appWillBackground:(GDTApplication *)app {
self->_runningInBackground = YES;
dispatch_async(_storageQueue, ^{
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
requiringSecureCoding:YES
error:nil];
[data writeToFile:[GDTStorage archivePath] atomically:YES];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
#endif
}
});
// Create an immediate background task to run until the end of the current queue of work.
__block GDTBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
}];
dispatch_async(_storageQueue, ^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
});
}
- (void)appWillTerminate:(GDTApplication *)application {
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
requiringSecureCoding:YES
error:nil];
[data writeToFile:[GDTStorage archivePath] atomically:YES];
} else {
#if !defined(TARGET_OS_MACCATALYST)
[NSKeyedArchiver archiveRootObject:self toFile:[GDTStorage archivePath]];
#endif
}
}
#pragma mark - NSSecureCoding
/** The NSKeyedCoder key for the storedEvents property. */
static NSString *const kGDTStorageStoredEventsKey = @"GDTStorageStoredEventsKey";
/** The NSKeyedCoder key for the targetToEventSet property. */
static NSString *const kGDTStorageTargetToEventSetKey = @"GDTStorageTargetToEventSetKey";
/** The NSKeyedCoder key for the uploadCoordinator property. */
static NSString *const kGDTStorageUploadCoordinatorKey = @"GDTStorageUploadCoordinatorKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
// Create the singleton and populate its ivars.
GDTStorage *sharedInstance = [self.class sharedInstance];
dispatch_sync(sharedInstance.storageQueue, ^{
NSSet *classes =
[NSSet setWithObjects:[NSMutableOrderedSet class], [GDTStoredEvent class], nil];
sharedInstance->_storedEvents = [aDecoder decodeObjectOfClasses:classes
forKey:kGDTStorageStoredEventsKey];
classes = [NSSet setWithObjects:[NSMutableDictionary class], [NSMutableSet class],
[GDTStoredEvent class], nil];
sharedInstance->_targetToEventSet =
[aDecoder decodeObjectOfClasses:classes forKey:kGDTStorageTargetToEventSetKey];
sharedInstance->_uploadCoordinator =
[aDecoder decodeObjectOfClass:[GDTUploadCoordinator class]
forKey:kGDTStorageUploadCoordinatorKey];
});
return sharedInstance;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
GDTStorage *sharedInstance = [self.class sharedInstance];
NSMutableOrderedSet<GDTStoredEvent *> *storedEvents = sharedInstance->_storedEvents;
if (storedEvents) {
[aCoder encodeObject:storedEvents forKey:kGDTStorageStoredEventsKey];
}
NSMutableDictionary<NSNumber *, NSMutableSet<GDTStoredEvent *> *> *targetToEventSet =
sharedInstance->_targetToEventSet;
if (targetToEventSet) {
[aCoder encodeObject:targetToEventSet forKey:kGDTStorageTargetToEventSetKey];
}
GDTUploadCoordinator *uploadCoordinator = sharedInstance->_uploadCoordinator;
if (uploadCoordinator) {
[aCoder encodeObject:uploadCoordinator forKey:kGDTStorageUploadCoordinatorKey];
}
}
@end

View File

@ -0,0 +1,94 @@
/*
* 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 <GoogleDataTransport/GDTStoredEvent.h>
#import <GoogleDataTransport/GDTClock.h>
#import "GDTLibrary/Private/GDTStorage_Private.h"
@implementation GDTStoredEvent
- (instancetype)initWithEvent:(GDTEvent *)event dataFuture:(nonnull GDTDataFuture *)dataFuture {
self = [super init];
if (self) {
_dataFuture = dataFuture;
_mappingID = event.mappingID;
_target = @(event.target);
_qosTier = event.qosTier;
_clockSnapshot = event.clockSnapshot;
_customPrioritizationParams = event.customPrioritizationParams;
}
return self;
}
#pragma mark - NSSecureCoding
/** Coding key for the dataFuture ivar. */
static NSString *kDataFutureKey = @"GDTStoredEventDataFutureKey";
/** Coding key for mappingID ivar. */
static NSString *kMappingIDKey = @"GDTStoredEventMappingIDKey";
/** Coding key for target ivar. */
static NSString *kTargetKey = @"GDTStoredEventTargetKey";
/** Coding key for qosTier ivar. */
static NSString *kQosTierKey = @"GDTStoredEventQosTierKey";
/** Coding key for clockSnapshot ivar. */
static NSString *kClockSnapshotKey = @"GDTStoredEventClockSnapshotKey";
/** Coding key for customPrioritizationParams ivar. */
static NSString *kCustomPrioritizationParamsKey = @"GDTStoredEventcustomPrioritizationParamsKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:_dataFuture forKey:kDataFutureKey];
[aCoder encodeObject:_mappingID forKey:kMappingIDKey];
[aCoder encodeObject:_target forKey:kTargetKey];
[aCoder encodeObject:@(_qosTier) forKey:kQosTierKey];
[aCoder encodeObject:_clockSnapshot forKey:kClockSnapshotKey];
[aCoder encodeObject:_customPrioritizationParams forKey:kCustomPrioritizationParamsKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
self = [self init];
if (self) {
_dataFuture = [aDecoder decodeObjectOfClass:[GDTDataFuture class] forKey:kDataFutureKey];
_mappingID = [aDecoder decodeObjectOfClass:[NSString class] forKey:kMappingIDKey];
_target = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kTargetKey];
NSNumber *qosTier = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:kQosTierKey];
_qosTier = [qosTier intValue];
_clockSnapshot = [aDecoder decodeObjectOfClass:[GDTClock class] forKey:kClockSnapshotKey];
_customPrioritizationParams = [aDecoder decodeObjectOfClass:[NSDictionary class]
forKey:kCustomPrioritizationParamsKey];
}
return self;
}
- (BOOL)isEqual:(GDTStoredEvent *)other {
return [self hash] == [other hash];
}
- (NSUInteger)hash {
return [_dataFuture hash] ^ [_mappingID hash] ^ [_target hash] ^ [_clockSnapshot hash] ^ _qosTier;
}
@end

View File

@ -0,0 +1,112 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Private/GDTTransformer.h"
#import "GDTLibrary/Private/GDTTransformer_Private.h"
#import <GoogleDataTransport/GDTAssert.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <GoogleDataTransport/GDTEventTransformer.h>
#import <GoogleDataTransport/GDTLifecycle.h>
#import "GDTLibrary/Private/GDTStorage.h"
@implementation GDTTransformer
+ (instancetype)sharedInstance {
static GDTTransformer *eventTransformer;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
eventTransformer = [[self alloc] init];
});
return eventTransformer;
}
- (instancetype)init {
self = [super init];
if (self) {
_eventWritingQueue = dispatch_queue_create("com.google.GDTTransformer", DISPATCH_QUEUE_SERIAL);
_storageInstance = [GDTStorage sharedInstance];
}
return self;
}
- (void)transformEvent:(GDTEvent *)event
withTransformers:(NSArray<id<GDTEventTransformer>> *)transformers {
GDTAssert(event, @"You can't write a nil event");
__block GDTBackgroundIdentifier bgID = GDTBackgroundIdentifierInvalid;
if (_runningInBackground) {
bgID = [[GDTApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
}];
}
dispatch_async(_eventWritingQueue, ^{
GDTEvent *transformedEvent = event;
for (id<GDTEventTransformer> transformer in transformers) {
if ([transformer respondsToSelector:@selector(transform:)]) {
transformedEvent = [transformer transform:transformedEvent];
if (!transformedEvent) {
return;
}
} else {
GDTLogError(GDTMCETransformerDoesntImplementTransform,
@"Transformer doesn't implement transform: %@", transformer);
return;
}
}
[self.storageInstance storeEvent:transformedEvent];
if (self->_runningInBackground) {
[[GDTApplication sharedApplication] endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
});
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillForeground:(GDTApplication *)app {
dispatch_async(_eventWritingQueue, ^{
self->_runningInBackground = NO;
});
}
- (void)appWillBackground:(GDTApplication *)app {
// Create an immediate background task to run until the end of the current queue of work.
__block GDTBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
}];
dispatch_async(_eventWritingQueue, ^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
});
}
- (void)appWillTerminate:(GDTApplication *)application {
// Flush the queue immediately.
dispatch_sync(_eventWritingQueue, ^{
});
}
@end

View File

@ -0,0 +1,68 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <GoogleDataTransport/GDTTransport.h>
#import "GDTLibrary/Private/GDTTransport_Private.h"
#import <GoogleDataTransport/GDTAssert.h>
#import <GoogleDataTransport/GDTClock.h>
#import <GoogleDataTransport/GDTEvent.h>
#import "GDTLibrary/Private/GDTTransformer.h"
@implementation GDTTransport
- (instancetype)initWithMappingID:(NSString *)mappingID
transformers:(nullable NSArray<id<GDTEventTransformer>> *)transformers
target:(NSInteger)target {
GDTAssert(mappingID.length > 0, @"A mapping ID cannot be nil or empty");
GDTAssert(target > 0, @"A target cannot be negative or 0");
if (mappingID == nil || mappingID.length == 0 || target <= 0) {
return nil;
}
self = [super init];
if (self) {
_mappingID = mappingID;
_transformers = transformers;
_target = target;
_transformerInstance = [GDTTransformer sharedInstance];
}
return self;
}
- (void)sendTelemetryEvent:(GDTEvent *)event {
// TODO: Determine if sending an event before registration is allowed.
GDTAssert(event, @"You can't send a nil event");
GDTEvent *copiedEvent = [event copy];
copiedEvent.qosTier = GDTEventQoSTelemetry;
copiedEvent.clockSnapshot = [GDTClock snapshot];
[self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers];
}
- (void)sendDataEvent:(GDTEvent *)event {
// TODO: Determine if sending an event before registration is allowed.
GDTAssert(event, @"You can't send a nil event");
GDTAssert(event.qosTier != GDTEventQoSTelemetry, @"Use -sendTelemetryEvent, please.");
GDTEvent *copiedEvent = [event copy];
copiedEvent.clockSnapshot = [GDTClock snapshot];
[self.transformerInstance transformEvent:copiedEvent withTransformers:_transformers];
}
- (GDTEvent *)eventForTransport {
return [[GDTEvent alloc] initWithMappingID:_mappingID target:_target];
}
@end

View File

@ -0,0 +1,274 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Private/GDTUploadCoordinator.h"
#import <GoogleDataTransport/GDTAssert.h>
#import <GoogleDataTransport/GDTClock.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import "GDTLibrary/Private/GDTReachability.h"
#import "GDTLibrary/Private/GDTRegistrar_Private.h"
#import "GDTLibrary/Private/GDTStorage.h"
@implementation GDTUploadCoordinator
+ (instancetype)sharedInstance {
static GDTUploadCoordinator *sharedUploader;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedUploader = [[GDTUploadCoordinator alloc] init];
[sharedUploader startTimer];
});
return sharedUploader;
}
- (instancetype)init {
self = [super init];
if (self) {
_coordinationQueue =
dispatch_queue_create("com.google.GDTUploadCoordinator", DISPATCH_QUEUE_SERIAL);
_registrar = [GDTRegistrar sharedInstance];
_timerInterval = 30 * NSEC_PER_SEC;
_timerLeeway = 5 * NSEC_PER_SEC;
_targetToInFlightPackages = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)forceUploadForTarget:(GDTTarget)target {
dispatch_async(_coordinationQueue, ^{
GDTUploadConditions conditions = [self uploadConditions];
conditions |= GDTUploadConditionHighPriority;
[self uploadTargets:@[ @(target) ] conditions:conditions];
});
}
#pragma mark - Property overrides
// GDTStorage and GDTUploadCoordinator +sharedInstance methods call each other, so this breaks
// the loop.
- (GDTStorage *)storage {
if (!_storage) {
_storage = [GDTStorage sharedInstance];
}
return _storage;
}
#pragma mark - Private helper methods
/** Starts a timer that checks whether or not events can be uploaded at regular intervals. It will
* check the next-upload clocks of all targets to determine if an upload attempt can be made.
*/
- (void)startTimer {
dispatch_sync(_coordinationQueue, ^{
self->_timer =
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self->_coordinationQueue);
dispatch_source_set_timer(self->_timer, DISPATCH_TIME_NOW, self->_timerInterval,
self->_timerLeeway);
dispatch_source_set_event_handler(self->_timer, ^{
if (!self->_runningInBackground) {
GDTUploadConditions conditions = [self uploadConditions];
[self uploadTargets:[self.registrar.targetToUploader allKeys] conditions:conditions];
}
});
dispatch_resume(self->_timer);
});
}
/** Stops the currently running timer. */
- (void)stopTimer {
if (_timer) {
dispatch_source_cancel(_timer);
}
}
/** Triggers the uploader implementations for the given targets to upload.
*
* @param targets An array of targets to trigger.
* @param conditions The set of upload conditions.
*/
- (void)uploadTargets:(NSArray<NSNumber *> *)targets conditions:(GDTUploadConditions)conditions {
dispatch_async(_coordinationQueue, ^{
if ((conditions & GDTUploadConditionNoNetwork) == GDTUploadConditionNoNetwork) {
return;
}
for (NSNumber *target in targets) {
// Don't trigger uploads for targets that have an in-flight package already.
if (self->_targetToInFlightPackages[target]) {
continue;
}
// Ask the uploader if they can upload and do so, if it can.
id<GDTUploader> uploader = self.registrar.targetToUploader[target];
if ([uploader readyToUploadWithConditions:conditions]) {
id<GDTPrioritizer> prioritizer = self.registrar.targetToPrioritizer[target];
GDTUploadPackage *package = [prioritizer uploadPackageWithConditions:conditions];
if (package.events.count) {
self->_targetToInFlightPackages[target] = package;
[uploader uploadPackage:package];
} else {
[package completeDelivery];
}
}
}
});
}
/** Returns the current upload conditions after making determinations about the network connection.
*
* @return The current upload conditions.
*/
- (GDTUploadConditions)uploadConditions {
SCNetworkReachabilityFlags currentFlags = [GDTReachability currentFlags];
BOOL reachable =
(currentFlags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable;
BOOL connectionRequired = (currentFlags & kSCNetworkReachabilityFlagsConnectionRequired) ==
kSCNetworkReachabilityFlagsConnectionRequired;
BOOL networkConnected = reachable && !connectionRequired;
if (!networkConnected) {
return GDTUploadConditionNoNetwork;
}
BOOL isWWAN = GDTReachabilityFlagsContainWWAN(currentFlags);
if (isWWAN) {
return GDTUploadConditionMobileData;
} else {
return GDTUploadConditionWifiData;
}
}
#pragma mark - NSSecureCoding support
/** The NSKeyedCoder key for the targetToInFlightPackages property. */
static NSString *const ktargetToInFlightPackagesKey =
@"GDTUploadCoordinatortargetToInFlightPackages";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
GDTUploadCoordinator *sharedCoordinator = [GDTUploadCoordinator sharedInstance];
@try {
sharedCoordinator->_targetToInFlightPackages =
[aDecoder decodeObjectOfClass:[NSMutableDictionary class]
forKey:ktargetToInFlightPackagesKey];
} @catch (NSException *exception) {
sharedCoordinator->_targetToInFlightPackages = [NSMutableDictionary dictionary];
}
return sharedCoordinator;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
// All packages that have been given to uploaders need to be tracked so that their expiration
// timers can be called.
if (_targetToInFlightPackages.count > 0) {
[aCoder encodeObject:_targetToInFlightPackages forKey:ktargetToInFlightPackagesKey];
}
}
#pragma mark - GDTLifecycleProtocol
- (void)appWillForeground:(GDTApplication *)app {
// Not entirely thread-safe, but it should be fine.
self->_runningInBackground = NO;
[self startTimer];
}
- (void)appWillBackground:(GDTApplication *)app {
// Not entirely thread-safe, but it should be fine.
self->_runningInBackground = YES;
// Should be thread-safe. If it ends up not being, put this in a dispatch_sync.
[self stopTimer];
// Create an immediate background task to run until the end of the current queue of work.
__block GDTBackgroundIdentifier bgID = [app beginBackgroundTaskWithExpirationHandler:^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
}];
dispatch_async(_coordinationQueue, ^{
if (bgID != GDTBackgroundIdentifierInvalid) {
[app endBackgroundTask:bgID];
bgID = GDTBackgroundIdentifierInvalid;
}
});
}
- (void)appWillTerminate:(GDTApplication *)application {
dispatch_sync(_coordinationQueue, ^{
[self stopTimer];
});
}
#pragma mark - GDTUploadPackageProtocol
- (void)packageDelivered:(GDTUploadPackage *)package successful:(BOOL)successful {
if (!_coordinationQueue) {
return;
}
dispatch_async(_coordinationQueue, ^{
NSNumber *targetNumber = @(package.target);
NSMutableDictionary<NSNumber *, GDTUploadPackage *> *targetToInFlightPackages =
self->_targetToInFlightPackages;
GDTRegistrar *registrar = self->_registrar;
if (targetToInFlightPackages) {
[targetToInFlightPackages removeObjectForKey:targetNumber];
}
if (registrar) {
id<GDTPrioritizer> prioritizer = registrar.targetToPrioritizer[targetNumber];
if (!prioritizer) {
GDTLogError(GDTMCEPrioritizerError,
@"A prioritizer should be registered for this target: %@", targetNumber);
}
if ([prioritizer respondsToSelector:@selector(packageDelivered:successful:)]) {
[prioritizer packageDelivered:package successful:successful];
}
}
[self.storage removeEvents:package.events];
});
}
- (void)packageExpired:(GDTUploadPackage *)package {
if (!_coordinationQueue) {
return;
}
dispatch_async(_coordinationQueue, ^{
NSNumber *targetNumber = @(package.target);
NSMutableDictionary<NSNumber *, GDTUploadPackage *> *targetToInFlightPackages =
self->_targetToInFlightPackages;
GDTRegistrar *registrar = self->_registrar;
if (targetToInFlightPackages) {
[targetToInFlightPackages removeObjectForKey:targetNumber];
}
if (registrar) {
id<GDTPrioritizer> prioritizer = registrar.targetToPrioritizer[targetNumber];
id<GDTUploader> uploader = registrar.targetToUploader[targetNumber];
if ([prioritizer respondsToSelector:@selector(packageExpired:)]) {
[prioritizer packageExpired:package];
}
if ([uploader respondsToSelector:@selector(packageExpired:)]) {
[uploader packageExpired:package];
}
}
});
}
@end

View File

@ -0,0 +1,154 @@
/*
* 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 <GoogleDataTransport/GDTUploadPackage.h>
#import <GoogleDataTransport/GDTClock.h>
#import <GoogleDataTransport/GDTConsoleLogger.h>
#import <GoogleDataTransport/GDTStoredEvent.h>
#import "GDTLibrary/Private/GDTStorage_Private.h"
#import "GDTLibrary/Private/GDTUploadCoordinator.h"
#import "GDTLibrary/Private/GDTUploadPackage_Private.h"
@implementation GDTUploadPackage {
/** If YES, the package's -completeDelivery method has been called. */
BOOL _isDelivered;
/** If YES, is being handled by the handler. */
BOOL _isHandled;
/** A timer that will regularly check to see whether this package has expired or not. */
NSTimer *_expirationTimer;
}
- (instancetype)initWithTarget:(GDTTarget)target {
self = [super init];
if (self) {
_target = target;
_storage = [GDTStorage sharedInstance];
_deliverByTime = [GDTClock clockSnapshotInTheFuture:180000];
_handler = [GDTUploadCoordinator sharedInstance];
_expirationTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:self
selector:@selector(checkIfPackageIsExpired:)
userInfo:nil
repeats:YES];
}
return self;
}
- (instancetype)copy {
GDTUploadPackage *newPackage = [[GDTUploadPackage alloc] initWithTarget:_target];
newPackage->_events = [_events copy];
return newPackage;
}
- (NSUInteger)hash {
return [_events hash];
}
- (BOOL)isEqual:(id)object {
return [self hash] == [object hash];
}
- (void)dealloc {
[_expirationTimer invalidate];
}
- (void)setStorage:(GDTStorage *)storage {
if (storage != _storage) {
_storage = storage;
}
}
- (void)completeDelivery {
if (_isDelivered) {
GDTLogError(GDTMCEDeliverTwice, @"%@",
@"It's an API violation to call -completeDelivery twice.");
}
_isDelivered = YES;
if (!_isHandled && _handler &&
[_handler respondsToSelector:@selector(packageDelivered:successful:)]) {
[_expirationTimer invalidate];
_isHandled = YES;
[_handler packageDelivered:self successful:YES];
}
}
- (void)retryDeliveryInTheFuture {
if (!_isHandled && _handler &&
[_handler respondsToSelector:@selector(packageDelivered:successful:)]) {
[_expirationTimer invalidate];
_isHandled = YES;
[_handler packageDelivered:self successful:NO];
}
}
- (void)checkIfPackageIsExpired:(NSTimer *)timer {
if ([[GDTClock snapshot] isAfter:_deliverByTime]) {
if (_handler && [_handler respondsToSelector:@selector(packageExpired:)]) {
_isHandled = YES;
[_expirationTimer invalidate];
[_handler packageExpired:self];
}
}
}
#pragma mark - NSSecureCoding
/** The keyed archiver key for the events property. */
static NSString *const kEventsKey = @"GDTUploadPackageEventsKey";
/** The keyed archiver key for the _isHandled property. */
static NSString *const kDeliverByTimeKey = @"GDTUploadPackageDeliveryByTimeKey";
/** The keyed archiver key for the _isHandled ivar. */
static NSString *const kIsHandledKey = @"GDTUploadPackageIsHandledKey";
/** The keyed archiver key for the handler property. */
static NSString *const kHandlerKey = @"GDTUploadPackageHandlerKey";
/** The keyed archiver key for the target property. */
static NSString *const kTargetKey = @"GDTUploadPackageTargetKey";
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:_events forKey:kEventsKey];
[aCoder encodeObject:_deliverByTime forKey:kDeliverByTimeKey];
[aCoder encodeBool:_isHandled forKey:kIsHandledKey];
[aCoder encodeObject:_handler forKey:kHandlerKey];
[aCoder encodeInteger:_target forKey:kTargetKey];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
GDTTarget target = [aDecoder decodeIntegerForKey:kTargetKey];
self = [self initWithTarget:target];
if (self) {
NSSet *classes = [NSSet setWithObjects:[NSSet class], [GDTStoredEvent class], nil];
_events = [aDecoder decodeObjectOfClasses:classes forKey:kEventsKey];
_deliverByTime = [aDecoder decodeObjectOfClass:[GDTClock class] forKey:kDeliverByTimeKey];
_isHandled = [aDecoder decodeBoolForKey:kIsHandledKey];
// _handler isn't technically NSSecureCoding, because we don't know the class of this object.
// but it gets decoded anyway.
}
return self;
}
@end

View File

@ -0,0 +1,30 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <GoogleDataTransport/GDTEvent.h>
#import <GoogleDataTransport/GDTClock.h>
NS_ASSUME_NONNULL_BEGIN
@interface GDTEvent ()
/** The serialized bytes of the event data object. */
@property(nonatomic) NSData *dataObjectTransportBytes;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,31 @@
/*
* Copyright 2019 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SCNetworkReachability.h>
NS_ASSUME_NONNULL_BEGIN
/** This class helps determine upload conditions by determining connectivity. */
@interface GDTReachability : NSObject
/** The current set flags indicating network conditions */
+ (SCNetworkReachabilityFlags)currentFlags;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,30 @@
/*
* 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 "GDTLibrary/Private/GDTReachability.h"
@interface GDTReachability ()
/** Allows manually setting the flags for testing purposes. */
@property(nonatomic, readwrite) SCNetworkReachabilityFlags flags;
/** Creates/returns the singleton instance of this class.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance;
@end

View File

@ -0,0 +1,35 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <GoogleDataTransport/GDTRegistrar.h>
@interface GDTRegistrar ()
NS_ASSUME_NONNULL_BEGIN
/** The concurrent queue on which all registration occurs. */
@property(nonatomic, readonly) dispatch_queue_t registrarQueue;
/** A map of targets to backend implementations. */
@property(atomic, readonly) NSMutableDictionary<NSNumber *, id<GDTUploader>> *targetToUploader;
/** A map of targets to prioritizer implementations. */
@property(atomic, readonly)
NSMutableDictionary<NSNumber *, id<GDTPrioritizer>> *targetToPrioritizer;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,50 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTLifecycle.h>
@class GDTEvent;
@class GDTStoredEvent;
NS_ASSUME_NONNULL_BEGIN
/** Manages the storage of events. This class is thread-safe. */
@interface GDTStorage : NSObject <NSSecureCoding, GDTLifecycleProtocol>
/** Creates and/or returns the storage singleton.
*
* @return The storage singleton.
*/
+ (instancetype)sharedInstance;
/** Stores event.dataObjectTransportBytes into a shared on-device folder and tracks the event via
* a GDTStoredEvent instance.
*
* @param event The event to store.
*/
- (void)storeEvent:(GDTEvent *)event;
/** Removes a set of events from storage specified by their hash.
*
* @param events The set of stored events to remove.
*/
- (void)removeEvents:(NSSet<GDTStoredEvent *> *)events;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,51 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Private/GDTStorage.h"
@class GDTUploadCoordinator;
NS_ASSUME_NONNULL_BEGIN
@interface GDTStorage ()
/** The queue on which all storage work will occur. */
@property(nonatomic) dispatch_queue_t storageQueue;
/** A map of targets to a set of stored events. */
@property(nonatomic)
NSMutableDictionary<NSNumber *, NSMutableSet<GDTStoredEvent *> *> *targetToEventSet;
/** All the events that have been stored. */
@property(readonly, nonatomic) NSMutableOrderedSet<GDTStoredEvent *> *storedEvents;
/** The upload coordinator instance used by this storage instance. */
@property(nonatomic) GDTUploadCoordinator *uploadCoordinator;
/** If YES, every call to -storeLog results in background task and serializes the singleton to disk.
*/
@property(nonatomic) BOOL runningInBackground;
/** Returns the path to the keyed archive of the singleton. This is where the singleton is saved
* to disk during certain app lifecycle events.
*
* @return File path to serialized singleton.
*/
+ (NSString *)archivePath;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,54 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTLifecycle.h>
@class GDTEvent;
@protocol GDTEventTransformer;
NS_ASSUME_NONNULL_BEGIN
/** Manages the transforming of events. It's desirable for this to be its own class
* because running all events through a single instance ensures that transformers are thread-safe.
* Having a per-transport queue to run on isn't sufficient because transformer objects could
* maintain state (or at least, there's nothing to stop them from doing that) and the same instances
* may be used across multiple instances.
*/
@interface GDTTransformer : NSObject <GDTLifecycleProtocol>
/** Instantiates or returns the event transformer singleton.
*
* @return The singleton instance of the event transformer.
*/
+ (instancetype)sharedInstance;
/** Writes the result of applying the given transformers' -transform method on the given event.
*
* @note If the app is suspended, a background task will be created to complete work in-progress,
* but this method will not send any further events until the app is resumed.
*
* @param event The event to apply transformers on.
* @param transformers The list of transformers to apply.
*/
- (void)transformEvent:(GDTEvent *)event
withTransformers:(nullable NSArray<id<GDTEventTransformer>> *)transformers;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,36 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import "GDTLibrary/Private/GDTTransformer.h"
@class GDTStorage;
NS_ASSUME_NONNULL_BEGIN
@interface GDTTransformer ()
/** The queue on which all work will occur. */
@property(nonatomic) dispatch_queue_t eventWritingQueue;
/** The storage instance used to store events. Should only be used to inject a testing fake. */
@property(nonatomic) GDTStorage *storageInstance;
/** If YES, every call to -transformEvent will result in a background task. */
@property(nonatomic, readonly) BOOL runningInBackground;
@end
NS_ASSUME_NONNULL_END

View File

@ -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 <GoogleDataTransport/GDTTransport.h>
@class GDTTransformer;
NS_ASSUME_NONNULL_BEGIN
@interface GDTTransport ()
/** The mapping identifier that the target backend will use to map the transport bytes to proto. */
@property(nonatomic) NSString *mappingID;
/** The transformers that will operate on events sent by this transport. */
@property(nonatomic) NSArray<id<GDTEventTransformer>> *transformers;
/** The target backend of this transport. */
@property(nonatomic) NSInteger target;
/** The transformer instance to used to transform events. Allows injecting a fake during testing. */
@property(nonatomic) GDTTransformer *transformerInstance;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,80 @@
/*
* Copyright 2018 Google
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <GoogleDataTransport/GDTLifecycle.h>
#import <GoogleDataTransport/GDTRegistrar.h>
#import "GDTLibrary/Private/GDTUploadPackage_Private.h"
@class GDTClock;
@class GDTStorage;
NS_ASSUME_NONNULL_BEGIN
/** This class connects storage and uploader implementations, providing events to an uploader
* and informing the storage what events were successfully uploaded or not.
*/
@interface GDTUploadCoordinator
: NSObject <NSSecureCoding, GDTLifecycleProtocol, GDTUploadPackageProtocol>
/** The queue on which all upload coordination will occur. Also used by a dispatch timer. */
/** Creates and/or returrns the singleton.
*
* @return The singleton instance of this class.
*/
+ (instancetype)sharedInstance;
@property(nonatomic, readonly) dispatch_queue_t coordinationQueue;
/** A timer that will causes regular checks for events to upload. */
@property(nonatomic, readonly) dispatch_source_t timer;
/** The interval the timer will fire. */
@property(nonatomic, readonly) uint64_t timerInterval;
/** Some leeway given to libdispatch for the timer interval event. */
@property(nonatomic, readonly) uint64_t timerLeeway;
/** The map of targets to in-flight packages. */
@property(nonatomic, readonly)
NSMutableDictionary<NSNumber *, GDTUploadPackage *> *targetToInFlightPackages;
/** The storage object the coordinator will use. Generally used for testing. */
@property(nonatomic) GDTStorage *storage;
/** The registrar object the coordinator will use. Generally used for testing. */
@property(nonatomic) GDTRegistrar *registrar;
/** If YES, completion and other operations will result in serializing the singleton to disk. */
@property(nonatomic, readonly) BOOL runningInBackground;
/** Forces the backend specified by the target to upload the provided set of events. This should
* only ever happen when the QoS tier of an event requires it.
*
* @param target The target that should force an upload.
*/
- (void)forceUploadForTarget:(GDTTarget)target;
/** Starts the upload timer. */
- (void)startTimer;
/** Stops the upload timer from running. */
- (void)stopTimer;
@end
NS_ASSUME_NONNULL_END

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