From 8137b8575a36c3ca9d617a2bcb88ab57b0a1b4d7 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Mon, 21 Oct 2019 11:52:32 -0700 Subject: [PATCH 1/9] [MM-18983] Listen to safeAreaInsetsForRootViewDidChange over Dimension change (#3413) * Listen to safeAreaInsetsForRootViewDidChange over Dimension change * Add snapshots --- .../safe_area_view.ios.test.js.snap | 32 ++++ .../safe_area_view/safe_area_view.ios.js | 16 +- .../safe_area_view/safe_area_view.ios.test.js | 151 ++++++++++++++++++ app/screens/permalink/index.js | 4 - app/screens/permalink/permalink.js | 12 +- app/screens/permalink/permalink.test.js | 2 - 6 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 app/components/safe_area_view/__snapshots__/safe_area_view.ios.test.js.snap create mode 100644 app/components/safe_area_view/safe_area_view.ios.test.js diff --git a/app/components/safe_area_view/__snapshots__/safe_area_view.ios.test.js.snap b/app/components/safe_area_view/__snapshots__/safe_area_view.ios.test.js.snap new file mode 100644 index 000000000..de2a8784e --- /dev/null +++ b/app/components/safe_area_view/__snapshots__/safe_area_view.ios.test.js.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SafeAreaIos should match snapshot 1`] = ` + + + + +`; diff --git a/app/components/safe_area_view/safe_area_view.ios.js b/app/components/safe_area_view/safe_area_view.ios.js index ada7d83e4..b517e86ba 100644 --- a/app/components/safe_area_view/safe_area_view.ios.js +++ b/app/components/safe_area_view/safe_area_view.ios.js @@ -3,7 +3,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; -import {Dimensions, Keyboard, NativeModules, View} from 'react-native'; +import {Keyboard, NativeModules, View} from 'react-native'; import SafeArea from 'react-native-safe-area'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; @@ -57,17 +57,16 @@ export default class SafeAreaIos extends PureComponent { componentDidMount() { this.mounted = true; - Dimensions.addEventListener('change', this.getSafeAreaInsets); + SafeArea.addEventListener('safeAreaInsetsForRootViewDidChange', this.onSafeAreaInsetsForRootViewChange); EventEmitter.on('update_safe_area_view', this.getSafeAreaInsets); this.keyboardDidShowListener = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow); this.keyboardDidHideListener = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide); this.getSafeAreaInsets(); - this.getStatusBarHeight(); } componentWillUnmount() { - Dimensions.removeEventListener('change', this.getSafeAreaInsets); + SafeArea.removeEventListener('safeAreaInsetsForRootViewDidChange', this.onSafeAreaInsetsForRootViewChange); EventEmitter.off('update_safe_area_view', this.getSafeAreaInsets); this.keyboardDidShowListener.remove(); this.keyboardDidHideListener.remove(); @@ -103,6 +102,15 @@ export default class SafeAreaIos extends PureComponent { } }; + onSafeAreaInsetsForRootViewChange = (result) => { + const {safeAreaInsets} = result; + + if (this.mounted && (DeviceTypes.IS_IPHONE_WITH_INSETS || mattermostManaged.hasSafeAreaInsets)) { + this.getStatusBarHeight(); + this.setState({safeAreaInsets}); + } + } + keyboardWillHide = () => { this.setState({keyboard: false}); }; diff --git a/app/components/safe_area_view/safe_area_view.ios.test.js b/app/components/safe_area_view/safe_area_view.ios.test.js new file mode 100644 index 000000000..85c64b572 --- /dev/null +++ b/app/components/safe_area_view/safe_area_view.ios.test.js @@ -0,0 +1,151 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React from 'react'; +import SafeArea from 'react-native-safe-area'; +import {shallow} from 'enzyme'; + +import Preferences from 'mattermost-redux/constants/preferences'; + +import {DeviceTypes} from 'app/constants'; +import mattermostManaged from 'app/mattermost_managed'; + +import SafeAreaIos from './safe_area_view.ios'; + +describe('SafeAreaIos', () => { + const baseProps = { + children: [], + keyboardOffset: 100, + useLandscapeMargin: false, + theme: Preferences.THEMES.default, + }; + + const TEST_INSETS_1 = { + safeAreaInsets: { + top: 123, + left: 123, + bottom: 123, + right: 123, + }, + }; + + const TEST_INSETS_2 = { + safeAreaInsets: { + top: 456, + left: 456, + bottom: 456, + right: 456, + }, + }; + + SafeArea.getSafeAreaInsetsForRootView = jest.fn().mockImplementation(() => { + return Promise.resolve(TEST_INSETS_1); + }); + + test('should match snapshot', () => { + const wrapper = shallow( + + ); + + expect(wrapper.getElement()).toMatchSnapshot(); + }); + + test('should get safe area insets on mount if DeviceTypes.IS_IPHONE_WITH_INSETS is true', async () => { + DeviceTypes.IS_IPHONE_WITH_INSETS = true; + mattermostManaged.hasSafeAreaInsets = false; + + const wrapper = shallow( + + ); + + expect(SafeArea.getSafeAreaInsetsForRootView).toHaveBeenCalled(); + await SafeArea.getSafeAreaInsetsForRootView(); + expect(wrapper.state().safeAreaInsets).toEqual(TEST_INSETS_1.safeAreaInsets); + }); + + test('should get safe area insets on mount if mattermostManaged.hasSafeAreaInsets is true', async () => { + DeviceTypes.IS_IPHONE_WITH_INSETS = false; + mattermostManaged.hasSafeAreaInsets = true; + + const wrapper = shallow( + + ); + + expect(SafeArea.getSafeAreaInsetsForRootView).toHaveBeenCalled(); + await SafeArea.getSafeAreaInsetsForRootView(); + expect(wrapper.state().safeAreaInsets).toEqual(TEST_INSETS_1.safeAreaInsets); + }); + + test('should not get safe area insets on mount if neither DeviceTypes.IS_IPHONE_WITH_INSET nor mattermostManaged.hasSafeAreaInsets is true', async () => { + DeviceTypes.IS_IPHONE_WITH_INSETS = false; + mattermostManaged.hasSafeAreaInsets = false; + + const wrapper = shallow( + + ); + + expect(SafeArea.getSafeAreaInsetsForRootView).not.toHaveBeenCalled(); + await SafeArea.getSafeAreaInsetsForRootView(); + expect(wrapper.state().safeAreaInsets).not.toEqual(TEST_INSETS_1.safeAreaInsets); + }); + + test('should set safe area insets on change if mounted and DeviceTypes.IS_IPHONE_WITH_INSETS is true', () => { + DeviceTypes.IS_IPHONE_WITH_INSETS = true; + mattermostManaged.hasSafeAreaInsets = false; + + const wrapper = shallow( + + ); + + expect(wrapper.state().safeAreaInsets).not.toEqual(TEST_INSETS_2.safeAreaInsets); + + const instance = wrapper.instance(); + instance.onSafeAreaInsetsForRootViewChange(TEST_INSETS_2); + expect(wrapper.state().safeAreaInsets).toEqual(TEST_INSETS_2.safeAreaInsets); + }); + + test('should set safe area insets on change if mounted and mattermostManaged.hasSafeAreaInsets is true', () => { + DeviceTypes.IS_IPHONE_WITH_INSETS = false; + mattermostManaged.hasSafeAreaInsets = true; + + const wrapper = shallow( + + ); + + expect(wrapper.state().safeAreaInsets).not.toEqual(TEST_INSETS_2.safeAreaInsets); + + const instance = wrapper.instance(); + instance.onSafeAreaInsetsForRootViewChange(TEST_INSETS_2); + expect(wrapper.state().safeAreaInsets).toEqual(TEST_INSETS_2.safeAreaInsets); + }); + + test('should not set safe area insets on change if mounted and neither DeviceTypes.IS_IPHONE_WITH_INSETS nor mattermostManaged.hasSafeAreaInsets is true', () => { + DeviceTypes.IS_IPHONE_WITH_INSETS = false; + mattermostManaged.hasSafeAreaInsets = false; + + const wrapper = shallow( + + ); + + expect(wrapper.state().safeAreaInsets).not.toEqual(TEST_INSETS_2.safeAreaInsets); + + const instance = wrapper.instance(); + instance.onSafeAreaInsetsForRootViewChange(TEST_INSETS_2); + expect(wrapper.state().safeAreaInsets).not.toEqual(TEST_INSETS_2.safeAreaInsets); + }); + + test('should set safe area insets on change not mounted', () => { + DeviceTypes.IS_IPHONE_WITH_INSETS = true; + mattermostManaged.hasSafeAreaInsets = true; + + const wrapper = shallow( + + ); + + expect(wrapper.state().safeAreaInsets).not.toEqual(TEST_INSETS_2.safeAreaInsets); + + const instance = wrapper.instance(); + instance.mounted = false; + instance.onSafeAreaInsetsForRootViewChange(TEST_INSETS_2); + expect(wrapper.state().safeAreaInsets).not.toEqual(TEST_INSETS_2.safeAreaInsets); + }); +}); diff --git a/app/screens/permalink/index.js b/app/screens/permalink/index.js index d97525ec9..275d4df3e 100644 --- a/app/screens/permalink/index.js +++ b/app/screens/permalink/index.js @@ -20,8 +20,6 @@ import {isLandscape} from 'app/selectors/device'; import { handleSelectChannel, loadThreadIfNecessary, - setChannelDisplayName, - setChannelLoading, } from 'app/actions/views/channel'; import {handleTeamChange} from 'app/actions/views/select_team'; @@ -73,8 +71,6 @@ function mapDispatchToProps(dispatch) { joinChannel, loadThreadIfNecessary, selectPost, - setChannelDisplayName, - setChannelLoading, }, dispatch), }; } diff --git a/app/screens/permalink/permalink.js b/app/screens/permalink/permalink.js index 7d6bb3aeb..8577461f1 100644 --- a/app/screens/permalink/permalink.js +++ b/app/screens/permalink/permalink.js @@ -65,8 +65,6 @@ export default class Permalink extends PureComponent { joinChannel: PropTypes.func.isRequired, loadThreadIfNecessary: PropTypes.func.isRequired, selectPost: PropTypes.func.isRequired, - setChannelDisplayName: PropTypes.func.isRequired, - setChannelLoading: PropTypes.func.isRequired, }).isRequired, channelId: PropTypes.string, channelIsArchived: PropTypes.bool, @@ -221,24 +219,22 @@ export default class Permalink extends PureComponent { }; handlePress = () => { - const {channelIdState, channelNameState} = this.state; + const {channelIdState} = this.state; if (this.refs.view) { this.refs.view.growOut().then(() => { - this.jumpToChannel(channelIdState, channelNameState); + this.jumpToChannel(channelIdState); }); } }; - jumpToChannel = async (channelId, channelDisplayName) => { + jumpToChannel = async (channelId) => { if (channelId) { const {actions, channelTeamId, currentTeamId, onClose} = this.props; const currentChannelId = this.props.channelId; const { handleSelectChannel, handleTeamChange, - setChannelLoading, - setChannelDisplayName, } = actions; actions.selectPost(''); @@ -263,8 +259,6 @@ export default class Permalink extends PureComponent { handleTeamChange(channelTeamId); } - setChannelLoading(channelId !== currentChannelId); - setChannelDisplayName(channelDisplayName); handleSelectChannel(channelId); } }; diff --git a/app/screens/permalink/permalink.test.js b/app/screens/permalink/permalink.test.js index 0d8edbe6b..d104633b9 100644 --- a/app/screens/permalink/permalink.test.js +++ b/app/screens/permalink/permalink.test.js @@ -20,8 +20,6 @@ describe('Permalink', () => { joinChannel: jest.fn(), loadThreadIfNecessary: jest.fn(), selectPost: jest.fn(), - setChannelDisplayName: jest.fn(), - setChannelLoading: jest.fn(), }; const baseProps = { From 1fbf0d28d32d6ed2d62a045f414855e1394d865f Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Mon, 21 Oct 2019 12:11:56 -0700 Subject: [PATCH 2/9] [MM-19084] Apply theme change for all components when theme changes (#3415) * Apply theme change for all components when theme changes * Remove unnecessary snapshot test --- app/screens/channel/channel_base.js | 24 +++-- app/screens/channel/channel_base.test.js | 77 +++++++++++++++ app/screens/settings/theme/theme.test.js | 114 +---------------------- 3 files changed, 92 insertions(+), 123 deletions(-) create mode 100644 app/screens/channel/channel_base.test.js diff --git a/app/screens/channel/channel_base.js b/app/screens/channel/channel_base.js index 76e6227bc..65a0ae1d0 100644 --- a/app/screens/channel/channel_base.js +++ b/app/screens/channel/channel_base.js @@ -20,6 +20,7 @@ import SafeAreaView from 'app/components/safe_area_view'; import SettingsSidebar from 'app/components/sidebars/settings'; import {preventDoubleTap} from 'app/utils/tap'; +import {setNavigatorStyles} from 'app/utils/theme'; import PushNotifications from 'app/push_notifications'; import EphemeralStore from 'app/store/ephemeral_store'; import tracker from 'app/utils/time_tracker'; @@ -27,7 +28,6 @@ import telemetry from 'app/telemetry'; import { goToScreen, showModalOverCurrentContext, - mergeNavigationOptions, } from 'app/actions/navigation'; import LocalConfig from 'assets/config'; @@ -69,12 +69,7 @@ export default class ChannelBase extends PureComponent { this.postTextbox = React.createRef(); this.keyboardTracker = React.createRef(); - const options = { - layout: { - backgroundColor: props.theme.centerChannelBg, - }, - }; - mergeNavigationOptions(props.componentId, options); + setNavigatorStyles(props.componentId, props.theme); if (LocalConfig.EnableMobileClientUpgrade && !ClientUpgradeListener) { ClientUpgradeListener = require('app/components/client_upgrade_listener').default; @@ -113,12 +108,9 @@ export default class ChannelBase extends PureComponent { componentWillReceiveProps(nextProps) { if (this.props.theme !== nextProps.theme) { - const options = { - layout: { - backgroundColor: nextProps.theme.centerChannelBg, - }, - }; - mergeNavigationOptions(this.props.componentId, options); + EphemeralStore.allNavigationComponentIds.forEach((componentId) => { + setNavigatorStyles(componentId, nextProps.theme); + }); } if (nextProps.currentTeamId && this.props.currentTeamId !== nextProps.currentTeamId) { @@ -322,6 +314,12 @@ export default class ChannelBase extends PureComponent { ); } + + render() { + // Overriden in channel.android.js and channel.ios.js + // but defined here for channel_base.test.js + return; // eslint-disable-line no-useless-return + } } export const style = StyleSheet.create({ diff --git a/app/screens/channel/channel_base.test.js b/app/screens/channel/channel_base.test.js new file mode 100644 index 000000000..7dc29d405 --- /dev/null +++ b/app/screens/channel/channel_base.test.js @@ -0,0 +1,77 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import Preferences from 'mattermost-redux/constants/preferences'; + +import EphemeralStore from 'app/store/ephemeral_store'; +import * as NavigationActions from 'app/actions/navigation'; + +import ChannelBase from './channel_base'; + +jest.mock('react-intl'); + +describe('ChannelBase', () => { + const componentIds = ['component-1', 'component-2', 'component-3']; + const baseProps = { + actions: { + loadChannelsIfNecessary: jest.fn(), + loadProfilesAndTeamMembersForDMSidebar: jest.fn(), + selectDefaultTeam: jest.fn(), + selectInitialChannel: jest.fn(), + recordLoadTime: jest.fn(), + getChannelStats: jest.fn(), + }, + componentId: componentIds[0], + theme: Preferences.THEMES.default, + }; + const optionsForTheme = (theme) => { + return { + topBar: { + backButton: { + color: theme.sidebarHeaderTextColor, + }, + background: { + color: theme.sidebarHeaderBg, + }, + title: { + color: theme.sidebarHeaderTextColor, + }, + leftButtonColor: theme.sidebarHeaderTextColor, + rightButtonColor: theme.sidebarHeaderTextColor, + }, + layout: { + backgroundColor: theme.centerChannelBg, + }, + }; + }; + + test('should call mergeNavigationOptions on all navigation components when theme changes', () => { + const mergeNavigationOptions = jest.spyOn(NavigationActions, 'mergeNavigationOptions'); + + componentIds.forEach((componentId) => { + EphemeralStore.addNavigationComponentId(componentId); + }); + + const wrapper = shallow( + , + ); + + const themeOptions = optionsForTheme(Preferences.THEMES.default); + expect(mergeNavigationOptions.mock.calls).toEqual([ + [componentIds[0], themeOptions], + ]); + mergeNavigationOptions.mockClear(); + + wrapper.setProps({theme: Preferences.THEMES.mattermostDark}); + + const newThemeOptions = optionsForTheme(Preferences.THEMES.mattermostDark); + expect(mergeNavigationOptions.mock.calls).toEqual([ + [componentIds[2], newThemeOptions], + [componentIds[1], newThemeOptions], + [componentIds[0], newThemeOptions], + ]); + }); +}); diff --git a/app/screens/settings/theme/theme.test.js b/app/screens/settings/theme/theme.test.js index cbc658296..eea8a067b 100644 --- a/app/screens/settings/theme/theme.test.js +++ b/app/screens/settings/theme/theme.test.js @@ -14,116 +14,10 @@ import ThemeTile from './theme_tile'; jest.mock('react-intl'); -const allowedThemes = [ - { - type: 'Mattermost', - sidebarBg: '#145dbf', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#4578bf', - sidebarTextActiveBorder: '#579eff', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#1153ab', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#06d6a0', - awayIndicator: '#ffbc42', - dndIndicator: '#f74343', - mentionBg: '#ffffff', - mentionColor: '#145dbf', - centerChannelBg: '#ffffff', - centerChannelColor: '#3d3c40', - newMessageSeparator: '#ff8800', - linkColor: '#2389d7', - buttonBg: '#166de0', - buttonColor: '#ffffff', - errorTextColor: '#fd5960', - mentionHighlightBg: '#ffe577', - mentionHighlightLink: '#166de0', - codeTheme: 'github', - key: 'default', - }, - { - type: 'Organization', - sidebarBg: '#2071a7', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#136197', - sidebarTextActiveBorder: '#7ab0d6', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#2f81b7', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#7dbe00', - awayIndicator: '#dcbd4e', - dndIndicator: '#ff6a6a', - mentionBg: '#fbfbfb', - mentionColor: '#2071f7', - centerChannelBg: '#f2f4f8', - centerChannelColor: '#333333', - newMessageSeparator: '#ff8800', - linkColor: '#2f81b7', - buttonBg: '#1dacfc', - buttonColor: '#ffffff', - errorTextColor: '#a94442', - mentionHighlightBg: '#f3e197', - mentionHighlightLink: '#2f81b7', - codeTheme: 'github', - key: 'organization', - }, - { - type: 'Mattermost Dark', - sidebarBg: '#1b2c3e', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#4a5664', - sidebarTextActiveBorder: '#66b9a7', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#1b2c3e', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#65dcc8', - awayIndicator: '#c1b966', - dndIndicator: '#e81023', - mentionBg: '#b74a4a', - mentionColor: '#ffffff', - centerChannelBg: '#2f3e4e', - centerChannelColor: '#dddddd', - newMessageSeparator: '#5de5da', - linkColor: '#a4ffeb', - buttonBg: '#4cbba4', - buttonColor: '#ffffff', - errorTextColor: '#ff6461', - mentionHighlightBg: '#984063', - mentionHighlightLink: '#a4ffeb', - codeTheme: 'solarized-dark', - key: 'mattermostDark', - }, - { - type: 'Windows Dark', - sidebarBg: '#171717', - sidebarText: '#ffffff', - sidebarUnreadText: '#ffffff', - sidebarTextHoverBg: '#302e30', - sidebarTextActiveBorder: '#196caf', - sidebarTextActiveColor: '#ffffff', - sidebarHeaderBg: '#1f1f1f', - sidebarHeaderTextColor: '#ffffff', - onlineIndicator: '#399fff', - awayIndicator: '#c1b966', - dndIndicator: '#e81023', - mentionBg: '#0177e7', - mentionColor: '#ffffff', - centerChannelBg: '#1f1f1f', - centerChannelColor: '#dddddd', - newMessageSeparator: '#cc992d', - linkColor: '#0d93ff', - buttonBg: '#0177e7', - buttonColor: '#ffffff', - errorTextColor: '#ff6461', - mentionHighlightBg: '#784098', - mentionHighlightLink: '#a4ffeb', - codeTheme: 'monokai', - key: 'windows10', - }, -]; +const allowedThemes = Object.keys(Preferences.THEMES).map((key) => ({ + key, + ...Preferences.THEMES[key], +})); describe('Theme', () => { const baseProps = { From 4f1211a5860b32cd1db9d7ba406f6b2fc158099f Mon Sep 17 00:00:00 2001 From: Bryan Culver Date: Mon, 21 Oct 2019 16:01:17 -0400 Subject: [PATCH 3/9] MM-19328 | Add accessibility label to channel drawer button. (#3448) * MM-19328 | Add accessibility label to channel drawer button. * MM-19328 | Add accessibility hint and role to channel drawer button. --- .../channel_drawer_button.test.js.snap | 8 ++++++ .../channel_drawer_button.js | 26 +++++++++++++++++++ .../channel_drawer_button.test.js | 12 ++++----- assets/base/i18n/en.json | 2 ++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/app/screens/channel/channel_nav_bar/channel_drawer_button/__snapshots__/channel_drawer_button.test.js.snap b/app/screens/channel/channel_nav_bar/channel_drawer_button/__snapshots__/channel_drawer_button.test.js.snap index df71b3cb8..42aec1dc8 100644 --- a/app/screens/channel/channel_nav_bar/channel_drawer_button/__snapshots__/channel_drawer_button.test.js.snap +++ b/app/screens/channel/channel_nav_bar/channel_drawer_button/__snapshots__/channel_drawer_button.test.js.snap @@ -2,6 +2,10 @@ exports[`ChannelDrawerButton should match, full snapshot 1`] = ` diff --git a/app/screens/channel/channel_nav_bar/channel_drawer_button/channel_drawer_button.test.js b/app/screens/channel/channel_nav_bar/channel_drawer_button/channel_drawer_button.test.js index cd9eef22a..499cf4a22 100644 --- a/app/screens/channel/channel_nav_bar/channel_drawer_button/channel_drawer_button.test.js +++ b/app/screens/channel/channel_nav_bar/channel_drawer_button/channel_drawer_button.test.js @@ -2,13 +2,13 @@ // See LICENSE.txt for license information. import React from 'react'; -import {shallow} from 'enzyme'; import NotificationsIOS from 'react-native-notifications'; import Preferences from 'mattermost-redux/constants/preferences'; import Badge from 'app/components/badge'; import PushNotification from 'app/push_notifications/push_notifications.ios'; +import {shallowWithIntl} from 'test/intl-test-helper'; import ChannelDrawerButton from './channel_drawer_button'; @@ -48,7 +48,7 @@ describe('ChannelDrawerButton', () => { afterEach(() => NotificationsIOS.setBadgesCount(0)); test('should match, full snapshot', () => { - const wrapper = shallow( + const wrapper = shallowWithIntl( ); @@ -69,7 +69,7 @@ describe('ChannelDrawerButton', () => { badgeCount: 0, }; - shallow( + shallowWithIntl( ); expect(setApplicationIconBadgeNumber).not.toBeCalled(); @@ -83,7 +83,7 @@ describe('ChannelDrawerButton', () => { badgeCount: 1, }; - shallow( + shallowWithIntl( ); expect(setApplicationIconBadgeNumber).toHaveBeenCalledTimes(1); @@ -97,7 +97,7 @@ describe('ChannelDrawerButton', () => { badgeCount: 0, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( ); NotificationsIOS.getBadgesCount((count) => expect(count).toBe(0)); @@ -115,7 +115,7 @@ describe('ChannelDrawerButton', () => { badgeCount: 0, }; - const wrapper = shallow( + const wrapper = shallowWithIntl( ); wrapper.setProps({badgeCount: 2}); diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 61fad8f9c..bf311153b 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -486,6 +486,8 @@ "msg_typing.areTyping": "{users} and {last} are typing...", "msg_typing.isTyping": "{user} is typing...", "navbar_dropdown.logout": "Logout", + "navbar.channel_drawer.button": "Channels and teams", + "navbar.channel_drawer.hint": "Opens the channels and teams drawer", "navbar.leave": "Leave Channel", "password_form.title": "Password Reset", "password_send.checkInbox": "Please check your inbox.", From 010c099654f4ca75d84f33b698a202250986c734 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Mon, 21 Oct 2019 23:42:49 +0300 Subject: [PATCH 4/9] MM-18490 Migrate app builds to circleCi (#3373) * Add pods to source control * Update Fastlane to work with circleCi * Configure circleCi workflows to build the app * Set BRANCH_TO_BUILD env var to CIRCLE_BRANCH for PRs * Use different context for each platform and type * Fix regex * Add i18n check --- .circleci/config.yml | 398 +++++- .gitignore | 4 - Makefile | 18 +- fastlane/Fastfile | 500 ++++---- .../Private/XCDYouTubeKit/XCDYouTubeClient.h | 1 + .../XCDYouTubeKit/XCDYouTubeDashManifestXML.h | 1 + .../Private/XCDYouTubeKit/XCDYouTubeError.h | 1 + .../Private/XCDYouTubeKit/XCDYouTubeKit.h | 1 + .../XCDYouTubeKit/XCDYouTubeLogger+Private.h | 1 + .../Private/XCDYouTubeKit/XCDYouTubeLogger.h | 1 + .../XCDYouTubeKit/XCDYouTubeOperation.h | 1 + .../XCDYouTubeKit/XCDYouTubePlayerScript.h | 1 + .../XCDYouTubeKit/XCDYouTubeVideo+Private.h | 1 + .../Private/XCDYouTubeKit/XCDYouTubeVideo.h | 1 + .../XCDYouTubeKit/XCDYouTubeVideoOperation.h | 1 + .../XCDYouTubeVideoPlayerViewController.h | 1 + .../XCDYouTubeKit/XCDYouTubeVideoWebpage.h | 1 + .../WKYTPlayerView.h | 1 + .../Public/XCDYouTubeKit/XCDYouTubeClient.h | 1 + .../Public/XCDYouTubeKit/XCDYouTubeError.h | 1 + .../Public/XCDYouTubeKit/XCDYouTubeKit.h | 1 + .../Public/XCDYouTubeKit/XCDYouTubeLogger.h | 1 + .../XCDYouTubeKit/XCDYouTubeOperation.h | 1 + .../Public/XCDYouTubeKit/XCDYouTubeVideo.h | 1 + .../XCDYouTubeKit/XCDYouTubeVideoOperation.h | 1 + .../XCDYouTubeVideoPlayerViewController.h | 1 + .../WKYTPlayerView.h | 1 + ios/Pods/Manifest.lock | 20 + ios/Pods/Pods.xcodeproj/project.pbxproj | 893 +++++++++++++ .../Pods-Mattermost-acknowledgements.markdown | 46 + .../Pods-Mattermost-acknowledgements.plist | 84 ++ .../Pods-Mattermost/Pods-Mattermost-dummy.m | 5 + .../Pods-Mattermost-frameworks.sh | 146 +++ .../Pods-Mattermost-resources.sh | 124 ++ .../Pods-Mattermost.debug.xcconfig | 9 + .../Pods-Mattermost.release.xcconfig | 9 + ...-MattermostTests-acknowledgements.markdown | 3 + ...ods-MattermostTests-acknowledgements.plist | 29 + .../Pods-MattermostTests-dummy.m | 5 + .../Pods-MattermostTests-frameworks.sh | 146 +++ .../Pods-MattermostTests-resources.sh | 118 ++ .../Pods-MattermostTests.debug.xcconfig | 9 + .../Pods-MattermostTests.release.xcconfig | 9 + .../XCDYouTubeKit/XCDYouTubeKit-dummy.m | 5 + .../XCDYouTubeKit/XCDYouTubeKit-prefix.pch | 12 + .../XCDYouTubeKit/XCDYouTubeKit.xcconfig | 10 + .../YoutubePlayer-in-WKWebView-dummy.m | 5 + .../YoutubePlayer-in-WKWebView-prefix.pch | 12 + .../YoutubePlayer-in-WKWebView.xcconfig | 9 + ios/Pods/XCDYouTubeKit/LICENSE | 22 + ios/Pods/XCDYouTubeKit/README.md | 151 +++ .../XCDYouTubeKit/XCDYouTubeClient.h | 98 ++ .../XCDYouTubeKit/XCDYouTubeClient.m | 83 ++ .../XCDYouTubeKit/XCDYouTubeDashManifestXML.h | 17 + .../XCDYouTubeKit/XCDYouTubeDashManifestXML.m | 98 ++ .../XCDYouTubeKit/XCDYouTubeError.h | 47 + .../XCDYouTubeKit/XCDYouTubeKit.h | 16 + .../XCDYouTubeKit/XCDYouTubeLogger+Private.h | 21 + .../XCDYouTubeKit/XCDYouTubeLogger.h | 103 ++ .../XCDYouTubeKit/XCDYouTubeLogger.m | 63 + .../XCDYouTubeKit/XCDYouTubeOperation.h | 23 + .../XCDYouTubeKit/XCDYouTubePlayerScript.h | 14 + .../XCDYouTubeKit/XCDYouTubePlayerScript.m | 125 ++ .../XCDYouTubeKit/XCDYouTubeVideo+Private.h | 24 + .../XCDYouTubeKit/XCDYouTubeVideo.h | 147 +++ .../XCDYouTubeKit/XCDYouTubeVideo.m | 319 +++++ .../XCDYouTubeKit/XCDYouTubeVideoOperation.h | 74 ++ .../XCDYouTubeKit/XCDYouTubeVideoOperation.m | 410 ++++++ .../XCDYouTubeVideoPlayerViewController.h | 126 ++ .../XCDYouTubeVideoPlayerViewController.m | 209 +++ .../XCDYouTubeKit/XCDYouTubeVideoWebpage.h | 18 + .../XCDYouTubeKit/XCDYouTubeVideoWebpage.m | 124 ++ ios/Pods/YoutubePlayer-in-WKWebView/LICENSE | 13 + ios/Pods/YoutubePlayer-in-WKWebView/README.md | 59 + .../Assets/YTPlayerView-iframe-player.html | 90 ++ .../WKYTPlayerView/WKYTPlayerView.h | 734 +++++++++++ .../WKYTPlayerView/WKYTPlayerView.m | 1123 +++++++++++++++++ metro.config.js | 1 + 78 files changed, 6755 insertions(+), 248 deletions(-) create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h create mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h create mode 120000 ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h create mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h create mode 120000 ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h create mode 100644 ios/Pods/Manifest.lock create mode 100644 ios/Pods/Pods.xcodeproj/project.pbxproj create mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown create mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist create mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m create mode 100755 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh create mode 100755 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh create mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown create mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist create mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m create mode 100755 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh create mode 100755 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh create mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig create mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig create mode 100644 ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m create mode 100644 ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch create mode 100644 ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig create mode 100644 ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m create mode 100644 ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch create mode 100644 ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig create mode 100644 ios/Pods/XCDYouTubeKit/LICENSE create mode 100644 ios/Pods/XCDYouTubeKit/README.md create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h create mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m create mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/LICENSE create mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/README.md create mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html create mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h create mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m diff --git a/.circleci/config.yml b/.circleci/config.yml index c44a1c76f..64adeca88 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,5 +1,201 @@ version: 2.1 +executors: + android: + parameters: + resource_class: + default: large + type: string + environment: + NODE_OPTIONS: --max_old_space_size=12000 + NODE_ENV: production + BABEL_ENV: production + docker: + - image: circleci/android:api-27-node + working_directory: ~/mattermost-mobile + resource_class: <> + + ios: + environment: + NODE_OPTIONS: --max_old_space_size=12000 + NODE_ENV: production + BABEL_ENV: production + macos: + xcode: "11.0.0" + working_directory: ~/mattermost-mobile + shell: /bin/bash --login -o pipefail + +commands: + checkout-private: + description: "Checkout the private repo with build env vars" + steps: + - add_ssh_keys: + fingerprints: + - "59:4d:99:5e:1c:6d:30:36:6d:60:76:88:ff:a7:ab:63" + - run: + name: Clone the mobile private repo + command: git clone git@github.com:mattermost/mattermost-mobile-private.git ~/mattermost-mobile-private + + android-gem-dependencies: + description: "Get Fastlane dependencies for Android builds" + steps: + - restore_cache: + name: Restore Fastlane cache + key: v1-gems-android-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} + - run: + working_directory: fastlane + name: Download Fastlane dependencies + command: bundle install --path vendor/bundle + - save_cache: + name: Save Fastlane cache + key: v1-gems-android-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} + paths: + - fastlane/vendor/bundle + + android-gradle-dependencies: + description: "Get Gradle dependencies for Android buils" + steps: + - restore_cache: + name: Restore Gradle cache + key: v1-gradle-{{ checksum "android/build.gradle" }}-{{ checksum "android/app/build.gradle" }} + - run: + working_directory: android + name: Download Gradle dependencies + command: ./gradlew dependencies + - save_cache: + name: Save Gradle cache + paths: + - ~/.gradle + key: v1-gradle-{{ checksum "android/build.gradle" }}-{{ checksum "android/app/build.gradle" }} + + assets: + description: "Generate app assets" + steps: + - restore_cache: + name: Restore assets cache + key: v1-assets-{{ checksum "assets/base/config.json" }}-{{ arch }} + - run: + name: Generate assets + command: make dist/assets + - save_cache: + name: Save assets cache + key: v1-assets-{{ checksum "assets/base/config.json" }}-{{ arch }} + paths: + - dist + + ios-gem-dependencies: + description: "Get Fastlane dependencies for iOS builds" + steps: + - run: + name: Set Ruby version + command: echo "ruby-2.6.3" > ~/.ruby-version + - restore_cache: + name: Restore Fastlane cache + key: v1-gems-ios-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} + - run: + working_directory: fastlane + name: Download Fastlane dependencies + command: bundle install --path vendor/bundle + - save_cache: + name: Save Fastlane cache + key: v1-gems-ios-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} + paths: + - fastlane/vendor/bundle + + npm-dependencies: + description: "Get JavaScript dependencies for the job" + steps: + - restore_cache: + name: Restore npm cache + key: v1-npm-{{ checksum "package.json" }}-{{ arch }} + - run: + name: Getting JavaScript dependencies + command: NODE_ENV=development npm install + - save_cache: + name: Save npm cache + key: v1-npm-{{ checksum "package.json" }}-{{ arch }} + paths: + - node_modules + + build-android: + description: "Build the android app" + steps: + - checkout: + path: ~/mattermost-mobile + - checkout-private + - npm-dependencies + - assets + - android-gem-dependencies + - android-gradle-dependencies + - run: + name: Append Keystore to build Android + command: | + cp ~/mattermost-mobile-private/android/${STORE_FILE} android/app/${STORE_FILE} + echo MATTERMOST_RELEASE_STORE_FILE=${STORE_FILE} | tee -a android/gradle.properties > /dev/null + echo ${STORE_ALIAS} | tee -a android/gradle.properties > /dev/null + echo ${STORE_PASSWORD} | tee -a android/gradle.properties > /dev/null + - run: + working_directory: fastlane + name: Run fastlane to build android + no_output_timeout: 30m + command: bundle exec fastlane android build || exit 1 + + build-ios: + description: "Build the iOS app" + steps: + - checkout: + path: ~/mattermost-mobile + - npm-dependencies + - assets + - ios-gem-dependencies + - run: + working_directory: fastlane + name: Run fastlane to build iOS + no_output_timeout: 30m + command: bundle exec fastlane ios build || exit 1 + + deploy-android: + description: "Deploy apk to Google Play" + parameters: + apk_path: + type: string + steps: + - attach_workspace: + at: ~/mattermost-mobile + - deploy: + name: "Deploy apk to Google Play" + working_directory: fastlane + command: bundle exec fastlane android deploy apk:../<> + + deploy-ios: + description: "Deploy ipa to TestFlight" + parameters: + ipa_path: + type: string + steps: + - attach_workspace: + at: ~/mattermost-mobile + - deploy: + name: "Deploy ipa to TestFlight" + working_directory: fastlane + command: bundle exec fastlane ios deploy ipa:../<> + + persist: + description: "Persist mattermost-mobile directory" + steps: + - persist_to_workspace: + root: ./ + paths: + - ./ + + save: + description: "Save binaries artifacts" + parameters: + filename: + type: string + steps: + - store_artifacts: + path: ~/mattermost-mobile/<> jobs: test: @@ -7,17 +203,201 @@ jobs: docker: - image: circleci/node:10 steps: - - checkout - - run: | - echo assets/base/config.json - cat assets/base/config.json - # Avoid installing pods - touch .podinstall - # Run tests - make test || exit 1 + - checkout: + path: ~/mattermost-mobile + - npm-dependencies + - assets + - run: + name: Check styles + command: npm run check + - run: + name: Running Tests + command: npm test + - run: + name: Check i18n + command: make i18n-extract-ci + + build-android-beta: + executor: android + steps: + - build-android + - persist + - save: + filename: "Mattermost_Beta.apk" + + build-android-release: + executor: android + steps: + - build-android + - persist + - save: + filename: "Mattermost.apk" + + build-android-pr: + executor: android + environment: + BRANCH_TO_BUILD: ${CIRCLE_BRANCH} + steps: + - build-android + - persist + - save: + filename: "Mattermost_Beta.apk" + + build-ios-beta: + executor: ios + steps: + - build-ios + - persist + - save: + filename: "Mattermost_Beta.ipa" + + build-ios-release: + executor: ios + steps: + - build-ios + - persist + - save: + filename: "Mattermost.ipa" + + build-ios-pr: + executor: ios + environment: + BRANCH_TO_BUILD: ${CIRCLE_BRANCH} + steps: + - build-ios + - persist + - save: + filename: "Mattermost_Beta.ipa" + + deploy-android-release: + executor: + name: android + resource_class: medium + steps: + - deploy-android: + apk_path: Mattermost.apk + + deploy-android-beta: + executor: + name: android + resource_class: medium + steps: + - deploy-android: + apk_path: Mattermost_Beta.apk + + deploy-ios-release: + executor: ios + steps: + - deploy-ios: + ipa_path: Mattermost.ipa + + deploy-ios-beta: + executor: ios + steps: + - deploy-ios: + ipa_path: Mattermost_Beta.ipa workflows: version: 2 - pr-test: + build: jobs: - test + + - build-android-release: + context: mattermost-mobile-android-release + requires: + - test + filters: + branches: + only: + - /^build-\d+$/ + - /^build-android-\d+$/ + - /^build-android-release-\d+$/ + - deploy-android-release: + context: mattermost-mobile-android-release + requires: + - build-android-release + filters: + branches: + only: + - /^build-\d+$/ + - /^build-android-\d+$/ + - /^build-android-release-\d+$/ + + - build-android-beta: + context: mattermost-mobile-android-beta + requires: + - test + filters: + branches: + only: + - /^build-\d+$/ + - /^build-android-\d+$/ + - /^build-android-beta-\d+$/ + - deploy-android-beta: + context: mattermost-mobile-android-beta + requires: + - build-android-beta + filters: + branches: + only: + - /^build-\d+$/ + - /^build-android-\d+$/ + - /^build-android-beta-\d+$/ + + - build-ios-release: + context: mattermost-mobile-ios-release + requires: + - test + filters: + branches: + only: + - /^build-\d+$/ + - /^build-ios-\d+$/ + - /^build-ios-release-\d+$/ + - deploy-ios-release: + context: mattermost-mobile-ios-release + requires: + - build-ios-release + filters: + branches: + only: + - /^build-\d+$/ + - /^build-ios-\d+$/ + - /^build-ios-release-\d+$/ + + - build-ios-beta: + context: mattermost-mobile-ios-beta + requires: + - test + filters: + branches: + only: + - /^build-\d+$/ + - /^build-ios-\d+$/ + - /^build-ios-beta-\d+$/ + - deploy-ios-beta: + context: mattermost-mobile-ios-beta + requires: + - build-ios-beta + filters: + branches: + only: + - /^build-\d+$/ + - /^build-ios-\d+$/ + - /^build-ios-beta-\d+$/ + + - build-android-pr: + context: mattermost-mobile-android-pr + requires: + - test + filters: + branches: + only: /^build-pr-.*/ + - build-ios-pr: + context: mattermost-mobile-ios-pr + requires: + - test + filters: + branches: + only: /^build-pr-.*/ diff --git a/.gitignore b/.gitignore index e8c5ae3c7..bd5cbe33e 100644 --- a/.gitignore +++ b/.gitignore @@ -85,10 +85,6 @@ ios/sentry.properties .nyc_output coverage -# Pods -.podinstall -ios/Pods/ - # Bundle artifact *.jsbundle diff --git a/Makefile b/Makefile index 4c3178a3f..8e3c9ab1d 100644 --- a/Makefile +++ b/Makefile @@ -31,18 +31,6 @@ npm-ci: package.json @echo Getting Javascript dependencies @npm ci -.podinstall: -ifeq ($(OS), Darwin) -ifdef POD - @echo Getting Cocoapods dependencies; - @cd ios && pod install; -else - @echo "Cocoapods is not installed https://cocoapods.org/" - @exit 1 -endif -endif - @touch $@ - dist/assets: $(BASE_ASSETS) $(OVERRIDE_ASSETS) @mkdir -p dist @@ -53,9 +41,9 @@ dist/assets: $(BASE_ASSETS) $(OVERRIDE_ASSETS) @echo "Generating app assets" @node scripts/make-dist-assets.js -pre-run: | node_modules .podinstall dist/assets ## Installs dependencies and assets +pre-run: | node_modules dist/assets ## Installs dependencies and assets -pre-build: | npm-ci .podinstall dist/assets ## Install dependencies and assets before building +pre-build: | npm-ci dist/assets ## Install dependencies and assets before building check-style: node_modules ## Runs eslint @echo Checking for style guide compliance @@ -65,10 +53,8 @@ clean: ## Cleans dependencies, previous builds and temp files @echo Cleaning started @rm -rf node_modules - @rm -f .podinstall @rm -rf dist @rm -rf ios/build - @rm -rf ios/Pods @rm -rf android/app/build @echo Cleanup finished diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 329082953..301ec13dc 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -11,69 +11,69 @@ configured = false is_build_pr = false # Executes before anything else use to setup the script -before_all do |lane, options| - if lane.to_s == 'build_pr' - pr = options[:pr] - UI.success("Building #{pr}") - ENV['BRANCH_TO_BUILD'] = pr - is_build_pr = true - end +before_all do + is_build_pr = ENV['BUILD_PR'] == 'true' + UI.success("Building for release #{ENV['BUILD_FOR_RELEASE']}") - # Raises an error is git is not clean - if ENV['COMMIT_CHANGES_TO_GIT'] == 'true' - ensure_git_status_clean - end + if ENV['CIRCLECI'] != 'true' + # Raises an error is git is not clean + if ENV['COMMIT_CHANGES_TO_GIT'] == 'true' + ensure_git_status_clean + end - # Block to ensure we are on the right branch - branch = ENV['BRANCH_TO_BUILD'] || ENV['GIT_BRANCH'] - begin - ensure_git_branch( - branch: branch - ) - rescue - sh "git checkout #{branch}" - UI.success("Using branch \"#{branch}\"") - end + # Block to ensure we are on the right branch + branch = ENV['BRANCH_TO_BUILD'] || ENV['GIT_BRANCH'] + begin + ensure_git_branch( + branch: branch + ) + rescue + sh "git checkout #{branch}" + UI.success("Using branch \"#{branch}\"") + end - # If we are going to commit changes to git create a separate branch - # so no changes are done in the branch that is being built - if ENV['COMMIT_CHANGES_TO_GIT'] == 'true' - local_branch = ENV['GIT_LOCAL_BRANCH'] || 'build' - sh "git checkout -b #{local_branch}" - UI.success("Creating branch \"#{local_branch}\"") + # If we are going to commit changes to git create a separate branch + # so no changes are done in the branch that is being built + if ENV['COMMIT_CHANGES_TO_GIT'] == 'true' + local_branch = ENV['GIT_LOCAL_BRANCH'] || 'build' + sh "git checkout -b #{local_branch}" + UI.success("Creating branch \"#{local_branch}\"") + end end end after_all do |lane| - if lane.to_s == 'build' - submit_to_store - end - - if ENV['RESET_GIT_BRANCH'] == 'true' - branch = ENV['BRANCH_TO_BUILD'] || 'master' - package_id = ENV['MAIN_APP_IDENTIFIER'] || 'com.mattermost.rnbeta' - beta_dir = '../android/app/src/main/java/com/mattermost/rnbeta' - release_dir = "../android/app/src/main/java/#{package_id.gsub '.', '/'}" - - if beta_dir != release_dir - sh "rm -rf #{release_dir}" + if ENV['CIRCLECI'] != 'true' + if lane.to_s == 'build' + submit_to_store end - reset_git_repo( - force: true, - skip_clean: true - ) - sh "git checkout #{branch}" + if ENV['RESET_GIT_BRANCH'] == 'true' + branch = ENV['BRANCH_TO_BUILD'] || 'master' + package_id = ENV['MAIN_APP_IDENTIFIER'] || 'com.mattermost.rnbeta' + beta_dir = '../android/app/src/main/java/com/mattermost/rnbeta' + release_dir = "../android/app/src/main/java/#{package_id.gsub '.', '/'}" - if ENV['COMMIT_CHANGES_TO_GIT'] == 'true' - local_branch = ENV['GIT_LOCAL_BRANCH'] || 'build' - sh "git branch -D #{local_branch}" - UI.success("Deleted working branch \"#{local_branch}\"") - if lane.to_s == 'build_pr' - sh 'git checkout master' - ## Remove the branch for the PR - sh "git branch -D #{branch}" - UI.success("Deleted PR branch \"#{branch}\"") + if beta_dir != release_dir + sh "rm -rf #{release_dir}" + end + + reset_git_repo( + force: true, + skip_clean: true + ) + sh "git checkout #{branch}" + + if ENV['COMMIT_CHANGES_TO_GIT'] == 'true' + local_branch = ENV['GIT_LOCAL_BRANCH'] || 'build' + sh "git branch -D #{local_branch}" + UI.success("Deleted working branch \"#{local_branch}\"") + if lane.to_s == 'build_pr' + sh 'git checkout master' + ## Remove the branch for the PR + sh "git branch -D #{branch}" + UI.success("Deleted PR branch \"#{branch}\"") + end end end end @@ -196,20 +196,110 @@ lane :unsigned do unsigned end -desc 'Build the app using a PR so QA can test' -lane :build_pr do - configure +desc 'Upload file to s3' +lane :upload_file_to_s3 do |options| + os_type = options[:os_type] + file = options[:file] + file_plist = "" - # Build the android app - self.runner.current_platform = :android - build + if file.nil? || file.empty? + app_name = ENV['APP_NAME'] || 'Mattermost Beta' + filename = app_name.gsub(" ", "_") - # Build the ios app - self.runner.current_platform = :ios - build + if os_type == 'Android' + file = "#{filename}.apk" + elsif os_type == 'iOS' + file = "#{filename}.ipa" + file_plist = "#{filename}.plist" + end + end + + build_folder_path = Dir[File.expand_path('..')].first + file_path = "#{build_folder_path}/#{file}" + + unless ENV['AWS_BUCKET_NAME'].nil? || ENV['AWS_BUCKET_NAME'].empty? || ENV['AWS_REGION'].nil? || ENV['AWS_REGION'].empty? || file_path.nil? + s3_region = ENV['AWS_REGION'] + s3_bucket = ENV['AWS_BUCKET_NAME'] + s3_folder = '' + + version_number = '' + build_number = '' + + if is_build_pr + s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{ENV['BRANCH_TO_BUILD']}" + else + if os_type == 'Android' + version_number = android_get_version_name(gradle_file: './android/app/build.gradle') + build_number = android_get_version_code(gradle_file: './android/app/build.gradle') + elsif os_type == 'iOS' + version_number = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost') + build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj') + end + + s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}" + end + + s3 = Aws::S3::Resource.new(region: s3_region) + file_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{file}") + file_obj.upload_file("#{file_path}") + + if is_build_pr + if os_type == 'Android' + install_url = "https://#{s3_bucket}/#{s3_folder}/#{file}" + elsif os_type == 'iOS' + current_build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj') + plist_template = File.read('plist.erb') + plist_body = ERB.new(plist_template).result(binding) + + plist_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{file_plist}") + plist_obj.put(body: plist_body) + + install_url = "itms-services://?action=download-manifest&url=https://#{s3_bucket}/#{s3_folder}/#{file_plist}" + end + + qa_build_message({ + :os_type => os_type, + :install_url => install_url + }) + end + + public_link = "https://#{s3_bucket}/#{s3_folder}/#{file}" + if file == 'Mattermost-simulator-x86_64.app.zip' + pretext = '#### New iOS build for VM/Simulator' + msg = "Download link: #{public_link}" + send_message_to_mattermost({ + :version_number => version_number, + :build_number => build_number, + :pretext => pretext, + :title => '', + :thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png', + :msg => msg, + :default_payloads => [], + :success => true, + }) + end + + UI.success("S3 bucket @#{s3_bucket}, object @#{s3_folder}/#{file}") + UI.success("S3 public path: #{public_link}") + end end platform :ios do + before_all do + if ENV['CIRCLECI'] == 'true' + setup_circle_ci + end + end + + desc 'Get iOS adhoc profiles' + lane :adhoc do + if ENV['MATCH_TYPE'] != 'adhoc' && !ENV['MATCH_PASSWORD'].nil? && !ENV['MATCH_PASSWORD'].empty? + match( + type: 'adhoc' + ) + end + end + desc 'Build iOS app' lane :build do unless configured @@ -321,7 +411,7 @@ platform :ios do ) # Sync the provisioning profiles using match - if ENV['SYNC_PROVISIONING_PROFILES'] == 'true' + if ENV['SYNC_PROVISIONING_PROFILES'] == 'true' && !ENV['MATCH_PASSWORD'].nil? && !ENV['MATCH_PASSWORD'].empty? match(type: ENV['MATCH_TYPE'] || 'adhoc') end end @@ -333,6 +423,14 @@ platform :ios do end end + lane :deploy do |options| + ipa_path = options[:ipa] + + unless ipa_path.nil? + submit_to_testflight(ipa_path) + end + end + error do |lane, exception| version = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost') build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj') @@ -348,12 +446,37 @@ platform :ios do }) end + def setup_code_signing + disable_automatic_code_signing(path: './ios/Mattermost.xcodeproj') + + ENV['MATCH_APP_IDENTIFIER'].split(',').each do |id| + target = 'Mattermost' + if id.include? 'NotificationService' + target = 'NotificationService' + elsif id.include? 'MattermostShare' + target = 'MattermostShare' + end + + profile = "sigh_#{id}_#{ENV['MATCH_TYPE']}" + + update_project_provisioning( + xcodeproj: './ios/Mattermost.xcodeproj', + profile: ENV["#{profile}_profile-path"], # optional if you use sigh + target_filter: ".*#{target}$", # matches name or type of a target + build_configuration: 'Release', + code_signing_identity: 'iPhone Distribution' # optionally specify the codesigning identity + ) + end + end + def build_ios() app_name = ENV['APP_NAME'] || 'Mattermost Beta' app_name_sub = app_name.gsub(" ", "_") config_mode = ENV['BUILD_FOR_RELEASE'] == 'true' ? 'Release' : 'Debug' method = ENV['IOS_BUILD_EXPORT_METHOD'].nil? || ENV['IOS_BUILD_EXPORT_METHOD'].empty? ? 'ad-hoc' : ENV['IOS_BUILD_EXPORT_METHOD'] + setup_code_signing + gym( clean: true, scheme: 'Mattermost', @@ -362,6 +485,7 @@ platform :ios do export_method: method, skip_profile_detection: true, output_name: "#{app_name_sub}.ipa", + export_xcargs: "-allowProvisioningUpdates", export_options: { signingStyle: 'manual', iCloudContainerEnvironment: 'Production' @@ -483,6 +607,14 @@ platform :android do end end + lane :deploy do |options| + apk_path = options[:apk] + + unless apk_path.nil? + submit_to_google_play(apk_path) + end + end + error do |lane, exception| build_number = android_get_version_code( gradle_file: './android/app/build.gradle' @@ -587,83 +719,102 @@ def send_message_to_mattermost(options) end end +def submit_to_testflight(ipa_path) + app_name = ENV['APP_NAME'] || 'Mattermost Beta' + app_name_sub = app_name.gsub(" ", "_") + + if(File.file?(ipa_path)) + UI.success("ipa file #{ipa_path}") + upload_to_tesflight( + ipa: ipa_path + ) + else + UI.user_error! "ipa file does not exist #{ipa_path}" + return + end + + version_number = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost') + build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj') + s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}" + public_link = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}/#{app_name_sub}.ipa" + + + # Send a build message to Mattermost + pretext = '#### New iOS released ready to be published' + msg = "Release has been cut and is on TestFlight ready to be published.\nDownload link: #{public_link}" + + if ENV['BETA_BUILD'] == 'true' + pretext = '#### New iOS beta published to TestFlight' + msg = "Sign up as a beta tester [here](https://testflight.apple.com/join/Q7Rx7K9P).\nDownload link: #{public_link}" + end + + send_message_to_mattermost({ + :version_number => version_number, + :build_number => build_number, + :pretext => pretext, + :title => '', + :thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png', + :msg => msg, + :default_payloads => [], + :success => true, + }) +end + +def submit_to_google_play(apk_path) + app_name = ENV['APP_NAME'] || 'Mattermost Beta' + app_name_sub = app_name.gsub(" ", "_") + + if(File.file?(apk_path)) + UI.success("apk file #{apk_path}") + unless ENV['SUPPLY_JSON_KEY'].nil? || ENV['SUPPLY_JSON_KEY'].empty? + supply( + track: ENV['SUPPLY_TRACK'] || 'alpha', + apk: apk_path + ) + end + else + UI.user_error! "apk file does not exist #{apk_path}" + return + end + + version_number = android_get_version_name(gradle_file: './android/app/build.gradle') + build_number = android_get_version_code(gradle_file: './android/app/build.gradle') + s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}" + public_link = "https://#{ENV['AWS_BUCKET_NAME']}/#{s3_folder}/#{app_name_sub}.apk" + + # Send a build message to Mattermost + pretext = '#### New Android released ready to be published' + msg = "Release has been cut and is on the Beta track ready to be published.\nDownload link: #{public_link}" + + if ENV['BETA_BUILD'] == 'true' + pretext = '#### New Android beta published to Google Play' + msg = "Sign up as a beta tester [here](https://play.google.com/apps/testing/com.mattermost.rnbeta).\nDownload link: #{public_link}" + end + + send_message_to_mattermost({ + :version_number => version_number, + :build_number => build_number, + :pretext => pretext, + :title => '', + :thumb_url => 'https://lh3.ggpht.com/XL0CrI8skkxnboGct-duyg-bZ_MxJDTrjczyjdU8OP2PM1dmj7SP4jL1K8JQeMIB3AM=w300', + :msg => msg, + :default_payloads => [], + :success => true, + }) +end + def submit_to_store apk_path = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] ipa_path = lane_context[SharedValues::IPA_OUTPUT_PATH] - app_name = ENV['APP_NAME'] || 'Mattermost Beta' - app_name_sub = app_name.gsub(" ", "_") - - s3_region = ENV['AWS_REGION'] - s3_bucket = ENV['AWS_BUCKET_NAME'] - # Submit to Google Play if required if !apk_path.nil? && ENV['SUBMIT_ANDROID_TO_GOOGLE_PLAY'] == 'true' - UI.success("apk file #{apk_path}") - - supply( - track: ENV['SUPPLY_TRACK'] || 'alpha', - apk: apk_path - ) - - version_number = android_get_version_name(gradle_file: './android/app/build.gradle') - build_number = android_get_version_code(gradle_file: './android/app/build.gradle') - - s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}" - public_link = "https://#{s3_bucket}/#{s3_folder}/#{app_name_sub}.apk" - - # Send a build message to Mattermost - pretext = '#### New Android released ready to be published' - msg = "Release has been cut and is on the Beta track ready to be published.\nDownload link: #{public_link}" - - if ENV['BETA_BUILD'] == 'true' - pretext = '#### New Android beta published to Google Play' - msg = "Sign up as a beta tester [here](https://play.google.com/apps/testing/com.mattermost.rnbeta).\nDownload link: #{public_link}" - end - - send_message_to_mattermost({ - :version_number => version_number, - :build_number => build_number, - :pretext => pretext, - :title => '', - :thumb_url => 'https://lh3.ggpht.com/XL0CrI8skkxnboGct-duyg-bZ_MxJDTrjczyjdU8OP2PM1dmj7SP4jL1K8JQeMIB3AM=w300', - :msg => msg, - :default_payloads => [], - :success => true, - }) + submit_to_google_play(apk_path) end # Submit to TestFlight if required if !ipa_path.nil? && ENV['SUBMIT_IOS_TO_TESTFLIGHT'] == 'true' - UI.success("ipa file #{ipa_path}") - - pilot - - version_number = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost') - build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj') - - s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}" - public_link = "https://#{s3_bucket}/#{s3_folder}/#{app_name_sub}.ipa" - - # Send a build message to Mattermost - pretext = '#### New iOS released ready to be published' - msg = "Release has been cut and is on TestFlight ready to be published.\nDownload link: #{public_link}" - - if ENV['BETA_BUILD'] == 'true' - pretext = '#### New iOS beta published to TestFlight' - msg = "Sign up as a beta tester [here](https://testflight.apple.com/join/Q7Rx7K9P).\nDownload link: #{public_link}" - end - - send_message_to_mattermost({ - :version_number => version_number, - :build_number => build_number, - :pretext => pretext, - :title => '', - :thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png', - :msg => msg, - :default_payloads => [], - :success => true, - }) + submit_to_testflight(ipa_path) end end @@ -686,92 +837,3 @@ def qa_build_message(options) UI.success("PR Built for #{os_type}: #{install_url}") end end - -desc 'Upload file to s3' -lane :upload_file_to_s3 do |options| - os_type = options[:os_type] - file = options[:file] - file_plist = "" - - if file.nil? || file.empty? - app_name = ENV['APP_NAME'] || 'Mattermost Beta' - filename = app_name.gsub(" ", "_") - - if os_type == 'Android' - file = "#{filename}.apk" - elsif os_type == 'iOS' - file = "#{filename}.ipa" - file_plist = "#{filename}.plist" - end - end - - - build_folder_path = Dir[File.expand_path('..')].first - file_path = "#{build_folder_path}/#{file}" - - unless ENV['AWS_BUCKET_NAME'].nil? || ENV['AWS_BUCKET_NAME'].empty? || ENV['AWS_REGION'].nil? || ENV['AWS_REGION'].empty? || file_path.nil? - s3_region = ENV['AWS_REGION'] - s3_bucket = ENV['AWS_BUCKET_NAME'] - s3_folder = '' - - version_number = '' - build_number = '' - - if is_build_pr - s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{ENV['BRANCH_TO_BUILD']}" - else - if os_type == 'Android' - version_number = android_get_version_name(gradle_file: './android/app/build.gradle') - build_number = android_get_version_code(gradle_file: './android/app/build.gradle') - elsif os_type == 'iOS' - version_number = get_version_number(xcodeproj: './ios/Mattermost.xcodeproj', target: 'Mattermost') - build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj') - end - - s3_folder = "#{ENV['AWS_FOLDER_NAME']}/#{version_number}/#{build_number}" - end - - s3 = Aws::S3::Resource.new(region: s3_region) - file_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{file}") - file_obj.upload_file("#{file_path}") - - if is_build_pr - if os_type == 'Android' - install_url = "https://#{s3_bucket}/#{s3_folder}/#{file}" - elsif os_type == 'iOS' - current_build_number = get_build_number(xcodeproj: './ios/Mattermost.xcodeproj') - plist_template = File.read('plist.erb') - plist_body = ERB.new(plist_template).result(binding) - - plist_obj = s3.bucket(s3_bucket).object("#{s3_folder}/#{file_plist}") - plist_obj.put(body: plist_body) - - install_url = "itms-services://?action=download-manifest&url=https://#{s3_bucket}/#{s3_folder}/#{file_plist}" - end - - qa_build_message({ - :os_type => os_type, - :install_url => install_url - }) - end - - public_link = "https://#{s3_bucket}/#{s3_folder}/#{file}" - if file == 'Mattermost-simulator-x86_64.app.zip' - pretext = '#### New iOS build for VM/Simulator' - msg = "Download link: #{public_link}" - send_message_to_mattermost({ - :version_number => version_number, - :build_number => build_number, - :pretext => pretext, - :title => '', - :thumb_url => 'https://support.apple.com/library/content/dam/edam/applecare/images/en_US/iOS/move-to-ios-icon.png', - :msg => msg, - :default_payloads => [], - :success => true, - }) - end - - UI.success("S3 bucket @#{s3_bucket}, object @#{s3_folder}/#{file}") - UI.success("S3 public path: #{public_link}") - end -end diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h new file mode 120000 index 000000000..2bd449376 --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h new file mode 120000 index 000000000..02be59fee --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h new file mode 120000 index 000000000..4c6252175 --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h new file mode 120000 index 000000000..d16f6f871 --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h new file mode 120000 index 000000000..df0c2ebce --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h new file mode 120000 index 000000000..0d47450a6 --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h new file mode 120000 index 000000000..e8d6b951c --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h new file mode 120000 index 000000000..caeaae4a1 --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h new file mode 120000 index 000000000..e8344312d --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h new file mode 120000 index 000000000..ee13de8b6 --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h new file mode 120000 index 000000000..ac0730e71 --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h new file mode 120000 index 000000000..0ac32058e --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h new file mode 120000 index 000000000..4c0f3d23d --- /dev/null +++ b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h b/ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h new file mode 120000 index 000000000..f174706c0 --- /dev/null +++ b/ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h @@ -0,0 +1 @@ +../../../YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h new file mode 120000 index 000000000..2bd449376 --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h new file mode 120000 index 000000000..4c6252175 --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h new file mode 120000 index 000000000..d16f6f871 --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h new file mode 120000 index 000000000..0d47450a6 --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h new file mode 120000 index 000000000..e8d6b951c --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h new file mode 120000 index 000000000..ee13de8b6 --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h new file mode 120000 index 000000000..ac0730e71 --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h new file mode 120000 index 000000000..0ac32058e --- /dev/null +++ b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h @@ -0,0 +1 @@ +../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h b/ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h new file mode 120000 index 000000000..f174706c0 --- /dev/null +++ b/ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h @@ -0,0 +1 @@ +../../../YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h \ No newline at end of file diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock new file mode 100644 index 000000000..dd0e6c594 --- /dev/null +++ b/ios/Pods/Manifest.lock @@ -0,0 +1,20 @@ +PODS: + - XCDYouTubeKit (2.7.1) + - YoutubePlayer-in-WKWebView (0.3.3) + +DEPENDENCIES: + - XCDYouTubeKit (= 2.7.1) + - YoutubePlayer-in-WKWebView (~> 0.3.1) + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - XCDYouTubeKit + - YoutubePlayer-in-WKWebView + +SPEC CHECKSUMS: + XCDYouTubeKit: c8567fd5cb388a3099fa26eee4b30df2a467847d + YoutubePlayer-in-WKWebView: 7694e858c5c3472ed067d6fe34eb9b944845e63c + +PODFILE CHECKSUM: e565d3af8eabb0e489b73c79433e140e30f8fa9c + +COCOAPODS: 1.5.3 diff --git a/ios/Pods/Pods.xcodeproj/project.pbxproj b/ios/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 000000000..be530aa9c --- /dev/null +++ b/ios/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,893 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 00EF717F2CE5C08CE046FAB25A49C579 /* Pods-MattermostTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C0A3E27A51ED227A8460BDBC0B1A5BDC /* Pods-MattermostTests-dummy.m */; }; + 19A3D8279379A5ACBE7F699CB120E20C /* XCDYouTubeOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = A16570331366E84C38260E575C1ACDAF /* XCDYouTubeOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1B069680B86677A2EA0846201573A2C9 /* XCDYouTubeClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 218BAF448A4CEBF30D9938922CA236C2 /* XCDYouTubeClient.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 1C5F63B626D206B0ED32320876A3EA60 /* XCDYouTubeVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = BB52596A2D243BBDCE8BCB3CF50A6E20 /* XCDYouTubeVideo.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 2F6113A993C9CA73926D2946728A7D37 /* XCDYouTubeVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD5E806A91AFE8DD2B077AE695123547 /* XCDYouTubeVideoPlayerViewController.m */; }; + 32BC55AFE9DADF2BE266AB44A9F9C382 /* XCDYouTubeDashManifestXML.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A014D164D90DF54F9BD4AC60868D11D /* XCDYouTubeDashManifestXML.m */; }; + 3933FE5CEDAAFF0AE1DE7413EDA8CA38 /* XCDYouTubeDashManifestXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 18BED515670B4F1D15806832C57D59F9 /* XCDYouTubeDashManifestXML.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 3F0B35BE6C13ADDF8364D0E9F7A07522 /* XCDYouTubeKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = CFFE5759AD00493BC5172561B1C1C7CD /* XCDYouTubeKit-dummy.m */; }; + 4C91B4A27AFBF35591DD3CDF23E2DC17 /* XCDYouTubeError.h in Headers */ = {isa = PBXBuildFile; fileRef = 162E428722ECD765CEE83EB3D33C71E2 /* XCDYouTubeError.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5B8AEF4A577D21542D6F8DE2B248C8E7 /* XCDYouTubeVideoWebpage.m in Sources */ = {isa = PBXBuildFile; fileRef = 920F9C3CEECF4C1924F458E3D0428ED8 /* XCDYouTubeVideoWebpage.m */; }; + 5BE4F6E90A8A1644C0BBB4635AD0D84F /* WKYTPlayerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 19281C3EF03E987F37C203D17BCA2EC5 /* WKYTPlayerView.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 5ED62229AE2031D59F1DF033F77D03B2 /* XCDYouTubeVideoOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = BA781F6AB0F7487AD05BC63EA40AB8BF /* XCDYouTubeVideoOperation.m */; }; + 607B50452E483ACCA8FA33B3C0114BFD /* XCDYouTubeVideoOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 281750171C47F186466B86E65956736D /* XCDYouTubeVideoOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 6A881BEE202BC2AC64ACE1C2E7583756 /* XCDYouTubeVideo+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CBB4B14F0B800EC1527A5353A3AE1CA /* XCDYouTubeVideo+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 7621E888B2755702A10B6724E4107332 /* XCDYouTubePlayerScript.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E94B5BEED415E94C72301A86E7A9859 /* XCDYouTubePlayerScript.m */; }; + 76BE93DCF558BFA281930A26018F9330 /* XCDYouTubeLogger+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DAF995C826738DB2D85603716D91154 /* XCDYouTubeLogger+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 77F5E0033E60972EC230A8ABD78071A3 /* XCDYouTubeVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = EFFFFBEC2A07FFA8841667B371ED34E0 /* XCDYouTubeVideo.m */; }; + 80627FF4ECD0A58F8A6C2874FA612EB1 /* YoutubePlayer-in-WKWebView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 72206B26A17824A476D334C5612D4142 /* YoutubePlayer-in-WKWebView-dummy.m */; }; + 8433D3049BEEF12D212D6B1DD6DD0512 /* XCDYouTubeClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 46714161B2C62428DB972729952DC352 /* XCDYouTubeClient.m */; }; + 90A4DC7BA3E2C3C54D484B8959CFB708 /* XCDYouTubePlayerScript.h in Headers */ = {isa = PBXBuildFile; fileRef = F1766C4F2FDC063F421CA2D4688FED68 /* XCDYouTubePlayerScript.h */; settings = {ATTRIBUTES = (Project, ); }; }; + 9EFEB7A7C37759A0328EB1B198E2C37D /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15D48C716A411AD7621E7125E083F54A /* MediaPlayer.framework */; }; + A53E28B0072944DEE03E0D2DA20C0FA0 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98C181471820795EDEAACBC10B84709A /* JavaScriptCore.framework */; }; + C50CA7A8B2F6691C7E29BD7BC2733975 /* XCDYouTubeLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F752B1C4CE355EE8A9DA89A25D0AE73 /* XCDYouTubeLogger.m */; }; + CA1EF66BB4AA70EFF1869D1FE0A373AE /* Pods-Mattermost-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 926D56CC36F536DEFF1A45331E070750 /* Pods-Mattermost-dummy.m */; }; + D56363E3D25AE7C3C37948A2ED267EFC /* WKYTPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A081D43F9928F93E8E7FCA268B6660DA /* WKYTPlayerView.m */; }; + E74005FCD11C34B3705DE02F89D4028B /* XCDYouTubeKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 724042289C9DC57B313DD893EFD1AA54 /* XCDYouTubeKit.h */; settings = {ATTRIBUTES = (Project, ); }; }; + EA6A2186A0767AD5C4A45579994658BD /* XCDYouTubeVideoPlayerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 294543F53C1E8CC7BECE8214C09765EE /* XCDYouTubeVideoPlayerViewController.h */; settings = {ATTRIBUTES = (Project, ); }; }; + F99FDC5AFF20BC2A65AEE5A41209A42C /* XCDYouTubeVideoWebpage.h in Headers */ = {isa = PBXBuildFile; fileRef = 8540AD01026C33CA77834BD0EE077CDB /* XCDYouTubeVideoWebpage.h */; settings = {ATTRIBUTES = (Project, ); }; }; + FB83D8680F87F8315167D84D2BEEDF6C /* XCDYouTubeLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 510EF6F5EB43790577352F6C8A0718B0 /* XCDYouTubeLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 165E90C48BE6DE526DE6EFE88F787BE2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 191A114290607BBCF32141FA83AB9F6E; + remoteInfo = "Pods-Mattermost"; + }; + A2AEF4FA0A01D420BBD3AC4B1428E3FD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5552A5DF4126A9D12B869B8272B40FF1; + remoteInfo = XCDYouTubeKit; + }; + DB4FDEE7D3C70BEC4323EBE859F93B11 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D5F98C908412D8333DECA6E74A2FC15E; + remoteInfo = "YoutubePlayer-in-WKWebView"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0800A13BE9EB3366E47E9A990DDEF5A4 /* Pods-MattermostTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-MattermostTests-acknowledgements.plist"; sourceTree = ""; }; + 0AD2A25648B085555683131216BAD797 /* Pods-Mattermost-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Mattermost-acknowledgements.markdown"; sourceTree = ""; }; + 0CA88A7462704DBCB53C017185C1A65F /* Pods-Mattermost-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Mattermost-acknowledgements.plist"; sourceTree = ""; }; + 15D48C716A411AD7621E7125E083F54A /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/MediaPlayer.framework; sourceTree = DEVELOPER_DIR; }; + 162E428722ECD765CEE83EB3D33C71E2 /* XCDYouTubeError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeError.h; path = XCDYouTubeKit/XCDYouTubeError.h; sourceTree = ""; }; + 18BED515670B4F1D15806832C57D59F9 /* XCDYouTubeDashManifestXML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeDashManifestXML.h; path = XCDYouTubeKit/XCDYouTubeDashManifestXML.h; sourceTree = ""; }; + 19281C3EF03E987F37C203D17BCA2EC5 /* WKYTPlayerView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = WKYTPlayerView.h; path = WKYTPlayerView/WKYTPlayerView.h; sourceTree = ""; }; + 1CBB4B14F0B800EC1527A5353A3AE1CA /* XCDYouTubeVideo+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "XCDYouTubeVideo+Private.h"; path = "XCDYouTubeKit/XCDYouTubeVideo+Private.h"; sourceTree = ""; }; + 218BAF448A4CEBF30D9938922CA236C2 /* XCDYouTubeClient.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeClient.h; path = XCDYouTubeKit/XCDYouTubeClient.h; sourceTree = ""; }; + 24390EFD555DD124430DFF9724065945 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 281750171C47F186466B86E65956736D /* XCDYouTubeVideoOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideoOperation.h; path = XCDYouTubeKit/XCDYouTubeVideoOperation.h; sourceTree = ""; }; + 294543F53C1E8CC7BECE8214C09765EE /* XCDYouTubeVideoPlayerViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideoPlayerViewController.h; path = XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h; sourceTree = ""; }; + 377B2C391CA94B7612EDC96C2306DB48 /* Pods-MattermostTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MattermostTests.release.xcconfig"; sourceTree = ""; }; + 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = XCDYouTubeKit.xcconfig; sourceTree = ""; }; + 3DAF995C826738DB2D85603716D91154 /* XCDYouTubeLogger+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "XCDYouTubeLogger+Private.h"; path = "XCDYouTubeKit/XCDYouTubeLogger+Private.h"; sourceTree = ""; }; + 46714161B2C62428DB972729952DC352 /* XCDYouTubeClient.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeClient.m; path = XCDYouTubeKit/XCDYouTubeClient.m; sourceTree = ""; }; + 510EF6F5EB43790577352F6C8A0718B0 /* XCDYouTubeLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeLogger.h; path = XCDYouTubeKit/XCDYouTubeLogger.h; sourceTree = ""; }; + 60FB83EAEF578DD0C2DE316FCF4322A9 /* Pods-MattermostTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MattermostTests.debug.xcconfig"; sourceTree = ""; }; + 6170F2E79BA4DE1619C6FD1F25C3901F /* Pods-Mattermost-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Mattermost-frameworks.sh"; sourceTree = ""; }; + 6CD50F1ED20084866B7CE47B8F8E4BE4 /* libYoutubePlayer-in-WKWebView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libYoutubePlayer-in-WKWebView.a"; path = "libYoutubePlayer-in-WKWebView.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6F752B1C4CE355EE8A9DA89A25D0AE73 /* XCDYouTubeLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeLogger.m; path = XCDYouTubeKit/XCDYouTubeLogger.m; sourceTree = ""; }; + 72206B26A17824A476D334C5612D4142 /* YoutubePlayer-in-WKWebView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "YoutubePlayer-in-WKWebView-dummy.m"; sourceTree = ""; }; + 724042289C9DC57B313DD893EFD1AA54 /* XCDYouTubeKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeKit.h; path = XCDYouTubeKit/XCDYouTubeKit.h; sourceTree = ""; }; + 7703139D81F98B8B6130B2DAFB831C41 /* libPods-Mattermost.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-Mattermost.a"; path = "libPods-Mattermost.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A014D164D90DF54F9BD4AC60868D11D /* XCDYouTubeDashManifestXML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeDashManifestXML.m; path = XCDYouTubeKit/XCDYouTubeDashManifestXML.m; sourceTree = ""; }; + 8540AD01026C33CA77834BD0EE077CDB /* XCDYouTubeVideoWebpage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideoWebpage.h; path = XCDYouTubeKit/XCDYouTubeVideoWebpage.h; sourceTree = ""; }; + 8A89EF129E834187D884270CF5CAF3C5 /* Pods-Mattermost-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Mattermost-resources.sh"; sourceTree = ""; }; + 8E94B5BEED415E94C72301A86E7A9859 /* XCDYouTubePlayerScript.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubePlayerScript.m; path = XCDYouTubeKit/XCDYouTubePlayerScript.m; sourceTree = ""; }; + 920F9C3CEECF4C1924F458E3D0428ED8 /* XCDYouTubeVideoWebpage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideoWebpage.m; path = XCDYouTubeKit/XCDYouTubeVideoWebpage.m; sourceTree = ""; }; + 926D56CC36F536DEFF1A45331E070750 /* Pods-Mattermost-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Mattermost-dummy.m"; sourceTree = ""; }; + 934A4B9575F4CBDCBE7B044C1F049365 /* Pods-Mattermost.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Mattermost.debug.xcconfig"; sourceTree = ""; }; + 974866440990F0A14694BD9B2E9962E0 /* Pods-MattermostTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-MattermostTests-acknowledgements.markdown"; sourceTree = ""; }; + 98C181471820795EDEAACBC10B84709A /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; + A081D43F9928F93E8E7FCA268B6660DA /* WKYTPlayerView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = WKYTPlayerView.m; path = WKYTPlayerView/WKYTPlayerView.m; sourceTree = ""; }; + A16570331366E84C38260E575C1ACDAF /* XCDYouTubeOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeOperation.h; path = XCDYouTubeKit/XCDYouTubeOperation.h; sourceTree = ""; }; + A43B4ECF27212BC0465EDA19AE3164CC /* XCDYouTubeKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "XCDYouTubeKit-prefix.pch"; sourceTree = ""; }; + B564ED8191C54D3CAD8D2A2A6B9B9D1B /* Pods-Mattermost.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Mattermost.release.xcconfig"; sourceTree = ""; }; + BA781F6AB0F7487AD05BC63EA40AB8BF /* XCDYouTubeVideoOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideoOperation.m; path = XCDYouTubeKit/XCDYouTubeVideoOperation.m; sourceTree = ""; }; + BB52596A2D243BBDCE8BCB3CF50A6E20 /* XCDYouTubeVideo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideo.h; path = XCDYouTubeKit/XCDYouTubeVideo.h; sourceTree = ""; }; + C0A3E27A51ED227A8460BDBC0B1A5BDC /* Pods-MattermostTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-MattermostTests-dummy.m"; sourceTree = ""; }; + C70E5870375DC970207B78465E560E64 /* YoutubePlayer-in-WKWebView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "YoutubePlayer-in-WKWebView-prefix.pch"; sourceTree = ""; }; + C99CF8DC55235B8BA857A47235948EE7 /* libXCDYouTubeKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libXCDYouTubeKit.a; path = libXCDYouTubeKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; + CA6DA7F1941EC2569E22ACF8A89FA03B /* Pods-MattermostTests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-MattermostTests-resources.sh"; sourceTree = ""; }; + CFFE5759AD00493BC5172561B1C1C7CD /* XCDYouTubeKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "XCDYouTubeKit-dummy.m"; sourceTree = ""; }; + DD5E806A91AFE8DD2B077AE695123547 /* XCDYouTubeVideoPlayerViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideoPlayerViewController.m; path = XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m; sourceTree = ""; }; + E0545ADDF3E1FF7C1BC19F497787C42B /* WKYTPlayerView.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = WKYTPlayerView.bundle; path = WKYTPlayerView/WKYTPlayerView.bundle; sourceTree = ""; }; + E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "YoutubePlayer-in-WKWebView.xcconfig"; sourceTree = ""; }; + E99B330F0F9A45FF2E1ACB80DA268841 /* libPods-MattermostTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-MattermostTests.a"; path = "libPods-MattermostTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + EFFFFBEC2A07FFA8841667B371ED34E0 /* XCDYouTubeVideo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideo.m; path = XCDYouTubeKit/XCDYouTubeVideo.m; sourceTree = ""; }; + F1766C4F2FDC063F421CA2D4688FED68 /* XCDYouTubePlayerScript.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubePlayerScript.h; path = XCDYouTubeKit/XCDYouTubePlayerScript.h; sourceTree = ""; }; + FFD8DC263483FE0689017EF3573C5C71 /* Pods-MattermostTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-MattermostTests-frameworks.sh"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3BF9126BA0554EB8C75B0C9B1F5908D5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9607CC185A314A5FB24F3B67E3C0B743 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A53E28B0072944DEE03E0D2DA20C0FA0 /* JavaScriptCore.framework in Frameworks */, + 9EFEB7A7C37759A0328EB1B198E2C37D /* MediaPlayer.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E909807276D8D8C1F62A49D70C2048F0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EBE0F2650665138130247C39F808CDFB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1628BF05B4CAFDCC3549A101F5A10A17 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B01816E2D09E8070AEE9E96136D917B6 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 3EAAB2CFFBD8598AB3144F85D9EDACD8 /* Pods-Mattermost */ = { + isa = PBXGroup; + children = ( + 0AD2A25648B085555683131216BAD797 /* Pods-Mattermost-acknowledgements.markdown */, + 0CA88A7462704DBCB53C017185C1A65F /* Pods-Mattermost-acknowledgements.plist */, + 926D56CC36F536DEFF1A45331E070750 /* Pods-Mattermost-dummy.m */, + 6170F2E79BA4DE1619C6FD1F25C3901F /* Pods-Mattermost-frameworks.sh */, + 8A89EF129E834187D884270CF5CAF3C5 /* Pods-Mattermost-resources.sh */, + 934A4B9575F4CBDCBE7B044C1F049365 /* Pods-Mattermost.debug.xcconfig */, + B564ED8191C54D3CAD8D2A2A6B9B9D1B /* Pods-Mattermost.release.xcconfig */, + ); + name = "Pods-Mattermost"; + path = "Target Support Files/Pods-Mattermost"; + sourceTree = ""; + }; + 428BC12C5AD7D04CCD5564A473A61275 /* Pods */ = { + isa = PBXGroup; + children = ( + 950725B3833769C44391B7E733E759D0 /* XCDYouTubeKit */, + 6571058FEA94E8172ABEAFD4A1D3C1A7 /* YoutubePlayer-in-WKWebView */, + ); + name = Pods; + sourceTree = ""; + }; + 6571058FEA94E8172ABEAFD4A1D3C1A7 /* YoutubePlayer-in-WKWebView */ = { + isa = PBXGroup; + children = ( + 19281C3EF03E987F37C203D17BCA2EC5 /* WKYTPlayerView.h */, + A081D43F9928F93E8E7FCA268B6660DA /* WKYTPlayerView.m */, + 92B16AFE4649769630669C6CDA9910C9 /* Resources */, + C609F4F9C6363362CDC6B91DC2BB8DA7 /* Support Files */, + ); + name = "YoutubePlayer-in-WKWebView"; + path = "YoutubePlayer-in-WKWebView"; + sourceTree = ""; + }; + 6CDFB0049112FA789537AC9B9E94FC6E /* Pods-MattermostTests */ = { + isa = PBXGroup; + children = ( + 974866440990F0A14694BD9B2E9962E0 /* Pods-MattermostTests-acknowledgements.markdown */, + 0800A13BE9EB3366E47E9A990DDEF5A4 /* Pods-MattermostTests-acknowledgements.plist */, + C0A3E27A51ED227A8460BDBC0B1A5BDC /* Pods-MattermostTests-dummy.m */, + FFD8DC263483FE0689017EF3573C5C71 /* Pods-MattermostTests-frameworks.sh */, + CA6DA7F1941EC2569E22ACF8A89FA03B /* Pods-MattermostTests-resources.sh */, + 60FB83EAEF578DD0C2DE316FCF4322A9 /* Pods-MattermostTests.debug.xcconfig */, + 377B2C391CA94B7612EDC96C2306DB48 /* Pods-MattermostTests.release.xcconfig */, + ); + name = "Pods-MattermostTests"; + path = "Target Support Files/Pods-MattermostTests"; + sourceTree = ""; + }; + 7D3B7C9338829DEF8BC3A52614FF35D6 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 3EAAB2CFFBD8598AB3144F85D9EDACD8 /* Pods-Mattermost */, + 6CDFB0049112FA789537AC9B9E94FC6E /* Pods-MattermostTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + 92B16AFE4649769630669C6CDA9910C9 /* Resources */ = { + isa = PBXGroup; + children = ( + E0545ADDF3E1FF7C1BC19F497787C42B /* WKYTPlayerView.bundle */, + ); + name = Resources; + sourceTree = ""; + }; + 950725B3833769C44391B7E733E759D0 /* XCDYouTubeKit */ = { + isa = PBXGroup; + children = ( + 218BAF448A4CEBF30D9938922CA236C2 /* XCDYouTubeClient.h */, + 46714161B2C62428DB972729952DC352 /* XCDYouTubeClient.m */, + 18BED515670B4F1D15806832C57D59F9 /* XCDYouTubeDashManifestXML.h */, + 7A014D164D90DF54F9BD4AC60868D11D /* XCDYouTubeDashManifestXML.m */, + 162E428722ECD765CEE83EB3D33C71E2 /* XCDYouTubeError.h */, + 724042289C9DC57B313DD893EFD1AA54 /* XCDYouTubeKit.h */, + 510EF6F5EB43790577352F6C8A0718B0 /* XCDYouTubeLogger.h */, + 6F752B1C4CE355EE8A9DA89A25D0AE73 /* XCDYouTubeLogger.m */, + 3DAF995C826738DB2D85603716D91154 /* XCDYouTubeLogger+Private.h */, + A16570331366E84C38260E575C1ACDAF /* XCDYouTubeOperation.h */, + F1766C4F2FDC063F421CA2D4688FED68 /* XCDYouTubePlayerScript.h */, + 8E94B5BEED415E94C72301A86E7A9859 /* XCDYouTubePlayerScript.m */, + BB52596A2D243BBDCE8BCB3CF50A6E20 /* XCDYouTubeVideo.h */, + EFFFFBEC2A07FFA8841667B371ED34E0 /* XCDYouTubeVideo.m */, + 1CBB4B14F0B800EC1527A5353A3AE1CA /* XCDYouTubeVideo+Private.h */, + 281750171C47F186466B86E65956736D /* XCDYouTubeVideoOperation.h */, + BA781F6AB0F7487AD05BC63EA40AB8BF /* XCDYouTubeVideoOperation.m */, + 294543F53C1E8CC7BECE8214C09765EE /* XCDYouTubeVideoPlayerViewController.h */, + DD5E806A91AFE8DD2B077AE695123547 /* XCDYouTubeVideoPlayerViewController.m */, + 8540AD01026C33CA77834BD0EE077CDB /* XCDYouTubeVideoWebpage.h */, + 920F9C3CEECF4C1924F458E3D0428ED8 /* XCDYouTubeVideoWebpage.m */, + E6B7DAED273DD709FF4449921C0817D9 /* Support Files */, + ); + name = XCDYouTubeKit; + path = XCDYouTubeKit; + sourceTree = ""; + }; + A3D27D0A8AF193B0B5A20E3F139F18EE /* Products */ = { + isa = PBXGroup; + children = ( + 7703139D81F98B8B6130B2DAFB831C41 /* libPods-Mattermost.a */, + E99B330F0F9A45FF2E1ACB80DA268841 /* libPods-MattermostTests.a */, + C99CF8DC55235B8BA857A47235948EE7 /* libXCDYouTubeKit.a */, + 6CD50F1ED20084866B7CE47B8F8E4BE4 /* libYoutubePlayer-in-WKWebView.a */, + ); + name = Products; + sourceTree = ""; + }; + B01816E2D09E8070AEE9E96136D917B6 /* iOS */ = { + isa = PBXGroup; + children = ( + 98C181471820795EDEAACBC10B84709A /* JavaScriptCore.framework */, + 15D48C716A411AD7621E7125E083F54A /* MediaPlayer.framework */, + ); + name = iOS; + sourceTree = ""; + }; + C609F4F9C6363362CDC6B91DC2BB8DA7 /* Support Files */ = { + isa = PBXGroup; + children = ( + E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */, + 72206B26A17824A476D334C5612D4142 /* YoutubePlayer-in-WKWebView-dummy.m */, + C70E5870375DC970207B78465E560E64 /* YoutubePlayer-in-WKWebView-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/YoutubePlayer-in-WKWebView"; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 24390EFD555DD124430DFF9724065945 /* Podfile */, + 1628BF05B4CAFDCC3549A101F5A10A17 /* Frameworks */, + 428BC12C5AD7D04CCD5564A473A61275 /* Pods */, + A3D27D0A8AF193B0B5A20E3F139F18EE /* Products */, + 7D3B7C9338829DEF8BC3A52614FF35D6 /* Targets Support Files */, + ); + sourceTree = ""; + }; + E6B7DAED273DD709FF4449921C0817D9 /* Support Files */ = { + isa = PBXGroup; + children = ( + 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */, + CFFE5759AD00493BC5172561B1C1C7CD /* XCDYouTubeKit-dummy.m */, + A43B4ECF27212BC0465EDA19AE3164CC /* XCDYouTubeKit-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/XCDYouTubeKit"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 121FA629058A6D7550BAA7E46A2DFF6C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 1B069680B86677A2EA0846201573A2C9 /* XCDYouTubeClient.h in Headers */, + 3933FE5CEDAAFF0AE1DE7413EDA8CA38 /* XCDYouTubeDashManifestXML.h in Headers */, + 4C91B4A27AFBF35591DD3CDF23E2DC17 /* XCDYouTubeError.h in Headers */, + E74005FCD11C34B3705DE02F89D4028B /* XCDYouTubeKit.h in Headers */, + 76BE93DCF558BFA281930A26018F9330 /* XCDYouTubeLogger+Private.h in Headers */, + FB83D8680F87F8315167D84D2BEEDF6C /* XCDYouTubeLogger.h in Headers */, + 19A3D8279379A5ACBE7F699CB120E20C /* XCDYouTubeOperation.h in Headers */, + 90A4DC7BA3E2C3C54D484B8959CFB708 /* XCDYouTubePlayerScript.h in Headers */, + 6A881BEE202BC2AC64ACE1C2E7583756 /* XCDYouTubeVideo+Private.h in Headers */, + 1C5F63B626D206B0ED32320876A3EA60 /* XCDYouTubeVideo.h in Headers */, + 607B50452E483ACCA8FA33B3C0114BFD /* XCDYouTubeVideoOperation.h in Headers */, + EA6A2186A0767AD5C4A45579994658BD /* XCDYouTubeVideoPlayerViewController.h in Headers */, + F99FDC5AFF20BC2A65AEE5A41209A42C /* XCDYouTubeVideoWebpage.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 87BA6E865700D97E7F71638AAE9AFC0D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 946AD953D8D82CF2E2C495E655189FD9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6DE8E634D9D8E06BFF5F56A21C33E98 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5BE4F6E90A8A1644C0BBB4635AD0D84F /* WKYTPlayerView.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 191A114290607BBCF32141FA83AB9F6E /* Pods-Mattermost */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0A6512CB2B9DF1554E714C059EE642EE /* Build configuration list for PBXNativeTarget "Pods-Mattermost" */; + buildPhases = ( + 946AD953D8D82CF2E2C495E655189FD9 /* Headers */, + 4A0A92E8C79C534671C264B96C2D3980 /* Sources */, + EBE0F2650665138130247C39F808CDFB /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 32D9BECA4EDB7295613C7FBBFF1947E3 /* PBXTargetDependency */, + 12B381BF70C519F8F5E118A06F0715F4 /* PBXTargetDependency */, + ); + name = "Pods-Mattermost"; + productName = "Pods-Mattermost"; + productReference = 7703139D81F98B8B6130B2DAFB831C41 /* libPods-Mattermost.a */; + productType = "com.apple.product-type.library.static"; + }; + 5552A5DF4126A9D12B869B8272B40FF1 /* XCDYouTubeKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = A447275384E1E65FCC685DD176B14980 /* Build configuration list for PBXNativeTarget "XCDYouTubeKit" */; + buildPhases = ( + 121FA629058A6D7550BAA7E46A2DFF6C /* Headers */, + 6E02437F69A69D78682AC11124EF1525 /* Sources */, + 9607CC185A314A5FB24F3B67E3C0B743 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = XCDYouTubeKit; + productName = XCDYouTubeKit; + productReference = C99CF8DC55235B8BA857A47235948EE7 /* libXCDYouTubeKit.a */; + productType = "com.apple.product-type.library.static"; + }; + D5F98C908412D8333DECA6E74A2FC15E /* YoutubePlayer-in-WKWebView */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1F5DC73135AFED29F2A39052787E8E88 /* Build configuration list for PBXNativeTarget "YoutubePlayer-in-WKWebView" */; + buildPhases = ( + B6DE8E634D9D8E06BFF5F56A21C33E98 /* Headers */, + BAD0507591933394D74E430744349DCA /* Sources */, + 3BF9126BA0554EB8C75B0C9B1F5908D5 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "YoutubePlayer-in-WKWebView"; + productName = "YoutubePlayer-in-WKWebView"; + productReference = 6CD50F1ED20084866B7CE47B8F8E4BE4 /* libYoutubePlayer-in-WKWebView.a */; + productType = "com.apple.product-type.library.static"; + }; + DE7906C32BF02EEC471C43D2EE24AA47 /* Pods-MattermostTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 42CD827C438E22B810D149D4E59AC5FC /* Build configuration list for PBXNativeTarget "Pods-MattermostTests" */; + buildPhases = ( + 87BA6E865700D97E7F71638AAE9AFC0D /* Headers */, + 7FF2A957D32E6EED1BD925761A4422B4 /* Sources */, + E909807276D8D8C1F62A49D70C2048F0 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 2FADDBF2AD8381C1F61DFCE1A376150B /* PBXTargetDependency */, + ); + name = "Pods-MattermostTests"; + productName = "Pods-MattermostTests"; + productReference = E99B330F0F9A45FF2E1ACB80DA268841 /* libPods-MattermostTests.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1100; + LastUpgradeCheck = 1100; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = A3D27D0A8AF193B0B5A20E3F139F18EE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 191A114290607BBCF32141FA83AB9F6E /* Pods-Mattermost */, + DE7906C32BF02EEC471C43D2EE24AA47 /* Pods-MattermostTests */, + 5552A5DF4126A9D12B869B8272B40FF1 /* XCDYouTubeKit */, + D5F98C908412D8333DECA6E74A2FC15E /* YoutubePlayer-in-WKWebView */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 4A0A92E8C79C534671C264B96C2D3980 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CA1EF66BB4AA70EFF1869D1FE0A373AE /* Pods-Mattermost-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 6E02437F69A69D78682AC11124EF1525 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8433D3049BEEF12D212D6B1DD6DD0512 /* XCDYouTubeClient.m in Sources */, + 32BC55AFE9DADF2BE266AB44A9F9C382 /* XCDYouTubeDashManifestXML.m in Sources */, + 3F0B35BE6C13ADDF8364D0E9F7A07522 /* XCDYouTubeKit-dummy.m in Sources */, + C50CA7A8B2F6691C7E29BD7BC2733975 /* XCDYouTubeLogger.m in Sources */, + 7621E888B2755702A10B6724E4107332 /* XCDYouTubePlayerScript.m in Sources */, + 77F5E0033E60972EC230A8ABD78071A3 /* XCDYouTubeVideo.m in Sources */, + 5ED62229AE2031D59F1DF033F77D03B2 /* XCDYouTubeVideoOperation.m in Sources */, + 2F6113A993C9CA73926D2946728A7D37 /* XCDYouTubeVideoPlayerViewController.m in Sources */, + 5B8AEF4A577D21542D6F8DE2B248C8E7 /* XCDYouTubeVideoWebpage.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7FF2A957D32E6EED1BD925761A4422B4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00EF717F2CE5C08CE046FAB25A49C579 /* Pods-MattermostTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BAD0507591933394D74E430744349DCA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D56363E3D25AE7C3C37948A2ED267EFC /* WKYTPlayerView.m in Sources */, + 80627FF4ECD0A58F8A6C2874FA612EB1 /* YoutubePlayer-in-WKWebView-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 12B381BF70C519F8F5E118A06F0715F4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "YoutubePlayer-in-WKWebView"; + target = D5F98C908412D8333DECA6E74A2FC15E /* YoutubePlayer-in-WKWebView */; + targetProxy = DB4FDEE7D3C70BEC4323EBE859F93B11 /* PBXContainerItemProxy */; + }; + 2FADDBF2AD8381C1F61DFCE1A376150B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-Mattermost"; + target = 191A114290607BBCF32141FA83AB9F6E /* Pods-Mattermost */; + targetProxy = 165E90C48BE6DE526DE6EFE88F787BE2 /* PBXContainerItemProxy */; + }; + 32D9BECA4EDB7295613C7FBBFF1947E3 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = XCDYouTubeKit; + target = 5552A5DF4126A9D12B869B8272B40FF1 /* XCDYouTubeKit */; + targetProxy = A2AEF4FA0A01D420BBD3AC4B1428E3FD /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0430B9AA2C1C5F6D02D86CB026100EE8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = YoutubePlayer_in_WKWebView; + PRODUCT_NAME = "YoutubePlayer-in-WKWebView"; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 161EADF999CB6B2B796E0054E074569D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 60FB83EAEF578DD0C2DE316FCF4322A9 /* Pods-MattermostTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2D52F03349CA866E882EB87084FF9D21 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 3536F4235A4BE9CAF9A3A90CECC64AD5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = YoutubePlayer_in_WKWebView; + PRODUCT_NAME = "YoutubePlayer-in-WKWebView"; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 45C68B99EB6B81E0DD1319B540AF6A4D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = XCDYouTubeKit; + PRODUCT_NAME = XCDYouTubeKit; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 50A87C79F1E5C7C6BFADB42D9E50A087 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 377B2C391CA94B7612EDC96C2306DB48 /* Pods-MattermostTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 682764580BDEF953B0E72A847F082705 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 934A4B9575F4CBDCBE7B044C1F049365 /* Pods-Mattermost.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 86B3C2B708242AD4B00334BBE7D14EFB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + GCC_PREFIX_HEADER = "Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_MODULE_NAME = XCDYouTubeKit; + PRODUCT_NAME = XCDYouTubeKit; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 4.2; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + A0A8AE5FB77A90328CE173082A521CA4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B564ED8191C54D3CAD8D2A2A6B9B9D1B /* Pods-Mattermost.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B45EE3CA30FA810239EAC87C682A2A52 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0A6512CB2B9DF1554E714C059EE642EE /* Build configuration list for PBXNativeTarget "Pods-Mattermost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 682764580BDEF953B0E72A847F082705 /* Debug */, + A0A8AE5FB77A90328CE173082A521CA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1F5DC73135AFED29F2A39052787E8E88 /* Build configuration list for PBXNativeTarget "YoutubePlayer-in-WKWebView" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3536F4235A4BE9CAF9A3A90CECC64AD5 /* Debug */, + 0430B9AA2C1C5F6D02D86CB026100EE8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 42CD827C438E22B810D149D4E59AC5FC /* Build configuration list for PBXNativeTarget "Pods-MattermostTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 161EADF999CB6B2B796E0054E074569D /* Debug */, + 50A87C79F1E5C7C6BFADB42D9E50A087 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D52F03349CA866E882EB87084FF9D21 /* Debug */, + B45EE3CA30FA810239EAC87C682A2A52 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A447275384E1E65FCC685DD176B14980 /* Build configuration list for PBXNativeTarget "XCDYouTubeKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 45C68B99EB6B81E0DD1319B540AF6A4D /* Debug */, + 86B3C2B708242AD4B00334BBE7D14EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown new file mode 100644 index 000000000..8f762b302 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown @@ -0,0 +1,46 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## XCDYouTubeKit + +The MIT License (MIT) + +Copyright (c) 2013-2016 Cédric Luthi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +## YoutubePlayer-in-WKWebView + +Copyright 2014 Google Inc. All rights reserved. + +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. + +Generated by CocoaPods - https://cocoapods.org diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist new file mode 100644 index 000000000..5935b6519 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist @@ -0,0 +1,84 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2013-2016 Cédric Luthi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + License + MIT + Title + XCDYouTubeKit + Type + PSGroupSpecifier + + + FooterText + Copyright 2014 Google Inc. All rights reserved. + +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. + + License + Apache + Title + YoutubePlayer-in-WKWebView + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m new file mode 100644 index 000000000..8ec219786 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_Mattermost : NSObject +@end +@implementation PodsDummy_Pods_Mattermost +@end diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh new file mode 100755 index 000000000..08e3eaaca --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh @@ -0,0 +1,146 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + if [ -r "$source" ]; then + # Copy the dSYM into a the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=1 +} + +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh new file mode 100755 index 000000000..40b59bd7a --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh @@ -0,0 +1,124 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then + # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy + # resources to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +case "${TARGETED_DEVICE_FAMILY:-}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" || true + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_resource "${PODS_ROOT}/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_resource "${PODS_ROOT}/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle" +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + else + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" + fi +fi diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig new file mode 100644 index 000000000..eb4f27eb5 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +OTHER_LDFLAGS = $(inherited) -ObjC -l"XCDYouTubeKit" -l"YoutubePlayer-in-WKWebView" -framework "JavaScriptCore" -framework "MediaPlayer" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig new file mode 100644 index 000000000..eb4f27eb5 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +OTHER_LDFLAGS = $(inherited) -ObjC -l"XCDYouTubeKit" -l"YoutubePlayer-in-WKWebView" -framework "JavaScriptCore" -framework "MediaPlayer" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown new file mode 100644 index 000000000..102af7538 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist new file mode 100644 index 000000000..7acbad1ea --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m new file mode 100644 index 000000000..a018bb5d5 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_MattermostTests : NSObject +@end +@implementation PodsDummy_Pods_MattermostTests +@end diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh new file mode 100755 index 000000000..08e3eaaca --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh @@ -0,0 +1,146 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + if [ -r "$source" ]; then + # Copy the dSYM into a the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=1 +} + +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh new file mode 100755 index 000000000..345301f2c --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh @@ -0,0 +1,118 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then + # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy + # resources to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +case "${TARGETED_DEVICE_FAMILY:-}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" || true + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + else + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" + fi +fi diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig new file mode 100644 index 000000000..eb5fde866 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +OTHER_LDFLAGS = $(inherited) -ObjC -framework "JavaScriptCore" -framework "MediaPlayer" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig new file mode 100644 index 000000000..eb5fde866 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +OTHER_LDFLAGS = $(inherited) -ObjC -framework "JavaScriptCore" -framework "MediaPlayer" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m new file mode 100644 index 000000000..218334986 --- /dev/null +++ b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_XCDYouTubeKit : NSObject +@end +@implementation PodsDummy_XCDYouTubeKit +@end diff --git a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch new file mode 100644 index 000000000..beb2a2441 --- /dev/null +++ b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig new file mode 100644 index 000000000..af4d7a935 --- /dev/null +++ b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" +OTHER_LDFLAGS = -framework "JavaScriptCore" -framework "MediaPlayer" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/XCDYouTubeKit +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m new file mode 100644 index 000000000..a5085282e --- /dev/null +++ b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_YoutubePlayer_in_WKWebView : NSObject +@end +@implementation PodsDummy_YoutubePlayer_in_WKWebView +@end diff --git a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch new file mode 100644 index 000000000..beb2a2441 --- /dev/null +++ b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig new file mode 100644 index 000000000..ac30dae33 --- /dev/null +++ b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/YoutubePlayer-in-WKWebView" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/YoutubePlayer-in-WKWebView +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/ios/Pods/XCDYouTubeKit/LICENSE b/ios/Pods/XCDYouTubeKit/LICENSE new file mode 100644 index 000000000..e21050fe2 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 Cédric Luthi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/ios/Pods/XCDYouTubeKit/README.md b/ios/Pods/XCDYouTubeKit/README.md new file mode 100644 index 000000000..71f688c3a --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/README.md @@ -0,0 +1,151 @@ +## About + +[![Build Status](https://img.shields.io/circleci/project/0xced/XCDYouTubeKit/develop.svg?style=flat)](https://circleci.com/gh/0xced/XCDYouTubeKit) +[![Coverage Status](https://img.shields.io/codecov/c/github/0xced/XCDYouTubeKit/develop.svg?style=flat)](https://codecov.io/gh/0xced/XCDYouTubeKit/branch/develop) +[![Platform](https://img.shields.io/cocoapods/p/XCDYouTubeKit.svg?style=flat)](http://cocoadocs.org/docsets/XCDYouTubeKit/) +[![Pod Version](https://img.shields.io/cocoapods/v/XCDYouTubeKit.svg?style=flat)](https://cocoapods.org/pods/XCDYouTubeKit) +[![Carthage Compatibility](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage/) +[![License](https://img.shields.io/cocoapods/l/XCDYouTubeKit.svg?style=flat)](LICENSE) + +**XCDYouTubeKit** is a YouTube video player for iOS, tvOS and macOS. + + + +Are you enjoying XCDYouTubeKit? You can say thank you with [a tweet](https://twitter.com/intent/tweet?text=%400xced%20Thank%20you%20for%20XCDYouTubeKit%2E). I am also accepting donations. ;-) + +[![Donate button](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MGEPRSNQFMV3W) + +## Requirements + +- Runs on iOS 8.0 and later +- Runs on macOS 10.9 and later +- Runs on tvOS 9.0 and later + +## Warning + +XCDYouTubeKit is against the YouTube [Terms of Service](https://www.youtube.com/t/terms). The only *official* way of playing a YouTube video inside an app is with a web view and the [iframe player API](https://developers.google.com/youtube/iframe_api_reference). Unfortunately, this is very slow and quite ugly, so I wrote this player to give users a better viewing experience. + +## Installation + +XCDYouTubeKit is available through CocoaPods and Carthage. + +CocoaPods: + +```ruby +pod "XCDYouTubeKit", "~> 2.7" +``` + +Carthage: + +```objc +github "0xced/XCDYouTubeKit" ~> 2.7 +``` + +Alternatively, you can manually use the provided static library or dynamic framework. In order to use the static library, you must: + +1. Create a workspace (File → New → Workspace…) +2. Add your project to the workspace +3. Add the XCDYouTubeKit project to the workspace +4. Drag and drop the `libXCDYouTubeKit.a` file referenced from XCDYouTubeKit → Products → libXCDYouTubeKit.a into the *Link Binary With Libraries* build phase of your app’s target. + +These steps will ensure that `#import ` will work properly in your project. + +## Usage + +XCDYouTubeKit is [fully documented](http://cocoadocs.org/docsets/XCDYouTubeKit/). + +### iOS 8.0+ & tvOS (AVPlayerViewController) + +```objc +AVPlayerViewController *playerViewController = [AVPlayerViewController new]; +[self presentViewController:playerViewController animated:YES completion:nil]; + +__weak AVPlayerViewController *weakPlayerViewController = playerViewController; +[[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo * _Nullable video, NSError * _Nullable error) { + if (video) + { + NSDictionary *streamURLs = video.streamURLs; + NSURL *streamURL = streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming] ?: streamURLs[@(XCDYouTubeVideoQualityHD720)] ?: streamURLs[@(XCDYouTubeVideoQualityMedium360)] ?: streamURLs[@(XCDYouTubeVideoQualitySmall240)]; + weakPlayerViewController.player = [AVPlayer playerWithURL:streamURL]; + [weakPlayerViewController.player play]; + } + else + { + [self dismissViewControllerAnimated:YES completion:nil]; + } +}]; +``` + +### iOS, tvOS and macOS + +```objc +NSString *videoIdentifier = @"9bZkp7q19f0"; // A 11 characters YouTube video identifier +[[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + if (video) + { + // Do something with the `video` object + } + else + { + // Handle error + } +}]; +``` + +### iOS 8.0 + +On iOS, you can use the class `XCDYouTubeVideoPlayerViewController` the same way you use a `MPMoviePlayerViewController`, except you initialize it with a YouTube video identifier instead of a content URL. + +#### Present the video in full-screen + +```objc +- (void) playVideo +{ + XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayerViewController.moviePlayer]; + [self presentMoviePlayerViewControllerAnimated:videoPlayerViewController]; +} + +- (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:notification.object]; + MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; + if (finishReason == MPMovieFinishReasonPlaybackError) + { + NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; + // Handle error + } +} + +``` + +#### Present the video in a non full-screen view + +```objc +XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; +[videoPlayerViewController presentInView:self.videoContainerView]; +[videoPlayerViewController.moviePlayer play]; +``` + +See the demo project for more sample code. + +## Logging + +Since version 2.2.0, XCDYouTubeKit produces logs. XCDYouTubeKit supports [CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack) but does not require it. + +See the `XCDYouTubeLogger` class [documentation](http://cocoadocs.org/docsets/XCDYouTubeKit/) for more information. + +## Credits + +The URL extraction algorithms in *XCDYouTubeKit* are inspired by the [YouTube extractor](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py) module of the *youtube-dl* project. + +## Contact + +Cédric Luthi + +- http://github.com/0xced +- http://twitter.com/0xced + +## License + +XCDYouTubeKit is available under the MIT license. See the [LICENSE](LICENSE) file for more information. diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h new file mode 100644 index 000000000..2b89201c3 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h @@ -0,0 +1,98 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#if !__has_feature(nullability) +#define NS_ASSUME_NONNULL_BEGIN +#define NS_ASSUME_NONNULL_END +#define nullable +#define __nullable +#endif + +#import + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The `XCDYouTubeClient` class is responsible for interacting with the YouTube API. Given a YouTube video identifier, you will get video information with the `<-getVideoWithIdentifier:completionHandler:>` method. + * + * On iOS, you probably don’t want to use `XCDYouTubeClient` directly but the higher level class ``. + */ +@interface XCDYouTubeClient : NSObject + +/** + * ------------------ + * @name Initializing + * ------------------ + */ + +/** + * Returns the shared client with the default language, i.e. the preferred language of the main bundle. + * + * @return The default client. + */ ++ (instancetype) defaultClient; + +/** + * Initializes a client with the specified language identifier. + * + * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier, the preferred language of the main bundle will be used. + * + * @return A client with the specified language identifier. + */ +- (instancetype) initWithLanguageIdentifier:(nullable NSString *)languageIdentifier; + +/** + * --------------------------------- + * @name Accessing client properties + * --------------------------------- + */ + +/** + * The language identifier of the client, used for error localization. + * + * @see -initWithLanguageIdentifier: + */ +@property (nonatomic, readonly) NSString *languageIdentifier; + +/** + * -------------------------------------- + * @name Interacting with the YouTube API + * -------------------------------------- + */ + +/** + * Starts an asynchronous operation for the specified video identifier, and calls a handler upon completion. + * + * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid (including nil) the completion handler will be called with an error with `XCDYouTubeVideoErrorDomain` domain and `XCDYouTubeErrorInvalidVideoIdentifier` code. + * @param completionHandler A block to execute when the client finishes the operation. The completion handler is executed on the main thread. If the completion handler is nil, this method throws an exception. + * + * @discussion If the operation completes successfully, the video parameter of the handler block contains a `` object, and the error parameter is nil. If the operation fails, the video parameter is nil and the error parameter contains information about the failure. The error's domain is always `XCDYouTubeVideoErrorDomain`. + * + * @see XCDYouTubeErrorCode + * + * @return An opaque object conforming to the `` protocol for canceling the asynchronous video information operation. If you call the `cancel` method before the operation is finished, the completion handler will not be called. It is recommended that you store this opaque object as a weak property. + */ +- (id) getVideoWithIdentifier:(nullable NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler; + +/** + * Starts an asynchronous operation for the specified video identifier, and calls a handler upon completion. + * + * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid (including nil) the completion handler will be called with an error with `XCDYouTubeVideoErrorDomain` domain and `XCDYouTubeErrorInvalidVideoIdentifier` code. + * @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. + * @param completionHandler A block to execute when the client finishes the operation. The completion handler is executed on the main thread. If the completion handler is nil, this method throws an exception. + * + * @discussion If the operation completes successfully, the video parameter of the handler block contains a `` object, and the error parameter is nil. If the operation fails, the video parameter is nil and the error parameter contains information about the failure. The error's domain is always `XCDYouTubeVideoErrorDomain`. + * + * @see XCDYouTubeErrorCode + * + * @return An opaque object conforming to the `` protocol for canceling the asynchronous video information operation. If you call the `cancel` method before the operation is finished, the completion handler will not be called. It is recommended that you store this opaque object as a weak property. + */ +- (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(nullable NSArray *)cookies completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler; +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m new file mode 100644 index 000000000..95ff0053c --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m @@ -0,0 +1,83 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeClient.h" + +#import "XCDYouTubeVideoOperation.h" + +@interface XCDYouTubeClient () +@property (nonatomic, strong) NSOperationQueue *queue; +@end + +@implementation XCDYouTubeClient + +@synthesize languageIdentifier = _languageIdentifier; + ++ (instancetype) defaultClient +{ + static XCDYouTubeClient *defaultClient; + static dispatch_once_t once; + dispatch_once(&once, ^{ + defaultClient = [self new]; + }); + return defaultClient; +} + +- (instancetype) init +{ + return [self initWithLanguageIdentifier:nil]; +} + +- (instancetype) initWithLanguageIdentifier:(NSString *)languageIdentifier +{ + if (!(self = [super init])) + return nil; // LCOV_EXCL_LINE + + _languageIdentifier = ^{ + return languageIdentifier ?: ^{ + NSArray *preferredLocalizations = [[NSBundle mainBundle] preferredLocalizations]; + NSString *preferredLocalization = preferredLocalizations.firstObject ?: @"en"; + return [NSLocale canonicalLanguageIdentifierFromString:preferredLocalization] ?: @"en"; + }(); + }(); + + _queue = [NSOperationQueue new]; + _queue.maxConcurrentOperationCount = 6; // paul_irish: Chrome re-confirmed that the 6 connections-per-host limit is the right magic number: https://code.google.com/p/chromium/issues/detail?id=285567#c14 [https://twitter.com/paul_irish/status/422808635698212864] + _queue.name = NSStringFromClass(self.class); + + return self; +} + +- (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(NSArray *)cookies completionHandler:(void (^)(XCDYouTubeVideo * _Nullable, NSError * _Nullable))completionHandler +{ + if (!completionHandler) + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The `completionHandler` argument must not be nil." userInfo:nil]; + + XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier cookies:cookies]; + operation.completionBlock = ^{ + [[NSOperationQueue mainQueue] addOperationWithBlock:^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" + if (operation.video || operation.error) + { + NSAssert(!(operation.video && operation.error), @"One of `video` or `error` must be nil."); + completionHandler(operation.video, operation.error); + } + else + { + NSAssert(operation.isCancelled, @"Both `video` and `error` can not be nil if the operation was not canceled."); + } + operation.completionBlock = nil; +#pragma clang diagnostic pop + }]; + }; + [self.queue addOperation:operation]; + return operation; +} +- (id) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler +{ + return [self getVideoWithIdentifier:videoIdentifier cookies:nil completionHandler:completionHandler]; +} + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h new file mode 100644 index 000000000..90658ecb0 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h @@ -0,0 +1,17 @@ +// +// XCDYouTubeDashManifestXML.h +// XCDYouTubeKit +// +// Created by Soneé John on 10/24/17. +// Copyright © 2017 Cédric Luthi. All rights reserved. +// + +#import +__attribute__((visibility("hidden"))) +@interface XCDYouTubeDashManifestXML : NSObject + +- (instancetype)initWithXMLString:(NSString *)XMLString; + +- (NSDictionary *)streamURLs; + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m new file mode 100644 index 000000000..ea7565645 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m @@ -0,0 +1,98 @@ +// +// XCDYouTubeDashManifestXML.m +// XCDYouTubeKit +// +// Created by Soneé John on 10/24/17. +// Copyright © 2017 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeDashManifestXML.h" + +@interface XCDYouTubeDashManifestXML() +@property (nonatomic, readonly) NSString *XMLString; +@end + + +@implementation XCDYouTubeDashManifestXML + +- (instancetype)initWithXMLString:(NSString *)XMLString +{ + if (!(self = [super init])) + return nil; // LCOV_EXCL_LINE + + _XMLString = XMLString; + + return self; +} + +- (NSDictionary *)streamURLs +{ + + //Catch the type + NSError *xmlTypeRegexError = NULL; + NSRegularExpression *xmlTypeRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=(type=\"))(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=(\"))" options:NSRegularExpressionAnchorsMatchLines error:&xmlTypeRegexError]; + if (xmlTypeRegexError) + return nil; + NSTextCheckingResult *xmlTypeRegexCheckingResult = [xmlTypeRegex firstMatchInString:self.XMLString options:0 range:NSMakeRange(0, self.XMLString.length)]; + NSString *xmlType = [self.XMLString substringWithRange:xmlTypeRegexCheckingResult.range]; + + NSRange staticRange = [xmlType rangeOfString:@"static" options:0]; + if (staticRange.location == NSNotFound) + return nil; + + //Do not process manifests that have DRM protection + NSRange contentProtectionRange = [self.XMLString rangeOfString:@"ContentProtection" options:0]; + NSRange mp4ProtectionRange = [self.XMLString rangeOfString:@"mp4protection" options:0]; + if (contentProtectionRange.location != NSNotFound || mp4ProtectionRange.location != NSNotFound) + return nil; + + //Catch all URLs + NSError *error = nil; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=())(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=())" options:0 error:&error]; + + if (error) + return nil; + + NSArray *checkingResults = [regex matchesInString:self.XMLString options:0 range:NSMakeRange(0, [self.XMLString length])]; + + if (checkingResults.count == 0 || checkingResults == nil) + return nil; + + NSMutableArray *URLs = [NSMutableArray new]; + NSMutableDictionary *streamURLs = [NSMutableDictionary new]; + + for (NSTextCheckingResult *checkingResult in checkingResults) + { + NSString* substringForMatch = [self.XMLString substringWithRange:checkingResult.range]; + NSURL *url = [NSURL URLWithString:substringForMatch]; + + NSRange youtubeRange = [url.absoluteString rangeOfString:@"youtube" options:0]; + NSRange itagnRange = [url.absoluteString rangeOfString:@"itag" options:0]; + + if (youtubeRange.location != NSNotFound && itagnRange.location != NSNotFound ) + { + [URLs addObject:url]; + } + } + + for (NSURL *url in URLs) + { + NSError *itagRegexError = nil; + NSRegularExpression *itagRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=(/itag/))(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=(/))" options:NSRegularExpressionAnchorsMatchLines error:&itagRegexError]; + + if (itagRegexError) + continue; + + NSTextCheckingResult *itagCheckingResult = [itagRegex firstMatchInString:(NSString *_Nonnull)url.absoluteString options:0 range:NSMakeRange(0, url.absoluteString.length)]; + + NSString *itag = [url.absoluteString substringWithRange:itagCheckingResult.range]; + streamURLs[@(itag.integerValue)] = url; + } + + if (streamURLs.count == 0) + return nil; + + return streamURLs; +} + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h new file mode 100644 index 000000000..92ffe38e5 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h @@ -0,0 +1,47 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +/** + * The error domain used throughout XCDYouTubeKit. + */ +extern NSString *const XCDYouTubeVideoErrorDomain; + +/** + * A key that may be present in the error's userInfo dictionary when the error code is XCDYouTubeErrorRestrictedPlayback. + * The object for that key is a NSSet instance containing localized country names. + */ +extern NSString *const XCDYouTubeAllowedCountriesUserInfoKey; + +/** + * These values are returned as the error code property of an NSError object with the domain `XCDYouTubeVideoErrorDomain`. + */ +typedef NS_ENUM(NSInteger, XCDYouTubeErrorCode) { + /** + * Returned when no suitable video stream is available. + */ + XCDYouTubeErrorNoStreamAvailable = -2, + + /** + * Returned when a network error occurs. See `NSUnderlyingErrorKey` in the userInfo dictionary for more information. + */ + XCDYouTubeErrorNetwork = -1, + + /** + * Returned when the given video identifier string is invalid. + */ + XCDYouTubeErrorInvalidVideoIdentifier = 2, + + /** + * Previously returned when the video was removed as a violation of YouTube's policy or when the video did not exist. + * Now replaced by code 150, i.e. `XCDYouTubeErrorRestrictedPlayback`. + */ + XCDYouTubeErrorRemovedVideo DEPRECATED_MSG_ATTRIBUTE("YouTube has stopped using error code 100.") = 100, + + /** + * Returned when the video is not playable because of legal reasons or when the video is private. + */ + XCDYouTubeErrorRestrictedPlayback = 150 +}; diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h new file mode 100644 index 000000000..250ef88f2 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h @@ -0,0 +1,16 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +#import +#import +#import +#import +#import +#import + +#if TARGET_OS_IOS || (!defined(TARGET_OS_IOS) && TARGET_OS_IPHONE) +#import +#endif diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h new file mode 100644 index 000000000..65612410a --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +#import "XCDYouTubeLogger.h" + +@interface XCDYouTubeLogger () + ++ (void) logMessage:(NSString * (^)(void))message level:(XCDLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line; + +@end + +#define XCDYouTubeLog(_level, _message) [XCDYouTubeLogger logMessage:(_message) level:(_level) file:__FILE__ function:__PRETTY_FUNCTION__ line:__LINE__] + +#define XCDYouTubeLogError(format, ...) XCDYouTubeLog(XCDLogLevelError, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) +#define XCDYouTubeLogWarning(format, ...) XCDYouTubeLog(XCDLogLevelWarning, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) +#define XCDYouTubeLogInfo(format, ...) XCDYouTubeLog(XCDLogLevelInfo, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) +#define XCDYouTubeLogDebug(format, ...) XCDYouTubeLog(XCDLogLevelDebug, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) +#define XCDYouTubeLogVerbose(format, ...) XCDYouTubeLog(XCDLogLevelVerbose, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h new file mode 100644 index 000000000..b5737ee6e --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h @@ -0,0 +1,103 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +/** + * The [context][1] used when logging with CocoaLumberjack. + * + * [1]: https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/CustomContext.md + */ +extern const NSInteger XCDYouTubeKitLumberjackContext; + +/** + * The log levels, closely mirroring the log levels of CocoaLumberjack. + */ +typedef NS_ENUM(NSUInteger, XCDLogLevel) { + /** + * Used when an error is produced, e.g. when a `` finishes with an error. + */ + XCDLogLevelError = 0, + + /** + * Used on unusual conditions that may eventually lead to an error. + */ + XCDLogLevelWarning = 1, + + /** + * Used when logging normal operational information, e.g. when a `` starts, is cancelled or finishes. + */ + XCDLogLevelInfo = 2, + + /** + * Used throughout a `` for debugging purpose, e.g. for HTTP requests. + */ + XCDLogLevelDebug = 3, + + /** + * Used to report large amount of information, e.g. full HTTP responses. + */ + XCDLogLevelVerbose = 4, +}; + +/** + * You can use the `XCDYouTubeLogger` class to configure how the XCDYouTubeKit framework emits logs. + * + * By default, logs are emitted through CocoaLumberjack if it is available, i.e. if the `DDLog` class is found at runtime. + * The [context][1] used for CocoaLumberjack is the `XCDYouTubeKitLumberjackContext` constant whose value is `(NSInteger)0xced70676`. + * + * If CocoaLumberjack is not available, logs are emitted with `NSLog`, prefixed with the `[XCDYouTubeKit]` string. + * + * ## Controlling log levels + * + * If you are using CocoaLumberjack, you are responsible for controlling the log levels with the CocoaLumberjack APIs. + * + * If you are not using CocoaLumberjack, you can control the log levels with the `XCDYouTubeKitLogLevel` environment variable. See also the `` enum. + * + * Level | Value | Mask + * --------|-------|------ + * Error | 0 | 0x01 + * Warning | 1 | 0x02 + * Info | 2 | 0x04 + * Debug | 3 | 0x08 + * Verbose | 4 | 0x10 + * + * Use the corresponding bitmask to combine levels. For example, if you want to log *error*, *warning* and *info* levels, set the `XCDYouTubeKitLogLevel` environment variable to `0x7` (0x01 | 0x02 | 0x04). + * + * If you do not set the `XCDYouTubeKitLogLevel` environment variable, only warning and error levels are logged. + * + * [1]: https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/CustomContext.md + */ +@interface XCDYouTubeLogger : NSObject + +/** + * ------------------- + * @name Custom Logger + * ------------------- + */ + +/** + * If you prefer not to use CocoaLumberjack and want something more advanced than the default `NSLog` implementation, you can use this method to write your own logger. + * + * @param logHandler The block called when a log is emitted by the XCDYouTubeKit framework. If you set the log handler to nil, logging will be completely disabled. + * + * @discussion Here is a description of the log handler parameters. + * + * - The `message` parameter is a block returning a string that you must call to evaluate the log message. + * - The `level` parameter is the log level of the message, see ``. + * - The `file` parameter is the full path of the file, captured with the `__FILE__` macro where the log is emitted. + * - The `function` parameter is the function name, captured with the `__PRETTY_FUNCTION__` macro where the log is emitted. + * - The `line` parameter is the line number, captured with the `__LINE__` macro where the log is emitted. + * + * Here is how you could implement a custom log handler with [NSLogger](https://github.com/fpillet/NSLogger): + * + * ``` + * [XCDYouTubeLogger setLogHandler:^(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) { + * LogMessageRawF(file, (int)line, function, @"XCDYouTubeKit", (int)level, message()); + * }]; + * ``` + */ ++ (void) setLogHandler:(void (^)(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line))logHandler; + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m new file mode 100644 index 000000000..511e58c39 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m @@ -0,0 +1,63 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeLogger.h" + +#import + +const NSInteger XCDYouTubeKitLumberjackContext = (NSInteger)0xced70676; + +@protocol XCDYouTubeLogger_DDLog +// Copied from CocoaLumberjack's DDLog interface ++ (void) log:(BOOL)asynchronous message:(NSString *)message level:(NSUInteger)level flag:(NSUInteger)flag context:(NSInteger)context file:(const char *)file function:(const char *)function line:(NSUInteger)line tag:(id)tag; +@end + +static Class DDLogClass = Nil; + +static void (^const CocoaLumberjackLogHandler)(NSString * (^)(void), XCDLogLevel, const char *, const char *, NSUInteger) = ^(NSString *(^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) +{ + // The `XCDLogLevel` enum was carefully crafted to match the `DDLogFlag` options from DDLog.h + [DDLogClass log:YES message:message() level:NSUIntegerMax flag:(1 << level) context:XCDYouTubeKitLumberjackContext file:file function:function line:line tag:nil]; +}; + +static void (^LogHandler)(NSString * (^)(void), XCDLogLevel, const char *, const char *, NSUInteger) = ^(NSString *(^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) +{ + char *logLevelString = getenv("XCDYouTubeKitLogLevel"); + NSUInteger logLevelMask = logLevelString ? strtoul(logLevelString, NULL, 0) : (1 << XCDLogLevelError) | (1 << XCDLogLevelWarning); + if ((1 << level) & logLevelMask) + NSLog(@"[XCDYouTubeKit] %@", message()); +}; + +@implementation XCDYouTubeLogger + ++ (void) initialize +{ + static dispatch_once_t once; + dispatch_once(&once, ^{ + DDLogClass = objc_lookUpClass("DDLog"); + if (DDLogClass) + { + const SEL logSeletor = @selector(log:message:level:flag:context:file:function:line:tag:); + const char *typeEncoding = method_getTypeEncoding((Method)class_getClassMethod(DDLogClass, logSeletor)); + const char *expectedTypeEncoding = protocol_getMethodDescription(@protocol(XCDYouTubeLogger_DDLog), logSeletor, /* isRequiredMethod: */ YES, /* isInstanceMethod: */ NO).types; + if (typeEncoding && expectedTypeEncoding && strcmp(typeEncoding, expectedTypeEncoding) == 0) + LogHandler = CocoaLumberjackLogHandler; + else + NSLog(@"[XCDYouTubeKit] Incompatible CocoaLumberjack version. Expected \"%@\", got \"%@\".", expectedTypeEncoding ? @(expectedTypeEncoding) : @"", typeEncoding ? @(typeEncoding) : @""); + } + }); +} + ++ (void) setLogHandler:(void (^)(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line))logHandler +{ + LogHandler = logHandler; +} + ++ (void) logMessage:(NSString * (^)(void))message level:(XCDLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line +{ + if (LogHandler) + LogHandler(message, level, file, function, line); +} + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h new file mode 100644 index 000000000..0b425c4df --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h @@ -0,0 +1,23 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +/** + * The `XCDYouTubeOperation` protocol is adopted by opaque objects returned by the `<-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]>` method. + */ +@protocol XCDYouTubeOperation + +/** + * --------------- + * @name Canceling + * --------------- + */ + +/** + * Cancels the operation. If the operation is already finished, does nothing. + */ +- (void) cancel; + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h new file mode 100644 index 000000000..5421a1cc6 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h @@ -0,0 +1,14 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +__attribute__((visibility("hidden"))) +@interface XCDYouTubePlayerScript : NSObject + +- (instancetype) initWithString:(NSString *)string; + +- (NSString *) unscrambleSignature:(NSString *)scrambledSignature; + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m new file mode 100644 index 000000000..c40209d27 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m @@ -0,0 +1,125 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubePlayerScript.h" + +#import + +#import "XCDYouTubeLogger+Private.h" + +@interface XCDYouTubePlayerScript () +@property (nonatomic, strong) JSContext *context; +@property (nonatomic, strong) JSValue *signatureFunction; +@end + +@implementation XCDYouTubePlayerScript + +- (instancetype) initWithString:(NSString *)string +{ + if (!(self = [super init])) + return nil; // LCOV_EXCL_LINE + + _context = [JSContext new]; + _context.exceptionHandler = ^(JSContext *context, JSValue *exception) { + XCDYouTubeLogWarning(@"JavaScript exception: %@", exception); + }; + + NSDictionary *environment = @{ + @"document": @{ + @"documentElement": @{} + }, + @"location": @{ + @"hash": @"" + }, + @"navigator": @{ + @"userAgent": @"" + }, + }; + _context[@"window"] = @{}; + for (NSString *propertyName in environment) + { + JSValue *value = [JSValue valueWithObject:environment[propertyName] inContext:_context]; + _context[propertyName] = value; + _context[@"window"][propertyName] = value; + } + + NSString *matchMediaJsFunction = @"var matchMediaWindow=this;matchMediaWindow.matchMedia=function(a){return false;};"; + NSString *script = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + script=[matchMediaJsFunction stringByAppendingString:(script)]; + [_context evaluateScript:script]; + + NSRegularExpression *anonymousFunctionRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\(function\\(([^)]*)\\)\\{(.*)\\}\\)\\(([^)]*)\\)" options:NSRegularExpressionDotMatchesLineSeparators error:NULL]; + NSTextCheckingResult *anonymousFunctionResult = [anonymousFunctionRegularExpression firstMatchInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length)]; + if (anonymousFunctionResult.numberOfRanges > 3) + { + NSArray *parameters = [[script substringWithRange:[anonymousFunctionResult rangeAtIndex:1]] componentsSeparatedByString:@","]; + NSArray *arguments = [[script substringWithRange:[anonymousFunctionResult rangeAtIndex:3]] componentsSeparatedByString:@","]; + if (parameters.count == arguments.count) + { + for (NSUInteger i = 0; i < parameters.count; i++) + { + _context[parameters[i]] = _context[arguments[i]]; + } + } + NSString *anonymousFunctionBody = [script substringWithRange:[anonymousFunctionResult rangeAtIndex:2]]; + [_context evaluateScript:anonymousFunctionBody]; + } + else + { + XCDYouTubeLogWarning(@"Unexpected player script (no anonymous function found)"); + } + + //See list of regex patterns here https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L1179 + NSArray*patterns = @[@"\\.sig\\|\\|([a-zA-Z0-9$]+)\\(", + @"[\"']signature[\"']\\s*,\\s*([^\\(]+)", + @"yt\\.akamaized\\.net/\\)\\s*\\|\\|\\s*.*?\\s*c\\s*&&d.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)", + @"\\bcs*&&\\s*d\\.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)\\C", + @"\\bc\\s*&&\\s*d\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\(" + ]; + + NSMutableArray*validRegularExpressions = [NSMutableArray new]; + + for (NSString *pattern in patterns) { + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:NULL]; + if (regex != nil) + { + [validRegularExpressions addObject:regex]; + } + } + + for (NSRegularExpression *regularExpression in validRegularExpressions) { + + NSArray *regexResults = [regularExpression matchesInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length)]; + + for (NSTextCheckingResult *signatureResult in regexResults) + { + NSString *signatureFunctionName = signatureResult.numberOfRanges > 1 ? [script substringWithRange:[signatureResult rangeAtIndex:1]] : nil; + if (!signatureFunctionName) + continue; + + JSValue *signatureFunction = self.context[signatureFunctionName]; + if (signatureFunction.isObject) + { + _signatureFunction = signatureFunction; + break; + } + } + } + + if (!_signatureFunction) + XCDYouTubeLogWarning(@"No signature function in player script"); + + return self; +} + +- (NSString *) unscrambleSignature:(NSString *)scrambledSignature +{ + if (!self.signatureFunction || !scrambledSignature) + return nil; + + JSValue *unscrambledSignature = [self.signatureFunction callWithArguments:@[ scrambledSignature ]]; + return [unscrambledSignature isString] ? [unscrambledSignature toString] : nil; +} + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h new file mode 100644 index 000000000..9f751206b --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h @@ -0,0 +1,24 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +#import "XCDYouTubePlayerScript.h" + +#define XCDYouTubeErrorUseCipherSignature -1000 + +extern NSString *const XCDYouTubeNoStreamVideoUserInfoKey; + +extern NSDictionary *XCDDictionaryWithQueryString(NSString *string); +extern NSString *XCDQueryStringWithDictionary(NSDictionary *dictionary); +extern NSArray *XCDCaptionArrayWithString(NSString *string); + +@interface XCDYouTubeVideo () + +- (instancetype) initWithIdentifier:(NSString *)identifier info:(NSDictionary *)info playerScript:(XCDYouTubePlayerScript *)playerScript response:(NSURLResponse *)response error:(NSError **)error; + +- (void) mergeVideo:(XCDYouTubeVideo *)video; +- (void) mergeDashManifestStreamURLs:(NSDictionary *)dashManifestStreamURLs; + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h new file mode 100644 index 000000000..7668a9301 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h @@ -0,0 +1,147 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#if !__has_feature(nullability) +#define NS_ASSUME_NONNULL_BEGIN +#define NS_ASSUME_NONNULL_END +#define nullable +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * The quality of YouTube videos. These values are used as keys in the `<[XCDYouTubeVideo streamURLs]>` property. + * + * The constant numbers are the YouTube [itag](https://en.wikipedia.org/wiki/Talk:YouTube/Archive_22#Moved_from_YouTube#Quality_formats) values. + */ +typedef NS_ENUM(NSUInteger, XCDYouTubeVideoQuality) { + /** + * Video: 240p MPEG-4 Visual | 0.175 Mbit/s + * Audio: AAC | 36 kbit/s + */ + XCDYouTubeVideoQualitySmall240 = 36, + + /** + * Video: 360p H.264 | 0.5 Mbit/s + * Audio: AAC | 96 kbit/s + */ + XCDYouTubeVideoQualityMedium360 = 18, + + /** + * Video: 720p H.264 | 2-3 Mbit/s + * Audio: AAC | 192 kbit/s + */ + XCDYouTubeVideoQualityHD720 = 22, + + /** + * Video: 1080p H.264 | 3–5.9 Mbit/s + * Audio: AAC | 192 kbit/s + * + * @deprecated YouTube has removed 1080p mp4 videos. + */ + XCDYouTubeVideoQualityHD1080 DEPRECATED_MSG_ATTRIBUTE("YouTube has removed 1080p mp4 videos.") = 37, +}; + +/** + * Used as a key in the `streamURLs` property of the `XCDYouTubeVideo` class for live videos. + */ +extern NSString *const XCDYouTubeVideoQualityHTTPLiveStreaming; + +/** + * Represents a YouTube video. Use the `<-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]>` method to obtain a `XCDYouTubeVideo` object. + */ +@interface XCDYouTubeVideo : NSObject + +/** + * -------------------------------- + * @name Accessing video properties + * -------------------------------- + */ + +/** + * The 11 characters YouTube video identifier. + */ +@property (nonatomic, readonly) NSString *identifier; +/** + * The title of the video. + */ +@property (nonatomic, readonly) NSString *title; +/** + * The duration of the video in seconds. + */ +@property (nonatomic, readonly) NSTimeInterval duration; +/** + * A thumbnail URL for an image of small size, i.e. 120×90. May be nil. + */ +@property (nonatomic, readonly, nullable) NSURL *thumbnailURL; +/** + * A thumbnail URL for an image of small size, i.e. 120×90. May be nil. + */ +@property (nonatomic, readonly, nullable) NSURL *smallThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Renamed. Use `thumbnailURL` instead."); +/** + * A thumbnail URL for an image of medium size, i.e. 320×180, 480×360 or 640×480. May be nil. + */ +@property (nonatomic, readonly, nullable) NSURL *mediumThumbnailURL DEPRECATED_MSG_ATTRIBUTE("No longer available. Use `thumbnailURL` instead."); +/** + * A thumbnail URL for an image of large size, i.e. 1'280×720 or 1'980×1'080. May be nil. + */ +@property (nonatomic, readonly, nullable) NSURL *largeThumbnailURL DEPRECATED_MSG_ATTRIBUTE("No longer available. Use `thumbnailURL` instead."); + +/** + * A dictionary of video stream URLs. + * + * The keys are the YouTube [itag](https://en.wikipedia.org/wiki/YouTube#Quality_and_formats) values as `NSNumber` objects. The values are the video URLs as `NSURL` objects. There is also the special `XCDYouTubeVideoQualityHTTPLiveStreaming` key for live videos. + * + * You should not store the URLs for later use since they have a limited lifetime and are bound to an IP address. + * + * @see XCDYouTubeVideoQuality + * @see expirationDate + */ +#if __has_feature(objc_generics) +@property (nonatomic, readonly) NSDictionary *streamURLs; +#else +@property (nonatomic, readonly) NSDictionary *streamURLs; +#endif + +/** + + * A dictionary of caption URLs (XML). + * + * The keys are the [language codes](https://www.loc.gov/standards/iso639-2/php/code_list.php) values as `NSString` objects. The values are the caption URLs as `NSURL` objects. + * + * You should not store the URLs for later use since they have a limited lifetime. + */ +#if __has_feature(objc_generics) +@property (nonatomic, readonly, nullable) NSDictionary *captionURLs; +#else +@property (nonatomic, readonly, nullable) NSDictionary *captionURLs; +#endif + +/** + + * A dictionary of auto generated caption URLs (XML). + * + * The keys are the [language codes](https://www.loc.gov/standards/iso639-2/php/code_list.php) values as `NSString` objects. The values are the caption URLs as `NSURL` objects. These URLs are the ones that were automatically generated by YouTube. + * + * You should not store the URLs for later use since they have a limited lifetime. + */ + +#if __has_feature(objc_generics) +@property (nonatomic, readonly, nullable) NSDictionary *autoGeneratedCaptionURLs; +#else +@property (nonatomic, readonly, nullable) NSDictionary *autoGeneratedCaptionURLs; +#endif + +/** + * The expiration date of the video. + * + * After this date, the stream URLs will not be playable. May be nil if it can not be determined, for example in live videos. + */ +@property (nonatomic, readonly, nullable) NSDate *expirationDate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m new file mode 100644 index 000000000..c7ae55f0f --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m @@ -0,0 +1,319 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeVideo+Private.h" + +#import "XCDYouTubeError.h" +#import "XCDYouTubeLogger+Private.h" + +#import + +NSString *const XCDYouTubeVideoErrorDomain = @"XCDYouTubeVideoErrorDomain"; +NSString *const XCDYouTubeAllowedCountriesUserInfoKey = @"AllowedCountries"; +NSString *const XCDYouTubeNoStreamVideoUserInfoKey = @"NoStreamVideo"; +NSString *const XCDYouTubeVideoQualityHTTPLiveStreaming = @"HTTPLiveStreaming"; + +NSArray *XCDCaptionArrayWithString(NSString *string) +{ + NSError *error = nil; + NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; + if (!data) { return nil; } + NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error]; + + if (error) { return nil; } + + NSDictionary *captions = JSON[@"captions"]; + NSDictionary *playerCaptionsTracklistRenderer = captions[@"playerCaptionsTracklistRenderer"]; + NSArray *captionTracks = playerCaptionsTracklistRenderer[@"captionTracks"]; + + if (captionTracks.count == 0 || captionTracks == nil) { return nil; } + return captionTracks; +} + +NSDictionary *XCDDictionaryWithQueryString(NSString *string) +{ + NSMutableDictionary *dictionary = [NSMutableDictionary new]; + NSArray *fields = [string componentsSeparatedByString:@"&"]; + for (NSString *field in fields) + { + NSArray *pair = [field componentsSeparatedByString:@"="]; + if (pair.count == 2) + { + NSString *key = pair[0]; + NSString *value = [(NSString *)pair[1] stringByRemovingPercentEncoding]; + value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; + if (dictionary[key] && ![(NSObject *)dictionary[key] isEqual:value]) + { + XCDYouTubeLogWarning(@"Using XCDDictionaryWithQueryString is inappropriate because the query string has multiple values for the key '%@'\n" + @"Query: %@\n" + @"Discarded value: %@", key, string, dictionary[key]); + } + dictionary[key] = value; + } + } + return [dictionary copy]; +} + +NSString *XCDQueryStringWithDictionary(NSDictionary *dictionary) +{ + NSArray *keys = [dictionary.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + return [(NSObject *)evaluatedObject isKindOfClass:[NSString class]]; + }]]; + + NSMutableString *query = [NSMutableString new]; + for (NSString *key in [keys sortedArrayUsingSelector:@selector(compare:)]) + { + if (query.length > 0) + [query appendString:@"&"]; + + [query appendFormat:@"%@=%@", key, [(NSObject *)dictionary[key] description]]; + } + + return [query stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; +} + +static NSString *SortedDictionaryDescription(NSDictionary *dictionary) +{ + NSArray *sortedKeys = [dictionary.allKeys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { + return [[(NSObject *)obj1 description] compare:[(NSObject *) obj2 description] options:NSNumericSearch]; + }]; + + NSMutableString *description = [[NSMutableString alloc] initWithString:@"{\n"]; + for (id key in sortedKeys) + { + [description appendFormat:@"\t%@ \u2192 %@\n", key, dictionary[key]]; + } + [description appendString:@"}"]; + + return [description copy]; +} + +static NSURL * URLBySettingParameter(NSURL *URL, NSString *key, NSString *percentEncodedValue) +{ + NSString *pattern = [NSString stringWithFormat:@"((?:^|&)%@=)[^&]*", key]; + NSString *template = [NSString stringWithFormat:@"$1%@", percentEncodedValue]; + NSURLComponents *components = [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:NO]; + NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:pattern options:(NSRegularExpressionOptions)0 error:NULL]; + NSMutableString *percentEncodedQuery = [components.percentEncodedQuery ?: @"" mutableCopy]; + NSUInteger numberOfMatches = [regularExpression replaceMatchesInString:percentEncodedQuery options:(NSMatchingOptions)0 range:NSMakeRange(0, percentEncodedQuery.length) withTemplate:template]; + if (numberOfMatches == 0) + [percentEncodedQuery appendFormat:@"%@%@=%@", percentEncodedQuery.length > 0 ? @"&" : @"", key, percentEncodedValue]; + components.percentEncodedQuery = percentEncodedQuery; + return components.URL; +} + +@implementation XCDYouTubeVideo + +static NSDate * ExpirationDate(NSURL *streamURL) +{ + NSDictionary *query = XCDDictionaryWithQueryString(streamURL.query); + NSTimeInterval expire = [(NSString *)query[@"expire"] doubleValue]; + return expire > 0 ? [NSDate dateWithTimeIntervalSince1970:expire] : nil; +} + +- (instancetype) initWithIdentifier:(NSString *)identifier info:(NSDictionary *)info playerScript:(XCDYouTubePlayerScript *)playerScript response:(NSURLResponse *)response error:(NSError * __autoreleasing *)error +{ + if (!(self = [super init])) + return nil; // LCOV_EXCL_LINE + + _identifier = identifier; + + NSString *streamMap = info[@"url_encoded_fmt_stream_map"]; + NSString *captionsMap = info[@"player_response"]; + NSString *httpLiveStream = info[@"hlsvp"]; + NSString *adaptiveFormats = info[@"adaptive_fmts"]; + + NSMutableDictionary *userInfo = response.URL ? [@{ NSURLErrorKey: (id)response.URL } mutableCopy] : [NSMutableDictionary new]; + + if (streamMap.length > 0 || httpLiveStream.length > 0) + { + NSMutableArray *streamQueries = [[streamMap componentsSeparatedByString:@","] mutableCopy]; + [streamQueries addObjectsFromArray:[adaptiveFormats componentsSeparatedByString:@","]]; + + NSString *title = info[@"title"] ?: @""; + _title = title; + _duration = [(NSString *)info[@"length_seconds"] doubleValue]; + + NSString *thumbnail = info[@"thumbnail_url"] ?: info[@"iurl"]; + _thumbnailURL = thumbnail ? [NSURL URLWithString:thumbnail] : nil; + + NSMutableDictionary *streamURLs = [NSMutableDictionary new]; + + if (httpLiveStream) + streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming] = [NSURL URLWithString:httpLiveStream]; + + NSMutableDictionary *captionURLs = [NSMutableDictionary new]; + NSMutableDictionary *autoGeneratedCaptionURLs = [NSMutableDictionary new]; + + for (NSDictionary *caption in XCDCaptionArrayWithString(captionsMap)) + { + NSString *languageCode = caption[@"languageCode"]; + NSString *captionVersion = caption[@"vssId"]; + NSString *captionURLString = caption[@"baseUrl"]; + if (!captionURLString) + { + continue; + } + NSURL *captionURL = [NSURL URLWithString:captionURLString]; + if (captionURL && languageCode) + + { + if ([languageCode isEqualToString:@"und"]) + { + //Skip because this is a special code than is used to indicate that the lanauage code is undetermined. + continue; + } + if([captionVersion hasPrefix:@"a"]) + { + //Indicates the caption was auto generated + autoGeneratedCaptionURLs[languageCode] = captionURL; + } else + { + captionURLs[languageCode] = captionURL; + } + } + } + + if (captionURLs.count > 0) + { + _captionURLs = [captionURLs copy]; + } + + if (autoGeneratedCaptionURLs.count > 0) + { + _autoGeneratedCaptionURLs = [autoGeneratedCaptionURLs copy]; + } + + for (NSString *streamQuery in streamQueries) + { + NSDictionary *stream = XCDDictionaryWithQueryString(streamQuery); + + NSString *scrambledSignature = stream[@"s"]; + if (scrambledSignature && !playerScript) + { + userInfo[XCDYouTubeNoStreamVideoUserInfoKey] = self; + if (error) + *error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorUseCipherSignature userInfo:userInfo]; + + return nil; + } + NSString *signature = [playerScript unscrambleSignature:scrambledSignature]; + if (playerScript && scrambledSignature && !signature) + continue; + + NSString *urlString = stream[@"url"]; + NSString *itag = stream[@"itag"]; + if (urlString && itag) + { + NSURL *streamURL = [NSURL URLWithString:urlString]; + if (!_expirationDate) + _expirationDate = ExpirationDate(streamURL); + + if (signature) + { + NSString *escapedSignature = [signature stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; + streamURL = URLBySettingParameter(streamURL, @"signature", escapedSignature); + } + + streamURLs[@(itag.integerValue)] = URLBySettingParameter(streamURL, @"ratebypass", @"yes"); + } + } + _streamURLs = [streamURLs copy]; + + if (_streamURLs.count == 0) + { + if (error) + *error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorNoStreamAvailable userInfo:userInfo]; + + return nil; + } + + return self; + } + else + { + if (error) + { + NSString *reason = info[@"reason"]; + if (reason) + { + reason = [reason stringByReplacingOccurrencesOfString:@"" withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, reason.length)]; + reason = [reason stringByReplacingOccurrencesOfString:@"\n" withString:@" " options:(NSStringCompareOptions)0 range:NSMakeRange(0, reason.length)]; + NSRange range; + while ((range = [reason rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound) + reason = [reason stringByReplacingCharactersInRange:range withString:@""]; + + userInfo[NSLocalizedDescriptionKey] = reason; + } + + NSString *errorcode = info[@"errorcode"]; + NSInteger code = errorcode ? errorcode.integerValue : XCDYouTubeErrorNoStreamAvailable; + *error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:code userInfo:userInfo]; + } + return nil; + } +} + +- (void) mergeVideo:(XCDYouTubeVideo *)video +{ + unsigned int count; + objc_property_t *properties = class_copyPropertyList(self.class, &count); + for (unsigned int i = 0; i < count; i++) + { + NSString *propertyName = @(property_getName(properties[i])); + if (![self valueForKey:propertyName]) + [self setValue:[video valueForKey:propertyName] forKeyPath:propertyName]; + } + free(properties); +} + +- (void) mergeDashManifestStreamURLs:(NSDictionary *)dashManifestStreamURLs { + + NSMutableDictionary *approvedStreams = [NSMutableDictionary new]; + + for (NSString *itag in dashManifestStreamURLs) { + if (self.streamURLs[itag] == nil) + approvedStreams[itag] = dashManifestStreamURLs[itag]; + } + + NSMutableDictionary *newStreams = [NSMutableDictionary dictionaryWithDictionary:self.streamURLs]; + [newStreams addEntriesFromDictionary:approvedStreams]; + [self setValue:newStreams.copy forKeyPath:NSStringFromSelector(@selector(streamURLs))]; +} + +#pragma mark - NSObject + +- (BOOL) isEqual:(id)object +{ + return [(NSObject *)object isKindOfClass:[XCDYouTubeVideo class]] && [((XCDYouTubeVideo *)object).identifier isEqual:self.identifier]; +} + +- (NSUInteger) hash +{ + return self.identifier.hash; +} + +- (NSString *) description +{ + return [NSString stringWithFormat:@"[%@] %@", self.identifier, self.title]; +} + +- (NSString *) debugDescription +{ + NSDateComponentsFormatter *dateComponentsFormatter = [NSDateComponentsFormatter new]; + dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated; + NSString *duration = [dateComponentsFormatter stringFromTimeInterval:self.duration] ?: [NSString stringWithFormat:@"%@ seconds", @(self.duration)]; + NSString *thumbnailDescription = [NSString stringWithFormat:@"Thumbnail: %@", self.thumbnailURL]; + NSString *streamsDescription = SortedDictionaryDescription(self.streamURLs); + return [NSString stringWithFormat:@"<%@: %p> %@\nDuration: %@\nExpiration date: %@\n%@\nStreams: %@", self.class, self, self.description, duration, self.expirationDate, thumbnailDescription, streamsDescription]; +} + +#pragma mark - NSCopying + +- (id) copyWithZone:(NSZone *)zone +{ + return self; +} + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h new file mode 100644 index 000000000..4ee8b7b06 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h @@ -0,0 +1,74 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#if !__has_feature(nullability) +#define NS_ASSUME_NONNULL_BEGIN +#define NS_ASSUME_NONNULL_END +#define nullable +#endif + +#import + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * XCDYouTubeVideoOperation is a subclass of `NSOperation` that connects to the YouTube API and parse the response. + * + * You should probably use the higher level class ``. Use this class only if you are very familiar with `NSOperation` and need to manage dependencies between operations. + */ +@interface XCDYouTubeVideoOperation : NSOperation + +/** + * ------------------ + * @name Initializing + * ------------------ + */ + +/** + * Initializes a video operation with the specified video identifier and language identifier. + * + * @param videoIdentifier A 11 characters YouTube video identifier. + * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier then English (`en`) is used. + * + * @return An initialized `XCDYouTubeVideoOperation` object. + */ +- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(nullable NSString *)languageIdentifier; + + +/** + Initializes a video operation with the specified video identifier and language identifier and cookies. + + @param videoIdentifier A 11 characters YouTube video identifier. + @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier then English (`en`) is used. + @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. + + @return An initialized `XCDYouTubeVideoOperation` object. + */ +- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(nullable NSString *)languageIdentifier cookies:(nullable NSArray *)cookies NS_DESIGNATED_INITIALIZER; + +/** + * -------------------------------- + * @name Accessing operation result + * -------------------------------- + */ + +/** + * Returns an error of the `XCDYouTubeVideoErrorDomain` domain if the operation failed or nil if it succeeded. + * + * Returns nil if the operation is not yet finished or if it was canceled. + */ +@property (atomic, readonly, nullable) NSError *error; +/** + * Returns a video object if the operation succeeded or nil if it failed. + * + * Returns nil if the operation is not yet finished or if it was canceled. + */ +@property (atomic, readonly, nullable) XCDYouTubeVideo *video; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m new file mode 100644 index 000000000..5fa203f46 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m @@ -0,0 +1,410 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeVideoOperation.h" + +#import + +#import "XCDYouTubeVideo+Private.h" +#import "XCDYouTubeError.h" +#import "XCDYouTubeVideoWebpage.h" +#import "XCDYouTubeDashManifestXML.h" +#import "XCDYouTubePlayerScript.h" +#import "XCDYouTubeLogger+Private.h" + +typedef NS_ENUM(NSUInteger, XCDYouTubeRequestType) { + XCDYouTubeRequestTypeGetVideoInfo = 1, + XCDYouTubeRequestTypeWatchPage, + XCDYouTubeRequestTypeEmbedPage, + XCDYouTubeRequestTypeJavaScriptPlayer, + XCDYouTubeRequestTypeDashManifest, + +}; + +@interface XCDYouTubeVideoOperation () +@property (atomic, copy, readonly) NSString *videoIdentifier; +@property (atomic, copy, readonly) NSString *languageIdentifier; +@property (atomic, strong, readonly) NSArray *cookies; + +@property (atomic, assign) NSInteger requestCount; +@property (atomic, assign) XCDYouTubeRequestType requestType; +@property (atomic, strong) NSMutableArray *eventLabels; +@property (atomic, strong) XCDYouTubeVideo *lastSuccessfulVideo; +@property (atomic, readonly) NSURLSession *session; +@property (atomic, strong) NSURLSessionDataTask *dataTask; + +@property (atomic, assign) BOOL isExecuting; +@property (atomic, assign) BOOL isFinished; +@property (atomic, readonly) dispatch_semaphore_t operationStartSemaphore; + +@property (atomic, strong) XCDYouTubeVideoWebpage *webpage; +@property (atomic, strong) XCDYouTubeVideoWebpage *embedWebpage; +@property (atomic, strong) XCDYouTubePlayerScript *playerScript; +@property (atomic, strong) XCDYouTubeVideo *noStreamVideo; +@property (atomic, strong) NSError *lastError; +@property (atomic, strong) NSError *youTubeError; // Error actually coming from the YouTube API, i.e. explicit and localized error + +@property (atomic, strong, readwrite) NSError *error; +@property (atomic, strong, readwrite) XCDYouTubeVideo *video; +@end + +@implementation XCDYouTubeVideoOperation + +static NSError *YouTubeError(NSError *error, NSSet *regionsAllowed, NSString *languageIdentifier) +{ + if (error.code == XCDYouTubeErrorRestrictedPlayback && regionsAllowed.count > 0) + { + NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageIdentifier]; + NSMutableSet *allowedCountries = [NSMutableSet new]; + for (NSString *countryCode in regionsAllowed) + { + NSString *country = [locale displayNameForKey:NSLocaleCountryCode value:countryCode]; + [allowedCountries addObject:country ?: countryCode]; + } + NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo]; + userInfo[XCDYouTubeAllowedCountriesUserInfoKey] = [allowedCountries copy]; + return [NSError errorWithDomain:error.domain code:error.code userInfo:[userInfo copy]]; + } + else + { + return error; + } +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +- (instancetype) init +{ + @throw [NSException exceptionWithName:NSGenericException reason:@"Use the `initWithVideoIdentifier:cookies:languageIdentifier:` method instead." userInfo:nil]; +} // LCOV_EXCL_LINE +#pragma clang diagnostic pop + +- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier cookies:(NSArray *)cookies +{ + if (!(self = [super init])) + return nil; // LCOV_EXCL_LINE + + _videoIdentifier = videoIdentifier ?: @""; + _languageIdentifier = languageIdentifier ?: @"en"; + + _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; + _cookies = [cookies copy]; + for (NSHTTPCookie *cookie in _cookies) { + [_session.configuration.HTTPCookieStorage setCookie:cookie]; + } + _operationStartSemaphore = dispatch_semaphore_create(0); + + return self; +} + +- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier +{ + return [self initWithVideoIdentifier:videoIdentifier languageIdentifier:languageIdentifier cookies:nil]; +} + +#pragma mark - Requests + +- (void) startNextRequest +{ + if (self.eventLabels.count == 0) + { + if (self.requestType == XCDYouTubeRequestTypeWatchPage || self.webpage) + [self finishWithError]; + else + [self startWatchPageRequest]; + } + else + { + NSString *eventLabel = [self.eventLabels objectAtIndex:0]; + [self.eventLabels removeObjectAtIndex:0]; + + NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"el": eventLabel, @"ps": @"default" }; + NSString *queryString = XCDQueryStringWithDictionary(query); + NSURL *videoInfoURL = [NSURL URLWithString:[@"https://www.youtube.com/get_video_info?" stringByAppendingString:queryString]]; + [self startRequestWithURL:videoInfoURL type:XCDYouTubeRequestTypeGetVideoInfo]; + } +} + +- (void) startWatchPageRequest +{ + NSDictionary *query = @{ @"v": self.videoIdentifier, @"hl": self.languageIdentifier, @"has_verified": @YES, @"bpctr": @9999999999 }; + NSString *queryString = XCDQueryStringWithDictionary(query); + NSURL *webpageURL = [NSURL URLWithString:[@"https://www.youtube.com/watch?" stringByAppendingString:queryString]]; + [self startRequestWithURL:webpageURL type:XCDYouTubeRequestTypeWatchPage]; +} + +- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType +{ + if (self.isCancelled) + return; + + // Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo + 1xDashManifest + if (++self.requestCount > 7) + { + // This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests + [self finishWithError]; + return; + } + + XCDYouTubeLogDebug(@"Starting request: %@", url); + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; + [request setValue:self.languageIdentifier forHTTPHeaderField:@"Accept-Language"]; + [request setValue:[NSString stringWithFormat:@"https://youtube.com/watch?v=%@", self.videoIdentifier] forHTTPHeaderField:@"Referer"]; + + self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) + { + if (self.isCancelled) + return; + + if (error) + [self handleConnectionError:error requestType:requestType]; + else + [self handleConnectionSuccessWithData:data response:response requestType:requestType]; + }]; + [self.dataTask resume]; + + self.requestType = requestType; +} + +#pragma mark - Response Dispatch + +- (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType +{ + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)response.textEncodingName ?: CFSTR("")); + // Use kCFStringEncodingMacRoman as fallback because it defines characters for every byte value and is ASCII compatible. See https://mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html + NSString *responseString = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, data.bytes, (CFIndex)data.length, encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingMacRoman, false)) ?: @""; + NSAssert(responseString.length > 0, @"Failed to decode response from %@ (response.textEncodingName = %@, data.length = %@)", response.URL, response.textEncodingName, @(data.length)); + + XCDYouTubeLogVerbose(@"Response: %@\n%@", response, responseString); + + switch (requestType) + { + case XCDYouTubeRequestTypeGetVideoInfo: + [self handleVideoInfoResponseWithInfo:XCDDictionaryWithQueryString(responseString) response:response]; + break; + case XCDYouTubeRequestTypeWatchPage: + [self handleWebPageWithHTMLString:responseString]; + break; + case XCDYouTubeRequestTypeEmbedPage: + [self handleEmbedWebPageWithHTMLString:responseString]; + break; + case XCDYouTubeRequestTypeJavaScriptPlayer: + [self handleJavaScriptPlayerWithScript:responseString]; + break; + case XCDYouTubeRequestTypeDashManifest: + [self handleDashManifestWithXMLString:responseString response:response]; + break; + } +} + +- (void) handleConnectionError:(NSError *)connectionError requestType:(XCDYouTubeRequestType)requestType +{ + //Shoud not return a connection error if was as a result of requesting the Dash Manifiest (we have a sucessfully created `XCDYouTubeVideo` and should just finish the operation as if were a 'sucessful' one + if (requestType == XCDYouTubeRequestTypeDashManifest) + { + [self finishWithVideo:self.lastSuccessfulVideo]; + return; + } + + NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: connectionError.localizedDescription, + NSUnderlyingErrorKey: connectionError }; + self.lastError = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorNetwork userInfo:userInfo]; + + [self startNextRequest]; +} + +#pragma mark - Response Parsing + +- (void) handleVideoInfoResponseWithInfo:(NSDictionary *)info response:(NSURLResponse *)response +{ + XCDYouTubeLogDebug(@"Handling video info response"); + + NSError *error = nil; + XCDYouTubeVideo *video = [[XCDYouTubeVideo alloc] initWithIdentifier:self.videoIdentifier info:info playerScript:self.playerScript response:response error:&error]; + if (video) + { + self.lastSuccessfulVideo = video; + + if (info[@"dashmpd"]) + { + NSURL *dashmpdURL = [NSURL URLWithString:(NSString *_Nonnull)info[@"dashmpd"]]; + [self startRequestWithURL:dashmpdURL type:XCDYouTubeRequestTypeDashManifest]; + return; + } + [video mergeVideo:self.noStreamVideo]; + [self finishWithVideo:video]; + } + else + { + if ([error.domain isEqual:XCDYouTubeVideoErrorDomain] && error.code == XCDYouTubeErrorUseCipherSignature) + { + self.noStreamVideo = error.userInfo[XCDYouTubeNoStreamVideoUserInfoKey]; + + [self startWatchPageRequest]; + } + else + { + self.lastError = error; + if (error.code > 0) + self.youTubeError = error; + + [self startNextRequest]; + } + } +} + +- (void) handleWebPageWithHTMLString:(NSString *)html +{ + XCDYouTubeLogDebug(@"Handling web page response"); + + self.webpage = [[XCDYouTubeVideoWebpage alloc] initWithHTMLString:html]; + + if (self.webpage.javaScriptPlayerURL) + { + [self startRequestWithURL:self.webpage.javaScriptPlayerURL type:XCDYouTubeRequestTypeJavaScriptPlayer]; + } + else + { + if (self.webpage.isAgeRestricted) + { + NSString *embedURLString = [NSString stringWithFormat:@"https://www.youtube.com/embed/%@", self.videoIdentifier]; + [self startRequestWithURL:[NSURL URLWithString:embedURLString] type:XCDYouTubeRequestTypeEmbedPage]; + } + else + { + [self startNextRequest]; + } + } +} + +- (void) handleEmbedWebPageWithHTMLString:(NSString *)html +{ + XCDYouTubeLogDebug(@"Handling embed web page response"); + + self.embedWebpage = [[XCDYouTubeVideoWebpage alloc] initWithHTMLString:html]; + + if (self.embedWebpage.javaScriptPlayerURL) + { + [self startRequestWithURL:self.embedWebpage.javaScriptPlayerURL type:XCDYouTubeRequestTypeJavaScriptPlayer]; + } + else + { + [self startNextRequest]; + } +} + +- (void) handleJavaScriptPlayerWithScript:(NSString *)script +{ + XCDYouTubeLogDebug(@"Handling JavaScript player response"); + + self.playerScript = [[XCDYouTubePlayerScript alloc] initWithString:script]; + + if (self.webpage.isAgeRestricted && self.cookies.count == 0) + { + NSString *eurl = [@"https://youtube.googleapis.com/v/" stringByAppendingString:self.videoIdentifier]; + NSString *sts = [(NSObject *)self.embedWebpage.playerConfiguration[@"sts"] description] ?: [(NSObject *)self.webpage.playerConfiguration[@"sts"] description] ?: @""; + NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"eurl": eurl, @"sts": sts}; + NSString *queryString = XCDQueryStringWithDictionary(query); + NSURL *videoInfoURL = [NSURL URLWithString:[@"https://www.youtube.com/get_video_info?" stringByAppendingString:queryString]]; + [self startRequestWithURL:videoInfoURL type:XCDYouTubeRequestTypeGetVideoInfo]; + } + else + { + [self handleVideoInfoResponseWithInfo:self.webpage.videoInfo response:nil]; + } +} + +- (void) handleDashManifestWithXMLString:(NSString *)XMLString response:(NSURLResponse *)response +{ + XCDYouTubeLogDebug(@"Handling Dash Manifest response"); + + XCDYouTubeDashManifestXML *dashManifestXML = [[XCDYouTubeDashManifestXML alloc]initWithXMLString:XMLString]; + NSDictionary *dashhManifestStreamURLs = dashManifestXML.streamURLs; + if (dashhManifestStreamURLs) + [self.lastSuccessfulVideo mergeDashManifestStreamURLs:dashhManifestStreamURLs]; + + [self finishWithVideo:self.lastSuccessfulVideo]; +} + +#pragma mark - Finish Operation + +- (void) finishWithVideo:(XCDYouTubeVideo *)video +{ + self.video = video; + XCDYouTubeLogInfo(@"Video operation finished with success: %@", video); + XCDYouTubeLogDebug(@"%@", ^{ return video.debugDescription; }()); + [self finish]; +} + +- (void) finishWithError +{ + self.error = self.youTubeError ? YouTubeError(self.youTubeError, self.webpage.regionsAllowed, self.languageIdentifier) : self.lastError; + XCDYouTubeLogError(@"Video operation finished with error: %@\nDomain: %@\nCode: %@\nUser Info: %@", self.error.localizedDescription, self.error.domain, @(self.error.code), self.error.userInfo); + [self finish]; +} + +- (void) finish +{ + self.isExecuting = NO; + self.isFinished = YES; +} + +#pragma mark - NSOperation + ++ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key +{ + SEL selector = NSSelectorFromString(key); + return selector == @selector(isExecuting) || selector == @selector(isFinished) || [super automaticallyNotifiesObserversForKey:key]; +} + +- (BOOL) isConcurrent +{ + return YES; +} + +- (void) start +{ + dispatch_semaphore_signal(self.operationStartSemaphore); + + if (self.isCancelled) + return; + + if (self.videoIdentifier.length != 11) + { + XCDYouTubeLogWarning(@"Video identifier length should be 11. [%@]", self.videoIdentifier); + } + + XCDYouTubeLogInfo(@"Starting video operation: %@", self); + + self.isExecuting = YES; + + self.eventLabels = [[NSMutableArray alloc] initWithArray:@[ @"embedded", @"detailpage" ]]; + [self startNextRequest]; +} + +- (void) cancel +{ + if (self.isCancelled || self.isFinished) + return; + + XCDYouTubeLogInfo(@"Canceling video operation: %@", self); + + [super cancel]; + + [self.dataTask cancel]; + + // Wait for `start` to be called in order to avoid this warning: *** XCDYouTubeVideoOperation 0x7f8b18c84880 went isFinished=YES without being started by the queue it is in + dispatch_semaphore_wait(self.operationStartSemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(200 * NSEC_PER_MSEC))); + [self finish]; +} + +#pragma mark - NSObject + +- (NSString *) description +{ + return [NSString stringWithFormat:@"<%@: %p> %@ (%@)", self.class, self, self.videoIdentifier, self.languageIdentifier]; +} + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h new file mode 100644 index 000000000..812ee9c15 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h @@ -0,0 +1,126 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#if !__has_feature(nullability) +#define NS_ASSUME_NONNULL_BEGIN +#define NS_ASSUME_NONNULL_END +#define nullable +#define null_resettable +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * ------------------- + * @name Notifications + * ------------------- + */ + +/** + * NSError key for the `MPMoviePlayerPlaybackDidFinishNotification` userInfo dictionary. + * + * Ideally, there should be a `MPMoviePlayerPlaybackDidFinishErrorUserInfoKey` declared near to `MPMoviePlayerPlaybackDidFinishReasonUserInfoKey` in MPMoviePlayerController.h but since it doesn't exist, here is a convenient constant key. + */ +MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; + +/** + * Posted when the video player has received the video information. The `object` of the notification is the `XCDYouTubeVideoPlayerViewController` instance. The `userInfo` dictionary contains the `XCDYouTubeVideo` object. + */ +MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification; +/** + * The key for the `XCDYouTubeVideo` object in the user info dictionary of `XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification`. + */ +MP_EXTERN NSString *const XCDYouTubeVideoUserInfoKey; + +/** + * A subclass of `MPMoviePlayerViewController` for playing YouTube videos. + * + * Use UIViewController’s `presentMoviePlayerViewControllerAnimated:` method to play a YouTube video fullscreen. + * + * Use the `` method to play a YouTube video inline. + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +DEPRECATED_MSG_ATTRIBUTE("Use `AVFoundation` or `AVKit` APIs instead.") +@interface XCDYouTubeVideoPlayerViewController : MPMoviePlayerViewController +#pragma clang diagnostic pop + +/** + * ------------------ + * @name Initializing + * ------------------ + */ + +/** + * Initializes a YouTube video player view controller + * + * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid the `MPMoviePlayerPlaybackDidFinishNotification` will be posted with a `MPMovieFinishReasonPlaybackError` reason. + * + * @return An initialized YouTube video player view controller with the specified video identifier. + * + * @discussion You can pass a nil *videoIdentifier* (or use the standard `init` method instead) and set the `` property later. + */ +- (instancetype) initWithVideoIdentifier:(nullable NSString *)videoIdentifier NS_DESIGNATED_INITIALIZER; + +/** + * ------------------------------------ + * @name Accessing the video identifier + * ------------------------------------ + */ + +/** + * The 11 characters YouTube video identifier. + */ +@property (nonatomic, copy, nullable) NSString *videoIdentifier; + +/** + * ------------------------------------------ + * @name Defining the preferred video quality + * ------------------------------------------ + */ + +/** + * The preferred order for the quality of the video to play. Plays the first match when multiple video streams are available. + * + * Defaults to @[ XCDYouTubeVideoQualityHTTPLiveStreaming, @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ] + * + * You should set this property right after calling the `` method. Setting this property to nil restores its default values. + * + * @see XCDYouTubeVideoQuality + */ +@property (nonatomic, copy, null_resettable) NSArray *preferredVideoQualities; + +/** + * ------------------------ + * @name Presenting a video + * ------------------------ + */ + +/** + * Present the video inside a view. + * + * @param view The view inside which you want to present the video. + * + * @discussion The video view is added as a subview of the specified view. The video does not start playing immediately, you have to call `[videoPlayerViewController.moviePlayer play]` for playback to start. See `MPMoviePlayerController` documentation for more information. + * + * Ownership of the XCDYouTubeVideoPlayerViewController instance is transferred to the view. + */ +- (void) presentInView:(UIView *)view; + +@end + +/** + * ------------------------------ + * @name Deprecated notifications + * ------------------------------ + */ +MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification instead."); +MP_EXTERN NSString *const XCDMetadataKeyTitle DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); +MP_EXTERN NSString *const XCDMetadataKeySmallThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); +MP_EXTERN NSString *const XCDMetadataKeyMediumThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); +MP_EXTERN NSString *const XCDMetadataKeyLargeThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m new file mode 100644 index 000000000..ebec6c3c1 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m @@ -0,0 +1,209 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeVideoPlayerViewController.h" + +#import "XCDYouTubeClient.h" + +#import + +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey = @"error"; // documented in -[MPMoviePlayerController initWithContentURL:] + +NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification = @"XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification"; +NSString *const XCDMetadataKeyTitle = @"Title"; +NSString *const XCDMetadataKeySmallThumbnailURL = @"SmallThumbnailURL"; +NSString *const XCDMetadataKeyMediumThumbnailURL = @"MediumThumbnailURL"; +NSString *const XCDMetadataKeyLargeThumbnailURL = @"LargeThumbnailURL"; + +NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification = @"XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification"; +NSString *const XCDYouTubeVideoUserInfoKey = @"Video"; + +@interface XCDYouTubeVideoPlayerViewController () +@property (nonatomic, weak) id videoOperation; +@property (nonatomic, assign, getter = isEmbedded) BOOL embedded; +@end + +@implementation XCDYouTubeVideoPlayerViewController + +/* + * MPMoviePlayerViewController on iOS 7 and earlier + * - (id) init + * `-- [super init] + * + * - (id) initWithContentURL:(NSURL *)contentURL + * |-- [self init] + * `-- [self.moviePlayer setContentURL:contentURL] + * + * MPMoviePlayerViewController on iOS 8 and later + * - (id) init + * `-- [self initWithContentURL:nil] + * + * - (id) initWithContentURL:(NSURL *)contentURL + * |-- [super init] + * `-- [self.moviePlayer setContentURL:contentURL] + */ + +- (instancetype) init +{ + return [self initWithVideoIdentifier:nil]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-designated-initializers" +- (instancetype) initWithContentURL:(NSURL *)contentURL +{ + @throw [NSException exceptionWithName:NSGenericException reason:@"Use the `initWithVideoIdentifier:` method instead." userInfo:nil]; +} // LCOV_EXCL_LINE + +- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier +{ +#if defined(DEBUG) && DEBUG + NSString *callStackSymbols = [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]; + if (([callStackSymbols rangeOfString:@"-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]_block_invoke"].length > 0) || ([callStackSymbols rangeOfString:@"-[XCDYouTubeClient getVideoWithIdentifier:cookies:completionHandler:]_block_invoke"].length > 0)) + { + NSString *reason = @"XCDYouTubeVideoPlayerViewController must not be used in the completion handler of `-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]` or `-[XCDYouTubeClient getVideoWithIdentifier:cookies:completionHandler:]`. Please read the documentation and sample code to properly use XCDYouTubeVideoPlayerViewController."; + @throw [NSException exceptionWithName:NSGenericException reason:reason userInfo:nil]; + } +#endif + + if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 8) + self = [super initWithContentURL:nil]; + else + self = [super init]; // LCOV_EXCL_LINE + + if (!self) + return nil; // LCOV_EXCL_LINE + + // See https://github.com/0xced/XCDYouTubeKit/commit/cadec1c3857d6a302f71b9ce7d1ae48e389e6890 + [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; + + if (videoIdentifier) + self.videoIdentifier = videoIdentifier; + + return self; +} +#pragma clang diagnostic pop + +#pragma mark - Public + +- (NSArray *) preferredVideoQualities +{ + if (!_preferredVideoQualities) + _preferredVideoQualities = @[ XCDYouTubeVideoQualityHTTPLiveStreaming, @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ]; + + return _preferredVideoQualities; +} + +- (void) setVideoIdentifier:(NSString *)videoIdentifier +{ + if ([videoIdentifier isEqual:self.videoIdentifier]) + return; + + _videoIdentifier = [videoIdentifier copy]; + + [self.videoOperation cancel]; + self.videoOperation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { + if (video) + { + NSURL *streamURL = nil; + for (NSNumber *videoQuality in self.preferredVideoQualities) + { + streamURL = video.streamURLs[videoQuality]; + if (streamURL) + { + [self startVideo:video streamURL:streamURL]; + break; + } + } + + if (!streamURL) + { + NSError *noStreamError = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorNoStreamAvailable userInfo:nil]; + [self stopWithError:noStreamError]; + } + } + else + { + [self stopWithError:error]; + } + }]; +} + +- (void) presentInView:(UIView *)view +{ + static const void * const XCDYouTubeVideoPlayerViewControllerKey = &XCDYouTubeVideoPlayerViewControllerKey; + + self.embedded = YES; + + self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded; + self.moviePlayer.view.frame = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height); + self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + if (![view.subviews containsObject:self.moviePlayer.view]) + [view addSubview:self.moviePlayer.view]; + objc_setAssociatedObject(view, XCDYouTubeVideoPlayerViewControllerKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +#pragma mark - Private + +- (void) startVideo:(XCDYouTubeVideo *)video streamURL:(NSURL *)streamURL +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + NSMutableDictionary *userInfo = [NSMutableDictionary new]; + if (video.title) + userInfo[XCDMetadataKeyTitle] = video.title; + if (video.smallThumbnailURL) + userInfo[XCDMetadataKeySmallThumbnailURL] = video.smallThumbnailURL; + if (video.mediumThumbnailURL) + userInfo[XCDMetadataKeyMediumThumbnailURL] = video.mediumThumbnailURL; + if (video.largeThumbnailURL) + userInfo[XCDMetadataKeyLargeThumbnailURL] = video.largeThumbnailURL; + + [[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:self userInfo:userInfo]; +#pragma clang diagnostic pop + + self.moviePlayer.contentURL = streamURL; + + [[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification object:self userInfo:@{ XCDYouTubeVideoUserInfoKey: video }]; +} + +- (void) stopWithError:(NSError *)error +{ + NSDictionary *userInfo = @{ MPMoviePlayerPlaybackDidFinishReasonUserInfoKey: @(MPMovieFinishReasonPlaybackError), + XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey: error }; + [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer userInfo:userInfo]; + + if (self.isEmbedded) + [self.moviePlayer.view removeFromSuperview]; + else + [self.presentingViewController dismissMoviePlayerViewControllerAnimated]; +} + +#pragma mark - UIViewController + +- (void) viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (![self isBeingPresented]) + return; + + self.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; + [self.moviePlayer play]; +} + +- (void) viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + + if (![self isBeingDismissed]) + return; + + [self.videoOperation cancel]; +} + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h new file mode 100644 index 000000000..750cfdc74 --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h @@ -0,0 +1,18 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import + +__attribute__((visibility("hidden"))) +@interface XCDYouTubeVideoWebpage : NSObject + +- (instancetype) initWithHTMLString:(NSString *)html; + +@property (nonatomic, readonly) NSDictionary *playerConfiguration; +@property (nonatomic, readonly) NSDictionary *videoInfo; +@property (nonatomic, readonly) NSURL *javaScriptPlayerURL; +@property (nonatomic, readonly) BOOL isAgeRestricted; +@property (nonatomic, readonly) NSSet *regionsAllowed; + +@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m new file mode 100644 index 000000000..4e56feb2a --- /dev/null +++ b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m @@ -0,0 +1,124 @@ +// +// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. +// + +#import "XCDYouTubeVideoWebpage.h" + +@interface XCDYouTubeVideoWebpage () +@property (nonatomic, readonly) NSString *html; +@end + +@implementation XCDYouTubeVideoWebpage + +@synthesize playerConfiguration = _playerConfiguration; +@synthesize videoInfo = _videoInfo; +@synthesize javaScriptPlayerURL = _javaScriptPlayerURL; +@synthesize isAgeRestricted = _isAgeRestricted; +@synthesize regionsAllowed = _regionsAllowed; + +- (instancetype) initWithHTMLString:(NSString *)html +{ + if (!(self = [super init])) + return nil; // LCOV_EXCL_LINE + + _html = html; + + return self; +} + +- (NSDictionary *) playerConfiguration +{ + if (!_playerConfiguration) + { + __block NSDictionary *playerConfigurationDictionary; + NSRegularExpression *playerConfigRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"ytplayer.config\\s*=\\s*(\\{.*?\\});|[\\({]\\s*'PLAYER_CONFIG'[,:]\\s*(\\{.*?\\})\\s*(?:,'|\\))" options:NSRegularExpressionCaseInsensitive error:NULL]; + [playerConfigRegularExpression enumerateMatchesInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) + { + for (NSUInteger i = 1; i < result.numberOfRanges; i++) + { + NSRange range = [result rangeAtIndex:i]; + if (range.length == 0) + continue; + + NSString *configString = [self.html substringWithRange:range]; + NSData *configData = [configString dataUsingEncoding:NSUTF8StringEncoding]; + NSDictionary *playerConfiguration = [NSJSONSerialization JSONObjectWithData:configData ?: [NSData new] options:(NSJSONReadingOptions)0 error:NULL]; + if ([playerConfiguration isKindOfClass:[NSDictionary class]]) + { + playerConfigurationDictionary = playerConfiguration; + *stop = YES; + } + } + }]; + _playerConfiguration = playerConfigurationDictionary; + } + return _playerConfiguration; +} + +- (NSDictionary *) videoInfo +{ + if (!_videoInfo) + { + NSDictionary *args = self.playerConfiguration[@"args"]; + if ([args isKindOfClass:[NSDictionary class]]) + { + NSMutableDictionary *info = [NSMutableDictionary new]; + [args enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) + { + if ([(NSObject *)value isKindOfClass:[NSString class]] || [(NSObject *)value isKindOfClass:[NSNumber class]]) + info[key] = [(NSObject *)value description]; + }]; + _videoInfo = [info copy]; + } + } + return _videoInfo; +} + +- (NSURL *) javaScriptPlayerURL +{ + if (!_javaScriptPlayerURL) + { + NSString *jsAssets = [self.playerConfiguration valueForKeyPath:@"assets.js"]; + if ([jsAssets isKindOfClass:[NSString class]]) + { + NSString *javaScriptPlayerURLString = jsAssets; + if ([jsAssets hasPrefix:@"//"]) + javaScriptPlayerURLString = [@"https:" stringByAppendingString:jsAssets]; + else if ([jsAssets hasPrefix:@"/"]) + javaScriptPlayerURLString = [@"https://www.youtube.com" stringByAppendingString:jsAssets]; + + _javaScriptPlayerURL = [NSURL URLWithString:javaScriptPlayerURLString]; + } + } + return _javaScriptPlayerURL; +} + +- (BOOL) isAgeRestricted +{ + if (!_isAgeRestricted) + { + NSStringCompareOptions options = (NSStringCompareOptions)0; + NSRange range = NSMakeRange(0, self.html.length); + _isAgeRestricted = [self.html rangeOfString:@"og:restrictions:age" options:options range:range].location != NSNotFound; + } + return _isAgeRestricted; +} + +- (NSSet *) regionsAllowed +{ + if (!_regionsAllowed) + { + _regionsAllowed = [NSSet set]; + NSRegularExpression *regionsAllowedRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"meta\\s+itemprop=\"regionsAllowed\"\\s+content=\"(.*)\"" options:(NSRegularExpressionOptions)0 error:NULL]; + NSTextCheckingResult *regionsAllowedResult = [regionsAllowedRegularExpression firstMatchInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length)]; + if (regionsAllowedResult.numberOfRanges > 1) + { + NSString *regionsAllowed = [self.html substringWithRange:[regionsAllowedResult rangeAtIndex:1]]; + if (regionsAllowed.length > 0) + _regionsAllowed = [NSSet setWithArray:[regionsAllowed componentsSeparatedByString:@","]]; + } + } + return _regionsAllowed; +} + +@end diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/LICENSE b/ios/Pods/YoutubePlayer-in-WKWebView/LICENSE new file mode 100644 index 000000000..bfdf05f50 --- /dev/null +++ b/ios/Pods/YoutubePlayer-in-WKWebView/LICENSE @@ -0,0 +1,13 @@ +Copyright 2014 Google Inc. All rights reserved. + +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. \ No newline at end of file diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/README.md b/ios/Pods/YoutubePlayer-in-WKWebView/README.md new file mode 100644 index 000000000..67b80b8f5 --- /dev/null +++ b/ios/Pods/YoutubePlayer-in-WKWebView/README.md @@ -0,0 +1,59 @@ +# YoutubePlayer-in-WKWebView + +[![YoutubePlayer-in-WKWebView version](https://img.shields.io/cocoapods/v/YoutubePlayer-in-WKWebView.svg?style=plastic)](http://cocoadocs.org/docsets/YoutubePlayer-in-WKWebView) [![YoutubePlayer-in-WKWebView platform](https://img.shields.io/cocoapods/p/YoutubePlayer-in-WKWebView.svg?style=plastic)](http://cocoadocs.org/docsets/YoutubePlayer-in-WKWebView) [![ios 8.0](https://img.shields.io/badge/ios-8.0-blue.svg?style=plastic)](http://cocoadocs.org/docsets/YoutubePlayer-in-WKWebView) + +YoutubePlayer-in-WKWebView is forked from [youtube-ios-player-helper](https://github.com/youtube/youtube-ios-player-helper) For using WKWebView. + +## Changes from [youtube-ios-player-helper](https://github.com/youtube/youtube-ios-player-helper) + +- class prefix changed to WKYT from YT. `YTPlayerView` -> `WKYTPlayerView` +- using WKWebView instead of UIWebView. +- getting properties asynchronously. + +``` + // YTPlayerView + NSTimeInterval duration = self.playerView.duration; + + // WKYTPlayerView + [self.playerView getDuration:^(NSTimeInterval duration, NSError * _Nullable error) { + if (!error) { + float seekToTime = duration * self.slider.value; + } + }]; +``` + +## Usage + +To run the example project; clone the repo, and run `pod install` from the Project directory first. For a simple tutorial see this Google Developers article - [Using the YouTube Helper Library to embed YouTube videos in your iOS application](https://developers.google.com/youtube/v3/guides/ios_youtube_helper). + +## Requirements + +## Installation + +YouTube-Player-iOS-Helper is available through [CocoaPods](http://cocoapods.org), to install +it simply add the following line to your Podfile: + + pod "YoutubePlayer-in-WKWebView", "~> 0.3.0" + +After installing in your project and opening the workspace, to use the library: + + 1. Drag a UIView the desired size of your player onto your Storyboard. + 2. Change the UIView's class in the Identity Inspector tab to WKYTPlayerView + 3. Import "WKYTPlayerView.h" in your ViewController. + 4. Add the following property to your ViewController's header file: +```objc + @property(nonatomic, strong) IBOutlet WKYTPlayerView *playerView; +``` + 5. Load the video into the player in your controller's code with the following code: +```objc + [self.playerView loadWithVideoId:@"M7lc1UVf-VE"]; +``` + 6. Run your code! + +See the sample project for more advanced uses, including passing additional player parameters, custom html and +working with callbacks via WKYTPlayerViewDelegate. + + +## License + +YoutubePlayer-in-WKWebView is available under the Apache 2.0 license. See the LICENSE file for more info. diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html new file mode 100644 index 000000000..975510efa --- /dev/null +++ b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html @@ -0,0 +1,90 @@ + + + + + + + +
+
+
+ + + + diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h new file mode 100644 index 000000000..d532d1d25 --- /dev/null +++ b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h @@ -0,0 +1,734 @@ +// Copyright © 2014 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import + +@class WKYTPlayerView; + +/** These enums represent the state of the current video in the player. */ +typedef NS_ENUM(NSInteger, WKYTPlayerState) { + kWKYTPlayerStateUnstarted, + kWKYTPlayerStateEnded, + kWKYTPlayerStatePlaying, + kWKYTPlayerStatePaused, + kWKYTPlayerStateBuffering, + kWKYTPlayerStateQueued, + kWKYTPlayerStateUnknown +}; + +/** These enums represent the resolution of the currently loaded video. */ +typedef NS_ENUM(NSInteger, WKYTPlaybackQuality) { + kWKYTPlaybackQualitySmall, + kWKYTPlaybackQualityMedium, + kWKYTPlaybackQualityLarge, + kWKYTPlaybackQualityHD720, + kWKYTPlaybackQualityHD1080, + kWKYTPlaybackQualityHighRes, + kWKYTPlaybackQualityAuto, /** Addition for YouTube Live Events. */ + kWKYTPlaybackQualityDefault, + kWKYTPlaybackQualityUnknown /** This should never be returned. It is here for future proofing. */ +}; + +/** These enums represent error codes thrown by the player. */ +typedef NS_ENUM(NSInteger, WKYTPlayerError) { + kWKYTPlayerErrorInvalidParam, + kWKYTPlayerErrorHTML5Error, + kWKYTPlayerErrorVideoNotFound, // Functionally equivalent error codes 100 and + // 105 have been collapsed into |kWKYTPlayerErrorVideoNotFound|. + kWKYTPlayerErrorNotEmbeddable, // Functionally equivalent error codes 101 and + // 150 have been collapsed into |kWKYTPlayerErrorNotEmbeddable|. + kWKYTPlayerErrorUnknown +}; + +/** + * A delegate for ViewControllers to respond to YouTube player events outside + * of the view, such as changes to video playback state or playback errors. + * The callback functions correlate to the events fired by the IFrame API. + * For the full documentation, see the IFrame documentation here: + * https://developers.google.com/youtube/iframe_api_reference#Events + */ +@protocol WKYTPlayerViewDelegate + +@optional +/** + * Invoked when the player view is ready to receive API calls. + * + * @param playerView The WKYTPlayerView instance that has become ready. + */ +- (void)playerViewDidBecomeReady:(nonnull WKYTPlayerView *)playerView; + +/** + * Callback invoked when player state has changed, e.g. stopped or started playback. + * + * @param playerView The WKYTPlayerView instance where playback state has changed. + * @param state WKYTPlayerState designating the new playback state. + */ +- (void)playerView:(nonnull WKYTPlayerView *)playerView didChangeToState:(WKYTPlayerState)state; + +/** + * Callback invoked when playback quality has changed. + * + * @param playerView The WKYTPlayerView instance where playback quality has changed. + * @param quality WKYTPlaybackQuality designating the new playback quality. + */ +- (void)playerView:(nonnull WKYTPlayerView *)playerView didChangeToQuality:(WKYTPlaybackQuality)quality; + +/** + * Callback invoked when an error has occured. + * + * @param playerView The WKYTPlayerView instance where the error has occurred. + * @param error WKYTPlayerError containing the error state. + */ +- (void)playerView:(nonnull WKYTPlayerView *)playerView receivedError:(WKYTPlayerError)error; + +/** + * Callback invoked frequently when playBack is plaing. + * + * @param playerView The WKYTPlayerView instance where the error has occurred. + * @param playTime float containing curretn playback time. + */ +- (void)playerView:(nonnull WKYTPlayerView *)playerView didPlayTime:(float)playTime; + +/** + * Callback invoked when setting up the webview to allow custom colours so it fits in + * with app color schemes. If a transparent view is required specify clearColor and + * the code will handle the opacity etc. + * + * @param playerView The WKYTPlayerView instance where the error has occurred. + * @return A color object that represents the background color of the webview. + */ +- (nonnull UIColor *)playerViewPreferredWebViewBackgroundColor:(nonnull WKYTPlayerView *)playerView; + +/** + * Callback invoked when initially loading the YouTube iframe to the webview to display a custom + * loading view while the player view is not ready. This loading view will be dismissed just before + * -playerViewDidBecomeReady: callback is invoked. The loading view will be automatically resized + * to cover the entire player view. + * + * The default implementation does not display any custom loading views so the player will display + * a blank view with a background color of (-playerViewPreferredWebViewBackgroundColor:). + * + * Note that the custom loading view WILL NOT be displayed after iframe is loaded. It will be + * handled by YouTube iframe API. This callback is just intended to tell users the view is actually + * doing something while iframe is being loaded, which will take some time if users are in poor networks. + * + * @param playerView The WKYTPlayerView instance where the error has occurred. + * @return A view object that will be displayed while YouTube iframe API is being loaded. + * Pass nil to display no custom loading view. Default implementation returns nil. + */ +- (nullable UIView *)playerViewPreferredInitialLoadingView:(nonnull WKYTPlayerView *)playerView; + +/** + * Callback invoked when an api loading error has occured. + * + * @param playerView The WKYTPlayerView instance where the error has occurred. + */ +- (void)playerViewIframeAPIDidFailedToLoad:(nonnull WKYTPlayerView *)playerView; + +@end + +/** + * WKYTPlayerView is a custom UIView that client developers will use to include YouTube + * videos in their iOS applications. It can be instantiated programmatically, or via + * Interface Builder. Use the methods WKYTPlayerView::loadWithVideoId:, + * WKYTPlayerView::loadWithPlaylistId: or their variants to set the video or playlist + * to populate the view with. + */ +@interface WKYTPlayerView : UIView + +@property(nonatomic, strong, nullable, readonly) WKWebView *webView; + +/** A delegate to be notified on playback events. */ +@property(nonatomic, weak, nullable) id delegate; + +/** + * This method loads the player with the given video ID. + * This is a convenience method for calling WKYTPlayerView::loadPlayerWithVideoId:withPlayerVars: + * without player variables. + * + * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire WKWebView, use the + * WKYTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. + * + * @param videoId The YouTube video ID of the video to load in the player view. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithVideoId:(nonnull NSString *)videoId; + +/** + * This method loads the player with the given playlist ID. + * This is a convenience method for calling WKYTPlayerView::loadWithPlaylistId:withPlayerVars: + * without player variables. + * + * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire WKWebView, use the + * WKYTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality: + * family of methods. + * + * @param playlistId The YouTube playlist ID of the playlist to load in the player view. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId; + +/** + * This method loads the player with the given video ID and player variables. Player variables + * specify optional parameters for video playback. For instance, to play a YouTube + * video inline, the following playerVars dictionary would be used: + * + * @code + * @{ @"playsinline" : @1 }; + * @endcode + * + * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), + * both strings and integers are valid values. The full list of parameters is defined at: + * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. + * + * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire WKWebView, use the + * WKYTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. + * + * @param videoId The YouTube video ID of the video to load in the player view. + * @param playerVars An NSDictionary of player parameters. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithVideoId:(nonnull NSString *)videoId playerVars:(nullable NSDictionary *)playerVars; + +/** + * This method loads the player with the given video ID and player variables. Player variables + * specify optional parameters for video playback. For instance, to play a YouTube + * video inline, the following playerVars dictionary would be used: + * + * @code + * @{ @"playsinline" : @1 }; + * @endcode + * + * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), + * both strings and integers are valid values. The full list of parameters is defined at: + * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. + * + * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire WKWebView, use the + * WKYTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. + * + * @param videoId The YouTube video ID of the video to load in the player view. + * @param playerVars An NSDictionary of player parameters. + * @param path String with the path for HTML template used for viewing video. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithVideoId:(nonnull NSString *)videoId playerVars:(nullable NSDictionary *)playerVars templatePath:(nullable NSString *)path; + +/** + * This method loads the player with the given playlist ID and player variables. Player variables + * specify optional parameters for video playback. For instance, to play a YouTube + * video inline, the following playerVars dictionary would be used: + * + * @code + * @{ @"playsinline" : @1 }; + * @endcode + * + * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), + * both strings and integers are valid values. The full list of parameters is defined at: + * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. + * + * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. + * To change the currently loaded video without reloading the entire WKWebView, use the + * WKYTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality: + * family of methods. + * + * @param playlistId The YouTube playlist ID of the playlist to load in the player view. + * @param playerVars An NSDictionary of player parameters. + * @return YES if player has been configured correctly, NO otherwise. + */ +- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId playerVars:(nullable NSDictionary *)playerVars; + +/** + * This method loads an iframe player with the given player parameters. Usually you may want to use + * -loadWithVideoId:playerVars: or -loadWithPlaylistId:playerVars: instead of this method does not handle + * video_id or playlist_id at all. The full list of parameters is defined at: + * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. + * + * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters + * to instantiate the HTML5 player with. This differs depending on + * whether a single video or playlist is being loaded. + * @return YES if successful, NO if not. + */ +- (BOOL)loadWithPlayerParams:(nullable NSDictionary *)additionalPlayerParams; + +#pragma mark - Player controls + +// These methods correspond to their JavaScript equivalents as documented here: +// https://developers.google.com/youtube/iframe_api_reference#Playback_controls + +/** + * Starts or resumes playback on the loaded video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#playVideo + */ +- (void)playVideo; + +/** + * Pauses playback on a playing video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#pauseVideo + */ +- (void)pauseVideo; + +/** + * Stops playback on a playing video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#stopVideo + */ +- (void)stopVideo; + +/** + * Seek to a given time on a playing video. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#seekTo + * + * @param seekToSeconds The time in seconds to seek to in the loaded video. + * @param allowSeekAhead Whether to make a new request to the server if the time is + * outside what is currently buffered. Recommended to set to YES. + */ +- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead; + +#pragma mark - Queuing videos + +// Queueing functions for videos. These methods correspond to their JavaScript +// equivalents as documented here: +// https://developers.google.com/youtube/iframe_api_reference#Queueing_Functions + +/** + * Cues a given video by its video ID for playback starting at the given time and with the + * suggested quality. Cueing loads a video, but does not start video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoById + * + * @param videoId A video ID to cue. + * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Cues a given video by its video ID for playback starting and ending at the given times + * with the suggested quality. Cueing loads a video, but does not start video playback. This + * method corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoById + * + * @param videoId A video ID to cue. + * @param startSeconds Time in seconds to start the video when playVideo() is called. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting at the given time and with the + * suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoById + * + * @param videoId A video ID to load and begin playing. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting and ending at the given times + * with the suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoById + * + * @param videoId A video ID to load and begin playing. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoById:(nonnull NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Cues a given video by its URL on YouTube.com for playback starting at the given time + * and with the suggested quality. Cueing loads a video, but does not start video playback. + * This method corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl + * + * @param videoURL URL of a YouTube video to cue for playback. + * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Cues a given video by its URL on YouTube.com for playback starting at the given time + * and with the suggested quality. Cueing loads a video, but does not start video playback. + * This method corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl + * + * @param videoURL URL of a YouTube video to cue for playback. + * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)cueVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting at the given time + * with the suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl + * + * @param videoURL URL of a YouTube video to load and play. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Loads a given video by its video ID for playback starting and ending at the given times + * with the suggested quality. Loading a video both loads it and begins playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl + * + * @param videoURL URL of a YouTube video to load and play. + * @param startSeconds Time in seconds to start the video when it has loaded. + * @param endSeconds Time in seconds to end the video after it begins playing. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadVideoByURL:(nonnull NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +#pragma mark - Queuing functions for playlists + +// Queueing functions for playlists. These methods correspond to +// the JavaScript methods defined here: +// https://developers.google.com/youtube/js_api_reference#Playlist_Queueing_Functions + +/** + * Cues a given playlist with the given ID. The |index| parameter specifies the 0-indexed + * position of the first video to play, starting at the given time and with the + * suggested quality. Cueing loads a playlist, but does not start video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cuePlaylist + * + * @param playlistId Playlist ID of a YouTube playlist to cue. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)cuePlaylistByPlaylistId:(nonnull NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Cues a playlist of videos with the given video IDs. The |index| parameter specifies the + * 0-indexed position of the first video to play, starting at the given time and with the + * suggested quality. Cueing loads a playlist, but does not start video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#cuePlaylist + * + * @param videoIds An NSArray of video IDs to compose the playlist of. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)cuePlaylistByVideos:(nonnull NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Loads a given playlist with the given ID. The |index| parameter specifies the 0-indexed + * position of the first video to play, starting at the given time and with the + * suggested quality. Loading a playlist starts video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadPlaylist + * + * @param playlistId Playlist ID of a YouTube playlist to cue. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadPlaylistByPlaylistId:(nonnull NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Loads a playlist of videos with the given video IDs. The |index| parameter specifies the + * 0-indexed position of the first video to play, starting at the given time and with the + * suggested quality. Loading a playlist starts video playback. This method + * corresponds with its JavaScript API equivalent as documented here: + * https://developers.google.com/youtube/iframe_api_reference#loadPlaylist + * + * @param videoIds An NSArray of video IDs to compose the playlist of. + * @param index A 0-indexed position specifying the first video to play. + * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. + * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. + */ +- (void)loadPlaylistByVideos:(nonnull NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; + +#pragma mark - Playing a video in a playlist + +// These methods correspond to the JavaScript API as defined under the +// "Playing a video in a playlist" section here: +// https://developers.google.com/youtube/iframe_api_reference#Playback_status + +/** + * Loads and plays the next video in the playlist. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#nextVideo + */ +- (void)nextVideo; + +/** + * Loads and plays the previous video in the playlist. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#previousVideo + */ +- (void)previousVideo; + +/** + * Loads and plays the video at the given 0-indexed position in the playlist. + * Corresponds to this method from the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#playVideoAt + * + * @param index The 0-indexed position of the video in the playlist to load and play. + */ +- (void)playVideoAt:(int)index; + +#pragma mark - Setting the playback rate + +/** + * Gets the playback rate. The default value is 1.0, which represents a video + * playing at normal speed. Other values may include 0.25 or 0.5 for slower + * speeds, and 1.5 or 2.0 for faster speeds. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate + */ +- (void)getPlaybackRate:(void (^ __nullable)(float playbackRate, NSError * __nullable error))completionHandler; + +/** + * Sets the playback rate. The default value is 1.0, which represents a video + * playing at normal speed. Other values may include 0.25 or 0.5 for slower + * speeds, and 1.5 or 2.0 for faster speeds. To fetch a list of valid values for + * this method, call WKYTPlayerView::getAvailablePlaybackRates. This method does not + * guarantee that the playback rate will change. + * This method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate + * + * @param suggestedRate A playback rate to suggest for the player. + */ +- (void)setPlaybackRate:(float)suggestedRate; + +/** + * Gets a list of the valid playback rates, useful in conjunction with + * WKYTPlayerView::setPlaybackRate. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate + */ +- (void)getAvailablePlaybackRates:(void (^ __nullable)(NSArray * __nullable availablePlaybackRates, NSError * __nullable error))completionHandler; + +#pragma mark - Setting playback behavior for playlists + +/** + * Sets whether the player should loop back to the first video in the playlist + * after it has finished playing the last video. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#loopPlaylist + * + * @param loop A boolean representing whether the player should loop. + */ +- (void)setLoop:(BOOL)loop; + +/** + * Sets whether the player should shuffle through the playlist. This method + * corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#shufflePlaylist + * + * @param shuffle A boolean representing whether the player should + * shuffle through the playlist. + */ +- (void)setShuffle:(BOOL)shuffle; + +#pragma mark - Playback status +// These methods correspond to the JavaScript methods defined here: +// https://developers.google.com/youtube/js_api_reference#Playback_status + +/** + * Returns a number between 0 and 1 that specifies the percentage of the video + * that the player shows as buffered. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getVideoLoadedFraction + * + */ +- (void)getVideoLoadedFraction:(void (^ __nullable)(float videoLoadedFraction, NSError * __nullable error))completionHandler; + +/** + * Returns the state of the player. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlayerState + * + */ +- (void)getPlayerState:(void (^ __nullable)(WKYTPlayerState playerState, NSError * __nullable error))completionHandler; + +/** + * Returns the elapsed time in seconds since the video started playing. This + * method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getCurrentTime + * + */ +- (void)getCurrentTime:(void (^ __nullable)(float currentTime, NSError * __nullable error))completionHandler; + +#pragma mark - Playback quality + +// Playback quality. These methods correspond to the JavaScript +// methods defined here: +// https://developers.google.com/youtube/js_api_reference#Playback_quality + +/** + * Returns the playback quality. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality + * + */ +- (void)getPlaybackQuality:(void (^ __nullable)(WKYTPlaybackQuality playbackQuality, NSError * __nullable error))completionHandler; + +/** + * Suggests playback quality for the video. It is recommended to leave this setting to + * |default|. This method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#setPlaybackQuality + * + */ +- (void)setPlaybackQuality:(WKYTPlaybackQuality)suggestedQuality; + +/** + * Gets a list of the valid playback quality values, useful in conjunction with + * WKYTPlayerView::setPlaybackQuality. This method corresponds to the + * JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels + * + */ +- (void)getAvailableQualityLevels:(void (^ __nullable)(NSArray * __nullable availableQualityLevels, NSError * __nullable error))completionHandler; + +#pragma mark - Retrieving video information + +// Retrieving video information. These methods correspond to the JavaScript +// methods defined here: +// https://developers.google.com/youtube/js_api_reference#Retrieving_video_information + +/** + * Returns the duration in seconds since the video of the video. This + * method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getDuration + * + */ +- (void)getDuration:(void (^ __nullable)(NSTimeInterval duration, NSError * __nullable error))completionHandler; + +/** + * Returns the YouTube.com URL for the video. This method corresponds + * to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getVideoUrl + * + */ +- (void)getVideoUrl:(void (^ __nullable)(NSURL * __nullable videoUrl, NSError * __nullable error))completionHandler; + +/** + * Returns the embed code for the current video. This method corresponds + * to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getVideoEmbedCode + * + */ +- (void)getVideoEmbedCode:(void (^ __nullable)(NSString * __nullable videoEmbedCode, NSError * __nullable error))completionHandler; + +#pragma mark - Retrieving playlist information + +// Retrieving playlist information. These methods correspond to the +// JavaScript defined here: +// https://developers.google.com/youtube/js_api_reference#Retrieving_playlist_information + +/** + * Returns an ordered array of video IDs in the playlist. This method corresponds + * to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaylist + * + */ +- (void)getPlaylist:(void (^ __nullable)(NSArray * __nullable playlist, NSError * __nullable error))completionHandler; + +/** + * Returns the 0-based index of the currently playing item in the playlist. + * This method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getPlaylistIndex + * + */ +- (void)getPlaylistIndex:(void (^ __nullable)(int playlistIndex, NSError * __nullable error))completionHandler; + +#pragma mark - Mute + +/** + * Mutes the player. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#mute + */ +- (void)mute; + +/** + * Unmutes the player. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#unMute + */ +- (void)unMute; + +/** + * Returns true if the player is muted, false if not. + * This method corresponds to the JavaScript API defined here: + * https://developers.google.com/youtube/iframe_api_reference#getVolume + * + */ +- (void)isMuted:(void (^ __nullable)(BOOL isMuted, NSError * __nullable error))completionHandler; + + +#pragma mark - Exposed for Testing + +/** + * Removes the internal web view from this player view. + * Intended to use for testing, should not be used in production code. + */ +- (void)removeWebView; + +@end diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m new file mode 100644 index 000000000..d30b8a9f6 --- /dev/null +++ b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m @@ -0,0 +1,1123 @@ +// Copyright © 2014 Google Inc. All rights reserved. +// +// 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 "WKYTPlayerView.h" + +// These are instances of NSString because we get them from parsing a URL. It would be silly to +// convert these into an integer just to have to convert the URL query string value into an integer +// as well for the sake of doing a value comparison. A full list of response error codes can be +// found here: +// https://developers.google.com/youtube/iframe_api_reference +NSString static *const kWKYTPlayerStateUnstartedCode = @"-1"; +NSString static *const kWKYTPlayerStateEndedCode = @"0"; +NSString static *const kWKYTPlayerStatePlayingCode = @"1"; +NSString static *const kWKYTPlayerStatePausedCode = @"2"; +NSString static *const kWKYTPlayerStateBufferingCode = @"3"; +NSString static *const kWKYTPlayerStateCuedCode = @"5"; +NSString static *const kWKYTPlayerStateUnknownCode = @"unknown"; + +// Constants representing playback quality. +NSString static *const kWKYTPlaybackQualitySmallQuality = @"small"; +NSString static *const kWKYTPlaybackQualityMediumQuality = @"medium"; +NSString static *const kWKYTPlaybackQualityLargeQuality = @"large"; +NSString static *const kWKYTPlaybackQualityHD720Quality = @"hd720"; +NSString static *const kWKYTPlaybackQualityHD1080Quality = @"hd1080"; +NSString static *const kWKYTPlaybackQualityHighResQuality = @"highres"; +NSString static *const kWKYTPlaybackQualityAutoQuality = @"auto"; +NSString static *const kWKYTPlaybackQualityDefaultQuality = @"default"; +NSString static *const kWKYTPlaybackQualityUnknownQuality = @"unknown"; + +// Constants representing YouTube player errors. +NSString static *const kWKYTPlayerErrorInvalidParamErrorCode = @"2"; +NSString static *const kWKYTPlayerErrorHTML5ErrorCode = @"5"; +NSString static *const kWKYTPlayerErrorVideoNotFoundErrorCode = @"100"; +NSString static *const kWKYTPlayerErrorNotEmbeddableErrorCode = @"101"; +NSString static *const kWKYTPlayerErrorCannotFindVideoErrorCode = @"105"; +NSString static *const kWKYTPlayerErrorSameAsNotEmbeddableErrorCode = @"150"; + +// Constants representing player callbacks. +NSString static *const kWKYTPlayerCallbackOnReady = @"onReady"; +NSString static *const kWKYTPlayerCallbackOnStateChange = @"onStateChange"; +NSString static *const kWKYTPlayerCallbackOnPlaybackQualityChange = @"onPlaybackQualityChange"; +NSString static *const kWKYTPlayerCallbackOnError = @"onError"; +NSString static *const kWKYTPlayerCallbackOnPlayTime = @"onPlayTime"; + +NSString static *const kWKYTPlayerCallbackOnYouTubeIframeAPIReady = @"onYouTubeIframeAPIReady"; +NSString static *const kWKYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad = @"onYouTubeIframeAPIFailedToLoad"; + +NSString static *const kWKYTPlayerEmbedUrlRegexPattern = @"^http(s)://(www.)youtube.com/embed/(.*)$"; +NSString static *const kWKYTPlayerAdUrlRegexPattern = @"^http(s)://pubads.g.doubleclick.net/pagead/conversion/"; +NSString static *const kWKYTPlayerOAuthRegexPattern = @"^http(s)://accounts.google.com/o/oauth2/(.*)$"; +NSString static *const kWKYTPlayerStaticProxyRegexPattern = @"^https://content.googleapis.com/static/proxy.html(.*)$"; +NSString static *const kWKYTPlayerSyndicationRegexPattern = @"^https://tpc.googlesyndication.com/sodar/(.*).html$"; + +@interface WKYTPlayerView() + +@property (nonatomic, strong) NSURL *originURL; +@property (nonatomic, weak) UIView *initialLoadingView; + +@end + +@implementation WKYTPlayerView + +- (BOOL)loadWithVideoId:(NSString *)videoId { + return [self loadWithVideoId:videoId playerVars:nil]; +} + +- (BOOL)loadWithPlaylistId:(NSString *)playlistId { + return [self loadWithPlaylistId:playlistId playerVars:nil]; +} + +- (BOOL)loadWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars { + if (!playerVars) { + playerVars = @{}; + } + NSDictionary *playerParams = @{ @"videoId" : videoId, @"playerVars" : playerVars }; + return [self loadWithPlayerParams:playerParams]; +} + +- (BOOL)loadWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars templatePath:(NSString *)path { + if (!playerVars) { + playerVars = @{}; + } + NSMutableDictionary *playerParams = [@{@"videoId" : videoId, @"playerVars" : playerVars} mutableCopy]; + if (path) { + playerParams[@"templatePath"] = path; + } + return [self loadWithPlayerParams:[playerParams copy]]; +} + +- (BOOL)loadWithPlaylistId:(NSString *)playlistId playerVars:(NSDictionary *)playerVars { + + // Mutable copy because we may have been passed an immutable config dictionary. + NSMutableDictionary *tempPlayerVars = [[NSMutableDictionary alloc] init]; + [tempPlayerVars setValue:@"playlist" forKey:@"listType"]; + [tempPlayerVars setValue:playlistId forKey:@"list"]; + if (playerVars) { + [tempPlayerVars addEntriesFromDictionary:playerVars]; + } + + NSDictionary *playerParams = @{ @"playerVars" : tempPlayerVars }; + return [self loadWithPlayerParams:playerParams]; +} + +#pragma mark - Player methods + +- (void)playVideo { + [self stringFromEvaluatingJavaScript:@"player.playVideo();" completionHandler:nil]; +} + +- (void)pauseVideo { + [self notifyDelegateOfYouTubeCallbackUrl:[NSURL URLWithString:[NSString stringWithFormat:@"ytplayer://onStateChange?data=%@", kWKYTPlayerStatePausedCode]]]; + [self stringFromEvaluatingJavaScript:@"player.pauseVideo();" completionHandler:nil]; +} + +- (void)stopVideo { + [self stringFromEvaluatingJavaScript:@"player.stopVideo();" completionHandler:nil]; +} + +- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead { + NSNumber *secondsValue = [NSNumber numberWithFloat:seekToSeconds]; + NSString *allowSeekAheadValue = [self stringForJSBoolean:allowSeekAhead]; + NSString *command = [NSString stringWithFormat:@"player.seekTo(%@, %@);", secondsValue, allowSeekAheadValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +#pragma mark - Cueing methods + +- (void)cueVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoById('%@', %@, '%@');", + videoId, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)cueVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});", videoId, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)loadVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoById('%@', %@, '%@');", + videoId, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)loadVideoById:(NSString *)videoId + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});",videoId, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)cueVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, '%@');", + videoURL, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)cueVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, %@, '%@');", + videoURL, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)loadVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, '%@');", + videoURL, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)loadVideoByURL:(NSString *)videoURL + startSeconds:(float)startSeconds + endSeconds:(float)endSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, %@, '%@');", + videoURL, startSecondsValue, endSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +#pragma mark - Cueing methods for lists + +- (void)cuePlaylistByPlaylistId:(NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId]; + [self cuePlaylist:playlistIdString + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +- (void)cuePlaylistByVideos:(NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + [self cuePlaylist:[self stringFromVideoIdArray:videoIds] + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +- (void)loadPlaylistByPlaylistId:(NSString *)playlistId + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId]; + [self loadPlaylist:playlistIdString + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +- (void)loadPlaylistByVideos:(NSArray *)videoIds + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + [self loadPlaylist:[self stringFromVideoIdArray:videoIds] + index:index + startSeconds:startSeconds + suggestedQuality:suggestedQuality]; +} + +#pragma mark - Setting the playback rate + +- (void)getPlaybackRate:(void (^ __nullable)(float playbackRate, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getPlaybackRate();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(0, error); + } else { + completionHandler([response floatValue], nil); + } + } + }]; +} + +- (void)setPlaybackRate:(float)suggestedRate { + NSString *command = [NSString stringWithFormat:@"player.setPlaybackRate(%f);", suggestedRate]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)getAvailablePlaybackRates:(void (^ __nullable)(NSArray * __nullable availablePlaybackRates, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getAvailablePlaybackRates();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(nil, error); + } else { + NSData *playbackRateData = [response dataUsingEncoding:NSUTF8StringEncoding]; + NSError *jsonDeserializationError; + NSArray *playbackRates = [NSJSONSerialization JSONObjectWithData:playbackRateData + options:kNilOptions + error:&jsonDeserializationError]; + if (jsonDeserializationError) { + completionHandler(nil, jsonDeserializationError); + } + + completionHandler(playbackRates, nil); + } + } + }]; +} + +#pragma mark - Setting playback behavior for playlists + +- (void)setLoop:(BOOL)loop { + NSString *loopPlayListValue = [self stringForJSBoolean:loop]; + NSString *command = [NSString stringWithFormat:@"player.setLoop(%@);", loopPlayListValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +- (void)setShuffle:(BOOL)shuffle { + NSString *shufflePlayListValue = [self stringForJSBoolean:shuffle]; + NSString *command = [NSString stringWithFormat:@"player.setShuffle(%@);", shufflePlayListValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +#pragma mark - Playback status + +- (void)getVideoLoadedFraction:(void (^ __nullable)(float videoLoadedFraction, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getVideoLoadedFraction();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(0, error); + } else { + completionHandler([response floatValue], nil); + } + } + }]; +} + +- (void)getPlayerState:(void (^ __nullable)(WKYTPlayerState playerState, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getPlayerState();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(kWKYTPlayerStateUnknown, error); + } else { + if ([response isKindOfClass: [NSNumber class]]) { + NSNumber *value = (NSNumber *)response; + response = [value stringValue]; + } + completionHandler([WKYTPlayerView playerStateForString:response], nil); + } + } + }]; +} + +- (void)getCurrentTime:(void (^ __nullable)(float currentTime, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getCurrentTime();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(0, error); + } else { + completionHandler([response floatValue], nil); + } + } + }]; +} + +// Playback quality +- (void)getPlaybackQuality:(void (^ __nullable)(WKYTPlaybackQuality playbackQuality, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getPlaybackQuality();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(kWKYTPlaybackQualityUnknown, error); + } else { + completionHandler([WKYTPlayerView playbackQualityForString:response], nil); + } + } + }]; +} + +- (void)setPlaybackQuality:(WKYTPlaybackQuality)suggestedQuality { + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.setPlaybackQuality('%@');", qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +#pragma mark - Video information methods + +- (void)getDuration:(void (^ __nullable)(NSTimeInterval duration, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getDuration();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(0, error); + } else { + if (response != (id) [NSNull null]) { + completionHandler([response doubleValue], nil); + } + else { + completionHandler(0, nil); + } + } + } + }]; +} + +- (void)getVideoUrl:(void (^ __nullable)(NSURL * __nullable videoUrl, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getVideoUrl();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(nil, error); + } else { + completionHandler([NSURL URLWithString:response], nil); + } + } + }]; +} + +- (void)getVideoEmbedCode:(void (^ __nullable)(NSString * __nullable videoEmbedCode, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getVideoEmbedCode();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(nil, error); + } else { + completionHandler(response, nil); + } + } + }]; +} + +#pragma mark - Playlist methods + +- (void)getPlaylist:(void (^ __nullable)(NSArray * __nullable playlist, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getPlaylist();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(nil, error); + } else { + + if ([response isKindOfClass:[NSNull class]]) { + completionHandler(nil, nil); + return; + } + + NSArray *videoIds; + + if ([response isKindOfClass:[NSArray class]]) + { + videoIds = (NSArray *)response; + } + else + { + NSData *playlistData = [response dataUsingEncoding:NSUTF8StringEncoding]; + NSError *jsonDeserializationError; + videoIds = [NSJSONSerialization JSONObjectWithData:playlistData + options:kNilOptions + error:&jsonDeserializationError]; + if (jsonDeserializationError) { + completionHandler(nil, jsonDeserializationError); + } + } + + completionHandler(videoIds, nil); + } + } + }]; +} + +- (void)getPlaylistIndex:(void (^ __nullable)(int playlistIndex, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getPlaylistIndex();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(0, error); + } else { + completionHandler([response intValue], nil); + } + } + }]; +} + +#pragma mark - Playing a video in a playlist + +- (void)nextVideo { + [self stringFromEvaluatingJavaScript:@"player.nextVideo();" completionHandler:nil]; +} + +- (void)previousVideo { + [self stringFromEvaluatingJavaScript:@"player.previousVideo();" completionHandler:nil]; +} + +- (void)playVideoAt:(int)index { + NSString *command = + [NSString stringWithFormat:@"player.playVideoAt(%@);", [NSNumber numberWithInt:index]]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + + +#pragma mark - Changing the player volume + +/** + * Mutes the player. Corresponds to this method from + * the JavaScript API: + * https://developers.google.com/youtube/iframe_api_reference#mute + */ + +- (void)mute +{ + [self stringFromEvaluatingJavaScript:@"player.mute();" completionHandler:nil]; +} + +- (void)unMute +{ + [self stringFromEvaluatingJavaScript:@"player.unMute();" completionHandler:nil]; +} + +- (void)isMuted:(void (^ __nullable)(BOOL isMuted, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.isMuted();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(0, error); + } else { + completionHandler([response boolValue], nil); + } + } + }]; +} + + +#pragma mark - Helper methods + +- (void)getAvailableQualityLevels:(void (^ __nullable)(NSArray * __nullable availableQualityLevels, NSError * __nullable error))completionHandler +{ + [self stringFromEvaluatingJavaScript:@"player.getAvailableQualityLevels().toString();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + if (error) { + completionHandler(nil, error); + } else { + NSArray *rawQualityValues = [response componentsSeparatedByString:@","]; + NSMutableArray *levels = [[NSMutableArray alloc] init]; + for (NSString *rawQualityValue in rawQualityValues) { + WKYTPlaybackQuality quality = [WKYTPlayerView playbackQualityForString:rawQualityValue]; + [levels addObject:[NSNumber numberWithInt:quality]]; + } + + completionHandler(levels, nil); + } + } + }]; +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler +{ + NSURLRequest *request = navigationAction.request; + + if ([request.URL.host isEqual: self.originURL.host]) { + decisionHandler(WKNavigationActionPolicyAllow); + return; + } else if ([request.URL.scheme isEqual:@"ytplayer"]) { + [self notifyDelegateOfYouTubeCallbackUrl:request.URL]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } else if ([request.URL.scheme isEqual: @"http"] || [request.URL.scheme isEqual:@"https"]) { + if([self handleHttpNavigationToUrl:request.URL]) { + decisionHandler(WKNavigationActionPolicyAllow); + } else { + decisionHandler(WKNavigationActionPolicyCancel); + } + return; + } + + decisionHandler(WKNavigationActionPolicyAllow); +} + +- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error +{ + if (self.initialLoadingView) { + [self.initialLoadingView removeFromSuperview]; + } +} + +- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { + if (!navigationAction.targetFrame.isMainFrame) { + // open link with target="_blank" in the same webView + [webView loadRequest:navigationAction.request]; + } + return nil; +} + +/** + * Convert a quality value from NSString to the typed enum value. + * + * @param qualityString A string representing playback quality. Ex: "small", "medium", "hd1080". + * @return An enum value representing the playback quality. + */ ++ (WKYTPlaybackQuality)playbackQualityForString:(NSString *)qualityString { + WKYTPlaybackQuality quality = kWKYTPlaybackQualityUnknown; + + if ([qualityString isEqualToString:kWKYTPlaybackQualitySmallQuality]) { + quality = kWKYTPlaybackQualitySmall; + } else if ([qualityString isEqualToString:kWKYTPlaybackQualityMediumQuality]) { + quality = kWKYTPlaybackQualityMedium; + } else if ([qualityString isEqualToString:kWKYTPlaybackQualityLargeQuality]) { + quality = kWKYTPlaybackQualityLarge; + } else if ([qualityString isEqualToString:kWKYTPlaybackQualityHD720Quality]) { + quality = kWKYTPlaybackQualityHD720; + } else if ([qualityString isEqualToString:kWKYTPlaybackQualityHD1080Quality]) { + quality = kWKYTPlaybackQualityHD1080; + } else if ([qualityString isEqualToString:kWKYTPlaybackQualityHighResQuality]) { + quality = kWKYTPlaybackQualityHighRes; + } else if ([qualityString isEqualToString:kWKYTPlaybackQualityAutoQuality]) { + quality = kWKYTPlaybackQualityAuto; + } + + return quality; +} + +/** + * Convert a |WKYTPlaybackQuality| value from the typed value to NSString. + * + * @param quality A |WKYTPlaybackQuality| parameter. + * @return An |NSString| value to be used in the JavaScript bridge. + */ ++ (NSString *)stringForPlaybackQuality:(WKYTPlaybackQuality)quality { + switch (quality) { + case kWKYTPlaybackQualitySmall: + return kWKYTPlaybackQualitySmallQuality; + case kWKYTPlaybackQualityMedium: + return kWKYTPlaybackQualityMediumQuality; + case kWKYTPlaybackQualityLarge: + return kWKYTPlaybackQualityLargeQuality; + case kWKYTPlaybackQualityHD720: + return kWKYTPlaybackQualityHD720Quality; + case kWKYTPlaybackQualityHD1080: + return kWKYTPlaybackQualityHD1080Quality; + case kWKYTPlaybackQualityHighRes: + return kWKYTPlaybackQualityHighResQuality; + case kWKYTPlaybackQualityAuto: + return kWKYTPlaybackQualityAutoQuality; + default: + return kWKYTPlaybackQualityUnknownQuality; + } +} + +/** + * Convert a state value from NSString to the typed enum value. + * + * @param stateString A string representing player state. Ex: "-1", "0", "1". + * @return An enum value representing the player state. + */ ++ (WKYTPlayerState)playerStateForString:(NSString *)stateString { + WKYTPlayerState state = kWKYTPlayerStateUnknown; + if ([stateString isEqualToString:kWKYTPlayerStateUnstartedCode]) { + state = kWKYTPlayerStateUnstarted; + } else if ([stateString isEqualToString:kWKYTPlayerStateEndedCode]) { + state = kWKYTPlayerStateEnded; + } else if ([stateString isEqualToString:kWKYTPlayerStatePlayingCode]) { + state = kWKYTPlayerStatePlaying; + } else if ([stateString isEqualToString:kWKYTPlayerStatePausedCode]) { + state = kWKYTPlayerStatePaused; + } else if ([stateString isEqualToString:kWKYTPlayerStateBufferingCode]) { + state = kWKYTPlayerStateBuffering; + } else if ([stateString isEqualToString:kWKYTPlayerStateCuedCode]) { + state = kWKYTPlayerStateQueued; + } + return state; +} + +/** + * Convert a state value from the typed value to NSString. + * + * @param state A |WKYTPlayerState| parameter. + * @return A string value to be used in the JavaScript bridge. + */ ++ (NSString *)stringForPlayerState:(WKYTPlayerState)state { + switch (state) { + case kWKYTPlayerStateUnstarted: + return kWKYTPlayerStateUnstartedCode; + case kWKYTPlayerStateEnded: + return kWKYTPlayerStateEndedCode; + case kWKYTPlayerStatePlaying: + return kWKYTPlayerStatePlayingCode; + case kWKYTPlayerStatePaused: + return kWKYTPlayerStatePausedCode; + case kWKYTPlayerStateBuffering: + return kWKYTPlayerStateBufferingCode; + case kWKYTPlayerStateQueued: + return kWKYTPlayerStateCuedCode; + default: + return kWKYTPlayerStateUnknownCode; + } +} + +#pragma mark - Private methods + +/** + * Private method to handle "navigation" to a callback URL of the format + * ytplayer://action?data=someData + * This is how the WKWebView communicates with the containing Objective-C code. + * Side effects of this method are that it calls methods on this class's delegate. + * + * @param url A URL of the format ytplayer://action?data=value. + */ +- (void)notifyDelegateOfYouTubeCallbackUrl: (NSURL *) url { + NSString *action = url.host; + + // We know the query can only be of the format ytplayer://action?data=SOMEVALUE, + // so we parse out the value. + NSString *query = url.query; + NSString *data; + if (query) { + data = [query componentsSeparatedByString:@"="][1]; + } + + if ([action isEqual:kWKYTPlayerCallbackOnReady]) { + if (self.initialLoadingView) { + [self.initialLoadingView removeFromSuperview]; + } + if ([self.delegate respondsToSelector:@selector(playerViewDidBecomeReady:)]) { + [self.delegate playerViewDidBecomeReady:self]; + } + } else if ([action isEqual:kWKYTPlayerCallbackOnStateChange]) { + if ([self.delegate respondsToSelector:@selector(playerView:didChangeToState:)]) { + WKYTPlayerState state = kWKYTPlayerStateUnknown; + + if ([data isEqual:kWKYTPlayerStateEndedCode]) { + state = kWKYTPlayerStateEnded; + } else if ([data isEqual:kWKYTPlayerStatePlayingCode]) { + state = kWKYTPlayerStatePlaying; + } else if ([data isEqual:kWKYTPlayerStatePausedCode]) { + state = kWKYTPlayerStatePaused; + } else if ([data isEqual:kWKYTPlayerStateBufferingCode]) { + state = kWKYTPlayerStateBuffering; + } else if ([data isEqual:kWKYTPlayerStateCuedCode]) { + state = kWKYTPlayerStateQueued; + } else if ([data isEqual:kWKYTPlayerStateUnstartedCode]) { + state = kWKYTPlayerStateUnstarted; + } + + [self.delegate playerView:self didChangeToState:state]; + } + } else if ([action isEqual:kWKYTPlayerCallbackOnPlaybackQualityChange]) { + if ([self.delegate respondsToSelector:@selector(playerView:didChangeToQuality:)]) { + WKYTPlaybackQuality quality = [WKYTPlayerView playbackQualityForString:data]; + [self.delegate playerView:self didChangeToQuality:quality]; + } + } else if ([action isEqual:kWKYTPlayerCallbackOnError]) { + if ([self.delegate respondsToSelector:@selector(playerView:receivedError:)]) { + WKYTPlayerError error = kWKYTPlayerErrorUnknown; + + if ([data isEqual:kWKYTPlayerErrorInvalidParamErrorCode]) { + error = kWKYTPlayerErrorInvalidParam; + } else if ([data isEqual:kWKYTPlayerErrorHTML5ErrorCode]) { + error = kWKYTPlayerErrorHTML5Error; + } else if ([data isEqual:kWKYTPlayerErrorNotEmbeddableErrorCode] || + [data isEqual:kWKYTPlayerErrorSameAsNotEmbeddableErrorCode]) { + error = kWKYTPlayerErrorNotEmbeddable; + } else if ([data isEqual:kWKYTPlayerErrorVideoNotFoundErrorCode] || + [data isEqual:kWKYTPlayerErrorCannotFindVideoErrorCode]) { + error = kWKYTPlayerErrorVideoNotFound; + } + + [self.delegate playerView:self receivedError:error]; + } + } else if ([action isEqualToString:kWKYTPlayerCallbackOnPlayTime]) { + if ([self.delegate respondsToSelector:@selector(playerView:didPlayTime:)]) { + float time = [data floatValue]; + [self.delegate playerView:self didPlayTime:time]; + } + } else if ([action isEqualToString:kWKYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad]) { + if (self.initialLoadingView) { + [self.initialLoadingView removeFromSuperview]; + } + + if ([self.delegate respondsToSelector:@selector(playerViewIframeAPIDidFailedToLoad:)]) { + [self.delegate playerViewIframeAPIDidFailedToLoad:self]; + } + } +} + +- (BOOL)handleHttpNavigationToUrl:(NSURL *) url { + // Usually this means the user has clicked on the YouTube logo or an error message in the + // player. Most URLs should open in the browser. The only http(s) URL that should open in this + // WKWebView is the URL for the embed, which is of the format: + // http(s)://www.youtube.com/embed/[VIDEO ID]?[PARAMETERS] + NSError *error = NULL; + NSRegularExpression *ytRegex = + [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerEmbedUrlRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *ytMatch = + [ytRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *adRegex = + [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerAdUrlRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *adMatch = + [adRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *syndicationRegex = + [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerSyndicationRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + + NSTextCheckingResult *syndicationMatch = + [syndicationRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *oauthRegex = + [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerOAuthRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *oauthMatch = + [oauthRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + NSRegularExpression *staticProxyRegex = + [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerStaticProxyRegexPattern + options:NSRegularExpressionCaseInsensitive + error:&error]; + NSTextCheckingResult *staticProxyMatch = + [staticProxyRegex firstMatchInString:url.absoluteString + options:0 + range:NSMakeRange(0, [url.absoluteString length])]; + + if (ytMatch || adMatch || oauthMatch || staticProxyMatch || syndicationMatch) { + return YES; + } else { + [[UIApplication sharedApplication] openURL:url]; + return NO; + } +} + + +/** + * Private helper method to load an iframe player with the given player parameters. + * + * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters + * to instantiate the HTML5 player with. This differs depending on + * whether a single video or playlist is being loaded. + * @return YES if successful, NO if not. + */ +- (BOOL)loadWithPlayerParams:(NSDictionary *)additionalPlayerParams { + NSDictionary *playerCallbacks = @{ + @"onReady" : @"onReady", + @"onStateChange" : @"onStateChange", + @"onPlaybackQualityChange" : @"onPlaybackQualityChange", + @"onError" : @"onPlayerError" + }; + NSMutableDictionary *playerParams = [[NSMutableDictionary alloc] init]; + if (additionalPlayerParams) { + [playerParams addEntriesFromDictionary:additionalPlayerParams]; + } + if (![playerParams objectForKey:@"height"]) { + [playerParams setValue:@"100%" forKey:@"height"]; + } + if (![playerParams objectForKey:@"width"]) { + [playerParams setValue:@"100%" forKey:@"width"]; + } + + [playerParams setValue:playerCallbacks forKey:@"events"]; + + if ([playerParams objectForKey:@"playerVars"]) { + NSMutableDictionary *playerVars = [[NSMutableDictionary alloc] init]; + [playerVars addEntriesFromDictionary:[playerParams objectForKey:@"playerVars"]]; + + if (![playerVars objectForKey:@"origin"]) { + self.originURL = [NSURL URLWithString:@"about:blank"]; + } else { + self.originURL = [NSURL URLWithString: [playerVars objectForKey:@"origin"]]; + } + } else { + // This must not be empty so we can render a '{}' in the output JSON + [playerParams setValue:[[NSDictionary alloc] init] forKey:@"playerVars"]; + } + + // Remove the existing webView to reset any state + [self.webView removeFromSuperview]; + _webView = [self createNewWebView]; + [self addSubview:self.webView]; + + self.webView.translatesAutoresizingMaskIntoConstraints = NO; + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.webView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.webView + attribute:NSLayoutAttributeLeft + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeLeft + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:self.webView + attribute:NSLayoutAttributeRight + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeRight + multiplier:1.0 + constant:0.0]; + NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.webView + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:0.0]; + NSArray *constraints = @[topConstraint, leftConstraint, rightConstraint, bottomConstraint]; + [self addConstraints:constraints]; + + NSError *error = nil; + NSString *path = [additionalPlayerParams objectForKey:@"templatePath"]; + + //in case path to the HTML template wan't provided from the outside + if (!path) { + path = [[NSBundle bundleForClass:[WKYTPlayerView class]] pathForResource:@"YTPlayerView-iframe-player" + ofType:@"html" + inDirectory:@"Assets"]; + } + + // in case of using Swift and embedded frameworks, resources included not in main bundle, + // but in framework bundle + if (!path) { + path = [[[self class] frameworkBundle] pathForResource:@"YTPlayerView-iframe-player" + ofType:@"html" + inDirectory:@"Assets"]; + } + + NSString *embedHTMLTemplate = + [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; + + if (error) { + NSLog(@"Received error rendering template: %@", error); + return NO; + } + + // Render the playerVars as a JSON dictionary. + NSError *jsonRenderingError = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:playerParams + options:NSJSONWritingPrettyPrinted + error:&jsonRenderingError]; + if (jsonRenderingError) { + NSLog(@"Attempted configuration of player with invalid playerVars: %@ \tError: %@", + playerParams, + jsonRenderingError); + return NO; + } + + NSString *playerVarsJsonString = + [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + + NSString *embedHTML = [NSString stringWithFormat:embedHTMLTemplate, playerVarsJsonString]; + [self.webView loadHTMLString:embedHTML baseURL: self.originURL]; + self.webView.navigationDelegate = self; + self.webView.UIDelegate = self; + + if ([self.delegate respondsToSelector:@selector(playerViewPreferredInitialLoadingView:)]) { + UIView *initialLoadingView = [self.delegate playerViewPreferredInitialLoadingView:self]; + if (initialLoadingView) { + initialLoadingView.frame = self.bounds; + initialLoadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubview:initialLoadingView]; + self.initialLoadingView = initialLoadingView; + } + } + + return YES; +} + +/** + * Private method for cueing both cases of playlist ID and array of video IDs. Cueing + * a playlist does not start playback. + * + * @param cueingString A JavaScript string representing an array, playlist ID or list of + * video IDs to play with the playlist player. + * @param index 0-index position of video to start playback on. + * @param startSeconds Seconds after start of video to begin playback. + * @param suggestedQuality Suggested WKYTPlaybackQuality to play the videos. + */ +- (void)cuePlaylist:(NSString *)cueingString + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *indexValue = [NSNumber numberWithInt:index]; + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.cuePlaylist(%@, %@, %@, '%@');", + cueingString, indexValue, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +/** + * Private method for loading both cases of playlist ID and array of video IDs. Loading + * a playlist automatically starts playback. + * + * @param cueingString A JavaScript string representing an array, playlist ID or list of + * video IDs to play with the playlist player. + * @param index 0-index position of video to start playback on. + * @param startSeconds Seconds after start of video to begin playback. + * @param suggestedQuality Suggested WKYTPlaybackQuality to play the videos. + */ +- (void)loadPlaylist:(NSString *)cueingString + index:(int)index + startSeconds:(float)startSeconds + suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { + NSNumber *indexValue = [NSNumber numberWithInt:index]; + NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; + NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; + NSString *command = [NSString stringWithFormat:@"player.loadPlaylist(%@, %@, %@, '%@');", + cueingString, indexValue, startSecondsValue, qualityValue]; + [self stringFromEvaluatingJavaScript:command completionHandler:nil]; +} + +/** + * Private helper method for converting an NSArray of video IDs into its JavaScript equivalent. + * + * @param videoIds An array of video ID strings to convert into JavaScript format. + * @return A JavaScript array in String format containing video IDs. + */ +- (NSString *)stringFromVideoIdArray:(NSArray *)videoIds { + NSMutableArray *formattedVideoIds = [[NSMutableArray alloc] init]; + + for (id unformattedId in videoIds) { + [formattedVideoIds addObject:[NSString stringWithFormat:@"'%@'", unformattedId]]; + } + + return [NSString stringWithFormat:@"[%@]", [formattedVideoIds componentsJoinedByString:@", "]]; +} + +/** + * Private method for evaluating JavaScript in the WebView. + * + * @param jsToExecute The JavaScript code in string format that we want to execute. + */ +- (void)stringFromEvaluatingJavaScript:(NSString *)jsToExecute completionHandler:(void (^ __nullable)(NSString * __nullable response, NSError * __nullable error))completionHandler{ + [self.webView evaluateJavaScript:jsToExecute completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { + if (completionHandler) { + completionHandler(response, error); + } + }]; +} + +/** + * Private method to convert a Objective-C BOOL value to JS boolean value. + * + * @param boolValue Objective-C BOOL value. + * @return JavaScript Boolean value, i.e. "true" or "false". + */ +- (NSString *)stringForJSBoolean:(BOOL)boolValue { + return boolValue ? @"true" : @"false"; +} + +#pragma mark - Exposed for Testing + +- (void)setWebView:(WKWebView *)webView { + _webView = webView; +} + +- (WKWebView *)createNewWebView { + + // WKWebView equivalent for UI Web View's scalesPageToFit + // + NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"; + + WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; + WKUserContentController *wkUController = [[WKUserContentController alloc] init]; + [wkUController addUserScript:wkUScript]; + + WKWebViewConfiguration *configuration = [WKWebViewConfiguration new]; + + configuration.userContentController = wkUController; + + configuration.allowsInlineMediaPlayback = YES; + configuration.mediaPlaybackRequiresUserAction = NO; + + WKWebView *webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:configuration]; + webView.scrollView.scrollEnabled = NO; + webView.scrollView.bounces = NO; + + if ([self.delegate respondsToSelector:@selector(playerViewPreferredWebViewBackgroundColor:)]) { + webView.backgroundColor = [self.delegate playerViewPreferredWebViewBackgroundColor:self]; + if (webView.backgroundColor == [UIColor clearColor]) { + webView.opaque = NO; + } + } + + return webView; +} + +- (void)removeWebView { + [self.webView removeFromSuperview]; + self.webView = nil; +} + ++ (NSBundle *)frameworkBundle { + static NSBundle* frameworkBundle = nil; + static dispatch_once_t predicate; + dispatch_once(&predicate, ^{ + NSString* mainBundlePath = [[NSBundle bundleForClass:[WKYTPlayerView class]] resourcePath]; + NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"WKYTPlayerView.bundle"]; + frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath]; + }); + return frameworkBundle; +} + +@end diff --git a/metro.config.js b/metro.config.js index 47579372c..585fb667c 100644 --- a/metro.config.js +++ b/metro.config.js @@ -40,6 +40,7 @@ const config = { }; }, }, + maxWorkers: 4, }; module.exports = config; From e69b7c46babb234e2ccdd2b81979507f27172f4c Mon Sep 17 00:00:00 2001 From: CJ <38697367+imisshtml@users.noreply.github.com> Date: Mon, 21 Oct 2019 16:56:58 -0400 Subject: [PATCH 5/9] MM-18790 Fix Keyboard Color Flash (#3380) * MM-18790 Fix Keyboard Color Flash Resolved the autoresponder keyboard flash by adding a 500ms delay to the keyboard focus, allowing for a smooth keyboard interaction without the color flash. * MM-18790 Used CreateRef Updated the reference to React.createRef * Update app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js Co-Authored-By: Elias Nahum * MM-18790 Added check for current Updated the focus check to validate current is truthy --- .../notification_settings_auto_responder.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js index 9609bd855..f9ecde3a2 100644 --- a/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js +++ b/app/screens/settings/notification_settings_auto_responder/notification_settings_auto_responder.js @@ -44,6 +44,8 @@ export default class NotificationSettingsAutoResponder extends PureComponent { const {intl} = this.context; const notifyProps = getNotificationProps(currentUser); + this.autoresponderRef = React.createRef(); + const autoResponderDefault = intl.formatMessage({ id: 'mobile.notification_settings.auto_responder.default_message', defaultMessage: 'Hello, I am out of office and unable to respond to messages.', @@ -65,6 +67,16 @@ export default class NotificationSettingsAutoResponder extends PureComponent { this.saveUserNotifyProps(); } + componentDidMount() { + setTimeout(() => { + requestAnimationFrame(() => { + if (this.autoresponderRef.current) { + this.autoresponderRef.current.focus(); + } + }); + }, 500); + } + saveUserNotifyProps = () => { this.props.onBack({ ...this.state, @@ -126,8 +138,7 @@ export default class NotificationSettingsAutoResponder extends PureComponent { > Date: Tue, 22 Oct 2019 02:20:58 +0200 Subject: [PATCH 6/9] Remove deprecated lifecycle methods from misc components (#3426) * Remove deprecated lifecycle methods from misc components * Fixed linting errors --- .../profile_picture/profile_picture.js | 38 +++++++++---------- .../retry_bar_indicator.js | 6 +-- app/components/search_bar/search_box.js | 6 +-- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/app/components/profile_picture/profile_picture.js b/app/components/profile_picture/profile_picture.js index 61a20bcbc..82f6b63d5 100644 --- a/app/components/profile_picture/profile_picture.js +++ b/app/components/profile_picture/profile_picture.js @@ -67,27 +67,6 @@ export default class ProfilePicture extends PureComponent { } } - componentWillReceiveProps(nextProps) { - if (this.mounted) { - if (nextProps.edit && nextProps.imageUri && nextProps.imageUri !== this.props.imageUri) { - this.setImageURL(nextProps.imageUri); - return; - } - - if (nextProps.profileImageUri !== '' && nextProps.profileImageUri !== this.props.profileImageUri) { - this.setImageURL(nextProps.profileImageUri); - } - - const url = this.props.user ? Client4.getProfilePictureUrl(this.props.user.id, this.props.user.last_picture_update) : null; - const nextUrl = nextProps.user ? Client4.getProfilePictureUrl(nextProps.user.id, nextProps.user.last_picture_update) : null; - - if (nextUrl && url !== nextUrl) { - // empty function is so that promise unhandled is not triggered in dev mode - ImageCacheManager.cache('', nextUrl, this.setImageURL).then(this.clearProfileImageUri).catch(emptyFunction); - } - } - } - componentWillUnmount() { this.mounted = false; } @@ -107,6 +86,23 @@ export default class ProfilePicture extends PureComponent { componentDidUpdate(prevProps) { if (this.props.profileImageRemove !== prevProps.profileImageRemove) { this.setImageURL(null); + } else if (this.mounted) { + if (this.props.edit && this.props.imageUri && this.props.imageUri !== prevProps.imageUri) { + this.setImageURL(this.props.imageUri); + return; + } + + if (this.props.profileImageUri !== '' && this.props.profileImageUri !== prevProps.profileImageUri) { + this.setImageURL(this.props.profileImageUri); + } + + const url = prevProps.user ? Client4.getProfilePictureUrl(prevProps.user.id, prevProps.user.last_picture_update) : null; + const nextUrl = this.props.user ? Client4.getProfilePictureUrl(this.props.user.id, this.props.user.last_picture_update) : null; + + if (nextUrl && url !== nextUrl) { + // empty function is so that promise unhandled is not triggered in dev mode + ImageCacheManager.cache('', nextUrl, this.setImageURL).then(this.clearProfileImageUri).catch(emptyFunction); + } } } diff --git a/app/components/retry_bar_indicator/retry_bar_indicator.js b/app/components/retry_bar_indicator/retry_bar_indicator.js index 0cb943b55..c812cb5e1 100644 --- a/app/components/retry_bar_indicator/retry_bar_indicator.js +++ b/app/components/retry_bar_indicator/retry_bar_indicator.js @@ -19,9 +19,9 @@ export default class RetryBarIndicator extends PureComponent { retryMessageHeight: new Animated.Value(0), }; - componentWillReceiveProps(nextProps) { - if (this.props.failed !== nextProps.failed) { - this.toggleRetryMessage(nextProps.failed); + componentDidUpdate(prevProps) { + if (this.props.failed !== prevProps.failed) { + this.toggleRetryMessage(this.props.failed); } } diff --git a/app/components/search_bar/search_box.js b/app/components/search_bar/search_box.js index dbf390bfb..1f2f12180 100644 --- a/app/components/search_bar/search_box.js +++ b/app/components/search_bar/search_box.js @@ -132,9 +132,9 @@ export default class Search extends Component { this.shadowHeight = this.props.shadowOffsetHeightCollapsed; } - componentWillReceiveProps(nextProps) { - if (this.props.value !== nextProps.value) { - if (nextProps.value) { + componentDidUpdate(prevProps) { + if (this.props.value !== prevProps.value) { + if (this.props.value) { this.iconDeleteAnimated = new Animated.Value(1); } else { this.iconDeleteAnimated = new Animated.Value(0); From b06c2d8541b5ec61989ecf3c752e38263a1f417a Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Tue, 22 Oct 2019 17:05:54 +0300 Subject: [PATCH 7/9] Removing pods from source control, add cocoapods to circleCI (#3454) --- .circleci/config.yml | 63 +- .gitignore | 3 +- Makefile | 31 +- fastlane/Fastfile | 4 + ios/Mattermost.xcodeproj/project.pbxproj | 8 +- ios/Podfile.lock | 2 +- .../Private/XCDYouTubeKit/XCDYouTubeClient.h | 1 - .../XCDYouTubeKit/XCDYouTubeDashManifestXML.h | 1 - .../Private/XCDYouTubeKit/XCDYouTubeError.h | 1 - .../Private/XCDYouTubeKit/XCDYouTubeKit.h | 1 - .../XCDYouTubeKit/XCDYouTubeLogger+Private.h | 1 - .../Private/XCDYouTubeKit/XCDYouTubeLogger.h | 1 - .../XCDYouTubeKit/XCDYouTubeOperation.h | 1 - .../XCDYouTubeKit/XCDYouTubePlayerScript.h | 1 - .../XCDYouTubeKit/XCDYouTubeVideo+Private.h | 1 - .../Private/XCDYouTubeKit/XCDYouTubeVideo.h | 1 - .../XCDYouTubeKit/XCDYouTubeVideoOperation.h | 1 - .../XCDYouTubeVideoPlayerViewController.h | 1 - .../XCDYouTubeKit/XCDYouTubeVideoWebpage.h | 1 - .../WKYTPlayerView.h | 1 - .../Public/XCDYouTubeKit/XCDYouTubeClient.h | 1 - .../Public/XCDYouTubeKit/XCDYouTubeError.h | 1 - .../Public/XCDYouTubeKit/XCDYouTubeKit.h | 1 - .../Public/XCDYouTubeKit/XCDYouTubeLogger.h | 1 - .../XCDYouTubeKit/XCDYouTubeOperation.h | 1 - .../Public/XCDYouTubeKit/XCDYouTubeVideo.h | 1 - .../XCDYouTubeKit/XCDYouTubeVideoOperation.h | 1 - .../XCDYouTubeVideoPlayerViewController.h | 1 - .../WKYTPlayerView.h | 1 - ios/Pods/Manifest.lock | 20 - ios/Pods/Pods.xcodeproj/project.pbxproj | 893 ------------- .../Pods-Mattermost-acknowledgements.markdown | 46 - .../Pods-Mattermost-acknowledgements.plist | 84 -- .../Pods-Mattermost/Pods-Mattermost-dummy.m | 5 - .../Pods-Mattermost-frameworks.sh | 146 --- .../Pods-Mattermost-resources.sh | 124 -- .../Pods-Mattermost.debug.xcconfig | 9 - .../Pods-Mattermost.release.xcconfig | 9 - ...-MattermostTests-acknowledgements.markdown | 3 - ...ods-MattermostTests-acknowledgements.plist | 29 - .../Pods-MattermostTests-dummy.m | 5 - .../Pods-MattermostTests-frameworks.sh | 146 --- .../Pods-MattermostTests-resources.sh | 118 -- .../Pods-MattermostTests.debug.xcconfig | 9 - .../Pods-MattermostTests.release.xcconfig | 9 - .../XCDYouTubeKit/XCDYouTubeKit-dummy.m | 5 - .../XCDYouTubeKit/XCDYouTubeKit-prefix.pch | 12 - .../XCDYouTubeKit/XCDYouTubeKit.xcconfig | 10 - .../YoutubePlayer-in-WKWebView-dummy.m | 5 - .../YoutubePlayer-in-WKWebView-prefix.pch | 12 - .../YoutubePlayer-in-WKWebView.xcconfig | 9 - ios/Pods/XCDYouTubeKit/LICENSE | 22 - ios/Pods/XCDYouTubeKit/README.md | 151 --- .../XCDYouTubeKit/XCDYouTubeClient.h | 98 -- .../XCDYouTubeKit/XCDYouTubeClient.m | 83 -- .../XCDYouTubeKit/XCDYouTubeDashManifestXML.h | 17 - .../XCDYouTubeKit/XCDYouTubeDashManifestXML.m | 98 -- .../XCDYouTubeKit/XCDYouTubeError.h | 47 - .../XCDYouTubeKit/XCDYouTubeKit.h | 16 - .../XCDYouTubeKit/XCDYouTubeLogger+Private.h | 21 - .../XCDYouTubeKit/XCDYouTubeLogger.h | 103 -- .../XCDYouTubeKit/XCDYouTubeLogger.m | 63 - .../XCDYouTubeKit/XCDYouTubeOperation.h | 23 - .../XCDYouTubeKit/XCDYouTubePlayerScript.h | 14 - .../XCDYouTubeKit/XCDYouTubePlayerScript.m | 125 -- .../XCDYouTubeKit/XCDYouTubeVideo+Private.h | 24 - .../XCDYouTubeKit/XCDYouTubeVideo.h | 147 --- .../XCDYouTubeKit/XCDYouTubeVideo.m | 319 ----- .../XCDYouTubeKit/XCDYouTubeVideoOperation.h | 74 -- .../XCDYouTubeKit/XCDYouTubeVideoOperation.m | 410 ------ .../XCDYouTubeVideoPlayerViewController.h | 126 -- .../XCDYouTubeVideoPlayerViewController.m | 209 --- .../XCDYouTubeKit/XCDYouTubeVideoWebpage.h | 18 - .../XCDYouTubeKit/XCDYouTubeVideoWebpage.m | 124 -- ios/Pods/YoutubePlayer-in-WKWebView/LICENSE | 13 - ios/Pods/YoutubePlayer-in-WKWebView/README.md | 59 - .../Assets/YTPlayerView-iframe-player.html | 90 -- .../WKYTPlayerView/WKYTPlayerView.h | 734 ----------- .../WKYTPlayerView/WKYTPlayerView.m | 1123 ----------------- package-lock.json | 50 +- package.json | 2 +- 81 files changed, 94 insertions(+), 6151 deletions(-) delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h delete mode 120000 ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h delete mode 120000 ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h delete mode 120000 ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h delete mode 120000 ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h delete mode 100644 ios/Pods/Manifest.lock delete mode 100644 ios/Pods/Pods.xcodeproj/project.pbxproj delete mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown delete mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist delete mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m delete mode 100755 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh delete mode 100755 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh delete mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig delete mode 100644 ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig delete mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown delete mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist delete mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m delete mode 100755 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh delete mode 100755 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh delete mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig delete mode 100644 ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig delete mode 100644 ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m delete mode 100644 ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch delete mode 100644 ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig delete mode 100644 ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m delete mode 100644 ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch delete mode 100644 ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig delete mode 100644 ios/Pods/XCDYouTubeKit/LICENSE delete mode 100644 ios/Pods/XCDYouTubeKit/README.md delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h delete mode 100644 ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m delete mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/LICENSE delete mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/README.md delete mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html delete mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h delete mode 100644 ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m diff --git a/.circleci/config.yml b/.circleci/config.yml index 64adeca88..5bd327dba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,24 +36,27 @@ commands: name: Clone the mobile private repo command: git clone git@github.com:mattermost/mattermost-mobile-private.git ~/mattermost-mobile-private - android-gem-dependencies: - description: "Get Fastlane dependencies for Android builds" + fastlane-dependencies: + description: "Get Fastlane dependencies" + parameters: + for: + type: string steps: - restore_cache: name: Restore Fastlane cache - key: v1-gems-android-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} + key: v1-gems-<< parameters.for >>-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} - run: working_directory: fastlane name: Download Fastlane dependencies command: bundle install --path vendor/bundle - save_cache: name: Save Fastlane cache - key: v1-gems-android-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} + key: v1-gems-<< parameters.for >>-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} paths: - fastlane/vendor/bundle - android-gradle-dependencies: - description: "Get Gradle dependencies for Android buils" + gradle-dependencies: + description: "Get Gradle dependencies" steps: - restore_cache: name: Restore Gradle cache @@ -83,27 +86,8 @@ commands: paths: - dist - ios-gem-dependencies: - description: "Get Fastlane dependencies for iOS builds" - steps: - - run: - name: Set Ruby version - command: echo "ruby-2.6.3" > ~/.ruby-version - - restore_cache: - name: Restore Fastlane cache - key: v1-gems-ios-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} - - run: - working_directory: fastlane - name: Download Fastlane dependencies - command: bundle install --path vendor/bundle - - save_cache: - name: Save Fastlane cache - key: v1-gems-ios-{{ checksum "fastlane/Gemfile.lock" }}-{{ arch }} - paths: - - fastlane/vendor/bundle - npm-dependencies: - description: "Get JavaScript dependencies for the job" + description: "Get JavaScript dependencies" steps: - restore_cache: name: Restore npm cache @@ -117,6 +101,23 @@ commands: paths: - node_modules + pods-dependencies: + description: "Get cocoapods dependencies" + steps: + - restore_cache: + name: Restore cocoapods specs and pods + key: v1-cocoapods-{{ checksum "ios/Podfile.lock" }}-{{ arch }} + - run: + name: Getting cocoapods dependencies + working_directory: ios + command: pod install + - save_cache: + name: Save cocoapods specs and pods cache + key: v1-cocoapods-{{ checksum "ios/Podfile.lock" }}-{{ arch }} + paths: + - ios/Pods + - ~/.cocoapods + build-android: description: "Build the android app" steps: @@ -125,12 +126,14 @@ commands: - checkout-private - npm-dependencies - assets - - android-gem-dependencies - - android-gradle-dependencies + - fastlane-dependencies: + for: android + - gradle-dependencies - run: name: Append Keystore to build Android command: | cp ~/mattermost-mobile-private/android/${STORE_FILE} android/app/${STORE_FILE} + echo "" | tee -a android/gradle.properties > /dev/null echo MATTERMOST_RELEASE_STORE_FILE=${STORE_FILE} | tee -a android/gradle.properties > /dev/null echo ${STORE_ALIAS} | tee -a android/gradle.properties > /dev/null echo ${STORE_PASSWORD} | tee -a android/gradle.properties > /dev/null @@ -146,8 +149,10 @@ commands: - checkout: path: ~/mattermost-mobile - npm-dependencies + - pods-dependencies - assets - - ios-gem-dependencies + - fastlane-dependencies: + for: ios - run: working_directory: fastlane name: Run fastlane to build iOS diff --git a/.gitignore b/.gitignore index bd5cbe33e..4c7e3f3f7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,6 @@ build/ *.perspectivev3 !default.perspectivev3 xcuserdata -xcshareddata *.xccheckout *.moved-aside DerivedData @@ -31,7 +30,7 @@ DerivedData *.apk *.xcuserstate project.xcworkspace -xcshareddata/ +ios/Pods # Android/IntelliJ # diff --git a/Makefile b/Makefile index 8e3c9ab1d..38d55dcb1 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,18 @@ npm-ci: package.json @echo Getting Javascript dependencies @npm ci +.podinstall: +ifeq ($(OS), Darwin) +ifdef POD + @echo Getting Cocoapods dependencies; + @cd ios && pod install; +else + @echo "Cocoapods is not installed https://cocoapods.org/" + @exit 1 +endif +endif + @touch $@ + dist/assets: $(BASE_ASSETS) $(OVERRIDE_ASSETS) @mkdir -p dist @@ -41,9 +53,9 @@ dist/assets: $(BASE_ASSETS) $(OVERRIDE_ASSETS) @echo "Generating app assets" @node scripts/make-dist-assets.js -pre-run: | node_modules dist/assets ## Installs dependencies and assets +pre-run: | node_modules .podinstall dist/assets ## Installs dependencies and assets -pre-build: | npm-ci dist/assets ## Install dependencies and assets before building +pre-build: | npm-ci .podinstall dist/assets ## Install dependencies and assets before building check-style: node_modules ## Runs eslint @echo Checking for style guide compliance @@ -52,6 +64,8 @@ check-style: node_modules ## Runs eslint clean: ## Cleans dependencies, previous builds and temp files @echo Cleaning started + @rm -f .podinstall + @rm -rf ios/Pods @rm -rf node_modules @rm -rf dist @rm -rf ios/build @@ -60,15 +74,6 @@ clean: ## Cleans dependencies, previous builds and temp files @echo Cleanup finished post-install: - @# Need to copy custom RNDocumentPicker.m that implements direct access to the document picker in iOS - @cp ./native_modules/RNDocumentPicker.m node_modules/react-native-document-picker/ios/RNDocumentPicker/RNDocumentPicker.m - - @# Need to copy custom RNCookieManagerIOS.m that fixes a crash when cookies does not have expiration date set - @cp ./native_modules/RNCookieManagerIOS.m node_modules/react-native-cookies/ios/RNCookieManagerIOS/RNCookieManagerIOS.m - - @# Need to copy custom RNCNetInfo.m that checks for internet connectivity instead of reaching a host by default - @cp ./native_modules/RNCNetInfo.m node_modules/@react-native-community/netinfo/ios/RNCNetInfo.m - @rm -f node_modules/intl/.babelrc @# Hack to get react-intl and its dependencies to work with react-native @# Based off of https://github.com/este/este/blob/master/gulp/native-fix.js @@ -76,10 +81,6 @@ post-install: @sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-messageformat/package.json @sed -i'' -e 's|"./lib/locales": false|"./lib/locales": "./lib/locales"|g' node_modules/intl-relativeformat/package.json @sed -i'' -e 's|"./locale-data/complete.js": false|"./locale-data/complete.js": "./locale-data/complete.js"|g' node_modules/intl/package.json - @if [ $(shell grep "const Platform" node_modules/react-native/Libraries/Lists/VirtualizedList.js | grep -civ grep) -eq 0 ]; then \ - sed $ -i'' -e "s|const ReactNative = require('ReactNative');|const ReactNative = require('ReactNative');`echo $\\\\\\r;`const Platform = require('Platform');|g" node_modules/react-native/Libraries/Lists/VirtualizedList.js; \ - fi - @sed -i'' -e 's|transform: \[{scaleY: -1}\],|...Platform.select({android: {transform: \[{perspective: 1}, {scaleY: -1}\]}, ios: {transform: \[{scaleY: -1}\]}}),|g' node_modules/react-native/Libraries/Lists/VirtualizedList.js @./node_modules/.bin/patch-package diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 301ec13dc..8593d2e92 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -13,6 +13,10 @@ is_build_pr = false # Executes before anything else use to setup the script before_all do is_build_pr = ENV['BUILD_PR'] == 'true' + if is_build_pr && ENV['CIRCLECI'] == 'true' + ENV['BRANCH_TO_BUILD'] = ENV['CIRCLE_BRANCH'] + end + UI.success("Building for release #{ENV['BUILD_FOR_RELEASE']}") if ENV['CIRCLECI'] != 'true' diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index 1b10393ed..a33a2f7b8 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -2637,21 +2637,17 @@ buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh", + "${PODS_ROOT}/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh", "${PODS_ROOT}/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle", ); name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - ); outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/WKYTPlayerView.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh\"\n"; showEnvVarsInLog = 0; }; AE4769B235D14E6C9C64EA78 /* Upload Debug Symbols to Sentry */ = { diff --git a/ios/Podfile.lock b/ios/Podfile.lock index dd0e6c594..a59d3a50e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -17,4 +17,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: e565d3af8eabb0e489b73c79433e140e30f8fa9c -COCOAPODS: 1.5.3 +COCOAPODS: 1.7.5 diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h deleted file mode 120000 index 2bd449376..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeClient.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h deleted file mode 120000 index 02be59fee..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeDashManifestXML.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h deleted file mode 120000 index 4c6252175..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeError.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h deleted file mode 120000 index d16f6f871..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeKit.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h deleted file mode 120000 index df0c2ebce..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger+Private.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h deleted file mode 120000 index 0d47450a6..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeLogger.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h deleted file mode 120000 index e8d6b951c..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h deleted file mode 120000 index caeaae4a1..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubePlayerScript.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h deleted file mode 120000 index e8344312d..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo+Private.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h deleted file mode 120000 index ee13de8b6..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideo.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h deleted file mode 120000 index ac0730e71..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h deleted file mode 120000 index 0ac32058e..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h b/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h deleted file mode 120000 index 4c0f3d23d..000000000 --- a/ios/Pods/Headers/Private/XCDYouTubeKit/XCDYouTubeVideoWebpage.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h b/ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h deleted file mode 120000 index f174706c0..000000000 --- a/ios/Pods/Headers/Private/YoutubePlayer-in-WKWebView/WKYTPlayerView.h +++ /dev/null @@ -1 +0,0 @@ -../../../YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h deleted file mode 120000 index 2bd449376..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeClient.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h deleted file mode 120000 index 4c6252175..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeError.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h deleted file mode 120000 index d16f6f871..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeKit.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h deleted file mode 120000 index 0d47450a6..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeLogger.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h deleted file mode 120000 index e8d6b951c..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h deleted file mode 120000 index ee13de8b6..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideo.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h deleted file mode 120000 index ac0730e71..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoOperation.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h b/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h deleted file mode 120000 index 0ac32058e..000000000 --- a/ios/Pods/Headers/Public/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h +++ /dev/null @@ -1 +0,0 @@ -../../../XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h b/ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h deleted file mode 120000 index f174706c0..000000000 --- a/ios/Pods/Headers/Public/YoutubePlayer-in-WKWebView/WKYTPlayerView.h +++ /dev/null @@ -1 +0,0 @@ -../../../YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h \ No newline at end of file diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock deleted file mode 100644 index dd0e6c594..000000000 --- a/ios/Pods/Manifest.lock +++ /dev/null @@ -1,20 +0,0 @@ -PODS: - - XCDYouTubeKit (2.7.1) - - YoutubePlayer-in-WKWebView (0.3.3) - -DEPENDENCIES: - - XCDYouTubeKit (= 2.7.1) - - YoutubePlayer-in-WKWebView (~> 0.3.1) - -SPEC REPOS: - https://github.com/cocoapods/specs.git: - - XCDYouTubeKit - - YoutubePlayer-in-WKWebView - -SPEC CHECKSUMS: - XCDYouTubeKit: c8567fd5cb388a3099fa26eee4b30df2a467847d - YoutubePlayer-in-WKWebView: 7694e858c5c3472ed067d6fe34eb9b944845e63c - -PODFILE CHECKSUM: e565d3af8eabb0e489b73c79433e140e30f8fa9c - -COCOAPODS: 1.5.3 diff --git a/ios/Pods/Pods.xcodeproj/project.pbxproj b/ios/Pods/Pods.xcodeproj/project.pbxproj deleted file mode 100644 index be530aa9c..000000000 --- a/ios/Pods/Pods.xcodeproj/project.pbxproj +++ /dev/null @@ -1,893 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 00EF717F2CE5C08CE046FAB25A49C579 /* Pods-MattermostTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C0A3E27A51ED227A8460BDBC0B1A5BDC /* Pods-MattermostTests-dummy.m */; }; - 19A3D8279379A5ACBE7F699CB120E20C /* XCDYouTubeOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = A16570331366E84C38260E575C1ACDAF /* XCDYouTubeOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 1B069680B86677A2EA0846201573A2C9 /* XCDYouTubeClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 218BAF448A4CEBF30D9938922CA236C2 /* XCDYouTubeClient.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 1C5F63B626D206B0ED32320876A3EA60 /* XCDYouTubeVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = BB52596A2D243BBDCE8BCB3CF50A6E20 /* XCDYouTubeVideo.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 2F6113A993C9CA73926D2946728A7D37 /* XCDYouTubeVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = DD5E806A91AFE8DD2B077AE695123547 /* XCDYouTubeVideoPlayerViewController.m */; }; - 32BC55AFE9DADF2BE266AB44A9F9C382 /* XCDYouTubeDashManifestXML.m in Sources */ = {isa = PBXBuildFile; fileRef = 7A014D164D90DF54F9BD4AC60868D11D /* XCDYouTubeDashManifestXML.m */; }; - 3933FE5CEDAAFF0AE1DE7413EDA8CA38 /* XCDYouTubeDashManifestXML.h in Headers */ = {isa = PBXBuildFile; fileRef = 18BED515670B4F1D15806832C57D59F9 /* XCDYouTubeDashManifestXML.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 3F0B35BE6C13ADDF8364D0E9F7A07522 /* XCDYouTubeKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = CFFE5759AD00493BC5172561B1C1C7CD /* XCDYouTubeKit-dummy.m */; }; - 4C91B4A27AFBF35591DD3CDF23E2DC17 /* XCDYouTubeError.h in Headers */ = {isa = PBXBuildFile; fileRef = 162E428722ECD765CEE83EB3D33C71E2 /* XCDYouTubeError.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5B8AEF4A577D21542D6F8DE2B248C8E7 /* XCDYouTubeVideoWebpage.m in Sources */ = {isa = PBXBuildFile; fileRef = 920F9C3CEECF4C1924F458E3D0428ED8 /* XCDYouTubeVideoWebpage.m */; }; - 5BE4F6E90A8A1644C0BBB4635AD0D84F /* WKYTPlayerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 19281C3EF03E987F37C203D17BCA2EC5 /* WKYTPlayerView.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 5ED62229AE2031D59F1DF033F77D03B2 /* XCDYouTubeVideoOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = BA781F6AB0F7487AD05BC63EA40AB8BF /* XCDYouTubeVideoOperation.m */; }; - 607B50452E483ACCA8FA33B3C0114BFD /* XCDYouTubeVideoOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 281750171C47F186466B86E65956736D /* XCDYouTubeVideoOperation.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 6A881BEE202BC2AC64ACE1C2E7583756 /* XCDYouTubeVideo+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CBB4B14F0B800EC1527A5353A3AE1CA /* XCDYouTubeVideo+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 7621E888B2755702A10B6724E4107332 /* XCDYouTubePlayerScript.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E94B5BEED415E94C72301A86E7A9859 /* XCDYouTubePlayerScript.m */; }; - 76BE93DCF558BFA281930A26018F9330 /* XCDYouTubeLogger+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DAF995C826738DB2D85603716D91154 /* XCDYouTubeLogger+Private.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 77F5E0033E60972EC230A8ABD78071A3 /* XCDYouTubeVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = EFFFFBEC2A07FFA8841667B371ED34E0 /* XCDYouTubeVideo.m */; }; - 80627FF4ECD0A58F8A6C2874FA612EB1 /* YoutubePlayer-in-WKWebView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 72206B26A17824A476D334C5612D4142 /* YoutubePlayer-in-WKWebView-dummy.m */; }; - 8433D3049BEEF12D212D6B1DD6DD0512 /* XCDYouTubeClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 46714161B2C62428DB972729952DC352 /* XCDYouTubeClient.m */; }; - 90A4DC7BA3E2C3C54D484B8959CFB708 /* XCDYouTubePlayerScript.h in Headers */ = {isa = PBXBuildFile; fileRef = F1766C4F2FDC063F421CA2D4688FED68 /* XCDYouTubePlayerScript.h */; settings = {ATTRIBUTES = (Project, ); }; }; - 9EFEB7A7C37759A0328EB1B198E2C37D /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15D48C716A411AD7621E7125E083F54A /* MediaPlayer.framework */; }; - A53E28B0072944DEE03E0D2DA20C0FA0 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98C181471820795EDEAACBC10B84709A /* JavaScriptCore.framework */; }; - C50CA7A8B2F6691C7E29BD7BC2733975 /* XCDYouTubeLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 6F752B1C4CE355EE8A9DA89A25D0AE73 /* XCDYouTubeLogger.m */; }; - CA1EF66BB4AA70EFF1869D1FE0A373AE /* Pods-Mattermost-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 926D56CC36F536DEFF1A45331E070750 /* Pods-Mattermost-dummy.m */; }; - D56363E3D25AE7C3C37948A2ED267EFC /* WKYTPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A081D43F9928F93E8E7FCA268B6660DA /* WKYTPlayerView.m */; }; - E74005FCD11C34B3705DE02F89D4028B /* XCDYouTubeKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 724042289C9DC57B313DD893EFD1AA54 /* XCDYouTubeKit.h */; settings = {ATTRIBUTES = (Project, ); }; }; - EA6A2186A0767AD5C4A45579994658BD /* XCDYouTubeVideoPlayerViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 294543F53C1E8CC7BECE8214C09765EE /* XCDYouTubeVideoPlayerViewController.h */; settings = {ATTRIBUTES = (Project, ); }; }; - F99FDC5AFF20BC2A65AEE5A41209A42C /* XCDYouTubeVideoWebpage.h in Headers */ = {isa = PBXBuildFile; fileRef = 8540AD01026C33CA77834BD0EE077CDB /* XCDYouTubeVideoWebpage.h */; settings = {ATTRIBUTES = (Project, ); }; }; - FB83D8680F87F8315167D84D2BEEDF6C /* XCDYouTubeLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 510EF6F5EB43790577352F6C8A0718B0 /* XCDYouTubeLogger.h */; settings = {ATTRIBUTES = (Project, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 165E90C48BE6DE526DE6EFE88F787BE2 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 191A114290607BBCF32141FA83AB9F6E; - remoteInfo = "Pods-Mattermost"; - }; - A2AEF4FA0A01D420BBD3AC4B1428E3FD /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 5552A5DF4126A9D12B869B8272B40FF1; - remoteInfo = XCDYouTubeKit; - }; - DB4FDEE7D3C70BEC4323EBE859F93B11 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D5F98C908412D8333DECA6E74A2FC15E; - remoteInfo = "YoutubePlayer-in-WKWebView"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 0800A13BE9EB3366E47E9A990DDEF5A4 /* Pods-MattermostTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-MattermostTests-acknowledgements.plist"; sourceTree = ""; }; - 0AD2A25648B085555683131216BAD797 /* Pods-Mattermost-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Mattermost-acknowledgements.markdown"; sourceTree = ""; }; - 0CA88A7462704DBCB53C017185C1A65F /* Pods-Mattermost-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Mattermost-acknowledgements.plist"; sourceTree = ""; }; - 15D48C716A411AD7621E7125E083F54A /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/MediaPlayer.framework; sourceTree = DEVELOPER_DIR; }; - 162E428722ECD765CEE83EB3D33C71E2 /* XCDYouTubeError.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeError.h; path = XCDYouTubeKit/XCDYouTubeError.h; sourceTree = ""; }; - 18BED515670B4F1D15806832C57D59F9 /* XCDYouTubeDashManifestXML.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeDashManifestXML.h; path = XCDYouTubeKit/XCDYouTubeDashManifestXML.h; sourceTree = ""; }; - 19281C3EF03E987F37C203D17BCA2EC5 /* WKYTPlayerView.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = WKYTPlayerView.h; path = WKYTPlayerView/WKYTPlayerView.h; sourceTree = ""; }; - 1CBB4B14F0B800EC1527A5353A3AE1CA /* XCDYouTubeVideo+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "XCDYouTubeVideo+Private.h"; path = "XCDYouTubeKit/XCDYouTubeVideo+Private.h"; sourceTree = ""; }; - 218BAF448A4CEBF30D9938922CA236C2 /* XCDYouTubeClient.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeClient.h; path = XCDYouTubeKit/XCDYouTubeClient.h; sourceTree = ""; }; - 24390EFD555DD124430DFF9724065945 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; - 281750171C47F186466B86E65956736D /* XCDYouTubeVideoOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideoOperation.h; path = XCDYouTubeKit/XCDYouTubeVideoOperation.h; sourceTree = ""; }; - 294543F53C1E8CC7BECE8214C09765EE /* XCDYouTubeVideoPlayerViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideoPlayerViewController.h; path = XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h; sourceTree = ""; }; - 377B2C391CA94B7612EDC96C2306DB48 /* Pods-MattermostTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MattermostTests.release.xcconfig"; sourceTree = ""; }; - 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = XCDYouTubeKit.xcconfig; sourceTree = ""; }; - 3DAF995C826738DB2D85603716D91154 /* XCDYouTubeLogger+Private.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "XCDYouTubeLogger+Private.h"; path = "XCDYouTubeKit/XCDYouTubeLogger+Private.h"; sourceTree = ""; }; - 46714161B2C62428DB972729952DC352 /* XCDYouTubeClient.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeClient.m; path = XCDYouTubeKit/XCDYouTubeClient.m; sourceTree = ""; }; - 510EF6F5EB43790577352F6C8A0718B0 /* XCDYouTubeLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeLogger.h; path = XCDYouTubeKit/XCDYouTubeLogger.h; sourceTree = ""; }; - 60FB83EAEF578DD0C2DE316FCF4322A9 /* Pods-MattermostTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MattermostTests.debug.xcconfig"; sourceTree = ""; }; - 6170F2E79BA4DE1619C6FD1F25C3901F /* Pods-Mattermost-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Mattermost-frameworks.sh"; sourceTree = ""; }; - 6CD50F1ED20084866B7CE47B8F8E4BE4 /* libYoutubePlayer-in-WKWebView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libYoutubePlayer-in-WKWebView.a"; path = "libYoutubePlayer-in-WKWebView.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 6F752B1C4CE355EE8A9DA89A25D0AE73 /* XCDYouTubeLogger.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeLogger.m; path = XCDYouTubeKit/XCDYouTubeLogger.m; sourceTree = ""; }; - 72206B26A17824A476D334C5612D4142 /* YoutubePlayer-in-WKWebView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "YoutubePlayer-in-WKWebView-dummy.m"; sourceTree = ""; }; - 724042289C9DC57B313DD893EFD1AA54 /* XCDYouTubeKit.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeKit.h; path = XCDYouTubeKit/XCDYouTubeKit.h; sourceTree = ""; }; - 7703139D81F98B8B6130B2DAFB831C41 /* libPods-Mattermost.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-Mattermost.a"; path = "libPods-Mattermost.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 7A014D164D90DF54F9BD4AC60868D11D /* XCDYouTubeDashManifestXML.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeDashManifestXML.m; path = XCDYouTubeKit/XCDYouTubeDashManifestXML.m; sourceTree = ""; }; - 8540AD01026C33CA77834BD0EE077CDB /* XCDYouTubeVideoWebpage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideoWebpage.h; path = XCDYouTubeKit/XCDYouTubeVideoWebpage.h; sourceTree = ""; }; - 8A89EF129E834187D884270CF5CAF3C5 /* Pods-Mattermost-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Mattermost-resources.sh"; sourceTree = ""; }; - 8E94B5BEED415E94C72301A86E7A9859 /* XCDYouTubePlayerScript.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubePlayerScript.m; path = XCDYouTubeKit/XCDYouTubePlayerScript.m; sourceTree = ""; }; - 920F9C3CEECF4C1924F458E3D0428ED8 /* XCDYouTubeVideoWebpage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideoWebpage.m; path = XCDYouTubeKit/XCDYouTubeVideoWebpage.m; sourceTree = ""; }; - 926D56CC36F536DEFF1A45331E070750 /* Pods-Mattermost-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Mattermost-dummy.m"; sourceTree = ""; }; - 934A4B9575F4CBDCBE7B044C1F049365 /* Pods-Mattermost.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Mattermost.debug.xcconfig"; sourceTree = ""; }; - 974866440990F0A14694BD9B2E9962E0 /* Pods-MattermostTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-MattermostTests-acknowledgements.markdown"; sourceTree = ""; }; - 98C181471820795EDEAACBC10B84709A /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; - A081D43F9928F93E8E7FCA268B6660DA /* WKYTPlayerView.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = WKYTPlayerView.m; path = WKYTPlayerView/WKYTPlayerView.m; sourceTree = ""; }; - A16570331366E84C38260E575C1ACDAF /* XCDYouTubeOperation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeOperation.h; path = XCDYouTubeKit/XCDYouTubeOperation.h; sourceTree = ""; }; - A43B4ECF27212BC0465EDA19AE3164CC /* XCDYouTubeKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "XCDYouTubeKit-prefix.pch"; sourceTree = ""; }; - B564ED8191C54D3CAD8D2A2A6B9B9D1B /* Pods-Mattermost.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Mattermost.release.xcconfig"; sourceTree = ""; }; - BA781F6AB0F7487AD05BC63EA40AB8BF /* XCDYouTubeVideoOperation.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideoOperation.m; path = XCDYouTubeKit/XCDYouTubeVideoOperation.m; sourceTree = ""; }; - BB52596A2D243BBDCE8BCB3CF50A6E20 /* XCDYouTubeVideo.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubeVideo.h; path = XCDYouTubeKit/XCDYouTubeVideo.h; sourceTree = ""; }; - C0A3E27A51ED227A8460BDBC0B1A5BDC /* Pods-MattermostTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-MattermostTests-dummy.m"; sourceTree = ""; }; - C70E5870375DC970207B78465E560E64 /* YoutubePlayer-in-WKWebView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "YoutubePlayer-in-WKWebView-prefix.pch"; sourceTree = ""; }; - C99CF8DC55235B8BA857A47235948EE7 /* libXCDYouTubeKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libXCDYouTubeKit.a; path = libXCDYouTubeKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; - CA6DA7F1941EC2569E22ACF8A89FA03B /* Pods-MattermostTests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-MattermostTests-resources.sh"; sourceTree = ""; }; - CFFE5759AD00493BC5172561B1C1C7CD /* XCDYouTubeKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "XCDYouTubeKit-dummy.m"; sourceTree = ""; }; - DD5E806A91AFE8DD2B077AE695123547 /* XCDYouTubeVideoPlayerViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideoPlayerViewController.m; path = XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m; sourceTree = ""; }; - E0545ADDF3E1FF7C1BC19F497787C42B /* WKYTPlayerView.bundle */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "wrapper.plug-in"; name = WKYTPlayerView.bundle; path = WKYTPlayerView/WKYTPlayerView.bundle; sourceTree = ""; }; - E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "YoutubePlayer-in-WKWebView.xcconfig"; sourceTree = ""; }; - E99B330F0F9A45FF2E1ACB80DA268841 /* libPods-MattermostTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-MattermostTests.a"; path = "libPods-MattermostTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - EFFFFBEC2A07FFA8841667B371ED34E0 /* XCDYouTubeVideo.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = XCDYouTubeVideo.m; path = XCDYouTubeKit/XCDYouTubeVideo.m; sourceTree = ""; }; - F1766C4F2FDC063F421CA2D4688FED68 /* XCDYouTubePlayerScript.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = XCDYouTubePlayerScript.h; path = XCDYouTubeKit/XCDYouTubePlayerScript.h; sourceTree = ""; }; - FFD8DC263483FE0689017EF3573C5C71 /* Pods-MattermostTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-MattermostTests-frameworks.sh"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 3BF9126BA0554EB8C75B0C9B1F5908D5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 9607CC185A314A5FB24F3B67E3C0B743 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A53E28B0072944DEE03E0D2DA20C0FA0 /* JavaScriptCore.framework in Frameworks */, - 9EFEB7A7C37759A0328EB1B198E2C37D /* MediaPlayer.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - E909807276D8D8C1F62A49D70C2048F0 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - EBE0F2650665138130247C39F808CDFB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 1628BF05B4CAFDCC3549A101F5A10A17 /* Frameworks */ = { - isa = PBXGroup; - children = ( - B01816E2D09E8070AEE9E96136D917B6 /* iOS */, - ); - name = Frameworks; - sourceTree = ""; - }; - 3EAAB2CFFBD8598AB3144F85D9EDACD8 /* Pods-Mattermost */ = { - isa = PBXGroup; - children = ( - 0AD2A25648B085555683131216BAD797 /* Pods-Mattermost-acknowledgements.markdown */, - 0CA88A7462704DBCB53C017185C1A65F /* Pods-Mattermost-acknowledgements.plist */, - 926D56CC36F536DEFF1A45331E070750 /* Pods-Mattermost-dummy.m */, - 6170F2E79BA4DE1619C6FD1F25C3901F /* Pods-Mattermost-frameworks.sh */, - 8A89EF129E834187D884270CF5CAF3C5 /* Pods-Mattermost-resources.sh */, - 934A4B9575F4CBDCBE7B044C1F049365 /* Pods-Mattermost.debug.xcconfig */, - B564ED8191C54D3CAD8D2A2A6B9B9D1B /* Pods-Mattermost.release.xcconfig */, - ); - name = "Pods-Mattermost"; - path = "Target Support Files/Pods-Mattermost"; - sourceTree = ""; - }; - 428BC12C5AD7D04CCD5564A473A61275 /* Pods */ = { - isa = PBXGroup; - children = ( - 950725B3833769C44391B7E733E759D0 /* XCDYouTubeKit */, - 6571058FEA94E8172ABEAFD4A1D3C1A7 /* YoutubePlayer-in-WKWebView */, - ); - name = Pods; - sourceTree = ""; - }; - 6571058FEA94E8172ABEAFD4A1D3C1A7 /* YoutubePlayer-in-WKWebView */ = { - isa = PBXGroup; - children = ( - 19281C3EF03E987F37C203D17BCA2EC5 /* WKYTPlayerView.h */, - A081D43F9928F93E8E7FCA268B6660DA /* WKYTPlayerView.m */, - 92B16AFE4649769630669C6CDA9910C9 /* Resources */, - C609F4F9C6363362CDC6B91DC2BB8DA7 /* Support Files */, - ); - name = "YoutubePlayer-in-WKWebView"; - path = "YoutubePlayer-in-WKWebView"; - sourceTree = ""; - }; - 6CDFB0049112FA789537AC9B9E94FC6E /* Pods-MattermostTests */ = { - isa = PBXGroup; - children = ( - 974866440990F0A14694BD9B2E9962E0 /* Pods-MattermostTests-acknowledgements.markdown */, - 0800A13BE9EB3366E47E9A990DDEF5A4 /* Pods-MattermostTests-acknowledgements.plist */, - C0A3E27A51ED227A8460BDBC0B1A5BDC /* Pods-MattermostTests-dummy.m */, - FFD8DC263483FE0689017EF3573C5C71 /* Pods-MattermostTests-frameworks.sh */, - CA6DA7F1941EC2569E22ACF8A89FA03B /* Pods-MattermostTests-resources.sh */, - 60FB83EAEF578DD0C2DE316FCF4322A9 /* Pods-MattermostTests.debug.xcconfig */, - 377B2C391CA94B7612EDC96C2306DB48 /* Pods-MattermostTests.release.xcconfig */, - ); - name = "Pods-MattermostTests"; - path = "Target Support Files/Pods-MattermostTests"; - sourceTree = ""; - }; - 7D3B7C9338829DEF8BC3A52614FF35D6 /* Targets Support Files */ = { - isa = PBXGroup; - children = ( - 3EAAB2CFFBD8598AB3144F85D9EDACD8 /* Pods-Mattermost */, - 6CDFB0049112FA789537AC9B9E94FC6E /* Pods-MattermostTests */, - ); - name = "Targets Support Files"; - sourceTree = ""; - }; - 92B16AFE4649769630669C6CDA9910C9 /* Resources */ = { - isa = PBXGroup; - children = ( - E0545ADDF3E1FF7C1BC19F497787C42B /* WKYTPlayerView.bundle */, - ); - name = Resources; - sourceTree = ""; - }; - 950725B3833769C44391B7E733E759D0 /* XCDYouTubeKit */ = { - isa = PBXGroup; - children = ( - 218BAF448A4CEBF30D9938922CA236C2 /* XCDYouTubeClient.h */, - 46714161B2C62428DB972729952DC352 /* XCDYouTubeClient.m */, - 18BED515670B4F1D15806832C57D59F9 /* XCDYouTubeDashManifestXML.h */, - 7A014D164D90DF54F9BD4AC60868D11D /* XCDYouTubeDashManifestXML.m */, - 162E428722ECD765CEE83EB3D33C71E2 /* XCDYouTubeError.h */, - 724042289C9DC57B313DD893EFD1AA54 /* XCDYouTubeKit.h */, - 510EF6F5EB43790577352F6C8A0718B0 /* XCDYouTubeLogger.h */, - 6F752B1C4CE355EE8A9DA89A25D0AE73 /* XCDYouTubeLogger.m */, - 3DAF995C826738DB2D85603716D91154 /* XCDYouTubeLogger+Private.h */, - A16570331366E84C38260E575C1ACDAF /* XCDYouTubeOperation.h */, - F1766C4F2FDC063F421CA2D4688FED68 /* XCDYouTubePlayerScript.h */, - 8E94B5BEED415E94C72301A86E7A9859 /* XCDYouTubePlayerScript.m */, - BB52596A2D243BBDCE8BCB3CF50A6E20 /* XCDYouTubeVideo.h */, - EFFFFBEC2A07FFA8841667B371ED34E0 /* XCDYouTubeVideo.m */, - 1CBB4B14F0B800EC1527A5353A3AE1CA /* XCDYouTubeVideo+Private.h */, - 281750171C47F186466B86E65956736D /* XCDYouTubeVideoOperation.h */, - BA781F6AB0F7487AD05BC63EA40AB8BF /* XCDYouTubeVideoOperation.m */, - 294543F53C1E8CC7BECE8214C09765EE /* XCDYouTubeVideoPlayerViewController.h */, - DD5E806A91AFE8DD2B077AE695123547 /* XCDYouTubeVideoPlayerViewController.m */, - 8540AD01026C33CA77834BD0EE077CDB /* XCDYouTubeVideoWebpage.h */, - 920F9C3CEECF4C1924F458E3D0428ED8 /* XCDYouTubeVideoWebpage.m */, - E6B7DAED273DD709FF4449921C0817D9 /* Support Files */, - ); - name = XCDYouTubeKit; - path = XCDYouTubeKit; - sourceTree = ""; - }; - A3D27D0A8AF193B0B5A20E3F139F18EE /* Products */ = { - isa = PBXGroup; - children = ( - 7703139D81F98B8B6130B2DAFB831C41 /* libPods-Mattermost.a */, - E99B330F0F9A45FF2E1ACB80DA268841 /* libPods-MattermostTests.a */, - C99CF8DC55235B8BA857A47235948EE7 /* libXCDYouTubeKit.a */, - 6CD50F1ED20084866B7CE47B8F8E4BE4 /* libYoutubePlayer-in-WKWebView.a */, - ); - name = Products; - sourceTree = ""; - }; - B01816E2D09E8070AEE9E96136D917B6 /* iOS */ = { - isa = PBXGroup; - children = ( - 98C181471820795EDEAACBC10B84709A /* JavaScriptCore.framework */, - 15D48C716A411AD7621E7125E083F54A /* MediaPlayer.framework */, - ); - name = iOS; - sourceTree = ""; - }; - C609F4F9C6363362CDC6B91DC2BB8DA7 /* Support Files */ = { - isa = PBXGroup; - children = ( - E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */, - 72206B26A17824A476D334C5612D4142 /* YoutubePlayer-in-WKWebView-dummy.m */, - C70E5870375DC970207B78465E560E64 /* YoutubePlayer-in-WKWebView-prefix.pch */, - ); - name = "Support Files"; - path = "../Target Support Files/YoutubePlayer-in-WKWebView"; - sourceTree = ""; - }; - CF1408CF629C7361332E53B88F7BD30C = { - isa = PBXGroup; - children = ( - 24390EFD555DD124430DFF9724065945 /* Podfile */, - 1628BF05B4CAFDCC3549A101F5A10A17 /* Frameworks */, - 428BC12C5AD7D04CCD5564A473A61275 /* Pods */, - A3D27D0A8AF193B0B5A20E3F139F18EE /* Products */, - 7D3B7C9338829DEF8BC3A52614FF35D6 /* Targets Support Files */, - ); - sourceTree = ""; - }; - E6B7DAED273DD709FF4449921C0817D9 /* Support Files */ = { - isa = PBXGroup; - children = ( - 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */, - CFFE5759AD00493BC5172561B1C1C7CD /* XCDYouTubeKit-dummy.m */, - A43B4ECF27212BC0465EDA19AE3164CC /* XCDYouTubeKit-prefix.pch */, - ); - name = "Support Files"; - path = "../Target Support Files/XCDYouTubeKit"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 121FA629058A6D7550BAA7E46A2DFF6C /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 1B069680B86677A2EA0846201573A2C9 /* XCDYouTubeClient.h in Headers */, - 3933FE5CEDAAFF0AE1DE7413EDA8CA38 /* XCDYouTubeDashManifestXML.h in Headers */, - 4C91B4A27AFBF35591DD3CDF23E2DC17 /* XCDYouTubeError.h in Headers */, - E74005FCD11C34B3705DE02F89D4028B /* XCDYouTubeKit.h in Headers */, - 76BE93DCF558BFA281930A26018F9330 /* XCDYouTubeLogger+Private.h in Headers */, - FB83D8680F87F8315167D84D2BEEDF6C /* XCDYouTubeLogger.h in Headers */, - 19A3D8279379A5ACBE7F699CB120E20C /* XCDYouTubeOperation.h in Headers */, - 90A4DC7BA3E2C3C54D484B8959CFB708 /* XCDYouTubePlayerScript.h in Headers */, - 6A881BEE202BC2AC64ACE1C2E7583756 /* XCDYouTubeVideo+Private.h in Headers */, - 1C5F63B626D206B0ED32320876A3EA60 /* XCDYouTubeVideo.h in Headers */, - 607B50452E483ACCA8FA33B3C0114BFD /* XCDYouTubeVideoOperation.h in Headers */, - EA6A2186A0767AD5C4A45579994658BD /* XCDYouTubeVideoPlayerViewController.h in Headers */, - F99FDC5AFF20BC2A65AEE5A41209A42C /* XCDYouTubeVideoWebpage.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 87BA6E865700D97E7F71638AAE9AFC0D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 946AD953D8D82CF2E2C495E655189FD9 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - B6DE8E634D9D8E06BFF5F56A21C33E98 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 5BE4F6E90A8A1644C0BBB4635AD0D84F /* WKYTPlayerView.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 191A114290607BBCF32141FA83AB9F6E /* Pods-Mattermost */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0A6512CB2B9DF1554E714C059EE642EE /* Build configuration list for PBXNativeTarget "Pods-Mattermost" */; - buildPhases = ( - 946AD953D8D82CF2E2C495E655189FD9 /* Headers */, - 4A0A92E8C79C534671C264B96C2D3980 /* Sources */, - EBE0F2650665138130247C39F808CDFB /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 32D9BECA4EDB7295613C7FBBFF1947E3 /* PBXTargetDependency */, - 12B381BF70C519F8F5E118A06F0715F4 /* PBXTargetDependency */, - ); - name = "Pods-Mattermost"; - productName = "Pods-Mattermost"; - productReference = 7703139D81F98B8B6130B2DAFB831C41 /* libPods-Mattermost.a */; - productType = "com.apple.product-type.library.static"; - }; - 5552A5DF4126A9D12B869B8272B40FF1 /* XCDYouTubeKit */ = { - isa = PBXNativeTarget; - buildConfigurationList = A447275384E1E65FCC685DD176B14980 /* Build configuration list for PBXNativeTarget "XCDYouTubeKit" */; - buildPhases = ( - 121FA629058A6D7550BAA7E46A2DFF6C /* Headers */, - 6E02437F69A69D78682AC11124EF1525 /* Sources */, - 9607CC185A314A5FB24F3B67E3C0B743 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = XCDYouTubeKit; - productName = XCDYouTubeKit; - productReference = C99CF8DC55235B8BA857A47235948EE7 /* libXCDYouTubeKit.a */; - productType = "com.apple.product-type.library.static"; - }; - D5F98C908412D8333DECA6E74A2FC15E /* YoutubePlayer-in-WKWebView */ = { - isa = PBXNativeTarget; - buildConfigurationList = 1F5DC73135AFED29F2A39052787E8E88 /* Build configuration list for PBXNativeTarget "YoutubePlayer-in-WKWebView" */; - buildPhases = ( - B6DE8E634D9D8E06BFF5F56A21C33E98 /* Headers */, - BAD0507591933394D74E430744349DCA /* Sources */, - 3BF9126BA0554EB8C75B0C9B1F5908D5 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "YoutubePlayer-in-WKWebView"; - productName = "YoutubePlayer-in-WKWebView"; - productReference = 6CD50F1ED20084866B7CE47B8F8E4BE4 /* libYoutubePlayer-in-WKWebView.a */; - productType = "com.apple.product-type.library.static"; - }; - DE7906C32BF02EEC471C43D2EE24AA47 /* Pods-MattermostTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 42CD827C438E22B810D149D4E59AC5FC /* Build configuration list for PBXNativeTarget "Pods-MattermostTests" */; - buildPhases = ( - 87BA6E865700D97E7F71638AAE9AFC0D /* Headers */, - 7FF2A957D32E6EED1BD925761A4422B4 /* Sources */, - E909807276D8D8C1F62A49D70C2048F0 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 2FADDBF2AD8381C1F61DFCE1A376150B /* PBXTargetDependency */, - ); - name = "Pods-MattermostTests"; - productName = "Pods-MattermostTests"; - productReference = E99B330F0F9A45FF2E1ACB80DA268841 /* libPods-MattermostTests.a */; - productType = "com.apple.product-type.library.static"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - BFDFE7DC352907FC980B868725387E98 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1100; - LastUpgradeCheck = 1100; - }; - buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = CF1408CF629C7361332E53B88F7BD30C; - productRefGroup = A3D27D0A8AF193B0B5A20E3F139F18EE /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 191A114290607BBCF32141FA83AB9F6E /* Pods-Mattermost */, - DE7906C32BF02EEC471C43D2EE24AA47 /* Pods-MattermostTests */, - 5552A5DF4126A9D12B869B8272B40FF1 /* XCDYouTubeKit */, - D5F98C908412D8333DECA6E74A2FC15E /* YoutubePlayer-in-WKWebView */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 4A0A92E8C79C534671C264B96C2D3980 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - CA1EF66BB4AA70EFF1869D1FE0A373AE /* Pods-Mattermost-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 6E02437F69A69D78682AC11124EF1525 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 8433D3049BEEF12D212D6B1DD6DD0512 /* XCDYouTubeClient.m in Sources */, - 32BC55AFE9DADF2BE266AB44A9F9C382 /* XCDYouTubeDashManifestXML.m in Sources */, - 3F0B35BE6C13ADDF8364D0E9F7A07522 /* XCDYouTubeKit-dummy.m in Sources */, - C50CA7A8B2F6691C7E29BD7BC2733975 /* XCDYouTubeLogger.m in Sources */, - 7621E888B2755702A10B6724E4107332 /* XCDYouTubePlayerScript.m in Sources */, - 77F5E0033E60972EC230A8ABD78071A3 /* XCDYouTubeVideo.m in Sources */, - 5ED62229AE2031D59F1DF033F77D03B2 /* XCDYouTubeVideoOperation.m in Sources */, - 2F6113A993C9CA73926D2946728A7D37 /* XCDYouTubeVideoPlayerViewController.m in Sources */, - 5B8AEF4A577D21542D6F8DE2B248C8E7 /* XCDYouTubeVideoWebpage.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7FF2A957D32E6EED1BD925761A4422B4 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 00EF717F2CE5C08CE046FAB25A49C579 /* Pods-MattermostTests-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BAD0507591933394D74E430744349DCA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D56363E3D25AE7C3C37948A2ED267EFC /* WKYTPlayerView.m in Sources */, - 80627FF4ECD0A58F8A6C2874FA612EB1 /* YoutubePlayer-in-WKWebView-dummy.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 12B381BF70C519F8F5E118A06F0715F4 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "YoutubePlayer-in-WKWebView"; - target = D5F98C908412D8333DECA6E74A2FC15E /* YoutubePlayer-in-WKWebView */; - targetProxy = DB4FDEE7D3C70BEC4323EBE859F93B11 /* PBXContainerItemProxy */; - }; - 2FADDBF2AD8381C1F61DFCE1A376150B /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "Pods-Mattermost"; - target = 191A114290607BBCF32141FA83AB9F6E /* Pods-Mattermost */; - targetProxy = 165E90C48BE6DE526DE6EFE88F787BE2 /* PBXContainerItemProxy */; - }; - 32D9BECA4EDB7295613C7FBBFF1947E3 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = XCDYouTubeKit; - target = 5552A5DF4126A9D12B869B8272B40FF1 /* XCDYouTubeKit */; - targetProxy = A2AEF4FA0A01D420BBD3AC4B1428E3FD /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 0430B9AA2C1C5F6D02D86CB026100EE8 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - GCC_PREFIX_HEADER = "Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PRIVATE_HEADERS_FOLDER_PATH = ""; - PRODUCT_MODULE_NAME = YoutubePlayer_in_WKWebView; - PRODUCT_NAME = "YoutubePlayer-in-WKWebView"; - PUBLIC_HEADERS_FOLDER_PATH = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 161EADF999CB6B2B796E0054E074569D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 60FB83EAEF578DD0C2DE316FCF4322A9 /* Pods-MattermostTests.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MACH_O_TYPE = staticlib; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 2D52F03349CA866E882EB87084FF9D21 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGNING_ALLOWED = NO; - CODE_SIGNING_REQUIRED = NO; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_DEBUG=1", - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Debug; - }; - 3536F4235A4BE9CAF9A3A90CECC64AD5 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E4748ACD84B23087E6F590E0904FFEFB /* YoutubePlayer-in-WKWebView.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - GCC_PREFIX_HEADER = "Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PRIVATE_HEADERS_FOLDER_PATH = ""; - PRODUCT_MODULE_NAME = YoutubePlayer_in_WKWebView; - PRODUCT_NAME = "YoutubePlayer-in-WKWebView"; - PUBLIC_HEADERS_FOLDER_PATH = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 45C68B99EB6B81E0DD1319B540AF6A4D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - GCC_PREFIX_HEADER = "Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PRIVATE_HEADERS_FOLDER_PATH = ""; - PRODUCT_MODULE_NAME = XCDYouTubeKit; - PRODUCT_NAME = XCDYouTubeKit; - PUBLIC_HEADERS_FOLDER_PATH = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 50A87C79F1E5C7C6BFADB42D9E50A087 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 377B2C391CA94B7612EDC96C2306DB48 /* Pods-MattermostTests.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MACH_O_TYPE = staticlib; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 682764580BDEF953B0E72A847F082705 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 934A4B9575F4CBDCBE7B044C1F049365 /* Pods-Mattermost.debug.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MACH_O_TYPE = staticlib; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 86B3C2B708242AD4B00334BBE7D14EFB /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3BC2AF7C034FA52EAB440AC387B0F3E9 /* XCDYouTubeKit.xcconfig */; - buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - GCC_PREFIX_HEADER = "Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PRIVATE_HEADERS_FOLDER_PATH = ""; - PRODUCT_MODULE_NAME = XCDYouTubeKit; - PRODUCT_NAME = XCDYouTubeKit; - PUBLIC_HEADERS_FOLDER_PATH = ""; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; - SWIFT_VERSION = 4.2; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - A0A8AE5FB77A90328CE173082A521CA4 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = B564ED8191C54D3CAD8D2A2A6B9B9D1B /* Pods-Mattermost.release.xcconfig */; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; - CLANG_ENABLE_OBJC_WEAK = NO; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MACH_O_TYPE = staticlib; - OTHER_LDFLAGS = ""; - OTHER_LIBTOOLFLAGS = ""; - PODS_ROOT = "$(SRCROOT)"; - PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - B45EE3CA30FA810239EAC87C682A2A52 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGNING_ALLOWED = NO; - CODE_SIGNING_REQUIRED = NO; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "POD_CONFIGURATION_RELEASE=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRIP_INSTALLED_PRODUCT = NO; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - SWIFT_VERSION = 5.0; - SYMROOT = "${SRCROOT}/../build"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0A6512CB2B9DF1554E714C059EE642EE /* Build configuration list for PBXNativeTarget "Pods-Mattermost" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 682764580BDEF953B0E72A847F082705 /* Debug */, - A0A8AE5FB77A90328CE173082A521CA4 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 1F5DC73135AFED29F2A39052787E8E88 /* Build configuration list for PBXNativeTarget "YoutubePlayer-in-WKWebView" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3536F4235A4BE9CAF9A3A90CECC64AD5 /* Debug */, - 0430B9AA2C1C5F6D02D86CB026100EE8 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 42CD827C438E22B810D149D4E59AC5FC /* Build configuration list for PBXNativeTarget "Pods-MattermostTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 161EADF999CB6B2B796E0054E074569D /* Debug */, - 50A87C79F1E5C7C6BFADB42D9E50A087 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 2D52F03349CA866E882EB87084FF9D21 /* Debug */, - B45EE3CA30FA810239EAC87C682A2A52 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A447275384E1E65FCC685DD176B14980 /* Build configuration list for PBXNativeTarget "XCDYouTubeKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 45C68B99EB6B81E0DD1319B540AF6A4D /* Debug */, - 86B3C2B708242AD4B00334BBE7D14EFB /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; -} diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown deleted file mode 100644 index 8f762b302..000000000 --- a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.markdown +++ /dev/null @@ -1,46 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: - -## XCDYouTubeKit - -The MIT License (MIT) - -Copyright (c) 2013-2016 Cédric Luthi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -## YoutubePlayer-in-WKWebView - -Copyright 2014 Google Inc. All rights reserved. - -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. - -Generated by CocoaPods - https://cocoapods.org diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist deleted file mode 100644 index 5935b6519..000000000 --- a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-acknowledgements.plist +++ /dev/null @@ -1,84 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - The MIT License (MIT) - -Copyright (c) 2013-2016 Cédric Luthi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - License - MIT - Title - XCDYouTubeKit - Type - PSGroupSpecifier - - - FooterText - Copyright 2014 Google Inc. All rights reserved. - -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. - - License - Apache - Title - YoutubePlayer-in-WKWebView - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m deleted file mode 100644 index 8ec219786..000000000 --- a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_Mattermost : NSObject -@end -@implementation PodsDummy_Pods_Mattermost -@end diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh deleted file mode 100755 index 08e3eaaca..000000000 --- a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} - -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - if [ -r "$source" ]; then - # Copy the dSYM into a the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .framework.dSYM "$source")" - binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then - strip_invalid_archs "$binary" - fi - - if [[ $STRIP_BINARY_RETVAL == 1 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" - fi - fi -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identitiy - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - STRIP_BINARY_RETVAL=0 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" || exit 1 - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=1 -} - -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh deleted file mode 100755 index 40b59bd7a..000000000 --- a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then - # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy - # resources to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - -RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt -> "$RESOURCES_TO_COPY" - -XCASSET_FILES=() - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -case "${TARGETED_DEVICE_FAMILY:-}" in - 1,2) - TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" - ;; - 1) - TARGET_DEVICE_ARGS="--target-device iphone" - ;; - 2) - TARGET_DEVICE_ARGS="--target-device ipad" - ;; - 3) - TARGET_DEVICE_ARGS="--target-device tv" - ;; - 4) - TARGET_DEVICE_ARGS="--target-device watch" - ;; - *) - TARGET_DEVICE_ARGS="--target-device mac" - ;; -esac - -install_resource() -{ - if [[ "$1" = /* ]] ; then - RESOURCE_PATH="$1" - else - RESOURCE_PATH="${PODS_ROOT}/$1" - fi - if [[ ! -e "$RESOURCE_PATH" ]] ; then - cat << EOM -error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. -EOM - exit 1 - fi - case $RESOURCE_PATH in - *.storyboard) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true - ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} - ;; - *.xib) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true - ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} - ;; - *.framework) - echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true - mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - ;; - *.xcdatamodel) - echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true - xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" - ;; - *.xcdatamodeld) - echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true - xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" - ;; - *.xcmappingmodel) - echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true - xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" - ;; - *.xcassets) - ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" - XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") - ;; - *) - echo "$RESOURCE_PATH" || true - echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" - ;; - esac -} -if [[ "$CONFIGURATION" == "Debug" ]]; then - install_resource "${PODS_ROOT}/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle" -fi -if [[ "$CONFIGURATION" == "Release" ]]; then - install_resource "${PODS_ROOT}/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle" -fi - -mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then - mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi -rm -f "$RESOURCES_TO_COPY" - -if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] -then - # Find all other xcassets (this unfortunately includes those of path pods and other targets). - OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) - while read line; do - if [[ $line != "${PODS_ROOT}*" ]]; then - XCASSET_FILES+=("$line") - fi - done <<<"$OTHER_XCASSETS" - - if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - else - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" - fi -fi diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig deleted file mode 100644 index eb4f27eb5..000000000 --- a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig +++ /dev/null @@ -1,9 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" -OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -OTHER_LDFLAGS = $(inherited) -ObjC -l"XCDYouTubeKit" -l"YoutubePlayer-in-WKWebView" -framework "JavaScriptCore" -framework "MediaPlayer" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig b/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig deleted file mode 100644 index eb4f27eb5..000000000 --- a/ios/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig +++ /dev/null @@ -1,9 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" -OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -OTHER_LDFLAGS = $(inherited) -ObjC -l"XCDYouTubeKit" -l"YoutubePlayer-in-WKWebView" -framework "JavaScriptCore" -framework "MediaPlayer" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown deleted file mode 100644 index 102af7538..000000000 --- a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.markdown +++ /dev/null @@ -1,3 +0,0 @@ -# Acknowledgements -This application makes use of the following third party libraries: -Generated by CocoaPods - https://cocoapods.org diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist deleted file mode 100644 index 7acbad1ea..000000000 --- a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-acknowledgements.plist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - PreferenceSpecifiers - - - FooterText - This application makes use of the following third party libraries: - Title - Acknowledgements - Type - PSGroupSpecifier - - - FooterText - Generated by CocoaPods - https://cocoapods.org - Title - - Type - PSGroupSpecifier - - - StringsTable - Acknowledgements - Title - Acknowledgements - - diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m deleted file mode 100644 index a018bb5d5..000000000 --- a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_Pods_MattermostTests : NSObject -@end -@implementation PodsDummy_Pods_MattermostTests -@end diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh deleted file mode 100755 index 08e3eaaca..000000000 --- a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then - # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy - # frameworks to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" -mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - -COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" -SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" - -# Used as a return value for each invocation of `strip_invalid_archs` function. -STRIP_BINARY_RETVAL=0 - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -# Copies and strips a vendored framework -install_framework() -{ - if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then - local source="${BUILT_PRODUCTS_DIR}/$1" - elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then - local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" - elif [ -r "$1" ]; then - local source="$1" - fi - - local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - - if [ -L "${source}" ]; then - echo "Symlinked..." - source="$(readlink "${source}")" - fi - - # Use filter instead of exclude so missing patterns don't throw errors. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" - - local basename - basename="$(basename -s .framework "$1")" - binary="${destination}/${basename}.framework/${basename}" - if ! [ -r "$binary" ]; then - binary="${destination}/${basename}" - fi - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then - strip_invalid_archs "$binary" - fi - - # Resign the code if required by the build settings to avoid unstable apps - code_sign_if_enabled "${destination}/$(basename "$1")" - - # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. - if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then - local swift_runtime_libs - swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) - for lib in $swift_runtime_libs; do - echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" - rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" - code_sign_if_enabled "${destination}/${lib}" - done - fi -} - -# Copies and strips a vendored dSYM -install_dsym() { - local source="$1" - if [ -r "$source" ]; then - # Copy the dSYM into a the targets temp dir. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" - - local basename - basename="$(basename -s .framework.dSYM "$source")" - binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" - - # Strip invalid architectures so "fat" simulator / device frameworks work on device - if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then - strip_invalid_archs "$binary" - fi - - if [[ $STRIP_BINARY_RETVAL == 1 ]]; then - # Move the stripped file into its final destination. - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" - else - # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. - touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" - fi - fi -} - -# Signs a framework with the provided identity -code_sign_if_enabled() { - if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then - # Use the current code_sign_identitiy - echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" - local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" - - if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - code_sign_cmd="$code_sign_cmd &" - fi - echo "$code_sign_cmd" - eval "$code_sign_cmd" - fi -} - -# Strip invalid architectures -strip_invalid_archs() { - binary="$1" - # Get architectures for current target binary - binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" - # Intersect them with the architectures we are building for - intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" - # If there are no archs supported by this binary then warn the user - if [[ -z "$intersected_archs" ]]; then - echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." - STRIP_BINARY_RETVAL=0 - return - fi - stripped="" - for arch in $binary_archs; do - if ! [[ "${ARCHS}" == *"$arch"* ]]; then - # Strip non-valid architectures in-place - lipo -remove "$arch" -output "$binary" "$binary" || exit 1 - stripped="$stripped $arch" - fi - done - if [[ "$stripped" ]]; then - echo "Stripped $binary of architectures:$stripped" - fi - STRIP_BINARY_RETVAL=1 -} - -if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then - wait -fi diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh deleted file mode 100755 index 345301f2c..000000000 --- a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/sh -set -e -set -u -set -o pipefail - -if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then - # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy - # resources to, so exit 0 (signalling the script phase was successful). - exit 0 -fi - -mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - -RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt -> "$RESOURCES_TO_COPY" - -XCASSET_FILES=() - -# This protects against multiple targets copying the same framework dependency at the same time. The solution -# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html -RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") - -case "${TARGETED_DEVICE_FAMILY:-}" in - 1,2) - TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" - ;; - 1) - TARGET_DEVICE_ARGS="--target-device iphone" - ;; - 2) - TARGET_DEVICE_ARGS="--target-device ipad" - ;; - 3) - TARGET_DEVICE_ARGS="--target-device tv" - ;; - 4) - TARGET_DEVICE_ARGS="--target-device watch" - ;; - *) - TARGET_DEVICE_ARGS="--target-device mac" - ;; -esac - -install_resource() -{ - if [[ "$1" = /* ]] ; then - RESOURCE_PATH="$1" - else - RESOURCE_PATH="${PODS_ROOT}/$1" - fi - if [[ ! -e "$RESOURCE_PATH" ]] ; then - cat << EOM -error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. -EOM - exit 1 - fi - case $RESOURCE_PATH in - *.storyboard) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true - ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} - ;; - *.xib) - echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true - ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} - ;; - *.framework) - echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true - mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true - rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" - ;; - *.xcdatamodel) - echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true - xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" - ;; - *.xcdatamodeld) - echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true - xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" - ;; - *.xcmappingmodel) - echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true - xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" - ;; - *.xcassets) - ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" - XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") - ;; - *) - echo "$RESOURCE_PATH" || true - echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" - ;; - esac -} - -mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then - mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" -fi -rm -f "$RESOURCES_TO_COPY" - -if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] -then - # Find all other xcassets (this unfortunately includes those of path pods and other targets). - OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) - while read line; do - if [[ $line != "${PODS_ROOT}*" ]]; then - XCASSET_FILES+=("$line") - fi - done <<<"$OTHER_XCASSETS" - - if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" - else - printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" - fi -fi diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig deleted file mode 100644 index eb5fde866..000000000 --- a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig +++ /dev/null @@ -1,9 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" -OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -OTHER_LDFLAGS = $(inherited) -ObjC -framework "JavaScriptCore" -framework "MediaPlayer" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig b/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig deleted file mode 100644 index eb5fde866..000000000 --- a/ios/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig +++ /dev/null @@ -1,9 +0,0 @@ -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit" "${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView" -OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -isystem "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -OTHER_LDFLAGS = $(inherited) -ObjC -framework "JavaScriptCore" -framework "MediaPlayer" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_PODFILE_DIR_PATH = ${SRCROOT}/. -PODS_ROOT = ${SRCROOT}/Pods diff --git a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m deleted file mode 100644 index 218334986..000000000 --- a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_XCDYouTubeKit : NSObject -@end -@implementation PodsDummy_XCDYouTubeKit -@end diff --git a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch deleted file mode 100644 index beb2a2441..000000000 --- a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig b/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig deleted file mode 100644 index af4d7a935..000000000 --- a/ios/Pods/Target Support Files/XCDYouTubeKit/XCDYouTubeKit.xcconfig +++ /dev/null @@ -1,10 +0,0 @@ -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/XCDYouTubeKit -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/XCDYouTubeKit" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/XCDYouTubeKit" -OTHER_LDFLAGS = -framework "JavaScriptCore" -framework "MediaPlayer" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/XCDYouTubeKit -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES diff --git a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m deleted file mode 100644 index a5085282e..000000000 --- a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-dummy.m +++ /dev/null @@ -1,5 +0,0 @@ -#import -@interface PodsDummy_YoutubePlayer_in_WKWebView : NSObject -@end -@implementation PodsDummy_YoutubePlayer_in_WKWebView -@end diff --git a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch deleted file mode 100644 index beb2a2441..000000000 --- a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView-prefix.pch +++ /dev/null @@ -1,12 +0,0 @@ -#ifdef __OBJC__ -#import -#else -#ifndef FOUNDATION_EXPORT -#if defined(__cplusplus) -#define FOUNDATION_EXPORT extern "C" -#else -#define FOUNDATION_EXPORT extern -#endif -#endif -#endif - diff --git a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig b/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig deleted file mode 100644 index ac30dae33..000000000 --- a/ios/Pods/Target Support Files/YoutubePlayer-in-WKWebView/YoutubePlayer-in-WKWebView.xcconfig +++ /dev/null @@ -1,9 +0,0 @@ -CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YoutubePlayer-in-WKWebView -GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 -HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/YoutubePlayer-in-WKWebView" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/YoutubePlayer-in-WKWebView" -PODS_BUILD_DIR = ${BUILD_DIR} -PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) -PODS_ROOT = ${SRCROOT} -PODS_TARGET_SRCROOT = ${PODS_ROOT}/YoutubePlayer-in-WKWebView -PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} -SKIP_INSTALL = YES diff --git a/ios/Pods/XCDYouTubeKit/LICENSE b/ios/Pods/XCDYouTubeKit/LICENSE deleted file mode 100644 index e21050fe2..000000000 --- a/ios/Pods/XCDYouTubeKit/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2016 Cédric Luthi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/ios/Pods/XCDYouTubeKit/README.md b/ios/Pods/XCDYouTubeKit/README.md deleted file mode 100644 index 71f688c3a..000000000 --- a/ios/Pods/XCDYouTubeKit/README.md +++ /dev/null @@ -1,151 +0,0 @@ -## About - -[![Build Status](https://img.shields.io/circleci/project/0xced/XCDYouTubeKit/develop.svg?style=flat)](https://circleci.com/gh/0xced/XCDYouTubeKit) -[![Coverage Status](https://img.shields.io/codecov/c/github/0xced/XCDYouTubeKit/develop.svg?style=flat)](https://codecov.io/gh/0xced/XCDYouTubeKit/branch/develop) -[![Platform](https://img.shields.io/cocoapods/p/XCDYouTubeKit.svg?style=flat)](http://cocoadocs.org/docsets/XCDYouTubeKit/) -[![Pod Version](https://img.shields.io/cocoapods/v/XCDYouTubeKit.svg?style=flat)](https://cocoapods.org/pods/XCDYouTubeKit) -[![Carthage Compatibility](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage/) -[![License](https://img.shields.io/cocoapods/l/XCDYouTubeKit.svg?style=flat)](LICENSE) - -**XCDYouTubeKit** is a YouTube video player for iOS, tvOS and macOS. - - - -Are you enjoying XCDYouTubeKit? You can say thank you with [a tweet](https://twitter.com/intent/tweet?text=%400xced%20Thank%20you%20for%20XCDYouTubeKit%2E). I am also accepting donations. ;-) - -[![Donate button](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MGEPRSNQFMV3W) - -## Requirements - -- Runs on iOS 8.0 and later -- Runs on macOS 10.9 and later -- Runs on tvOS 9.0 and later - -## Warning - -XCDYouTubeKit is against the YouTube [Terms of Service](https://www.youtube.com/t/terms). The only *official* way of playing a YouTube video inside an app is with a web view and the [iframe player API](https://developers.google.com/youtube/iframe_api_reference). Unfortunately, this is very slow and quite ugly, so I wrote this player to give users a better viewing experience. - -## Installation - -XCDYouTubeKit is available through CocoaPods and Carthage. - -CocoaPods: - -```ruby -pod "XCDYouTubeKit", "~> 2.7" -``` - -Carthage: - -```objc -github "0xced/XCDYouTubeKit" ~> 2.7 -``` - -Alternatively, you can manually use the provided static library or dynamic framework. In order to use the static library, you must: - -1. Create a workspace (File → New → Workspace…) -2. Add your project to the workspace -3. Add the XCDYouTubeKit project to the workspace -4. Drag and drop the `libXCDYouTubeKit.a` file referenced from XCDYouTubeKit → Products → libXCDYouTubeKit.a into the *Link Binary With Libraries* build phase of your app’s target. - -These steps will ensure that `#import ` will work properly in your project. - -## Usage - -XCDYouTubeKit is [fully documented](http://cocoadocs.org/docsets/XCDYouTubeKit/). - -### iOS 8.0+ & tvOS (AVPlayerViewController) - -```objc -AVPlayerViewController *playerViewController = [AVPlayerViewController new]; -[self presentViewController:playerViewController animated:YES completion:nil]; - -__weak AVPlayerViewController *weakPlayerViewController = playerViewController; -[[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo * _Nullable video, NSError * _Nullable error) { - if (video) - { - NSDictionary *streamURLs = video.streamURLs; - NSURL *streamURL = streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming] ?: streamURLs[@(XCDYouTubeVideoQualityHD720)] ?: streamURLs[@(XCDYouTubeVideoQualityMedium360)] ?: streamURLs[@(XCDYouTubeVideoQualitySmall240)]; - weakPlayerViewController.player = [AVPlayer playerWithURL:streamURL]; - [weakPlayerViewController.player play]; - } - else - { - [self dismissViewControllerAnimated:YES completion:nil]; - } -}]; -``` - -### iOS, tvOS and macOS - -```objc -NSString *videoIdentifier = @"9bZkp7q19f0"; // A 11 characters YouTube video identifier -[[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) { - if (video) - { - // Do something with the `video` object - } - else - { - // Handle error - } -}]; -``` - -### iOS 8.0 - -On iOS, you can use the class `XCDYouTubeVideoPlayerViewController` the same way you use a `MPMoviePlayerViewController`, except you initialize it with a YouTube video identifier instead of a content URL. - -#### Present the video in full-screen - -```objc -- (void) playVideo -{ - XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayerViewController.moviePlayer]; - [self presentMoviePlayerViewControllerAnimated:videoPlayerViewController]; -} - -- (void) moviePlayerPlaybackDidFinish:(NSNotification *)notification -{ - [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:notification.object]; - MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; - if (finishReason == MPMovieFinishReasonPlaybackError) - { - NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; - // Handle error - } -} - -``` - -#### Present the video in a non full-screen view - -```objc -XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"9bZkp7q19f0"]; -[videoPlayerViewController presentInView:self.videoContainerView]; -[videoPlayerViewController.moviePlayer play]; -``` - -See the demo project for more sample code. - -## Logging - -Since version 2.2.0, XCDYouTubeKit produces logs. XCDYouTubeKit supports [CocoaLumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack) but does not require it. - -See the `XCDYouTubeLogger` class [documentation](http://cocoadocs.org/docsets/XCDYouTubeKit/) for more information. - -## Credits - -The URL extraction algorithms in *XCDYouTubeKit* are inspired by the [YouTube extractor](https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py) module of the *youtube-dl* project. - -## Contact - -Cédric Luthi - -- http://github.com/0xced -- http://twitter.com/0xced - -## License - -XCDYouTubeKit is available under the MIT license. See the [LICENSE](LICENSE) file for more information. diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h deleted file mode 100644 index 2b89201c3..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.h +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#if !__has_feature(nullability) -#define NS_ASSUME_NONNULL_BEGIN -#define NS_ASSUME_NONNULL_END -#define nullable -#define __nullable -#endif - -#import - -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * The `XCDYouTubeClient` class is responsible for interacting with the YouTube API. Given a YouTube video identifier, you will get video information with the `<-getVideoWithIdentifier:completionHandler:>` method. - * - * On iOS, you probably don’t want to use `XCDYouTubeClient` directly but the higher level class ``. - */ -@interface XCDYouTubeClient : NSObject - -/** - * ------------------ - * @name Initializing - * ------------------ - */ - -/** - * Returns the shared client with the default language, i.e. the preferred language of the main bundle. - * - * @return The default client. - */ -+ (instancetype) defaultClient; - -/** - * Initializes a client with the specified language identifier. - * - * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier, the preferred language of the main bundle will be used. - * - * @return A client with the specified language identifier. - */ -- (instancetype) initWithLanguageIdentifier:(nullable NSString *)languageIdentifier; - -/** - * --------------------------------- - * @name Accessing client properties - * --------------------------------- - */ - -/** - * The language identifier of the client, used for error localization. - * - * @see -initWithLanguageIdentifier: - */ -@property (nonatomic, readonly) NSString *languageIdentifier; - -/** - * -------------------------------------- - * @name Interacting with the YouTube API - * -------------------------------------- - */ - -/** - * Starts an asynchronous operation for the specified video identifier, and calls a handler upon completion. - * - * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid (including nil) the completion handler will be called with an error with `XCDYouTubeVideoErrorDomain` domain and `XCDYouTubeErrorInvalidVideoIdentifier` code. - * @param completionHandler A block to execute when the client finishes the operation. The completion handler is executed on the main thread. If the completion handler is nil, this method throws an exception. - * - * @discussion If the operation completes successfully, the video parameter of the handler block contains a `` object, and the error parameter is nil. If the operation fails, the video parameter is nil and the error parameter contains information about the failure. The error's domain is always `XCDYouTubeVideoErrorDomain`. - * - * @see XCDYouTubeErrorCode - * - * @return An opaque object conforming to the `` protocol for canceling the asynchronous video information operation. If you call the `cancel` method before the operation is finished, the completion handler will not be called. It is recommended that you store this opaque object as a weak property. - */ -- (id) getVideoWithIdentifier:(nullable NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler; - -/** - * Starts an asynchronous operation for the specified video identifier, and calls a handler upon completion. - * - * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid (including nil) the completion handler will be called with an error with `XCDYouTubeVideoErrorDomain` domain and `XCDYouTubeErrorInvalidVideoIdentifier` code. - * @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. - * @param completionHandler A block to execute when the client finishes the operation. The completion handler is executed on the main thread. If the completion handler is nil, this method throws an exception. - * - * @discussion If the operation completes successfully, the video parameter of the handler block contains a `` object, and the error parameter is nil. If the operation fails, the video parameter is nil and the error parameter contains information about the failure. The error's domain is always `XCDYouTubeVideoErrorDomain`. - * - * @see XCDYouTubeErrorCode - * - * @return An opaque object conforming to the `` protocol for canceling the asynchronous video information operation. If you call the `cancel` method before the operation is finished, the completion handler will not be called. It is recommended that you store this opaque object as a weak property. - */ -- (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(nullable NSArray *)cookies completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler; -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m deleted file mode 100644 index 95ff0053c..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeClient.m +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubeClient.h" - -#import "XCDYouTubeVideoOperation.h" - -@interface XCDYouTubeClient () -@property (nonatomic, strong) NSOperationQueue *queue; -@end - -@implementation XCDYouTubeClient - -@synthesize languageIdentifier = _languageIdentifier; - -+ (instancetype) defaultClient -{ - static XCDYouTubeClient *defaultClient; - static dispatch_once_t once; - dispatch_once(&once, ^{ - defaultClient = [self new]; - }); - return defaultClient; -} - -- (instancetype) init -{ - return [self initWithLanguageIdentifier:nil]; -} - -- (instancetype) initWithLanguageIdentifier:(NSString *)languageIdentifier -{ - if (!(self = [super init])) - return nil; // LCOV_EXCL_LINE - - _languageIdentifier = ^{ - return languageIdentifier ?: ^{ - NSArray *preferredLocalizations = [[NSBundle mainBundle] preferredLocalizations]; - NSString *preferredLocalization = preferredLocalizations.firstObject ?: @"en"; - return [NSLocale canonicalLanguageIdentifierFromString:preferredLocalization] ?: @"en"; - }(); - }(); - - _queue = [NSOperationQueue new]; - _queue.maxConcurrentOperationCount = 6; // paul_irish: Chrome re-confirmed that the 6 connections-per-host limit is the right magic number: https://code.google.com/p/chromium/issues/detail?id=285567#c14 [https://twitter.com/paul_irish/status/422808635698212864] - _queue.name = NSStringFromClass(self.class); - - return self; -} - -- (id) getVideoWithIdentifier:(NSString *)videoIdentifier cookies:(NSArray *)cookies completionHandler:(void (^)(XCDYouTubeVideo * _Nullable, NSError * _Nullable))completionHandler -{ - if (!completionHandler) - @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The `completionHandler` argument must not be nil." userInfo:nil]; - - XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier cookies:cookies]; - operation.completionBlock = ^{ - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-retain-cycles" - if (operation.video || operation.error) - { - NSAssert(!(operation.video && operation.error), @"One of `video` or `error` must be nil."); - completionHandler(operation.video, operation.error); - } - else - { - NSAssert(operation.isCancelled, @"Both `video` and `error` can not be nil if the operation was not canceled."); - } - operation.completionBlock = nil; -#pragma clang diagnostic pop - }]; - }; - [self.queue addOperation:operation]; - return operation; -} -- (id) getVideoWithIdentifier:(NSString *)videoIdentifier completionHandler:(void (^)(XCDYouTubeVideo * __nullable video, NSError * __nullable error))completionHandler -{ - return [self getVideoWithIdentifier:videoIdentifier cookies:nil completionHandler:completionHandler]; -} - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h deleted file mode 100644 index 90658ecb0..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// XCDYouTubeDashManifestXML.h -// XCDYouTubeKit -// -// Created by Soneé John on 10/24/17. -// Copyright © 2017 Cédric Luthi. All rights reserved. -// - -#import -__attribute__((visibility("hidden"))) -@interface XCDYouTubeDashManifestXML : NSObject - -- (instancetype)initWithXMLString:(NSString *)XMLString; - -- (NSDictionary *)streamURLs; - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m deleted file mode 100644 index ea7565645..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeDashManifestXML.m +++ /dev/null @@ -1,98 +0,0 @@ -// -// XCDYouTubeDashManifestXML.m -// XCDYouTubeKit -// -// Created by Soneé John on 10/24/17. -// Copyright © 2017 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubeDashManifestXML.h" - -@interface XCDYouTubeDashManifestXML() -@property (nonatomic, readonly) NSString *XMLString; -@end - - -@implementation XCDYouTubeDashManifestXML - -- (instancetype)initWithXMLString:(NSString *)XMLString -{ - if (!(self = [super init])) - return nil; // LCOV_EXCL_LINE - - _XMLString = XMLString; - - return self; -} - -- (NSDictionary *)streamURLs -{ - - //Catch the type - NSError *xmlTypeRegexError = NULL; - NSRegularExpression *xmlTypeRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=(type=\"))(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=(\"))" options:NSRegularExpressionAnchorsMatchLines error:&xmlTypeRegexError]; - if (xmlTypeRegexError) - return nil; - NSTextCheckingResult *xmlTypeRegexCheckingResult = [xmlTypeRegex firstMatchInString:self.XMLString options:0 range:NSMakeRange(0, self.XMLString.length)]; - NSString *xmlType = [self.XMLString substringWithRange:xmlTypeRegexCheckingResult.range]; - - NSRange staticRange = [xmlType rangeOfString:@"static" options:0]; - if (staticRange.location == NSNotFound) - return nil; - - //Do not process manifests that have DRM protection - NSRange contentProtectionRange = [self.XMLString rangeOfString:@"ContentProtection" options:0]; - NSRange mp4ProtectionRange = [self.XMLString rangeOfString:@"mp4protection" options:0]; - if (contentProtectionRange.location != NSNotFound || mp4ProtectionRange.location != NSNotFound) - return nil; - - //Catch all URLs - NSError *error = nil; - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=())(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=())" options:0 error:&error]; - - if (error) - return nil; - - NSArray *checkingResults = [regex matchesInString:self.XMLString options:0 range:NSMakeRange(0, [self.XMLString length])]; - - if (checkingResults.count == 0 || checkingResults == nil) - return nil; - - NSMutableArray *URLs = [NSMutableArray new]; - NSMutableDictionary *streamURLs = [NSMutableDictionary new]; - - for (NSTextCheckingResult *checkingResult in checkingResults) - { - NSString* substringForMatch = [self.XMLString substringWithRange:checkingResult.range]; - NSURL *url = [NSURL URLWithString:substringForMatch]; - - NSRange youtubeRange = [url.absoluteString rangeOfString:@"youtube" options:0]; - NSRange itagnRange = [url.absoluteString rangeOfString:@"itag" options:0]; - - if (youtubeRange.location != NSNotFound && itagnRange.location != NSNotFound ) - { - [URLs addObject:url]; - } - } - - for (NSURL *url in URLs) - { - NSError *itagRegexError = nil; - NSRegularExpression *itagRegex = [NSRegularExpression regularExpressionWithPattern:@"(?<=(/itag/))(\\w|\\d|\\n|[().,\\-:;@#$%^&*\\[\\]\"'+–/\\/®°⁰!?{}|`~]| )+?(?=(/))" options:NSRegularExpressionAnchorsMatchLines error:&itagRegexError]; - - if (itagRegexError) - continue; - - NSTextCheckingResult *itagCheckingResult = [itagRegex firstMatchInString:(NSString *_Nonnull)url.absoluteString options:0 range:NSMakeRange(0, url.absoluteString.length)]; - - NSString *itag = [url.absoluteString substringWithRange:itagCheckingResult.range]; - streamURLs[@(itag.integerValue)] = url; - } - - if (streamURLs.count == 0) - return nil; - - return streamURLs; -} - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h deleted file mode 100644 index 92ffe38e5..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeError.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -/** - * The error domain used throughout XCDYouTubeKit. - */ -extern NSString *const XCDYouTubeVideoErrorDomain; - -/** - * A key that may be present in the error's userInfo dictionary when the error code is XCDYouTubeErrorRestrictedPlayback. - * The object for that key is a NSSet instance containing localized country names. - */ -extern NSString *const XCDYouTubeAllowedCountriesUserInfoKey; - -/** - * These values are returned as the error code property of an NSError object with the domain `XCDYouTubeVideoErrorDomain`. - */ -typedef NS_ENUM(NSInteger, XCDYouTubeErrorCode) { - /** - * Returned when no suitable video stream is available. - */ - XCDYouTubeErrorNoStreamAvailable = -2, - - /** - * Returned when a network error occurs. See `NSUnderlyingErrorKey` in the userInfo dictionary for more information. - */ - XCDYouTubeErrorNetwork = -1, - - /** - * Returned when the given video identifier string is invalid. - */ - XCDYouTubeErrorInvalidVideoIdentifier = 2, - - /** - * Previously returned when the video was removed as a violation of YouTube's policy or when the video did not exist. - * Now replaced by code 150, i.e. `XCDYouTubeErrorRestrictedPlayback`. - */ - XCDYouTubeErrorRemovedVideo DEPRECATED_MSG_ATTRIBUTE("YouTube has stopped using error code 100.") = 100, - - /** - * Returned when the video is not playable because of legal reasons or when the video is private. - */ - XCDYouTubeErrorRestrictedPlayback = 150 -}; diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h deleted file mode 100644 index 250ef88f2..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeKit.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -#import -#import -#import -#import -#import -#import - -#if TARGET_OS_IOS || (!defined(TARGET_OS_IOS) && TARGET_OS_IPHONE) -#import -#endif diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h deleted file mode 100644 index 65612410a..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger+Private.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -#import "XCDYouTubeLogger.h" - -@interface XCDYouTubeLogger () - -+ (void) logMessage:(NSString * (^)(void))message level:(XCDLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line; - -@end - -#define XCDYouTubeLog(_level, _message) [XCDYouTubeLogger logMessage:(_message) level:(_level) file:__FILE__ function:__PRETTY_FUNCTION__ line:__LINE__] - -#define XCDYouTubeLogError(format, ...) XCDYouTubeLog(XCDLogLevelError, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) -#define XCDYouTubeLogWarning(format, ...) XCDYouTubeLog(XCDLogLevelWarning, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) -#define XCDYouTubeLogInfo(format, ...) XCDYouTubeLog(XCDLogLevelInfo, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) -#define XCDYouTubeLogDebug(format, ...) XCDYouTubeLog(XCDLogLevelDebug, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) -#define XCDYouTubeLogVerbose(format, ...) XCDYouTubeLog(XCDLogLevelVerbose, (^{ return [NSString stringWithFormat:(format), ##__VA_ARGS__]; })) diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h deleted file mode 100644 index b5737ee6e..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.h +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -/** - * The [context][1] used when logging with CocoaLumberjack. - * - * [1]: https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/CustomContext.md - */ -extern const NSInteger XCDYouTubeKitLumberjackContext; - -/** - * The log levels, closely mirroring the log levels of CocoaLumberjack. - */ -typedef NS_ENUM(NSUInteger, XCDLogLevel) { - /** - * Used when an error is produced, e.g. when a `` finishes with an error. - */ - XCDLogLevelError = 0, - - /** - * Used on unusual conditions that may eventually lead to an error. - */ - XCDLogLevelWarning = 1, - - /** - * Used when logging normal operational information, e.g. when a `` starts, is cancelled or finishes. - */ - XCDLogLevelInfo = 2, - - /** - * Used throughout a `` for debugging purpose, e.g. for HTTP requests. - */ - XCDLogLevelDebug = 3, - - /** - * Used to report large amount of information, e.g. full HTTP responses. - */ - XCDLogLevelVerbose = 4, -}; - -/** - * You can use the `XCDYouTubeLogger` class to configure how the XCDYouTubeKit framework emits logs. - * - * By default, logs are emitted through CocoaLumberjack if it is available, i.e. if the `DDLog` class is found at runtime. - * The [context][1] used for CocoaLumberjack is the `XCDYouTubeKitLumberjackContext` constant whose value is `(NSInteger)0xced70676`. - * - * If CocoaLumberjack is not available, logs are emitted with `NSLog`, prefixed with the `[XCDYouTubeKit]` string. - * - * ## Controlling log levels - * - * If you are using CocoaLumberjack, you are responsible for controlling the log levels with the CocoaLumberjack APIs. - * - * If you are not using CocoaLumberjack, you can control the log levels with the `XCDYouTubeKitLogLevel` environment variable. See also the `` enum. - * - * Level | Value | Mask - * --------|-------|------ - * Error | 0 | 0x01 - * Warning | 1 | 0x02 - * Info | 2 | 0x04 - * Debug | 3 | 0x08 - * Verbose | 4 | 0x10 - * - * Use the corresponding bitmask to combine levels. For example, if you want to log *error*, *warning* and *info* levels, set the `XCDYouTubeKitLogLevel` environment variable to `0x7` (0x01 | 0x02 | 0x04). - * - * If you do not set the `XCDYouTubeKitLogLevel` environment variable, only warning and error levels are logged. - * - * [1]: https://github.com/CocoaLumberjack/CocoaLumberjack/blob/master/Documentation/CustomContext.md - */ -@interface XCDYouTubeLogger : NSObject - -/** - * ------------------- - * @name Custom Logger - * ------------------- - */ - -/** - * If you prefer not to use CocoaLumberjack and want something more advanced than the default `NSLog` implementation, you can use this method to write your own logger. - * - * @param logHandler The block called when a log is emitted by the XCDYouTubeKit framework. If you set the log handler to nil, logging will be completely disabled. - * - * @discussion Here is a description of the log handler parameters. - * - * - The `message` parameter is a block returning a string that you must call to evaluate the log message. - * - The `level` parameter is the log level of the message, see ``. - * - The `file` parameter is the full path of the file, captured with the `__FILE__` macro where the log is emitted. - * - The `function` parameter is the function name, captured with the `__PRETTY_FUNCTION__` macro where the log is emitted. - * - The `line` parameter is the line number, captured with the `__LINE__` macro where the log is emitted. - * - * Here is how you could implement a custom log handler with [NSLogger](https://github.com/fpillet/NSLogger): - * - * ``` - * [XCDYouTubeLogger setLogHandler:^(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) { - * LogMessageRawF(file, (int)line, function, @"XCDYouTubeKit", (int)level, message()); - * }]; - * ``` - */ -+ (void) setLogHandler:(void (^)(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line))logHandler; - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m deleted file mode 100644 index 511e58c39..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeLogger.m +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubeLogger.h" - -#import - -const NSInteger XCDYouTubeKitLumberjackContext = (NSInteger)0xced70676; - -@protocol XCDYouTubeLogger_DDLog -// Copied from CocoaLumberjack's DDLog interface -+ (void) log:(BOOL)asynchronous message:(NSString *)message level:(NSUInteger)level flag:(NSUInteger)flag context:(NSInteger)context file:(const char *)file function:(const char *)function line:(NSUInteger)line tag:(id)tag; -@end - -static Class DDLogClass = Nil; - -static void (^const CocoaLumberjackLogHandler)(NSString * (^)(void), XCDLogLevel, const char *, const char *, NSUInteger) = ^(NSString *(^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) -{ - // The `XCDLogLevel` enum was carefully crafted to match the `DDLogFlag` options from DDLog.h - [DDLogClass log:YES message:message() level:NSUIntegerMax flag:(1 << level) context:XCDYouTubeKitLumberjackContext file:file function:function line:line tag:nil]; -}; - -static void (^LogHandler)(NSString * (^)(void), XCDLogLevel, const char *, const char *, NSUInteger) = ^(NSString *(^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line) -{ - char *logLevelString = getenv("XCDYouTubeKitLogLevel"); - NSUInteger logLevelMask = logLevelString ? strtoul(logLevelString, NULL, 0) : (1 << XCDLogLevelError) | (1 << XCDLogLevelWarning); - if ((1 << level) & logLevelMask) - NSLog(@"[XCDYouTubeKit] %@", message()); -}; - -@implementation XCDYouTubeLogger - -+ (void) initialize -{ - static dispatch_once_t once; - dispatch_once(&once, ^{ - DDLogClass = objc_lookUpClass("DDLog"); - if (DDLogClass) - { - const SEL logSeletor = @selector(log:message:level:flag:context:file:function:line:tag:); - const char *typeEncoding = method_getTypeEncoding((Method)class_getClassMethod(DDLogClass, logSeletor)); - const char *expectedTypeEncoding = protocol_getMethodDescription(@protocol(XCDYouTubeLogger_DDLog), logSeletor, /* isRequiredMethod: */ YES, /* isInstanceMethod: */ NO).types; - if (typeEncoding && expectedTypeEncoding && strcmp(typeEncoding, expectedTypeEncoding) == 0) - LogHandler = CocoaLumberjackLogHandler; - else - NSLog(@"[XCDYouTubeKit] Incompatible CocoaLumberjack version. Expected \"%@\", got \"%@\".", expectedTypeEncoding ? @(expectedTypeEncoding) : @"", typeEncoding ? @(typeEncoding) : @""); - } - }); -} - -+ (void) setLogHandler:(void (^)(NSString * (^message)(void), XCDLogLevel level, const char *file, const char *function, NSUInteger line))logHandler -{ - LogHandler = logHandler; -} - -+ (void) logMessage:(NSString * (^)(void))message level:(XCDLogLevel)level file:(const char *)file function:(const char *)function line:(NSUInteger)line -{ - if (LogHandler) - LogHandler(message, level, file, function, line); -} - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h deleted file mode 100644 index 0b425c4df..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeOperation.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -/** - * The `XCDYouTubeOperation` protocol is adopted by opaque objects returned by the `<-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]>` method. - */ -@protocol XCDYouTubeOperation - -/** - * --------------- - * @name Canceling - * --------------- - */ - -/** - * Cancels the operation. If the operation is already finished, does nothing. - */ -- (void) cancel; - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h deleted file mode 100644 index 5421a1cc6..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -__attribute__((visibility("hidden"))) -@interface XCDYouTubePlayerScript : NSObject - -- (instancetype) initWithString:(NSString *)string; - -- (NSString *) unscrambleSignature:(NSString *)scrambledSignature; - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m deleted file mode 100644 index c40209d27..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubePlayerScript.m +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubePlayerScript.h" - -#import - -#import "XCDYouTubeLogger+Private.h" - -@interface XCDYouTubePlayerScript () -@property (nonatomic, strong) JSContext *context; -@property (nonatomic, strong) JSValue *signatureFunction; -@end - -@implementation XCDYouTubePlayerScript - -- (instancetype) initWithString:(NSString *)string -{ - if (!(self = [super init])) - return nil; // LCOV_EXCL_LINE - - _context = [JSContext new]; - _context.exceptionHandler = ^(JSContext *context, JSValue *exception) { - XCDYouTubeLogWarning(@"JavaScript exception: %@", exception); - }; - - NSDictionary *environment = @{ - @"document": @{ - @"documentElement": @{} - }, - @"location": @{ - @"hash": @"" - }, - @"navigator": @{ - @"userAgent": @"" - }, - }; - _context[@"window"] = @{}; - for (NSString *propertyName in environment) - { - JSValue *value = [JSValue valueWithObject:environment[propertyName] inContext:_context]; - _context[propertyName] = value; - _context[@"window"][propertyName] = value; - } - - NSString *matchMediaJsFunction = @"var matchMediaWindow=this;matchMediaWindow.matchMedia=function(a){return false;};"; - NSString *script = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - script=[matchMediaJsFunction stringByAppendingString:(script)]; - [_context evaluateScript:script]; - - NSRegularExpression *anonymousFunctionRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\(function\\(([^)]*)\\)\\{(.*)\\}\\)\\(([^)]*)\\)" options:NSRegularExpressionDotMatchesLineSeparators error:NULL]; - NSTextCheckingResult *anonymousFunctionResult = [anonymousFunctionRegularExpression firstMatchInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length)]; - if (anonymousFunctionResult.numberOfRanges > 3) - { - NSArray *parameters = [[script substringWithRange:[anonymousFunctionResult rangeAtIndex:1]] componentsSeparatedByString:@","]; - NSArray *arguments = [[script substringWithRange:[anonymousFunctionResult rangeAtIndex:3]] componentsSeparatedByString:@","]; - if (parameters.count == arguments.count) - { - for (NSUInteger i = 0; i < parameters.count; i++) - { - _context[parameters[i]] = _context[arguments[i]]; - } - } - NSString *anonymousFunctionBody = [script substringWithRange:[anonymousFunctionResult rangeAtIndex:2]]; - [_context evaluateScript:anonymousFunctionBody]; - } - else - { - XCDYouTubeLogWarning(@"Unexpected player script (no anonymous function found)"); - } - - //See list of regex patterns here https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L1179 - NSArray*patterns = @[@"\\.sig\\|\\|([a-zA-Z0-9$]+)\\(", - @"[\"']signature[\"']\\s*,\\s*([^\\(]+)", - @"yt\\.akamaized\\.net/\\)\\s*\\|\\|\\s*.*?\\s*c\\s*&&d.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)", - @"\\bcs*&&\\s*d\\.set\\([^,]+\\s*,\\s*([a-zA-Z0-9$]+)\\C", - @"\\bc\\s*&&\\s*d\\.set\\([^,]+\\s*,\\s*\\([^)]*\\)\\s*\\(\\s*([a-zA-Z0-9$]+)\\(" - ]; - - NSMutableArray*validRegularExpressions = [NSMutableArray new]; - - for (NSString *pattern in patterns) { - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:NULL]; - if (regex != nil) - { - [validRegularExpressions addObject:regex]; - } - } - - for (NSRegularExpression *regularExpression in validRegularExpressions) { - - NSArray *regexResults = [regularExpression matchesInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length)]; - - for (NSTextCheckingResult *signatureResult in regexResults) - { - NSString *signatureFunctionName = signatureResult.numberOfRanges > 1 ? [script substringWithRange:[signatureResult rangeAtIndex:1]] : nil; - if (!signatureFunctionName) - continue; - - JSValue *signatureFunction = self.context[signatureFunctionName]; - if (signatureFunction.isObject) - { - _signatureFunction = signatureFunction; - break; - } - } - } - - if (!_signatureFunction) - XCDYouTubeLogWarning(@"No signature function in player script"); - - return self; -} - -- (NSString *) unscrambleSignature:(NSString *)scrambledSignature -{ - if (!self.signatureFunction || !scrambledSignature) - return nil; - - JSValue *unscrambledSignature = [self.signatureFunction callWithArguments:@[ scrambledSignature ]]; - return [unscrambledSignature isString] ? [unscrambledSignature toString] : nil; -} - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h deleted file mode 100644 index 9f751206b..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo+Private.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -#import "XCDYouTubePlayerScript.h" - -#define XCDYouTubeErrorUseCipherSignature -1000 - -extern NSString *const XCDYouTubeNoStreamVideoUserInfoKey; - -extern NSDictionary *XCDDictionaryWithQueryString(NSString *string); -extern NSString *XCDQueryStringWithDictionary(NSDictionary *dictionary); -extern NSArray *XCDCaptionArrayWithString(NSString *string); - -@interface XCDYouTubeVideo () - -- (instancetype) initWithIdentifier:(NSString *)identifier info:(NSDictionary *)info playerScript:(XCDYouTubePlayerScript *)playerScript response:(NSURLResponse *)response error:(NSError **)error; - -- (void) mergeVideo:(XCDYouTubeVideo *)video; -- (void) mergeDashManifestStreamURLs:(NSDictionary *)dashManifestStreamURLs; - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h deleted file mode 100644 index 7668a9301..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.h +++ /dev/null @@ -1,147 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#if !__has_feature(nullability) -#define NS_ASSUME_NONNULL_BEGIN -#define NS_ASSUME_NONNULL_END -#define nullable -#endif - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * The quality of YouTube videos. These values are used as keys in the `<[XCDYouTubeVideo streamURLs]>` property. - * - * The constant numbers are the YouTube [itag](https://en.wikipedia.org/wiki/Talk:YouTube/Archive_22#Moved_from_YouTube#Quality_formats) values. - */ -typedef NS_ENUM(NSUInteger, XCDYouTubeVideoQuality) { - /** - * Video: 240p MPEG-4 Visual | 0.175 Mbit/s - * Audio: AAC | 36 kbit/s - */ - XCDYouTubeVideoQualitySmall240 = 36, - - /** - * Video: 360p H.264 | 0.5 Mbit/s - * Audio: AAC | 96 kbit/s - */ - XCDYouTubeVideoQualityMedium360 = 18, - - /** - * Video: 720p H.264 | 2-3 Mbit/s - * Audio: AAC | 192 kbit/s - */ - XCDYouTubeVideoQualityHD720 = 22, - - /** - * Video: 1080p H.264 | 3–5.9 Mbit/s - * Audio: AAC | 192 kbit/s - * - * @deprecated YouTube has removed 1080p mp4 videos. - */ - XCDYouTubeVideoQualityHD1080 DEPRECATED_MSG_ATTRIBUTE("YouTube has removed 1080p mp4 videos.") = 37, -}; - -/** - * Used as a key in the `streamURLs` property of the `XCDYouTubeVideo` class for live videos. - */ -extern NSString *const XCDYouTubeVideoQualityHTTPLiveStreaming; - -/** - * Represents a YouTube video. Use the `<-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]>` method to obtain a `XCDYouTubeVideo` object. - */ -@interface XCDYouTubeVideo : NSObject - -/** - * -------------------------------- - * @name Accessing video properties - * -------------------------------- - */ - -/** - * The 11 characters YouTube video identifier. - */ -@property (nonatomic, readonly) NSString *identifier; -/** - * The title of the video. - */ -@property (nonatomic, readonly) NSString *title; -/** - * The duration of the video in seconds. - */ -@property (nonatomic, readonly) NSTimeInterval duration; -/** - * A thumbnail URL for an image of small size, i.e. 120×90. May be nil. - */ -@property (nonatomic, readonly, nullable) NSURL *thumbnailURL; -/** - * A thumbnail URL for an image of small size, i.e. 120×90. May be nil. - */ -@property (nonatomic, readonly, nullable) NSURL *smallThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Renamed. Use `thumbnailURL` instead."); -/** - * A thumbnail URL for an image of medium size, i.e. 320×180, 480×360 or 640×480. May be nil. - */ -@property (nonatomic, readonly, nullable) NSURL *mediumThumbnailURL DEPRECATED_MSG_ATTRIBUTE("No longer available. Use `thumbnailURL` instead."); -/** - * A thumbnail URL for an image of large size, i.e. 1'280×720 or 1'980×1'080. May be nil. - */ -@property (nonatomic, readonly, nullable) NSURL *largeThumbnailURL DEPRECATED_MSG_ATTRIBUTE("No longer available. Use `thumbnailURL` instead."); - -/** - * A dictionary of video stream URLs. - * - * The keys are the YouTube [itag](https://en.wikipedia.org/wiki/YouTube#Quality_and_formats) values as `NSNumber` objects. The values are the video URLs as `NSURL` objects. There is also the special `XCDYouTubeVideoQualityHTTPLiveStreaming` key for live videos. - * - * You should not store the URLs for later use since they have a limited lifetime and are bound to an IP address. - * - * @see XCDYouTubeVideoQuality - * @see expirationDate - */ -#if __has_feature(objc_generics) -@property (nonatomic, readonly) NSDictionary *streamURLs; -#else -@property (nonatomic, readonly) NSDictionary *streamURLs; -#endif - -/** - - * A dictionary of caption URLs (XML). - * - * The keys are the [language codes](https://www.loc.gov/standards/iso639-2/php/code_list.php) values as `NSString` objects. The values are the caption URLs as `NSURL` objects. - * - * You should not store the URLs for later use since they have a limited lifetime. - */ -#if __has_feature(objc_generics) -@property (nonatomic, readonly, nullable) NSDictionary *captionURLs; -#else -@property (nonatomic, readonly, nullable) NSDictionary *captionURLs; -#endif - -/** - - * A dictionary of auto generated caption URLs (XML). - * - * The keys are the [language codes](https://www.loc.gov/standards/iso639-2/php/code_list.php) values as `NSString` objects. The values are the caption URLs as `NSURL` objects. These URLs are the ones that were automatically generated by YouTube. - * - * You should not store the URLs for later use since they have a limited lifetime. - */ - -#if __has_feature(objc_generics) -@property (nonatomic, readonly, nullable) NSDictionary *autoGeneratedCaptionURLs; -#else -@property (nonatomic, readonly, nullable) NSDictionary *autoGeneratedCaptionURLs; -#endif - -/** - * The expiration date of the video. - * - * After this date, the stream URLs will not be playable. May be nil if it can not be determined, for example in live videos. - */ -@property (nonatomic, readonly, nullable) NSDate *expirationDate; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m deleted file mode 100644 index c7ae55f0f..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideo.m +++ /dev/null @@ -1,319 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubeVideo+Private.h" - -#import "XCDYouTubeError.h" -#import "XCDYouTubeLogger+Private.h" - -#import - -NSString *const XCDYouTubeVideoErrorDomain = @"XCDYouTubeVideoErrorDomain"; -NSString *const XCDYouTubeAllowedCountriesUserInfoKey = @"AllowedCountries"; -NSString *const XCDYouTubeNoStreamVideoUserInfoKey = @"NoStreamVideo"; -NSString *const XCDYouTubeVideoQualityHTTPLiveStreaming = @"HTTPLiveStreaming"; - -NSArray *XCDCaptionArrayWithString(NSString *string) -{ - NSError *error = nil; - NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; - if (!data) { return nil; } - NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error]; - - if (error) { return nil; } - - NSDictionary *captions = JSON[@"captions"]; - NSDictionary *playerCaptionsTracklistRenderer = captions[@"playerCaptionsTracklistRenderer"]; - NSArray *captionTracks = playerCaptionsTracklistRenderer[@"captionTracks"]; - - if (captionTracks.count == 0 || captionTracks == nil) { return nil; } - return captionTracks; -} - -NSDictionary *XCDDictionaryWithQueryString(NSString *string) -{ - NSMutableDictionary *dictionary = [NSMutableDictionary new]; - NSArray *fields = [string componentsSeparatedByString:@"&"]; - for (NSString *field in fields) - { - NSArray *pair = [field componentsSeparatedByString:@"="]; - if (pair.count == 2) - { - NSString *key = pair[0]; - NSString *value = [(NSString *)pair[1] stringByRemovingPercentEncoding]; - value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; - if (dictionary[key] && ![(NSObject *)dictionary[key] isEqual:value]) - { - XCDYouTubeLogWarning(@"Using XCDDictionaryWithQueryString is inappropriate because the query string has multiple values for the key '%@'\n" - @"Query: %@\n" - @"Discarded value: %@", key, string, dictionary[key]); - } - dictionary[key] = value; - } - } - return [dictionary copy]; -} - -NSString *XCDQueryStringWithDictionary(NSDictionary *dictionary) -{ - NSArray *keys = [dictionary.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { - return [(NSObject *)evaluatedObject isKindOfClass:[NSString class]]; - }]]; - - NSMutableString *query = [NSMutableString new]; - for (NSString *key in [keys sortedArrayUsingSelector:@selector(compare:)]) - { - if (query.length > 0) - [query appendString:@"&"]; - - [query appendFormat:@"%@=%@", key, [(NSObject *)dictionary[key] description]]; - } - - return [query stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; -} - -static NSString *SortedDictionaryDescription(NSDictionary *dictionary) -{ - NSArray *sortedKeys = [dictionary.allKeys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { - return [[(NSObject *)obj1 description] compare:[(NSObject *) obj2 description] options:NSNumericSearch]; - }]; - - NSMutableString *description = [[NSMutableString alloc] initWithString:@"{\n"]; - for (id key in sortedKeys) - { - [description appendFormat:@"\t%@ \u2192 %@\n", key, dictionary[key]]; - } - [description appendString:@"}"]; - - return [description copy]; -} - -static NSURL * URLBySettingParameter(NSURL *URL, NSString *key, NSString *percentEncodedValue) -{ - NSString *pattern = [NSString stringWithFormat:@"((?:^|&)%@=)[^&]*", key]; - NSString *template = [NSString stringWithFormat:@"$1%@", percentEncodedValue]; - NSURLComponents *components = [NSURLComponents componentsWithURL:URL resolvingAgainstBaseURL:NO]; - NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:pattern options:(NSRegularExpressionOptions)0 error:NULL]; - NSMutableString *percentEncodedQuery = [components.percentEncodedQuery ?: @"" mutableCopy]; - NSUInteger numberOfMatches = [regularExpression replaceMatchesInString:percentEncodedQuery options:(NSMatchingOptions)0 range:NSMakeRange(0, percentEncodedQuery.length) withTemplate:template]; - if (numberOfMatches == 0) - [percentEncodedQuery appendFormat:@"%@%@=%@", percentEncodedQuery.length > 0 ? @"&" : @"", key, percentEncodedValue]; - components.percentEncodedQuery = percentEncodedQuery; - return components.URL; -} - -@implementation XCDYouTubeVideo - -static NSDate * ExpirationDate(NSURL *streamURL) -{ - NSDictionary *query = XCDDictionaryWithQueryString(streamURL.query); - NSTimeInterval expire = [(NSString *)query[@"expire"] doubleValue]; - return expire > 0 ? [NSDate dateWithTimeIntervalSince1970:expire] : nil; -} - -- (instancetype) initWithIdentifier:(NSString *)identifier info:(NSDictionary *)info playerScript:(XCDYouTubePlayerScript *)playerScript response:(NSURLResponse *)response error:(NSError * __autoreleasing *)error -{ - if (!(self = [super init])) - return nil; // LCOV_EXCL_LINE - - _identifier = identifier; - - NSString *streamMap = info[@"url_encoded_fmt_stream_map"]; - NSString *captionsMap = info[@"player_response"]; - NSString *httpLiveStream = info[@"hlsvp"]; - NSString *adaptiveFormats = info[@"adaptive_fmts"]; - - NSMutableDictionary *userInfo = response.URL ? [@{ NSURLErrorKey: (id)response.URL } mutableCopy] : [NSMutableDictionary new]; - - if (streamMap.length > 0 || httpLiveStream.length > 0) - { - NSMutableArray *streamQueries = [[streamMap componentsSeparatedByString:@","] mutableCopy]; - [streamQueries addObjectsFromArray:[adaptiveFormats componentsSeparatedByString:@","]]; - - NSString *title = info[@"title"] ?: @""; - _title = title; - _duration = [(NSString *)info[@"length_seconds"] doubleValue]; - - NSString *thumbnail = info[@"thumbnail_url"] ?: info[@"iurl"]; - _thumbnailURL = thumbnail ? [NSURL URLWithString:thumbnail] : nil; - - NSMutableDictionary *streamURLs = [NSMutableDictionary new]; - - if (httpLiveStream) - streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming] = [NSURL URLWithString:httpLiveStream]; - - NSMutableDictionary *captionURLs = [NSMutableDictionary new]; - NSMutableDictionary *autoGeneratedCaptionURLs = [NSMutableDictionary new]; - - for (NSDictionary *caption in XCDCaptionArrayWithString(captionsMap)) - { - NSString *languageCode = caption[@"languageCode"]; - NSString *captionVersion = caption[@"vssId"]; - NSString *captionURLString = caption[@"baseUrl"]; - if (!captionURLString) - { - continue; - } - NSURL *captionURL = [NSURL URLWithString:captionURLString]; - if (captionURL && languageCode) - - { - if ([languageCode isEqualToString:@"und"]) - { - //Skip because this is a special code than is used to indicate that the lanauage code is undetermined. - continue; - } - if([captionVersion hasPrefix:@"a"]) - { - //Indicates the caption was auto generated - autoGeneratedCaptionURLs[languageCode] = captionURL; - } else - { - captionURLs[languageCode] = captionURL; - } - } - } - - if (captionURLs.count > 0) - { - _captionURLs = [captionURLs copy]; - } - - if (autoGeneratedCaptionURLs.count > 0) - { - _autoGeneratedCaptionURLs = [autoGeneratedCaptionURLs copy]; - } - - for (NSString *streamQuery in streamQueries) - { - NSDictionary *stream = XCDDictionaryWithQueryString(streamQuery); - - NSString *scrambledSignature = stream[@"s"]; - if (scrambledSignature && !playerScript) - { - userInfo[XCDYouTubeNoStreamVideoUserInfoKey] = self; - if (error) - *error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorUseCipherSignature userInfo:userInfo]; - - return nil; - } - NSString *signature = [playerScript unscrambleSignature:scrambledSignature]; - if (playerScript && scrambledSignature && !signature) - continue; - - NSString *urlString = stream[@"url"]; - NSString *itag = stream[@"itag"]; - if (urlString && itag) - { - NSURL *streamURL = [NSURL URLWithString:urlString]; - if (!_expirationDate) - _expirationDate = ExpirationDate(streamURL); - - if (signature) - { - NSString *escapedSignature = [signature stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; - streamURL = URLBySettingParameter(streamURL, @"signature", escapedSignature); - } - - streamURLs[@(itag.integerValue)] = URLBySettingParameter(streamURL, @"ratebypass", @"yes"); - } - } - _streamURLs = [streamURLs copy]; - - if (_streamURLs.count == 0) - { - if (error) - *error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorNoStreamAvailable userInfo:userInfo]; - - return nil; - } - - return self; - } - else - { - if (error) - { - NSString *reason = info[@"reason"]; - if (reason) - { - reason = [reason stringByReplacingOccurrencesOfString:@"" withString:@" " options:NSRegularExpressionSearch range:NSMakeRange(0, reason.length)]; - reason = [reason stringByReplacingOccurrencesOfString:@"\n" withString:@" " options:(NSStringCompareOptions)0 range:NSMakeRange(0, reason.length)]; - NSRange range; - while ((range = [reason rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound) - reason = [reason stringByReplacingCharactersInRange:range withString:@""]; - - userInfo[NSLocalizedDescriptionKey] = reason; - } - - NSString *errorcode = info[@"errorcode"]; - NSInteger code = errorcode ? errorcode.integerValue : XCDYouTubeErrorNoStreamAvailable; - *error = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:code userInfo:userInfo]; - } - return nil; - } -} - -- (void) mergeVideo:(XCDYouTubeVideo *)video -{ - unsigned int count; - objc_property_t *properties = class_copyPropertyList(self.class, &count); - for (unsigned int i = 0; i < count; i++) - { - NSString *propertyName = @(property_getName(properties[i])); - if (![self valueForKey:propertyName]) - [self setValue:[video valueForKey:propertyName] forKeyPath:propertyName]; - } - free(properties); -} - -- (void) mergeDashManifestStreamURLs:(NSDictionary *)dashManifestStreamURLs { - - NSMutableDictionary *approvedStreams = [NSMutableDictionary new]; - - for (NSString *itag in dashManifestStreamURLs) { - if (self.streamURLs[itag] == nil) - approvedStreams[itag] = dashManifestStreamURLs[itag]; - } - - NSMutableDictionary *newStreams = [NSMutableDictionary dictionaryWithDictionary:self.streamURLs]; - [newStreams addEntriesFromDictionary:approvedStreams]; - [self setValue:newStreams.copy forKeyPath:NSStringFromSelector(@selector(streamURLs))]; -} - -#pragma mark - NSObject - -- (BOOL) isEqual:(id)object -{ - return [(NSObject *)object isKindOfClass:[XCDYouTubeVideo class]] && [((XCDYouTubeVideo *)object).identifier isEqual:self.identifier]; -} - -- (NSUInteger) hash -{ - return self.identifier.hash; -} - -- (NSString *) description -{ - return [NSString stringWithFormat:@"[%@] %@", self.identifier, self.title]; -} - -- (NSString *) debugDescription -{ - NSDateComponentsFormatter *dateComponentsFormatter = [NSDateComponentsFormatter new]; - dateComponentsFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated; - NSString *duration = [dateComponentsFormatter stringFromTimeInterval:self.duration] ?: [NSString stringWithFormat:@"%@ seconds", @(self.duration)]; - NSString *thumbnailDescription = [NSString stringWithFormat:@"Thumbnail: %@", self.thumbnailURL]; - NSString *streamsDescription = SortedDictionaryDescription(self.streamURLs); - return [NSString stringWithFormat:@"<%@: %p> %@\nDuration: %@\nExpiration date: %@\n%@\nStreams: %@", self.class, self, self.description, duration, self.expirationDate, thumbnailDescription, streamsDescription]; -} - -#pragma mark - NSCopying - -- (id) copyWithZone:(NSZone *)zone -{ - return self; -} - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h deleted file mode 100644 index 4ee8b7b06..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.h +++ /dev/null @@ -1,74 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#if !__has_feature(nullability) -#define NS_ASSUME_NONNULL_BEGIN -#define NS_ASSUME_NONNULL_END -#define nullable -#endif - -#import - -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * XCDYouTubeVideoOperation is a subclass of `NSOperation` that connects to the YouTube API and parse the response. - * - * You should probably use the higher level class ``. Use this class only if you are very familiar with `NSOperation` and need to manage dependencies between operations. - */ -@interface XCDYouTubeVideoOperation : NSOperation - -/** - * ------------------ - * @name Initializing - * ------------------ - */ - -/** - * Initializes a video operation with the specified video identifier and language identifier. - * - * @param videoIdentifier A 11 characters YouTube video identifier. - * @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier then English (`en`) is used. - * - * @return An initialized `XCDYouTubeVideoOperation` object. - */ -- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(nullable NSString *)languageIdentifier; - - -/** - Initializes a video operation with the specified video identifier and language identifier and cookies. - - @param videoIdentifier A 11 characters YouTube video identifier. - @param languageIdentifier An [ISO 639-1 two-letter language code](http://www.loc.gov/standards/iso639-2/php/code_list.php) used for error localization. If you pass a nil language identifier then English (`en`) is used. - @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login. - - @return An initialized `XCDYouTubeVideoOperation` object. - */ -- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(nullable NSString *)languageIdentifier cookies:(nullable NSArray *)cookies NS_DESIGNATED_INITIALIZER; - -/** - * -------------------------------- - * @name Accessing operation result - * -------------------------------- - */ - -/** - * Returns an error of the `XCDYouTubeVideoErrorDomain` domain if the operation failed or nil if it succeeded. - * - * Returns nil if the operation is not yet finished or if it was canceled. - */ -@property (atomic, readonly, nullable) NSError *error; -/** - * Returns a video object if the operation succeeded or nil if it failed. - * - * Returns nil if the operation is not yet finished or if it was canceled. - */ -@property (atomic, readonly, nullable) XCDYouTubeVideo *video; - -@end - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m deleted file mode 100644 index 5fa203f46..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoOperation.m +++ /dev/null @@ -1,410 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubeVideoOperation.h" - -#import - -#import "XCDYouTubeVideo+Private.h" -#import "XCDYouTubeError.h" -#import "XCDYouTubeVideoWebpage.h" -#import "XCDYouTubeDashManifestXML.h" -#import "XCDYouTubePlayerScript.h" -#import "XCDYouTubeLogger+Private.h" - -typedef NS_ENUM(NSUInteger, XCDYouTubeRequestType) { - XCDYouTubeRequestTypeGetVideoInfo = 1, - XCDYouTubeRequestTypeWatchPage, - XCDYouTubeRequestTypeEmbedPage, - XCDYouTubeRequestTypeJavaScriptPlayer, - XCDYouTubeRequestTypeDashManifest, - -}; - -@interface XCDYouTubeVideoOperation () -@property (atomic, copy, readonly) NSString *videoIdentifier; -@property (atomic, copy, readonly) NSString *languageIdentifier; -@property (atomic, strong, readonly) NSArray *cookies; - -@property (atomic, assign) NSInteger requestCount; -@property (atomic, assign) XCDYouTubeRequestType requestType; -@property (atomic, strong) NSMutableArray *eventLabels; -@property (atomic, strong) XCDYouTubeVideo *lastSuccessfulVideo; -@property (atomic, readonly) NSURLSession *session; -@property (atomic, strong) NSURLSessionDataTask *dataTask; - -@property (atomic, assign) BOOL isExecuting; -@property (atomic, assign) BOOL isFinished; -@property (atomic, readonly) dispatch_semaphore_t operationStartSemaphore; - -@property (atomic, strong) XCDYouTubeVideoWebpage *webpage; -@property (atomic, strong) XCDYouTubeVideoWebpage *embedWebpage; -@property (atomic, strong) XCDYouTubePlayerScript *playerScript; -@property (atomic, strong) XCDYouTubeVideo *noStreamVideo; -@property (atomic, strong) NSError *lastError; -@property (atomic, strong) NSError *youTubeError; // Error actually coming from the YouTube API, i.e. explicit and localized error - -@property (atomic, strong, readwrite) NSError *error; -@property (atomic, strong, readwrite) XCDYouTubeVideo *video; -@end - -@implementation XCDYouTubeVideoOperation - -static NSError *YouTubeError(NSError *error, NSSet *regionsAllowed, NSString *languageIdentifier) -{ - if (error.code == XCDYouTubeErrorRestrictedPlayback && regionsAllowed.count > 0) - { - NSLocale *locale = [NSLocale localeWithLocaleIdentifier:languageIdentifier]; - NSMutableSet *allowedCountries = [NSMutableSet new]; - for (NSString *countryCode in regionsAllowed) - { - NSString *country = [locale displayNameForKey:NSLocaleCountryCode value:countryCode]; - [allowedCountries addObject:country ?: countryCode]; - } - NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo]; - userInfo[XCDYouTubeAllowedCountriesUserInfoKey] = [allowedCountries copy]; - return [NSError errorWithDomain:error.domain code:error.code userInfo:[userInfo copy]]; - } - else - { - return error; - } -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-designated-initializers" -- (instancetype) init -{ - @throw [NSException exceptionWithName:NSGenericException reason:@"Use the `initWithVideoIdentifier:cookies:languageIdentifier:` method instead." userInfo:nil]; -} // LCOV_EXCL_LINE -#pragma clang diagnostic pop - -- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier cookies:(NSArray *)cookies -{ - if (!(self = [super init])) - return nil; // LCOV_EXCL_LINE - - _videoIdentifier = videoIdentifier ?: @""; - _languageIdentifier = languageIdentifier ?: @"en"; - - _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]]; - _cookies = [cookies copy]; - for (NSHTTPCookie *cookie in _cookies) { - [_session.configuration.HTTPCookieStorage setCookie:cookie]; - } - _operationStartSemaphore = dispatch_semaphore_create(0); - - return self; -} - -- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier languageIdentifier:(NSString *)languageIdentifier -{ - return [self initWithVideoIdentifier:videoIdentifier languageIdentifier:languageIdentifier cookies:nil]; -} - -#pragma mark - Requests - -- (void) startNextRequest -{ - if (self.eventLabels.count == 0) - { - if (self.requestType == XCDYouTubeRequestTypeWatchPage || self.webpage) - [self finishWithError]; - else - [self startWatchPageRequest]; - } - else - { - NSString *eventLabel = [self.eventLabels objectAtIndex:0]; - [self.eventLabels removeObjectAtIndex:0]; - - NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"el": eventLabel, @"ps": @"default" }; - NSString *queryString = XCDQueryStringWithDictionary(query); - NSURL *videoInfoURL = [NSURL URLWithString:[@"https://www.youtube.com/get_video_info?" stringByAppendingString:queryString]]; - [self startRequestWithURL:videoInfoURL type:XCDYouTubeRequestTypeGetVideoInfo]; - } -} - -- (void) startWatchPageRequest -{ - NSDictionary *query = @{ @"v": self.videoIdentifier, @"hl": self.languageIdentifier, @"has_verified": @YES, @"bpctr": @9999999999 }; - NSString *queryString = XCDQueryStringWithDictionary(query); - NSURL *webpageURL = [NSURL URLWithString:[@"https://www.youtube.com/watch?" stringByAppendingString:queryString]]; - [self startRequestWithURL:webpageURL type:XCDYouTubeRequestTypeWatchPage]; -} - -- (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestType -{ - if (self.isCancelled) - return; - - // Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 1×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo + 1xDashManifest - if (++self.requestCount > 7) - { - // This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests - [self finishWithError]; - return; - } - - XCDYouTubeLogDebug(@"Starting request: %@", url); - - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; - [request setValue:self.languageIdentifier forHTTPHeaderField:@"Accept-Language"]; - [request setValue:[NSString stringWithFormat:@"https://youtube.com/watch?v=%@", self.videoIdentifier] forHTTPHeaderField:@"Referer"]; - - self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) - { - if (self.isCancelled) - return; - - if (error) - [self handleConnectionError:error requestType:requestType]; - else - [self handleConnectionSuccessWithData:data response:response requestType:requestType]; - }]; - [self.dataTask resume]; - - self.requestType = requestType; -} - -#pragma mark - Response Dispatch - -- (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType -{ - CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)response.textEncodingName ?: CFSTR("")); - // Use kCFStringEncodingMacRoman as fallback because it defines characters for every byte value and is ASCII compatible. See https://mikeash.com/pyblog/friday-qa-2010-02-19-character-encodings.html - NSString *responseString = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, data.bytes, (CFIndex)data.length, encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingMacRoman, false)) ?: @""; - NSAssert(responseString.length > 0, @"Failed to decode response from %@ (response.textEncodingName = %@, data.length = %@)", response.URL, response.textEncodingName, @(data.length)); - - XCDYouTubeLogVerbose(@"Response: %@\n%@", response, responseString); - - switch (requestType) - { - case XCDYouTubeRequestTypeGetVideoInfo: - [self handleVideoInfoResponseWithInfo:XCDDictionaryWithQueryString(responseString) response:response]; - break; - case XCDYouTubeRequestTypeWatchPage: - [self handleWebPageWithHTMLString:responseString]; - break; - case XCDYouTubeRequestTypeEmbedPage: - [self handleEmbedWebPageWithHTMLString:responseString]; - break; - case XCDYouTubeRequestTypeJavaScriptPlayer: - [self handleJavaScriptPlayerWithScript:responseString]; - break; - case XCDYouTubeRequestTypeDashManifest: - [self handleDashManifestWithXMLString:responseString response:response]; - break; - } -} - -- (void) handleConnectionError:(NSError *)connectionError requestType:(XCDYouTubeRequestType)requestType -{ - //Shoud not return a connection error if was as a result of requesting the Dash Manifiest (we have a sucessfully created `XCDYouTubeVideo` and should just finish the operation as if were a 'sucessful' one - if (requestType == XCDYouTubeRequestTypeDashManifest) - { - [self finishWithVideo:self.lastSuccessfulVideo]; - return; - } - - NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: connectionError.localizedDescription, - NSUnderlyingErrorKey: connectionError }; - self.lastError = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorNetwork userInfo:userInfo]; - - [self startNextRequest]; -} - -#pragma mark - Response Parsing - -- (void) handleVideoInfoResponseWithInfo:(NSDictionary *)info response:(NSURLResponse *)response -{ - XCDYouTubeLogDebug(@"Handling video info response"); - - NSError *error = nil; - XCDYouTubeVideo *video = [[XCDYouTubeVideo alloc] initWithIdentifier:self.videoIdentifier info:info playerScript:self.playerScript response:response error:&error]; - if (video) - { - self.lastSuccessfulVideo = video; - - if (info[@"dashmpd"]) - { - NSURL *dashmpdURL = [NSURL URLWithString:(NSString *_Nonnull)info[@"dashmpd"]]; - [self startRequestWithURL:dashmpdURL type:XCDYouTubeRequestTypeDashManifest]; - return; - } - [video mergeVideo:self.noStreamVideo]; - [self finishWithVideo:video]; - } - else - { - if ([error.domain isEqual:XCDYouTubeVideoErrorDomain] && error.code == XCDYouTubeErrorUseCipherSignature) - { - self.noStreamVideo = error.userInfo[XCDYouTubeNoStreamVideoUserInfoKey]; - - [self startWatchPageRequest]; - } - else - { - self.lastError = error; - if (error.code > 0) - self.youTubeError = error; - - [self startNextRequest]; - } - } -} - -- (void) handleWebPageWithHTMLString:(NSString *)html -{ - XCDYouTubeLogDebug(@"Handling web page response"); - - self.webpage = [[XCDYouTubeVideoWebpage alloc] initWithHTMLString:html]; - - if (self.webpage.javaScriptPlayerURL) - { - [self startRequestWithURL:self.webpage.javaScriptPlayerURL type:XCDYouTubeRequestTypeJavaScriptPlayer]; - } - else - { - if (self.webpage.isAgeRestricted) - { - NSString *embedURLString = [NSString stringWithFormat:@"https://www.youtube.com/embed/%@", self.videoIdentifier]; - [self startRequestWithURL:[NSURL URLWithString:embedURLString] type:XCDYouTubeRequestTypeEmbedPage]; - } - else - { - [self startNextRequest]; - } - } -} - -- (void) handleEmbedWebPageWithHTMLString:(NSString *)html -{ - XCDYouTubeLogDebug(@"Handling embed web page response"); - - self.embedWebpage = [[XCDYouTubeVideoWebpage alloc] initWithHTMLString:html]; - - if (self.embedWebpage.javaScriptPlayerURL) - { - [self startRequestWithURL:self.embedWebpage.javaScriptPlayerURL type:XCDYouTubeRequestTypeJavaScriptPlayer]; - } - else - { - [self startNextRequest]; - } -} - -- (void) handleJavaScriptPlayerWithScript:(NSString *)script -{ - XCDYouTubeLogDebug(@"Handling JavaScript player response"); - - self.playerScript = [[XCDYouTubePlayerScript alloc] initWithString:script]; - - if (self.webpage.isAgeRestricted && self.cookies.count == 0) - { - NSString *eurl = [@"https://youtube.googleapis.com/v/" stringByAppendingString:self.videoIdentifier]; - NSString *sts = [(NSObject *)self.embedWebpage.playerConfiguration[@"sts"] description] ?: [(NSObject *)self.webpage.playerConfiguration[@"sts"] description] ?: @""; - NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"eurl": eurl, @"sts": sts}; - NSString *queryString = XCDQueryStringWithDictionary(query); - NSURL *videoInfoURL = [NSURL URLWithString:[@"https://www.youtube.com/get_video_info?" stringByAppendingString:queryString]]; - [self startRequestWithURL:videoInfoURL type:XCDYouTubeRequestTypeGetVideoInfo]; - } - else - { - [self handleVideoInfoResponseWithInfo:self.webpage.videoInfo response:nil]; - } -} - -- (void) handleDashManifestWithXMLString:(NSString *)XMLString response:(NSURLResponse *)response -{ - XCDYouTubeLogDebug(@"Handling Dash Manifest response"); - - XCDYouTubeDashManifestXML *dashManifestXML = [[XCDYouTubeDashManifestXML alloc]initWithXMLString:XMLString]; - NSDictionary *dashhManifestStreamURLs = dashManifestXML.streamURLs; - if (dashhManifestStreamURLs) - [self.lastSuccessfulVideo mergeDashManifestStreamURLs:dashhManifestStreamURLs]; - - [self finishWithVideo:self.lastSuccessfulVideo]; -} - -#pragma mark - Finish Operation - -- (void) finishWithVideo:(XCDYouTubeVideo *)video -{ - self.video = video; - XCDYouTubeLogInfo(@"Video operation finished with success: %@", video); - XCDYouTubeLogDebug(@"%@", ^{ return video.debugDescription; }()); - [self finish]; -} - -- (void) finishWithError -{ - self.error = self.youTubeError ? YouTubeError(self.youTubeError, self.webpage.regionsAllowed, self.languageIdentifier) : self.lastError; - XCDYouTubeLogError(@"Video operation finished with error: %@\nDomain: %@\nCode: %@\nUser Info: %@", self.error.localizedDescription, self.error.domain, @(self.error.code), self.error.userInfo); - [self finish]; -} - -- (void) finish -{ - self.isExecuting = NO; - self.isFinished = YES; -} - -#pragma mark - NSOperation - -+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key -{ - SEL selector = NSSelectorFromString(key); - return selector == @selector(isExecuting) || selector == @selector(isFinished) || [super automaticallyNotifiesObserversForKey:key]; -} - -- (BOOL) isConcurrent -{ - return YES; -} - -- (void) start -{ - dispatch_semaphore_signal(self.operationStartSemaphore); - - if (self.isCancelled) - return; - - if (self.videoIdentifier.length != 11) - { - XCDYouTubeLogWarning(@"Video identifier length should be 11. [%@]", self.videoIdentifier); - } - - XCDYouTubeLogInfo(@"Starting video operation: %@", self); - - self.isExecuting = YES; - - self.eventLabels = [[NSMutableArray alloc] initWithArray:@[ @"embedded", @"detailpage" ]]; - [self startNextRequest]; -} - -- (void) cancel -{ - if (self.isCancelled || self.isFinished) - return; - - XCDYouTubeLogInfo(@"Canceling video operation: %@", self); - - [super cancel]; - - [self.dataTask cancel]; - - // Wait for `start` to be called in order to avoid this warning: *** XCDYouTubeVideoOperation 0x7f8b18c84880 went isFinished=YES without being started by the queue it is in - dispatch_semaphore_wait(self.operationStartSemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(200 * NSEC_PER_MSEC))); - [self finish]; -} - -#pragma mark - NSObject - -- (NSString *) description -{ - return [NSString stringWithFormat:@"<%@: %p> %@ (%@)", self.class, self, self.videoIdentifier, self.languageIdentifier]; -} - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h deleted file mode 100644 index 812ee9c15..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.h +++ /dev/null @@ -1,126 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#if !__has_feature(nullability) -#define NS_ASSUME_NONNULL_BEGIN -#define NS_ASSUME_NONNULL_END -#define nullable -#define null_resettable -#endif - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * ------------------- - * @name Notifications - * ------------------- - */ - -/** - * NSError key for the `MPMoviePlayerPlaybackDidFinishNotification` userInfo dictionary. - * - * Ideally, there should be a `MPMoviePlayerPlaybackDidFinishErrorUserInfoKey` declared near to `MPMoviePlayerPlaybackDidFinishReasonUserInfoKey` in MPMoviePlayerController.h but since it doesn't exist, here is a convenient constant key. - */ -MP_EXTERN NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey; - -/** - * Posted when the video player has received the video information. The `object` of the notification is the `XCDYouTubeVideoPlayerViewController` instance. The `userInfo` dictionary contains the `XCDYouTubeVideo` object. - */ -MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification; -/** - * The key for the `XCDYouTubeVideo` object in the user info dictionary of `XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification`. - */ -MP_EXTERN NSString *const XCDYouTubeVideoUserInfoKey; - -/** - * A subclass of `MPMoviePlayerViewController` for playing YouTube videos. - * - * Use UIViewController’s `presentMoviePlayerViewControllerAnimated:` method to play a YouTube video fullscreen. - * - * Use the `` method to play a YouTube video inline. - */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -DEPRECATED_MSG_ATTRIBUTE("Use `AVFoundation` or `AVKit` APIs instead.") -@interface XCDYouTubeVideoPlayerViewController : MPMoviePlayerViewController -#pragma clang diagnostic pop - -/** - * ------------------ - * @name Initializing - * ------------------ - */ - -/** - * Initializes a YouTube video player view controller - * - * @param videoIdentifier A 11 characters YouTube video identifier. If the video identifier is invalid the `MPMoviePlayerPlaybackDidFinishNotification` will be posted with a `MPMovieFinishReasonPlaybackError` reason. - * - * @return An initialized YouTube video player view controller with the specified video identifier. - * - * @discussion You can pass a nil *videoIdentifier* (or use the standard `init` method instead) and set the `` property later. - */ -- (instancetype) initWithVideoIdentifier:(nullable NSString *)videoIdentifier NS_DESIGNATED_INITIALIZER; - -/** - * ------------------------------------ - * @name Accessing the video identifier - * ------------------------------------ - */ - -/** - * The 11 characters YouTube video identifier. - */ -@property (nonatomic, copy, nullable) NSString *videoIdentifier; - -/** - * ------------------------------------------ - * @name Defining the preferred video quality - * ------------------------------------------ - */ - -/** - * The preferred order for the quality of the video to play. Plays the first match when multiple video streams are available. - * - * Defaults to @[ XCDYouTubeVideoQualityHTTPLiveStreaming, @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ] - * - * You should set this property right after calling the `` method. Setting this property to nil restores its default values. - * - * @see XCDYouTubeVideoQuality - */ -@property (nonatomic, copy, null_resettable) NSArray *preferredVideoQualities; - -/** - * ------------------------ - * @name Presenting a video - * ------------------------ - */ - -/** - * Present the video inside a view. - * - * @param view The view inside which you want to present the video. - * - * @discussion The video view is added as a subview of the specified view. The video does not start playing immediately, you have to call `[videoPlayerViewController.moviePlayer play]` for playback to start. See `MPMoviePlayerController` documentation for more information. - * - * Ownership of the XCDYouTubeVideoPlayerViewController instance is transferred to the view. - */ -- (void) presentInView:(UIView *)view; - -@end - -/** - * ------------------------------ - * @name Deprecated notifications - * ------------------------------ - */ -MP_EXTERN NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification instead."); -MP_EXTERN NSString *const XCDMetadataKeyTitle DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); -MP_EXTERN NSString *const XCDMetadataKeySmallThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); -MP_EXTERN NSString *const XCDMetadataKeyMediumThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); -MP_EXTERN NSString *const XCDMetadataKeyLargeThumbnailURL DEPRECATED_MSG_ATTRIBUTE("Use XCDYouTubeVideoUserInfoKey instead."); - -NS_ASSUME_NONNULL_END diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m deleted file mode 100644 index ebec6c3c1..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m +++ /dev/null @@ -1,209 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubeVideoPlayerViewController.h" - -#import "XCDYouTubeClient.h" - -#import - -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - -NSString *const XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey = @"error"; // documented in -[MPMoviePlayerController initWithContentURL:] - -NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification = @"XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification"; -NSString *const XCDMetadataKeyTitle = @"Title"; -NSString *const XCDMetadataKeySmallThumbnailURL = @"SmallThumbnailURL"; -NSString *const XCDMetadataKeyMediumThumbnailURL = @"MediumThumbnailURL"; -NSString *const XCDMetadataKeyLargeThumbnailURL = @"LargeThumbnailURL"; - -NSString *const XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification = @"XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification"; -NSString *const XCDYouTubeVideoUserInfoKey = @"Video"; - -@interface XCDYouTubeVideoPlayerViewController () -@property (nonatomic, weak) id videoOperation; -@property (nonatomic, assign, getter = isEmbedded) BOOL embedded; -@end - -@implementation XCDYouTubeVideoPlayerViewController - -/* - * MPMoviePlayerViewController on iOS 7 and earlier - * - (id) init - * `-- [super init] - * - * - (id) initWithContentURL:(NSURL *)contentURL - * |-- [self init] - * `-- [self.moviePlayer setContentURL:contentURL] - * - * MPMoviePlayerViewController on iOS 8 and later - * - (id) init - * `-- [self initWithContentURL:nil] - * - * - (id) initWithContentURL:(NSURL *)contentURL - * |-- [super init] - * `-- [self.moviePlayer setContentURL:contentURL] - */ - -- (instancetype) init -{ - return [self initWithVideoIdentifier:nil]; -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-designated-initializers" -- (instancetype) initWithContentURL:(NSURL *)contentURL -{ - @throw [NSException exceptionWithName:NSGenericException reason:@"Use the `initWithVideoIdentifier:` method instead." userInfo:nil]; -} // LCOV_EXCL_LINE - -- (instancetype) initWithVideoIdentifier:(NSString *)videoIdentifier -{ -#if defined(DEBUG) && DEBUG - NSString *callStackSymbols = [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]; - if (([callStackSymbols rangeOfString:@"-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]_block_invoke"].length > 0) || ([callStackSymbols rangeOfString:@"-[XCDYouTubeClient getVideoWithIdentifier:cookies:completionHandler:]_block_invoke"].length > 0)) - { - NSString *reason = @"XCDYouTubeVideoPlayerViewController must not be used in the completion handler of `-[XCDYouTubeClient getVideoWithIdentifier:completionHandler:]` or `-[XCDYouTubeClient getVideoWithIdentifier:cookies:completionHandler:]`. Please read the documentation and sample code to properly use XCDYouTubeVideoPlayerViewController."; - @throw [NSException exceptionWithName:NSGenericException reason:reason userInfo:nil]; - } -#endif - - if ([[[UIDevice currentDevice] systemVersion] integerValue] >= 8) - self = [super initWithContentURL:nil]; - else - self = [super init]; // LCOV_EXCL_LINE - - if (!self) - return nil; // LCOV_EXCL_LINE - - // See https://github.com/0xced/XCDYouTubeKit/commit/cadec1c3857d6a302f71b9ce7d1ae48e389e6890 - [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil]; - - if (videoIdentifier) - self.videoIdentifier = videoIdentifier; - - return self; -} -#pragma clang diagnostic pop - -#pragma mark - Public - -- (NSArray *) preferredVideoQualities -{ - if (!_preferredVideoQualities) - _preferredVideoQualities = @[ XCDYouTubeVideoQualityHTTPLiveStreaming, @(XCDYouTubeVideoQualityHD720), @(XCDYouTubeVideoQualityMedium360), @(XCDYouTubeVideoQualitySmall240) ]; - - return _preferredVideoQualities; -} - -- (void) setVideoIdentifier:(NSString *)videoIdentifier -{ - if ([videoIdentifier isEqual:self.videoIdentifier]) - return; - - _videoIdentifier = [videoIdentifier copy]; - - [self.videoOperation cancel]; - self.videoOperation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) - { - if (video) - { - NSURL *streamURL = nil; - for (NSNumber *videoQuality in self.preferredVideoQualities) - { - streamURL = video.streamURLs[videoQuality]; - if (streamURL) - { - [self startVideo:video streamURL:streamURL]; - break; - } - } - - if (!streamURL) - { - NSError *noStreamError = [NSError errorWithDomain:XCDYouTubeVideoErrorDomain code:XCDYouTubeErrorNoStreamAvailable userInfo:nil]; - [self stopWithError:noStreamError]; - } - } - else - { - [self stopWithError:error]; - } - }]; -} - -- (void) presentInView:(UIView *)view -{ - static const void * const XCDYouTubeVideoPlayerViewControllerKey = &XCDYouTubeVideoPlayerViewControllerKey; - - self.embedded = YES; - - self.moviePlayer.controlStyle = MPMovieControlStyleEmbedded; - self.moviePlayer.view.frame = CGRectMake(0, 0, view.bounds.size.width, view.bounds.size.height); - self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - if (![view.subviews containsObject:self.moviePlayer.view]) - [view addSubview:self.moviePlayer.view]; - objc_setAssociatedObject(view, XCDYouTubeVideoPlayerViewControllerKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -#pragma mark - Private - -- (void) startVideo:(XCDYouTubeVideo *)video streamURL:(NSURL *)streamURL -{ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - if (video.title) - userInfo[XCDMetadataKeyTitle] = video.title; - if (video.smallThumbnailURL) - userInfo[XCDMetadataKeySmallThumbnailURL] = video.smallThumbnailURL; - if (video.mediumThumbnailURL) - userInfo[XCDMetadataKeyMediumThumbnailURL] = video.mediumThumbnailURL; - if (video.largeThumbnailURL) - userInfo[XCDMetadataKeyLargeThumbnailURL] = video.largeThumbnailURL; - - [[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveMetadataNotification object:self userInfo:userInfo]; -#pragma clang diagnostic pop - - self.moviePlayer.contentURL = streamURL; - - [[NSNotificationCenter defaultCenter] postNotificationName:XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification object:self userInfo:@{ XCDYouTubeVideoUserInfoKey: video }]; -} - -- (void) stopWithError:(NSError *)error -{ - NSDictionary *userInfo = @{ MPMoviePlayerPlaybackDidFinishReasonUserInfoKey: @(MPMovieFinishReasonPlaybackError), - XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey: error }; - [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer userInfo:userInfo]; - - if (self.isEmbedded) - [self.moviePlayer.view removeFromSuperview]; - else - [self.presentingViewController dismissMoviePlayerViewControllerAnimated]; -} - -#pragma mark - UIViewController - -- (void) viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - if (![self isBeingPresented]) - return; - - self.moviePlayer.controlStyle = MPMovieControlStyleFullscreen; - [self.moviePlayer play]; -} - -- (void) viewWillDisappear:(BOOL)animated -{ - [super viewWillDisappear:animated]; - - if (![self isBeingDismissed]) - return; - - [self.videoOperation cancel]; -} - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h deleted file mode 100644 index 750cfdc74..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import - -__attribute__((visibility("hidden"))) -@interface XCDYouTubeVideoWebpage : NSObject - -- (instancetype) initWithHTMLString:(NSString *)html; - -@property (nonatomic, readonly) NSDictionary *playerConfiguration; -@property (nonatomic, readonly) NSDictionary *videoInfo; -@property (nonatomic, readonly) NSURL *javaScriptPlayerURL; -@property (nonatomic, readonly) BOOL isAgeRestricted; -@property (nonatomic, readonly) NSSet *regionsAllowed; - -@end diff --git a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m b/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m deleted file mode 100644 index 4e56feb2a..000000000 --- a/ios/Pods/XCDYouTubeKit/XCDYouTubeKit/XCDYouTubeVideoWebpage.m +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright (c) 2013-2016 Cédric Luthi. All rights reserved. -// - -#import "XCDYouTubeVideoWebpage.h" - -@interface XCDYouTubeVideoWebpage () -@property (nonatomic, readonly) NSString *html; -@end - -@implementation XCDYouTubeVideoWebpage - -@synthesize playerConfiguration = _playerConfiguration; -@synthesize videoInfo = _videoInfo; -@synthesize javaScriptPlayerURL = _javaScriptPlayerURL; -@synthesize isAgeRestricted = _isAgeRestricted; -@synthesize regionsAllowed = _regionsAllowed; - -- (instancetype) initWithHTMLString:(NSString *)html -{ - if (!(self = [super init])) - return nil; // LCOV_EXCL_LINE - - _html = html; - - return self; -} - -- (NSDictionary *) playerConfiguration -{ - if (!_playerConfiguration) - { - __block NSDictionary *playerConfigurationDictionary; - NSRegularExpression *playerConfigRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"ytplayer.config\\s*=\\s*(\\{.*?\\});|[\\({]\\s*'PLAYER_CONFIG'[,:]\\s*(\\{.*?\\})\\s*(?:,'|\\))" options:NSRegularExpressionCaseInsensitive error:NULL]; - [playerConfigRegularExpression enumerateMatchesInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) - { - for (NSUInteger i = 1; i < result.numberOfRanges; i++) - { - NSRange range = [result rangeAtIndex:i]; - if (range.length == 0) - continue; - - NSString *configString = [self.html substringWithRange:range]; - NSData *configData = [configString dataUsingEncoding:NSUTF8StringEncoding]; - NSDictionary *playerConfiguration = [NSJSONSerialization JSONObjectWithData:configData ?: [NSData new] options:(NSJSONReadingOptions)0 error:NULL]; - if ([playerConfiguration isKindOfClass:[NSDictionary class]]) - { - playerConfigurationDictionary = playerConfiguration; - *stop = YES; - } - } - }]; - _playerConfiguration = playerConfigurationDictionary; - } - return _playerConfiguration; -} - -- (NSDictionary *) videoInfo -{ - if (!_videoInfo) - { - NSDictionary *args = self.playerConfiguration[@"args"]; - if ([args isKindOfClass:[NSDictionary class]]) - { - NSMutableDictionary *info = [NSMutableDictionary new]; - [args enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) - { - if ([(NSObject *)value isKindOfClass:[NSString class]] || [(NSObject *)value isKindOfClass:[NSNumber class]]) - info[key] = [(NSObject *)value description]; - }]; - _videoInfo = [info copy]; - } - } - return _videoInfo; -} - -- (NSURL *) javaScriptPlayerURL -{ - if (!_javaScriptPlayerURL) - { - NSString *jsAssets = [self.playerConfiguration valueForKeyPath:@"assets.js"]; - if ([jsAssets isKindOfClass:[NSString class]]) - { - NSString *javaScriptPlayerURLString = jsAssets; - if ([jsAssets hasPrefix:@"//"]) - javaScriptPlayerURLString = [@"https:" stringByAppendingString:jsAssets]; - else if ([jsAssets hasPrefix:@"/"]) - javaScriptPlayerURLString = [@"https://www.youtube.com" stringByAppendingString:jsAssets]; - - _javaScriptPlayerURL = [NSURL URLWithString:javaScriptPlayerURLString]; - } - } - return _javaScriptPlayerURL; -} - -- (BOOL) isAgeRestricted -{ - if (!_isAgeRestricted) - { - NSStringCompareOptions options = (NSStringCompareOptions)0; - NSRange range = NSMakeRange(0, self.html.length); - _isAgeRestricted = [self.html rangeOfString:@"og:restrictions:age" options:options range:range].location != NSNotFound; - } - return _isAgeRestricted; -} - -- (NSSet *) regionsAllowed -{ - if (!_regionsAllowed) - { - _regionsAllowed = [NSSet set]; - NSRegularExpression *regionsAllowedRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"meta\\s+itemprop=\"regionsAllowed\"\\s+content=\"(.*)\"" options:(NSRegularExpressionOptions)0 error:NULL]; - NSTextCheckingResult *regionsAllowedResult = [regionsAllowedRegularExpression firstMatchInString:self.html options:(NSMatchingOptions)0 range:NSMakeRange(0, self.html.length)]; - if (regionsAllowedResult.numberOfRanges > 1) - { - NSString *regionsAllowed = [self.html substringWithRange:[regionsAllowedResult rangeAtIndex:1]]; - if (regionsAllowed.length > 0) - _regionsAllowed = [NSSet setWithArray:[regionsAllowed componentsSeparatedByString:@","]]; - } - } - return _regionsAllowed; -} - -@end diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/LICENSE b/ios/Pods/YoutubePlayer-in-WKWebView/LICENSE deleted file mode 100644 index bfdf05f50..000000000 --- a/ios/Pods/YoutubePlayer-in-WKWebView/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2014 Google Inc. All rights reserved. - -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. \ No newline at end of file diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/README.md b/ios/Pods/YoutubePlayer-in-WKWebView/README.md deleted file mode 100644 index 67b80b8f5..000000000 --- a/ios/Pods/YoutubePlayer-in-WKWebView/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# YoutubePlayer-in-WKWebView - -[![YoutubePlayer-in-WKWebView version](https://img.shields.io/cocoapods/v/YoutubePlayer-in-WKWebView.svg?style=plastic)](http://cocoadocs.org/docsets/YoutubePlayer-in-WKWebView) [![YoutubePlayer-in-WKWebView platform](https://img.shields.io/cocoapods/p/YoutubePlayer-in-WKWebView.svg?style=plastic)](http://cocoadocs.org/docsets/YoutubePlayer-in-WKWebView) [![ios 8.0](https://img.shields.io/badge/ios-8.0-blue.svg?style=plastic)](http://cocoadocs.org/docsets/YoutubePlayer-in-WKWebView) - -YoutubePlayer-in-WKWebView is forked from [youtube-ios-player-helper](https://github.com/youtube/youtube-ios-player-helper) For using WKWebView. - -## Changes from [youtube-ios-player-helper](https://github.com/youtube/youtube-ios-player-helper) - -- class prefix changed to WKYT from YT. `YTPlayerView` -> `WKYTPlayerView` -- using WKWebView instead of UIWebView. -- getting properties asynchronously. - -``` - // YTPlayerView - NSTimeInterval duration = self.playerView.duration; - - // WKYTPlayerView - [self.playerView getDuration:^(NSTimeInterval duration, NSError * _Nullable error) { - if (!error) { - float seekToTime = duration * self.slider.value; - } - }]; -``` - -## Usage - -To run the example project; clone the repo, and run `pod install` from the Project directory first. For a simple tutorial see this Google Developers article - [Using the YouTube Helper Library to embed YouTube videos in your iOS application](https://developers.google.com/youtube/v3/guides/ios_youtube_helper). - -## Requirements - -## Installation - -YouTube-Player-iOS-Helper is available through [CocoaPods](http://cocoapods.org), to install -it simply add the following line to your Podfile: - - pod "YoutubePlayer-in-WKWebView", "~> 0.3.0" - -After installing in your project and opening the workspace, to use the library: - - 1. Drag a UIView the desired size of your player onto your Storyboard. - 2. Change the UIView's class in the Identity Inspector tab to WKYTPlayerView - 3. Import "WKYTPlayerView.h" in your ViewController. - 4. Add the following property to your ViewController's header file: -```objc - @property(nonatomic, strong) IBOutlet WKYTPlayerView *playerView; -``` - 5. Load the video into the player in your controller's code with the following code: -```objc - [self.playerView loadWithVideoId:@"M7lc1UVf-VE"]; -``` - 6. Run your code! - -See the sample project for more advanced uses, including passing additional player parameters, custom html and -working with callbacks via WKYTPlayerViewDelegate. - - -## License - -YoutubePlayer-in-WKWebView is available under the Apache 2.0 license. See the LICENSE file for more info. diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html deleted file mode 100644 index 975510efa..000000000 --- a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.bundle/Assets/YTPlayerView-iframe-player.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - -
-
-
- - - - diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h deleted file mode 100644 index d532d1d25..000000000 --- a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.h +++ /dev/null @@ -1,734 +0,0 @@ -// Copyright © 2014 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#import -#import - -@class WKYTPlayerView; - -/** These enums represent the state of the current video in the player. */ -typedef NS_ENUM(NSInteger, WKYTPlayerState) { - kWKYTPlayerStateUnstarted, - kWKYTPlayerStateEnded, - kWKYTPlayerStatePlaying, - kWKYTPlayerStatePaused, - kWKYTPlayerStateBuffering, - kWKYTPlayerStateQueued, - kWKYTPlayerStateUnknown -}; - -/** These enums represent the resolution of the currently loaded video. */ -typedef NS_ENUM(NSInteger, WKYTPlaybackQuality) { - kWKYTPlaybackQualitySmall, - kWKYTPlaybackQualityMedium, - kWKYTPlaybackQualityLarge, - kWKYTPlaybackQualityHD720, - kWKYTPlaybackQualityHD1080, - kWKYTPlaybackQualityHighRes, - kWKYTPlaybackQualityAuto, /** Addition for YouTube Live Events. */ - kWKYTPlaybackQualityDefault, - kWKYTPlaybackQualityUnknown /** This should never be returned. It is here for future proofing. */ -}; - -/** These enums represent error codes thrown by the player. */ -typedef NS_ENUM(NSInteger, WKYTPlayerError) { - kWKYTPlayerErrorInvalidParam, - kWKYTPlayerErrorHTML5Error, - kWKYTPlayerErrorVideoNotFound, // Functionally equivalent error codes 100 and - // 105 have been collapsed into |kWKYTPlayerErrorVideoNotFound|. - kWKYTPlayerErrorNotEmbeddable, // Functionally equivalent error codes 101 and - // 150 have been collapsed into |kWKYTPlayerErrorNotEmbeddable|. - kWKYTPlayerErrorUnknown -}; - -/** - * A delegate for ViewControllers to respond to YouTube player events outside - * of the view, such as changes to video playback state or playback errors. - * The callback functions correlate to the events fired by the IFrame API. - * For the full documentation, see the IFrame documentation here: - * https://developers.google.com/youtube/iframe_api_reference#Events - */ -@protocol WKYTPlayerViewDelegate - -@optional -/** - * Invoked when the player view is ready to receive API calls. - * - * @param playerView The WKYTPlayerView instance that has become ready. - */ -- (void)playerViewDidBecomeReady:(nonnull WKYTPlayerView *)playerView; - -/** - * Callback invoked when player state has changed, e.g. stopped or started playback. - * - * @param playerView The WKYTPlayerView instance where playback state has changed. - * @param state WKYTPlayerState designating the new playback state. - */ -- (void)playerView:(nonnull WKYTPlayerView *)playerView didChangeToState:(WKYTPlayerState)state; - -/** - * Callback invoked when playback quality has changed. - * - * @param playerView The WKYTPlayerView instance where playback quality has changed. - * @param quality WKYTPlaybackQuality designating the new playback quality. - */ -- (void)playerView:(nonnull WKYTPlayerView *)playerView didChangeToQuality:(WKYTPlaybackQuality)quality; - -/** - * Callback invoked when an error has occured. - * - * @param playerView The WKYTPlayerView instance where the error has occurred. - * @param error WKYTPlayerError containing the error state. - */ -- (void)playerView:(nonnull WKYTPlayerView *)playerView receivedError:(WKYTPlayerError)error; - -/** - * Callback invoked frequently when playBack is plaing. - * - * @param playerView The WKYTPlayerView instance where the error has occurred. - * @param playTime float containing curretn playback time. - */ -- (void)playerView:(nonnull WKYTPlayerView *)playerView didPlayTime:(float)playTime; - -/** - * Callback invoked when setting up the webview to allow custom colours so it fits in - * with app color schemes. If a transparent view is required specify clearColor and - * the code will handle the opacity etc. - * - * @param playerView The WKYTPlayerView instance where the error has occurred. - * @return A color object that represents the background color of the webview. - */ -- (nonnull UIColor *)playerViewPreferredWebViewBackgroundColor:(nonnull WKYTPlayerView *)playerView; - -/** - * Callback invoked when initially loading the YouTube iframe to the webview to display a custom - * loading view while the player view is not ready. This loading view will be dismissed just before - * -playerViewDidBecomeReady: callback is invoked. The loading view will be automatically resized - * to cover the entire player view. - * - * The default implementation does not display any custom loading views so the player will display - * a blank view with a background color of (-playerViewPreferredWebViewBackgroundColor:). - * - * Note that the custom loading view WILL NOT be displayed after iframe is loaded. It will be - * handled by YouTube iframe API. This callback is just intended to tell users the view is actually - * doing something while iframe is being loaded, which will take some time if users are in poor networks. - * - * @param playerView The WKYTPlayerView instance where the error has occurred. - * @return A view object that will be displayed while YouTube iframe API is being loaded. - * Pass nil to display no custom loading view. Default implementation returns nil. - */ -- (nullable UIView *)playerViewPreferredInitialLoadingView:(nonnull WKYTPlayerView *)playerView; - -/** - * Callback invoked when an api loading error has occured. - * - * @param playerView The WKYTPlayerView instance where the error has occurred. - */ -- (void)playerViewIframeAPIDidFailedToLoad:(nonnull WKYTPlayerView *)playerView; - -@end - -/** - * WKYTPlayerView is a custom UIView that client developers will use to include YouTube - * videos in their iOS applications. It can be instantiated programmatically, or via - * Interface Builder. Use the methods WKYTPlayerView::loadWithVideoId:, - * WKYTPlayerView::loadWithPlaylistId: or their variants to set the video or playlist - * to populate the view with. - */ -@interface WKYTPlayerView : UIView - -@property(nonatomic, strong, nullable, readonly) WKWebView *webView; - -/** A delegate to be notified on playback events. */ -@property(nonatomic, weak, nullable) id delegate; - -/** - * This method loads the player with the given video ID. - * This is a convenience method for calling WKYTPlayerView::loadPlayerWithVideoId:withPlayerVars: - * without player variables. - * - * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. - * To change the currently loaded video without reloading the entire WKWebView, use the - * WKYTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. - * - * @param videoId The YouTube video ID of the video to load in the player view. - * @return YES if player has been configured correctly, NO otherwise. - */ -- (BOOL)loadWithVideoId:(nonnull NSString *)videoId; - -/** - * This method loads the player with the given playlist ID. - * This is a convenience method for calling WKYTPlayerView::loadWithPlaylistId:withPlayerVars: - * without player variables. - * - * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. - * To change the currently loaded video without reloading the entire WKWebView, use the - * WKYTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality: - * family of methods. - * - * @param playlistId The YouTube playlist ID of the playlist to load in the player view. - * @return YES if player has been configured correctly, NO otherwise. - */ -- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId; - -/** - * This method loads the player with the given video ID and player variables. Player variables - * specify optional parameters for video playback. For instance, to play a YouTube - * video inline, the following playerVars dictionary would be used: - * - * @code - * @{ @"playsinline" : @1 }; - * @endcode - * - * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), - * both strings and integers are valid values. The full list of parameters is defined at: - * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. - * - * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. - * To change the currently loaded video without reloading the entire WKWebView, use the - * WKYTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. - * - * @param videoId The YouTube video ID of the video to load in the player view. - * @param playerVars An NSDictionary of player parameters. - * @return YES if player has been configured correctly, NO otherwise. - */ -- (BOOL)loadWithVideoId:(nonnull NSString *)videoId playerVars:(nullable NSDictionary *)playerVars; - -/** - * This method loads the player with the given video ID and player variables. Player variables - * specify optional parameters for video playback. For instance, to play a YouTube - * video inline, the following playerVars dictionary would be used: - * - * @code - * @{ @"playsinline" : @1 }; - * @endcode - * - * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), - * both strings and integers are valid values. The full list of parameters is defined at: - * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. - * - * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. - * To change the currently loaded video without reloading the entire WKWebView, use the - * WKYTPlayerView::cueVideoById:startSeconds:suggestedQuality: family of methods. - * - * @param videoId The YouTube video ID of the video to load in the player view. - * @param playerVars An NSDictionary of player parameters. - * @param path String with the path for HTML template used for viewing video. - * @return YES if player has been configured correctly, NO otherwise. - */ -- (BOOL)loadWithVideoId:(nonnull NSString *)videoId playerVars:(nullable NSDictionary *)playerVars templatePath:(nullable NSString *)path; - -/** - * This method loads the player with the given playlist ID and player variables. Player variables - * specify optional parameters for video playback. For instance, to play a YouTube - * video inline, the following playerVars dictionary would be used: - * - * @code - * @{ @"playsinline" : @1 }; - * @endcode - * - * Note that when the documentation specifies a valid value as a number (typically 0, 1 or 2), - * both strings and integers are valid values. The full list of parameters is defined at: - * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. - * - * This method reloads the entire contents of the WKWebView and regenerates its HTML contents. - * To change the currently loaded video without reloading the entire WKWebView, use the - * WKYTPlayerView::cuePlaylistByPlaylistId:index:startSeconds:suggestedQuality: - * family of methods. - * - * @param playlistId The YouTube playlist ID of the playlist to load in the player view. - * @param playerVars An NSDictionary of player parameters. - * @return YES if player has been configured correctly, NO otherwise. - */ -- (BOOL)loadWithPlaylistId:(nonnull NSString *)playlistId playerVars:(nullable NSDictionary *)playerVars; - -/** - * This method loads an iframe player with the given player parameters. Usually you may want to use - * -loadWithVideoId:playerVars: or -loadWithPlaylistId:playerVars: instead of this method does not handle - * video_id or playlist_id at all. The full list of parameters is defined at: - * https://developers.google.com/youtube/player_parameters?playerVersion=HTML5. - * - * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters - * to instantiate the HTML5 player with. This differs depending on - * whether a single video or playlist is being loaded. - * @return YES if successful, NO if not. - */ -- (BOOL)loadWithPlayerParams:(nullable NSDictionary *)additionalPlayerParams; - -#pragma mark - Player controls - -// These methods correspond to their JavaScript equivalents as documented here: -// https://developers.google.com/youtube/iframe_api_reference#Playback_controls - -/** - * Starts or resumes playback on the loaded video. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#playVideo - */ -- (void)playVideo; - -/** - * Pauses playback on a playing video. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#pauseVideo - */ -- (void)pauseVideo; - -/** - * Stops playback on a playing video. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#stopVideo - */ -- (void)stopVideo; - -/** - * Seek to a given time on a playing video. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#seekTo - * - * @param seekToSeconds The time in seconds to seek to in the loaded video. - * @param allowSeekAhead Whether to make a new request to the server if the time is - * outside what is currently buffered. Recommended to set to YES. - */ -- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead; - -#pragma mark - Queuing videos - -// Queueing functions for videos. These methods correspond to their JavaScript -// equivalents as documented here: -// https://developers.google.com/youtube/iframe_api_reference#Queueing_Functions - -/** - * Cues a given video by its video ID for playback starting at the given time and with the - * suggested quality. Cueing loads a video, but does not start video playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#cueVideoById - * - * @param videoId A video ID to cue. - * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)cueVideoById:(nonnull NSString *)videoId - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Cues a given video by its video ID for playback starting and ending at the given times - * with the suggested quality. Cueing loads a video, but does not start video playback. This - * method corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#cueVideoById - * - * @param videoId A video ID to cue. - * @param startSeconds Time in seconds to start the video when playVideo() is called. - * @param endSeconds Time in seconds to end the video after it begins playing. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)cueVideoById:(nonnull NSString *)videoId - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Loads a given video by its video ID for playback starting at the given time and with the - * suggested quality. Loading a video both loads it and begins playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#loadVideoById - * - * @param videoId A video ID to load and begin playing. - * @param startSeconds Time in seconds to start the video when it has loaded. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)loadVideoById:(nonnull NSString *)videoId - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Loads a given video by its video ID for playback starting and ending at the given times - * with the suggested quality. Loading a video both loads it and begins playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#loadVideoById - * - * @param videoId A video ID to load and begin playing. - * @param startSeconds Time in seconds to start the video when it has loaded. - * @param endSeconds Time in seconds to end the video after it begins playing. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)loadVideoById:(nonnull NSString *)videoId - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Cues a given video by its URL on YouTube.com for playback starting at the given time - * and with the suggested quality. Cueing loads a video, but does not start video playback. - * This method corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl - * - * @param videoURL URL of a YouTube video to cue for playback. - * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)cueVideoByURL:(nonnull NSString *)videoURL - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Cues a given video by its URL on YouTube.com for playback starting at the given time - * and with the suggested quality. Cueing loads a video, but does not start video playback. - * This method corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#cueVideoByUrl - * - * @param videoURL URL of a YouTube video to cue for playback. - * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. - * @param endSeconds Time in seconds to end the video after it begins playing. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)cueVideoByURL:(nonnull NSString *)videoURL - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Loads a given video by its video ID for playback starting at the given time - * with the suggested quality. Loading a video both loads it and begins playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl - * - * @param videoURL URL of a YouTube video to load and play. - * @param startSeconds Time in seconds to start the video when it has loaded. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)loadVideoByURL:(nonnull NSString *)videoURL - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Loads a given video by its video ID for playback starting and ending at the given times - * with the suggested quality. Loading a video both loads it and begins playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#loadVideoByUrl - * - * @param videoURL URL of a YouTube video to load and play. - * @param startSeconds Time in seconds to start the video when it has loaded. - * @param endSeconds Time in seconds to end the video after it begins playing. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)loadVideoByURL:(nonnull NSString *)videoURL - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -#pragma mark - Queuing functions for playlists - -// Queueing functions for playlists. These methods correspond to -// the JavaScript methods defined here: -// https://developers.google.com/youtube/js_api_reference#Playlist_Queueing_Functions - -/** - * Cues a given playlist with the given ID. The |index| parameter specifies the 0-indexed - * position of the first video to play, starting at the given time and with the - * suggested quality. Cueing loads a playlist, but does not start video playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#cuePlaylist - * - * @param playlistId Playlist ID of a YouTube playlist to cue. - * @param index A 0-indexed position specifying the first video to play. - * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)cuePlaylistByPlaylistId:(nonnull NSString *)playlistId - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Cues a playlist of videos with the given video IDs. The |index| parameter specifies the - * 0-indexed position of the first video to play, starting at the given time and with the - * suggested quality. Cueing loads a playlist, but does not start video playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#cuePlaylist - * - * @param videoIds An NSArray of video IDs to compose the playlist of. - * @param index A 0-indexed position specifying the first video to play. - * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)cuePlaylistByVideos:(nonnull NSArray *)videoIds - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Loads a given playlist with the given ID. The |index| parameter specifies the 0-indexed - * position of the first video to play, starting at the given time and with the - * suggested quality. Loading a playlist starts video playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#loadPlaylist - * - * @param playlistId Playlist ID of a YouTube playlist to cue. - * @param index A 0-indexed position specifying the first video to play. - * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)loadPlaylistByPlaylistId:(nonnull NSString *)playlistId - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Loads a playlist of videos with the given video IDs. The |index| parameter specifies the - * 0-indexed position of the first video to play, starting at the given time and with the - * suggested quality. Loading a playlist starts video playback. This method - * corresponds with its JavaScript API equivalent as documented here: - * https://developers.google.com/youtube/iframe_api_reference#loadPlaylist - * - * @param videoIds An NSArray of video IDs to compose the playlist of. - * @param index A 0-indexed position specifying the first video to play. - * @param startSeconds Time in seconds to start the video when WKYTPlayerView::playVideo is called. - * @param suggestedQuality WKYTPlaybackQuality value suggesting a playback quality. - */ -- (void)loadPlaylistByVideos:(nonnull NSArray *)videoIds - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality; - -#pragma mark - Playing a video in a playlist - -// These methods correspond to the JavaScript API as defined under the -// "Playing a video in a playlist" section here: -// https://developers.google.com/youtube/iframe_api_reference#Playback_status - -/** - * Loads and plays the next video in the playlist. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#nextVideo - */ -- (void)nextVideo; - -/** - * Loads and plays the previous video in the playlist. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#previousVideo - */ -- (void)previousVideo; - -/** - * Loads and plays the video at the given 0-indexed position in the playlist. - * Corresponds to this method from the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#playVideoAt - * - * @param index The 0-indexed position of the video in the playlist to load and play. - */ -- (void)playVideoAt:(int)index; - -#pragma mark - Setting the playback rate - -/** - * Gets the playback rate. The default value is 1.0, which represents a video - * playing at normal speed. Other values may include 0.25 or 0.5 for slower - * speeds, and 1.5 or 2.0 for faster speeds. This method corresponds to the - * JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate - */ -- (void)getPlaybackRate:(void (^ __nullable)(float playbackRate, NSError * __nullable error))completionHandler; - -/** - * Sets the playback rate. The default value is 1.0, which represents a video - * playing at normal speed. Other values may include 0.25 or 0.5 for slower - * speeds, and 1.5 or 2.0 for faster speeds. To fetch a list of valid values for - * this method, call WKYTPlayerView::getAvailablePlaybackRates. This method does not - * guarantee that the playback rate will change. - * This method corresponds to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#setPlaybackRate - * - * @param suggestedRate A playback rate to suggest for the player. - */ -- (void)setPlaybackRate:(float)suggestedRate; - -/** - * Gets a list of the valid playback rates, useful in conjunction with - * WKYTPlayerView::setPlaybackRate. This method corresponds to the - * JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getPlaybackRate - */ -- (void)getAvailablePlaybackRates:(void (^ __nullable)(NSArray * __nullable availablePlaybackRates, NSError * __nullable error))completionHandler; - -#pragma mark - Setting playback behavior for playlists - -/** - * Sets whether the player should loop back to the first video in the playlist - * after it has finished playing the last video. This method corresponds to the - * JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#loopPlaylist - * - * @param loop A boolean representing whether the player should loop. - */ -- (void)setLoop:(BOOL)loop; - -/** - * Sets whether the player should shuffle through the playlist. This method - * corresponds to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#shufflePlaylist - * - * @param shuffle A boolean representing whether the player should - * shuffle through the playlist. - */ -- (void)setShuffle:(BOOL)shuffle; - -#pragma mark - Playback status -// These methods correspond to the JavaScript methods defined here: -// https://developers.google.com/youtube/js_api_reference#Playback_status - -/** - * Returns a number between 0 and 1 that specifies the percentage of the video - * that the player shows as buffered. This method corresponds to the - * JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getVideoLoadedFraction - * - */ -- (void)getVideoLoadedFraction:(void (^ __nullable)(float videoLoadedFraction, NSError * __nullable error))completionHandler; - -/** - * Returns the state of the player. This method corresponds to the - * JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getPlayerState - * - */ -- (void)getPlayerState:(void (^ __nullable)(WKYTPlayerState playerState, NSError * __nullable error))completionHandler; - -/** - * Returns the elapsed time in seconds since the video started playing. This - * method corresponds to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getCurrentTime - * - */ -- (void)getCurrentTime:(void (^ __nullable)(float currentTime, NSError * __nullable error))completionHandler; - -#pragma mark - Playback quality - -// Playback quality. These methods correspond to the JavaScript -// methods defined here: -// https://developers.google.com/youtube/js_api_reference#Playback_quality - -/** - * Returns the playback quality. This method corresponds to the - * JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality - * - */ -- (void)getPlaybackQuality:(void (^ __nullable)(WKYTPlaybackQuality playbackQuality, NSError * __nullable error))completionHandler; - -/** - * Suggests playback quality for the video. It is recommended to leave this setting to - * |default|. This method corresponds to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#setPlaybackQuality - * - */ -- (void)setPlaybackQuality:(WKYTPlaybackQuality)suggestedQuality; - -/** - * Gets a list of the valid playback quality values, useful in conjunction with - * WKYTPlayerView::setPlaybackQuality. This method corresponds to the - * JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels - * - */ -- (void)getAvailableQualityLevels:(void (^ __nullable)(NSArray * __nullable availableQualityLevels, NSError * __nullable error))completionHandler; - -#pragma mark - Retrieving video information - -// Retrieving video information. These methods correspond to the JavaScript -// methods defined here: -// https://developers.google.com/youtube/js_api_reference#Retrieving_video_information - -/** - * Returns the duration in seconds since the video of the video. This - * method corresponds to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getDuration - * - */ -- (void)getDuration:(void (^ __nullable)(NSTimeInterval duration, NSError * __nullable error))completionHandler; - -/** - * Returns the YouTube.com URL for the video. This method corresponds - * to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getVideoUrl - * - */ -- (void)getVideoUrl:(void (^ __nullable)(NSURL * __nullable videoUrl, NSError * __nullable error))completionHandler; - -/** - * Returns the embed code for the current video. This method corresponds - * to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getVideoEmbedCode - * - */ -- (void)getVideoEmbedCode:(void (^ __nullable)(NSString * __nullable videoEmbedCode, NSError * __nullable error))completionHandler; - -#pragma mark - Retrieving playlist information - -// Retrieving playlist information. These methods correspond to the -// JavaScript defined here: -// https://developers.google.com/youtube/js_api_reference#Retrieving_playlist_information - -/** - * Returns an ordered array of video IDs in the playlist. This method corresponds - * to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getPlaylist - * - */ -- (void)getPlaylist:(void (^ __nullable)(NSArray * __nullable playlist, NSError * __nullable error))completionHandler; - -/** - * Returns the 0-based index of the currently playing item in the playlist. - * This method corresponds to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getPlaylistIndex - * - */ -- (void)getPlaylistIndex:(void (^ __nullable)(int playlistIndex, NSError * __nullable error))completionHandler; - -#pragma mark - Mute - -/** - * Mutes the player. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#mute - */ -- (void)mute; - -/** - * Unmutes the player. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#unMute - */ -- (void)unMute; - -/** - * Returns true if the player is muted, false if not. - * This method corresponds to the JavaScript API defined here: - * https://developers.google.com/youtube/iframe_api_reference#getVolume - * - */ -- (void)isMuted:(void (^ __nullable)(BOOL isMuted, NSError * __nullable error))completionHandler; - - -#pragma mark - Exposed for Testing - -/** - * Removes the internal web view from this player view. - * Intended to use for testing, should not be used in production code. - */ -- (void)removeWebView; - -@end diff --git a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m b/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m deleted file mode 100644 index d30b8a9f6..000000000 --- a/ios/Pods/YoutubePlayer-in-WKWebView/WKYTPlayerView/WKYTPlayerView.m +++ /dev/null @@ -1,1123 +0,0 @@ -// Copyright © 2014 Google Inc. All rights reserved. -// -// 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 "WKYTPlayerView.h" - -// These are instances of NSString because we get them from parsing a URL. It would be silly to -// convert these into an integer just to have to convert the URL query string value into an integer -// as well for the sake of doing a value comparison. A full list of response error codes can be -// found here: -// https://developers.google.com/youtube/iframe_api_reference -NSString static *const kWKYTPlayerStateUnstartedCode = @"-1"; -NSString static *const kWKYTPlayerStateEndedCode = @"0"; -NSString static *const kWKYTPlayerStatePlayingCode = @"1"; -NSString static *const kWKYTPlayerStatePausedCode = @"2"; -NSString static *const kWKYTPlayerStateBufferingCode = @"3"; -NSString static *const kWKYTPlayerStateCuedCode = @"5"; -NSString static *const kWKYTPlayerStateUnknownCode = @"unknown"; - -// Constants representing playback quality. -NSString static *const kWKYTPlaybackQualitySmallQuality = @"small"; -NSString static *const kWKYTPlaybackQualityMediumQuality = @"medium"; -NSString static *const kWKYTPlaybackQualityLargeQuality = @"large"; -NSString static *const kWKYTPlaybackQualityHD720Quality = @"hd720"; -NSString static *const kWKYTPlaybackQualityHD1080Quality = @"hd1080"; -NSString static *const kWKYTPlaybackQualityHighResQuality = @"highres"; -NSString static *const kWKYTPlaybackQualityAutoQuality = @"auto"; -NSString static *const kWKYTPlaybackQualityDefaultQuality = @"default"; -NSString static *const kWKYTPlaybackQualityUnknownQuality = @"unknown"; - -// Constants representing YouTube player errors. -NSString static *const kWKYTPlayerErrorInvalidParamErrorCode = @"2"; -NSString static *const kWKYTPlayerErrorHTML5ErrorCode = @"5"; -NSString static *const kWKYTPlayerErrorVideoNotFoundErrorCode = @"100"; -NSString static *const kWKYTPlayerErrorNotEmbeddableErrorCode = @"101"; -NSString static *const kWKYTPlayerErrorCannotFindVideoErrorCode = @"105"; -NSString static *const kWKYTPlayerErrorSameAsNotEmbeddableErrorCode = @"150"; - -// Constants representing player callbacks. -NSString static *const kWKYTPlayerCallbackOnReady = @"onReady"; -NSString static *const kWKYTPlayerCallbackOnStateChange = @"onStateChange"; -NSString static *const kWKYTPlayerCallbackOnPlaybackQualityChange = @"onPlaybackQualityChange"; -NSString static *const kWKYTPlayerCallbackOnError = @"onError"; -NSString static *const kWKYTPlayerCallbackOnPlayTime = @"onPlayTime"; - -NSString static *const kWKYTPlayerCallbackOnYouTubeIframeAPIReady = @"onYouTubeIframeAPIReady"; -NSString static *const kWKYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad = @"onYouTubeIframeAPIFailedToLoad"; - -NSString static *const kWKYTPlayerEmbedUrlRegexPattern = @"^http(s)://(www.)youtube.com/embed/(.*)$"; -NSString static *const kWKYTPlayerAdUrlRegexPattern = @"^http(s)://pubads.g.doubleclick.net/pagead/conversion/"; -NSString static *const kWKYTPlayerOAuthRegexPattern = @"^http(s)://accounts.google.com/o/oauth2/(.*)$"; -NSString static *const kWKYTPlayerStaticProxyRegexPattern = @"^https://content.googleapis.com/static/proxy.html(.*)$"; -NSString static *const kWKYTPlayerSyndicationRegexPattern = @"^https://tpc.googlesyndication.com/sodar/(.*).html$"; - -@interface WKYTPlayerView() - -@property (nonatomic, strong) NSURL *originURL; -@property (nonatomic, weak) UIView *initialLoadingView; - -@end - -@implementation WKYTPlayerView - -- (BOOL)loadWithVideoId:(NSString *)videoId { - return [self loadWithVideoId:videoId playerVars:nil]; -} - -- (BOOL)loadWithPlaylistId:(NSString *)playlistId { - return [self loadWithPlaylistId:playlistId playerVars:nil]; -} - -- (BOOL)loadWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars { - if (!playerVars) { - playerVars = @{}; - } - NSDictionary *playerParams = @{ @"videoId" : videoId, @"playerVars" : playerVars }; - return [self loadWithPlayerParams:playerParams]; -} - -- (BOOL)loadWithVideoId:(NSString *)videoId playerVars:(NSDictionary *)playerVars templatePath:(NSString *)path { - if (!playerVars) { - playerVars = @{}; - } - NSMutableDictionary *playerParams = [@{@"videoId" : videoId, @"playerVars" : playerVars} mutableCopy]; - if (path) { - playerParams[@"templatePath"] = path; - } - return [self loadWithPlayerParams:[playerParams copy]]; -} - -- (BOOL)loadWithPlaylistId:(NSString *)playlistId playerVars:(NSDictionary *)playerVars { - - // Mutable copy because we may have been passed an immutable config dictionary. - NSMutableDictionary *tempPlayerVars = [[NSMutableDictionary alloc] init]; - [tempPlayerVars setValue:@"playlist" forKey:@"listType"]; - [tempPlayerVars setValue:playlistId forKey:@"list"]; - if (playerVars) { - [tempPlayerVars addEntriesFromDictionary:playerVars]; - } - - NSDictionary *playerParams = @{ @"playerVars" : tempPlayerVars }; - return [self loadWithPlayerParams:playerParams]; -} - -#pragma mark - Player methods - -- (void)playVideo { - [self stringFromEvaluatingJavaScript:@"player.playVideo();" completionHandler:nil]; -} - -- (void)pauseVideo { - [self notifyDelegateOfYouTubeCallbackUrl:[NSURL URLWithString:[NSString stringWithFormat:@"ytplayer://onStateChange?data=%@", kWKYTPlayerStatePausedCode]]]; - [self stringFromEvaluatingJavaScript:@"player.pauseVideo();" completionHandler:nil]; -} - -- (void)stopVideo { - [self stringFromEvaluatingJavaScript:@"player.stopVideo();" completionHandler:nil]; -} - -- (void)seekToSeconds:(float)seekToSeconds allowSeekAhead:(BOOL)allowSeekAhead { - NSNumber *secondsValue = [NSNumber numberWithFloat:seekToSeconds]; - NSString *allowSeekAheadValue = [self stringForJSBoolean:allowSeekAhead]; - NSString *command = [NSString stringWithFormat:@"player.seekTo(%@, %@);", secondsValue, allowSeekAheadValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -#pragma mark - Cueing methods - -- (void)cueVideoById:(NSString *)videoId - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.cueVideoById('%@', %@, '%@');", - videoId, startSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)cueVideoById:(NSString *)videoId - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.cueVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});", videoId, startSecondsValue, endSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)loadVideoById:(NSString *)videoId - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.loadVideoById('%@', %@, '%@');", - videoId, startSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)loadVideoById:(NSString *)videoId - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.loadVideoById({'videoId': '%@', 'startSeconds': %@, 'endSeconds': %@, 'suggestedQuality': '%@'});",videoId, startSecondsValue, endSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)cueVideoByURL:(NSString *)videoURL - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, '%@');", - videoURL, startSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)cueVideoByURL:(NSString *)videoURL - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.cueVideoByUrl('%@', %@, %@, '%@');", - videoURL, startSecondsValue, endSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)loadVideoByURL:(NSString *)videoURL - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, '%@');", - videoURL, startSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)loadVideoByURL:(NSString *)videoURL - startSeconds:(float)startSeconds - endSeconds:(float)endSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSNumber *endSecondsValue = [NSNumber numberWithFloat:endSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.loadVideoByUrl('%@', %@, %@, '%@');", - videoURL, startSecondsValue, endSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -#pragma mark - Cueing methods for lists - -- (void)cuePlaylistByPlaylistId:(NSString *)playlistId - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId]; - [self cuePlaylist:playlistIdString - index:index - startSeconds:startSeconds - suggestedQuality:suggestedQuality]; -} - -- (void)cuePlaylistByVideos:(NSArray *)videoIds - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - [self cuePlaylist:[self stringFromVideoIdArray:videoIds] - index:index - startSeconds:startSeconds - suggestedQuality:suggestedQuality]; -} - -- (void)loadPlaylistByPlaylistId:(NSString *)playlistId - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSString *playlistIdString = [NSString stringWithFormat:@"'%@'", playlistId]; - [self loadPlaylist:playlistIdString - index:index - startSeconds:startSeconds - suggestedQuality:suggestedQuality]; -} - -- (void)loadPlaylistByVideos:(NSArray *)videoIds - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - [self loadPlaylist:[self stringFromVideoIdArray:videoIds] - index:index - startSeconds:startSeconds - suggestedQuality:suggestedQuality]; -} - -#pragma mark - Setting the playback rate - -- (void)getPlaybackRate:(void (^ __nullable)(float playbackRate, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getPlaybackRate();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(0, error); - } else { - completionHandler([response floatValue], nil); - } - } - }]; -} - -- (void)setPlaybackRate:(float)suggestedRate { - NSString *command = [NSString stringWithFormat:@"player.setPlaybackRate(%f);", suggestedRate]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)getAvailablePlaybackRates:(void (^ __nullable)(NSArray * __nullable availablePlaybackRates, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getAvailablePlaybackRates();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(nil, error); - } else { - NSData *playbackRateData = [response dataUsingEncoding:NSUTF8StringEncoding]; - NSError *jsonDeserializationError; - NSArray *playbackRates = [NSJSONSerialization JSONObjectWithData:playbackRateData - options:kNilOptions - error:&jsonDeserializationError]; - if (jsonDeserializationError) { - completionHandler(nil, jsonDeserializationError); - } - - completionHandler(playbackRates, nil); - } - } - }]; -} - -#pragma mark - Setting playback behavior for playlists - -- (void)setLoop:(BOOL)loop { - NSString *loopPlayListValue = [self stringForJSBoolean:loop]; - NSString *command = [NSString stringWithFormat:@"player.setLoop(%@);", loopPlayListValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -- (void)setShuffle:(BOOL)shuffle { - NSString *shufflePlayListValue = [self stringForJSBoolean:shuffle]; - NSString *command = [NSString stringWithFormat:@"player.setShuffle(%@);", shufflePlayListValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -#pragma mark - Playback status - -- (void)getVideoLoadedFraction:(void (^ __nullable)(float videoLoadedFraction, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getVideoLoadedFraction();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(0, error); - } else { - completionHandler([response floatValue], nil); - } - } - }]; -} - -- (void)getPlayerState:(void (^ __nullable)(WKYTPlayerState playerState, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getPlayerState();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(kWKYTPlayerStateUnknown, error); - } else { - if ([response isKindOfClass: [NSNumber class]]) { - NSNumber *value = (NSNumber *)response; - response = [value stringValue]; - } - completionHandler([WKYTPlayerView playerStateForString:response], nil); - } - } - }]; -} - -- (void)getCurrentTime:(void (^ __nullable)(float currentTime, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getCurrentTime();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(0, error); - } else { - completionHandler([response floatValue], nil); - } - } - }]; -} - -// Playback quality -- (void)getPlaybackQuality:(void (^ __nullable)(WKYTPlaybackQuality playbackQuality, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getPlaybackQuality();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(kWKYTPlaybackQualityUnknown, error); - } else { - completionHandler([WKYTPlayerView playbackQualityForString:response], nil); - } - } - }]; -} - -- (void)setPlaybackQuality:(WKYTPlaybackQuality)suggestedQuality { - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.setPlaybackQuality('%@');", qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -#pragma mark - Video information methods - -- (void)getDuration:(void (^ __nullable)(NSTimeInterval duration, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getDuration();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(0, error); - } else { - if (response != (id) [NSNull null]) { - completionHandler([response doubleValue], nil); - } - else { - completionHandler(0, nil); - } - } - } - }]; -} - -- (void)getVideoUrl:(void (^ __nullable)(NSURL * __nullable videoUrl, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getVideoUrl();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(nil, error); - } else { - completionHandler([NSURL URLWithString:response], nil); - } - } - }]; -} - -- (void)getVideoEmbedCode:(void (^ __nullable)(NSString * __nullable videoEmbedCode, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getVideoEmbedCode();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(nil, error); - } else { - completionHandler(response, nil); - } - } - }]; -} - -#pragma mark - Playlist methods - -- (void)getPlaylist:(void (^ __nullable)(NSArray * __nullable playlist, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getPlaylist();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(nil, error); - } else { - - if ([response isKindOfClass:[NSNull class]]) { - completionHandler(nil, nil); - return; - } - - NSArray *videoIds; - - if ([response isKindOfClass:[NSArray class]]) - { - videoIds = (NSArray *)response; - } - else - { - NSData *playlistData = [response dataUsingEncoding:NSUTF8StringEncoding]; - NSError *jsonDeserializationError; - videoIds = [NSJSONSerialization JSONObjectWithData:playlistData - options:kNilOptions - error:&jsonDeserializationError]; - if (jsonDeserializationError) { - completionHandler(nil, jsonDeserializationError); - } - } - - completionHandler(videoIds, nil); - } - } - }]; -} - -- (void)getPlaylistIndex:(void (^ __nullable)(int playlistIndex, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getPlaylistIndex();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(0, error); - } else { - completionHandler([response intValue], nil); - } - } - }]; -} - -#pragma mark - Playing a video in a playlist - -- (void)nextVideo { - [self stringFromEvaluatingJavaScript:@"player.nextVideo();" completionHandler:nil]; -} - -- (void)previousVideo { - [self stringFromEvaluatingJavaScript:@"player.previousVideo();" completionHandler:nil]; -} - -- (void)playVideoAt:(int)index { - NSString *command = - [NSString stringWithFormat:@"player.playVideoAt(%@);", [NSNumber numberWithInt:index]]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - - -#pragma mark - Changing the player volume - -/** - * Mutes the player. Corresponds to this method from - * the JavaScript API: - * https://developers.google.com/youtube/iframe_api_reference#mute - */ - -- (void)mute -{ - [self stringFromEvaluatingJavaScript:@"player.mute();" completionHandler:nil]; -} - -- (void)unMute -{ - [self stringFromEvaluatingJavaScript:@"player.unMute();" completionHandler:nil]; -} - -- (void)isMuted:(void (^ __nullable)(BOOL isMuted, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.isMuted();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(0, error); - } else { - completionHandler([response boolValue], nil); - } - } - }]; -} - - -#pragma mark - Helper methods - -- (void)getAvailableQualityLevels:(void (^ __nullable)(NSArray * __nullable availableQualityLevels, NSError * __nullable error))completionHandler -{ - [self stringFromEvaluatingJavaScript:@"player.getAvailableQualityLevels().toString();" completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - if (error) { - completionHandler(nil, error); - } else { - NSArray *rawQualityValues = [response componentsSeparatedByString:@","]; - NSMutableArray *levels = [[NSMutableArray alloc] init]; - for (NSString *rawQualityValue in rawQualityValues) { - WKYTPlaybackQuality quality = [WKYTPlayerView playbackQualityForString:rawQualityValue]; - [levels addObject:[NSNumber numberWithInt:quality]]; - } - - completionHandler(levels, nil); - } - } - }]; -} - -- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler -{ - NSURLRequest *request = navigationAction.request; - - if ([request.URL.host isEqual: self.originURL.host]) { - decisionHandler(WKNavigationActionPolicyAllow); - return; - } else if ([request.URL.scheme isEqual:@"ytplayer"]) { - [self notifyDelegateOfYouTubeCallbackUrl:request.URL]; - decisionHandler(WKNavigationActionPolicyCancel); - return; - } else if ([request.URL.scheme isEqual: @"http"] || [request.URL.scheme isEqual:@"https"]) { - if([self handleHttpNavigationToUrl:request.URL]) { - decisionHandler(WKNavigationActionPolicyAllow); - } else { - decisionHandler(WKNavigationActionPolicyCancel); - } - return; - } - - decisionHandler(WKNavigationActionPolicyAllow); -} - -- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error -{ - if (self.initialLoadingView) { - [self.initialLoadingView removeFromSuperview]; - } -} - -- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { - if (!navigationAction.targetFrame.isMainFrame) { - // open link with target="_blank" in the same webView - [webView loadRequest:navigationAction.request]; - } - return nil; -} - -/** - * Convert a quality value from NSString to the typed enum value. - * - * @param qualityString A string representing playback quality. Ex: "small", "medium", "hd1080". - * @return An enum value representing the playback quality. - */ -+ (WKYTPlaybackQuality)playbackQualityForString:(NSString *)qualityString { - WKYTPlaybackQuality quality = kWKYTPlaybackQualityUnknown; - - if ([qualityString isEqualToString:kWKYTPlaybackQualitySmallQuality]) { - quality = kWKYTPlaybackQualitySmall; - } else if ([qualityString isEqualToString:kWKYTPlaybackQualityMediumQuality]) { - quality = kWKYTPlaybackQualityMedium; - } else if ([qualityString isEqualToString:kWKYTPlaybackQualityLargeQuality]) { - quality = kWKYTPlaybackQualityLarge; - } else if ([qualityString isEqualToString:kWKYTPlaybackQualityHD720Quality]) { - quality = kWKYTPlaybackQualityHD720; - } else if ([qualityString isEqualToString:kWKYTPlaybackQualityHD1080Quality]) { - quality = kWKYTPlaybackQualityHD1080; - } else if ([qualityString isEqualToString:kWKYTPlaybackQualityHighResQuality]) { - quality = kWKYTPlaybackQualityHighRes; - } else if ([qualityString isEqualToString:kWKYTPlaybackQualityAutoQuality]) { - quality = kWKYTPlaybackQualityAuto; - } - - return quality; -} - -/** - * Convert a |WKYTPlaybackQuality| value from the typed value to NSString. - * - * @param quality A |WKYTPlaybackQuality| parameter. - * @return An |NSString| value to be used in the JavaScript bridge. - */ -+ (NSString *)stringForPlaybackQuality:(WKYTPlaybackQuality)quality { - switch (quality) { - case kWKYTPlaybackQualitySmall: - return kWKYTPlaybackQualitySmallQuality; - case kWKYTPlaybackQualityMedium: - return kWKYTPlaybackQualityMediumQuality; - case kWKYTPlaybackQualityLarge: - return kWKYTPlaybackQualityLargeQuality; - case kWKYTPlaybackQualityHD720: - return kWKYTPlaybackQualityHD720Quality; - case kWKYTPlaybackQualityHD1080: - return kWKYTPlaybackQualityHD1080Quality; - case kWKYTPlaybackQualityHighRes: - return kWKYTPlaybackQualityHighResQuality; - case kWKYTPlaybackQualityAuto: - return kWKYTPlaybackQualityAutoQuality; - default: - return kWKYTPlaybackQualityUnknownQuality; - } -} - -/** - * Convert a state value from NSString to the typed enum value. - * - * @param stateString A string representing player state. Ex: "-1", "0", "1". - * @return An enum value representing the player state. - */ -+ (WKYTPlayerState)playerStateForString:(NSString *)stateString { - WKYTPlayerState state = kWKYTPlayerStateUnknown; - if ([stateString isEqualToString:kWKYTPlayerStateUnstartedCode]) { - state = kWKYTPlayerStateUnstarted; - } else if ([stateString isEqualToString:kWKYTPlayerStateEndedCode]) { - state = kWKYTPlayerStateEnded; - } else if ([stateString isEqualToString:kWKYTPlayerStatePlayingCode]) { - state = kWKYTPlayerStatePlaying; - } else if ([stateString isEqualToString:kWKYTPlayerStatePausedCode]) { - state = kWKYTPlayerStatePaused; - } else if ([stateString isEqualToString:kWKYTPlayerStateBufferingCode]) { - state = kWKYTPlayerStateBuffering; - } else if ([stateString isEqualToString:kWKYTPlayerStateCuedCode]) { - state = kWKYTPlayerStateQueued; - } - return state; -} - -/** - * Convert a state value from the typed value to NSString. - * - * @param state A |WKYTPlayerState| parameter. - * @return A string value to be used in the JavaScript bridge. - */ -+ (NSString *)stringForPlayerState:(WKYTPlayerState)state { - switch (state) { - case kWKYTPlayerStateUnstarted: - return kWKYTPlayerStateUnstartedCode; - case kWKYTPlayerStateEnded: - return kWKYTPlayerStateEndedCode; - case kWKYTPlayerStatePlaying: - return kWKYTPlayerStatePlayingCode; - case kWKYTPlayerStatePaused: - return kWKYTPlayerStatePausedCode; - case kWKYTPlayerStateBuffering: - return kWKYTPlayerStateBufferingCode; - case kWKYTPlayerStateQueued: - return kWKYTPlayerStateCuedCode; - default: - return kWKYTPlayerStateUnknownCode; - } -} - -#pragma mark - Private methods - -/** - * Private method to handle "navigation" to a callback URL of the format - * ytplayer://action?data=someData - * This is how the WKWebView communicates with the containing Objective-C code. - * Side effects of this method are that it calls methods on this class's delegate. - * - * @param url A URL of the format ytplayer://action?data=value. - */ -- (void)notifyDelegateOfYouTubeCallbackUrl: (NSURL *) url { - NSString *action = url.host; - - // We know the query can only be of the format ytplayer://action?data=SOMEVALUE, - // so we parse out the value. - NSString *query = url.query; - NSString *data; - if (query) { - data = [query componentsSeparatedByString:@"="][1]; - } - - if ([action isEqual:kWKYTPlayerCallbackOnReady]) { - if (self.initialLoadingView) { - [self.initialLoadingView removeFromSuperview]; - } - if ([self.delegate respondsToSelector:@selector(playerViewDidBecomeReady:)]) { - [self.delegate playerViewDidBecomeReady:self]; - } - } else if ([action isEqual:kWKYTPlayerCallbackOnStateChange]) { - if ([self.delegate respondsToSelector:@selector(playerView:didChangeToState:)]) { - WKYTPlayerState state = kWKYTPlayerStateUnknown; - - if ([data isEqual:kWKYTPlayerStateEndedCode]) { - state = kWKYTPlayerStateEnded; - } else if ([data isEqual:kWKYTPlayerStatePlayingCode]) { - state = kWKYTPlayerStatePlaying; - } else if ([data isEqual:kWKYTPlayerStatePausedCode]) { - state = kWKYTPlayerStatePaused; - } else if ([data isEqual:kWKYTPlayerStateBufferingCode]) { - state = kWKYTPlayerStateBuffering; - } else if ([data isEqual:kWKYTPlayerStateCuedCode]) { - state = kWKYTPlayerStateQueued; - } else if ([data isEqual:kWKYTPlayerStateUnstartedCode]) { - state = kWKYTPlayerStateUnstarted; - } - - [self.delegate playerView:self didChangeToState:state]; - } - } else if ([action isEqual:kWKYTPlayerCallbackOnPlaybackQualityChange]) { - if ([self.delegate respondsToSelector:@selector(playerView:didChangeToQuality:)]) { - WKYTPlaybackQuality quality = [WKYTPlayerView playbackQualityForString:data]; - [self.delegate playerView:self didChangeToQuality:quality]; - } - } else if ([action isEqual:kWKYTPlayerCallbackOnError]) { - if ([self.delegate respondsToSelector:@selector(playerView:receivedError:)]) { - WKYTPlayerError error = kWKYTPlayerErrorUnknown; - - if ([data isEqual:kWKYTPlayerErrorInvalidParamErrorCode]) { - error = kWKYTPlayerErrorInvalidParam; - } else if ([data isEqual:kWKYTPlayerErrorHTML5ErrorCode]) { - error = kWKYTPlayerErrorHTML5Error; - } else if ([data isEqual:kWKYTPlayerErrorNotEmbeddableErrorCode] || - [data isEqual:kWKYTPlayerErrorSameAsNotEmbeddableErrorCode]) { - error = kWKYTPlayerErrorNotEmbeddable; - } else if ([data isEqual:kWKYTPlayerErrorVideoNotFoundErrorCode] || - [data isEqual:kWKYTPlayerErrorCannotFindVideoErrorCode]) { - error = kWKYTPlayerErrorVideoNotFound; - } - - [self.delegate playerView:self receivedError:error]; - } - } else if ([action isEqualToString:kWKYTPlayerCallbackOnPlayTime]) { - if ([self.delegate respondsToSelector:@selector(playerView:didPlayTime:)]) { - float time = [data floatValue]; - [self.delegate playerView:self didPlayTime:time]; - } - } else if ([action isEqualToString:kWKYTPlayerCallbackOnYouTubeIframeAPIFailedToLoad]) { - if (self.initialLoadingView) { - [self.initialLoadingView removeFromSuperview]; - } - - if ([self.delegate respondsToSelector:@selector(playerViewIframeAPIDidFailedToLoad:)]) { - [self.delegate playerViewIframeAPIDidFailedToLoad:self]; - } - } -} - -- (BOOL)handleHttpNavigationToUrl:(NSURL *) url { - // Usually this means the user has clicked on the YouTube logo or an error message in the - // player. Most URLs should open in the browser. The only http(s) URL that should open in this - // WKWebView is the URL for the embed, which is of the format: - // http(s)://www.youtube.com/embed/[VIDEO ID]?[PARAMETERS] - NSError *error = NULL; - NSRegularExpression *ytRegex = - [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerEmbedUrlRegexPattern - options:NSRegularExpressionCaseInsensitive - error:&error]; - NSTextCheckingResult *ytMatch = - [ytRegex firstMatchInString:url.absoluteString - options:0 - range:NSMakeRange(0, [url.absoluteString length])]; - - NSRegularExpression *adRegex = - [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerAdUrlRegexPattern - options:NSRegularExpressionCaseInsensitive - error:&error]; - NSTextCheckingResult *adMatch = - [adRegex firstMatchInString:url.absoluteString - options:0 - range:NSMakeRange(0, [url.absoluteString length])]; - - NSRegularExpression *syndicationRegex = - [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerSyndicationRegexPattern - options:NSRegularExpressionCaseInsensitive - error:&error]; - - NSTextCheckingResult *syndicationMatch = - [syndicationRegex firstMatchInString:url.absoluteString - options:0 - range:NSMakeRange(0, [url.absoluteString length])]; - - NSRegularExpression *oauthRegex = - [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerOAuthRegexPattern - options:NSRegularExpressionCaseInsensitive - error:&error]; - NSTextCheckingResult *oauthMatch = - [oauthRegex firstMatchInString:url.absoluteString - options:0 - range:NSMakeRange(0, [url.absoluteString length])]; - - NSRegularExpression *staticProxyRegex = - [NSRegularExpression regularExpressionWithPattern:kWKYTPlayerStaticProxyRegexPattern - options:NSRegularExpressionCaseInsensitive - error:&error]; - NSTextCheckingResult *staticProxyMatch = - [staticProxyRegex firstMatchInString:url.absoluteString - options:0 - range:NSMakeRange(0, [url.absoluteString length])]; - - if (ytMatch || adMatch || oauthMatch || staticProxyMatch || syndicationMatch) { - return YES; - } else { - [[UIApplication sharedApplication] openURL:url]; - return NO; - } -} - - -/** - * Private helper method to load an iframe player with the given player parameters. - * - * @param additionalPlayerParams An NSDictionary of parameters in addition to required parameters - * to instantiate the HTML5 player with. This differs depending on - * whether a single video or playlist is being loaded. - * @return YES if successful, NO if not. - */ -- (BOOL)loadWithPlayerParams:(NSDictionary *)additionalPlayerParams { - NSDictionary *playerCallbacks = @{ - @"onReady" : @"onReady", - @"onStateChange" : @"onStateChange", - @"onPlaybackQualityChange" : @"onPlaybackQualityChange", - @"onError" : @"onPlayerError" - }; - NSMutableDictionary *playerParams = [[NSMutableDictionary alloc] init]; - if (additionalPlayerParams) { - [playerParams addEntriesFromDictionary:additionalPlayerParams]; - } - if (![playerParams objectForKey:@"height"]) { - [playerParams setValue:@"100%" forKey:@"height"]; - } - if (![playerParams objectForKey:@"width"]) { - [playerParams setValue:@"100%" forKey:@"width"]; - } - - [playerParams setValue:playerCallbacks forKey:@"events"]; - - if ([playerParams objectForKey:@"playerVars"]) { - NSMutableDictionary *playerVars = [[NSMutableDictionary alloc] init]; - [playerVars addEntriesFromDictionary:[playerParams objectForKey:@"playerVars"]]; - - if (![playerVars objectForKey:@"origin"]) { - self.originURL = [NSURL URLWithString:@"about:blank"]; - } else { - self.originURL = [NSURL URLWithString: [playerVars objectForKey:@"origin"]]; - } - } else { - // This must not be empty so we can render a '{}' in the output JSON - [playerParams setValue:[[NSDictionary alloc] init] forKey:@"playerVars"]; - } - - // Remove the existing webView to reset any state - [self.webView removeFromSuperview]; - _webView = [self createNewWebView]; - [self addSubview:self.webView]; - - self.webView.translatesAutoresizingMaskIntoConstraints = NO; - NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:self.webView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:0.0]; - NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:self.webView - attribute:NSLayoutAttributeLeft - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeLeft - multiplier:1.0 - constant:0.0]; - NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:self.webView - attribute:NSLayoutAttributeRight - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeRight - multiplier:1.0 - constant:0.0]; - NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.webView - attribute:NSLayoutAttributeBottom - relatedBy:NSLayoutRelationEqual - toItem:self - attribute:NSLayoutAttributeBottom - multiplier:1.0 - constant:0.0]; - NSArray *constraints = @[topConstraint, leftConstraint, rightConstraint, bottomConstraint]; - [self addConstraints:constraints]; - - NSError *error = nil; - NSString *path = [additionalPlayerParams objectForKey:@"templatePath"]; - - //in case path to the HTML template wan't provided from the outside - if (!path) { - path = [[NSBundle bundleForClass:[WKYTPlayerView class]] pathForResource:@"YTPlayerView-iframe-player" - ofType:@"html" - inDirectory:@"Assets"]; - } - - // in case of using Swift and embedded frameworks, resources included not in main bundle, - // but in framework bundle - if (!path) { - path = [[[self class] frameworkBundle] pathForResource:@"YTPlayerView-iframe-player" - ofType:@"html" - inDirectory:@"Assets"]; - } - - NSString *embedHTMLTemplate = - [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; - - if (error) { - NSLog(@"Received error rendering template: %@", error); - return NO; - } - - // Render the playerVars as a JSON dictionary. - NSError *jsonRenderingError = nil; - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:playerParams - options:NSJSONWritingPrettyPrinted - error:&jsonRenderingError]; - if (jsonRenderingError) { - NSLog(@"Attempted configuration of player with invalid playerVars: %@ \tError: %@", - playerParams, - jsonRenderingError); - return NO; - } - - NSString *playerVarsJsonString = - [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; - - NSString *embedHTML = [NSString stringWithFormat:embedHTMLTemplate, playerVarsJsonString]; - [self.webView loadHTMLString:embedHTML baseURL: self.originURL]; - self.webView.navigationDelegate = self; - self.webView.UIDelegate = self; - - if ([self.delegate respondsToSelector:@selector(playerViewPreferredInitialLoadingView:)]) { - UIView *initialLoadingView = [self.delegate playerViewPreferredInitialLoadingView:self]; - if (initialLoadingView) { - initialLoadingView.frame = self.bounds; - initialLoadingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [self addSubview:initialLoadingView]; - self.initialLoadingView = initialLoadingView; - } - } - - return YES; -} - -/** - * Private method for cueing both cases of playlist ID and array of video IDs. Cueing - * a playlist does not start playback. - * - * @param cueingString A JavaScript string representing an array, playlist ID or list of - * video IDs to play with the playlist player. - * @param index 0-index position of video to start playback on. - * @param startSeconds Seconds after start of video to begin playback. - * @param suggestedQuality Suggested WKYTPlaybackQuality to play the videos. - */ -- (void)cuePlaylist:(NSString *)cueingString - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *indexValue = [NSNumber numberWithInt:index]; - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.cuePlaylist(%@, %@, %@, '%@');", - cueingString, indexValue, startSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -/** - * Private method for loading both cases of playlist ID and array of video IDs. Loading - * a playlist automatically starts playback. - * - * @param cueingString A JavaScript string representing an array, playlist ID or list of - * video IDs to play with the playlist player. - * @param index 0-index position of video to start playback on. - * @param startSeconds Seconds after start of video to begin playback. - * @param suggestedQuality Suggested WKYTPlaybackQuality to play the videos. - */ -- (void)loadPlaylist:(NSString *)cueingString - index:(int)index - startSeconds:(float)startSeconds - suggestedQuality:(WKYTPlaybackQuality)suggestedQuality { - NSNumber *indexValue = [NSNumber numberWithInt:index]; - NSNumber *startSecondsValue = [NSNumber numberWithFloat:startSeconds]; - NSString *qualityValue = [WKYTPlayerView stringForPlaybackQuality:suggestedQuality]; - NSString *command = [NSString stringWithFormat:@"player.loadPlaylist(%@, %@, %@, '%@');", - cueingString, indexValue, startSecondsValue, qualityValue]; - [self stringFromEvaluatingJavaScript:command completionHandler:nil]; -} - -/** - * Private helper method for converting an NSArray of video IDs into its JavaScript equivalent. - * - * @param videoIds An array of video ID strings to convert into JavaScript format. - * @return A JavaScript array in String format containing video IDs. - */ -- (NSString *)stringFromVideoIdArray:(NSArray *)videoIds { - NSMutableArray *formattedVideoIds = [[NSMutableArray alloc] init]; - - for (id unformattedId in videoIds) { - [formattedVideoIds addObject:[NSString stringWithFormat:@"'%@'", unformattedId]]; - } - - return [NSString stringWithFormat:@"[%@]", [formattedVideoIds componentsJoinedByString:@", "]]; -} - -/** - * Private method for evaluating JavaScript in the WebView. - * - * @param jsToExecute The JavaScript code in string format that we want to execute. - */ -- (void)stringFromEvaluatingJavaScript:(NSString *)jsToExecute completionHandler:(void (^ __nullable)(NSString * __nullable response, NSError * __nullable error))completionHandler{ - [self.webView evaluateJavaScript:jsToExecute completionHandler:^(NSString * _Nullable response, NSError * _Nullable error) { - if (completionHandler) { - completionHandler(response, error); - } - }]; -} - -/** - * Private method to convert a Objective-C BOOL value to JS boolean value. - * - * @param boolValue Objective-C BOOL value. - * @return JavaScript Boolean value, i.e. "true" or "false". - */ -- (NSString *)stringForJSBoolean:(BOOL)boolValue { - return boolValue ? @"true" : @"false"; -} - -#pragma mark - Exposed for Testing - -- (void)setWebView:(WKWebView *)webView { - _webView = webView; -} - -- (WKWebView *)createNewWebView { - - // WKWebView equivalent for UI Web View's scalesPageToFit - // - NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"; - - WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; - WKUserContentController *wkUController = [[WKUserContentController alloc] init]; - [wkUController addUserScript:wkUScript]; - - WKWebViewConfiguration *configuration = [WKWebViewConfiguration new]; - - configuration.userContentController = wkUController; - - configuration.allowsInlineMediaPlayback = YES; - configuration.mediaPlaybackRequiresUserAction = NO; - - WKWebView *webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:configuration]; - webView.scrollView.scrollEnabled = NO; - webView.scrollView.bounces = NO; - - if ([self.delegate respondsToSelector:@selector(playerViewPreferredWebViewBackgroundColor:)]) { - webView.backgroundColor = [self.delegate playerViewPreferredWebViewBackgroundColor:self]; - if (webView.backgroundColor == [UIColor clearColor]) { - webView.opaque = NO; - } - } - - return webView; -} - -- (void)removeWebView { - [self.webView removeFromSuperview]; - self.webView = nil; -} - -+ (NSBundle *)frameworkBundle { - static NSBundle* frameworkBundle = nil; - static dispatch_once_t predicate; - dispatch_once(&predicate, ^{ - NSString* mainBundlePath = [[NSBundle bundleForClass:[WKYTPlayerView class]] resourcePath]; - NSString* frameworkBundlePath = [mainBundlePath stringByAppendingPathComponent:@"WKYTPlayerView.bundle"]; - frameworkBundle = [NSBundle bundleWithPath:frameworkBundlePath]; - }); - return frameworkBundle; -} - -@end diff --git a/package-lock.json b/package-lock.json index 830ce926c..0b847596f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mattermost-mobile", - "version": "1.23.0", + "version": "1.25.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -8496,7 +8496,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true }, "aproba": { "version": "1.2.0", @@ -8517,12 +8518,14 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8537,17 +8540,20 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -8664,7 +8670,8 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "optional": true }, "ini": { "version": "1.3.5", @@ -8676,6 +8683,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8690,6 +8698,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -8697,12 +8706,14 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "optional": true }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -8721,6 +8732,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, "requires": { "minimist": "0.0.8" } @@ -8801,7 +8813,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true }, "object-assign": { "version": "4.1.1", @@ -8813,6 +8826,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, "requires": { "wrappy": "1" } @@ -8898,7 +8912,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -8934,6 +8949,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8953,6 +8969,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8996,12 +9013,14 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "optional": true } } }, @@ -17225,7 +17244,8 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "dev": true, + "optional": true }, "braces": { "version": "2.3.2", @@ -17488,7 +17508,8 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "dev": true, + "optional": true }, "micromatch": { "version": "3.1.10", @@ -17578,7 +17599,6 @@ "version": "git+https://github.com/enahum/redux-offline.git#4bd85e7e3b279a2b11fb4d587808d583d2b5e7b5", "from": "git+https://github.com/enahum/redux-offline.git#4bd85e7e3b279a2b11fb4d587808d583d2b5e7b5", "requires": { - "@react-native-community/netinfo": "^4.1.3", "redux-persist": "^4.5.0" }, "dependencies": { diff --git a/package.json b/package.json index 6b6f6535a..cb07d28a2 100644 --- a/package.json +++ b/package.json @@ -152,4 +152,4 @@ "node_modules/(?!react-native|jail-monkey)" ] } -} \ No newline at end of file +} From 8f0619d08374ecaf6bbe52d3232d1550b0858f2b Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Tue, 22 Oct 2019 17:53:19 +0300 Subject: [PATCH 8/9] re-add .podinstall to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4c7e3f3f7..1526310cc 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ DerivedData *.xcuserstate project.xcworkspace ios/Pods +.podinstall # Android/IntelliJ # From 5e5d3abd79ef64c6958f5988fd4b1336bc29fc88 Mon Sep 17 00:00:00 2001 From: Miguel Alatzar Date: Tue, 22 Oct 2019 11:18:59 -0700 Subject: [PATCH 9/9] [MM-17145] [MM-18947] [MM-17110] [MM-14926] [MM-18646] Use patched v2.0.6 of react-native-notifications and fix Android badge number (#3382) * Refactor custom push notification code * Use react-native-notifications 2.0.6 and patch for scheduled notifs * Fix patch * iOS changes * Fix delete * Fix setting of badge number on Android * Undo Reflect removal * Undo removal of didReceiveRemoteNotification * Use min importance for push notifs received while app is active * Correctly set badge number after push notificaiton reply * Fix tests * Localize reply action text * Add getDeliveredNotifications * Fix identifier check and failing test * Fix local push notif test for Android > 9 --- .../rnbeta/CustomPushNotification.java | 448 +++++++++++------- .../rnbeta/CustomPushNotificationDrawer.java | 2 +- android/settings.gradle | 2 +- .../push_notifications.android.js | 6 +- .../push_notifications.ios.js | 110 +++-- .../push_notifications.ios.test.js | 14 +- app/utils/push_notifications.js | 18 +- app/utils/push_notifications.test.js | 6 +- assets/base/i18n/en.json | 3 + ios/Mattermost/AppDelegate.m | 93 ++-- package-lock.json | 5 +- package.json | 2 +- .../react-native-notifications+2.0.6.patch | 378 +++++++++++++++ 13 files changed, 772 insertions(+), 315 deletions(-) create mode 100644 patches/react-native-notifications+2.0.6.patch diff --git a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java index 656a584e9..3824054ad 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java @@ -20,7 +20,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Build; import android.provider.Settings.System; -import android.util.Log; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; @@ -33,7 +32,6 @@ import com.wix.reactnativenotifications.core.NotificationIntentAdapter; import com.wix.reactnativenotifications.core.AppLaunchHelper; import com.wix.reactnativenotifications.core.AppLifecycleFacade; import com.wix.reactnativenotifications.core.JsIOHelper; -import com.wix.reactnativenotifications.helpers.ApplicationBadgeHelper; import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; @@ -44,6 +42,9 @@ public class CustomPushNotification extends PushNotification { public static final String KEY_TEXT_REPLY = "CAN_REPLY"; public static final String NOTIFICATION_REPLIED_EVENT_NAME = "notificationReplied"; + private NotificationChannel mHighImportanceChannel; + private NotificationChannel mMinImportanceChannel; + private static LinkedHashMap channelIdToNotificationCount = new LinkedHashMap(); private static LinkedHashMap> channelIdToNotification = new LinkedHashMap>(); private static AppLifecycleFacade lifecycleFacade; @@ -53,11 +54,12 @@ public class CustomPushNotification extends PushNotification { public CustomPushNotification(Context context, Bundle bundle, AppLifecycleFacade appLifecycleFacade, AppLaunchHelper appLaunchHelper, JsIOHelper jsIoHelper) { super(context, bundle, appLifecycleFacade, appLaunchHelper, jsIoHelper); this.context = context; + createNotificationChannels(); } public static void clearNotification(Context mContext, int notificationId, String channelId) { if (notificationId != -1) { - Object objCount = channelIdToNotificationCount.get(channelId); + Object objCount = channelIdToNotificationCount.get(channelId); Integer count = -1; if (objCount != null) { @@ -74,7 +76,6 @@ public class CustomPushNotification extends PushNotification { if (count != -1) { int total = CustomPushNotification.badgeCount - count; int badgeCount = total < 0 ? 0 : total; - ApplicationBadgeHelper.instance.setApplicationIconBadgeNumber(mContext.getApplicationContext(), badgeCount); CustomPushNotification.badgeCount = badgeCount; } } @@ -87,7 +88,6 @@ public class CustomPushNotification extends PushNotification { if (mContext != null) { final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancelAll(); - ApplicationBadgeHelper.instance.setApplicationIconBadgeNumber(mContext.getApplicationContext(), 0); } } @@ -121,7 +121,7 @@ public class CustomPushNotification extends PushNotification { } synchronized (list) { if (!"clear".equals(type)) { - String senderName = getSenderName(data.getString("sender_name"), data.getString("channel_name"), data.getString("message")); + String senderName = getSenderName(data); data.putLong("time", new Date().getTime()); data.putString("sender_name", senderName); data.putString("sender_id", data.getString("sender_id")); @@ -149,59 +149,142 @@ public class CustomPushNotification extends PushNotification { digestNotification(); } - @Override - protected void postNotification(int id, Notification notification) { - boolean force = false; - Bundle bundle = notification.extras; - if (bundle != null) { - force = bundle.getBoolean("localTest"); - } - - if (!mAppLifecycleFacade.isAppVisible() || force) { - super.postNotification(id, notification); - } - } - @Override protected Notification.Builder getNotificationBuilder(PendingIntent intent) { - final Resources res = mContext.getResources(); - String packageName = mContext.getPackageName(); - NotificationPreferences notificationPreferences = NotificationPreferences.getInstance(mContext); - // First, get a builder initialized with defaults from the core class. final Notification.Builder notification = new Notification.Builder(mContext); - // If Android Oreo or above we need to register a channel - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - String CHANNEL_ID = "channel_01"; - String CHANNEL_NAME = "Mattermost notifications"; - - NotificationChannel channel = new NotificationChannel(CHANNEL_ID, - CHANNEL_NAME, - NotificationManager.IMPORTANCE_HIGH); - channel.setShowBadge(true); - - final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.createNotificationChannel(channel); - notification.setChannelId(CHANNEL_ID); - } - Bundle bundle = mNotificationProps.asBundle(); - String version = bundle.getString("version"); + addNotificationExtras(notification, bundle); + setNotificationIcons(notification, bundle); + setNotificationMessagingStyle(notification, bundle); + setNotificationChannel(notification, bundle); + setNotificationBadgeIconType(notification); + + NotificationPreferences notificationPreferences = NotificationPreferences.getInstance(mContext); + setNotificationSound(notification, notificationPreferences); + setNotificationVibrate(notification, notificationPreferences); + setNotificationBlink(notification, notificationPreferences); + String channelId = bundle.getString("channel_id"); - String channelName = bundle.getString("channel_name"); - String senderName = bundle.getString("sender_name"); - String senderId = bundle.getString("sender_id"); - String postId = bundle.getString("post_id"); - String badge = bundle.getString("badge"); + int notificationId = channelId != null ? channelId.hashCode() : MESSAGE_NOTIFICATION_ID; + setNotificationNumber(notification, channelId); + setNotificationDeleteIntent(notification, notificationId); + addNotificationReplyAction(notification, notificationId, bundle); + + notification + .setContentIntent(intent) + .setVisibility(Notification.VISIBILITY_PRIVATE) + .setPriority(Notification.PRIORITY_HIGH) + .setAutoCancel(true); + + return notification; + } + + private void addNotificationExtras(Notification.Builder notification, Bundle bundle) { + Bundle userInfoBundle = bundle.getBundle("userInfo"); + if (userInfoBundle == null) { + userInfoBundle = new Bundle(); + } + + String channelId = bundle.getString("channel_id"); + userInfoBundle.putString("channel_id", channelId); + + notification.addExtras(userInfoBundle); + } + + private void setNotificationIcons(Notification.Builder notification, Bundle bundle) { String smallIcon = bundle.getString("smallIcon"); String largeIcon = bundle.getString("largeIcon"); - int notificationId = channelId != null ? channelId.hashCode() : MESSAGE_NOTIFICATION_ID; + int smallIconResId = getSmallIconResourceId(smallIcon); + notification.setSmallIcon(smallIconResId); + + int largeIconResId = getLargeIconResourceId(largeIcon); + final Resources res = mContext.getResources(); + Bitmap largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId); + if (largeIconResId != 0 && (largeIconBitmap != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) { + notification.setLargeIcon(largeIconBitmap); + } + } + + private int getSmallIconResourceId(String iconName) { + if (iconName == null) { + iconName = "ic_notification"; + } + + int resourceId = getIconResourceId(iconName); + + if (resourceId == 0) { + iconName = "ic_launcher"; + resourceId = getIconResourceId(iconName); + + if (resourceId == 0) { + resourceId = android.R.drawable.ic_dialog_info; + } + } + + return resourceId; + } + + private int getLargeIconResourceId(String iconName) { + if (iconName == null) { + iconName = "ic_launcher"; + } + + return getIconResourceId(iconName); + } + + private int getIconResourceId(String iconName) { + final Resources res = mContext.getResources(); + String packageName = mContext.getPackageName(); + String defType = "mipmap"; + + return res.getIdentifier(iconName, defType, packageName); + } + + private void setNotificationNumber(Notification.Builder notification, String channelId) { + Integer number = 1; + Object objCount = channelIdToNotificationCount.get(channelId); + if (objCount != null) { + number = (Integer)objCount; + } + notification.setNumber(number); + } + + private void setNotificationMessagingStyle(Notification.Builder notification, Bundle bundle) { + Notification.MessagingStyle messagingStyle = getMessagingStyle(bundle); + notification.setStyle(messagingStyle); + } + + private Notification.MessagingStyle getMessagingStyle(Bundle bundle) { + Notification.MessagingStyle messagingStyle; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + messagingStyle = new Notification.MessagingStyle(""); + } else { + String senderId = bundle.getString("sender_id"); + Person sender = new Person.Builder() + .setKey(senderId) + .setName("") + .build(); + messagingStyle = new Notification.MessagingStyle(sender); + } + + String conversationTitle = getConversationTitle(bundle); + setMessagingStyleConversationTitle(messagingStyle, conversationTitle, bundle); + addMessagingStyleMessages(messagingStyle, conversationTitle, bundle); + + return messagingStyle; + } + + private String getConversationTitle(Bundle bundle) { String title = null; + + String version = bundle.getString("version"); if (version != null && version.equals("v2")) { - title = channelName; + title = bundle.getString("channel_name"); } else { title = bundle.getString("title"); } @@ -211,149 +294,100 @@ public class CustomPushNotification extends PushNotification { title = mContext.getPackageManager().getApplicationLabel(appInfo).toString(); } - Bundle b = bundle.getBundle("userInfo"); - if (b == null) { - b = new Bundle(); - } - b.putString("channel_id", channelId); - notification.addExtras(b); - - int smallIconResId; - int largeIconResId; - - if (smallIcon != null) { - smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName); - } else { - smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName); - } - - if (smallIconResId == 0) { - smallIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName); - - if (smallIconResId == 0) { - smallIconResId = android.R.drawable.ic_dialog_info; - } - } - - if (largeIcon != null) { - largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName); - } else { - largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName); - } - - if (badge != null) { - int badgeCount = Integer.parseInt(badge); - CustomPushNotification.badgeCount = badgeCount; - notification.setNumber(badgeCount); - ApplicationBadgeHelper.instance.setApplicationIconBadgeNumber(mContext.getApplicationContext(), CustomPushNotification.badgeCount); - } + return title; + } + private void setMessagingStyleConversationTitle(Notification.MessagingStyle messagingStyle, String conversationTitle, Bundle bundle) { + String channelName = bundle.getString("channel_name"); + String senderName = bundle.getString("sender_name"); if (android.text.TextUtils.isEmpty(senderName)) { - senderName = getSenderName(senderName, channelName, bundle.getString("message")); + senderName = getSenderName(bundle); } - String personId = senderId; - if (!android.text.TextUtils.isEmpty(channelName)) { - personId = channelId; + if (conversationTitle != null && (!conversationTitle.startsWith("@") || channelName != senderName)) { + messagingStyle.setConversationTitle(conversationTitle); } + } - Notification.MessagingStyle messagingStyle; - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { - messagingStyle = new Notification.MessagingStyle(""); - } else { - Person sender = new Person.Builder() - .setKey(senderId) - .setName("") - .build(); - messagingStyle = new Notification.MessagingStyle(sender); - } - - if (title != null && (!title.startsWith("@") || channelName != senderName)) { - messagingStyle - .setConversationTitle(title); - } + private void addMessagingStyleMessages(Notification.MessagingStyle messagingStyle, String conversationTitle, Bundle bundle) { + List bundleList; + String channelId = bundle.getString("channel_id"); List bundleArray = channelIdToNotification.get(channelId); - List list; if (bundleArray != null) { - list = new ArrayList(bundleArray); + bundleList = new ArrayList(bundleArray); } else { - list = new ArrayList(); - list.add(bundle); + bundleList = new ArrayList(); + bundleList.add(bundle); } - int listCount = list.size() - 1; - for (int i = listCount; i >= 0; i--) { - Bundle data = list.get(i); + int bundleCount = bundleList.size() - 1; + for (int i = bundleCount; i >= 0; i--) { + Bundle data = bundleList.get(i); String message = data.getString("message"); - String previousPersonName = getSenderName(data.getString("sender_name"), channelName, message); - String previousPersonId = data.getString("sender_id"); - - if (title == null || !android.text.TextUtils.isEmpty(previousPersonName)) { - message = removeSenderFromMessage(previousPersonName, channelName, message); + String senderId = data.getString("sender_id"); + Bundle userInfoBundle = data.getBundle("userInfo"); + String senderName = getSenderName(data); + if (userInfoBundle != null) { + boolean localPushNotificationTest = userInfoBundle.getBoolean("localTest"); + if (localPushNotificationTest) { + senderName = "Test"; + } } + if (conversationTitle == null || !android.text.TextUtils.isEmpty(senderName.trim())) { + message = removeSenderNameFromMessage(message, senderName); + } + + long timestamp = data.getLong("time"); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { - messagingStyle.addMessage(message, data.getLong("time"), previousPersonName); + messagingStyle.addMessage(message, timestamp, senderName); } else { Person sender = new Person.Builder() - .setKey(previousPersonId) - .setName(previousPersonName) - .build(); - messagingStyle.addMessage(message, data.getLong("time"), sender); + .setKey(senderId) + .setName(senderName) + .build(); + messagingStyle.addMessage(message, timestamp, sender); } } + } - notification - .setContentIntent(intent) - .setGroupSummary(true) - .setStyle(messagingStyle) - .setSmallIcon(smallIconResId) - .setVisibility(Notification.VISIBILITY_PRIVATE) - .setPriority(Notification.PRIORITY_HIGH) - .setAutoCancel(true); + private void setNotificationChannel(Notification.Builder notification, Bundle bundle) { + // If Android Oreo or above we need to register a channel + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + NotificationChannel notificationChannel = mHighImportanceChannel; + + boolean localPushNotificationTest = false; + Bundle userInfoBundle = bundle.getBundle("userInfo"); + if (userInfoBundle != null) { + localPushNotificationTest = userInfoBundle.getBoolean("localTest"); + } + + if (mAppLifecycleFacade.isAppVisible() && !localPushNotificationTest) { + notificationChannel = mMinImportanceChannel; + } + + notification.setChannelId(notificationChannel.getId()); + } + + private void setNotificationBadgeIconType(Notification.Builder notification) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { notification.setBadgeIconType(Notification.BADGE_ICON_SMALL); } + } - // Let's add a delete intent when the notification is dismissed - Intent delIntent = new Intent(mContext, NotificationDismissService.class); - delIntent.putExtra(NOTIFICATION_ID, notificationId); - PendingIntent deleteIntent = NotificationIntentAdapter.createPendingNotificationIntent(mContext, delIntent, mNotificationProps); - notification.setDeleteIntent(deleteIntent); - + private void setNotificationGroup(Notification.Builder notification) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - notification.setGroup(GROUP_KEY_MESSAGES); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && postId != null) { - Intent replyIntent = new Intent(mContext, NotificationReplyBroadcastReceiver.class); - replyIntent.setAction(KEY_TEXT_REPLY); - replyIntent.putExtra(NOTIFICATION_ID, notificationId); - replyIntent.putExtra("pushNotification", bundle); - PendingIntent replyPendingIntent = PendingIntent.getBroadcast(mContext, notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY) - .setLabel("Reply") - .build(); - - Notification.Action replyAction = new Notification.Action.Builder( - R.drawable.ic_notif_action_reply, "Reply", replyPendingIntent) - .addRemoteInput(remoteInput) - .setAllowGeneratedReplies(true) - .build(); - notification - .setShowWhen(true) - .addAction(replyAction); - } - - Bitmap largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId); - if (largeIconResId != 0 && (largeIcon != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) { - notification.setLargeIcon(largeIconBitmap); + .setGroup(GROUP_KEY_MESSAGES) + .setGroupSummary(true); } + } + private void setNotificationSound(Notification.Builder notification, NotificationPreferences notificationPreferences) { String soundUri = notificationPreferences.getNotificationSound(); if (soundUri != null) { if (soundUri != "none") { @@ -363,65 +397,115 @@ public class CustomPushNotification extends PushNotification { Uri defaultUri = System.DEFAULT_NOTIFICATION_URI; notification.setSound(defaultUri, AudioManager.STREAM_NOTIFICATION); } + } + private void setNotificationVibrate(Notification.Builder notification, NotificationPreferences notificationPreferences) { boolean vibrate = notificationPreferences.getShouldVibrate(); if (vibrate) { - // use the system default for vibration + // Use the system default for vibration notification.setDefaults(Notification.DEFAULT_VIBRATE); } + } + private void setNotificationBlink(Notification.Builder notification, NotificationPreferences notificationPreferences) { boolean blink = notificationPreferences.getShouldBlink(); if (blink) { notification.setLights(Color.CYAN, 500, 500); } + } - return notification; + private void setNotificationDeleteIntent(Notification.Builder notification, int notificationId) { + // Let's add a delete intent when the notification is dismissed + Intent delIntent = new Intent(mContext, NotificationDismissService.class); + delIntent.putExtra(NOTIFICATION_ID, notificationId); + PendingIntent deleteIntent = NotificationIntentAdapter.createPendingNotificationIntent(mContext, delIntent, mNotificationProps); + notification.setDeleteIntent(deleteIntent); + } + + private void addNotificationReplyAction(Notification.Builder notification, int notificationId, Bundle bundle) { + String postId = bundle.getString("post_id"); + if (postId == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return; + } + + Intent replyIntent = new Intent(mContext, NotificationReplyBroadcastReceiver.class); + replyIntent.setAction(KEY_TEXT_REPLY); + replyIntent.putExtra(NOTIFICATION_ID, notificationId); + replyIntent.putExtra("pushNotification", bundle); + + PendingIntent replyPendingIntent = PendingIntent.getBroadcast( + mContext, + notificationId, + replyIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + + RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY) + .setLabel("Reply") + .build(); + + int icon = R.drawable.ic_notif_action_reply; + CharSequence title = "Reply"; + Notification.Action replyAction = new Notification.Action.Builder(icon, title, replyPendingIntent) + .addRemoteInput(remoteInput) + .setAllowGeneratedReplies(true) + .build(); + + notification + .setShowWhen(true) + .addAction(replyAction); } private void notifyReceivedToJS() { mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext()); } - public static Integer getMessageCountInChannel(String channelId) { - Object objCount = channelIdToNotificationCount.get(channelId); - if (objCount != null) { - return (Integer)objCount; - } - - return 1; - } - private void cancelNotification(Bundle data, int notificationId) { final String channelId = data.getString("channel_id"); - final String numberString = data.getString("badge"); + final String badge = data.getString("badge"); - CustomPushNotification.badgeCount = Integer.parseInt(numberString); + CustomPushNotification.badgeCount = Integer.parseInt(badge); CustomPushNotification.clearNotification(mContext.getApplicationContext(), notificationId, channelId); - - ApplicationBadgeHelper.instance.setApplicationIconBadgeNumber(mContext.getApplicationContext(), CustomPushNotification.badgeCount); } - private String getSenderName(String senderName, String channelName, String message) { + private String getSenderName(Bundle bundle) { + String senderName = bundle.getString("sender_name"); if (senderName != null) { return senderName; - } else if (channelName != null && channelName.startsWith("@")) { + } + + String channelName = bundle.getString("channel_name"); + if (channelName != null && channelName.startsWith("@")) { return channelName; } - String name = message.split(":")[0]; - if (name != message) { - return name; + String message = bundle.getString("message"); + if (message != null) { + String name = message.split(":")[0]; + if (name != message) { + return name; + } } return " "; } - private String removeSenderFromMessage(String senderName, String channelName, String message) { - String sender = String.format("%s", getSenderName(senderName, channelName, message)); - return message.replaceFirst(sender, "").replaceFirst(": ", "").trim(); + private String removeSenderNameFromMessage(String message, String senderName) { + return message.replaceFirst(senderName, "").replaceFirst(": ", "").trim(); } private void notificationReceiptDelivery(String ackId, String type) { ReceiptDelivery.send(context, ackId, type); } + + private void createNotificationChannels() { + final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + + mHighImportanceChannel = new NotificationChannel("channel_01", "High Importance", NotificationManager.IMPORTANCE_HIGH); + mHighImportanceChannel.setShowBadge(true); + notificationManager.createNotificationChannel(mHighImportanceChannel); + + mMinImportanceChannel = new NotificationChannel("channel_02", "Min Importance", NotificationManager.IMPORTANCE_MIN); + mMinImportanceChannel.setShowBadge(true); + notificationManager.createNotificationChannel(mMinImportanceChannel); + } } diff --git a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotificationDrawer.java b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotificationDrawer.java index 160a446be..7f3878091 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotificationDrawer.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotificationDrawer.java @@ -6,7 +6,7 @@ import com.wix.reactnativenotifications.core.AppLaunchHelper; import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer; import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer; import com.wix.reactnativenotifications.core.notificationdrawer.INotificationsDrawerApplication; -import com.wix.reactnativenotifications.helpers.PushNotificationHelper; + import static com.wix.reactnativenotifications.Defs.LOGTAG; public class CustomPushNotificationDrawer extends PushNotificationsDrawer { diff --git a/android/settings.gradle b/android/settings.gradle index 68f0dd54c..30b886887 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -36,7 +36,7 @@ project(':react-native-cookies').projectDir = new File(rootProject.projectDir, ' include ':react-native-vector-icons' project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') include ':reactnativenotifications' -project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android') +project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android/app') include ':app' include ':react-native-svg' diff --git a/app/push_notifications/push_notifications.android.js b/app/push_notifications/push_notifications.android.js index 97ac14965..a937c8f84 100644 --- a/app/push_notifications/push_notifications.android.js +++ b/app/push_notifications/push_notifications.android.js @@ -94,9 +94,8 @@ class PushNotification { NotificationsAndroid.cancelAllLocalNotifications(); } - setApplicationIconBadgeNumber(number) { - const count = number < 0 ? 0 : number; - NotificationsAndroid.setBadgesCount(count); + setApplicationIconBadgeNumber() { + // Not supported for Android } getNotification() { @@ -116,7 +115,6 @@ class PushNotification { } clearNotifications = () => { - this.setApplicationIconBadgeNumber(0); this.cancelAllLocalNotifications(); // TODO: Only cancel the local notifications that belong to this server } } diff --git a/app/push_notifications/push_notifications.ios.js b/app/push_notifications/push_notifications.ios.js index e9ac2e9a8..7036cca99 100644 --- a/app/push_notifications/push_notifications.ios.js +++ b/app/push_notifications/push_notifications.ios.js @@ -2,15 +2,23 @@ // See LICENSE.txt for license information. import {AppState} from 'react-native'; -import NotificationsIOS, {NotificationAction, NotificationCategory} from 'react-native-notifications'; +import NotificationsIOS, { + NotificationAction, + NotificationCategory, + DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT, + DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT, + DEVICE_NOTIFICATION_OPENED_EVENT, +} from 'react-native-notifications'; import {getBadgeCount} from 'app/selectors/views'; import ephemeralStore from 'app/store/ephemeral_store'; +import {getCurrentLocale} from 'app/selectors/i18n'; +import {getLocalizedMessage} from 'app/i18n'; +import {t} from 'app/utils/i18n'; const CATEGORY = 'CAN_REPLY'; const REPLY_ACTION = 'REPLY_ACTION'; -let replyCategory; const replies = new Set(); class PushNotification { @@ -21,24 +29,9 @@ class PushNotification { this.onReply = null; this.reduxStore = null; - NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onRemoteNotificationsRegistered); - NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground); - NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground); - NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened); - - const replyAction = new NotificationAction({ - activationMode: 'background', - title: 'Reply', - behavior: 'textInput', - authenticationRequired: true, - identifier: REPLY_ACTION, - }, this.handleReply); - - replyCategory = new NotificationCategory({ - identifier: CATEGORY, - actions: [replyAction], - context: 'default', - }); + NotificationsIOS.addEventListener(DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT, this.onRemoteNotificationsRegistered); + NotificationsIOS.addEventListener(DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT, this.onNotificationReceivedForeground); + NotificationsIOS.addEventListener(DEVICE_NOTIFICATION_OPENED_EVENT, this.onNotificationOpened); } handleNotification = (data, foreground, userInteraction) => { @@ -55,18 +48,14 @@ class PushNotification { } }; - handleReply = (action, completed) => { - if (action.identifier === REPLY_ACTION) { - const data = action.notification.getData(); - const text = action.text; - const badge = parseInt(action.notification._badge, 10) - 1; //eslint-disable-line no-underscore-dangle + handleReply = (notification, text, completion) => { + const data = notification.getData(); - if (this.onReply && !replies.has(action.completionKey)) { - replies.add(action.completionKey); - this.onReply(data, text, badge, completed); - } + if (this.onReply && !replies.has(data.identifier)) { + replies.add(data.identifier); + this.onReply(data, text, completion); } else { - completed(); + completion(); } }; @@ -76,9 +65,39 @@ class PushNotification { this.onNotification = options.onNotification; this.onReply = options.onReply; - this.requestPermissions([replyCategory]); + this.requestNotificationReplyPermissions(); + } - NotificationsIOS.consumeBackgroundQueue(); + requestNotificationReplyPermissions = () => { + const replyCategory = this.createReplyCategory(); + this.requestPermissions([replyCategory]); + } + + createReplyCategory = () => { + const {getState} = this.reduxStore; + const state = getState(); + const locale = getCurrentLocale(state); + + const replyTitle = getLocalizedMessage(locale, t('mobile.push_notification_reply.title')); + const replyButton = getLocalizedMessage(locale, t('mobile.push_notification_reply.button')); + const replyPlaceholder = getLocalizedMessage(locale, t('mobile.push_notification_reply.placeholder')); + + const replyAction = new NotificationAction({ + activationMode: 'background', + title: replyTitle, + textInput: { + buttonTitle: replyButton, + placeholder: replyPlaceholder, + }, + authenticationRequired: true, + identifier: REPLY_ACTION, + }); + + return new NotificationCategory({ + identifier: CATEGORY, + actions: [replyAction], + context: 'default', + }); } requestPermissions = (permissions) => { @@ -89,7 +108,7 @@ class PushNotification { if (notification.date) { const deviceNotification = { fireDate: notification.date.toISOString(), - alertBody: notification.message, + body: notification.message, alertAction: '', userInfo: notification.userInfo, }; @@ -100,7 +119,7 @@ class PushNotification { localNotification(notification) { this.deviceNotification = { - alertBody: notification.message, + body: notification.message, alertAction: '', userInfo: notification.userInfo, }; @@ -138,12 +157,17 @@ class PushNotification { this.handleNotification(info, true, false); }; - onNotificationOpened = (notification) => { - const info = { - ...notification.getData(), - message: notification.getMessage(), - }; - this.handleNotification(info, false, true); + onNotificationOpened = (notification, completion, action) => { + if (action.identifier === REPLY_ACTION) { + this.handleReply(notification, action.text, completion); + } else { + const info = { + ...notification.getData(), + message: notification.getMessage(), + }; + this.handleNotification(info, false, true); + completion(); + } }; onRemoteNotificationsRegistered = (deviceToken) => { @@ -165,6 +189,10 @@ class PushNotification { this.deviceNotification = null; } + getDeliveredNotifications(callback) { + NotificationsIOS.getDeliveredNotifications(callback); + } + clearChannelNotifications(channelId) { NotificationsIOS.getDeliveredNotifications((notifications) => { const ids = []; @@ -180,7 +208,7 @@ class PushNotification { for (let i = 0; i < notifications.length; i++) { const notification = notifications[i]; - if (notification.userInfo.channel_id === channelId) { + if (notification.channel_id === channelId) { ids.push(notification.identifier); } } diff --git a/app/push_notifications/push_notifications.ios.test.js b/app/push_notifications/push_notifications.ios.test.js index 51d249cb1..62ee2e6c5 100644 --- a/app/push_notifications/push_notifications.ios.test.js +++ b/app/push_notifications/push_notifications.ios.test.js @@ -39,25 +39,25 @@ describe('PushNotification', () => { // Three channel1 delivered notifications { identifier: 'channel1-1', - userInfo: {channel_id: channel1ID}, + channel_id: channel1ID, }, { identifier: 'channel1-2', - userInfo: {channel_id: channel1ID}, + channel_id: channel1ID, }, { identifier: 'channel1-3', - userInfo: {channel_id: channel1ID}, + channel_id: channel1ID, }, // Two channel2 delivered notifications { identifier: 'channel2-1', - userInfo: {channel_id: channel2ID}, + channel_id: channel2ID, }, { identifier: 'channel2-2', - userInfo: {channel_id: channel2ID}, + channel_id: channel2ID, }, ]; NotificationsIOS.setDeliveredNotifications(deliveredNotifications); @@ -73,8 +73,8 @@ describe('PushNotification', () => { await NotificationsIOS.getDeliveredNotifications(async (deliveredNotifs) => { expect(deliveredNotifs.length).toBe(2); - const channel1DeliveredNotifications = deliveredNotifs.filter((n) => n.userInfo.channel_id === channel1ID); - const channel2DeliveredNotifications = deliveredNotifs.filter((n) => n.userInfo.channel_id === channel2ID); + const channel1DeliveredNotifications = deliveredNotifs.filter((n) => n.channel_id === channel1ID); + const channel2DeliveredNotifications = deliveredNotifs.filter((n) => n.channel_id === channel2ID); expect(channel1DeliveredNotifications.length).toBe(0); expect(channel2DeliveredNotifications.length).toBe(2); }); diff --git a/app/utils/push_notifications.js b/app/utils/push_notifications.js index 84e726294..29e4dc274 100644 --- a/app/utils/push_notifications.js +++ b/app/utils/push_notifications.js @@ -102,7 +102,7 @@ class PushNotificationUtils { } }; - onPushNotificationReply = async (data, text, badge, completed) => { + onPushNotificationReply = async (data, text, completion) => { const {dispatch, getState} = this.store; const state = getState(); const credentials = await getAppCredentials(); // TODO Change to handle multiple servers @@ -144,23 +144,21 @@ class PushNotificationUtils { channel_id: data.channel_id, }, }); - completed(); + completion(); return; } - if (badge >= 0) { - PushNotifications.setApplicationIconBadgeNumber(badge); - } - - dispatch(markChannelViewedAndRead(data.channel_id)); this.replyNotificationData = null; - completed(); + + PushNotifications.getDeliveredNotifications((notifications) => { + PushNotifications.setApplicationIconBadgeNumber(notifications.length); + completion(); + }); } else { this.replyNotificationData = { data, text, - badge, - completed, + completion, }; } }; diff --git a/app/utils/push_notifications.test.js b/app/utils/push_notifications.test.js index 065f8cd6c..f61eca441 100644 --- a/app/utils/push_notifications.test.js +++ b/app/utils/push_notifications.test.js @@ -32,7 +32,6 @@ jest.mock('react-native-notifications', () => { addEventListener: jest.fn(), NotificationAction: jest.fn(), NotificationCategory: jest.fn(), - consumeBackgroundQueue: jest.fn(), localNotification: jest.fn(), }; }); @@ -49,9 +48,8 @@ describe('PushNotifications', () => { const channelID = 'channel-id'; const data = {channel_id: channelID}; const text = 'text'; - const badge = 1; - const completed = () => {}; - await PushNotificationUtils.onPushNotificationReply(data, text, badge, completed); + const completion = () => {}; + await PushNotificationUtils.onPushNotificationReply(data, text, completion); const storeActions = store.getActions(); const receivedPost = storeActions.some((action) => action.type === PostTypes.RECEIVED_POST); diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index bf311153b..6e9b3de02 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -383,6 +383,9 @@ "mobile.post.retry": "Refresh", "mobile.posts_view.moreMsg": "More New Messages Above", "mobile.privacy_link": "Privacy Policy", + "mobile.push_notification_reply.button": "Send", + "mobile.push_notification_reply.placeholder": "Write a reply...", + "mobile.push_notification_reply.title": "Reply", "mobile.reaction_header.all_emojis": "All", "mobile.recent_mentions.empty_description": "Messages containing your username and other words that trigger mentions will appear here.", "mobile.recent_mentions.empty_title": "No Recent Mentions", diff --git a/ios/Mattermost/AppDelegate.m b/ios/Mattermost/AppDelegate.m index 33cce9067..9222f9e7b 100644 --- a/ios/Mattermost/AppDelegate.m +++ b/ios/Mattermost/AppDelegate.m @@ -26,7 +26,7 @@ @implementation AppDelegate -NSString* const NotificationClearAction = @"clear"; +NSString* const NOTIFICATION_CLEAR_ACTION = @"clear"; -(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler { os_log(OS_LOG_DEFAULT, "Mattermost will attach session from handleEventsForBackgroundURLSession!! identifier=%{public}@", identifier); @@ -58,18 +58,14 @@ NSString* const NotificationClearAction = @"clear"; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil]; + [RNNotifications startMonitorNotifications]; + os_log(OS_LOG_DEFAULT, "Mattermost started!!"); return YES; } -// Required to register for notifications -- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings -{ - [RNNotifications didRegisterUserNotificationSettings:notificationSettings]; -} - - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; @@ -79,7 +75,34 @@ NSString* const NotificationClearAction = @"clear"; [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; } --(void)cleanNotificationsFromChannel:(NSString *)channelId andUpdateBadge:(BOOL)updateBadge { +// Required for the notification event. + +-(void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler { + UIApplicationState state = [UIApplication sharedApplication].applicationState; + NSString* action = [userInfo objectForKey:@"type"]; + NSString* channelId = [userInfo objectForKey:@"channel_id"]; + NSString* ackId = [userInfo objectForKey:@"ack_id"]; + + if (action && [action isEqualToString: NOTIFICATION_CLEAR_ACTION]) { + // If received a notification that a channel was read, remove all notifications from that channel (only with app in foreground/background) + [self cleanNotificationsFromChannel:channelId]; + RuntimeUtils *utils = [[RuntimeUtils alloc] init]; + [[UploadSession shared] notificationReceiptWithNotificationId:ackId receivedAt:round([[NSDate date] timeIntervalSince1970] * 1000.0) type:action]; + [utils delayWithSeconds:0.2 closure:^(void) { + // This is to notify the NotificationCenter that something has changed. + completionHandler(UIBackgroundFetchResultNewData); + }]; + + return; + } else if (state == UIApplicationStateInactive) { + // When the notification is opened + [self cleanNotificationsFromChannel:channelId]; + } + + completionHandler(UIBackgroundFetchResultNoData); +} + +-(void)cleanNotificationsFromChannel:(NSString *)channelId { if ([UNUserNotificationCenter class]) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) { @@ -97,64 +120,10 @@ NSString* const NotificationClearAction = @"clear"; } [center removeDeliveredNotificationsWithIdentifiers:notificationIds]; - NSInteger removed = (NSInteger)[notificationIds count] + 1; - if (removed > 0 && updateBadge) { - NSInteger badge = [UIApplication sharedApplication].applicationIconBadgeNumber; - NSInteger count = badge - removed; - if (count > 0) { - [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count]; - } else { - [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; - } - } }]; } } -// Required for the notification event. --(void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler { - UIApplicationState state = [UIApplication sharedApplication].applicationState; - NSString* action = [userInfo objectForKey:@"type"]; - NSString* channelId = [userInfo objectForKey:@"channel_id"]; - NSString* ackId = [userInfo objectForKey:@"ack_id"]; - - if (action && [action isEqualToString: NotificationClearAction]) { - // If received a notification that a channel was read, remove all notifications from that channel (only with app in foreground/background) - [self cleanNotificationsFromChannel:channelId andUpdateBadge:NO]; - RuntimeUtils *utils = [[RuntimeUtils alloc] init]; - [[UploadSession shared] notificationReceiptWithNotificationId:ackId receivedAt:round([[NSDate date] timeIntervalSince1970] * 1000.0) type:action]; - [utils delayWithSeconds:0.2 closure:^(void) { - // This is to notify the NotificationCenter that something has changed. - completionHandler(UIBackgroundFetchResultNewData); - }]; - - return; - } else if (state == UIApplicationStateInactive) { - // When the notification is opened - [self cleanNotificationsFromChannel:channelId andUpdateBadge:NO]; - } - - [RNNotifications didReceiveRemoteNotification:userInfo]; - completionHandler(UIBackgroundFetchResultNoData); -} - -// Required for the localNotification event. -- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification -{ - [RNNotifications didReceiveLocalNotification:notification]; -} - -// Required for the notification actions. -- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [RNNotifications handleActionWithIdentifier:identifier forLocalNotification:notification withResponseInfo:responseInfo completionHandler:completionHandler]; -} - -- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [RNNotifications handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:completionHandler]; -} - // Required for deeplinking - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url diff --git a/package-lock.json b/package-lock.json index 0b847596f..c92ce674e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16750,8 +16750,9 @@ } }, "react-native-notifications": { - "version": "github:mattermost/react-native-notifications#1b7ec8513606b42237ab4674de9dacb4d1935e38", - "from": "github:mattermost/react-native-notifications#1b7ec8513606b42237ab4674de9dacb4d1935e38", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/react-native-notifications/-/react-native-notifications-2.0.6.tgz", + "integrity": "sha512-NFx5ADlqfQYTFkKWvd/GxM8rxKf1lSWJZJY0jbydAOZAuhnKFR/CsH7Mpx6T+9pY5Z3rvu7UzBtVn9LTBx0jYg==", "requires": { "core-js": "^1.0.0", "uuid": "^2.0.3" diff --git a/package.json b/package.json index cb07d28a2..c3010e5ed 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "react-native-linear-gradient": "2.5.4", "react-native-local-auth": "github:mattermost/react-native-local-auth#cc9ce2f468fbf7b431dfad3191a31aaa9227a6ab", "react-native-navigation": "2.21.1", - "react-native-notifications": "github:mattermost/react-native-notifications#1b7ec8513606b42237ab4674de9dacb4d1935e38", + "react-native-notifications": "2.0.6", "react-native-passcode-status": "1.1.1", "react-native-permissions": "1.1.1", "react-native-safe-area": "0.5.1", diff --git a/patches/react-native-notifications+2.0.6.patch b/patches/react-native-notifications+2.0.6.patch new file mode 100644 index 000000000..56857ce1d --- /dev/null +++ b/patches/react-native-notifications+2.0.6.patch @@ -0,0 +1,378 @@ +diff --git a/node_modules/react-native-notifications/android/app/src/main/AndroidManifest.xml b/node_modules/react-native-notifications/android/app/src/main/AndroidManifest.xml +index ffef75f..a4df210 100644 +--- a/node_modules/react-native-notifications/android/app/src/main/AndroidManifest.xml ++++ b/node_modules/react-native-notifications/android/app/src/main/AndroidManifest.xml +@@ -32,6 +32,8 @@ + ++ ++ + + + +diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java +index 8fb5f01..74d6138 100644 +--- a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java ++++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java +@@ -103,12 +103,26 @@ public class RNNotificationsModule extends ReactContextBaseJavaModule implements + pushNotification.onPostRequest(notificationId); + } + ++ @ReactMethod ++ public void scheduleLocalNotification(ReadableMap notificationPropsMap, int notificationId) { ++ Log.d(LOGTAG, "Native method invocation: scheduleLocalNotification"); ++ final Bundle notificationProps = Arguments.toBundle(notificationPropsMap); ++ final IPushNotification pushNotification = PushNotification.get(getReactApplicationContext().getApplicationContext(), notificationProps); ++ pushNotification.onScheduleRequest(notificationId); ++ } ++ + @ReactMethod + public void cancelLocalNotification(int notificationId) { + IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); + notificationsDrawer.onNotificationClearRequest(notificationId); + } + ++ @ReactMethod ++ public void cancelAllLocalNotifications() { ++ IPushNotificationsDrawer notificationDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext()); ++ notificationDrawer.onCancelAllLocalNotifications(); ++ } ++ + @ReactMethod + public void isRegisteredForRemoteNotifications(Promise promise) { + boolean hasPermission = NotificationManagerCompat.from(getReactApplicationContext()).areNotificationsEnabled(); +diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/helpers/ScheduleNotificationHelper.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/helpers/ScheduleNotificationHelper.java +new file mode 100644 +index 0000000..c35076d +--- /dev/null ++++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/helpers/ScheduleNotificationHelper.java +@@ -0,0 +1,90 @@ ++package com.wix.reactnativenotifications.core.helpers; ++ ++import android.app.AlarmManager; ++import android.os.Build; ++import android.os.Bundle; ++import android.content.Context; ++import android.content.Intent; ++import android.app.PendingIntent; ++import android.content.SharedPreferences; ++import android.util.Log; ++ ++import com.wix.reactnativenotifications.core.notification.PushNotificationProps; ++import com.wix.reactnativenotifications.core.notification.PushNotificationPublisher; ++ ++import static com.wix.reactnativenotifications.Defs.LOGTAG; ++ ++public class ScheduleNotificationHelper { ++ public static ScheduleNotificationHelper sInstance; ++ public static final String PREFERENCES_KEY = "rn_push_notification"; ++ static final String NOTIFICATION_ID = "notificationId"; ++ ++ private final SharedPreferences scheduledNotificationsPersistence; ++ protected final Context mContext; ++ ++ private ScheduleNotificationHelper(Context context) { ++ this.mContext = context; ++ this.scheduledNotificationsPersistence = context.getSharedPreferences(ScheduleNotificationHelper.PREFERENCES_KEY, Context.MODE_PRIVATE); ++ } ++ ++ public static ScheduleNotificationHelper getInstance(Context context) { ++ if (sInstance == null) { ++ sInstance = new ScheduleNotificationHelper(context); ++ } ++ return sInstance; ++ } ++ ++ public PendingIntent createPendingNotificationIntent(Bundle bundle) { ++ Integer notificationId = Integer.valueOf(bundle.getString("id")); ++ Intent notificationIntent = new Intent(mContext, PushNotificationPublisher.class); ++ notificationIntent.putExtra(ScheduleNotificationHelper.NOTIFICATION_ID, notificationId); ++ notificationIntent.putExtras(bundle); ++ return PendingIntent.getBroadcast(mContext, notificationId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); ++ } ++ ++ public void schedulePendingNotificationIntent(PendingIntent intent, long fireDate) { ++ AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); ++ ++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { ++ alarmManager.setExact(AlarmManager.RTC_WAKEUP, fireDate, intent); ++ } else { ++ alarmManager.set(AlarmManager.RTC_WAKEUP, fireDate, intent); ++ } ++ } ++ ++ public void cancelScheduledNotificationIntent(PendingIntent intent) { ++ AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); ++ alarmManager.cancel(intent); ++ } ++ ++ public boolean savePreferences(String notificationId, PushNotificationProps notificationProps) { ++ SharedPreferences.Editor editor = scheduledNotificationsPersistence.edit(); ++ editor.putString(notificationId, notificationProps.toString()); ++ commit(editor); ++ ++ return scheduledNotificationsPersistence.contains(notificationId); ++ } ++ ++ public void removePreference(String notificationId) { ++ if (scheduledNotificationsPersistence.contains(notificationId)) { ++ // remove it from local storage ++ SharedPreferences.Editor editor = scheduledNotificationsPersistence.edit(); ++ editor.remove(notificationId); ++ commit(editor); ++ } else { ++ Log.w(LOGTAG, "Unable to find notification " + notificationId); ++ } ++ } ++ ++ public java.util.Set getPreferencesKeys() { ++ return scheduledNotificationsPersistence.getAll().keySet(); ++ } ++ ++ private static void commit(SharedPreferences.Editor editor) { ++ if (Build.VERSION.SDK_INT < 9) { ++ editor.commit(); ++ } else { ++ editor.apply(); ++ } ++ } ++} +\ No newline at end of file +diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/IPushNotification.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/IPushNotification.java +index 0d70024..47b962e 100644 +--- a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/IPushNotification.java ++++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/IPushNotification.java +@@ -26,5 +26,20 @@ public interface IPushNotification { + */ + int onPostRequest(Integer notificationId); + ++ /** ++ * Handle a request to schedule this notification. ++ * ++ * @param notificationId The specific ID to associated with the notification. ++ */ ++ void onScheduleRequest(Integer notificationId); ++ ++ /** ++ * Handle a request to post this scheduled notification. ++ * ++ * @param notificationId The specific ID to associated with the notification. ++ * @return The ID assigned to the notification. ++ */ ++ int onPostScheduledRequest(Integer notificationId); ++ + PushNotificationProps asProps(); + } +diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java +index 5e4e3d2..871e157 100644 +--- a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java ++++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java +@@ -1,5 +1,6 @@ + package com.wix.reactnativenotifications.core.notification; + ++import android.app.AlarmManager; + import android.app.Notification; + import android.app.NotificationChannel; + import android.app.NotificationManager; +@@ -20,7 +21,9 @@ import com.wix.reactnativenotifications.core.InitialNotificationHolder; + import com.wix.reactnativenotifications.core.JsIOHelper; + import com.wix.reactnativenotifications.core.NotificationIntentAdapter; + import com.wix.reactnativenotifications.core.ProxyService; ++import com.wix.reactnativenotifications.core.helpers.ScheduleNotificationHelper; + ++import static com.wix.reactnativenotifications.Defs.LOGTAG; + import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME; + import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME; + import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME; +@@ -80,6 +83,41 @@ public class PushNotification implements IPushNotification { + return postNotification(notificationId); + } + ++ @Override ++ public void onScheduleRequest(Integer notificationId) { ++ Bundle bundle = mNotificationProps.asBundle(); ++ ++ if (bundle.getString("message") == null) { ++ Log.e(LOGTAG, "No message specified for the scheduled notification"); ++ return; ++ } ++ ++ double date = bundle.getDouble("fireDate"); ++ if (date == 0) { ++ Log.e(LOGTAG, "No date specified for the scheduled notification"); ++ return; ++ } ++ ++ ScheduleNotificationHelper helper = ScheduleNotificationHelper.getInstance(mContext); ++ String notificationIdStr = Integer.toString(notificationId); ++ boolean isSaved = helper.savePreferences(notificationIdStr, mNotificationProps); ++ if (!isSaved) { ++ Log.e(LOGTAG, "Failed to save preference for notificationId " + notificationIdStr); ++ } ++ ++ PendingIntent pendingIntent = helper.createPendingNotificationIntent(bundle); ++ long fireDate = (long) date; ++ helper.schedulePendingNotificationIntent(pendingIntent, fireDate); ++ } ++ ++ @Override ++ public int onPostScheduledRequest(Integer notificationId) { ++ ScheduleNotificationHelper helper = ScheduleNotificationHelper.getInstance(mContext); ++ helper.removePreference(String.valueOf(notificationId)); ++ ++ return postNotification(notificationId); ++ } ++ + @Override + public PushNotificationProps asProps() { + return mNotificationProps.copy(); +@@ -140,11 +178,12 @@ public class PushNotification implements IPushNotification { + } + + protected Notification buildNotification(PendingIntent intent) { +- return getNotificationBuilder(intent).build(); ++ Notification.Builder builder = getNotificationBuilder(intent); ++ Notification notification = builder.build(); ++ return notification; + } + + protected Notification.Builder getNotificationBuilder(PendingIntent intent) { +- + String CHANNEL_ID = "channel_01"; + String CHANNEL_NAME = "Channel Name"; + +diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotificationPublisher.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotificationPublisher.java +new file mode 100644 +index 0000000..5b64593 +--- /dev/null ++++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotificationPublisher.java +@@ -0,0 +1,27 @@ ++package com.wix.reactnativenotifications.core.notification; ++ ++import android.app.Application; ++import android.content.BroadcastReceiver; ++import android.content.Context; ++import android.content.Intent; ++import android.util.Log; ++ ++import static com.wix.reactnativenotifications.Defs.LOGTAG; ++ ++public class PushNotificationPublisher extends BroadcastReceiver { ++ final static String NOTIFICATION_ID = "notificationId"; ++ ++ @Override ++ public void onReceive(Context context, Intent intent) { ++ Log.d(LOGTAG, "Received scheduled notification intent"); ++ int notificationId = intent.getIntExtra(NOTIFICATION_ID, 0); ++ long currentTime = System.currentTimeMillis(); ++ ++ Application applicationContext = (Application) context.getApplicationContext(); ++ final IPushNotification pushNotification = PushNotification.get(applicationContext, intent.getExtras()); ++ ++ Log.i(LOGTAG, "PushNotificationPublisher: Prepare To Publish: " + notificationId + ", Now Time: " + currentTime); ++ ++ pushNotification.onPostScheduledRequest(notificationId); ++ } ++} +\ No newline at end of file +diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/IPushNotificationsDrawer.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/IPushNotificationsDrawer.java +index 3be3dc1..7027958 100644 +--- a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/IPushNotificationsDrawer.java ++++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/IPushNotificationsDrawer.java +@@ -9,4 +9,5 @@ public interface IPushNotificationsDrawer { + + void onNotificationOpened(); + void onNotificationClearRequest(int id); ++ void onCancelAllLocalNotifications(); + } +diff --git a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java +index 7b320e1..d95535b 100644 +--- a/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java ++++ b/node_modules/react-native-notifications/android/app/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java +@@ -2,10 +2,16 @@ package com.wix.reactnativenotifications.core.notificationdrawer; + + import android.app.Activity; + import android.app.NotificationManager; ++import android.app.PendingIntent; + import android.content.Context; ++import android.os.Bundle; ++import android.util.Log; + + import com.wix.reactnativenotifications.core.AppLaunchHelper; + import com.wix.reactnativenotifications.core.InitialNotificationHolder; ++import com.wix.reactnativenotifications.core.helpers.ScheduleNotificationHelper; ++ ++import static com.wix.reactnativenotifications.Defs.LOGTAG; + + public class PushNotificationsDrawer implements IPushNotificationsDrawer { + +@@ -60,8 +66,41 @@ public class PushNotificationsDrawer implements IPushNotificationsDrawer { + notificationManager.cancel(id); + } + ++ @Override ++ public void onCancelAllLocalNotifications() { ++ clearAll(); ++ cancelAllScheduledNotifications(); ++ } ++ + protected void clearAll() { + final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancelAll(); + } ++ ++ protected void cancelAllScheduledNotifications() { ++ Log.i(LOGTAG, "Cancelling all scheduled notifications"); ++ ScheduleNotificationHelper helper = ScheduleNotificationHelper.getInstance(mContext); ++ ++ for (String notificationId : helper.getPreferencesKeys()) { ++ cancelScheduledNotification(notificationId); ++ } ++ } ++ ++ protected void cancelScheduledNotification(String notificationId) { ++ Log.i(LOGTAG, "Cancelling scheduled notification: " + notificationId); ++ ++ ScheduleNotificationHelper helper = ScheduleNotificationHelper.getInstance(mContext); ++ ++ // Remove it from the alarm manger schedule ++ Bundle bundle = new Bundle(); ++ bundle.putString("id", notificationId); ++ PendingIntent pendingIntent = helper.createPendingNotificationIntent(bundle); ++ helper.cancelScheduledNotificationIntent(pendingIntent); ++ ++ helper.removePreference(notificationId); ++ ++ // Remove it from the notification center ++ final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); ++ notificationManager.cancel(Integer.parseInt(notificationId)); ++ } + } +diff --git a/node_modules/react-native-notifications/lib/src/index.android.js b/node_modules/react-native-notifications/lib/src/index.android.js +index 51376bf..a5d9540 100644 +--- a/node_modules/react-native-notifications/lib/src/index.android.js ++++ b/node_modules/react-native-notifications/lib/src/index.android.js +@@ -67,9 +67,22 @@ export class NotificationsAndroid { + return id; + } + ++ static scheduleLocalNotification(notification: Object) { ++ const id = Math.random() * 100000000 | 0; // Bitwise-OR forces value onto a 32bit limit ++ if (!notification.hasOwnProperty('id')) { ++ notification.id = id.toString(); ++ } ++ RNNotifications.scheduleLocalNotification(notification, id); ++ return id; ++ } ++ + static cancelLocalNotification(id) { + RNNotifications.cancelLocalNotification(id); + } ++ ++ static cancelAllLocalNotifications() { ++ RNNotifications.cancelAllLocalNotifications(); ++ } + } + + export class PendingNotifications {