diff --git a/.babelrc b/.babelrc index 9762634..38fda4d 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,3 @@ { "presets": ["react-native-stage-0/decorator-support"] -} \ No newline at end of file +} diff --git a/.flowconfig b/.flowconfig index 876e701..a76425e 100644 --- a/.flowconfig +++ b/.flowconfig @@ -22,6 +22,8 @@ node_modules/react-native/flow flow/ [options] +emoji=true + module.system=haste experimental.strict_type_args=true @@ -34,11 +36,12 @@ suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-6]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-6]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-2]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-2]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError unsafe.enable_getters_and_setters=true [version] -^0.36.0 +^0.42.0 diff --git a/.gitignore b/.gitignore index fc13f16..a824475 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # Xcode # + build/ *.pbxuser !default.pbxuser @@ -34,11 +35,11 @@ local.properties # node_modules/ npm-debug.log +yarn-error.log # BUCK buck-out/ \.buckd/ -android/app/libs *.keystore # fastlane diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 0000000..9eb791c --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,53 @@ +Tekken Chicken Terms of Service and Privacy Policy + +1. Terms + +By accessing the Tekken Chicken app, you are agreeing to be bound by these terms of service, all applicable laws and regulations, and agree that you are responsible for compliance with any applicable local laws. If you do not agree with any of these terms, you are prohibited from using or accessing this site. The materials contained in this website are protected by applicable copyright and trademark law. + +2. Use License + +Permission is granted to temporarily download one copy of the materials (information or software) on Tekken Chicken's website for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title, and under this license you may not: +modify or copy the materials; +use the materials for any commercial purpose, or for any public display (commercial or non-commercial); +attempt to decompile or reverse engineer any software contained on Tekken Chicken's website; +remove any copyright or other proprietary notations from the materials; or +transfer the materials to another person or "mirror" the materials on any other server. +This license shall automatically terminate if you violate any of these restrictions and may be terminated by Tekken Chicken at any time. Upon terminating your viewing of these materials or upon the termination of this license, you must destroy any downloaded materials in your possession whether in electronic or printed format. +3. Disclaimer + +The materials on Tekken Chicken's website are provided on an 'as is' basis. Tekken Chicken makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights. +Further, Tekken Chicken does not warrant or make any representations concerning the accuracy, likely results, or reliability of the use of the materials on its website or otherwise relating to such materials or on any sites linked to this site. +4. Limitations + +In no event shall Tekken Chicken or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on Tekken Chicken's website, even if Tekken Chicken or a Tekken Chicken authorized representative has been notified orally or in writing of the possibility of such damage. Because some jurisdictions do not allow limitations on implied warranties, or limitations of liability for consequential or incidental damages, these limitations may not apply to you. + +5. Accuracy of materials + +The materials appearing on Tekken Chicken's website could include technical, typographical, or photographic errors. Tekken Chicken does not warrant that any of the materials on its website are accurate, complete or current. Tekken Chicken may make changes to the materials contained on its website at any time without notice. However Tekken Chicken does not make any commitment to update the materials. + +6. Links + +Tekken Chicken has not reviewed all of the sites linked to its website and is not responsible for the contents of any such linked site. The inclusion of any link does not imply endorsement by Tekken Chicken of the site. Use of any such linked website is at the user's own risk. + +7. Modifications + +Tekken Chicken may revise these terms of service for its website at any time without notice. By using this website you are agreeing to be bound by the then current version of these terms of service. + +8. Governing Law + +These terms and conditions are governed by and construed in accordance with the laws of Boston, MA and you irrevocably submit to the exclusive jurisdiction of the courts in that State or location. + +Privacy Policy + +Your privacy is important to us. + +It is Tekken Chicken's policy to respect your privacy regarding any information we may collect while operating our website. Accordingly, we have developed this privacy policy in order for you to understand how we collect, use, communicate, disclose and otherwise make use of personal information. We have outlined our privacy policy below. + +We will collect personal information by lawful and fair means and, where appropriate, with the knowledge or consent of the individual concerned. +Before or at the time of collecting personal information, we will identify the purposes for which information is being collected. +We will collect and use personal information solely for fulfilling those purposes specified by us and for other ancillary purposes, unless we obtain the consent of the individual concerned or as required by law. +Personal data should be relevant to the purposes for which it is to be used, and, to the extent necessary for those purposes, should be accurate, complete, and up-to-date. +We will protect personal information by using reasonable security safeguards against loss or theft, as well as unauthorized access, disclosure, copying, use or modification. +We will make readily available to customers information about our policies and practices relating to the management of personal information. +We will only retain personal information for as long as necessary for the fulfilment of those purposes. +We are committed to conducting our business in accordance with these principles in order to ensure that the confidentiality of personal information is protected and maintained. Tekken Chicken may change this privacy policy from time to time at Tekken Chicken's sole discretion. diff --git a/README.md b/README.md index 976c369..466a03c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ Before you get started, make sure you have the following dependencies installed Install dependencies $ npm install + $ react-native link ### Running the iOS application @@ -31,12 +32,25 @@ Install dependencies $ (cd ios; pod init; pod repo update; pod install) -3. Build the app and run the simulator: +3. Build the app and run the simulator via react-native: $ react-native run-ios **In the case, that this error `Print: Entry, ":CFBundleIdentifier", Does Not Exist` occurs on build, you may need to run `react-native upgrade` to run the latest version of react-native.** +4. Build the app and run on an iPhone via xcode: + +1. Open `ios/T7Chicken.xcworkspace` with Xcode +2. Plug in your device via USB cable +3. Change 'Iphone 7Plus' to your device +4. Click **View->Navigators->Project Navigator** +5. Scroll down to 'Signing' and add a signature. You will need an Apple developer account. +6. Click the 'Play' button to build and run on your device + +[More Information on running on a device](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/LaunchingYourApponDevices/LaunchingYourApponDevices.html) + +**If you see a linker error when trying to build the app, close the project and make sure you open the .xcworkspace file and not the .xcodeproj file** + ### Running the Android application More details here: [React Native Android Setup](https://facebook.github.io/react-native/docs/android-setup.html) @@ -98,3 +112,6 @@ $ brew install gradle 10. Build app and run emulator: $ react-native run-android + +### Contributing +Feel free to clone or fork this repo if you'd like to make any changes. `master branch` is the working branch, `develop` is used for any changes. Make a branch off of `develop`, once you confirm that your changes are good to go submit the Pull Request to `develop`. From there we will merge into master after review. diff --git a/android/app/BUCK b/android/app/BUCK index d0cd240..c398c42 100644 --- a/android/app/BUCK +++ b/android/app/BUCK @@ -1,5 +1,3 @@ -import re - # To learn about Buck see [Docs](https://buckbuild.com/). # To run your application with Buck: # - install Buck @@ -11,8 +9,9 @@ import re # lib_deps = [] + for jarfile in glob(['libs/*.jar']): - name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) + name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] lib_deps.append(':' + name) prebuilt_jar( name = name, @@ -20,7 +19,7 @@ for jarfile in glob(['libs/*.jar']): ) for aarfile in glob(['libs/*.aar']): - name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) + name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] lib_deps.append(':' + name) android_prebuilt_aar( name = name, @@ -28,39 +27,39 @@ for aarfile in glob(['libs/*.aar']): ) android_library( - name = 'all-libs', - exported_deps = lib_deps + name = "all-libs", + exported_deps = lib_deps, ) android_library( - name = 'app-code', - srcs = glob([ - 'src/main/java/**/*.java', - ]), - deps = [ - ':all-libs', - ':build_config', - ':res', - ], + name = "app-code", + srcs = glob([ + "src/main/java/**/*.java", + ]), + deps = [ + ":all-libs", + ":build_config", + ":res", + ], ) android_build_config( - name = 'build_config', - package = 'com.t7chickennative', + name = "build_config", + package = "com.t7chicken", ) android_resource( - name = 'res', - res = 'src/main/res', - package = 'com.t7chickennative', + name = "res", + package = "com.t7chicken", + res = "src/main/res", ) android_binary( - name = 'app', - package_type = 'debug', - manifest = 'src/main/AndroidManifest.xml', - keystore = '//android/keystores:debug', - deps = [ - ':app-code', - ], + name = "app", + keystore = "//android/keystores:debug", + manifest = "src/main/AndroidManifest.xml", + package_type = "debug", + deps = [ + ":app-code", + ], ) diff --git a/android/app/build.gradle b/android/app/build.gradle index d554866..bdbd6a0 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -58,7 +58,7 @@ import com.android.build.OutputFile * inputExcludes: ["android/**", "ios/**"], * * // override which node gets called and with what additional arguments - * nodeExecutableAndArgs: ["node"] + * nodeExecutableAndArgs: ["node"], * * // supply additional arguments to the packager * extraPackagerArgs: [] @@ -83,11 +83,11 @@ def enableSeparateBuildPerCPUArchitecture = false def enableProguardInReleaseBuilds = false android { - compileSdkVersion 24 - buildToolsVersion "24.0.3" + compileSdkVersion 23 + buildToolsVersion "23.0.1" defaultConfig { - applicationId "com.t7chickennative" + applicationId "com.t7chicken" minSdkVersion 16 targetSdkVersion 22 versionCode 1 @@ -95,7 +95,17 @@ android { ndk { abiFilters "armeabi-v7a", "x86" } - } + } + signingConfigs { + release { + if (project.hasProperty('TC_RELEASE_STORE_FILE')) { + storeFile file(TC_RELEASE_STORE_FILE) + storePassword TC_RELEASE_STORE_PASSWORD + keyAlias TC_RELEASE_KEY_ALIAS + keyPassword TC_RELEASE_KEY_PASSWORD + } + } + } splits { abi { reset() @@ -108,6 +118,7 @@ android { release { minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + signingConfig signingConfigs.release } } // applicationVariants are e.g. debug, release @@ -126,6 +137,9 @@ android { } dependencies { + compile project(':react-native-device-info') + compile project(':react-native-mixpanel') + compile project(':react-native-linear-gradient') compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" compile "com.facebook.react:react-native:+" // From node_modules diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro index 48361a9..6e8516c 100644 --- a/android/app/proguard-rules.pro +++ b/android/app/proguard-rules.pro @@ -50,6 +50,10 @@ -dontwarn com.facebook.react.** +# TextLayoutBuilder uses a non-public Android constructor within StaticLayout. +# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. +-dontwarn android.text.StaticLayout + # okhttp -keepattributes Signature diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 15c5ef0..845df9b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,10 +1,11 @@ + + android:configChanges="keyboard|keyboardHidden|orientation|screenSize" + android:windowSoftInputMode="adjustResize"> diff --git a/android/app/src/main/java/com/t7chickennative/MainActivity.java b/android/app/src/main/java/com/t7chicken/MainActivity.java similarity index 83% rename from android/app/src/main/java/com/t7chickennative/MainActivity.java rename to android/app/src/main/java/com/t7chicken/MainActivity.java index d3398ee..abad754 100644 --- a/android/app/src/main/java/com/t7chickennative/MainActivity.java +++ b/android/app/src/main/java/com/t7chicken/MainActivity.java @@ -1,4 +1,4 @@ -package com.t7chickennative; +package com.t7chicken; import com.facebook.react.ReactActivity; @@ -10,6 +10,6 @@ public class MainActivity extends ReactActivity { */ @Override protected String getMainComponentName() { - return "t7ChickenNative"; + return "T7Chicken"; } } diff --git a/android/app/src/main/java/com/t7chickennative/MainApplication.java b/android/app/src/main/java/com/t7chicken/MainApplication.java similarity index 71% rename from android/app/src/main/java/com/t7chickennative/MainApplication.java rename to android/app/src/main/java/com/t7chicken/MainApplication.java index 4bf2401..6125107 100644 --- a/android/app/src/main/java/com/t7chickennative/MainApplication.java +++ b/android/app/src/main/java/com/t7chicken/MainApplication.java @@ -1,10 +1,11 @@ -package com.t7chickennative; +package com.t7chicken; import android.app.Application; -import android.util.Log; import com.facebook.react.ReactApplication; -import com.facebook.react.ReactInstanceManager; +import com.learnium.RNDeviceInfo.RNDeviceInfo; +import com.kevinejohn.RNMixpanel.RNMixpanel; +import com.BV.LinearGradient.LinearGradientPackage; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; @@ -17,14 +18,17 @@ public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override - protected boolean getUseDeveloperSupport() { + public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List getPackages() { return Arrays.asList( - new MainReactPackage() + new MainReactPackage(), + new RNDeviceInfo(), + new RNMixpanel(), + new LinearGradientPackage() ); } }; diff --git a/android/app/src/main/res/ic_launcher.png b/android/app/src/main/res/ic_launcher.png deleted file mode 100644 index 26c3c71..0000000 Binary files a/android/app/src/main/res/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 26c3c71..f0a1aac 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 198099a..d205e73 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d49ad3f..191aab3 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index bdcdca6..d21762c 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - t7ChickenNative + T7Chicken diff --git a/android/build.gradle b/android/build.gradle index fcba4c5..eed9972 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.1' + classpath 'com.android.tools.build:gradle:2.2.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/android/gradle.properties b/android/gradle.properties index c624bad..379a9cc 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -18,9 +18,7 @@ # org.gradle.parallel=true android.useDeprecatedNdk=true - -systemProp.http.proxyHost=proxyHost -systemProp.http.proxyPort=proxyPort - -systemProp.https.proxyHost=proxyHost -systemProp.https.proxyPort=proxyPort +TC_RELEASE_STORE_FILE=tc-release-key.keystore +TC_RELEASE_KEY_ALIAS=tc-release-key +TC_RELEASE_STORE_PASSWORD=0100403Nn +TC_RELEASE_KEY_PASSWORD=0100403Nn diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index b9fbfab..dbdc05d 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/android/keystores/BUCK b/android/keystores/BUCK index 15da20e..88e4c31 100644 --- a/android/keystores/BUCK +++ b/android/keystores/BUCK @@ -1,8 +1,8 @@ keystore( - name = 'debug', - store = 'debug.keystore', - properties = 'debug.keystore.properties', - visibility = [ - 'PUBLIC', - ], + name = "debug", + properties = "debug.keystore.properties", + store = "debug.keystore", + visibility = [ + "PUBLIC", + ], ) diff --git a/android/settings.gradle b/android/settings.gradle index de8f766..00da0f1 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,3 +1,9 @@ -rootProject.name = 't7ChickenNative' +rootProject.name = 'T7Chicken' +include ':react-native-device-info' +project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android') +include ':react-native-mixpanel' +project(':react-native-mixpanel').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-mixpanel/android') +include ':react-native-linear-gradient' +project(':react-native-linear-gradient').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-linear-gradient/android') include ':app' diff --git a/app.json b/app.json new file mode 100644 index 0000000..192d0f2 --- /dev/null +++ b/app.json @@ -0,0 +1,4 @@ +{ + "name": "T7Chicken", + "displayName": "T7Chicken" +} diff --git a/index.android.js b/index.android.js index c46fd1b..c1721b6 100644 --- a/index.android.js +++ b/index.android.js @@ -15,4 +15,4 @@ const TekkenChicken = () => ( ); -AppRegistry.registerComponent('t7ChickenNative', () => (TekkenChicken)); +AppRegistry.registerComponent('T7Chicken', () => (TekkenChicken)); diff --git a/index.ios.js b/index.ios.js index c46fd1b..c1721b6 100644 --- a/index.ios.js +++ b/index.ios.js @@ -15,4 +15,4 @@ const TekkenChicken = () => ( ); -AppRegistry.registerComponent('t7ChickenNative', () => (TekkenChicken)); +AppRegistry.registerComponent('T7Chicken', () => (TekkenChicken)); diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..7411573 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,16 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'T7Chicken' do + # Uncomment the next line if you're using Swift or would like to use dynamic frameworks + # use_frameworks! + + # Pods for T7Chicken + pod 'Mixpanel' + + target 'T7ChickenTests' do + inherit! :search_paths + # Pods for testing + end + +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..7b38196 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,12 @@ +PODS: + - Mixpanel (3.1.5) + +DEPENDENCIES: + - Mixpanel + +SPEC CHECKSUMS: + Mixpanel: 9b33e5de998cd42597767f67e2b8fcd1d158c71d + +PODFILE CHECKSUM: 245390a6dda33e26e58ba339dc00513c0b1c7e7d + +COCOAPODS: 1.2.1 diff --git a/ios/Pods/Headers/Private/Mixpanel/AutomaticEvents.h b/ios/Pods/Headers/Private/Mixpanel/AutomaticEvents.h new file mode 120000 index 0000000..503a523 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/AutomaticEvents.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/AutomaticEvents.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/AutomaticTracksConstants.h b/ios/Pods/Headers/Private/Mixpanel/AutomaticTracksConstants.h new file mode 120000 index 0000000..62f6709 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/AutomaticTracksConstants.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/AutomaticTracksConstants.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerChangeRequestMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerChangeRequestMessage.h new file mode 120000 index 0000000..742d7ab --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerChangeRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerChangeResponseMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerChangeResponseMessage.h new file mode 120000 index 0000000..ad4107e --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerChangeResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerClearRequestMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerClearRequestMessage.h new file mode 120000 index 0000000..82be650 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerClearRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerClearResponseMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerClearResponseMessage.h new file mode 120000 index 0000000..e1dbeba --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerClearResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerConnection.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerConnection.h new file mode 120000 index 0000000..24265d9 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerConnection.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h new file mode 120000 index 0000000..f8f487d --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h new file mode 120000 index 0000000..ec1c62a --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDisconnectMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDisconnectMessage.h new file mode 120000 index 0000000..7c20a85 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerDisconnectMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerMessage.h new file mode 120000 index 0000000..0f62cb1 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h new file mode 120000 index 0000000..a0c663d --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h new file mode 120000 index 0000000..0a007da --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerTweakRequestMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerTweakRequestMessage.h new file mode 120000 index 0000000..b3d47a8 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerTweakRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerTweakResponseMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerTweakResponseMessage.h new file mode 120000 index 0000000..f6b028b --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPABTestDesignerTweakResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPAbstractABTestDesignerMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPAbstractABTestDesignerMessage.h new file mode 120000 index 0000000..b04e699 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPAbstractABTestDesignerMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPApplicationStateSerializer.h b/ios/Pods/Headers/Private/Mixpanel/MPApplicationStateSerializer.h new file mode 120000 index 0000000..74780b9 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPApplicationStateSerializer.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPApplicationStateSerializer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPClassDescription.h b/ios/Pods/Headers/Private/Mixpanel/MPClassDescription.h new file mode 120000 index 0000000..0f4ff03 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPClassDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPClassDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPDesignerEventBindingMessage.h b/ios/Pods/Headers/Private/Mixpanel/MPDesignerEventBindingMessage.h new file mode 120000 index 0000000..cdd6c18 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPDesignerEventBindingMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPDesignerEventBindingMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPDesignerSessionCollection.h b/ios/Pods/Headers/Private/Mixpanel/MPDesignerSessionCollection.h new file mode 120000 index 0000000..bad4949 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPDesignerSessionCollection.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPDesignerSessionCollection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPEnumDescription.h b/ios/Pods/Headers/Private/Mixpanel/MPEnumDescription.h new file mode 120000 index 0000000..2e78ea8 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPEnumDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPEnumDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPEventBinding.h b/ios/Pods/Headers/Private/Mixpanel/MPEventBinding.h new file mode 120000 index 0000000..f092b4a --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPEventBinding.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPEventBinding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPFoundation.h b/ios/Pods/Headers/Private/Mixpanel/MPFoundation.h new file mode 120000 index 0000000..0dd12d1 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPFoundation.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPFoundation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPLogger.h b/ios/Pods/Headers/Private/Mixpanel/MPLogger.h new file mode 120000 index 0000000..6f738dd --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPLogger.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPLogger.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPMiniNotification.h b/ios/Pods/Headers/Private/Mixpanel/MPMiniNotification.h new file mode 120000 index 0000000..410290a --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPMiniNotification.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPMiniNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPNetwork.h b/ios/Pods/Headers/Private/Mixpanel/MPNetwork.h new file mode 120000 index 0000000..2b1b223 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPNetwork.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNetwork.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPNetworkPrivate.h b/ios/Pods/Headers/Private/Mixpanel/MPNetworkPrivate.h new file mode 120000 index 0000000..5febc1d --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPNetworkPrivate.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNetworkPrivate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPNotification.h b/ios/Pods/Headers/Private/Mixpanel/MPNotification.h new file mode 120000 index 0000000..e4beab8 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPNotification.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPNotificationButton.h b/ios/Pods/Headers/Private/Mixpanel/MPNotificationButton.h new file mode 120000 index 0000000..30cd811 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPNotificationButton.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNotificationButton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPNotificationViewController.h b/ios/Pods/Headers/Private/Mixpanel/MPNotificationViewController.h new file mode 120000 index 0000000..0207aff --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPNotificationViewController.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNotificationViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPObjectIdentifierProvider.h b/ios/Pods/Headers/Private/Mixpanel/MPObjectIdentifierProvider.h new file mode 120000 index 0000000..2c9adc4 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPObjectIdentifierProvider.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectIdentifierProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPObjectIdentityProvider.h b/ios/Pods/Headers/Private/Mixpanel/MPObjectIdentityProvider.h new file mode 120000 index 0000000..112f2e8 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPObjectIdentityProvider.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectIdentityProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPObjectSelector.h b/ios/Pods/Headers/Private/Mixpanel/MPObjectSelector.h new file mode 120000 index 0000000..34d9f66 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPObjectSelector.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSelector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializer.h b/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializer.h new file mode 120000 index 0000000..6122f97 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializer.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSerializer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializerConfig.h b/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializerConfig.h new file mode 120000 index 0000000..40fa3dd --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializerConfig.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSerializerConfig.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializerContext.h b/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializerContext.h new file mode 120000 index 0000000..8306145 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPObjectSerializerContext.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSerializerContext.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPPropertyDescription.h b/ios/Pods/Headers/Private/Mixpanel/MPPropertyDescription.h new file mode 120000 index 0000000..09a69e9 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPPropertyDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPPropertyDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPResources.h b/ios/Pods/Headers/Private/Mixpanel/MPResources.h new file mode 120000 index 0000000..a13456d --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPResources.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPResources.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPSequenceGenerator.h b/ios/Pods/Headers/Private/Mixpanel/MPSequenceGenerator.h new file mode 120000 index 0000000..1302843 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPSequenceGenerator.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPSequenceGenerator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPSwizzle.h b/ios/Pods/Headers/Private/Mixpanel/MPSwizzle.h new file mode 120000 index 0000000..9c84381 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPSwizzle.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPSwizzle.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPSwizzler.h b/ios/Pods/Headers/Private/Mixpanel/MPSwizzler.h new file mode 120000 index 0000000..abed467 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPSwizzler.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPSwizzler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPTakeoverNotification.h b/ios/Pods/Headers/Private/Mixpanel/MPTakeoverNotification.h new file mode 120000 index 0000000..e71cd18 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPTakeoverNotification.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTakeoverNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPTweak.h b/ios/Pods/Headers/Private/Mixpanel/MPTweak.h new file mode 120000 index 0000000..a8101c0 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPTweak.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweak.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPTweakInline.h b/ios/Pods/Headers/Private/Mixpanel/MPTweakInline.h new file mode 120000 index 0000000..2d8b4bb --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPTweakInline.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweakInline.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPTweakInlineInternal.h b/ios/Pods/Headers/Private/Mixpanel/MPTweakInlineInternal.h new file mode 120000 index 0000000..1d13b79 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPTweakInlineInternal.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweakInlineInternal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPTweakStore.h b/ios/Pods/Headers/Private/Mixpanel/MPTweakStore.h new file mode 120000 index 0000000..1ab0ec5 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPTweakStore.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweakStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPTypeDescription.h b/ios/Pods/Headers/Private/Mixpanel/MPTypeDescription.h new file mode 120000 index 0000000..ac5c3bd --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPTypeDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTypeDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPUIControlBinding.h b/ios/Pods/Headers/Private/Mixpanel/MPUIControlBinding.h new file mode 120000 index 0000000..e30360d --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPUIControlBinding.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPUIControlBinding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPUITableViewBinding.h b/ios/Pods/Headers/Private/Mixpanel/MPUITableViewBinding.h new file mode 120000 index 0000000..8c9dbc9 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPUITableViewBinding.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPUITableViewBinding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPValueTransformers.h b/ios/Pods/Headers/Private/Mixpanel/MPValueTransformers.h new file mode 120000 index 0000000..ead98e3 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPValueTransformers.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPValueTransformers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPVariant.h b/ios/Pods/Headers/Private/Mixpanel/MPVariant.h new file mode 120000 index 0000000..76f6ffd --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPVariant.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPVariant.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MPWebSocket.h b/ios/Pods/Headers/Private/Mixpanel/MPWebSocket.h new file mode 120000 index 0000000..54b318a --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MPWebSocket.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPWebSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/Mixpanel+AutomaticTracks.h b/ios/Pods/Headers/Private/Mixpanel/Mixpanel+AutomaticTracks.h new file mode 120000 index 0000000..56a2fff --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/Mixpanel+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/Mixpanel.h b/ios/Pods/Headers/Private/Mixpanel/Mixpanel.h new file mode 120000 index 0000000..1f79002 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/Mixpanel.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/Mixpanel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MixpanelExceptionHandler.h b/ios/Pods/Headers/Private/Mixpanel/MixpanelExceptionHandler.h new file mode 120000 index 0000000..67a694f --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MixpanelExceptionHandler.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MixpanelExceptionHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MixpanelPeople.h b/ios/Pods/Headers/Private/Mixpanel/MixpanelPeople.h new file mode 120000 index 0000000..665f9c0 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MixpanelPeople.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MixpanelPeople.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MixpanelPeoplePrivate.h b/ios/Pods/Headers/Private/Mixpanel/MixpanelPeoplePrivate.h new file mode 120000 index 0000000..51823fc --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MixpanelPeoplePrivate.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MixpanelPeoplePrivate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/MixpanelPrivate.h b/ios/Pods/Headers/Private/Mixpanel/MixpanelPrivate.h new file mode 120000 index 0000000..a61c340 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/MixpanelPrivate.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MixpanelPrivate.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/NSInvocation+MPHelpers.h b/ios/Pods/Headers/Private/Mixpanel/NSInvocation+MPHelpers.h new file mode 120000 index 0000000..0f46ae5 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/NSInvocation+MPHelpers.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/NSInvocation+MPHelpers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/NSNotificationCenter+AutomaticTracks.h b/ios/Pods/Headers/Private/Mixpanel/NSNotificationCenter+AutomaticTracks.h new file mode 120000 index 0000000..5d139ed --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/NSNotificationCenter+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/UIApplication+AutomaticTracks.h b/ios/Pods/Headers/Private/Mixpanel/UIApplication+AutomaticTracks.h new file mode 120000 index 0000000..1baf935 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/UIApplication+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIApplication+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/UIColor+MPColor.h b/ios/Pods/Headers/Private/Mixpanel/UIColor+MPColor.h new file mode 120000 index 0000000..a5d755c --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/UIColor+MPColor.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIColor+MPColor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/UIImage+MPAverageColor.h b/ios/Pods/Headers/Private/Mixpanel/UIImage+MPAverageColor.h new file mode 120000 index 0000000..b5badcd --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/UIImage+MPAverageColor.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIImage+MPAverageColor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/UIImage+MPImageEffects.h b/ios/Pods/Headers/Private/Mixpanel/UIImage+MPImageEffects.h new file mode 120000 index 0000000..e5b7950 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/UIImage+MPImageEffects.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIImage+MPImageEffects.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/UIView+MPHelpers.h b/ios/Pods/Headers/Private/Mixpanel/UIView+MPHelpers.h new file mode 120000 index 0000000..18ac380 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/UIView+MPHelpers.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIView+MPHelpers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/UIViewController+AutomaticTracks.h b/ios/Pods/Headers/Private/Mixpanel/UIViewController+AutomaticTracks.h new file mode 120000 index 0000000..95e4f33 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/UIViewController+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIViewController+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Private/Mixpanel/_MPTweakBindObserver.h b/ios/Pods/Headers/Private/Mixpanel/_MPTweakBindObserver.h new file mode 120000 index 0000000..90812e6 --- /dev/null +++ b/ios/Pods/Headers/Private/Mixpanel/_MPTweakBindObserver.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/_MPTweakBindObserver.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/AutomaticEvents.h b/ios/Pods/Headers/Public/Mixpanel/AutomaticEvents.h new file mode 120000 index 0000000..503a523 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/AutomaticEvents.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/AutomaticEvents.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/AutomaticTracksConstants.h b/ios/Pods/Headers/Public/Mixpanel/AutomaticTracksConstants.h new file mode 120000 index 0000000..62f6709 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/AutomaticTracksConstants.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/AutomaticTracksConstants.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerChangeRequestMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerChangeRequestMessage.h new file mode 120000 index 0000000..742d7ab --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerChangeRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerChangeResponseMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerChangeResponseMessage.h new file mode 120000 index 0000000..ad4107e --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerChangeResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerClearRequestMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerClearRequestMessage.h new file mode 120000 index 0000000..82be650 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerClearRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerClearResponseMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerClearResponseMessage.h new file mode 120000 index 0000000..e1dbeba --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerClearResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerConnection.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerConnection.h new file mode 120000 index 0000000..24265d9 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerConnection.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerConnection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h new file mode 120000 index 0000000..f8f487d --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h new file mode 120000 index 0000000..ec1c62a --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDisconnectMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDisconnectMessage.h new file mode 120000 index 0000000..7c20a85 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerDisconnectMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerMessage.h new file mode 120000 index 0000000..0f62cb1 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h new file mode 120000 index 0000000..a0c663d --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h new file mode 120000 index 0000000..0a007da --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerTweakRequestMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerTweakRequestMessage.h new file mode 120000 index 0000000..b3d47a8 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerTweakRequestMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerTweakResponseMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerTweakResponseMessage.h new file mode 120000 index 0000000..f6b028b --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPABTestDesignerTweakResponseMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPAbstractABTestDesignerMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPAbstractABTestDesignerMessage.h new file mode 120000 index 0000000..b04e699 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPAbstractABTestDesignerMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPApplicationStateSerializer.h b/ios/Pods/Headers/Public/Mixpanel/MPApplicationStateSerializer.h new file mode 120000 index 0000000..74780b9 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPApplicationStateSerializer.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPApplicationStateSerializer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPClassDescription.h b/ios/Pods/Headers/Public/Mixpanel/MPClassDescription.h new file mode 120000 index 0000000..0f4ff03 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPClassDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPClassDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPDesignerEventBindingMessage.h b/ios/Pods/Headers/Public/Mixpanel/MPDesignerEventBindingMessage.h new file mode 120000 index 0000000..cdd6c18 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPDesignerEventBindingMessage.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPDesignerEventBindingMessage.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPDesignerSessionCollection.h b/ios/Pods/Headers/Public/Mixpanel/MPDesignerSessionCollection.h new file mode 120000 index 0000000..bad4949 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPDesignerSessionCollection.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPDesignerSessionCollection.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPEnumDescription.h b/ios/Pods/Headers/Public/Mixpanel/MPEnumDescription.h new file mode 120000 index 0000000..2e78ea8 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPEnumDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPEnumDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPEventBinding.h b/ios/Pods/Headers/Public/Mixpanel/MPEventBinding.h new file mode 120000 index 0000000..f092b4a --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPEventBinding.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPEventBinding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPFoundation.h b/ios/Pods/Headers/Public/Mixpanel/MPFoundation.h new file mode 120000 index 0000000..0dd12d1 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPFoundation.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPFoundation.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPMiniNotification.h b/ios/Pods/Headers/Public/Mixpanel/MPMiniNotification.h new file mode 120000 index 0000000..410290a --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPMiniNotification.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPMiniNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPNetwork.h b/ios/Pods/Headers/Public/Mixpanel/MPNetwork.h new file mode 120000 index 0000000..2b1b223 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPNetwork.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNetwork.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPNotification.h b/ios/Pods/Headers/Public/Mixpanel/MPNotification.h new file mode 120000 index 0000000..e4beab8 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPNotification.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPNotificationButton.h b/ios/Pods/Headers/Public/Mixpanel/MPNotificationButton.h new file mode 120000 index 0000000..30cd811 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPNotificationButton.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNotificationButton.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPNotificationViewController.h b/ios/Pods/Headers/Public/Mixpanel/MPNotificationViewController.h new file mode 120000 index 0000000..0207aff --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPNotificationViewController.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPNotificationViewController.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPObjectIdentifierProvider.h b/ios/Pods/Headers/Public/Mixpanel/MPObjectIdentifierProvider.h new file mode 120000 index 0000000..2c9adc4 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPObjectIdentifierProvider.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectIdentifierProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPObjectIdentityProvider.h b/ios/Pods/Headers/Public/Mixpanel/MPObjectIdentityProvider.h new file mode 120000 index 0000000..112f2e8 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPObjectIdentityProvider.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectIdentityProvider.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPObjectSelector.h b/ios/Pods/Headers/Public/Mixpanel/MPObjectSelector.h new file mode 120000 index 0000000..34d9f66 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPObjectSelector.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSelector.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializer.h b/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializer.h new file mode 120000 index 0000000..6122f97 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializer.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSerializer.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializerConfig.h b/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializerConfig.h new file mode 120000 index 0000000..40fa3dd --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializerConfig.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSerializerConfig.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializerContext.h b/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializerContext.h new file mode 120000 index 0000000..8306145 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPObjectSerializerContext.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPObjectSerializerContext.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPPropertyDescription.h b/ios/Pods/Headers/Public/Mixpanel/MPPropertyDescription.h new file mode 120000 index 0000000..09a69e9 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPPropertyDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPPropertyDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPResources.h b/ios/Pods/Headers/Public/Mixpanel/MPResources.h new file mode 120000 index 0000000..a13456d --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPResources.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPResources.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPSequenceGenerator.h b/ios/Pods/Headers/Public/Mixpanel/MPSequenceGenerator.h new file mode 120000 index 0000000..1302843 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPSequenceGenerator.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPSequenceGenerator.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPSwizzle.h b/ios/Pods/Headers/Public/Mixpanel/MPSwizzle.h new file mode 120000 index 0000000..9c84381 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPSwizzle.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPSwizzle.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPSwizzler.h b/ios/Pods/Headers/Public/Mixpanel/MPSwizzler.h new file mode 120000 index 0000000..abed467 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPSwizzler.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPSwizzler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPTakeoverNotification.h b/ios/Pods/Headers/Public/Mixpanel/MPTakeoverNotification.h new file mode 120000 index 0000000..e71cd18 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPTakeoverNotification.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTakeoverNotification.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPTweak.h b/ios/Pods/Headers/Public/Mixpanel/MPTweak.h new file mode 120000 index 0000000..a8101c0 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPTweak.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweak.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPTweakInline.h b/ios/Pods/Headers/Public/Mixpanel/MPTweakInline.h new file mode 120000 index 0000000..2d8b4bb --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPTweakInline.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweakInline.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPTweakInlineInternal.h b/ios/Pods/Headers/Public/Mixpanel/MPTweakInlineInternal.h new file mode 120000 index 0000000..1d13b79 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPTweakInlineInternal.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweakInlineInternal.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPTweakStore.h b/ios/Pods/Headers/Public/Mixpanel/MPTweakStore.h new file mode 120000 index 0000000..1ab0ec5 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPTweakStore.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTweakStore.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPTypeDescription.h b/ios/Pods/Headers/Public/Mixpanel/MPTypeDescription.h new file mode 120000 index 0000000..ac5c3bd --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPTypeDescription.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPTypeDescription.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPUIControlBinding.h b/ios/Pods/Headers/Public/Mixpanel/MPUIControlBinding.h new file mode 120000 index 0000000..e30360d --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPUIControlBinding.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPUIControlBinding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPUITableViewBinding.h b/ios/Pods/Headers/Public/Mixpanel/MPUITableViewBinding.h new file mode 120000 index 0000000..8c9dbc9 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPUITableViewBinding.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPUITableViewBinding.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPValueTransformers.h b/ios/Pods/Headers/Public/Mixpanel/MPValueTransformers.h new file mode 120000 index 0000000..ead98e3 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPValueTransformers.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPValueTransformers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPVariant.h b/ios/Pods/Headers/Public/Mixpanel/MPVariant.h new file mode 120000 index 0000000..76f6ffd --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPVariant.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPVariant.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MPWebSocket.h b/ios/Pods/Headers/Public/Mixpanel/MPWebSocket.h new file mode 120000 index 0000000..54b318a --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MPWebSocket.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MPWebSocket.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/Mixpanel+AutomaticTracks.h b/ios/Pods/Headers/Public/Mixpanel/Mixpanel+AutomaticTracks.h new file mode 120000 index 0000000..56a2fff --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/Mixpanel+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/Mixpanel.h b/ios/Pods/Headers/Public/Mixpanel/Mixpanel.h new file mode 120000 index 0000000..1f79002 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/Mixpanel.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/Mixpanel.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MixpanelExceptionHandler.h b/ios/Pods/Headers/Public/Mixpanel/MixpanelExceptionHandler.h new file mode 120000 index 0000000..67a694f --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MixpanelExceptionHandler.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MixpanelExceptionHandler.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/MixpanelPeople.h b/ios/Pods/Headers/Public/Mixpanel/MixpanelPeople.h new file mode 120000 index 0000000..665f9c0 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/MixpanelPeople.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/MixpanelPeople.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/NSInvocation+MPHelpers.h b/ios/Pods/Headers/Public/Mixpanel/NSInvocation+MPHelpers.h new file mode 120000 index 0000000..0f46ae5 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/NSInvocation+MPHelpers.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/NSInvocation+MPHelpers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/NSNotificationCenter+AutomaticTracks.h b/ios/Pods/Headers/Public/Mixpanel/NSNotificationCenter+AutomaticTracks.h new file mode 120000 index 0000000..5d139ed --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/NSNotificationCenter+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/UIApplication+AutomaticTracks.h b/ios/Pods/Headers/Public/Mixpanel/UIApplication+AutomaticTracks.h new file mode 120000 index 0000000..1baf935 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/UIApplication+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIApplication+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/UIColor+MPColor.h b/ios/Pods/Headers/Public/Mixpanel/UIColor+MPColor.h new file mode 120000 index 0000000..a5d755c --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/UIColor+MPColor.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIColor+MPColor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/UIImage+MPAverageColor.h b/ios/Pods/Headers/Public/Mixpanel/UIImage+MPAverageColor.h new file mode 120000 index 0000000..b5badcd --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/UIImage+MPAverageColor.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIImage+MPAverageColor.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/UIImage+MPImageEffects.h b/ios/Pods/Headers/Public/Mixpanel/UIImage+MPImageEffects.h new file mode 120000 index 0000000..e5b7950 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/UIImage+MPImageEffects.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIImage+MPImageEffects.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/UIView+MPHelpers.h b/ios/Pods/Headers/Public/Mixpanel/UIView+MPHelpers.h new file mode 120000 index 0000000..18ac380 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/UIView+MPHelpers.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIView+MPHelpers.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/UIViewController+AutomaticTracks.h b/ios/Pods/Headers/Public/Mixpanel/UIViewController+AutomaticTracks.h new file mode 120000 index 0000000..95e4f33 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/UIViewController+AutomaticTracks.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/UIViewController+AutomaticTracks.h \ No newline at end of file diff --git a/ios/Pods/Headers/Public/Mixpanel/_MPTweakBindObserver.h b/ios/Pods/Headers/Public/Mixpanel/_MPTweakBindObserver.h new file mode 120000 index 0000000..90812e6 --- /dev/null +++ b/ios/Pods/Headers/Public/Mixpanel/_MPTweakBindObserver.h @@ -0,0 +1 @@ +../../../Mixpanel/Mixpanel/_MPTweakBindObserver.h \ No newline at end of file diff --git a/ios/Pods/Manifest.lock b/ios/Pods/Manifest.lock new file mode 100644 index 0000000..7b38196 --- /dev/null +++ b/ios/Pods/Manifest.lock @@ -0,0 +1,12 @@ +PODS: + - Mixpanel (3.1.5) + +DEPENDENCIES: + - Mixpanel + +SPEC CHECKSUMS: + Mixpanel: 9b33e5de998cd42597767f67e2b8fcd1d158c71d + +PODFILE CHECKSUM: 245390a6dda33e26e58ba339dc00513c0b1c7e7d + +COCOAPODS: 1.2.1 diff --git a/ios/Pods/Mixpanel/LICENSE b/ios/Pods/Mixpanel/LICENSE new file mode 100644 index 0000000..b2aa8c2 --- /dev/null +++ b/ios/Pods/Mixpanel/LICENSE @@ -0,0 +1,259 @@ +Copyright 2013 Mixpanel, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this work except in compliance with the License. +You may obtain a copy of the License below, or 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. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +================================================================================ + +_MPTweakBindObserver.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +_MPTweakBindObserver.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweak.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweak.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInline.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInline.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInlineInternal.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakStore.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakStore.m Copyright (c) 2014, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation + +and/or other materials provided with the distribution. + +* Neither the name Facebook nor the names of its contributors may be used to + +endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ios/Pods/Mixpanel/Mixpanel/AutomaticEvents.h b/ios/Pods/Mixpanel/Mixpanel/AutomaticEvents.h new file mode 100644 index 0000000..1c83b8b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/AutomaticEvents.h @@ -0,0 +1,24 @@ +// +// AutomaticEvents.h +// Mixpanel +// +// Created by Yarden Eitan on 4/18/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import +#import +#import "MixpanelPeople.h" + +@protocol TrackDelegate +- (void)track:(NSString *)event properties:(NSDictionary *)properties; +@end + +@interface AutomaticEvents: NSObject +@property (atomic, weak) id delegate; +@property (atomic, assign) UInt64 minimumSessionDuration; +@property (atomic, assign) UInt64 maximumSessionDuration; +- (void)initializeEvents:(MixpanelPeople *)peopleInstance; + +@end + diff --git a/ios/Pods/Mixpanel/Mixpanel/AutomaticEvents.m b/ios/Pods/Mixpanel/Mixpanel/AutomaticEvents.m new file mode 100644 index 0000000..f331d0c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/AutomaticEvents.m @@ -0,0 +1,147 @@ +// +// AutomaticEvents.m +// Mixpanel +// +// Created by Yarden Eitan on 4/18/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import "AutomaticEvents.h" +#import "MPSwizzler.h" +#import + +@implementation AutomaticEvents { + NSMutableDictionary *awaitingTransactions; + NSUserDefaults *defaults; + NSTimeInterval sessionLength; + NSTimeInterval sessionStartTime; + MixpanelPeople *people; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + awaitingTransactions = [[NSMutableDictionary alloc] init]; + defaults = [[NSUserDefaults alloc] initWithSuiteName:@"Mixpanel"]; + sessionLength = 0; + sessionStartTime = [[NSDate date] timeIntervalSince1970]; + self.minimumSessionDuration = 10000; + self.maximumSessionDuration = UINT64_MAX; + } + return self; +} + +- (void)initializeEvents:(MixpanelPeople *)peopleInstance { + people = peopleInstance; + NSString *firstOpenKey = @"MPFirstOpen"; + if (defaults != nil && ![defaults boolForKey:firstOpenKey]) { + if (![self isExistingUser]) { + [self.delegate track:@"$ae_first_open" properties:nil]; + [people setOnce:@{@"$ae_first_app_open_date": [NSDate date]}]; + } + [defaults setBool:TRUE forKey:firstOpenKey]; + [defaults synchronize]; + } + + NSDictionary* infoDict = [NSBundle mainBundle].infoDictionary; + if (defaults != nil && infoDict != nil) { + NSString* appVersionKey = @"MPAppVersion"; + NSString* appVersionValue = infoDict[@"CFBundleShortVersionString"]; + NSString* savedVersionValue = [defaults stringForKey:appVersionKey]; + if (appVersionValue != nil && savedVersionValue != nil && + [appVersionValue compare:savedVersionValue options:NSNumericSearch] == NSOrderedDescending) { + [self.delegate track:@"$ae_updated" properties:@{@"$ae_updated_version": appVersionValue}]; + [defaults setObject:appVersionValue forKey:appVersionKey]; + [defaults synchronize]; + } else if (savedVersionValue == nil) { + [defaults setObject:appVersionValue forKey:appVersionKey]; + [defaults synchronize]; + } + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(appWillResignActive:) + name:UIApplicationWillResignActiveNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(appDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + + [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; + +} + +- (void)appWillResignActive:(NSNotification *)notification { + sessionLength = [self roundOneDigit:[[NSDate date] timeIntervalSince1970] - sessionStartTime]; + if (sessionLength >= (double)(self.minimumSessionDuration / 1000) && + sessionLength <= (double)(self.maximumSessionDuration / 1000)) { + NSMutableDictionary *properties = [[NSMutableDictionary alloc] + initWithObjectsAndKeys:[NSNumber numberWithDouble:sessionLength], @"$ae_session_length", nil]; + [self.delegate track:@"$ae_session" properties:properties]; + [people increment:@"$ae_total_app_sessions" by:[NSNumber numberWithInt:1]]; + [people increment:@"$ae_total_app_session_length" by:[NSNumber numberWithInt:(int)sessionLength]]; + } +} + +- (void)appDidBecomeActive:(NSNotification *)notification { + NSTimeInterval nowTime = [[NSDate date] timeIntervalSince1970]; + sessionStartTime = nowTime; +} + +- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { + NSMutableSet *productIdentifiers = [[NSMutableSet alloc] init]; + @synchronized (awaitingTransactions) { + for (SKPaymentTransaction *transaction in transactions) { + if (transaction != nil) { + switch (transaction.transactionState) { + case SKPaymentTransactionStatePurchased: + [productIdentifiers addObject:transaction.payment.productIdentifier]; + [awaitingTransactions setObject:transaction forKey:transaction.payment.productIdentifier]; + break; + case SKPaymentTransactionStateFailed: + case SKPaymentTransactionStateRestored: + default: + break; + } + } + } + } + if ([productIdentifiers count] > 0) { + SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers]; + productsRequest.delegate = self; + [productsRequest start]; + } +} + +- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { + @synchronized (awaitingTransactions) { + for (SKProduct *product in response.products) { + SKPaymentTransaction *transaction = [awaitingTransactions objectForKey:product.productIdentifier]; + if (transaction != nil) { + [self.delegate track:@"$ae_iap" properties:@{@"$ae_iap_price": product.price, + @"$ae_iap_quantity": [NSNumber numberWithInteger:transaction.payment.quantity], + @"$ae_iap_name": product.productIdentifier}]; + } + } + } +} + +- (NSTimeInterval)roundOneDigit:(NSTimeInterval) num { + return round(num * 10.0) / 10.0; +} + +- (BOOL)isExistingUser { + NSString *searchPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject]; + NSArray *pathContents = [NSFileManager.defaultManager contentsOfDirectoryAtPath:searchPath error:nil]; + for (NSString *path in pathContents) { + if ([path hasPrefix:@"mixpanel-"]) { + return true; + } + } + return false; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/AutomaticTracksConstants.h b/ios/Pods/Mixpanel/Mixpanel/AutomaticTracksConstants.h new file mode 100644 index 0000000..9e7907c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/AutomaticTracksConstants.h @@ -0,0 +1,17 @@ +// +// AutomaticTracksConstants.h +// Mixpanel +// +// Created by Sam Green on 3/22/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, AutomaticTrackMode) { + AutomaticTrackModeNone, + AutomaticTrackModeCount, +}; + +#pragma mark - Strings +static NSString *const kAutomaticTrackName = @"$ios_event"; diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowLeft.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowLeft.png new file mode 100644 index 0000000..caee8af Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowLeft.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowLeft@2x.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowLeft@2x.png new file mode 100644 index 0000000..88a7857 Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowLeft@2x.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowRight.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowRight.png new file mode 100644 index 0000000..1fc5c8b Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowRight.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowRight@2x.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowRight@2x.png new file mode 100644 index 0000000..baf9171 Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPArrowRight@2x.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPCheckmark.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPCheckmark.png new file mode 100644 index 0000000..c8dc06f Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPCheckmark.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPCheckmark@2x.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPCheckmark@2x.png new file mode 100644 index 0000000..cc6ec9f Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPCheckmark@2x.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton.png new file mode 100644 index 0000000..e4ab761 Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton@2x.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton@2x.png new file mode 100644 index 0000000..e710710 Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton@2x.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton@3x.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton@3x.png new file mode 100644 index 0000000..6945afd Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPCloseButton@3x.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPDismissKeyboard.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPDismissKeyboard.png new file mode 100644 index 0000000..eb30cf4 Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPDismissKeyboard.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPDismissKeyboard@2x.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPDismissKeyboard@2x.png new file mode 100644 index 0000000..bc9568a Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPDismissKeyboard@2x.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPLogo.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPLogo.png new file mode 100644 index 0000000..e07640b Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPLogo.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/MPLogo@2x.png b/ios/Pods/Mixpanel/Mixpanel/Images/MPLogo@2x.png new file mode 100644 index 0000000..14e619b Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/MPLogo@2x.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/Images/placeholder-image.png b/ios/Pods/Mixpanel/Mixpanel/Images/placeholder-image.png new file mode 100644 index 0000000..b8c902e Binary files /dev/null and b/ios/Pods/Mixpanel/Mixpanel/Images/placeholder-image.png differ diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.h new file mode 100644 index 0000000..485cb6c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.h @@ -0,0 +1,11 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPAbstractABTestDesignerMessage.h" + +extern NSString *const MPABTestDesignerChangeRequestMessageType; + +@interface MPABTestDesignerChangeRequestMessage : MPAbstractABTestDesignerMessage + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.m new file mode 100644 index 0000000..70310f5 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeRequestMessage.m @@ -0,0 +1,46 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPABTestDesignerChangeRequestMessage.h" +#import "MPABTestDesignerChangeResponseMessage.h" +#import "MPABTestDesignerConnection.h" +#import "MPABTestDesignerSnapshotResponseMessage.h" +#import "MPVariant.h" + +NSString *const MPABTestDesignerChangeRequestMessageType = @"change_request"; + +@implementation MPABTestDesignerChangeRequestMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerChangeRequestMessage *)[self alloc] initWithType:MPABTestDesignerChangeRequestMessageType]; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + __weak MPABTestDesignerConnection *weak_connection = connection; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + MPABTestDesignerConnection *conn = weak_connection; + + MPVariant *variant = [connection sessionObjectForKey:kSessionVariantKey]; + if (!variant) { + variant = [[MPVariant alloc] init]; + [connection setSessionObject:variant forKey:kSessionVariantKey]; + } + + id actions = [self payload][@"actions"]; + if ([actions isKindOfClass:[NSArray class]]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + [variant addActionsFromJSONObject:actions andExecute:YES]; + }); + } + + MPABTestDesignerChangeResponseMessage *changeResponseMessage = [MPABTestDesignerChangeResponseMessage message]; + changeResponseMessage.status = @"OK"; + [conn sendMessage:changeResponseMessage]; + }]; + + return operation; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.h new file mode 100644 index 0000000..7ce93d9 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.h @@ -0,0 +1,13 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPAbstractABTestDesignerMessage.h" + +@interface MPABTestDesignerChangeResponseMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; + +@property (nonatomic, copy) NSString *status; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.m new file mode 100644 index 0000000..78410da --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerChangeResponseMessage.m @@ -0,0 +1,23 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPABTestDesignerChangeResponseMessage.h" + +@implementation MPABTestDesignerChangeResponseMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerChangeResponseMessage *)[self alloc] initWithType:@"change_response"]; +} + +- (void)setStatus:(NSString *)status +{ + [self setPayloadObject:status forKey:@"status"]; +} + +- (NSString *)status +{ + return [self payloadObjectForKey:@"status"]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.h new file mode 100644 index 0000000..5767018 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.h @@ -0,0 +1,10 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPAbstractABTestDesignerMessage.h" + +extern NSString *const MPABTestDesignerClearRequestMessageType; + +@interface MPABTestDesignerClearRequestMessage : MPAbstractABTestDesignerMessage + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.m new file mode 100644 index 0000000..07bdd5f --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearRequestMessage.m @@ -0,0 +1,46 @@ +// +// MPABTestDesignerClearRequestMessage.m +// HelloMixpanel +// +// Created by Alex Hofsteede on 3/7/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPABTestDesignerClearRequestMessage.h" +#import "MPABTestDesignerClearResponseMessage.h" +#import "MPABTestDesignerConnection.h" +#import "MPVariant.h" + +NSString *const MPABTestDesignerClearRequestMessageType = @"clear_request"; + +@implementation MPABTestDesignerClearRequestMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerClearRequestMessage *)[self alloc] initWithType:MPABTestDesignerClearRequestMessageType]; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + __weak MPABTestDesignerConnection *weak_connection = connection; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + MPABTestDesignerConnection *conn = weak_connection; + + MPVariant *variant = [conn sessionObjectForKey:kSessionVariantKey]; + if (variant) { + NSArray *actions = [self payload][@"actions"]; + dispatch_sync(dispatch_get_main_queue(), ^{ + for (NSString *name in actions) { + [variant removeActionWithName:name]; + } + }); + } + + MPABTestDesignerClearResponseMessage *clearResponseMessage = [MPABTestDesignerClearResponseMessage message]; + clearResponseMessage.status = @"OK"; + [conn sendMessage:clearResponseMessage]; + }]; + return operation; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.h new file mode 100644 index 0000000..78d9a79 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.h @@ -0,0 +1,17 @@ +// +// MPABTestDesignerClearResponseMessage.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 3/7/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPAbstractABTestDesignerMessage.h" + +@interface MPABTestDesignerClearResponseMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; + +@property (nonatomic, copy) NSString *status; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.m new file mode 100644 index 0000000..31feba6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerClearResponseMessage.m @@ -0,0 +1,28 @@ +// +// MPABTestDesignerClearResponseMessage.m +// HelloMixpanel +// +// Created by Alex Hofsteede on 3/7/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPABTestDesignerClearResponseMessage.h" + +@implementation MPABTestDesignerClearResponseMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerClearResponseMessage *)[self alloc] initWithType:@"clear_response"]; +} + +- (void)setStatus:(NSString *)status +{ + [self setPayloadObject:status forKey:@"status"]; +} + +- (NSString *)status +{ + return [self payloadObjectForKey:@"status"]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerConnection.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerConnection.h new file mode 100644 index 0000000..581fcf9 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerConnection.h @@ -0,0 +1,24 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPWebSocket.h" + +@protocol MPABTestDesignerMessage; + +extern NSString *const kSessionVariantKey; + +@interface MPABTestDesignerConnection : NSObject + +@property (nonatomic, readonly) BOOL connected; +@property (nonatomic, assign) BOOL sessionEnded; + +- (instancetype)initWithURL:(NSURL *)url; +- (instancetype)initWithURL:(NSURL *)url keepTrying:(BOOL)keepTrying connectCallback:(void (^)())connectCallback disconnectCallback:(void (^)())disconnectCallback; + +- (void)setSessionObject:(id)object forKey:(NSString *)key; +- (id)sessionObjectForKey:(NSString *)key; +- (void)sendMessage:(id)message; +- (void)close; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerConnection.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerConnection.m new file mode 100644 index 0000000..f242bca --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerConnection.m @@ -0,0 +1,315 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "Mixpanel.h" +#import "MPABTestDesignerChangeRequestMessage.h" +#import "MPABTestDesignerClearRequestMessage.h" +#import "MPABTestDesignerConnection.h" +#import "MPABTestDesignerDeviceInfoRequestMessage.h" +#import "MPABTestDesignerDisconnectMessage.h" +#import "MPABTestDesignerMessage.h" +#import "MPABTestDesignerSnapshotRequestMessage.h" +#import "MPABTestDesignerSnapshotResponseMessage.h" +#import "MPABTestDesignerTweakRequestMessage.h" +#import "MPDesignerEventBindingMessage.h" +#import "MPDesignerSessionCollection.h" +#import "MPLogger.h" +#import "MPSwizzler.h" + +NSString * const kSessionVariantKey = @"session_variant"; +static NSString * const kStartLoadingAnimationKey = @"MPConnectivityBarLoadingAnimation"; +static NSString * const kFinishLoadingAnimationKey = @"MPConnectivityBarFinishLoadingAnimation"; + +@interface MPABTestDesignerConnection () +@property (strong, nonatomic) UIWindow *connectivityIndicatorWindow; +@end + +@implementation MPABTestDesignerConnection +{ + /* The difference between _open and _connected is that open + is set when the socket is open, and _connected is set when + we actually have started sending/receiving messages from + the server. A connection can become _open/not _open in quick + succession if the websocket proxy rejects the request, but + we will only try and reconnect if we were actually _connected. + */ + BOOL _open; + BOOL _connected; + + NSURL *_url; + NSMutableDictionary *_session; + NSDictionary *_typeToMessageClassMap; + MPWebSocket *_webSocket; + NSOperationQueue *_commandQueue; + UIView *_recordingView; + CALayer *_indeterminateLayer; + void (^_connectCallback)(); + void (^_disconnectCallback)(); +} + +- (instancetype)initWithURL:(NSURL *)url keepTrying:(BOOL)keepTrying connectCallback:(void (^)())connectCallback disconnectCallback:(void (^)())disconnectCallback +{ + self = [super init]; + if (self) { + _typeToMessageClassMap = @{ + MPABTestDesignerSnapshotRequestMessageType : [MPABTestDesignerSnapshotRequestMessage class], + MPABTestDesignerChangeRequestMessageType : [MPABTestDesignerChangeRequestMessage class], + MPABTestDesignerDeviceInfoRequestMessageType : [MPABTestDesignerDeviceInfoRequestMessage class], + MPABTestDesignerTweakRequestMessageType : [MPABTestDesignerTweakRequestMessage class], + MPABTestDesignerClearRequestMessageType : [MPABTestDesignerClearRequestMessage class], + MPABTestDesignerDisconnectMessageType : [MPABTestDesignerDisconnectMessage class], + MPDesignerEventBindingRequestMessageType : [MPDesignerEventBindingRequestMessage class], + }; + + _open = NO; + _connected = NO; + _sessionEnded = NO; + _session = [NSMutableDictionary dictionary]; + _url = url; + _connectCallback = connectCallback; + _disconnectCallback = disconnectCallback; + + _commandQueue = [[NSOperationQueue alloc] init]; + _commandQueue.maxConcurrentOperationCount = 1; + _commandQueue.suspended = YES; + + if (keepTrying) { + [self open:YES maxInterval:30 maxRetries:40]; + } else { + [self open:YES maxInterval:0 maxRetries:0]; + } + } + + return self; +} + +- (instancetype)initWithURL:(NSURL *)url +{ + return [self initWithURL:url keepTrying:NO connectCallback:nil disconnectCallback:nil]; +} + + +- (void)open:(BOOL)initiate maxInterval:(int)maxInterval maxRetries:(int)maxRetries +{ + static int retries = 0; + BOOL inRetryLoop = retries > 0; + + MPLogDebug(@"In open. initiate = %d, retries = %d, maxRetries = %d, maxInterval = %d, connected = %d", initiate, retries, maxRetries, maxInterval, _connected); + + if (self.sessionEnded || _connected || (inRetryLoop && retries >= maxRetries) ) { + // break out of retry loop if any of the success conditions are met. + retries = 0; + } else if (initiate ^ inRetryLoop) { + // If we are initiating a new connection, or we are already in a + // retry loop (but not both). Then open a socket. + if (!_open) { + MPLogDebug(@"Attempting to open WebSocket to: %@, try %d/%d ", _url, retries, maxRetries); + _open = YES; + _webSocket = [[MPWebSocket alloc] initWithURL:_url]; + _webSocket.delegate = self; + [_webSocket open]; + } + if (retries < maxRetries) { + __weak MPABTestDesignerConnection *weakSelf = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(MIN(pow(1.4, retries), maxInterval) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + MPABTestDesignerConnection *strongSelf = weakSelf; + [strongSelf open:NO maxInterval:maxInterval maxRetries:maxRetries]; + }); + retries++; + } + } +} + +- (void)close +{ + [_webSocket close]; + for (id value in _session.allValues) { + if ([value conformsToProtocol:@protocol(MPDesignerSessionCollection)]) { + [value cleanup]; + } + } + _session = nil; +} + +- (void)dealloc +{ + _webSocket.delegate = nil; + [self close]; +} + +- (void)setSessionObject:(id)object forKey:(NSString *)key +{ + NSParameterAssert(key != nil); + + @synchronized (_session) + { + _session[key] = object ?: [NSNull null]; + } +} + +- (id)sessionObjectForKey:(NSString *)key +{ + NSParameterAssert(key != nil); + + @synchronized (_session) + { + id object = _session[key]; + return [object isEqual:[NSNull null]] ? nil : object; + } +} + +- (void)sendMessage:(id)message +{ + if (_connected) { + MPLogDebug(@"Sending message: %@", [message debugDescription]); + NSString *jsonString = [[NSString alloc] initWithData:[message JSONData] encoding:NSUTF8StringEncoding]; + [_webSocket send:jsonString]; + } else { + MPLogDebug(@"Not sending message as we are not connected: %@", [message debugDescription]); + } +} + +- (id )designerMessageForMessage:(id)message +{ + MPLogInfo(@"raw message: %@", message); + + NSParameterAssert([message isKindOfClass:[NSString class]] || [message isKindOfClass:[NSData class]]); + + id designerMessage = nil; + + NSData *jsonData = [message isKindOfClass:[NSString class]] ? [(NSString *)message dataUsingEncoding:NSUTF8StringEncoding] : message; + + NSError *error = nil; + id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:(NSJSONReadingOptions)0 error:&error]; + if ([jsonObject isKindOfClass:[NSDictionary class]]) { + NSDictionary *messageDictionary = (NSDictionary *)jsonObject; + NSString *type = messageDictionary[@"type"]; + NSDictionary *payload = messageDictionary[@"payload"]; + + designerMessage = [_typeToMessageClassMap[type] messageWithType:type payload:payload]; + } else { + MPLogWarning(@"Badly formed socket message expected JSON dictionary: %@", error); + } + + return designerMessage; +} + +#pragma mark - MPWebSocketDelegate Methods + +- (void)webSocket:(MPWebSocket *)webSocket didReceiveMessage:(id)message +{ + if (!_connected) { + _connected = YES; + [self showConnectedViewWithLoading:NO]; + if (_connectCallback) { + _connectCallback(); + } + } + id designerMessage = [self designerMessageForMessage:message]; + MPLogInfo(@"WebSocket received message: %@", [designerMessage debugDescription]); + NSOperation *commandOperation = [designerMessage responseCommandWithConnection:self]; + + if (commandOperation) { + [_commandQueue addOperation:commandOperation]; + } +} + +- (void)webSocketDidOpen:(MPWebSocket *)webSocket +{ + MPLogInfo(@"WebSocket %@ did open.", webSocket); + _commandQueue.suspended = NO; + [self showConnectedViewWithLoading:YES]; +} + +- (void)webSocket:(MPWebSocket *)webSocket didFailWithError:(NSError *)error +{ + MPLogError(@"WebSocket did fail with error: %@", error); + _commandQueue.suspended = YES; + [_commandQueue cancelAllOperations]; + [self hideConnectedView]; + _open = NO; + if (_connected) { + _connected = NO; + [self open:YES maxInterval:10 maxRetries:10]; + if (_disconnectCallback) { + _disconnectCallback(); + } + } +} + +- (void)webSocket:(MPWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean +{ + MPLogDebug(@"WebSocket did close with code '%d' reason '%@'.", (int)code, reason); + + _commandQueue.suspended = YES; + [_commandQueue cancelAllOperations]; + [self hideConnectedView]; + _open = NO; + if (_connected) { + _connected = NO; + [self open:YES maxInterval:10 maxRetries:10]; + if (_disconnectCallback) { + _disconnectCallback(); + } + } +} + +- (void)showConnectedViewWithLoading:(BOOL)isLoading { + if (!self.connectivityIndicatorWindow) { + UIWindow *mainWindow = [[UIApplication sharedApplication] delegate].window; + self.connectivityIndicatorWindow = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, mainWindow.frame.size.width, 4.f)]; + self.connectivityIndicatorWindow.backgroundColor = [UIColor clearColor]; + self.connectivityIndicatorWindow.windowLevel = UIWindowLevelAlert; + self.connectivityIndicatorWindow.alpha = 0; + self.connectivityIndicatorWindow.hidden = NO; + + _recordingView = [[UIView alloc] initWithFrame:self.connectivityIndicatorWindow.frame]; + _recordingView.backgroundColor = [UIColor clearColor]; + _indeterminateLayer = [CALayer layer]; + _indeterminateLayer.backgroundColor = [UIColor colorWithRed:1/255.0 green:179/255.0 blue:109/255.0 alpha:1.0].CGColor; + _indeterminateLayer.frame = CGRectMake(0, 0, 0, 4.0f); + [_recordingView.layer addSublayer:_indeterminateLayer]; + [self.connectivityIndicatorWindow addSubview:_recordingView]; + [self.connectivityIndicatorWindow bringSubviewToFront:_recordingView]; + + [UIView animateWithDuration:0.3 animations:^{ + self.connectivityIndicatorWindow.alpha = 1; + }]; + } + [self animateConnecting:isLoading]; +} + +- (void)animateConnecting:(BOOL)isLoading { + if (isLoading) { + CABasicAnimation* myAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"]; + myAnimation.duration = 10.f; + myAnimation.fromValue = @0; + myAnimation.toValue = @(_connectivityIndicatorWindow.bounds.size.width * 1.9f); + myAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + myAnimation.fillMode = kCAFillModeForwards; + myAnimation.removedOnCompletion = NO; + [_indeterminateLayer addAnimation:myAnimation forKey:kStartLoadingAnimationKey]; + } else { + [_indeterminateLayer removeAnimationForKey:kStartLoadingAnimationKey]; + CABasicAnimation* myAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size.width"]; + myAnimation.duration = 0.4f; + myAnimation.fromValue = @([[_indeterminateLayer.presentationLayer valueForKeyPath: @"bounds.size.width"] floatValue]); + myAnimation.toValue = @(_connectivityIndicatorWindow.bounds.size.width * 2.f); + myAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; + myAnimation.fillMode = kCAFillModeForwards; + myAnimation.removedOnCompletion = NO; + [_indeterminateLayer addAnimation:myAnimation forKey:kFinishLoadingAnimationKey]; + } +} + +- (void)hideConnectedView { + if (self.connectivityIndicatorWindow) { + [_indeterminateLayer removeFromSuperlayer]; + [_recordingView removeFromSuperview]; + self.connectivityIndicatorWindow.hidden = YES; + } + self.connectivityIndicatorWindow = nil; +} + +@end + diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h new file mode 100644 index 0000000..b4f32bd --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h @@ -0,0 +1,11 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPAbstractABTestDesignerMessage.h" + +extern NSString *const MPABTestDesignerDeviceInfoRequestMessageType; + +@interface MPABTestDesignerDeviceInfoRequestMessage : MPAbstractABTestDesignerMessage + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.m new file mode 100644 index 0000000..cc6acdb --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.m @@ -0,0 +1,104 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "Mixpanel.h" +#import "MPABTestDesignerConnection.h" +#import "MPABTestDesignerDeviceInfoRequestMessage.h" +#import "MPABTestDesignerDeviceInfoResponseMessage.h" +#import "MPTweak.h" +#import "MPTweakStore.h" + +NSString *const MPABTestDesignerDeviceInfoRequestMessageType = @"device_info_request"; + +@implementation MPABTestDesignerDeviceInfoRequestMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerDeviceInfoRequestMessage *)[self alloc] initWithType:MPABTestDesignerDeviceInfoRequestMessageType]; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + __weak MPABTestDesignerConnection *weak_connection = connection; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + __strong MPABTestDesignerConnection *conn = weak_connection; + + MPABTestDesignerDeviceInfoResponseMessage *deviceInfoResponseMessage = [MPABTestDesignerDeviceInfoResponseMessage message]; + + dispatch_sync(dispatch_get_main_queue(), ^{ + UIDevice *currentDevice = [UIDevice currentDevice]; + + deviceInfoResponseMessage.systemName = currentDevice.systemName; + deviceInfoResponseMessage.systemVersion = currentDevice.systemVersion; + deviceInfoResponseMessage.deviceName = currentDevice.name; + deviceInfoResponseMessage.deviceModel = currentDevice.model; + deviceInfoResponseMessage.appVersion = [[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"]; + deviceInfoResponseMessage.appRelease = [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"]; + deviceInfoResponseMessage.libVersion = [[Mixpanel sharedInstance] libVersion]; + deviceInfoResponseMessage.mainBundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + deviceInfoResponseMessage.availableFontFamilies = [self availableFontFamilies]; + deviceInfoResponseMessage.tweaks = [self getTweaks]; + }); + + [conn sendMessage:deviceInfoResponseMessage]; + }]; + + return operation; +} + +- (NSArray *)availableFontFamilies +{ + NSMutableDictionary *fontFamilies = [NSMutableDictionary dictionary]; + + // Get all the font families and font names. + for (NSString *familyName in [UIFont familyNames]) { + fontFamilies[familyName] = [self fontDictionaryForFontFamilyName:familyName fontNames:[UIFont fontNamesForFamilyName:familyName]]; + } + + // For the system fonts update the font families. + NSArray *systemFonts = @[[UIFont systemFontOfSize:17.0f], + [UIFont boldSystemFontOfSize:17.0f], + [UIFont italicSystemFontOfSize:17.0f]]; + + for (UIFont *systemFont in systemFonts) { + NSString *familyName = systemFont.familyName; + NSString *fontName = systemFont.fontName; + + NSMutableDictionary *font = fontFamilies[familyName]; + if (font) { + NSMutableArray *fontNames = font[@"font_names"]; + if ([fontNames containsObject:fontName] == NO) { + [fontNames addObject:fontName]; + } + } else { + fontFamilies[familyName] = [self fontDictionaryForFontFamilyName:familyName fontNames:@[fontName]]; + } + } + + return fontFamilies.allValues; +} + +- (NSMutableDictionary *)fontDictionaryForFontFamilyName:(NSString *)familyName fontNames:(NSArray *)fontNames +{ + return [@{ + @"family": familyName, + @"font_names": [fontNames mutableCopy] + } mutableCopy]; +} + +- (NSArray *)getTweaks +{ + NSMutableArray *tweaks = [NSMutableArray array]; + for (MPTweak *t in [MPTweakStore sharedInstance].tweaks) { + [tweaks addObject:@{@"name": t.name, + @"encoding": t.encoding, + @"value": t.currentValue ?: [NSNull null], + @"default": t.defaultValue ?: [NSNull null], + @"minimum": t.minimumValue ?: [NSNull null], + @"maximum": t.maximumValue ?: [NSNull null] + }]; + } + return [tweaks copy]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h new file mode 100644 index 0000000..733d90b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h @@ -0,0 +1,22 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPAbstractABTestDesignerMessage.h" + +@interface MPABTestDesignerDeviceInfoResponseMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; + +@property (nonatomic, copy) NSString *systemName; +@property (nonatomic, copy) NSString *systemVersion; +@property (nonatomic, copy) NSString *appVersion; +@property (nonatomic, copy) NSString *appRelease; +@property (nonatomic, copy) NSString *deviceName; +@property (nonatomic, copy) NSString *deviceModel; +@property (nonatomic, copy) NSString *libVersion; +@property (nonatomic, copy) NSArray *availableFontFamilies; +@property (nonatomic, copy) NSString *mainBundleIdentifier; +@property (nonatomic, copy) NSArray *tweaks; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.m new file mode 100644 index 0000000..d776e91 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.m @@ -0,0 +1,114 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPABTestDesignerDeviceInfoResponseMessage.h" + +@implementation MPABTestDesignerDeviceInfoResponseMessage + ++ (instancetype)message +{ + // TODO: provide a payload + return [(MPABTestDesignerDeviceInfoResponseMessage *)[self alloc] initWithType:@"device_info_response"]; +} + +- (NSString *)systemName +{ + return [self payloadObjectForKey:@"system_name"]; +} + +- (void)setSystemName:(NSString *)systemName +{ + [self setPayloadObject:systemName forKey:@"system_name"]; +} + +- (NSString *)systemVersion +{ + return [self payloadObjectForKey:@"system_version"]; +} + +- (void)setSystemVersion:(NSString *)systemVersion +{ + [self setPayloadObject:systemVersion forKey:@"system_version"]; +} + +- (NSString *)appVersion +{ + return [self payloadObjectForKey:@"app_version"]; +} + +- (void)setAppVersion:(NSString *)appVersion +{ + [self setPayloadObject:appVersion forKey:@"app_version"]; +} + +- (NSString *)appRelease +{ + return [self payloadObjectForKey:@"app_release"]; +} + +- (void)setAppRelease:(NSString *)appRelease +{ + [self setPayloadObject:appRelease forKey:@"app_release"]; +} + +- (NSString *)deviceName +{ + return [self payloadObjectForKey:@"device_name"]; +} + +- (void)setDeviceName:(NSString *)deviceName +{ + [self setPayloadObject:deviceName forKey:@"device_name"]; +} + +- (NSString *)libVersion +{ + return [self payloadObjectForKey:@"lib_version"]; +} + +- (void)setLibVersion:(NSString *)libVersion +{ + [self setPayloadObject:libVersion forKey:@"lib_version"]; +} + +- (NSString *)deviceModel +{ + return [self payloadObjectForKey:@"device_model"]; +} + +- (void)setDeviceModel:(NSString *)deviceModel +{ + [self setPayloadObject:deviceModel forKey:@"device_model"]; +} + +- (NSArray *)availableFontFamilies +{ + return [self payloadObjectForKey:@"available_font_families"]; +} + +- (void)setAvailableFontFamilies:(NSArray *)availableFontFamilies +{ + [self setPayloadObject:availableFontFamilies forKey:@"available_font_families"]; +} + +- (NSString *)mainBundleIdentifier +{ + return [self payloadObjectForKey:@"main_bundle_identifier"]; +} + +- (void)setMainBundleIdentifier:(NSString *)mainBundleIdentifier +{ + [self setPayloadObject:mainBundleIdentifier forKey:@"main_bundle_identifier"]; +} + +- (void)setTweaks:(NSArray *)tweaks +{ + [self setPayloadObject:tweaks forKey:@"tweaks"]; +} + +- (NSArray *)tweaks +{ + return [self payloadObjectForKey:@"tweaks"]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.h new file mode 100644 index 0000000..8ef1485 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.h @@ -0,0 +1,15 @@ +// +// MPABTestDesignerDisconnectMessage.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 29/7/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPAbstractABTestDesignerMessage.h" + +extern NSString *const MPABTestDesignerDisconnectMessageType; + +@interface MPABTestDesignerDisconnectMessage : MPAbstractABTestDesignerMessage + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.m new file mode 100644 index 0000000..022cd8d --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerDisconnectMessage.m @@ -0,0 +1,41 @@ +// +// MPABTestDesignerDisconnectMessage.m +// HelloMixpanel +// +// Created by Alex Hofsteede on 29/7/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPABTestDesignerConnection.h" +#import "MPABTestDesignerDisconnectMessage.h" +#import "MPVariant.h" + +NSString *const MPABTestDesignerDisconnectMessageType = @"disconnect"; + +@implementation MPABTestDesignerDisconnectMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerDisconnectMessage *)[self alloc] initWithType:MPABTestDesignerDisconnectMessageType]; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + __weak MPABTestDesignerConnection *weak_connection = connection; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + MPABTestDesignerConnection *conn = weak_connection; + + MPVariant *variant = [connection sessionObjectForKey:kSessionVariantKey]; + if (variant) { + dispatch_sync(dispatch_get_main_queue(), ^{ + [variant stop]; + }); + } + + conn.sessionEnded = YES; + [conn close]; + }]; + return operation; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerMessage.h new file mode 100644 index 0000000..a89b0a0 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerMessage.h @@ -0,0 +1,19 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@class MPABTestDesignerConnection; + +@protocol MPABTestDesignerMessage + +@property (nonatomic, copy, readonly) NSString *type; + +- (void)setPayloadObject:(id)object forKey:(NSString *)key; +- (id)payloadObjectForKey:(NSString *)key; + +- (NSData *)JSONData; + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h new file mode 100644 index 0000000..5125012 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.h @@ -0,0 +1,17 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPAbstractABTestDesignerMessage.h" + +@class MPObjectSerializerConfig; + +extern NSString *const MPABTestDesignerSnapshotRequestMessageType; + +@interface MPABTestDesignerSnapshotRequestMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; + +@property (nonatomic, readonly) MPObjectSerializerConfig *configuration; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.m new file mode 100644 index 0000000..81f3b24 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotRequestMessage.m @@ -0,0 +1,85 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPABTestDesignerConnection.h" +#import "MPABTestDesignerSnapshotRequestMessage.h" +#import "MPABTestDesignerSnapshotResponseMessage.h" +#import "MPApplicationStateSerializer.h" +#import "MPObjectIdentityProvider.h" +#import "MPObjectSerializerConfig.h" + +NSString * const MPABTestDesignerSnapshotRequestMessageType = @"snapshot_request"; + +static NSString * const kSnapshotSerializerConfigKey = @"snapshot_class_descriptions"; +static NSString * const kObjectIdentityProviderKey = @"object_identity_provider"; + +@implementation MPABTestDesignerSnapshotRequestMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerSnapshotRequestMessage *)[self alloc] initWithType:MPABTestDesignerSnapshotRequestMessageType]; +} + +- (MPObjectSerializerConfig *)configuration +{ + NSDictionary *config = [self payloadObjectForKey:@"config"]; + return config ? [[MPObjectSerializerConfig alloc] initWithDictionary:config] : nil; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + __block MPObjectSerializerConfig *serializerConfig = self.configuration; + __block NSString *imageHash = [self payloadObjectForKey:@"image_hash"]; + + __weak MPABTestDesignerConnection *weak_connection = connection; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + __strong MPABTestDesignerConnection *conn = weak_connection; + + // Update the class descriptions in the connection session if provided as part of the message. + if (serializerConfig) { + [connection setSessionObject:serializerConfig forKey:kSnapshotSerializerConfigKey]; + } else if ([connection sessionObjectForKey:kSnapshotSerializerConfigKey]) { + // Get the class descriptions from the connection session store. + serializerConfig = [connection sessionObjectForKey:kSnapshotSerializerConfigKey]; + } else { + // If neither place has a config, this is probably a stale message and we can't create a snapshot. + return; + } + + // Get the object identity provider from the connection's session store or create one if there is none already. + MPObjectIdentityProvider *objectIdentityProvider = [connection sessionObjectForKey:kObjectIdentityProviderKey]; + if (objectIdentityProvider == nil) { + objectIdentityProvider = [[MPObjectIdentityProvider alloc] init]; + [connection setSessionObject:objectIdentityProvider forKey:kObjectIdentityProviderKey]; + } + + MPApplicationStateSerializer *serializer = [[MPApplicationStateSerializer alloc] initWithApplication:[UIApplication sharedApplication] + configuration:serializerConfig + objectIdentityProvider:objectIdentityProvider]; + + MPABTestDesignerSnapshotResponseMessage *snapshotMessage = [MPABTestDesignerSnapshotResponseMessage message]; + __block UIImage *screenshot = nil; + __block NSDictionary *serializedObjects = nil; + + dispatch_sync(dispatch_get_main_queue(), ^{ + screenshot = [serializer screenshotImageForWindowAtIndex:0]; + }); + snapshotMessage.screenshot = screenshot; + + if ([imageHash isEqualToString:snapshotMessage.imageHash]) { + serializedObjects = [connection sessionObjectForKey:@"snapshot_hierarchy"]; + } else { + dispatch_sync(dispatch_get_main_queue(), ^{ + serializedObjects = [serializer objectHierarchyForWindowAtIndex:0]; + }); + [connection setSessionObject:serializedObjects forKey:@"snapshot_hierarchy"]; + } + + snapshotMessage.serializedObjects = serializedObjects; + [conn sendMessage:snapshotMessage]; + }]; + + return operation; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h new file mode 100644 index 0000000..1e9ff3f --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.h @@ -0,0 +1,15 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPAbstractABTestDesignerMessage.h" + +@interface MPABTestDesignerSnapshotResponseMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; + +@property (nonatomic, strong) UIImage *screenshot; +@property (nonatomic, copy) NSDictionary *serializedObjects; +@property (nonatomic, strong, readonly) NSString *imageHash; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.m new file mode 100644 index 0000000..bb7b88b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerSnapshotResponseMessage.m @@ -0,0 +1,61 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPABTestDesignerSnapshotResponseMessage.h" + +@implementation MPABTestDesignerSnapshotResponseMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerSnapshotResponseMessage *)[self alloc] initWithType:@"snapshot_response"]; +} + +- (void)setScreenshot:(UIImage *)screenshot +{ + id payloadObject = nil; + id imageHash = nil; + if (screenshot) { + NSData *jpegSnapshotImageData = UIImageJPEGRepresentation(screenshot, 0.5); + if (jpegSnapshotImageData) { + payloadObject = [jpegSnapshotImageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + imageHash = [self getImageHash:jpegSnapshotImageData]; + } + } + + _imageHash = imageHash; + [self setPayloadObject:(payloadObject ?: [NSNull null]) forKey:@"screenshot"]; + [self setPayloadObject:(imageHash ?: [NSNull null]) forKey:@"image_hash"]; +} + +- (UIImage *)screenshot +{ + NSString *base64Image = [self payloadObjectForKey:@"screenshot"]; + NSData *imageData = [[NSData alloc] initWithBase64EncodedString:base64Image + options:NSDataBase64DecodingIgnoreUnknownCharacters]; + return imageData ? [UIImage imageWithData:imageData] : nil; +} + +- (void)setSerializedObjects:(NSDictionary *)serializedObjects +{ + [self setPayloadObject:serializedObjects forKey:@"serialized_objects"]; +} + +- (NSDictionary *)serializedObjects +{ + return [self payloadObjectForKey:@"serialized_objects"]; +} + +- (NSString *)getImageHash:(NSData *)imageData +{ + unsigned char result[CC_MD5_DIGEST_LENGTH]; + CC_MD5(imageData.bytes, (uint)imageData.length, result); + NSString *imageHash = [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + result[0], result[1], result[2], result[3], + result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], + result[12], result[13], result[14], result[15]]; + return imageHash; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.h new file mode 100644 index 0000000..38bb726 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.h @@ -0,0 +1,16 @@ +// +// MPABTestDesignerTweakRequestMessage.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 7/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import +#import "MPAbstractABTestDesignerMessage.h" + +extern NSString *const MPABTestDesignerTweakRequestMessageType; + +@interface MPABTestDesignerTweakRequestMessage : MPAbstractABTestDesignerMessage + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.m new file mode 100644 index 0000000..b0ec3a2 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakRequestMessage.m @@ -0,0 +1,51 @@ +// +// MPABTestDesignerTweakRequestMessage.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 7/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPABTestDesignerConnection.h" +#import "MPABTestDesignerTweakRequestMessage.h" +#import "MPABTestDesignerTweakResponseMessage.h" +#import "MPLogger.h" +#import "MPVariant.h" + +NSString *const MPABTestDesignerTweakRequestMessageType = @"tweak_request"; + +@implementation MPABTestDesignerTweakRequestMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerTweakRequestMessage *)[self alloc] initWithType:MPABTestDesignerTweakRequestMessageType]; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + __weak MPABTestDesignerConnection *weak_connection = connection; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + MPABTestDesignerConnection *conn = weak_connection; + + MPVariant *variant = [conn sessionObjectForKey:kSessionVariantKey]; + if (!variant) { + variant = [[MPVariant alloc] init]; + [conn setSessionObject:variant forKey:kSessionVariantKey]; + } + + id tweaks = [self payload][@"tweaks"]; + if ([tweaks isKindOfClass:[NSArray class]]) { + dispatch_sync(dispatch_get_main_queue(), ^{ + [variant addTweaksFromJSONObject:tweaks andExecute:YES]; + }); + } + + MPABTestDesignerTweakResponseMessage *changeResponseMessage = [MPABTestDesignerTweakResponseMessage message]; + changeResponseMessage.status = @"OK"; + [conn sendMessage:changeResponseMessage]; + }]; + + return operation; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.h new file mode 100644 index 0000000..68d2ac8 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.h @@ -0,0 +1,17 @@ +// +// MPABTestDesignerTweakResponseMessage.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 7/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPAbstractABTestDesignerMessage.h" + +@interface MPABTestDesignerTweakResponseMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; + +@property (nonatomic, copy) NSString *status; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.m new file mode 100644 index 0000000..8ce4db5 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPABTestDesignerTweakResponseMessage.m @@ -0,0 +1,28 @@ +// +// MPABTestDesignerTweakResponseMessage.m +// HelloMixpanel +// +// Created by Alex Hofsteede on 7/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPABTestDesignerTweakResponseMessage.h" + +@implementation MPABTestDesignerTweakResponseMessage + ++ (instancetype)message +{ + return [(MPABTestDesignerTweakResponseMessage *)[self alloc] initWithType:@"tweak_response"]; +} + +- (void)setStatus:(NSString *)status +{ + [self setPayloadObject:status forKey:@"status"]; +} + +- (NSString *)status +{ + return [self payloadObjectForKey:@"status"]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.h new file mode 100644 index 0000000..d1f55c1 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.h @@ -0,0 +1,22 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPABTestDesignerMessage.h" + +@interface MPAbstractABTestDesignerMessage : NSObject + +@property (nonatomic, copy, readonly) NSString *type; + ++ (instancetype)messageWithType:(NSString *)type payload:(NSDictionary *)payload; + +- (instancetype)initWithType:(NSString *)type; +- (instancetype)initWithType:(NSString *)type payload:(NSDictionary *)payload; + +- (void)setPayloadObject:(id)object forKey:(NSString *)key; +- (id)payloadObjectForKey:(NSString *)key; +- (NSDictionary *)payload; + +- (NSData *)JSONData; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.m new file mode 100644 index 0000000..bec4473 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPAbstractABTestDesignerMessage.m @@ -0,0 +1,79 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPAbstractABTestDesignerMessage.h" +#import "MPLogger.h" + +@interface MPAbstractABTestDesignerMessage () + +@property (nonatomic, copy, readwrite) NSString *type; + +@end + +@implementation MPAbstractABTestDesignerMessage + +{ + NSMutableDictionary *_payload; +} + ++ (instancetype)messageWithType:(NSString *)type payload:(NSDictionary *)payload +{ + return [(MPAbstractABTestDesignerMessage *)[self alloc] initWithType:type payload:payload]; +} + +- (instancetype)initWithType:(NSString *)type +{ + return [self initWithType:type payload:@{}]; +} + +- (instancetype)initWithType:(NSString *)type payload:(NSDictionary *)payload +{ + self = [super init]; + if (self) { + _type = type; + _payload = [payload mutableCopy]; + } + + return self; +} + +- (void)setPayloadObject:(id)object forKey:(NSString *)key +{ + _payload[key] = object ?: [NSNull null]; +} + +- (id)payloadObjectForKey:(NSString *)key +{ + id object = _payload[key]; + return [object isEqual:[NSNull null]] ? nil : object; +} + +- (NSDictionary *)payload +{ + return [_payload copy]; +} + +- (NSData *)JSONData +{ + NSDictionary *jsonObject = @{ @"type": _type, @"payload": [_payload copy] }; + + NSError *error = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:(NSJSONWritingOptions)0 error:&error]; + if (error) { + MPLogError(@"Failed to serialize test designer message: %@", error); + } + + return jsonData; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + return nil; +} + +- (NSString *)debugDescription +{ + return [NSString stringWithFormat:@"<%@:%p type='%@'>", NSStringFromClass([self class]), (__bridge void *)self, self.type]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPApplicationStateSerializer.h b/ios/Pods/Mixpanel/Mixpanel/MPApplicationStateSerializer.h new file mode 100644 index 0000000..eddc5a2 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPApplicationStateSerializer.h @@ -0,0 +1,17 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@class MPObjectSerializerConfig; +@class MPObjectIdentityProvider; + +@interface MPApplicationStateSerializer : NSObject + +- (instancetype)initWithApplication:(UIApplication *)application configuration:(MPObjectSerializerConfig *)configuration objectIdentityProvider:(MPObjectIdentityProvider *)objectIdentityProvider; + +- (UIImage *)screenshotImageForWindowAtIndex:(NSUInteger)index; + +- (NSDictionary *)objectHierarchyForWindowAtIndex:(NSUInteger)index; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPApplicationStateSerializer.m b/ios/Pods/Mixpanel/Mixpanel/MPApplicationStateSerializer.m new file mode 100644 index 0000000..f2fee83 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPApplicationStateSerializer.m @@ -0,0 +1,66 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPApplicationStateSerializer.h" +#import "MPClassDescription.h" +#import "MPLogger.h" +#import "MPObjectIdentityProvider.h" +#import "MPObjectSerializer.h" +#import "MPObjectSerializerConfig.h" + +@implementation MPApplicationStateSerializer + +{ + MPObjectSerializer *_serializer; + UIApplication *_application; +} + +- (instancetype)initWithApplication:(UIApplication *)application configuration:(MPObjectSerializerConfig *)configuration objectIdentityProvider:(MPObjectIdentityProvider *)objectIdentityProvider +{ + NSParameterAssert(application != nil); + NSParameterAssert(configuration != nil); + + self = [super init]; + if (self) { + _application = application; + _serializer = [[MPObjectSerializer alloc] initWithConfiguration:configuration objectIdentityProvider:objectIdentityProvider]; + } + + return self; +} + +- (UIImage *)screenshotImageForWindowAtIndex:(NSUInteger)index +{ + UIImage *image = nil; + + UIWindow *window = [self windowAtIndex:index]; + if (window && !CGRectEqualToRect(window.frame, CGRectZero)) { + UIGraphicsBeginImageContextWithOptions(window.bounds.size, YES, window.screen.scale); + if ([window drawViewHierarchyInRect:window.bounds afterScreenUpdates:NO] == NO) { + MPLogError(@"Unable to get complete screenshot for window at index: %d.", (int)index); + } + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + + return image; +} + +- (UIWindow *)windowAtIndex:(NSUInteger)index +{ + NSParameterAssert(index < _application.windows.count); + return _application.windows[index]; +} + +- (NSDictionary *)objectHierarchyForWindowAtIndex:(NSUInteger)index +{ + UIWindow *window = [self windowAtIndex:index]; + if (window) { + return [_serializer serializedObjectsWithRootObject:window]; + } + + return @{}; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPBOOLToNSNumberValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPBOOLToNSNumberValueTransformer.m new file mode 100644 index 0000000..3c8a61b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPBOOLToNSNumberValueTransformer.m @@ -0,0 +1,27 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPBOOLToNSNumberValueTransformer + ++ (Class)transformedValueClass +{ + return [@YES class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return NO; +} + +- (id)transformedValue:(id)value +{ + if ([value respondsToSelector:@selector(boolValue)]) { + return @([value boolValue]); + } + + return nil; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPCATransform3DToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPCATransform3DToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..a524422 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPCATransform3DToNSDictionaryValueTransformer.m @@ -0,0 +1,117 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +static NSDictionary *MPCATransform3DCreateDictionaryRepresentation(CATransform3D transform) +{ + return @{ + @"m11": @(transform.m11), + @"m12": @(transform.m12), + @"m13": @(transform.m13), + @"m14": @(transform.m14), + + @"m21": @(transform.m21), + @"m22": @(transform.m22), + @"m23": @(transform.m23), + @"m24": @(transform.m24), + + @"m31": @(transform.m31), + @"m32": @(transform.m32), + @"m33": @(transform.m33), + @"m34": @(transform.m34), + + @"m41": @(transform.m41), + @"m42": @(transform.m42), + @"m43": @(transform.m43), + @"m44": @(transform.m44), + }; +} + +static BOOL MPCATransform3DMakeWithDictionaryRepresentation(NSDictionary *dictionary, CATransform3D *transform) +{ + if (transform) { + id m11 = dictionary[@"m11"]; + id m12 = dictionary[@"m12"]; + id m13 = dictionary[@"m13"]; + id m14 = dictionary[@"m14"]; + + id m21 = dictionary[@"m21"]; + id m22 = dictionary[@"m22"]; + id m23 = dictionary[@"m23"]; + id m24 = dictionary[@"m24"]; + + id m31 = dictionary[@"m31"]; + id m32 = dictionary[@"m32"]; + id m33 = dictionary[@"m33"]; + id m34 = dictionary[@"m34"]; + + id m41 = dictionary[@"m41"]; + id m42 = dictionary[@"m42"]; + id m43 = dictionary[@"m43"]; + id m44 = dictionary[@"m44"]; + + if (m11 && m12 && m13 && m14 && + m21 && m22 && m23 && m24 && + m31 && m32 && m33 && m34 && + m41 && m42 && m43 && m44) + { + transform->m11 = (CGFloat)[m11 doubleValue]; + transform->m12 = (CGFloat)[m12 doubleValue]; + transform->m13 = (CGFloat)[m13 doubleValue]; + transform->m14 = (CGFloat)[m14 doubleValue]; + + transform->m21 = (CGFloat)[m21 doubleValue]; + transform->m22 = (CGFloat)[m22 doubleValue]; + transform->m23 = (CGFloat)[m23 doubleValue]; + transform->m24 = (CGFloat)[m24 doubleValue]; + + transform->m31 = (CGFloat)[m31 doubleValue]; + transform->m32 = (CGFloat)[m32 doubleValue]; + transform->m33 = (CGFloat)[m33 doubleValue]; + transform->m34 = (CGFloat)[m34 doubleValue]; + + transform->m41 = (CGFloat)[m41 doubleValue]; + transform->m42 = (CGFloat)[m42 doubleValue]; + transform->m43 = (CGFloat)[m43 doubleValue]; + transform->m44 = (CGFloat)[m44 doubleValue]; + + return YES; + } + } + + return NO; +} + +@implementation MPCATransform3DToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value respondsToSelector:@selector(CATransform3DValue)]) { + return MPCATransform3DCreateDictionaryRepresentation([value CATransform3DValue]); + } + + return @{}; +} + +- (id)reverseTransformedValue:(id)value +{ + CATransform3D transform = CATransform3DIdentity; + if ([value isKindOfClass:[NSDictionary class]] && MPCATransform3DMakeWithDictionaryRepresentation(value, &transform)) { + return [NSValue valueWithCATransform3D:transform]; + } + + return [NSValue valueWithCATransform3D:CATransform3DIdentity]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPCGAffineTransformToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPCGAffineTransformToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..7c2c1a2 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPCGAffineTransformToNSDictionaryValueTransformer.m @@ -0,0 +1,74 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +static NSDictionary *MPCGAffineTransformCreateDictionaryRepresentation(CGAffineTransform transform) +{ + return @{ + @"a": @(transform.a), + @"b": @(transform.b), + @"c": @(transform.c), + @"d": @(transform.d), + @"tx": @(transform.tx), + @"ty": @(transform.ty) + }; +} + +static BOOL MPCGAffineTransformMakeWithDictionaryRepresentation(NSDictionary *dictionary, CGAffineTransform *transform) +{ + if (transform) { + id a = dictionary[@"a"]; + id b = dictionary[@"b"]; + id c = dictionary[@"c"]; + id d = dictionary[@"d"]; + id tx = dictionary[@"tx"]; + id ty = dictionary[@"ty"]; + + if (a && b && c && d && tx && ty) { + transform->a = (CGFloat)[a doubleValue]; + transform->b = (CGFloat)[b doubleValue]; + transform->c = (CGFloat)[c doubleValue]; + transform->d = (CGFloat)[d doubleValue]; + transform->tx = (CGFloat)[tx doubleValue]; + transform->ty = (CGFloat)[ty doubleValue]; + + return YES; + } + } + + return NO; +} + +@implementation MPCGAffineTransformToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value respondsToSelector:@selector(CGAffineTransformValue)]) { + return MPCGAffineTransformCreateDictionaryRepresentation([value CGAffineTransformValue]); + } + + return @{}; +} + +- (id)reverseTransformedValue:(id)value +{ + CGAffineTransform transform = CGAffineTransformIdentity; + if ([value isKindOfClass:[NSDictionary class]] && MPCGAffineTransformMakeWithDictionaryRepresentation(value, &transform)) { + return [NSValue valueWithCGAffineTransform:transform]; + } + + return [NSValue valueWithCGAffineTransform:CGAffineTransformIdentity]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPCGColorRefToNSStringValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPCGColorRefToNSStringValueTransformer.m new file mode 100644 index 0000000..b08ae43 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPCGColorRefToNSStringValueTransformer.m @@ -0,0 +1,30 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPCGColorRefToNSStringValueTransformer + ++ (Class)transformedValueClass +{ + return [NSString class]; +} + +- (id)transformedValue:(id)value +{ + if (value && CFGetTypeID((__bridge CFTypeRef)value) == CGColorGetTypeID()) { + NSValueTransformer *transformer = [NSValueTransformer valueTransformerForName:@"MPUIColorToNSStringValueTransformer"]; + return [transformer transformedValue:[[UIColor alloc] initWithCGColor:(__bridge CGColorRef)value]]; + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + NSValueTransformer *transformer = [NSValueTransformer valueTransformerForName:@"MPUIColorToNSStringValueTransformer"]; + UIColor *uiColor = [transformer reverseTransformedValue:value]; + return CFBridgingRelease(CGColorCreateCopy([uiColor CGColor])); +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPCGPointToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPCGPointToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..dae1738 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPCGPointToNSDictionaryValueTransformer.m @@ -0,0 +1,40 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPCGPointToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value respondsToSelector:@selector(CGPointValue)]) { + CGPoint point = [value CGPointValue]; + point.x = isnormal(point.x) ? point.x : 0.0f; + point.y = isnormal(point.y) ? point.y : 0.0f; + return CFBridgingRelease(CGPointCreateDictionaryRepresentation(point)); + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + CGPoint point = CGPointZero; + if ([value isKindOfClass:[NSDictionary class]] && CGPointMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)value, &point)) { + return [NSValue valueWithCGPoint:point]; + } + + return [NSValue valueWithCGPoint:CGPointZero]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPCGRectToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPCGRectToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..65a96bc --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPCGRectToNSDictionaryValueTransformer.m @@ -0,0 +1,42 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPCGRectToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value respondsToSelector:@selector(CGRectValue)]) { + CGRect rect = [value CGRectValue]; + rect.origin.x = isnormal(rect.origin.x) ? rect.origin.x : 0.0f; + rect.origin.y = isnormal(rect.origin.y) ? rect.origin.y : 0.0f; + rect.size.width = isnormal(rect.size.width) ? rect.size.width : 0.0f; + rect.size.height = isnormal(rect.size.height) ? rect.size.height : 0.0f; + return CFBridgingRelease(CGRectCreateDictionaryRepresentation(rect)); + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + CGRect rect = CGRectZero; + if ([value isKindOfClass:[NSDictionary class]] && CGRectMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)value, &rect)) { + return [NSValue valueWithCGRect:rect]; + } + + return [NSValue valueWithCGRect:CGRectZero]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPCGSizeToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPCGSizeToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..cdc3821 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPCGSizeToNSDictionaryValueTransformer.m @@ -0,0 +1,40 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPCGSizeToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value respondsToSelector:@selector(CGSizeValue)]) { + CGSize size = [value CGSizeValue]; + size.width = isnormal(size.width) ? size.width : 0.0f; + size.height = isnormal(size.height) ? size.height : 0.0f; + return CFBridgingRelease(CGSizeCreateDictionaryRepresentation(size)); + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + CGSize size = CGSizeZero; + if ([value isKindOfClass:[NSDictionary class]] && CGSizeMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)value, &size)) { + return [NSValue valueWithCGSize:size]; + } + + return [NSValue valueWithCGSize:CGSizeZero]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPClassDescription.h b/ios/Pods/Mixpanel/Mixpanel/MPClassDescription.h new file mode 100644 index 0000000..1dd40da --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPClassDescription.h @@ -0,0 +1,25 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPTypeDescription.h" + +@interface MPClassDescription : MPTypeDescription + +@property (nonatomic, readonly) MPClassDescription *superclassDescription; +@property (nonatomic, readonly) NSArray *propertyDescriptions; +@property (nonatomic, readonly) NSArray *delegateInfos; + +- (instancetype)initWithSuperclassDescription:(MPClassDescription *)superclassDescription dictionary:(NSDictionary *)dictionary; + +- (BOOL)isDescriptionForKindOfClass:(Class)aClass; + +@end + +@interface MPDelegateInfo : NSObject + +@property (nonatomic, readonly) NSString *selectorName; + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPClassDescription.m b/ios/Pods/Mixpanel/Mixpanel/MPClassDescription.m new file mode 100644 index 0000000..638dfa5 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPClassDescription.m @@ -0,0 +1,77 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPClassDescription.h" +#import "MPPropertyDescription.h" + +@implementation MPDelegateInfo + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + if (self = [super init]) { + _selectorName = dictionary[@"selector"]; + } + return self; +} + +@end + +@implementation MPClassDescription + +{ + NSArray *_propertyDescriptions; + NSArray *_delegateInfos; +} + +- (instancetype)initWithSuperclassDescription:(MPClassDescription *)superclassDescription dictionary:(NSDictionary *)dictionary +{ + self = [super initWithDictionary:dictionary]; + if (self) { + _superclassDescription = superclassDescription; + + NSMutableArray *propertyDescriptions = [NSMutableArray array]; + for (NSDictionary *propertyDictionary in dictionary[@"properties"]) { + [propertyDescriptions addObject:[[MPPropertyDescription alloc] initWithDictionary:propertyDictionary]]; + } + + _propertyDescriptions = [propertyDescriptions copy]; + + NSMutableArray *delegateInfos = [NSMutableArray array]; + for (NSDictionary *delegateInfoDictionary in dictionary[@"delegateImplements"]) { + [delegateInfos addObject:[[MPDelegateInfo alloc] initWithDictionary:delegateInfoDictionary]]; + } + _delegateInfos = [delegateInfos copy]; + } + + return self; +} + +- (NSArray *)propertyDescriptions +{ + NSMutableDictionary *allPropertyDescriptions = [NSMutableDictionary dictionary]; + + MPClassDescription *description = self; + while (description) + { + for (MPPropertyDescription *propertyDescription in description->_propertyDescriptions) { + if (!allPropertyDescriptions[propertyDescription.name]) { + allPropertyDescriptions[propertyDescription.name] = propertyDescription; + } + } + description = description.superclassDescription; + } + + return allPropertyDescriptions.allValues; +} + +- (BOOL)isDescriptionForKindOfClass:(Class)aClass +{ + return [self.name isEqualToString:NSStringFromClass(aClass)] && [self.superclassDescription isDescriptionForKindOfClass:[aClass superclass]]; +} + +- (NSString *)debugDescription +{ + return [NSString stringWithFormat:@"<%@:%p name='%@' superclass='%@'>", NSStringFromClass([self class]), (__bridge void *)self, self.name, self.superclassDescription ? self.superclassDescription.name : @""]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingMessage.h b/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingMessage.h new file mode 100644 index 0000000..3c1f293 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingMessage.h @@ -0,0 +1,44 @@ +// +// MPDesignerEventBindingMessage.h +// HelloMixpanel +// +// Created by Amanda Canyon on 11/18/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPAbstractABTestDesignerMessage.h" + +extern NSString *const MPDesignerEventBindingRequestMessageType; + +@interface MPDesignerEventBindingRequestMessage : MPAbstractABTestDesignerMessage + +@end + +__deprecated +@interface MPDesignerEventBindingRequestMesssage : MPDesignerEventBindingRequestMessage + +@end + + +@interface MPDesignerEventBindingResponseMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; + +@property (nonatomic, copy) NSString *status; + +@end + +__deprecated +@interface MPDesignerEventBindingResponseMesssage : MPDesignerEventBindingResponseMessage + +@end + + +@interface MPDesignerTrackMessage : MPAbstractABTestDesignerMessage + ++ (instancetype)message; ++ (instancetype)messageWithPayload:(NSDictionary *)payload; + +@end + + diff --git a/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingRequestMesssage.m b/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingRequestMesssage.m new file mode 100644 index 0000000..f4c1631 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingRequestMesssage.m @@ -0,0 +1,88 @@ +// +// MPDesignerEventBindingRequestMesssage.m +// HelloMixpanel +// +// Created by Amanda Canyon on 7/15/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "Mixpanel.h" +#import "MPABTestDesignerConnection.h" +#import "MPDesignerEventBindingMessage.h" +#import "MPDesignerSessionCollection.h" +#import "MPEventBinding.h" +#import "MPObjectSelector.h" +#import "MPSwizzler.h" + +NSString *const MPDesignerEventBindingRequestMessageType = @"event_binding_request"; + +@interface MPEventBindingCollection : NSObject + +@property (nonatomic) NSMutableArray *bindings; + +@end + +@implementation MPEventBindingCollection + +- (void)updateBindings:(NSArray *)bindingPayload +{ + NSMutableArray *newBindings = [NSMutableArray array]; + for (NSDictionary *bindingInfo in bindingPayload) { + MPEventBinding *binding = [MPEventBinding bindingWithJSONObject:bindingInfo]; + if (binding) { + [newBindings addObject:binding]; + } + } + + for (MPEventBinding *oldBinding in self.bindings) { + [oldBinding stop]; + } + self.bindings = newBindings; + for (MPEventBinding *newBinding in self.bindings) { + [newBinding execute]; + } +} + +- (void)cleanup +{ + for (MPEventBinding *oldBinding in self.bindings) { + [oldBinding stop]; + } + self.bindings = nil; +} + +@end + +@implementation MPDesignerEventBindingRequestMessage + ++ (instancetype)message +{ + return [(MPDesignerEventBindingRequestMessage *)[self alloc] initWithType:@"event_binding_request"]; +} + +- (NSOperation *)responseCommandWithConnection:(MPABTestDesignerConnection *)connection +{ + __weak MPABTestDesignerConnection *weak_connection = connection; + NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ + MPABTestDesignerConnection *conn = weak_connection; + + dispatch_sync(dispatch_get_main_queue(), ^{ + NSArray *payload = [self payload][@"events"]; + NSLog(@"Loading event bindings:\n%@", payload); + MPEventBindingCollection *bindingCollection = [conn sessionObjectForKey:@"event_bindings"]; + if (!bindingCollection) { + bindingCollection = [[MPEventBindingCollection alloc] init]; + [conn setSessionObject:bindingCollection forKey:@"event_bindings"]; + } + [bindingCollection updateBindings:payload]; + }); + + MPDesignerEventBindingResponseMessage *changeResponseMessage = [MPDesignerEventBindingResponseMessage message]; + changeResponseMessage.status = @"OK"; + [conn sendMessage:changeResponseMessage]; + }]; + + return operation; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingResponseMesssage.m b/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingResponseMesssage.m new file mode 100644 index 0000000..0087b01 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPDesignerEventBindingResponseMesssage.m @@ -0,0 +1,28 @@ +// +// MPDesignerEventBindingResponseMesssage.m +// HelloMixpanel +// +// Created by Amanda Canyon on 7/15/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPDesignerEventBindingMessage.h" + +@implementation MPDesignerEventBindingResponseMessage + ++ (instancetype)message +{ + return [(MPDesignerEventBindingResponseMessage *)[self alloc] initWithType:@"event_binding_response"]; +} + +- (void)setStatus:(NSString *)status +{ + [self setPayloadObject:status forKey:@"status"]; +} + +- (NSString *)status +{ + return [self payloadObjectForKey:@"status"]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPDesignerSessionCollection.h b/ios/Pods/Mixpanel/Mixpanel/MPDesignerSessionCollection.h new file mode 100644 index 0000000..590e47f --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPDesignerSessionCollection.h @@ -0,0 +1,15 @@ +// +// MPDesignerSessionCollection.h +// HelloMixpanel +// +// Created by Amanda Canyon on 8/22/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import + +@protocol MPDesignerSessionCollection + +- (void)cleanup; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPDesignerTrackMessage.m b/ios/Pods/Mixpanel/Mixpanel/MPDesignerTrackMessage.m new file mode 100644 index 0000000..b698f22 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPDesignerTrackMessage.m @@ -0,0 +1,53 @@ +// +// MPDesignerTrackMessage.m +// HelloMixpanel +// +// Created by Amanda Canyon on 9/3/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPDesignerEventBindingMessage.h" + +@implementation MPDesignerTrackMessage + +{ + NSDictionary *_payload; +} + ++ (instancetype)message +{ + return [(MPDesignerTrackMessage *)[self alloc] initWithType:@"track_message"]; +} + ++ (instancetype)messageWithPayload:(NSDictionary *)payload +{ + return [(MPDesignerTrackMessage *)[self alloc] initWithType:@"track_message" andPayload:payload]; +} + +- (instancetype)initWithType:(NSString *)type +{ + return [self initWithType:type andPayload:@{}]; +} + +- (instancetype)initWithType:(NSString *)type andPayload:(NSDictionary *)payload +{ + if (self = [super initWithType:type]) { + _payload = payload; + } + return self; +} + +- (NSData *)JSONData +{ + NSDictionary *jsonObject = @{ @"type": self.type, @"payload": [_payload copy] }; + + NSError *error = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject options:(NSJSONWritingOptions)0 error:&error]; + if (error) { + NSLog(@"Failed to serialize test designer message: %@", error); + } + + return jsonData; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPEnumDescription.h b/ios/Pods/Mixpanel/Mixpanel/MPEnumDescription.h new file mode 100644 index 0000000..283be76 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPEnumDescription.h @@ -0,0 +1,14 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPTypeDescription.h" + +@interface MPEnumDescription : MPTypeDescription + +@property (nonatomic, assign, getter=isFlagsSet, readonly) BOOL flagSet; +@property (nonatomic, copy, readonly) NSString *baseType; + +- (NSArray *)allValues; // array of NSNumber instances + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPEnumDescription.m b/ios/Pods/Mixpanel/Mixpanel/MPEnumDescription.m new file mode 100644 index 0000000..cd1bde0 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPEnumDescription.m @@ -0,0 +1,37 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPEnumDescription.h" + +@implementation MPEnumDescription + +{ + NSMutableDictionary *_values; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + NSParameterAssert(dictionary[@"flag_set"] != nil); + NSParameterAssert(dictionary[@"base_type"] != nil); + NSParameterAssert(dictionary[@"values"] != nil); + + self = [super initWithDictionary:dictionary]; + if (self) { + _flagSet = [dictionary[@"flag_set"] boolValue]; + _baseType = [dictionary[@"base_type"] copy]; + _values = [NSMutableDictionary dictionary]; + + for (NSDictionary *value in dictionary[@"values"]) { + _values[value[@"value"]] = value[@"display_name"]; + } + } + + return self; +} + +- (NSArray *)allValues +{ + return _values.allKeys; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPEventBinding.h b/ios/Pods/Mixpanel/Mixpanel/MPEventBinding.h new file mode 100644 index 0000000..bc34ba2 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPEventBinding.h @@ -0,0 +1,50 @@ +// +// MPEventBinding.h +// HelloMixpanel +// +// Created by Amanda Canyon on 7/22/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import +#import "MPObjectSelector.h" + +@interface MPEventBinding : NSObject + +@property (nonatomic) NSUInteger ID; +@property (nonatomic, copy) NSString *name; +@property (nonatomic, strong) MPObjectSelector *path; +@property (nonatomic, copy) NSString *eventName; + +@property (nonatomic, assign) Class swizzleClass; + +/*! + @property + + @abstract + Whether this specific binding is currently running on the device. + + @discussion + This property will not be restored on unarchive, as the binding will need + to be run again once the app is restarted. + */ +@property (nonatomic) BOOL running; + ++ (id)bindingWithJSONObject:(id)object; + +- (instancetype)init __unavailable; +- (instancetype)initWithEventName:(NSString *)eventName onPath:(NSString *)path; + +/*! + Intercepts track calls and adds a property indicating the track event + was from a binding + */ ++ (void)track:(NSString *)event properties:(NSDictionary *)properties; +/*! + Method stubs. Implement them in subclasses + */ ++ (NSString *)typeName; +- (void)execute; +- (void)stop; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPEventBinding.m b/ios/Pods/Mixpanel/Mixpanel/MPEventBinding.m new file mode 100644 index 0000000..2554548 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPEventBinding.m @@ -0,0 +1,125 @@ +// +// MPEventBinding.m +// HelloMixpanel +// +// Created by Amanda Canyon on 7/22/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "Mixpanel.h" +#import "MPEventBinding.h" +#import "MPUIControlBinding.h" +#import "MPUITableViewBinding.h" + +@implementation MPEventBinding + ++ (MPEventBinding *)bindingWithJSONObject:(NSDictionary *)object +{ + if (object == nil) { + NSLog(@"must supply an JSON object to initialize from"); + return nil; + } + + NSString *bindingType = object[@"event_type"]; + Class klass = [self subclassFromString:bindingType]; + return [klass bindingWithJSONObject:object]; +} + ++ (MPEventBinding *)bindngWithJSONObject:(NSDictionary *)object +{ + return [self bindingWithJSONObject:object]; +} + ++ (Class)subclassFromString:(NSString *)bindingType +{ + NSDictionary *classTypeMap = @{ + [MPUIControlBinding typeName]: [MPUIControlBinding class], + [MPUITableViewBinding typeName]: [MPUITableViewBinding class] + }; + return[classTypeMap valueForKey:bindingType] ?: [MPUIControlBinding class]; +} + ++ (void)track:(NSString *)event properties:(NSDictionary *)properties +{ + NSMutableDictionary *bindingProperties = [NSMutableDictionary dictionaryWithObjectsAndKeys: @YES, @"$from_binding", nil]; + [bindingProperties addEntriesFromDictionary:properties]; + [[Mixpanel sharedInstance] track:event properties:bindingProperties]; +} + +- (instancetype)initWithEventName:(NSString *)eventName onPath:(NSString *)path +{ + if (self = [super init]) { + self.eventName = eventName; + self.path = [[MPObjectSelector alloc] initWithString:path]; + self.name = [[NSUUID UUID] UUIDString]; + self.running = NO; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"Event Binding base class: '%@' for '%@'", [self eventName], [self path]]; +} + +#pragma mark -- Method stubs + ++ (NSString *)typeName +{ + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (void)execute +{ + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +- (void)stop +{ + @throw [NSException exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +#pragma mark -- NSCoder + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + NSString *path = [aDecoder decodeObjectForKey:@"path"]; + NSString *eventName = [aDecoder decodeObjectForKey:@"eventName"]; + if (self = [self initWithEventName:eventName onPath:path]) { + self.ID = [[aDecoder decodeObjectForKey:@"ID"] unsignedLongValue]; + self.name = [aDecoder decodeObjectForKey:@"name"]; + self.swizzleClass = NSClassFromString([aDecoder decodeObjectForKey:@"swizzleClass"]); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:@(_ID) forKey:@"ID"]; + [aCoder encodeObject:_name forKey:@"name"]; + [aCoder encodeObject:_path.string forKey:@"path"]; + [aCoder encodeObject:_eventName forKey:@"eventName"]; + [aCoder encodeObject:NSStringFromClass(_swizzleClass) forKey:@"swizzleClass"]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[MPEventBinding class]]) { + return NO; + } else { + return [self.eventName isEqual:((MPEventBinding *)other).eventName] && [self.path isEqual:((MPEventBinding *)other).path]; + } +} + +- (NSUInteger)hash { + return [self.eventName hash] ^ [self.path hash]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPFoundation.h b/ios/Pods/Mixpanel/Mixpanel/MPFoundation.h new file mode 100644 index 0000000..4ff5778 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPFoundation.h @@ -0,0 +1,10 @@ +#import + +#if TARGET_OS_IPHONE +#ifndef NSFoundationVersionNumber_iOS_9_0 +// support for Xcode 7.* +#define NSFoundationVersionNumber_iOS_8_x_Max 1199 +#define NSFoundationVersionNumber_iOS_9_0 1240.1 +#define NSFoundationVersionNumber_iOS_9_x_Max 1299 +#endif +#endif diff --git a/ios/Pods/Mixpanel/Mixpanel/MPLogger.h b/ios/Pods/Mixpanel/Mixpanel/MPLogger.h new file mode 100644 index 0000000..718210c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPLogger.h @@ -0,0 +1,37 @@ +// +// MPLogger.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 7/11/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import +#import + +static bool gLoggingEnabled = NO; + +#define __MP_MAKE_LOG_FUNCTION(LEVEL, NAME) \ +static inline void NAME(NSString *format, ...) { \ + if (!gLoggingEnabled) return; \ + va_list arg_list; \ + va_start(arg_list, format); \ + NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list]; \ + asl_add_log_file(NULL, STDERR_FILENO); \ + asl_log(NULL, NULL, (LEVEL), "%s", [formattedString UTF8String]); \ + va_end(arg_list); \ +} + +// Something has failed. +__MP_MAKE_LOG_FUNCTION(ASL_LEVEL_ERR, MPLogError) + +// Something is amiss and might fail if not corrected. +__MP_MAKE_LOG_FUNCTION(ASL_LEVEL_WARNING, MPLogWarning) + +// The lowest priority that you would normally log, and purely informational in nature. +__MP_MAKE_LOG_FUNCTION(ASL_LEVEL_INFO, MPLogInfo) + +// The lowest priority, and normally not logged except for code based messages. +__MP_MAKE_LOG_FUNCTION(ASL_LEVEL_DEBUG, MPLogDebug) + +#undef __MP_MAKE_LOG_FUNCTION diff --git a/ios/Pods/Mixpanel/Mixpanel/MPMiniNotification.h b/ios/Pods/Mixpanel/Mixpanel/MPMiniNotification.h new file mode 100644 index 0000000..76b039a --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPMiniNotification.h @@ -0,0 +1,17 @@ +// +// MPMiniNotification.h +// Mixpanel +// +// Created by Sergio Alonso on 1/24/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import "MPNotification.h" + +@interface MPMiniNotification : MPNotification + +@property (nonatomic, copy) NSURL *ctaUrl; +@property (nonatomic) NSUInteger imageTintColor; +@property (nonatomic) NSUInteger borderColor; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPMiniNotification.m b/ios/Pods/Mixpanel/Mixpanel/MPMiniNotification.m new file mode 100644 index 0000000..76c7434 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPMiniNotification.m @@ -0,0 +1,59 @@ +// +// MPMiniNotification.m +// Mixpanel +// +// Created by Sergio Alonso on 1/24/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import "MPMiniNotification.h" + +@implementation MPMiniNotification + +- (instancetype)initWithJSONObject:(NSDictionary *)jsonObject { + if (self = [super initWithJSONObject:jsonObject]) { + NSURL *callToActionURL = nil; + NSObject *URLString = jsonObject[@"cta_url"]; + if (URLString != nil && ![URLString isKindOfClass:[NSNull class]]) { + if (![URLString isKindOfClass:[NSString class]] || [(NSString *)URLString length] == 0) { + [MPNotification logNotificationError:@"cta url" withValue:URLString]; + return nil; + } + + callToActionURL = [NSURL URLWithString:(NSString *)URLString]; + if (callToActionURL == nil) { + [MPNotification logNotificationError:@"cta url" withValue:URLString]; + return nil; + } + } + + NSNumber *imageTintColor = jsonObject[@"image_tint_color"]; + if (![imageTintColor isKindOfClass:[NSNumber class]]) { + [MPNotification logNotificationError:@"image tint color" withValue:imageTintColor]; + return nil; + } + + NSNumber *borderColor = jsonObject[@"border_color"]; + if (![borderColor isKindOfClass:[NSNumber class]]) { + [MPNotification logNotificationError:@"border color" withValue:borderColor]; + return nil; + } + + if (!self.body) { + [MPNotification logNotificationError:@"body" withValue:self.body]; + return nil; + } + + self.ctaUrl = callToActionURL; + self.imageTintColor = imageTintColor.unsignedIntegerValue; + self.borderColor = borderColor.unsignedIntegerValue; + } + + return self; +} + +- (NSString *)type { + return MPNotificationTypeMini; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNSAttributedStringToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPNSAttributedStringToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..9aa752a --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNSAttributedStringToNSDictionaryValueTransformer.m @@ -0,0 +1,80 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPLogger.h" +#import "MPValueTransformers.h" + +@implementation MPNSAttributedStringToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value isKindOfClass:[NSAttributedString class]]) { + NSMutableAttributedString *attributedString = [value mutableCopy]; + [attributedString beginEditing]; + __block BOOL safe = NO; + [attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id valueObject, NSRange range, BOOL *stop) { + if (valueObject) { + NSParagraphStyle *paragraphStyle = valueObject; + if([paragraphStyle respondsToSelector:@selector(headIndent)]) { + safe = YES; + } + } + }]; + if (!safe) { + [attributedString removeAttribute:NSParagraphStyleAttributeName range:NSMakeRange(0, attributedString.length)]; + } + [attributedString endEditing]; + + NSError *error = nil; + NSData *data = [attributedString dataFromRange:NSMakeRange(0, attributedString.length) + documentAttributes:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} + error:&error]; + if (data) { + return @{ + @"mime_type": @"text/html", + @"data": [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] + }; + } else { + MPLogError(@"Failed to convert NSAttributedString to HTML: %@", error); + } + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionaryValue = value; + NSString *mimeType = dictionaryValue[@"mime_type"]; + NSString *dataString = dictionaryValue[@"data"]; + + if ([mimeType isEqualToString:@"text/html"] && dataString) { + NSError *error = nil; + NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding]; + NSAttributedString *attributedString = [[NSAttributedString alloc] initWithData:data + options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} + documentAttributes:NULL + error:&error]; + if (attributedString == nil) { + MPLogError(@"Failed to convert HTML to NSAttributed string: %@", error); + } + + return attributedString; + } + } + + return nil; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNSNumberToCGFloatValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPNSNumberToCGFloatValueTransformer.m new file mode 100644 index 0000000..ca4f8b9 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNSNumberToCGFloatValueTransformer.m @@ -0,0 +1,38 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPNSNumberToCGFloatValueTransformer + ++ (Class)transformedValueClass +{ + return [NSNumber class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return NO; +} + +- (id)transformedValue:(id)value +{ + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *number = (NSNumber *) value; + + // if the number is not a cgfloat, cast it to a cgfloat + if (strcmp(number.objCType, @encode(CGFloat)) != 0) { + if (strcmp(@encode(CGFloat), @encode(double)) == 0) { + value = @(number.doubleValue); + } else { + value = @(number.floatValue); + } + } + + return value; + } + + return nil; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNetwork.h b/ios/Pods/Mixpanel/Mixpanel/MPNetwork.h new file mode 100644 index 0000000..0e6193b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNetwork.h @@ -0,0 +1,37 @@ +// +// MPNetwork.h +// Mixpanel +// +// Created by Sam Green on 6/12/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import + +@class Mixpanel; + +typedef NS_ENUM(NSUInteger, MPNetworkEndpoint) { + MPNetworkEndpointTrack, + MPNetworkEndpointEngage, + MPNetworkEndpointDecide +}; + +@interface MPNetwork : NSObject + +@property (nonatomic) BOOL shouldManageNetworkActivityIndicator; +@property (nonatomic) BOOL useIPAddressForGeoLocation; + +- (instancetype)initWithServerURL:(NSURL *)serverURL mixpanel:(Mixpanel *)mixpanel; + +- (void)flushEventQueue:(NSMutableArray *)events; +- (void)flushPeopleQueue:(NSMutableArray *)people; + +- (void)updateNetworkActivityIndicator:(BOOL)enabled; + +- (NSURLRequest *)buildGetRequestForEndpoint:(MPNetworkEndpoint)endpoint + withQueryItems:(NSArray *)queryItems; + +- (NSURLRequest *)buildPostRequestForEndpoint:(MPNetworkEndpoint)endpoint + andBody:(NSString *)body; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNetwork.m b/ios/Pods/Mixpanel/Mixpanel/MPNetwork.m new file mode 100644 index 0000000..0a7f5f1 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNetwork.m @@ -0,0 +1,345 @@ +// +// MPNetwork.m +// Mixpanel +// +// Created by Sam Green on 6/12/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "MPNetwork.h" +#import "MPNetworkPrivate.h" +#import "MPLogger.h" +#import "Mixpanel.h" +#import "MixpanelPrivate.h" +#if !TARGET_OS_OSX +#import +#endif + +#define MIXPANEL_NO_NETWORK_ACTIVITY_INDICATOR (defined(MIXPANEL_APP_EXTENSION) || defined(MIXPANEL_TVOS) || defined(MIXPANEL_WATCHOS) || defined(MIXPANEL_MACOS)) + +static const NSUInteger kBatchSize = 50; + +@implementation MPNetwork + ++ (NSURLSession *)sharedURLSession { + static NSURLSession *sharedSession = nil; + @synchronized(self) { + if (sharedSession == nil) { + NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; + sessionConfig.timeoutIntervalForRequest = 7.0; + sharedSession = [NSURLSession sessionWithConfiguration:sessionConfig]; + } + } + return sharedSession; +} + +- (instancetype)initWithServerURL:(NSURL *)serverURL mixpanel:(Mixpanel *)mixpanel { + self = [super init]; + if (self) { + self.serverURL = serverURL; + self.shouldManageNetworkActivityIndicator = YES; + self.useIPAddressForGeoLocation = YES; + self.mixpanel = mixpanel; + } + return self; +} + +#pragma mark - Flush +- (void)flushEventQueue:(NSMutableArray *)events { + NSMutableArray *automaticEventsQueue; + @synchronized (self.mixpanel) { + automaticEventsQueue = [self orderAutomaticEvents:events]; + } + [self flushQueue:events endpoint:MPNetworkEndpointTrack]; + @synchronized (self.mixpanel) { + if (automaticEventsQueue) { + [events addObjectsFromArray:automaticEventsQueue]; + } + } +} + +- (NSMutableArray *)orderAutomaticEvents:(NSMutableArray *)events { + if (!self.mixpanel.automaticEventsEnabled || !self.mixpanel.automaticEventsEnabled.boolValue) { + NSMutableArray *discardedItems = [NSMutableArray array]; + for (NSDictionary *e in events) { + if ([e[@"event"] hasPrefix:@"$ae_"]) { + [discardedItems addObject:e]; + } + } + [events removeObjectsInArray:discardedItems]; + if (!self.mixpanel.automaticEventsEnabled) { + return discardedItems; + } + } + return nil; +} + +- (void)flushPeopleQueue:(NSMutableArray *)people { + [self flushQueue:people endpoint:MPNetworkEndpointEngage]; +} + +- (void)flushQueue:(NSMutableArray *)queue endpoint:(MPNetworkEndpoint)endpoint { + if ([[NSDate date] timeIntervalSince1970] < self.requestsDisabledUntilTime) { + MPLogDebug(@"Attempted to flush to %lu, when we still have a timeout. Ignoring flush.", endpoint); + return; + } + + NSMutableArray *queueCopyForFlushing; + + Mixpanel *mixpanel = self.mixpanel; + @synchronized (mixpanel) { + queueCopyForFlushing = [queue mutableCopy]; + } + + while (queueCopyForFlushing.count > 0) { + NSUInteger batchSize = MIN(queueCopyForFlushing.count, kBatchSize); + NSArray *batch = [queueCopyForFlushing subarrayWithRange:NSMakeRange(0, batchSize)]; + + NSString *requestData = [MPNetwork encodeArrayForAPI:batch]; + NSString *postBody = [NSString stringWithFormat:@"ip=%d&data=%@", self.useIPAddressForGeoLocation, requestData]; + MPLogDebug(@"%@ flushing %lu of %lu to %lu: %@", self, (unsigned long)batch.count, (unsigned long)queue.count, endpoint, queueCopyForFlushing); + NSURLRequest *request = [self buildPostRequestForEndpoint:endpoint andBody:postBody]; + + [self updateNetworkActivityIndicator:YES]; + + __block BOOL didFail = NO; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [[[MPNetwork sharedURLSession] dataTaskWithRequest:request completionHandler:^(NSData *responseData, + NSURLResponse *urlResponse, + NSError *error) { + [self updateNetworkActivityIndicator:NO]; + + BOOL success = [self handleNetworkResponse:(NSHTTPURLResponse *)urlResponse withError:error]; + if (error || !success) { + MPLogError(@"%@ network failure: %@", self, error); + didFail = YES; + } else { + NSString *response = [[NSString alloc] initWithData:responseData + encoding:NSUTF8StringEncoding]; + if ([response intValue] == 0) { + MPLogInfo(@"%@ %lu api rejected some items", self, endpoint); + } + } + + dispatch_semaphore_signal(semaphore); + }] resume]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + if (didFail) { + break; + } + + @synchronized (mixpanel) { + [queueCopyForFlushing removeObjectsInArray:batch]; + [queue removeObjectsInArray:batch]; + } + } +} + +- (BOOL)handleNetworkResponse:(NSHTTPURLResponse *)response withError:(NSError *)error { + MPLogDebug(@"HTTP Response: %@", response.allHeaderFields); + MPLogDebug(@"HTTP Error: %@", error.localizedDescription); + + BOOL failed = [MPNetwork parseHTTPFailure:response withError:error]; + if (failed) { + MPLogDebug(@"Consecutive network failures: %lu", self.consecutiveFailures); + self.consecutiveFailures++; + } else { + MPLogDebug(@"Consecutive network failures reset to 0"); + self.consecutiveFailures = 0; + } + + // Did the server response with an HTTP `Retry-After` header? + NSTimeInterval retryTime = [MPNetwork parseRetryAfterTime:response]; + if (self.consecutiveFailures >= 2) { + + // Take the larger of exponential back off and server provided `Retry-After` + retryTime = MAX(retryTime, [MPNetwork calculateBackOffTimeFromFailures:self.consecutiveFailures]); + } + + NSDate *retryDate = [NSDate dateWithTimeIntervalSinceNow:retryTime]; + self.requestsDisabledUntilTime = [retryDate timeIntervalSince1970]; + + MPLogDebug(@"Retry backoff time: %.2f - %@", retryTime, retryDate); + + return !failed; +} + +#pragma mark - Helpers ++ (NSArray *)buildDecideQueryForProperties:(NSDictionary *)properties + withDistinctID:(NSString *)distinctID + andToken:(NSString *)token { + NSURLQueryItem *itemVersion = [NSURLQueryItem queryItemWithName:@"version" value:@"1"]; + NSURLQueryItem *itemLib = [NSURLQueryItem queryItemWithName:@"lib" value:@"iphone"]; + NSURLQueryItem *itemToken = [NSURLQueryItem queryItemWithName:@"token" value:token]; + NSURLQueryItem *itemDistinctID = [NSURLQueryItem queryItemWithName:@"distinct_id" value:distinctID]; + + // Convert properties dictionary to a string + NSData *propertiesData = [NSJSONSerialization dataWithJSONObject:properties + options:0 + error:NULL]; + NSString *propertiesString = [[NSString alloc] initWithData:propertiesData + encoding:NSUTF8StringEncoding]; + NSURLQueryItem *itemProperties = [NSURLQueryItem queryItemWithName:@"properties" value:propertiesString]; + + return @[ itemVersion, itemLib, itemToken, itemDistinctID, itemProperties ]; +} + ++ (NSString *)pathForEndpoint:(MPNetworkEndpoint)endpoint { + static NSDictionary *endPointToPath = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + endPointToPath = @{ @(MPNetworkEndpointTrack): @"/track/", + @(MPNetworkEndpointEngage): @"/engage/", + @(MPNetworkEndpointDecide): @"/decide" }; + }); + NSNumber *key = @(endpoint); + return endPointToPath[key]; +} + +- (NSURLRequest *)buildGetRequestForEndpoint:(MPNetworkEndpoint)endpoint + withQueryItems:(NSArray *)queryItems { + return [self buildRequestForEndpoint:[MPNetwork pathForEndpoint:endpoint] + byHTTPMethod:@"GET" + withQueryItems:queryItems + andBody:nil]; +} + +- (NSURLRequest *)buildPostRequestForEndpoint:(MPNetworkEndpoint)endpoint + andBody:(NSString *)body { + return [self buildRequestForEndpoint:[MPNetwork pathForEndpoint:endpoint] + byHTTPMethod:@"POST" + withQueryItems:nil + andBody:body]; +} + +- (NSURLRequest *)buildRequestForEndpoint:(NSString *)endpoint + byHTTPMethod:(NSString *)method + withQueryItems:(NSArray *)queryItems + andBody:(NSString *)body { + // Build URL from path and query items + NSURL *urlWithEndpoint = [self.serverURL URLByAppendingPathComponent:endpoint]; + NSURLComponents *components = [NSURLComponents componentsWithURL:urlWithEndpoint + resolvingAgainstBaseURL:YES]; + if (queryItems) { + components.queryItems = queryItems; + } + + // NSURLComponents/NSURLQueryItem doesn't encode + as %2B, and then the + is interpreted as a space on servers + components.percentEncodedQuery = [components.percentEncodedQuery stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"]; + + // Build request from URL + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:components.URL]; + [request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"]; + [request setHTTPMethod:method]; + [request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]]; + + MPLogDebug(@"%@ http request: %@?%@", self, request, body); + + return [request copy]; +} + ++ (NSString *)encodeArrayForAPI:(NSArray *)array { + NSData *data = [MPNetwork encodeArrayAsJSONData:array]; + return [MPNetwork encodeJSONDataAsBase64:data]; +} + ++ (NSData *)encodeArrayAsJSONData:(NSArray *)array { + NSError *error = NULL; + NSData *data = nil; + @try { + data = [NSJSONSerialization dataWithJSONObject:[self convertFoundationTypesToJSON:array] + options:(NSJSONWritingOptions)0 + error:&error]; + } + @catch (NSException *exception) { + MPLogError(@"exception encoding api data: %@", exception); + } + + if (error) { + MPLogError(@"error encoding api data: %@", error); + } + + return data; +} + ++ (NSString *)encodeJSONDataAsBase64:(NSData *)data { + return [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; +} + ++ (id)convertFoundationTypesToJSON:(id)obj { + // valid json types + if ([obj isKindOfClass:NSString.class] || [obj isKindOfClass:NSNumber.class] || [obj isKindOfClass:NSNull.class]) { + return obj; + } + + if ([obj isKindOfClass:NSDate.class]) { + return [[self dateFormatter] stringFromDate:obj]; + } else if ([obj isKindOfClass:NSURL.class]) { + return [obj absoluteString]; + } + + // recurse on containers + if ([obj isKindOfClass:NSArray.class]) { + NSMutableArray *a = [NSMutableArray array]; + for (id i in obj) { + [a addObject:[self convertFoundationTypesToJSON:i]]; + } + return [NSArray arrayWithArray:a]; + } + + if ([obj isKindOfClass:NSDictionary.class]) { + NSMutableDictionary *d = [NSMutableDictionary dictionary]; + for (id key in obj) { + NSString *stringKey = key; + if (![key isKindOfClass:[NSString class]]) { + stringKey = [key description]; + MPLogWarning(@"%@ property keys should be strings. got: %@. coercing to: %@", self, [key class], stringKey); + } + id v = [self convertFoundationTypesToJSON:obj[key]]; + d[stringKey] = v; + } + return [NSDictionary dictionaryWithDictionary:d]; + } + + // default to sending the object's description + NSString *s = [obj description]; + MPLogWarning(@"%@ property values should be valid json types. got: %@. coercing to: %@", self, [obj class], s); + return s; +} + ++ (NSDateFormatter *)dateFormatter { + static NSDateFormatter *formatter = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [[NSDateFormatter alloc] init]; + formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; + formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + }); + return formatter; +} + ++ (NSTimeInterval)calculateBackOffTimeFromFailures:(NSUInteger)failureCount { + NSTimeInterval time = pow(2.0, failureCount - 1) * 60 + arc4random_uniform(30); + return MIN(MAX(60, time), 600); +} + ++ (NSTimeInterval)parseRetryAfterTime:(NSHTTPURLResponse *)response { + return [response.allHeaderFields[@"Retry-After"] doubleValue]; +} + ++ (BOOL)parseHTTPFailure:(NSHTTPURLResponse *)response withError:(NSError *)error { + return (error != nil || (500 <= response.statusCode && response.statusCode <= 599)); +} + +- (void)updateNetworkActivityIndicator:(BOOL)enabled { +#if !MIXPANEL_NO_NETWORK_ACTIVITY_INDICATOR + if (self.shouldManageNetworkActivityIndicator) { + [UIApplication sharedApplication].networkActivityIndicatorVisible = enabled; + } +#endif +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNetworkPrivate.h b/ios/Pods/Mixpanel/Mixpanel/MPNetworkPrivate.h new file mode 100644 index 0000000..1679e94 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNetworkPrivate.h @@ -0,0 +1,40 @@ +// +// MPNetworkPrivate.h +// Mixpanel +// +// Created by Sam Green on 6/17/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "MPNetwork.h" + +@interface MPNetwork () + +@property (nonatomic, weak) Mixpanel *mixpanel; +@property (nonatomic, strong) NSURL *serverURL; + +@property (nonatomic) NSTimeInterval requestsDisabledUntilTime; +@property (nonatomic) NSUInteger consecutiveFailures; + +- (BOOL)handleNetworkResponse:(NSHTTPURLResponse *)response withError:(NSError *)error; + ++ (NSTimeInterval)calculateBackOffTimeFromFailures:(NSUInteger)failureCount; ++ (NSTimeInterval)parseRetryAfterTime:(NSHTTPURLResponse *)response; ++ (BOOL)parseHTTPFailure:(NSHTTPURLResponse *)response withError:(NSError *)error; + ++ (NSString *)encodeArrayForAPI:(NSArray *)array; ++ (NSData *)encodeArrayAsJSONData:(NSArray *)array; ++ (NSString *)encodeJSONDataAsBase64:(NSData *)data; + ++ (NSArray *)buildDecideQueryForProperties:(NSDictionary *)properties + withDistinctID:(NSString *)distinctID + andToken:(NSString *)token; + +- (NSURLRequest *)buildRequestForEndpoint:(NSString *)endpoint + byHTTPMethod:(NSString *)method + withQueryItems:(NSArray *)queryItems + andBody:(NSString *)body; + ++ (NSURLSession *)sharedURLSession; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNotification.h b/ios/Pods/Mixpanel/Mixpanel/MPNotification.h new file mode 100644 index 0000000..8509df0 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNotification.h @@ -0,0 +1,23 @@ +#import + +extern NSString *const MPNotificationTypeMini; +extern NSString *const MPNotificationTypeTakeover; + +@interface MPNotification : NSObject + +@property (nonatomic, readonly) NSDictionary *jsonDescription; +@property (nonatomic, readonly) NSDictionary *extrasDescription; +@property (nonatomic, readonly) NSUInteger ID; +@property (nonatomic, readonly) NSUInteger messageID; +@property (nonatomic, readonly) NSString *type; +@property (nonatomic, copy) NSURL *imageURL; +@property (nonatomic, strong) NSData *image; +@property (nonatomic, readonly) NSString *body; +@property (nonatomic, readonly) NSUInteger bodyColor; +@property (nonatomic, readonly) NSUInteger backgroundColor; + +- (instancetype)init __unavailable; +- (instancetype)initWithJSONObject:(NSDictionary *)jsonObject; ++ (void)logNotificationError:(NSString *)field withValue:(id)value; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNotification.m b/ios/Pods/Mixpanel/Mixpanel/MPNotification.m new file mode 100644 index 0000000..7ffd481 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNotification.m @@ -0,0 +1,118 @@ +#import "MPLogger.h" +#import "MPNotification.h" + +NSString *const MPNotificationTypeMini = @"mini"; +NSString *const MPNotificationTypeTakeover = @"takeover"; + +@implementation MPNotification + +- (instancetype)initWithJSONObject:(NSDictionary *)object { + if (self = [super init]) { + if (object == nil) { + MPLogError(@"notif json object should not be nil"); + return nil; + } + + NSNumber *ID = object[@"id"]; + if (!([ID isKindOfClass:[NSNumber class]] && ID.integerValue > 0)) { + [MPNotification logNotificationError:@"id" withValue:ID]; + return nil; + } + + NSNumber *messageID = object[@"message_id"]; + if (!([messageID isKindOfClass:[NSNumber class]] && messageID.integerValue > 0)) { + [MPNotification logNotificationError:@"message" withValue:messageID]; + return nil; + } + + NSString *body = object[@"body"]; + if ([body isEqual:[NSNull null]]) { + body = nil; + } + + NSNumber *bodyColor = object[@"body_color"]; + if (!([bodyColor isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@"body color" withValue:bodyColor]; + return nil; + } + + NSNumber *backgroundColor = object[@"bg_color"]; + if (!([backgroundColor isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@"background color" withValue:bodyColor]; + return nil; + } + + NSURL *imageURL = nil; + NSString *imageURLString = object[@"image_url"]; + if (imageURLString != nil && ![imageURLString isKindOfClass:[NSNull class]]) { + if (![imageURLString isKindOfClass:[NSString class]]) { + [MPNotification logNotificationError:@"image url" withValue:imageURLString]; + return nil; + } + + NSString *escapedURLString = [imageURLString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; + imageURL = [NSURL URLWithString:escapedURLString]; + if (imageURL == nil) { + [MPNotification logNotificationError:@"image url" withValue:escapedURLString]; + return nil; + } + + NSString *imagePath = imageURL.path; + if ([self.type isEqualToString:MPNotificationTypeTakeover]) { + NSString *imageName = [imagePath stringByDeletingPathExtension]; + NSString *extension = [imagePath pathExtension]; + imagePath = [[imageName stringByAppendingString:@"@2x"] stringByAppendingPathExtension:extension]; + } + + NSURLComponents *imageURLComponents = [[NSURLComponents alloc] init]; + imageURLComponents.scheme = imageURL.scheme; + imageURLComponents.host = imageURL.host; + imageURLComponents.path = imagePath; + + if (imageURLComponents.URL == nil) { + [MPNotification logNotificationError:@"image url" withValue:imageURLString]; + return nil; + } + imageURL = imageURLComponents.URL; + } else { + [MPNotification logNotificationError:@"image url" withValue:imageURLString]; + return nil; + } + + _jsonDescription = object; + _extrasDescription = object[@"extras"]; + _ID = ID.unsignedIntegerValue; + _messageID = messageID.unsignedIntegerValue; + _body = body; + _bodyColor = bodyColor.unsignedIntegerValue; + _backgroundColor = backgroundColor.unsignedIntegerValue; + _imageURL = imageURL; + _image = nil; + } + + return self; +} + +- (NSString *)type { + NSAssert(false, @"Sub-classes must override this method"); + return nil; +} + +- (NSData *)image { + if (_image == nil && _imageURL != nil) { + NSError *error = nil; + NSData *imageData = [NSData dataWithContentsOfURL:_imageURL options:NSDataReadingMappedIfSafe error:&error]; + if (error || !imageData) { + MPLogError(@"image failed to load from URL: %@", _imageURL); + return nil; + } + _image = imageData; + } + return _image; +} + ++ (void)logNotificationError:(NSString *)field withValue:(id)value { + MPLogError(@"Invalid notification %@: %@", field, value); +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNotificationButton.h b/ios/Pods/Mixpanel/Mixpanel/MPNotificationButton.h new file mode 100644 index 0000000..c02188e --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNotificationButton.h @@ -0,0 +1,22 @@ +// +// MPNotificationButton.h +// Mixpanel +// +// Created by Sergio Alonso on 1/25/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import + +@interface MPNotificationButton : NSObject + +@property (nonatomic, copy) NSDictionary *jsonDescription; +@property (nonatomic, copy) NSString *text; +@property (nonatomic) NSUInteger textColor; +@property (nonatomic) NSUInteger backgroundColor; +@property (nonatomic) NSUInteger borderColor; +@property (nonatomic, copy) NSURL *ctaUrl; + +- (instancetype)initWithJSONObject:(NSDictionary *)jsonObject; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNotificationButton.m b/ios/Pods/Mixpanel/Mixpanel/MPNotificationButton.m new file mode 100644 index 0000000..7e5b03b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNotificationButton.m @@ -0,0 +1,69 @@ +// +// MPNotificationButton.m +// Mixpanel +// +// Created by Sergio Alonso on 1/25/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import "MPNotificationButton.h" +#import "MPNotification.h" + +@implementation MPNotificationButton + +- (instancetype)initWithJSONObject:(NSDictionary *)jsonObject { + if (self = [super init]) { + if (jsonObject == nil) { + [MPNotification logNotificationError:@"button JSON can not be nil" withValue:@""]; + return nil; + } + NSString *text = jsonObject[@"text"]; + if (![text isKindOfClass:[NSString class]]) { + [MPNotification logNotificationError:@"button text" withValue:text]; + return nil; + } + + NSNumber *textColor = jsonObject[@"text_color"]; + if (!([textColor isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@"button text color" withValue:textColor]; + return nil; + } + + NSNumber *backgroundColor = jsonObject[@"bg_color"]; + if (!([backgroundColor isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@" button background color" withValue:backgroundColor]; + return nil; + } + + NSNumber *borderColor = jsonObject[@"border_color"]; + if (!([borderColor isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@"button border color" withValue:borderColor]; + return nil; + } + + NSURL *callToActionURL = nil; + NSObject *URLString = jsonObject[@"cta_url"]; + if (URLString != nil && ![URLString isKindOfClass:[NSNull class]]) { + if (![URLString isKindOfClass:[NSString class]] || [(NSString *)URLString length] == 0) { + [MPNotification logNotificationError:@"button cta url" withValue:URLString]; + return nil; + } + callToActionURL = [NSURL URLWithString:(NSString *)URLString]; + if (callToActionURL == nil) { + [MPNotification logNotificationError:@"button cta url" withValue:URLString]; + return nil; + } + } + + self.jsonDescription = jsonObject; + self.text = text; + self.textColor = textColor.unsignedIntegerValue; + self.backgroundColor = backgroundColor.unsignedIntegerValue; + self.borderColor = borderColor.unsignedIntegerValue; + self.ctaUrl = callToActionURL; + } + + return self; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNotificationViewController.h b/ios/Pods/Mixpanel/Mixpanel/MPNotificationViewController.h new file mode 100644 index 0000000..2e19d5c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNotificationViewController.h @@ -0,0 +1,28 @@ +#import "MPTakeoverNotification.h" +#import "MPMiniNotification.h" + +@protocol MPNotificationViewControllerDelegate; + +@interface MPNotificationViewController : UIViewController + +@property (nonatomic, weak) id delegate; +@property (nonatomic, strong) MPNotification *notification; + +- (void)show; +- (void)hide:(BOOL)animated completion:(void (^)(void))completion; + +@end + +@interface MPTakeoverNotificationViewController : MPNotificationViewController + +@end + +@interface MPMiniNotificationViewController : MPNotificationViewController + +@end + +@protocol MPNotificationViewControllerDelegate + +- (void)notificationController:(MPNotificationViewController *)controller wasDismissedWithCtaUrl:(NSURL *)ctaUrl; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPNotificationViewController.m b/ios/Pods/Mixpanel/Mixpanel/MPNotificationViewController.m new file mode 100644 index 0000000..1c95df1 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPNotificationViewController.m @@ -0,0 +1,587 @@ +#import +#import +#import "UIView+MPHelpers.h" +#import "MPLogger.h" +#import "MPNotification.h" +#import "MPNotificationViewController.h" +#import "UIColor+MPColor.h" +#import "UIImage+MPAverageColor.h" +#import "UIImage+MPImageEffects.h" +#import "MPFoundation.h" +#import "UIColor+MPColor.h" +#import "MPResources.h" + +#define MPNotifHeight 65.0f + + +@interface CircleLayer : CALayer {} + +@property (nonatomic, assign) CGFloat circlePadding; + +@end + +@interface ElasticEaseOutAnimation : CAKeyframeAnimation {} + +- (instancetype)initWithStartValue:(CGRect)start endValue:(CGRect)end andDuration:(double)duration; + +@end + +@interface GradientMaskLayer : CAGradientLayer {} + +@end + +@interface MPAlphaMaskView : UIView { + +@protected + CAGradientLayer *_maskLayer; +} + +@end + +@interface MPActionButton : UIButton + +@property (nonatomic, strong) UIColor *origColor; +@property (nonatomic, assign) BOOL highlightedWasCalled; + +@end + +@interface MPNotificationViewController () + +@end + +@implementation MPNotificationViewController + +- (void)show { + NSAssert(false, @"Sub-classes must override this method"); +} + +- (void)hide:(BOOL)animated completion:(void (^)(void))completion { + NSAssert(false, @"Sub-classes must override this method"); +} + +@end + +@interface MPTakeoverNotificationViewController () + +@property (nonatomic, strong) IBOutlet UIImageView *backgroundImageView; +@property (nonatomic, strong) IBOutlet UIImageView *imageView; +@property (nonatomic, strong) IBOutlet NSLayoutConstraint *bottomImageSpacing; +@property (nonatomic, strong) IBOutlet MPAlphaMaskView *fadingView; +@property (nonatomic, strong) IBOutlet UILabel *titleLabel; +@property (nonatomic, strong) IBOutlet UILabel *bodyLabel; +@property (nonatomic, strong) IBOutlet UIButton *firstButton; +@property (nonatomic, strong) IBOutlet UIButton *secondButton; +@property (nonatomic, strong) IBOutlet UIView *secondButtonContainer; +@property (nonatomic, strong) IBOutlet UIView *viewMask; +@property (nonatomic, strong) IBOutlet UIButton *closeButton; +@property (nonatomic, strong) UIWindow *window; + +@end + + +@implementation MPTakeoverNotificationViewController + +- (instancetype)init { + self = [super initWithNibName:[MPResources notificationXibName] bundle:[MPResources frameworkBundle]]; + + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + + if (self.notification) { + if (self.notification.image) { + UIImage *image = [UIImage imageWithData:self.notification.image scale:2.0f]; + if (image) { + if (image.size.width / [UIScreen mainScreen].bounds.size.width <= 0.6 && + image.size.height / [UIScreen mainScreen].bounds.size.height <= 0.3) { + self.imageView.contentMode = UIViewContentModeCenter; + } + self.imageView.image = image; + } else { + MPLogError(@"image failed to load from data: %@", self.notification.image); + } + } + + MPTakeoverNotification *notification = (MPTakeoverNotification *) self.notification; + + if (!notification.title || !notification.body) { + [[NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0] setActive:YES]; + [[NSLayoutConstraint constraintWithItem:self.bodyLabel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0] setActive:YES]; + } else { + self.titleLabel.text = notification.title; + self.bodyLabel.text = notification.body; + self.titleLabel.textColor = [UIColor mp_colorFromRGB:notification.titleColor]; + self.bodyLabel.textColor = [UIColor mp_colorFromRGB:notification.bodyColor]; + } + + UIImage *originalImage = self.closeButton.imageView.image; + UIImage *tintedImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + [self.closeButton setImage:tintedImage forState:UIControlStateNormal]; + self.closeButton.tintColor = [UIColor mp_colorFromRGB:notification.closeButtonColor]; + + if (!notification.shouldFadeImage) { + self.bottomImageSpacing.constant = 30; + self.fadingView.layer.mask = nil; + } + + [self setUpButtonView:self.firstButton withData:notification.buttons[0] forIndex:0]; + + if (notification.buttons.count == 2) { + [self setUpButtonView:self.secondButton withData:notification.buttons[1] forIndex:1]; + } else { + [[NSLayoutConstraint constraintWithItem:self.secondButtonContainer attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:0] setActive:YES]; + } + + self.viewMask.backgroundColor = [UIColor mp_colorFromRGB:notification.backgroundColor]; + + + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { + self.view.backgroundColor = [UIColor mp_colorFromRGB:notification.backgroundColor]; + self.view.backgroundColor = [self.view.backgroundColor colorWithAlphaComponent:0.8]; + self.viewMask.clipsToBounds = YES; + self.viewMask.layer.cornerRadius = 6; + } + } +} + +- (void)setUpButtonView:(UIButton *)buttonView withData:(MPNotificationButton *)notificationButton forIndex:(NSInteger)index { + [buttonView setTitle:notificationButton.text forState:UIControlStateNormal]; + buttonView.titleLabel.adjustsFontSizeToFitWidth = YES; + buttonView.layer.cornerRadius = 5.0f; + buttonView.layer.borderWidth = 2.0f; + UIColor *textColor = [UIColor mp_colorFromRGB:notificationButton.textColor]; + [buttonView setTitleColor:textColor forState:UIControlStateNormal]; + [buttonView setTitleColor:textColor forState:UIControlStateHighlighted]; + [buttonView setTitleColor:textColor forState:UIControlStateSelected]; + [buttonView setTag:index]; + UIColor *borderColor = [UIColor mp_colorFromRGB:notificationButton.borderColor]; + [buttonView.layer setBorderColor:borderColor.CGColor]; + [buttonView setBackgroundColor:[UIColor mp_colorFromRGB:notificationButton.backgroundColor]]; + [buttonView addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside]; +} + +- (void)buttonTapped:(UIButton *)button { + [self.delegate notificationController:self wasDismissedWithCtaUrl:((MPTakeoverNotification *)self.notification).buttons[button.tag].ctaUrl]; +} + +- (void)show { + self.window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)]; + self.window.alpha = 0; + self.window.windowLevel = UIWindowLevelAlert; + self.window.rootViewController = self; + [self.window setHidden:NO]; + + [UIView animateWithDuration:0.25 animations:^{ + self.window.alpha = 1; + }]; +} + +- (void)hide:(BOOL)animated completion:(void (^)(void))completion { + [UIView animateWithDuration:0.25 animations:^{ + self.window.alpha = 0; + } completion:^(BOOL finished) { + [self.window setHidden:YES]; + [self.window removeFromSuperview]; + self.window = nil; + if (completion != nil) { + completion(); + } + }]; +} + +- (BOOL)shouldAutorotate { + return NO; +} + +- (UIStatusBarStyle)preferredStatusBarStyle { + return UIStatusBarStyleLightContent; +} + +- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation { + return UIStatusBarAnimationFade; +} + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { + return UIInterfaceOrientationMaskAll; +} + +- (IBAction)tappedClose:(UITapGestureRecognizer *)gesture { + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(notificationController:wasDismissedWithCtaUrl:)]) { + [delegate notificationController:self wasDismissedWithCtaUrl:nil]; + } +} + +@end + +@interface MPMiniNotificationViewController () { + CGPoint _panStartPoint; + CGPoint _position; + BOOL _canPan; + BOOL _isBeingDismissed; +} + +@property (nonatomic, strong) UIImageView *imageView; +@property (nonatomic, strong) CircleLayer *circleLayer; +@property (nonatomic, strong) UILabel *bodyLabel; + +@end + +@implementation MPMiniNotificationViewController + +static const NSUInteger MPMiniNotificationSpacingFromBottom = 10; + +- (void)viewDidLoad { + [super viewDidLoad]; + + _canPan = YES; + _isBeingDismissed = NO; + self.view.clipsToBounds = YES; + + MPMiniNotification *notification = (MPMiniNotification *) self.notification; + + self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero]; + self.imageView.layer.masksToBounds = YES; + + self.bodyLabel = [[UILabel alloc] initWithFrame:CGRectZero]; + self.bodyLabel.textColor = [UIColor mp_colorFromRGB:notification.bodyColor]; + self.bodyLabel.backgroundColor = [UIColor clearColor]; + self.bodyLabel.font = [UIFont systemFontOfSize:14.0f]; + self.bodyLabel.lineBreakMode = NSLineBreakByWordWrapping; + self.bodyLabel.numberOfLines = 0; + + self.view.backgroundColor = [UIColor mp_colorFromRGB:notification.backgroundColor]; + + if (notification != nil) { + if (notification.image != nil) { + self.imageView.image = [UIImage imageWithData:notification.image scale:2.0f]; + UIImage *originalImage = [UIImage imageWithData:notification.image scale:2.0f]; + UIImage *tintedImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + [self.imageView setImage:tintedImage]; + self.imageView.tintColor = [UIColor mp_colorFromRGB:notification.imageTintColor]; + self.imageView.hidden = NO; + } else { + self.imageView.hidden = YES; + } + self.bodyLabel.text = notification.body; + } + + [self.view addSubview:self.imageView]; + [self.view addSubview:self.bodyLabel]; + + self.view.frame = CGRectMake(0.0f, 0.0f, 0.0f, 30.0f); + + UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)]; + gesture.numberOfTouchesRequired = 1; + [self.view addGestureRecognizer:gesture]; + + UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didPan:)]; + [self.view addGestureRecognizer:pan]; +} + +- (void)viewWillLayoutSubviews { + UIView *parentView = self.view.superview; + CGRect parentFrame = parentView.frame; + + if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { + self.view.frame = CGRectMake(15, parentFrame.size.height - MPNotifHeight - MPMiniNotificationSpacingFromBottom, parentFrame.size.width - 30, MPNotifHeight); + } else { + self.view.frame = CGRectMake(parentFrame.size.width/4, parentFrame.size.height - MPNotifHeight - MPMiniNotificationSpacingFromBottom, parentFrame.size.width/2, MPNotifHeight); + } + self.view.clipsToBounds = YES; + self.view.layer.cornerRadius = 6.f; + + // Position images + self.imageView.layer.position = CGPointMake(MPNotifHeight / 2.0f, MPNotifHeight / 2.0f); + + // Position circle around image + self.circleLayer.position = self.imageView.layer.position; + [self.circleLayer setNeedsDisplay]; + + // Position body label + CGSize constraintSize = CGSizeMake(self.view.frame.size.width - MPNotifHeight - 12.5f, CGFLOAT_MAX); + CGSize sizeToFit = [self.bodyLabel.text boundingRectWithSize:constraintSize + options:NSStringDrawingUsesLineFragmentOrigin + attributes:@{NSFontAttributeName: self.bodyLabel.font} + context:nil].size; + + self.bodyLabel.frame = CGRectMake(MPNotifHeight, (CGFloat)ceil((MPNotifHeight - sizeToFit.height) / 2.0f) - 2.0f, (CGFloat)ceil(sizeToFit.width), (CGFloat)ceil(sizeToFit.height)); +} + +- (UIView *)getTopView { + UIView *topView = nil; + for (UIView *subview in [UIApplication sharedApplication].keyWindow.subviews) { + if (!subview.hidden && subview.alpha > 0 && subview.frame.size.width > 0 && subview.frame.size.height > 0) { + topView = subview; + } + } + return topView; +} + +- (double)angleForInterfaceOrientation:(UIInterfaceOrientation)orientation { + switch (orientation) { + case UIInterfaceOrientationLandscapeLeft: + return -M_PI_2; + case UIInterfaceOrientationLandscapeRight: + return M_PI_2; + case UIInterfaceOrientationPortraitUpsideDown: + return M_PI; + default: + return 0.0; + } +} + +- (void)show { + [self.view removeFromSuperview]; + + UIView *topView = [self getTopView]; + if (topView) { + CGRect topFrame = topView.frame; + [topView addSubview:self.view]; + + _canPan = NO; + + self.view.frame = CGRectMake(0.0f, topFrame.size.height, topFrame.size.width, MPNotifHeight * 3.0f); + _position = self.view.layer.position; + + [UIView animateWithDuration:0.1f animations:^{ + self.view.frame = CGRectMake(0.0f, topFrame.size.height - MPNotifHeight, topFrame.size.width, MPNotifHeight * 3.0f); + } completion:^(BOOL finished) { + self->_position = self.view.layer.position; + [self performSelector:@selector(animateImage) withObject:nil afterDelay:0.1]; + self->_canPan = YES; + }]; + } +} + +- (void)animateImage { + CGSize imageViewSize = CGSizeMake(40.0f, 40.0f); + CGFloat duration = 0.5f; + + // Animate the circle around the image + CGRect before = _circleLayer.bounds; + CGRect after = CGRectMake(0.0f, 0.0f, imageViewSize.width + (_circleLayer.circlePadding * 2.0f), imageViewSize.height + (_circleLayer.circlePadding * 2.0f)); + + ElasticEaseOutAnimation *circleAnimation = [[ElasticEaseOutAnimation alloc] initWithStartValue:before endValue:after andDuration:duration]; + _circleLayer.bounds = after; + [_circleLayer addAnimation:circleAnimation forKey:@"bounds"]; + + // Animate the image + before = _imageView.bounds; + after = CGRectMake(0.0f, 0.0f, imageViewSize.width, imageViewSize.height); + ElasticEaseOutAnimation *imageAnimation = [[ElasticEaseOutAnimation alloc] initWithStartValue:before endValue:after andDuration:duration]; + _imageView.layer.bounds = after; + [_imageView.layer addAnimation:imageAnimation forKey:@"bounds"]; +} + +- (void)hide:(BOOL)animated completion:(void (^)(void))completion { + _canPan = NO; + + if (!_isBeingDismissed) { + _isBeingDismissed = YES; + + CGFloat duration = animated ? 0.5f : 0.f; + CGRect parentFrame = self.view.superview.frame; + + [UIView animateWithDuration:duration + animations:^{ + self.view.frame = CGRectMake(self.view.frame.origin.x, parentFrame.size.height, self.view.frame.size.width, self.view.frame.size.height); + } completion:^(BOOL finished) { + [self.view removeFromSuperview]; + if (completion) { + completion(); + } + }]; + } +} + +- (void)didTap:(UITapGestureRecognizer *)gesture { + if (!_isBeingDismissed && gesture.state == UIGestureRecognizerStateEnded) { + [self.delegate notificationController:self wasDismissedWithCtaUrl:((MPMiniNotification *)self.notification).ctaUrl]; + } +} + +- (void)didPan:(UIPanGestureRecognizer *)gesture { + if (_canPan) { + UIViewController *parentViewController = self.parentViewController; + if (gesture.state == UIGestureRecognizerStateBegan && gesture.numberOfTouches == 1) { + _panStartPoint = [gesture locationInView:parentViewController.view]; + } else if (gesture.state == UIGestureRecognizerStateChanged) { + CGPoint position = [gesture locationInView:parentViewController.view]; + CGFloat diffY = position.y - _panStartPoint.y; + + if (diffY > 0) { + position.y = _position.y + diffY * 2.0f; + } else { + position.y = _position.y + diffY * 0.1f; + } + + self.view.layer.position = CGPointMake(self.view.layer.position.x, position.y); + } else if (gesture.state == UIGestureRecognizerStateEnded || gesture.state == UIGestureRecognizerStateCancelled) { + id strongDelegate = self.delegate; + if (self.view.layer.position.y > _position.y + MPNotifHeight / 2.0f && strongDelegate != nil) { + [strongDelegate notificationController:self wasDismissedWithCtaUrl:nil]; + } else { + [UIView animateWithDuration:0.2f animations:^{ + self.view.layer.position = self->_position; + }]; + } + } + } +} + +@end + +@implementation MPAlphaMaskView + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if (self = [super initWithCoder:aDecoder]) { + _maskLayer = [GradientMaskLayer layer]; + [self.layer setMask:_maskLayer]; + [_maskLayer setColors:@[[UIColor blackColor], [UIColor blackColor], [UIColor clearColor],[UIColor clearColor]]]; + [_maskLayer setLocations:@[@0, @0.4, @0.9, @1]]; + [_maskLayer setStartPoint:CGPointMake(0, 0)]; + [_maskLayer setEndPoint:CGPointMake(0, 1)]; + self.opaque = NO; + _maskLayer.opaque = NO; + _maskLayer.needsDisplayOnBoundsChange = YES; + [_maskLayer setNeedsDisplay]; + } + return self; +} + +- (void)layoutSubviews { + [super layoutSubviews]; + [_maskLayer setFrame:self.bounds]; +} + +@end + +@implementation MPActionButton + +- (void) setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + UIColor *overlayColor = [UIColor colorWithRed:134/255.0 green:134/255.0 blue:134/255.0 alpha:0.2]; + + if (highlighted) { + if (!self.highlightedWasCalled) { + self.origColor = self.backgroundColor; + if ([self.origColor isEqual:[UIColor colorWithRed:0 green:0 blue:0 alpha:0]]) { + self.backgroundColor = overlayColor; + } else { + self.backgroundColor = [self.backgroundColor mp_colorAddColor:overlayColor]; + } + self.highlightedWasCalled = YES; + } + } + else { + self.backgroundColor = self.origColor; + self.highlightedWasCalled = NO; + } +} + +@end + +@implementation CircleLayer + ++ (instancetype)layer { + CircleLayer *cl = (CircleLayer *)[super layer]; + cl.circlePadding = 2.5f; + return cl; +} + +- (void)drawInContext:(CGContextRef)ctx { + CGFloat edge = 1.5f; //the distance from the edge so we don't get clipped. + CGContextSetAllowsAntialiasing(ctx, true); + CGContextSetShouldAntialias(ctx, true); + + CGMutablePathRef thePath = CGPathCreateMutable(); + CGContextSetStrokeColorWithColor(ctx, [UIColor whiteColor].CGColor); + CGPathAddArc(thePath, NULL, self.frame.size.width / 2.0f, self.frame.size.height / 2.0f, MIN(self.frame.size.width, self.frame.size.height) / 2.0f - (2 * edge), (float)-M_PI, (float)M_PI, YES); + + CGContextBeginPath(ctx); + CGContextAddPath(ctx, thePath); + + CGContextSetLineWidth(ctx, 1.5f); + CGContextStrokePath(ctx); + + CFRelease(thePath); +} + +@end + +@implementation GradientMaskLayer + +- (void)drawInContext:(CGContextRef)ctx { + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + + CGFloat components[] = { //[Grayscale, Alpha] for each component + 1.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 0.0f}; + + CGFloat locations[] = {0.0f, 0.4f, 0.9f, 1.0f}; + CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 4); + CGContextDrawLinearGradient(ctx, gradient, CGPointMake(0.0f, 0.0f), CGPointMake(5.0f, self.bounds.size.height), (CGGradientDrawingOptions)0); + + + NSUInteger bits = (NSUInteger)fabs(self.bounds.size.width) * (NSUInteger)fabs(self.bounds.size.height); + char *rgba = (char *)malloc(bits); + srand(124); + + for (NSUInteger i = 0; i < bits; ++i) { + rgba[i] = (rand() % 8); + } + + CGContextRef noise = CGBitmapContextCreate(rgba, (NSUInteger)fabs(self.bounds.size.width), (NSUInteger)fabs(self.bounds.size.height), 8, (NSUInteger)fabs(self.bounds.size.width), NULL, (CGBitmapInfo)kCGImageAlphaOnly); + CGImageRef image = CGBitmapContextCreateImage(noise); + + CGContextSetBlendMode(ctx, kCGBlendModeSourceOut); + CGContextDrawImage(ctx, self.bounds, image); + + CGImageRelease(image); + CGColorSpaceRelease(colorSpace); + CGGradientRelease(gradient); + CGContextRelease(noise); + free(rgba); +} + +@end + +@implementation ElasticEaseOutAnimation + +- (instancetype)initWithStartValue:(CGRect)start endValue:(CGRect)end andDuration:(double)duration { + if ((self = [super init])) { + self.duration = duration; + self.values = [self generateValuesFrom:start to:end]; + } + return self; +} + +- (NSArray *)generateValuesFrom:(CGRect)start to:(CGRect)end { + NSUInteger steps = (NSUInteger)ceil(60 * self.duration) + 2; + NSMutableArray *valueArray = [NSMutableArray arrayWithCapacity:steps]; + const double increment = 1.0 / (double)(steps - 1); + double t = 0.0; + CGRect range = CGRectMake(end.origin.x - start.origin.x, end.origin.y - start.origin.y, end.size.width - start.size.width, end.size.height - start.size.height); + + NSUInteger i; + for (i = 0; i < steps; i++) { + float v = (float) -(pow(M_E, -8*t) * cos(12*t)) + 1; // Cosine wave with exponential decay + + CGRect value = CGRectMake(start.origin.x + v * range.origin.x, + start.origin.y + v * range.origin.y, + start.size.width + v * range.size.width, + start.size.height + v *range.size.height); + + [valueArray addObject:[NSValue valueWithCGRect:value]]; + t += increment; + } + + return [NSArray arrayWithArray:valueArray]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentifierProvider.h b/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentifierProvider.h new file mode 100644 index 0000000..8223fbc --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentifierProvider.h @@ -0,0 +1,9 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@protocol MPObjectIdentifierProvider +- (NSString *)identifierForObject:(id)object; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentityProvider.h b/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentityProvider.h new file mode 100644 index 0000000..c27c5f6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentityProvider.h @@ -0,0 +1,10 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@interface MPObjectIdentityProvider : NSObject + +- (NSString *)identifierForObject:(id)object; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentityProvider.m b/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentityProvider.m new file mode 100644 index 0000000..190ad77 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectIdentityProvider.m @@ -0,0 +1,39 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPObjectIdentityProvider.h" +#import "MPSequenceGenerator.h" + +@implementation MPObjectIdentityProvider + +{ + NSMapTable *_objectToIdentifierMap; + MPSequenceGenerator *_sequenceGenerator; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _objectToIdentifierMap = [NSMapTable weakToStrongObjectsMapTable]; + _sequenceGenerator = [[MPSequenceGenerator alloc] init]; + } + + return self; +} + +- (NSString *)identifierForObject:(id)object +{ + if ([object isKindOfClass:[NSString class]]) { + return object; + } + NSString *identifier = [_objectToIdentifierMap objectForKey:object]; + if (identifier == nil) { + identifier = [NSString stringWithFormat:@"$%" PRIi32, [_sequenceGenerator nextValue]]; + [_objectToIdentifierMap setObject:identifier forKey:object]; + } + + return identifier; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSelector.h b/ios/Pods/Mixpanel/Mixpanel/MPObjectSelector.h new file mode 100644 index 0000000..bd97707 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSelector.h @@ -0,0 +1,28 @@ +// +// ObjectSelector.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 5/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import + +@interface MPObjectSelector : NSObject + +@property (nonatomic, strong, readonly) NSString *string; + ++ (MPObjectSelector *)objectSelectorWithString:(NSString *)string; +- (instancetype)initWithString:(NSString *)string; + +- (NSArray *)selectFromRoot:(id)root; +- (NSArray *)fuzzySelectFromRoot:(id)root; + +- (BOOL)isLeafSelected:(id)leaf fromRoot:(id)root; +- (BOOL)fuzzyIsLeafSelected:(id)leaf fromRoot:(id)root; + +- (Class)selectedClass; +- (BOOL)pathContainsObjectOfClass:(Class)klass; +- (NSString *)description; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSelector.m b/ios/Pods/Mixpanel/Mixpanel/MPObjectSelector.m new file mode 100644 index 0000000..522d04d --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSelector.m @@ -0,0 +1,440 @@ +// +// ObjectSelector.m +// HelloMixpanel +// +// Created by Alex Hofsteede on 5/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import +#import +#import "MPObjectSelector.h" + +@interface MPObjectFilter : NSObject + +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSPredicate *predicate; +@property (nonatomic, strong) NSNumber *index; +@property (nonatomic, assign) BOOL unique; +@property (nonatomic, assign) BOOL nameOnly; + +- (NSArray *)apply:(NSArray *)views; +- (NSArray *)applyReverse:(NSArray *)views; +- (BOOL)appliesTo:(NSObject *)view; +- (BOOL)appliesToAny:(NSArray *)views; + +@end + +@interface MPObjectSelector () { + NSCharacterSet *_classAndPropertyChars; + NSCharacterSet *_separatorChars; + NSCharacterSet *_predicateStartChar; + NSCharacterSet *_predicateEndChar; + NSCharacterSet *_flagStartChar; + NSCharacterSet *_flagEndChar; + +} + +@property (nonatomic, strong) NSScanner *scanner; +@property (nonatomic, strong) NSArray *filters; + +@end + +@implementation MPObjectSelector + ++ (MPObjectSelector *)objectSelectorWithString:(NSString *)string +{ + return [[MPObjectSelector alloc] initWithString:string]; +} + +- (instancetype)initWithString:(NSString *)string +{ + if (self = [super init]) { + _string = string; + _scanner = [NSScanner scannerWithString:string]; + [_scanner setCharactersToBeSkipped:nil]; + _separatorChars = [NSCharacterSet characterSetWithCharactersInString:@"/"]; + _predicateStartChar = [NSCharacterSet characterSetWithCharactersInString:@"["]; + _predicateEndChar = [NSCharacterSet characterSetWithCharactersInString:@"]"]; + _classAndPropertyChars = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.*"]; + _flagStartChar = [NSCharacterSet characterSetWithCharactersInString:@"("]; + _flagEndChar = [NSCharacterSet characterSetWithCharactersInString:@")"]; + + NSMutableArray *filters = [NSMutableArray array]; + MPObjectFilter *filter; + while ((filter = [self nextFilter])) { + [filters addObject:filter]; + } + self.filters = [filters copy]; + } + return self; +} + +/* + Starting at the root object, try and find an object + in the view/controller tree that matches this selector. +*/ + +- (NSArray *)selectFromRoot:(id)root +{ + return [self selectFromRoot:root evaluatingFinalPredicate:YES]; +} + +- (NSArray *)fuzzySelectFromRoot:(id)root +{ + return [self selectFromRoot:root evaluatingFinalPredicate:NO]; +} + +- (NSArray *)selectFromRoot:(id)root evaluatingFinalPredicate:(BOOL)finalPredicate +{ + NSArray *views = @[]; + if (root) { + views = @[root]; + + NSUInteger i = 0, n = _filters.count; + for (MPObjectFilter *filter in _filters) { + filter.nameOnly = (i == n-1 && !finalPredicate); + views = [filter apply:views]; + if (views.count == 0) { + break; + } + i++; + } + } + return views; +} + + +/* + Starting at a leaf node, determine if it would be selected + by this selector starting from the root object given. + */ + +- (BOOL)isLeafSelected:(id)leaf fromRoot:(id)root +{ + return [self isLeafSelected:leaf fromRoot:root evaluatingFinalPredicate:YES]; +} + +- (BOOL)fuzzyIsLeafSelected:(id)leaf fromRoot:(id)root +{ + return [self isLeafSelected:leaf fromRoot:root evaluatingFinalPredicate:NO]; +} + +- (BOOL)isLeafSelected:(id)leaf fromRoot:(id)root evaluatingFinalPredicate:(BOOL)finalPredicate +{ + BOOL isSelected = YES; + NSArray *views = @[leaf]; + NSUInteger n = _filters.count, i = n; + while (i--) { + MPObjectFilter *filter = _filters[i]; + filter.nameOnly = (i == n-1 && !finalPredicate); + if (![filter appliesToAny:views]) { + isSelected = NO; + break; + } + views = [filter applyReverse:views]; + if (views.count == 0) { + break; + } + } + return isSelected && [views indexOfObject:root] != NSNotFound; +} + +- (MPObjectFilter *)nextFilter +{ + MPObjectFilter *filter; + if ([_scanner scanCharactersFromSet:_separatorChars intoString:nil]) { + NSString *name; + filter = [[MPObjectFilter alloc] init]; + if ([_scanner scanCharactersFromSet:_classAndPropertyChars intoString:&name]) { + filter.name = name; + } else { + filter.name = @"*"; + } + if ([_scanner scanCharactersFromSet:_flagStartChar intoString:nil]) { + NSString *flags; + [_scanner scanUpToCharactersFromSet:_flagEndChar intoString:&flags]; + for (NSString *flag in[flags componentsSeparatedByString:@"|"]) { + if ([flag isEqualToString:@"unique"]) { + filter.unique = YES; + } + } + } + if ([_scanner scanCharactersFromSet:_predicateStartChar intoString:nil]) { + NSString *predicateFormat; + NSInteger index = 0; + if ([_scanner scanInteger:&index] && [_scanner scanCharactersFromSet:_predicateEndChar intoString:nil]) { + filter.index = @((NSUInteger)index); + } else { + [_scanner scanUpToCharactersFromSet:_predicateEndChar intoString:&predicateFormat]; + @try { + NSPredicate *parsedPredicate = [NSPredicate predicateWithFormat:predicateFormat]; + filter.predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { + @try { + return [parsedPredicate evaluateWithObject:evaluatedObject substitutionVariables:bindings]; + } + @catch (NSException *exception) { + return false; + } + }]; + } + @catch (NSException *exception) { + filter.predicate = [NSPredicate predicateWithValue:NO]; + } + + [_scanner scanCharactersFromSet:_predicateEndChar intoString:nil]; + } + } + } + return filter; +} + +- (Class)selectedClass +{ + MPObjectFilter *filter = _filters.lastObject; + if (filter) { + return NSClassFromString(filter.name); + } + return nil; +} + +- (BOOL)pathContainsObjectOfClass:(Class)klass { + for (MPObjectFilter *filter in _filters) { + if ([NSClassFromString(filter.name) isSubclassOfClass:klass]) { + return YES; + } + } + return NO; +} + +- (NSString *)description +{ + return self.string; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[MPObjectSelector class]]) { + return NO; + } else { + return [self.string isEqual:((MPObjectSelector *)other).string]; + } +} + +- (NSUInteger)hash { + return [self.string hash]; +} + +@end + +@implementation MPObjectFilter + +- (instancetype)init +{ + if ((self = [super init])) { + self.unique = NO; + self.nameOnly = NO; + } + return self; +} + +/* + Apply this filter to the views, returning all of their children + that match this filter's class / predicate pattern + */ +- (NSArray *)apply:(NSArray *)views +{ + NSMutableArray *result = [NSMutableArray array]; + + Class class = NSClassFromString(_name); + if (class || [_name isEqualToString:@"*"]) { + // Select all children + for (NSObject *view in views) { + NSArray *children = [self getChildrenOfObject:view ofType:class]; + if (_index && _index.unsignedIntegerValue < children.count) { + // Indexing can only be used for subviews of UIView + if ([view isKindOfClass:[UIView class]]) { + children = @[children[_index.unsignedIntegerValue]]; + } else { + children = @[]; + } + } + [result addObjectsFromArray:children]; + } + } + + if (!self.nameOnly) { + // If unique is set and there are more than one, return nothing + if (self.unique && result.count != 1) { + return @[]; + } + // Filter any resulting views by predicate + if (self.predicate) { + return [result filteredArrayUsingPredicate:self.predicate]; + } + } + return [result copy]; +} + +/* + Apply this filter to the views. For any view that + matches this filter's class / predicate pattern, return + its parents. + */ +- (NSArray *)applyReverse:(NSArray *)views +{ + NSMutableArray *result = [NSMutableArray array]; + for (NSObject *view in views) { + if ([self appliesTo:view]) { + [result addObjectsFromArray:[self getParentsOfObject:view]]; + } + } + return [result copy]; +} + +/* + Returns whether the given view would pass this filter. + */ +- (BOOL)appliesTo:(NSObject *)view +{ + return (([self.name isEqualToString:@"*"] || [view isKindOfClass:NSClassFromString(self.name)]) + && (self.nameOnly || ( + (!self.predicate || [_predicate evaluateWithObject:view]) + && (!self.index || [self isView:view siblingNumber:_index.integerValue]) + && (!(self.unique) || [self isView:view oneOfNSiblings:1]))) + ); +} + +/* + Returns whether any of the given views would pass this filter + */ +- (BOOL)appliesToAny:(NSArray *)views +{ + for (NSObject *view in views) { + if ([self appliesTo:view]) { + return YES; + } + } + return NO; +} + +/* + Returns true if the given view is at the index given by number in + its parent's subviews. The view's parent must be of type UIView + */ + +- (BOOL)isView:(NSObject *)view siblingNumber:(NSInteger)number +{ + return [self isView:view siblingNumber:number of:-1]; +} + +- (BOOL)isView:(NSObject *)view oneOfNSiblings:(NSInteger)number +{ + return [self isView:view siblingNumber:-1 of:number]; +} + +- (BOOL)isView:(NSObject *)view siblingNumber:(NSInteger)index of:(NSInteger)numSiblings +{ + NSArray *parents = [self getParentsOfObject:view]; + for (NSObject *parent in parents) { + if ([parent isKindOfClass:[UIView class]]) { + NSArray *siblings = [self getChildrenOfObject:parent ofType:NSClassFromString(_name)]; + if ((index < 0 || ((NSUInteger)index < siblings.count && siblings[(NSUInteger)index] == view)) + && (numSiblings < 0 || siblings.count == (NSUInteger)numSiblings)) { + return YES; + } + } + } + return NO; +} + +- (NSArray *)getParentsOfObject:(NSObject *)obj +{ + NSMutableArray *result = [NSMutableArray array]; + if ([obj isKindOfClass:[UIView class]]) { + UIView *superview = [(UIView *)obj superview]; + if (superview) { + [result addObject:superview]; + } + UIResponder *nextResponder = [(UIView *)obj nextResponder]; + // For UIView, nextResponder should be its controller or its superview. + if (nextResponder && nextResponder != superview) { + [result addObject:nextResponder]; + } + } else if ([obj isKindOfClass:[UIViewController class]]) { + UIViewController *parentViewController = [(UIViewController *)obj parentViewController]; + if (parentViewController) { + [result addObject:parentViewController]; + } + UIViewController *presentingViewController = [(UIViewController *)obj presentingViewController]; + if (presentingViewController) { + [result addObject:presentingViewController]; + } + UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; + if (keyWindow.rootViewController == obj) { + //TODO is there a better way to get the actual window that has this VC + [result addObject:keyWindow]; + } + } + return [result copy]; +} + +- (NSArray *)getChildrenOfObject:(NSObject *)obj ofType:(Class)class +{ + NSMutableArray *children = [NSMutableArray array]; + // A UIWindow is also a UIView, so we could in theory follow the subviews chain from UIWindow, but + // for now we only follow rootViewController from UIView. + if ([obj isKindOfClass:[UIWindow class]]) { + UIViewController *rootViewController = ((UIWindow *)obj).rootViewController; + if ([rootViewController isKindOfClass:class]) { + [children addObject:rootViewController]; + } + } else if ([obj isKindOfClass:[UIView class]]) { + // NB. For UIViews, only add subviews, nothing else. + // The ordering of this result is critical to being able to + // apply the index filter. + NSArray *subviews = [[(UIView *)obj subviews] copy]; + for (NSObject *child in subviews) { + if (!class || [child isKindOfClass:class]) { + [children addObject:child]; + } + } + } else if ([obj isKindOfClass:[UIViewController class]]) { + UIViewController *viewController = (UIViewController *)obj; + for (NSObject *child in [viewController childViewControllers]) { + if (!class || [child isKindOfClass:class]) { + [children addObject:child]; + } + } + UIViewController *presentedViewController = viewController.presentedViewController; + if (presentedViewController && (!class || [presentedViewController isKindOfClass:class])) { + [children addObject:presentedViewController]; + } + if (!class || (viewController.isViewLoaded && [viewController.view isKindOfClass:class])) { + [children addObject:viewController.view]; + } + } + NSArray *result; + // Reorder the cells in a table view so that they are arranged by y position + if ([class isSubclassOfClass:[UITableViewCell class]]) { + result = [children sortedArrayUsingComparator:^NSComparisonResult(UIView *obj1, UIView *obj2) { + if (obj2.frame.origin.y > obj1.frame.origin.y) { + return NSOrderedAscending; + } else if (obj2.frame.origin.y < obj1.frame.origin.y) { + return NSOrderedDescending; + } + return NSOrderedSame; + }]; + } else { + result = [children copy]; + } + return result; +} + +- (NSString *)description; +{ + return [NSString stringWithFormat:@"%@[%@]", self.name, self.index ?: self.predicate]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializer.h b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializer.h new file mode 100644 index 0000000..20682a4 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializer.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@class MPClassDescription; +@class MPObjectSerializerContext; +@class MPObjectSerializerConfig; +@class MPObjectIdentityProvider; + +@interface MPObjectSerializer : NSObject + +/*! + An array of MPClassDescription instances. + */ +- (instancetype)initWithConfiguration:(MPObjectSerializerConfig *)configuration objectIdentityProvider:(MPObjectIdentityProvider *)objectIdentityProvider; + +- (NSDictionary *)serializedObjectsWithRootObject:(id)rootObject; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializer.m b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializer.m new file mode 100644 index 0000000..e5f90e3 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializer.m @@ -0,0 +1,317 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "UIView+MPHelpers.h" +#import "MPClassDescription.h" +#import "MPEnumDescription.h" +#import "MPObjectIdentityProvider.h" +#import "MPObjectSerializer.h" +#import "MPObjectSerializerConfig.h" +#import "MPObjectSerializerContext.h" +#import "MPPropertyDescription.h" +#import "NSInvocation+MPHelpers.h" + +@interface MPObjectSerializer () + +@end + +@implementation MPObjectSerializer + +{ + MPObjectSerializerConfig *_configuration; + MPObjectIdentityProvider *_objectIdentityProvider; +} + +- (instancetype)initWithConfiguration:(MPObjectSerializerConfig *)configuration objectIdentityProvider:(MPObjectIdentityProvider *)objectIdentityProvider +{ + self = [super init]; + if (self) { + _configuration = configuration; + _objectIdentityProvider = objectIdentityProvider; + } + + return self; +} + +- (NSDictionary *)serializedObjectsWithRootObject:(id)rootObject +{ + NSParameterAssert(rootObject != nil); + + MPObjectSerializerContext *context = [[MPObjectSerializerContext alloc] initWithRootObject:rootObject]; + + while ([context hasUnvisitedObjects]) + { + [self visitObject:[context dequeueUnvisitedObject] withContext:context]; + } + + return @{ + @"objects": [context allSerializedObjects], + @"rootObject": [_objectIdentityProvider identifierForObject:rootObject] + }; +} + +- (void)visitObject:(NSObject *)object withContext:(MPObjectSerializerContext *)context +{ + NSParameterAssert(object != nil); + NSParameterAssert(context != nil); + + [context addVisitedObject:object]; + + NSMutableDictionary *propertyValues = [NSMutableDictionary dictionary]; + + MPClassDescription *classDescription = [self classDescriptionForObject:object]; + if (classDescription) { + for (MPPropertyDescription *propertyDescription in [classDescription propertyDescriptions]) { + if ([propertyDescription shouldReadPropertyValueForObject:object]) { + id propertyValue = [self propertyValueForObject:object withPropertyDescription:propertyDescription context:context]; + propertyValues[propertyDescription.name] = propertyValue ?: [NSNull null]; + } + } + } + + NSMutableArray *delegateMethods = [NSMutableArray array]; + id delegate; + SEL delegateSelector = @selector(delegate); + + if ([classDescription delegateInfos].count > 0 && [object respondsToSelector:delegateSelector]) { + delegate = ((id (*)(id, SEL))[object methodForSelector:delegateSelector])(object, delegateSelector); + for (MPDelegateInfo *delegateInfo in [classDescription delegateInfos]) { + if ([delegate respondsToSelector:NSSelectorFromString(delegateInfo.selectorName)]) { + [delegateMethods addObject:delegateInfo.selectorName]; + } + } + } + + NSDictionary *serializedObject = @{ + @"id": [_objectIdentityProvider identifierForObject:object], + @"class": [self classHierarchyArrayForObject:object], + @"properties": propertyValues, + @"delegate": @{ + @"class": delegate ? NSStringFromClass([delegate class]) : @"", + @"selectors": delegateMethods + } + }; + + [context addSerializedObject:serializedObject]; +} + +- (NSArray *)classHierarchyArrayForObject:(NSObject *)object +{ + NSMutableArray *classHierarchy = [NSMutableArray array]; + + Class aClass = [object class]; + while (aClass) + { + [classHierarchy addObject:NSStringFromClass(aClass)]; + aClass = [aClass superclass]; + } + + return [classHierarchy copy]; +} + +- (NSArray *)allValuesForType:(NSString *)typeName +{ + NSParameterAssert(typeName != nil); + + MPTypeDescription *typeDescription = [_configuration typeWithName:typeName]; + if ([typeDescription isKindOfClass:[MPEnumDescription class]]) { + MPEnumDescription *enumDescription = (MPEnumDescription *)typeDescription; + return [enumDescription allValues]; + } + + return @[]; +} + +- (NSArray *)parameterVariationsForPropertySelector:(MPPropertySelectorDescription *)selectorDescription +{ + NSAssert(selectorDescription.parameters.count <= 1, @"Currently only support selectors that take 0 to 1 arguments."); + + NSMutableArray *variations = [NSMutableArray array]; + + // TODO: write an algorithm that generates all the variations of parameter combinations. + if (selectorDescription.parameters.count > 0) { + MPPropertySelectorParameterDescription *parameterDescription = selectorDescription.parameters[0]; + for (id value in [self allValuesForType:parameterDescription.type]) { + [variations addObject:@[ value ]]; + } + } else { + // An empty array of parameters (for methods that have no parameters). + [variations addObject:@[]]; + } + + return [variations copy]; +} + +- (id)instanceVariableValueForObject:(id)object propertyDescription:(MPPropertyDescription *)propertyDescription +{ + NSParameterAssert(object != nil); + NSParameterAssert(propertyDescription != nil); + + Ivar ivar = class_getInstanceVariable([object class], [propertyDescription.name UTF8String]); + if (ivar) { + const char *objCType = ivar_getTypeEncoding(ivar); + + ptrdiff_t ivarOffset = ivar_getOffset(ivar); + const void *objectBaseAddress = (__bridge const void *)object; + const void *ivarAddress = (((const uint8_t *)objectBaseAddress) + ivarOffset); + + switch (objCType[0]) + { + case _C_ID: return object_getIvar(object, ivar); + case _C_CHR: return @(*((char *)ivarAddress)); + case _C_UCHR: return @(*((unsigned char *)ivarAddress)); + case _C_SHT: return @(*((short *)ivarAddress)); + case _C_USHT: return @(*((unsigned short *)ivarAddress)); + case _C_INT: return @(*((int *)ivarAddress)); + case _C_UINT: return @(*((unsigned int *)ivarAddress)); + case _C_LNG: return @(*((long *)ivarAddress)); + case _C_ULNG: return @(*((unsigned long *)ivarAddress)); + case _C_LNG_LNG: return @(*((long long *)ivarAddress)); + case _C_ULNG_LNG: return @(*((unsigned long long *)ivarAddress)); + case _C_FLT: return @(*((float *)ivarAddress)); + case _C_DBL: return @(*((double *)ivarAddress)); + case _C_BOOL: return @(*((_Bool *)ivarAddress)); + case _C_SEL: return NSStringFromSelector(*((SEL*)ivarAddress)); + default: + NSAssert(NO, @"Currently unsupported return type!"); + break; + } + } + + return nil; +} + +- (NSInvocation *)invocationForObject:(id)object withSelectorDescription:(MPPropertySelectorDescription *)selectorDescription +{ + NSUInteger __unused parameterCount = selectorDescription.parameters.count; + + SEL aSelector = NSSelectorFromString(selectorDescription.selectorName); + NSAssert(aSelector != nil, @"Expected non-nil selector!"); + + NSMethodSignature *methodSignature = [object methodSignatureForSelector:aSelector]; + NSInvocation *invocation = nil; + + if (methodSignature) { + NSAssert(methodSignature.numberOfArguments == (parameterCount + 2), @"Unexpected number of arguments!"); + + invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; + invocation.selector = aSelector; + } + return invocation; +} + +- (id)propertyValue:(id)propertyValue propertyDescription:(MPPropertyDescription *)propertyDescription context:(MPObjectSerializerContext *)context +{ + if (propertyValue != nil) { + if ([context isVisitedObject:propertyValue]) { + return [_objectIdentityProvider identifierForObject:propertyValue]; + } + else if ([self isNestedObjectType:propertyDescription.type]) + { + [context enqueueUnvisitedObject:propertyValue]; + return [_objectIdentityProvider identifierForObject:propertyValue]; + } + else if ([propertyValue isKindOfClass:[NSArray class]] || [propertyValue isKindOfClass:[NSSet class]]) + { + NSMutableArray *arrayOfIdentifiers = [NSMutableArray array]; + for (id value in propertyValue) { + if ([context isVisitedObject:value] == NO) { + [context enqueueUnvisitedObject:value]; + } + + [arrayOfIdentifiers addObject:[_objectIdentityProvider identifierForObject:value]]; + } + propertyValue = [arrayOfIdentifiers copy]; + } + } + + return [propertyDescription.valueTransformer transformedValue:propertyValue]; +} + +- (id)propertyValueForObject:(NSObject *)object withPropertyDescription:(MPPropertyDescription *)propertyDescription context:(MPObjectSerializerContext *)context +{ + NSMutableArray *values = [NSMutableArray array]; + + MPPropertySelectorDescription *selectorDescription = propertyDescription.getSelectorDescription; + + if (propertyDescription.useKeyValueCoding) { + // the "fast" (also also simple) path is to use KVC + id valueForKey = [object valueForKey:selectorDescription.selectorName]; + + id value = [self propertyValue:valueForKey + propertyDescription:propertyDescription + context:context]; + + NSDictionary *valueDictionary = @{ + @"value": (value ?: [NSNull null]) + }; + + [values addObject:valueDictionary]; + } + else if (propertyDescription.useInstanceVariableAccess) + { + id valueForIvar = [self instanceVariableValueForObject:object propertyDescription:propertyDescription]; + + id value = [self propertyValue:valueForIvar + propertyDescription:propertyDescription + context:context]; + + NSDictionary *valueDictionary = @{ + @"value": (value ?: [NSNull null]) + }; + + [values addObject:valueDictionary]; + } else { + // the "slow" NSInvocation path. Required in order to invoke methods that take parameters. + NSInvocation *invocation = [self invocationForObject:object withSelectorDescription:selectorDescription]; + if (invocation) { + NSArray *parameterVariations = [self parameterVariationsForPropertySelector:selectorDescription]; + + for (NSArray *parameters in parameterVariations) { + [invocation mp_setArgumentsFromArray:parameters]; + [invocation invokeWithTarget:object]; + + id returnValue = [invocation mp_returnValue]; + + id value = [self propertyValue:returnValue + propertyDescription:propertyDescription + context:context]; + + NSDictionary *valueDictionary = @{ + @"where": @{ @"parameters": parameters }, + @"value": (value ?: [NSNull null]) + }; + + [values addObject:valueDictionary]; + } + } + } + + return @{@"values": values}; +} + +- (BOOL)isNestedObjectType:(NSString *)typeName +{ + return [_configuration classWithName:typeName] != nil; +} + +- (MPClassDescription *)classDescriptionForObject:(NSObject *)object +{ + NSParameterAssert(object != nil); + + Class aClass = [object class]; + while (aClass != nil) + { + MPClassDescription *classDescription = [_configuration classWithName:NSStringFromClass(aClass)]; + if (classDescription) { + return classDescription; + } + + aClass = [aClass superclass]; + } + + return nil; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerConfig.h b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerConfig.h new file mode 100644 index 0000000..539d1c4 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerConfig.h @@ -0,0 +1,22 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@class MPEnumDescription; +@class MPClassDescription; +@class MPTypeDescription; + + +@interface MPObjectSerializerConfig : NSObject + +@property (nonatomic, readonly) NSArray *classDescriptions; +@property (nonatomic, readonly) NSArray *enumDescriptions; + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; + +- (MPTypeDescription *)typeWithName:(NSString *)name; +- (MPEnumDescription *)enumWithName:(NSString *)name; +- (MPClassDescription *)classWithName:(NSString *)name; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerConfig.m b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerConfig.m new file mode 100644 index 0000000..5361255 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerConfig.m @@ -0,0 +1,73 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPClassDescription.h" +#import "MPEnumDescription.h" +#import "MPObjectSerializerConfig.h" +#import "MPTypeDescription.h" + +@implementation MPObjectSerializerConfig + +{ + NSDictionary *_classes; + NSDictionary *_enums; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + self = [super init]; + if (self) { + NSMutableDictionary *classDescriptions = [NSMutableDictionary dictionary]; + for (NSDictionary *d in dictionary[@"classes"]) { + NSString *superclassName = d[@"superclass"]; + MPClassDescription *superclassDescription = superclassName ? classDescriptions[superclassName] : nil; + MPClassDescription *classDescription = [[MPClassDescription alloc] initWithSuperclassDescription:superclassDescription + dictionary:d]; + + classDescriptions[classDescription.name] = classDescription; + } + + NSMutableDictionary *enumDescriptions = [NSMutableDictionary dictionary]; + for (NSDictionary *d in dictionary[@"enums"]) { + MPEnumDescription *enumDescription = [[MPEnumDescription alloc] initWithDictionary:d]; + enumDescriptions[enumDescription.name] = enumDescription; + } + + _classes = [classDescriptions copy]; + _enums = [enumDescriptions copy]; + } + + return self; +} + +- (NSArray *)classDescriptions +{ + return _classes.allValues; +} + +- (MPEnumDescription *)enumWithName:(NSString *)name +{ + return _enums[name]; +} + +- (MPClassDescription *)classWithName:(NSString *)name +{ + return _classes[name]; +} + +- (MPTypeDescription *)typeWithName:(NSString *)name +{ + MPEnumDescription *enumDescription = [self enumWithName:name]; + if (enumDescription) { + return enumDescription; + } + + MPClassDescription *classDescription = [self classWithName:name]; + if (classDescription) { + return classDescription; + } + + return nil; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerContext.h b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerContext.h new file mode 100644 index 0000000..f32ce93 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerContext.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@interface MPObjectSerializerContext : NSObject + +- (instancetype)initWithRootObject:(id)object; + +- (BOOL)hasUnvisitedObjects; + +- (void)enqueueUnvisitedObject:(NSObject *)object; +- (NSObject *)dequeueUnvisitedObject; + +- (void)addVisitedObject:(NSObject *)object; +- (BOOL)isVisitedObject:(NSObject *)object; + +- (void)addSerializedObject:(NSDictionary *)serializedObject; +- (NSArray *)allSerializedObjects; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerContext.m b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerContext.m new file mode 100644 index 0000000..359232e --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPObjectSerializerContext.m @@ -0,0 +1,69 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPObjectSerializerContext.h" + +@implementation MPObjectSerializerContext + +{ + NSMutableSet *_visitedObjects; + NSMutableSet *_unvisitedObjects; + NSMutableDictionary *_serializedObjects; +} + +- (instancetype)initWithRootObject:(id)object +{ + self = [super init]; + if (self) { + _visitedObjects = [NSMutableSet set]; + _unvisitedObjects = [NSMutableSet setWithObject:object]; + _serializedObjects = [NSMutableDictionary dictionary]; + } + + return self; +} + +- (BOOL)hasUnvisitedObjects +{ + return _unvisitedObjects.count > 0; +} + +- (void)enqueueUnvisitedObject:(NSObject *)object +{ + NSParameterAssert(object != nil); + + [_unvisitedObjects addObject:object]; +} + +- (NSObject *)dequeueUnvisitedObject +{ + NSObject *object = [_unvisitedObjects anyObject]; + [_unvisitedObjects removeObject:object]; + + return object; +} + +- (void)addVisitedObject:(NSObject *)object +{ + NSParameterAssert(object != nil); + + [_visitedObjects addObject:object]; +} + +- (BOOL)isVisitedObject:(NSObject *)object +{ + return object && [_visitedObjects containsObject:object]; +} + +- (void)addSerializedObject:(NSDictionary *)serializedObject +{ + NSParameterAssert(serializedObject[@"id"] != nil); + _serializedObjects[serializedObject[@"id"]] = serializedObject; +} + +- (NSArray *)allSerializedObjects +{ + return _serializedObjects.allValues; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPPassThroughValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPPassThroughValueTransformer.m new file mode 100644 index 0000000..0412b4d --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPPassThroughValueTransformer.m @@ -0,0 +1,31 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPPassThroughValueTransformer + ++ (Class)transformedValueClass +{ + return [NSObject class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return NO; +} + +- (id)transformedValue:(id)value +{ + if ([[NSNull null] isEqual:value]) { + return nil; + } + + if (value == nil) { + return [NSNull null]; + } + + return value; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPPropertyDescription.h b/ios/Pods/Mixpanel/Mixpanel/MPPropertyDescription.h new file mode 100644 index 0000000..487d0c5 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPPropertyDescription.h @@ -0,0 +1,43 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@class MPObjectSerializerContext; + +@interface MPPropertySelectorParameterDescription : NSObject + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; +@property (nonatomic, readonly) NSString *name; +@property (nonatomic, readonly) NSString *type; + +@end + +@interface MPPropertySelectorDescription : NSObject + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; +@property (nonatomic, readonly) NSString *selectorName; +@property (nonatomic, readonly) NSString *returnType; +@property (nonatomic, readonly) NSArray *parameters; // array of MPPropertySelectorParameterDescription + +@end + +@interface MPPropertyDescription : NSObject + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; + +@property (nonatomic, readonly) NSString *type; +@property (nonatomic, readonly) BOOL readonly; +@property (nonatomic, readonly) BOOL nofollow; +@property (nonatomic, readonly) BOOL useKeyValueCoding; +@property (nonatomic, readonly) BOOL useInstanceVariableAccess; +@property (nonatomic, readonly) NSString *name; + +@property (nonatomic, readonly) MPPropertySelectorDescription *getSelectorDescription; +@property (nonatomic, readonly) MPPropertySelectorDescription *setSelectorDescription; + +- (BOOL)shouldReadPropertyValueForObject:(NSObject *)object; + +- (NSValueTransformer *)valueTransformer; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPPropertyDescription.m b/ios/Pods/Mixpanel/Mixpanel/MPPropertyDescription.m new file mode 100644 index 0000000..c11cec6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPPropertyDescription.m @@ -0,0 +1,158 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPPropertyDescription.h" + +@implementation MPPropertySelectorParameterDescription + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + NSParameterAssert(dictionary[@"name"] != nil); + NSParameterAssert(dictionary[@"type"] != nil); + + self = [super init]; + if (self) { + _name = [dictionary[@"name"] copy]; + _type = [dictionary[@"type"] copy]; + } + + return self; +} + +@end + +@implementation MPPropertySelectorDescription + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + NSParameterAssert(dictionary[@"selector"] != nil); + NSParameterAssert(dictionary[@"parameters"] != nil); + + self = [super init]; + if (self) { + _selectorName = [dictionary[@"selector"] copy]; + NSMutableArray *parameters = [NSMutableArray arrayWithCapacity:[dictionary[@"parameters"] count]]; + for (NSDictionary *parameter in dictionary[@"parameters"]) { + [parameters addObject:[[MPPropertySelectorParameterDescription alloc] initWithDictionary:parameter]]; + } + + _parameters = [parameters copy]; + _returnType = [dictionary[@"result"][@"type"] copy]; // optional + } + + return self; +} + +@end + +@interface MPPropertyDescription () + +@property (nonatomic, readonly) NSPredicate *predicate; + +@end + +@implementation MPPropertyDescription + ++ (NSValueTransformer *)valueTransformerForType:(NSString *)typeName +{ + // TODO: lookup transformer by type + + for (NSString *toTypeName in @[@"NSDictionary", @"NSNumber", @"NSString"]) { + NSString *toTransformerName = [NSString stringWithFormat:@"MP%@To%@ValueTransformer", typeName, toTypeName]; + NSValueTransformer *toTransformer = [NSValueTransformer valueTransformerForName:toTransformerName]; + if (toTransformer) { + return toTransformer; + } + } + + // Default to pass-through. + return [NSValueTransformer valueTransformerForName:@"MPPassThroughValueTransformer"]; +} + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + NSParameterAssert(dictionary[@"name"] != nil); + + self = [super init]; + if (self) { + _name = [dictionary[@"name"] copy]; // required + _useInstanceVariableAccess = [dictionary[@"use_ivar"] boolValue]; // Optional + _readonly = [dictionary[@"readonly"] boolValue]; // Optional + _nofollow = [dictionary[@"nofollow"] boolValue]; // Optional + + NSString *predicateFormat = dictionary[@"predicate"]; // Optional + if (predicateFormat) { + _predicate = [NSPredicate predicateWithFormat:predicateFormat]; + } + + NSDictionary *get = dictionary[@"get"]; + if (get == nil) { + NSParameterAssert(dictionary[@"type"] != nil); + get = @{ + @"selector": _name, + @"result": @{ + @"type": dictionary[@"type"], + @"name": @"value" + }, + @"parameters": @[] + }; + } + + NSDictionary *set = dictionary[@"set"]; + if (set == nil && _readonly == NO) { + NSParameterAssert(dictionary[@"type"] != nil); + set = @{ + @"selector": [NSString stringWithFormat:@"set%@:", _name.capitalizedString], + @"parameters": @[ + @{ + @"name": @"value", + @"type": dictionary[@"type"] + } + ] + }; + } + + _getSelectorDescription = [[MPPropertySelectorDescription alloc] initWithDictionary:get]; + if (set) { + _setSelectorDescription = [[MPPropertySelectorDescription alloc] initWithDictionary:set]; + } else { + _readonly = YES; + } + + BOOL useKVC = (dictionary[@"use_kvc"] == nil ? YES : [dictionary[@"use_kvc"] boolValue]) && _useInstanceVariableAccess == NO; + _useKeyValueCoding = useKVC && + _getSelectorDescription.parameters.count == 0 && + (_setSelectorDescription == nil || _setSelectorDescription.parameters.count == 1); + } + + return self; +} + +- (NSString *)type +{ + return _getSelectorDescription.returnType; +} + +- (NSValueTransformer *)valueTransformer +{ + return [[self class] valueTransformerForType:self.type]; +} + +- (NSString *)debugDescription +{ + return [NSString stringWithFormat:@"<%@:%p name='%@' type='%@' %@>", NSStringFromClass([self class]), (__bridge void *)self, self.name, self.type, self.readonly ? @"readonly" : @""]; +} + +- (BOOL)shouldReadPropertyValueForObject:(NSObject *)object +{ + if (_nofollow) { + return NO; + } + if (_predicate) { + return [_predicate evaluateWithObject:object]; + } + + return YES; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPResources.h b/ios/Pods/Mixpanel/Mixpanel/MPResources.h new file mode 100644 index 0000000..29763de --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPResources.h @@ -0,0 +1,20 @@ +// +// MPResources.h +// Mixpanel +// +// Created by Sam Green on 5/2/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import +#import + +#import "Mixpanel.h" + +@interface MPResources : NSObject + ++ (NSBundle *)frameworkBundle; ++ (NSString *)notificationXibName; ++ (UIImage *)imageNamed:(NSString *)name; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPResources.m b/ios/Pods/Mixpanel/Mixpanel/MPResources.m new file mode 100644 index 0000000..094d677 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPResources.m @@ -0,0 +1,43 @@ +// +// MPResources.m +// Mixpanel +// +// Created by Sam Green on 5/2/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "MPResources.h" + +@implementation MPResources + ++ (UIStoryboard *)storyboardWithName:(NSString *)name { + return [UIStoryboard storyboardWithName:name bundle:[MPResources frameworkBundle]]; +} + ++ (NSBundle *)frameworkBundle { + return [NSBundle bundleForClass:self.class]; +} + ++ (NSString *)notificationXibName { + NSMutableString *xibFileName = [NSMutableString stringWithString:@"MPTakeoverNotificationViewController"]; + + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + BOOL isLandscape = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation); + if (isLandscape) { + [xibFileName appendString:@"~iphonelandscape"]; + } else { + [xibFileName appendString:@"~iphoneportrait"]; + } + } else { + [xibFileName appendString:@"~ipad"]; + } + + return [xibFileName copy]; +} + ++ (UIImage *)imageNamed:(NSString *)name { + NSString *imagePath = [[MPResources frameworkBundle] pathForResource:name ofType:@"png"]; + return [UIImage imageWithContentsOfFile:imagePath]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPSequenceGenerator.h b/ios/Pods/Mixpanel/Mixpanel/MPSequenceGenerator.h new file mode 100644 index 0000000..b80f30c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPSequenceGenerator.h @@ -0,0 +1,10 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@interface MPSequenceGenerator : NSObject + +- (int32_t)nextValue; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPSequenceGenerator.m b/ios/Pods/Mixpanel/Mixpanel/MPSequenceGenerator.m new file mode 100644 index 0000000..c7ae870 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPSequenceGenerator.m @@ -0,0 +1,33 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPSequenceGenerator.h" + +@implementation MPSequenceGenerator + +{ + int32_t _value; +} + +- (instancetype)init +{ + return [self initWithInitialValue:0]; +} + +- (instancetype)initWithInitialValue:(int32_t)initialValue +{ + self = [super init]; + if (self) { + _value = initialValue; + } + + return self; +} + +- (int32_t)nextValue +{ + return OSAtomicAdd32(1, &_value); +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPSwizzle.h b/ios/Pods/Mixpanel/Mixpanel/MPSwizzle.h new file mode 100755 index 0000000..2cf0809 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPSwizzle.h @@ -0,0 +1,20 @@ +// JRSwizzle.h semver:1.0 +// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/MIT +// https://github.com/rentzsch/jrswizzle +// +// Methods and Category have been renamed to namespace to Mixpanel iOS SDK +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSObject (MPSwizzle) + ++ (BOOL)mp_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError **)error_; ++ (BOOL)mp_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError **)error_; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Mixpanel/Mixpanel/MPSwizzle.m b/ios/Pods/Mixpanel/Mixpanel/MPSwizzle.m new file mode 100755 index 0000000..a545434 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPSwizzle.m @@ -0,0 +1,134 @@ +// JRSwizzle.m semver:1.0 +// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/MIT +// https://github.com/rentzsch/jrswizzle + +#import "MPSwizzle.h" + +#if TARGET_OS_IPHONE + #import + #import +#else + #import +#endif + +#define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \ + if (ERROR_VAR) { \ + NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \ + *ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \ + code:-1 \ + userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \ + } +#define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__) + +#if OBJC_API_VERSION >= 2 +#define GetClass(obj) object_getClass(obj) +#else +#define GetClass(obj) (obj ? obj->isa : Nil) +#endif + +@implementation NSObject (MPSwizzle) + ++ (BOOL)mp_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ { +#if OBJC_API_VERSION >= 2 + Method origMethod = class_getInstanceMethod(self, origSel_); + if (!origMethod) { +#if TARGET_OS_IPHONE + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]); +#else + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); +#endif + return NO; + } + + Method altMethod = class_getInstanceMethod(self, altSel_); + if (!altMethod) { +#if TARGET_OS_IPHONE + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]); +#else + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); +#endif + return NO; + } + + class_addMethod(self, + origSel_, + class_getMethodImplementation(self, origSel_), + method_getTypeEncoding(origMethod)); + class_addMethod(self, + altSel_, + class_getMethodImplementation(self, altSel_), + method_getTypeEncoding(altMethod)); + + method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_)); + return YES; +#else + // Scan for non-inherited methods. + Method directOriginalMethod = NULL, directAlternateMethod = NULL; + + void *iterator = NULL; + struct objc_method_list *mlist = class_nextMethodList(self, &iterator); + while (mlist) { + int method_index = 0; + for (; method_index < mlist->method_count; method_index++) { + if (mlist->method_list[method_index].method_name == origSel_) { + assert(!directOriginalMethod); + directOriginalMethod = &mlist->method_list[method_index]; + } + if (mlist->method_list[method_index].method_name == altSel_) { + assert(!directAlternateMethod); + directAlternateMethod = &mlist->method_list[method_index]; + } + } + mlist = class_nextMethodList(self, &iterator); + } + + // If either method is inherited, copy it up to the target class to make it non-inherited. + if (!directOriginalMethod || !directAlternateMethod) { + Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL; + if (!directOriginalMethod) { + inheritedOriginalMethod = class_getInstanceMethod(self, origSel_); + if (!inheritedOriginalMethod) { + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); + return NO; + } + } + if (!directAlternateMethod) { + inheritedAlternateMethod = class_getInstanceMethod(self, altSel_); + if (!inheritedAlternateMethod) { + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); + return NO; + } + } + + int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1; + struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1))); + hoisted_method_list->obsolete = NULL; // soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind + hoisted_method_list->method_count = hoisted_method_count; + Method hoisted_method = hoisted_method_list->method_list; + + if (!directOriginalMethod) { + bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method)); + directOriginalMethod = hoisted_method++; + } + if (!directAlternateMethod) { + bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method)); + directAlternateMethod = hoisted_method; + } + class_addMethods(self, hoisted_method_list); + } + + // Swizzle. + IMP temp = directOriginalMethod->method_imp; + directOriginalMethod->method_imp = directAlternateMethod->method_imp; + directAlternateMethod->method_imp = temp; + + return YES; +#endif +} + ++ (BOOL)mp_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ { + return [GetClass((id)self) mp_swizzleMethod:origSel_ withMethod:altSel_ error:error_]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPSwizzler.h b/ios/Pods/Mixpanel/Mixpanel/MPSwizzler.h new file mode 100644 index 0000000..a0ea5fa --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPSwizzler.h @@ -0,0 +1,22 @@ +// +// MPSwizzler.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 1/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import + +// Cast to turn things that are not ids into NSMapTable keys +#define MAPTABLE_ID(x) (__bridge id)((void *)x) + +typedef void (^swizzleBlock)(); + +@interface MPSwizzler : NSObject + ++ (void)swizzleSelector:(SEL)aSelector onClass:(Class)aClass withBlock:(swizzleBlock)block named:(NSString *)aName; ++ (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass named:(NSString *)aName; ++ (void)printSwizzles; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPSwizzler.m b/ios/Pods/Mixpanel/Mixpanel/MPSwizzler.m new file mode 100644 index 0000000..cb66d1e --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPSwizzler.m @@ -0,0 +1,266 @@ +// +// MPSwizzler.m +// HelloMixpanel +// +// Created by Alex Hofsteede on 1/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import +#import "MPLogger.h" +#import "MPSwizzler.h" + +#define MIN_ARGS 2 +#define MAX_ARGS 5 + +@interface MPSwizzle : NSObject + +@property (nonatomic, assign) Class class; +@property (nonatomic, assign) SEL selector; +@property (nonatomic, assign) IMP originalMethod; +@property (nonatomic, assign) uint numArgs; +@property (nonatomic, copy) NSMapTable *blocks; + +- (instancetype)initWithBlock:(swizzleBlock)aBlock + named:(NSString *)aName + forClass:(Class)aClass + selector:(SEL)aSelector + originalMethod:(IMP)aMethod + withNumArgs:(uint)numArgs; + +@end + +static NSMapTable *swizzles; + +static void mp_swizzledMethod_2(id self, SEL _cmd) +{ + Method aMethod = class_getInstanceMethod([self class], _cmd); + MPSwizzle *swizzle = (MPSwizzle *)[swizzles objectForKey:MAPTABLE_ID(aMethod)]; + if (swizzle) { + ((void(*)(id, SEL))swizzle.originalMethod)(self, _cmd); + + NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; + swizzleBlock block; + while ((block = [blocks nextObject])) { + block(self, _cmd); + } + } +} + +static void mp_swizzledMethod_3(id self, SEL _cmd, id arg) +{ + Method aMethod = class_getInstanceMethod([self class], _cmd); + MPSwizzle *swizzle = (MPSwizzle *)[swizzles objectForKey:MAPTABLE_ID(aMethod)]; + if (swizzle) { + ((void(*)(id, SEL, id))swizzle.originalMethod)(self, _cmd, arg); + + NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; + swizzleBlock block; + while ((block = [blocks nextObject])) { + block(self, _cmd, arg); + } + } +} + +static void mp_swizzledMethod_4(id self, SEL _cmd, id arg, id arg2) +{ + Method aMethod = class_getInstanceMethod([self class], _cmd); + MPSwizzle *swizzle = (MPSwizzle *)[swizzles objectForKey:(__bridge id)((void *)aMethod)]; + if (swizzle) { + ((void(*)(id, SEL, id, id))swizzle.originalMethod)(self, _cmd, arg, arg2); + + NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; + swizzleBlock block; + while ((block = [blocks nextObject])) { + block(self, _cmd, arg, arg2); + } + } +} + +static void mp_swizzledMethod_5(id self, SEL _cmd, id arg, id arg2, id arg3) +{ + Method aMethod = class_getInstanceMethod([self class], _cmd); + MPSwizzle *swizzle = (MPSwizzle *)[swizzles objectForKey:(__bridge id)((void *)aMethod)]; + if (swizzle) { + ((void(*)(id, SEL, id, id, id))swizzle.originalMethod)(self, _cmd, arg, arg2, arg3); + + NSEnumerator *blocks = [swizzle.blocks objectEnumerator]; + swizzleBlock block; + while ((block = [blocks nextObject])) { + block(self, _cmd, arg, arg2, arg3); + } + } +} + +static void (*mp_swizzledMethods[MAX_ARGS - MIN_ARGS + 1])() = {mp_swizzledMethod_2, mp_swizzledMethod_3, mp_swizzledMethod_4, mp_swizzledMethod_5}; + +@implementation MPSwizzler + ++ (void)load +{ + swizzles = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) + valueOptions:(NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality)]; +} + ++ (void)printSwizzles +{ + NSEnumerator *en = [swizzles objectEnumerator]; + MPSwizzle *swizzle; + while ((swizzle = (MPSwizzle *)[en nextObject])) { + MPLogError(@"%@", swizzle); + } +} + ++ (MPSwizzle *)swizzleForMethod:(Method)aMethod +{ + return (MPSwizzle *)[swizzles objectForKey:MAPTABLE_ID(aMethod)]; +} + ++ (void)removeSwizzleForMethod:(Method)aMethod +{ + [swizzles removeObjectForKey:MAPTABLE_ID(aMethod)]; +} + ++ (void)setSwizzle:(MPSwizzle *)swizzle forMethod:(Method)aMethod +{ + [swizzles setObject:swizzle forKey:MAPTABLE_ID(aMethod)]; +} + ++ (BOOL)isLocallyDefinedMethod:(Method)aMethod onClass:(Class)aClass +{ + uint count; + BOOL isLocal = NO; + Method *methods = class_copyMethodList(aClass, &count); + for (NSUInteger i = 0; i < count; i++) { + if (aMethod == methods[i]) { + isLocal = YES; + break; + } + } + free(methods); + return isLocal; +} + ++ (void)swizzleSelector:(SEL)aSelector onClass:(Class)aClass withBlock:(swizzleBlock)aBlock named:(NSString *)aName +{ + Method aMethod = class_getInstanceMethod(aClass, aSelector); + if (aMethod) { + uint numArgs = method_getNumberOfArguments(aMethod); + if (numArgs >= MIN_ARGS && numArgs <= MAX_ARGS) { + + BOOL isLocal = [self isLocallyDefinedMethod:aMethod onClass:aClass]; + IMP swizzledMethod = (IMP)mp_swizzledMethods[numArgs - 2]; + MPSwizzle *swizzle = [self swizzleForMethod:aMethod]; + + if (isLocal) { + if (!swizzle) { + IMP originalMethod = method_getImplementation(aMethod); + + // Replace the local implementation of this method with the swizzled one + method_setImplementation(aMethod,swizzledMethod); + + // Create and add the swizzle + swizzle = [[MPSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod withNumArgs:numArgs]; + [self setSwizzle:swizzle forMethod:aMethod]; + + } else { + [swizzle.blocks setObject:aBlock forKey:aName]; + } + } else { + IMP originalMethod = swizzle ? swizzle.originalMethod : method_getImplementation(aMethod); + + // Add the swizzle as a new local method on the class. + if (!class_addMethod(aClass, aSelector, swizzledMethod, method_getTypeEncoding(aMethod))) { + NSAssert(NO, @"SwizzlerAssert: Could not add swizzled for %@::%@, even though it didn't already exist locally", NSStringFromClass(aClass), NSStringFromSelector(aSelector)); + return; + } + // Now re-get the Method, it should be the one we just added. + Method newMethod = class_getInstanceMethod(aClass, aSelector); + if (aMethod == newMethod) { + NSAssert(NO, @"SwizzlerAssert: Newly added method for %@::%@ was the same as the old method", NSStringFromClass(aClass), NSStringFromSelector(aSelector)); + return; + } + + MPSwizzle *newSwizzle = [[MPSwizzle alloc] initWithBlock:aBlock named:aName forClass:aClass selector:aSelector originalMethod:originalMethod withNumArgs:numArgs]; + [self setSwizzle:newSwizzle forMethod:newMethod]; + } + } else { + NSAssert(NO, @"SwizzlerAssert: Cannot swizzle method with %d args", numArgs); + } + } else { + NSAssert(NO, @"SwizzlerAssert: Cannot find method for %@ on %@", NSStringFromSelector(aSelector), NSStringFromClass(aClass)); + } +} + ++ (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass +{ + Method aMethod = class_getInstanceMethod(aClass, aSelector); + MPSwizzle *swizzle = [self swizzleForMethod:aMethod]; + if (swizzle) { + method_setImplementation(aMethod, swizzle.originalMethod); + [self removeSwizzleForMethod:aMethod]; + } +} + +/* + Remove the named swizzle from the given class/selector. If aName is nil, remove all + swizzles for this class/selector +*/ ++ (void)unswizzleSelector:(SEL)aSelector onClass:(Class)aClass named:(NSString *)aName +{ + Method aMethod = class_getInstanceMethod(aClass, aSelector); + MPSwizzle *swizzle = [self swizzleForMethod:aMethod]; + if (swizzle) { + if (aName) { + [swizzle.blocks removeObjectForKey:aName]; + } + if (!aName || swizzle.blocks.count == 0) { + method_setImplementation(aMethod, swizzle.originalMethod); + [self removeSwizzleForMethod:aMethod]; + } + } +} + +@end + + +@implementation MPSwizzle + +- (instancetype)init +{ + if ((self = [super init])) { + self.blocks = [NSMapTable mapTableWithKeyOptions:(NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality) + valueOptions:(NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality)]; + } + return self; +} + +- (instancetype)initWithBlock:(swizzleBlock)aBlock + named:(NSString *)aName + forClass:(Class)aClass + selector:(SEL)aSelector + originalMethod:(IMP)aMethod + withNumArgs:(uint)numArgs +{ + if ((self = [self init])) { + self.class = aClass; + self.selector = aSelector; + self.numArgs = numArgs; + self.originalMethod = aMethod; + [self.blocks setObject:aBlock forKey:aName]; + } + return self; +} + +- (NSString *)description +{ + NSString *descriptors = @""; + NSString *key; + NSEnumerator *keys = [self.blocks keyEnumerator]; + while ((key = [keys nextObject])) { + descriptors = [descriptors stringByAppendingFormat:@"\t%@ : %@\n", key, [self.blocks objectForKey:key]]; + } + return [NSString stringWithFormat:@"Swizzle on %@::%@ [\n%@]", NSStringFromClass(self.class), NSStringFromSelector(self.selector), descriptors]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotification.h b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotification.h new file mode 100644 index 0000000..99c9fc6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotification.h @@ -0,0 +1,20 @@ +// +// MPTakeoverNotification.h +// Mixpanel +// +// Created by Sergio Alonso on 1/24/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import "MPNotification.h" +#import "MPNotificationButton.h" + +@interface MPTakeoverNotification : MPNotification + +@property (nonatomic, copy) NSString *title; +@property (nonatomic) NSUInteger titleColor; +@property (nonatomic) NSUInteger closeButtonColor; +@property (nonatomic) BOOL shouldFadeImage; +@property (nonatomic, copy) NSArray *buttons; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotification.m b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotification.m new file mode 100644 index 0000000..afbb569 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotification.m @@ -0,0 +1,68 @@ +// +// MPTakeoverNotification.m +// Mixpanel +// +// Created by Sergio Alonso on 1/24/17. +// Copyright © 2017 Mixpanel. All rights reserved. +// + +#import "MPTakeoverNotification.h" + +@implementation MPTakeoverNotification + +- (instancetype)initWithJSONObject:(NSDictionary *)jsonObject { + if (self = [super initWithJSONObject:jsonObject]) { + NSString *title = jsonObject[@"title"]; + if ([title isEqual:[NSNull null]]) { + title = nil; + } + + NSNumber *titleColor = jsonObject[@"title_color"]; + if (!([titleColor isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@"title color" withValue:titleColor]; + return nil; + } + + NSNumber *closeButton = jsonObject[@"close_color"]; + if (!([closeButton isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@"close color" withValue:closeButton]; + return nil; + } + + NSArray *buttonsJson = jsonObject[@"buttons"]; + NSMutableArray *buttons = [NSMutableArray array]; + for (NSDictionary *buttonJson in buttonsJson) { + MPNotificationButton *notificationButton = [[MPNotificationButton alloc] initWithJSONObject:buttonJson]; + if (notificationButton) { + [buttons addObject:notificationButton]; + } else { + return nil; + } + } + + if (buttons.count == 0) { + [MPNotification logNotificationError:@"buttons" withValue:@"not enough (0)"]; + return nil; + } + + NSNumber *shouldFadeIamge = self.extrasDescription[@"image_fade"]; + if (!([shouldFadeIamge isKindOfClass:[NSNumber class]])) { + [MPNotification logNotificationError:@"image fade" withValue:shouldFadeIamge]; + return nil; + } + + self.title = title; + self.titleColor = titleColor.unsignedIntegerValue; + self.closeButtonColor = closeButton.unsignedIntegerValue; + self.buttons = [buttons copy]; + self.shouldFadeImage = [shouldFadeIamge boolValue]; + } + + return self; +} + +- (NSString *)type { + return MPNotificationTypeTakeover; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~ipad.xib b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~ipad.xib new file mode 100644 index 0000000..bc4030f --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~ipad.xib @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphonelandscape.xib b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphonelandscape.xib new file mode 100644 index 0000000..09242dc --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphonelandscape.xib @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphoneportrait.xib b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphoneportrait.xib new file mode 100644 index 0000000..b9d9eb6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphoneportrait.xib @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTweak.h b/ios/Pods/Mixpanel/Mixpanel/MPTweak.h new file mode 100644 index 0000000..51c391c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTweak.h @@ -0,0 +1,99 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol MPTweakObserver; + +/** + @abstract Represents a possible value of a tweak. + @discussion Should be able to be persisted in user defaults. + For minimum and maximum values, should implement -compare:. + */ +typedef id MPTweakValue; + +/** + @abstract Represents a tweak + @discussion A tweak contains a persistent, editable value. + */ +@interface MPTweak : NSObject + +/** + @abstract Creates a new tweak model. + @discussion This is the designated initializer. + */ +- (instancetype)initWithName:(NSString *)name andEncoding:(NSString *)encoding; + +/** + @abstract This tweak's unique name. + @discussion Used when reading and writing the tweak's value. + */ +@property (nonatomic, copy, readonly) NSString *name; + +/** + @abstract This tweak's value encoding, as returned by `@encoding` + */ +@property (nonatomic, copy, readonly) NSString *encoding; + +/** + @abstract The default value of the tweak. + @discussion Use this when the current value is unset. + */ +@property (nonatomic, strong, readwrite) MPTweakValue defaultValue; + +/** + @abstract The current value of the tweak. Can be nil. + @discussion Changes to this property will be propagated to disk. + */ +@property (nullable, nonatomic, strong, readwrite) MPTweakValue currentValue; + +/** + @abstract The minimum value of the tweak. + @discussion Optional. If nil, there is no minimum. + */ +@property (nonatomic, strong, readwrite) MPTweakValue minimumValue; + +/** + @abstract The maximum value of the tweak. + @discussion Optional. If nil, there is no maximum. + */ +@property (nonatomic, strong, readwrite) MPTweakValue maximumValue; + +/** + @abstract Adds an observer to the tweak. + @param observer The observer. Must not be nil. + @discussion A weak reference is taken on the observer. + */ +- (void)addObserver:(id)observer; + +/** + @abstract Removes an observer from the tweak. + @param observer The observer to remove. Must not be nil. + @discussion Optional, removing an observer isn't required. + */ +- (void)removeObserver:(id)observer; + +@end + +/** + @abstract Responds to updates when a tweak changes. + */ +@protocol MPTweakObserver + +/** + @abstract Called when a tweak's value changes. + @param tweak The tweak which changed in value. + */ +- (void)tweakDidChange:(MPTweak *)tweak; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTweak.m b/ios/Pods/Mixpanel/Mixpanel/MPTweak.m new file mode 100644 index 0000000..4b19acd --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTweak.m @@ -0,0 +1,62 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "MPTweak.h" + +@implementation MPTweak { + NSHashTable *_observers; +} + +- (instancetype)initWithName:(NSString *)name andEncoding:(NSString *)encoding +{ + if ((self = [super init])) { + _name = name; + _encoding = encoding; + } + return self; +} + +- (void)setCurrentValue:(MPTweakValue)currentValue +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-selector-match" + if (_minimumValue != nil && currentValue != nil && [_minimumValue compare:currentValue] == NSOrderedDescending) { + currentValue = _minimumValue; + } + + if (_maximumValue != nil && currentValue != nil && [_maximumValue compare:currentValue] == NSOrderedAscending) { + currentValue = _maximumValue; + } +#pragma clang diagnostic pop + + if (_currentValue != currentValue) { + _currentValue = currentValue; + for (id observer in [_observers setRepresentation]) { + [observer tweakDidChange:self]; + } + } +} + +- (void)addObserver:(id)observer +{ + if (_observers == nil) { + _observers = [NSHashTable weakObjectsHashTable]; + } + + NSAssert(observer != nil, @"observer is required"); + [_observers addObject:observer]; +} + +- (void)removeObserver:(id)observer +{ + NSAssert(observer != nil, @"observer is required"); + [_observers removeObject:observer]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTweakInline.h b/ios/Pods/Mixpanel/Mixpanel/MPTweakInline.h new file mode 100644 index 0000000..318fdbe --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTweakInline.h @@ -0,0 +1,40 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "MPTweakInlineInternal.h" + +/*! + @abstract Loads the value of a tweak inline. + @discussion To use a tweak, use this instead of the constant value you otherwise would. + To use the same tweak in two places, define a C function that returns MPTweakValue. + @param name_ The name of the tweak. Must be a constant NSString. + @param default_ The default value of the tweak. If the user doesn't configure + a custom value or the build is a release build, then the default value is used. + The default value supports a variety of types, but all must be constant literals. + Supported types include: BOOL, NSInteger, NSUInteger, CGFloat, NSString *, char *. + @param min_ Optional, for numbers. The minimum value. Same restrictions as default. + @param max_ Optional, for numbers. The maximum value. Same restrictions as default. + @return The current value of the tweak, or the default value if none is set. + */ +#define MPTweakValue(name_, ...) _MPTweakValue(name_, __VA_ARGS__) + +/*! + @abstract Binds an object property to a tweak. + @param object_ The object to bind to. + @param property_ The property to bind. + @param name_ The name of the tweak. Must be a constant NSString. + @param default_ The default value of the tweak. If the user doesn't configure + a custom value or the build is a release build, then the default value is used. + The default value supports a variety of types, but all must be constant literals. + Supported types include: BOOL, NSInteger, NSUInteger, CGFloat, NSString *, char *. + @param min_ Optional, for numbers. The minimum value. Same restrictions as default. + @param max_ Optional, for numbers. The maximum value. Same restrictions as default. + @discussion As long as the object is alive, the property will be updated to match the tweak. + */ +#define MPTweakBind(object_, property_, name_, ...) _MPTweakBind(object_, property_, name_, __VA_ARGS__) diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTweakInline.m b/ios/Pods/Mixpanel/Mixpanel/MPTweakInline.m new file mode 100644 index 0000000..6b6da17 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTweakInline.m @@ -0,0 +1,145 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import +#import +#import +#import "MPTweak.h" +#import "MPTweakInline.h" +#import "MPTweakStore.h" + +static MPTweak *_MPTweakCreateWithEntry(NSString *name, mp_tweak_entry *entry) +{ + NSString *encoding = [NSString stringWithFormat:@"%s", *entry->encoding]; + MPTweak *tweak = [[MPTweak alloc] initWithName:name andEncoding:encoding]; + + if (strcmp(*entry->encoding, @encode(BOOL)) == 0) { + tweak.defaultValue = @(*(BOOL *)entry->value); + } else if (strcmp(*entry->encoding, @encode(float)) == 0) { + tweak.defaultValue = @(*(float *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(float *)entry->min); + tweak.maximumValue = @(*(float *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(double)) == 0) { + tweak.defaultValue = @(*(double *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(double *)entry->min); + tweak.maximumValue = @(*(double *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(short)) == 0) { + tweak.defaultValue = @(*(short *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(short *)entry->min); + tweak.maximumValue = @(*(short *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(unsigned short)) == 0) { + tweak.defaultValue = @(*(unsigned short int *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(unsigned short *)entry->min); + tweak.maximumValue = @(*(unsigned short *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(int)) == 0) { + tweak.defaultValue = @(*(int *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(int *)entry->min); + tweak.maximumValue = @(*(int *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(uint)) == 0) { + tweak.defaultValue = @(*(uint *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(uint *)entry->min); + tweak.maximumValue = @(*(uint *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(long)) == 0) { + tweak.defaultValue = @(*(long *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(long *)entry->min); + tweak.maximumValue = @(*(long *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(unsigned long)) == 0) { + tweak.defaultValue = @(*(unsigned long *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(unsigned long *)entry->min); + tweak.maximumValue = @(*(unsigned long *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(long long)) == 0) { + tweak.defaultValue = @(*(long long *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(long long *)entry->min); + tweak.maximumValue = @(*(long long *)entry->max); + } + } else if (strcmp(*entry->encoding, @encode(unsigned long long)) == 0) { + tweak.defaultValue = @(*(unsigned long long *)entry->value); + if (entry->min != NULL && entry->max != NULL) { + tweak.minimumValue = @(*(unsigned long long *)entry->min); + tweak.maximumValue = @(*(unsigned long long *)entry->max); + } + } else if (*entry->encoding[0] == '[') { + // Assume it's a C string. + tweak.defaultValue = [NSString stringWithUTF8String:entry->value]; + } else if (strcmp(*entry->encoding, @encode(id)) == 0) { + tweak.defaultValue = *((__unsafe_unretained id *)entry->value); + } else { + NSCAssert(NO, @"Unknown encoding %s for tweak %@. Value was %p.", *entry->encoding, *entry->name, entry->value); + tweak = nil; + } + + return tweak; +} + +@interface _MPTweakInlineLoader : NSObject + +@end + +@implementation _MPTweakInlineLoader + ++ (void)load { + static uint32_t _tweaksLoaded = 0; + if (OSAtomicTestAndSetBarrier(1, &_tweaksLoaded)) { + return; + } + +#ifdef __LP64__ + typedef struct mach_header_64 mp_tweak_header; +#else + typedef struct mach_header mp_tweak_header; +#endif + + MPTweakStore *store = [MPTweakStore sharedInstance]; + + Dl_info info; + dladdr((void *)&_MPTweakCreateWithEntry, &info); + + uint32_t image_count = _dyld_image_count(); + for (uint32_t image_index = 0; image_index < image_count; image_index++) { + const mp_tweak_header *mach_header = (const mp_tweak_header *)_dyld_get_image_header(image_index); + + unsigned long size; + mp_tweak_entry *data = (mp_tweak_entry *)getsectiondata(mach_header, MPTweakSegmentName, MPTweakSectionName, &size); + if (data == NULL) { + continue; + } + size_t count = size / sizeof(mp_tweak_entry); + for (size_t i = 0; i < count; i++) { + mp_tweak_entry *entry = &data[i]; + NSString *name = [NSString stringWithString:*entry->name]; + if ([store tweakWithName:name] == nil) { + MPTweak *tweak = _MPTweakCreateWithEntry(name, entry); + if (tweak != nil) { + [store addTweak:tweak]; + } + } + } + } +} + +@end + diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTweakInlineInternal.h b/ios/Pods/Mixpanel/Mixpanel/MPTweakInlineInternal.h new file mode 100644 index 0000000..6e5e569 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTweakInlineInternal.h @@ -0,0 +1,133 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "_MPTweakBindObserver.h" +#import "MPTweak.h" +#import "MPTweakStore.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MPTweakSegmentName "__DATA" +#define MPTweakSectionName "MPTweak" + +typedef __unsafe_unretained NSString *MPTweakLiteralString; + +typedef struct { + MPTweakLiteralString *name; + void *value; + void *min; + void *max; + char **encoding; +} mp_tweak_entry; + +#if __has_feature(objc_arc) +#define _MPTweakRelease(x) +#else +#define _MPTweakRelease(x) [x release] +#endif + +#define __MPTweakConcat_(X, Y) X ## Y +#define __MPTweakConcat(X, Y) __MPTweakConcat_(X, Y) + +#define __MPTweakIndex(_1, _2, _3, value, ...) value +#define __MPTweakIndexCount(...) __MPTweakIndex(__VA_ARGS__, 3, 2, 1) + +#define __MPTweakHasRange1(__withoutRange, __withRange, ...) __withoutRange +#define __MPTweakHasRange2(__withoutRange, __withRange, ...) __MPTweakInvalidNumberOfArgumentsPassed +#define __MPTweakHasRange3(__withoutRange, __withRange, ...) __withRange +#define _MPTweakHasRange(__withoutRange, __withRange, ...) __MPTweakConcat(__MPTweakHasRange, __MPTweakIndexCount(__VA_ARGS__))(__withoutRange, __withRange) + +#define _MPTweakInlineWithoutRange(name_, default_) \ + _MPTweakInlineWithRangeInternal(name_, default_, NO, NULL, NO, NULL) +#define _MPTweakInlineWithRange(name_, default_, min_, max_) \ + _MPTweakInlineWithRangeInternal(name_, default_, YES, min_, YES, max_) +#define _MPTweakInlineWithRangeInternal(name_, default_, hasmin_, min_, hasmax_, max_) \ +((^{ \ + /* store the tweak data in the binary at compile time. */ \ + __attribute__((used)) static MPTweakLiteralString name__ = name_; \ + __attribute__((used)) static __typeof__(default_) default__ = default_; \ + __attribute__((used)) static __typeof__(min_) min__ = min_; \ + __attribute__((used)) static __typeof__(max_) max__ = max_; \ + __attribute__((used)) static char *encoding__ = (char *)@encode(__typeof__(default_)); \ + __attribute__((used)) __attribute__((section (MPTweakSegmentName "," MPTweakSectionName))) static mp_tweak_entry entry = \ + {&name__, &default__, hasmin_ ? &min__ : NULL, hasmax_ ? &max__ : NULL, &encoding__ }; \ +\ + /* find the registered tweak with the given name. */ \ + return [[MPTweakStore sharedInstance] tweakWithName:[NSString stringWithString:*entry.name]]; \ +})()) +#define _MPTweakInline(name_, ...) _MPTweakHasRange(_MPTweakInlineWithoutRange, _MPTweakInlineWithRange, __VA_ARGS__)(name_, __VA_ARGS__) + +#define _MPTweakValueInternal(tweak_, name_, default_, hasmin_, min_, hasmax_, max_) \ +((^{ \ + /* returns a correctly typed version of the current tweak value */ \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wc11-extensions\"") \ + MPTweakValue currentValue = tweak_.currentValue ?: tweak_.defaultValue; \ + return _Generic(default_, \ + float: [currentValue floatValue], \ + const float: [currentValue floatValue], \ + double: [currentValue doubleValue], \ + const double: [currentValue doubleValue], \ + short: [currentValue shortValue], \ + const short: [currentValue shortValue], \ + unsigned short: [currentValue unsignedShortValue], \ + const unsigned short: [currentValue unsignedShortValue], \ + int: [currentValue intValue], \ + const int: [currentValue intValue], \ + unsigned int: [currentValue unsignedIntValue], \ + const unsigned int: [currentValue unsignedIntValue], \ + long long: [currentValue longLongValue], \ + const long long: [currentValue longLongValue], \ + unsigned long long: [currentValue unsignedLongLongValue], \ + const unsigned long long: [currentValue unsignedLongLongValue], \ + BOOL: [currentValue boolValue], \ + const BOOL: [currentValue boolValue], \ + id: currentValue, \ + const id: currentValue, \ + /* assume char * as the default. */ \ + /* constant strings are typed as char[N] */ \ + /* and we can't enumerate all of those. */ \ + /* luckily, we only need one fallback */ \ + default: [currentValue UTF8String] \ + ); \ + _Pragma("clang diagnostic pop") \ +})()) + +#define _MPTweakValueWithoutRange(name_, default_) _MPTweakValueWithRangeInternal(name_, default_, NO, NULL, NO, NULL) +#define _MPTweakValueWithRange(name_, default_, min_, max_) _MPTweakValueWithRangeInternal(name_, default_, YES, min_, YES, max_) +#define _MPTweakValueWithRangeInternal(name_, default_, hasmin_, min_, hasmax_, max_) \ +((^{ \ + MPTweak *tweak = _MPTweakInlineWithRangeInternal(name_, default_, hasmin_, min_, hasmax_, max_); \ + return _MPTweakValueInternal(tweak, name_, default_, hasmin_, min_, hasmax_, max_); \ +})()) +#define _MPTweakValue(name_, ...) _MPTweakHasRange(_MPTweakValueWithoutRange, _MPTweakValueWithRange, __VA_ARGS__)(name_, __VA_ARGS__) + +#define _MPTweakBindWithoutRange(object_, property_, name_, default_) \ + _MPTweakBindWithRangeInternal(object_, property_, name_, default_, NO, NULL, NO, NULL) +#define _MPTweakBindWithRange(object_, property_, name_, default_, min_, max_) \ + _MPTweakBindWithRangeInternal(object_, property_, name_, default_, YES, min_, YES, max_) +#define _MPTweakBindWithRangeInternal(object_, property_, name_, default_, hasmin_, min_, hasmax_, max_) \ +((^{ \ + MPTweak *tweak = _MPTweakInlineWithRangeInternal(name_, default_, hasmin_, min_, hasmax_, max_); \ + object_.property_ = _MPTweakValueInternal(tweak, name_, default_, hasmin_, min_, hasmax_, max_); \ +\ + _MPTweakBindObserver *observer__ = [[_MPTweakBindObserver alloc] initWithTweak:tweak block:^(id object__) { \ + __typeof__(object_) object___ = object__; \ + object___.property_ = _MPTweakValueInternal(tweak, name_, default_, hasmin_, min_, hasmax_, max_); \ + }]; \ + [observer__ attachToObject:object_]; \ +})()) +#define _MPTweakBind(object_, property_, name_, ...) _MPTweakHasRange(_MPTweakBindWithoutRange, _MPTweakBindWithRange, __VA_ARGS__)(object_, property_, name_, __VA_ARGS__) + +#ifdef __cplusplus +} +#endif + diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTweakStore.h b/ios/Pods/Mixpanel/Mixpanel/MPTweakStore.h new file mode 100644 index 0000000..9589aa8 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTweakStore.h @@ -0,0 +1,54 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import "MPTweak.h" + +@class MPTweak; + +/** + @abstract The global store for tweaks. + */ +@interface MPTweakStore : NSObject + +/** + @abstract Creates or returns the shared global store. + */ ++ (instancetype)sharedInstance; + +/** + @abstract The tweak categories in the store. + */ +@property (nonatomic, copy, readonly) NSArray *tweaks; + +/** + @abstract Finds a tweak by name. + @param name The name of the tweak to find. + @return The tweak if found, nil otherwise. + */ +- (MPTweak *)tweakWithName:(NSString *)name; + +/** + @abstract Registers a tweak with the store. + @param tweak The tweak to register. + */ +- (void)addTweak:(MPTweak *)tweak; + +/** + @abstract Removes a tweak from the store. + @param tweak The tweak to remove. + */ +- (void)removeTweak:(MPTweak *)tweak; + +/** + @abstract Resets all tweaks in the store. + */ +- (void)reset; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTweakStore.m b/ios/Pods/Mixpanel/Mixpanel/MPTweakStore.m new file mode 100644 index 0000000..4702c73 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTweakStore.m @@ -0,0 +1,69 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "MPTweak.h" +#import "MPTweakStore.h" + +@implementation MPTweakStore { + NSMutableArray *_orderedTweaks; + NSMutableDictionary *_namedTweaks; +} + ++ (instancetype)sharedInstance +{ + static MPTweakStore *sharedInstance; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + + return sharedInstance; +} + +- (instancetype)init +{ + if ((self = [super init])) { + _orderedTweaks = [NSMutableArray arrayWithCapacity:16]; + _namedTweaks = [NSMutableDictionary dictionaryWithCapacity:16]; + } + + return self; +} + +- (NSArray *)tweaks +{ + return [_orderedTweaks copy]; +} + +- (MPTweak *)tweakWithName:(NSString *)name +{ + return _namedTweaks[name]; +} + +- (void)addTweak:(MPTweak *)tweak +{ + _namedTweaks[tweak.name] = tweak; + [_orderedTweaks addObject:tweak]; +} + +- (void)removeTweak:(MPTweak *)tweak +{ + _namedTweaks[tweak.name] = nil; + [_orderedTweaks removeObject:tweak]; +} + +- (void)reset +{ + for (MPTweak *tweak in self.tweaks) { + tweak.currentValue = nil; + } +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTypeDescription.h b/ios/Pods/Mixpanel/Mixpanel/MPTypeDescription.h new file mode 100644 index 0000000..409a321 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTypeDescription.h @@ -0,0 +1,12 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@interface MPTypeDescription : NSObject + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary; + +@property (nonatomic, readonly) NSString *name; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPTypeDescription.m b/ios/Pods/Mixpanel/Mixpanel/MPTypeDescription.m new file mode 100644 index 0000000..2acbad1 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPTypeDescription.m @@ -0,0 +1,18 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPTypeDescription.h" + +@implementation MPTypeDescription + +- (instancetype)initWithDictionary:(NSDictionary *)dictionary +{ + self = [super init]; + if (self) { + _name = [dictionary[@"name"] copy]; + } + + return self; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUIColorToNSStringValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPUIColorToNSStringValueTransformer.m new file mode 100644 index 0000000..a10da1a --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUIColorToNSStringValueTransformer.m @@ -0,0 +1,73 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPUIColorToNSStringValueTransformer + ++ (Class)transformedValueClass +{ + return [NSString class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value isKindOfClass:[UIColor class]]) { + UIColor *colorValue = (UIColor *)value; + + CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(CGColorGetColorSpace(colorValue.CGColor)); + if (colorSpaceModel == kCGColorSpaceModelMonochrome || colorSpaceModel == kCGColorSpaceModelRGB) { + size_t numberOfComponents = CGColorGetNumberOfComponents(colorValue.CGColor); + const CGFloat *components = CGColorGetComponents(colorValue.CGColor); + + if (colorSpaceModel == kCGColorSpaceModelMonochrome && numberOfComponents >= 1) { + CGFloat w = (255 * components[0]); + CGFloat a = (numberOfComponents > 1 ? components[1] : 1.0f); + + return [NSString stringWithFormat:@"rgba(%.0f, %.0f, %.0f, %.2f)", w, w, w, a]; + } + else if (colorSpaceModel == kCGColorSpaceModelRGB && numberOfComponents >= 3) + { + CGFloat r = (255 * components[0]); + CGFloat g = (255 * components[1]); + CGFloat b = (255 * components[2]); + CGFloat a = (numberOfComponents > 3 ? components[3] : 1.0f); + + return [NSString stringWithFormat:@"rgba(%.0f, %.0f, %.0f, %.2f)", r, g, b, a]; + } + } + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + if ([value isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)value; + + NSScanner *scanner = [NSScanner scannerWithString:stringValue]; + [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@"rgba(), "]]; + [scanner setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]]; + + int r = 0, g = 0, b = 0; + float a = 1.0f; + if ([scanner scanInt:&r] && + [scanner scanInt:&g] && + [scanner scanInt:&b] && + [scanner scanFloat:&a]) + { + UIColor *color = [[UIColor alloc] initWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:a]; + return color; + } + } + + return nil; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUIControlBinding.h b/ios/Pods/Mixpanel/Mixpanel/MPUIControlBinding.h new file mode 100644 index 0000000..3e01886 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUIControlBinding.h @@ -0,0 +1,23 @@ +// +// MPUIControlBinding.h +// HelloMixpanel +// +// Created by Amanda Canyon on 8/4/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import +#import "MPEventBinding.h" + +@interface MPUIControlBinding : MPEventBinding + +@property (nonatomic, readonly) UIControlEvents controlEvent; +@property (nonatomic, readonly) UIControlEvents verifyEvent; + +- (instancetype)init __unavailable; +- (instancetype)initWithEventName:(NSString *)eventName + onPath:(NSString *)path + withControlEvent:(UIControlEvents)controlEvent + andVerifyEvent:(UIControlEvents)verifyEvent; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUIControlBinding.m b/ios/Pods/Mixpanel/Mixpanel/MPUIControlBinding.m new file mode 100644 index 0000000..7ba99d1 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUIControlBinding.m @@ -0,0 +1,259 @@ +// +// MPUIControlBinding.m +// HelloMixpanel +// +// Created by Amanda Canyon on 8/4/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPSwizzler.h" +#import "MPUIControlBinding.h" + +@interface MPUIControlBinding() + +/* + This table contains all the UIControls we are currently bound to + */ +@property (nonatomic, copy) NSHashTable *appliedTo; +/* + A table of all objects that matched the full path including + predicates the last time they dispatched a UIControlEventTouchDown + */ +@property (nonatomic, copy) NSHashTable *verified; + +- (void)stopOnView:(UIView *)view; + +@end + +@implementation MPUIControlBinding + ++ (NSString *)typeName +{ + return @"ui_control"; +} + ++ (MPEventBinding *)bindingWithJSONObject:(NSDictionary *)object +{ + NSString *path = object[@"path"]; + if (![path isKindOfClass:[NSString class]] || path.length < 1) { + NSLog(@"must supply a view path to bind by"); + return nil; + } + + NSString *eventName = object[@"event_name"]; + if (![eventName isKindOfClass:[NSString class]] || eventName.length < 1 ) { + NSLog(@"binding requires an event name"); + return nil; + } + + if (!([object[@"control_event"] unsignedIntegerValue] & UIControlEventAllEvents)) { + NSLog(@"must supply a valid UIControlEvents value for control_event"); + return nil; + } + + UIControlEvents verifyEvent = [object[@"verify_event"] unsignedIntegerValue]; + return [[MPUIControlBinding alloc] initWithEventName:eventName + onPath:path + withControlEvent:[object[@"control_event"] unsignedIntegerValue] + andVerifyEvent:verifyEvent]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" ++ (MPEventBinding *)bindngWithJSONObject:(NSDictionary *)object +{ + return [self bindingWithJSONObject:object]; +} +#pragma clang diagnostic pop + +- (instancetype)initWithEventName:(NSString *)eventName + onPath:(NSString *)path + withControlEvent:(UIControlEvents)controlEvent + andVerifyEvent:(UIControlEvents)verifyEvent +{ + if (self = [super initWithEventName:eventName onPath:path]) { + [self setSwizzleClass:[UIControl class]]; + _controlEvent = controlEvent; + + if (verifyEvent == 0) { + if (controlEvent & UIControlEventAllTouchEvents) { + verifyEvent = UIControlEventTouchDown; + } else if (controlEvent & UIControlEventAllEditingEvents) { + verifyEvent = UIControlEventEditingDidBegin; + } + } + _verifyEvent = verifyEvent; + + [self resetAppliedTo]; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"Event Binding: '%@' for '%@'", [self eventName], [self path]]; +} + +- (void)resetAppliedTo +{ + self.verified = [NSHashTable hashTableWithOptions:(NSHashTableWeakMemory|NSHashTableObjectPointerPersonality)]; + self.appliedTo = [NSHashTable hashTableWithOptions:(NSHashTableWeakMemory|NSHashTableObjectPointerPersonality)]; +} + +#pragma mark -- Executing Actions + +- (void)execute +{ + if (!self.appliedTo) { + [self resetAppliedTo]; + } + + if (!self.running) { + void (^executeBlock)(id, SEL) = ^(id view, SEL command) { + NSArray *objects; + NSObject *root = [[UIApplication sharedApplication] keyWindow].rootViewController; + if (view && [self.appliedTo containsObject:view]) { + if (![self.path fuzzyIsLeafSelected:view fromRoot:root]) { + [self stopOnView:view]; + [self.appliedTo removeObject:view]; + } + } else { + // select targets based off path + if (view) { + if ([self.path fuzzyIsLeafSelected:view fromRoot:root]) { + objects = @[view]; + } else { + objects = @[]; + } + } else { + objects = [self.path fuzzySelectFromRoot:root]; + } + + for (UIControl *control in objects) { + if ([control isKindOfClass:[UIControl class]]) { + if (self.verifyEvent != 0 && self.verifyEvent != self.controlEvent) { + [control addTarget:self + action:@selector(preVerify:forEvent:) + forControlEvents:self.verifyEvent]; + } + + [control addTarget:self + action:@selector(execute:forEvent:) + forControlEvents:self.controlEvent]; + [self.appliedTo addObject:control]; + } + } + } + }; + + executeBlock(nil, _cmd); + + [MPSwizzler swizzleSelector:NSSelectorFromString(@"didMoveToWindow") + onClass:self.swizzleClass + withBlock:executeBlock + named:self.name]; + [MPSwizzler swizzleSelector:NSSelectorFromString(@"didMoveToSuperview") + onClass:self.swizzleClass + withBlock:executeBlock + named:self.name]; + self.running = true; + } +} + +- (void)stop +{ + if (self.running) { + // remove what has been swizzled + [MPSwizzler unswizzleSelector:NSSelectorFromString(@"didMoveToWindow") + onClass:self.swizzleClass + named:self.name]; + [MPSwizzler unswizzleSelector:NSSelectorFromString(@"didMoveToSuperview") + onClass:self.swizzleClass + named:self.name]; + + // remove target-action pairs + for (UIControl *control in self.appliedTo.allObjects) { + if (control && [control isKindOfClass:[UIControl class]]) { + [self stopOnView:control]; + } + } + [self resetAppliedTo]; + self.running = false; + } +} + +- (void)stopOnView:(UIControl *)view +{ + if (self.verifyEvent != 0 && self.verifyEvent != self.controlEvent) { + [view removeTarget:self + action:@selector(preVerify:forEvent:) + forControlEvents:self.verifyEvent]; + } + [view removeTarget:self + action:@selector(execute:forEvent:) + forControlEvents:self.controlEvent]; +} + +#pragma mark -- To execute for Target-Action event firing + +- (BOOL)verifyControlMatchesPath:(id)control +{ + NSObject *root = [[UIApplication sharedApplication] keyWindow].rootViewController; + return [self.path isLeafSelected:control fromRoot:root]; +} + +- (void)preVerify:(id)sender forEvent:(UIEvent *)event +{ + if ([self verifyControlMatchesPath:sender]) { + [self.verified addObject:sender]; + } else { + [self.verified removeObject:sender]; + } +} + +- (void)execute:(id)sender forEvent:(UIEvent *)event +{ + BOOL shouldTrack; + if (self.verifyEvent != 0 && self.verifyEvent != self.controlEvent) { + shouldTrack = [self.verified containsObject:sender]; + } else { + shouldTrack = [self verifyControlMatchesPath:sender]; + } + if (shouldTrack) { + [[self class] track:[self eventName] properties:nil]; + } +} + +#pragma mark -- NSCoder + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:@(_controlEvent) forKey:@"controlEvent"]; + [aCoder encodeObject:@(_verifyEvent) forKey:@"verifyEvent"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super initWithCoder:aDecoder]) { + _controlEvent = [[aDecoder decodeObjectForKey:@"controlEvent"] unsignedIntegerValue]; + _verifyEvent = [[aDecoder decodeObjectForKey:@"verifyEvent"] unsignedIntegerValue]; + } + return self; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } else if (![other isKindOfClass:[MPUIControlBinding class]]) { + return NO; + } else { + return [super isEqual:other] && self.controlEvent == ((MPUIControlBinding *)other).controlEvent && self.verifyEvent == ((MPUIControlBinding *)other).verifyEvent; + } +} + +- (NSUInteger)hash { + return [super hash] ^ self.controlEvent ^ self.verifyEvent; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUIEdgeInsetsToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPUIEdgeInsetsToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..6f2e018 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUIEdgeInsetsToNSDictionaryValueTransformer.m @@ -0,0 +1,54 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPUIEdgeInsetsToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value respondsToSelector:@selector(UIEdgeInsetsValue)]) { + UIEdgeInsets edgeInsetsValue = [value UIEdgeInsetsValue]; + + return @{ + @"top" : @(edgeInsetsValue.top), + @"bottom" : @(edgeInsetsValue.bottom), + @"left" : @(edgeInsetsValue.left), + @"right" : @(edgeInsetsValue.right) + }; + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionaryValue = value; + + id top = dictionaryValue[@"top"]; + id bottom = dictionaryValue[@"bottom"]; + id left = dictionaryValue[@"left"]; + id right = dictionaryValue[@"right"]; + + if (top && bottom && left && right) { + UIEdgeInsets edgeInsets = UIEdgeInsetsMake([top floatValue], [left floatValue], [bottom floatValue], [right floatValue]); + return [NSValue valueWithUIEdgeInsets:edgeInsets]; + } + } + + return [NSValue valueWithUIEdgeInsets:UIEdgeInsetsZero]; +} + + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUIFontToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPUIFontToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..8bd8a46 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUIFontToNSDictionaryValueTransformer.m @@ -0,0 +1,67 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import "MPValueTransformers.h" + +@implementation MPUIFontToNSDictionaryValueTransformer + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + if ([value isKindOfClass:[UIFont class]]) { + UIFont *fontValue = value; + + return @{ + @"familyName": fontValue.familyName, + @"fontName": fontValue.fontName, + @"pointSize": @(fontValue.pointSize), + }; + } + + return nil; +} + +- (id)reverseTransformedValue:(id)value +{ + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionaryValue = value; + + NSNumber *pointSize = dictionaryValue[@"pointSize"]; + NSString *fontName = dictionaryValue[@"fontName"]; + + float fontSize = [pointSize floatValue]; + if (fontSize > 0.0f && fontName) { + UIFont *systemFont = [UIFont systemFontOfSize:fontSize]; + UIFont *boldSystemFont = [UIFont boldSystemFontOfSize:fontSize]; + UIFont *italicSystemFont = [UIFont italicSystemFontOfSize:fontSize]; + + if ([systemFont.fontName isEqualToString:fontName]) { + return systemFont; + } + else if ([boldSystemFont.fontName isEqualToString:fontName]) + { + return boldSystemFont; + } + else if ([italicSystemFont.fontName isEqualToString:fontName]) + { + return italicSystemFont; + } else { + return [UIFont fontWithName:fontName size:fontSize]; + } + } + } + + return nil; +} + + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUIImageToNSDictionaryValueTransformer.m b/ios/Pods/Mixpanel/Mixpanel/MPUIImageToNSDictionaryValueTransformer.m new file mode 100644 index 0000000..1096aa7 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUIImageToNSDictionaryValueTransformer.m @@ -0,0 +1,144 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import "MPValueTransformers.h" + +@implementation MPUIImageToNSDictionaryValueTransformer + +static NSMutableDictionary *imageCache; + ++ (void)load { + imageCache = [NSMutableDictionary dictionary]; +} + ++ (Class)transformedValueClass +{ + return [NSDictionary class]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)transformedValue:(id)value +{ + NSDictionary *transformedValue = nil; + + if ([value isKindOfClass:[UIImage class]]) { + UIImage *image = value; + + NSValueTransformer *sizeTransformer = [NSValueTransformer valueTransformerForName:NSStringFromClass([MPCGSizeToNSDictionaryValueTransformer class])]; + NSValueTransformer *insetsTransformer = [NSValueTransformer valueTransformerForName:NSStringFromClass([MPUIEdgeInsetsToNSDictionaryValueTransformer class])]; + + NSValue *sizeValue = [NSValue valueWithCGSize:image.size]; + NSValue *capInsetsValue = [NSValue valueWithUIEdgeInsets:image.capInsets]; + NSValue *alignmentRectInsetsValue = [NSValue valueWithUIEdgeInsets:image.alignmentRectInsets]; + + NSArray *images = image.images ?: @[ image ]; + + NSMutableArray *imageDictionaries = [NSMutableArray array]; + for (UIImage *frame in images) { + NSData *imageData = UIImagePNGRepresentation(frame); + NSString *imageDataString = [imageData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + NSDictionary *imageDictionary = @{ @"scale": @(image.scale), + @"mime_type": @"image/png", + @"data": (imageData != nil ? imageDataString : [NSNull null]) }; + + [imageDictionaries addObject:imageDictionary]; + } + + transformedValue = @{ + @"imageOrientation": @(image.imageOrientation), + @"size": [sizeTransformer transformedValue:sizeValue], + @"renderingMode": @(image.renderingMode), + @"resizingMode": @(image.resizingMode), + @"duration": @(image.duration), + @"capInsets": [insetsTransformer transformedValue:capInsetsValue], + @"alignmentRectInsets": [insetsTransformer transformedValue:alignmentRectInsetsValue], + @"images": [imageDictionaries copy], + }; + } + + return transformedValue; +} + +- (id)reverseTransformedValue:(id)value +{ + if ([value isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictionaryValue = value; + + NSValueTransformer *insetsTransformer = [NSValueTransformer valueTransformerForName:NSStringFromClass([MPUIEdgeInsetsToNSDictionaryValueTransformer class])]; + + NSArray *imagesDictionary = dictionaryValue[@"images"]; + UIEdgeInsets capInsets = [[insetsTransformer reverseTransformedValue:dictionaryValue[@"capInsets"]] UIEdgeInsetsValue]; + + NSMutableArray *images = [NSMutableArray array]; + for (NSDictionary *imageDictionary in imagesDictionary) { + NSNumber *scale = imageDictionary[@"scale"]; + UIImage *image; + if (imageDictionary[@"url"]) { + @synchronized(imageCache) { + image = [imageCache valueForKey:imageDictionary[@"url"]]; + } + if (!image) { + NSURL *imageUrl = [NSURL URLWithString: imageDictionary[@"url"]]; + NSError *error; + NSData *imageData = [NSData dataWithContentsOfURL:imageUrl options:(NSDataReadingOptions)0 error:&error]; + if (!error) { + image = [UIImage imageWithData:imageData scale:fminf(1.0, scale.floatValue)]; + @synchronized(imageCache) { + if (image) { + imageCache[imageDictionary[@"url"]] = image; + } + } + } + } + if (image && imageDictionary[@"dimensions"]) { + NSDictionary *dimensions = imageDictionary[@"dimensions"]; + CGSize size = CGSizeMake([dimensions[@"Width"] floatValue], [dimensions[@"Height"] floatValue]); + UIGraphicsBeginImageContext(size); + [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + } + else if (imageDictionary[@"data"] && imageDictionary[@"data"] != [NSNull null]) { + NSData *imageData = [[NSData alloc] initWithBase64EncodedString:imageDictionary[@"data"] + options:NSDataBase64DecodingIgnoreUnknownCharacters]; + image = [UIImage imageWithData:imageData scale:fminf(1.0, scale.floatValue)]; + } + + if (image) { + [images addObject:image]; + } + } + + UIImage *image = nil; + + if (images.count > 1) { + // animated image + image = [UIImage animatedImageWithImages:images duration:[dictionaryValue[@"duration"] doubleValue]]; + } + else if (images.count > 0) + { + image = images[0]; + } + + if (image && UIEdgeInsetsEqualToEdgeInsets(capInsets, UIEdgeInsetsZero) == NO) { + if (dictionaryValue[@"resizingMode"]) { + UIImageResizingMode resizingMode = (UIImageResizingMode)[dictionaryValue[@"resizingMode"] integerValue]; + image = [image resizableImageWithCapInsets:capInsets resizingMode:resizingMode]; + } else { + image = [image resizableImageWithCapInsets:capInsets]; + } + } + + return image; + } + + return nil; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUITableViewBinding.h b/ios/Pods/Mixpanel/Mixpanel/MPUITableViewBinding.h new file mode 100644 index 0000000..696fa2d --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUITableViewBinding.h @@ -0,0 +1,16 @@ +// +// MPUITableViewBinding.h +// HelloMixpanel +// +// Created by Amanda Canyon on 8/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPEventBinding.h" + +@interface MPUITableViewBinding : MPEventBinding + +- (instancetype)init __unavailable; +- (instancetype)initWithEventName:(NSString *)eventName onPath:(NSString *)path withDelegate:(Class)delegateClass; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPUITableViewBinding.m b/ios/Pods/Mixpanel/Mixpanel/MPUITableViewBinding.m new file mode 100644 index 0000000..df5a881 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPUITableViewBinding.m @@ -0,0 +1,125 @@ +// +// MPUITableViewBinding.m +// HelloMixpanel +// +// Created by Amanda Canyon on 8/5/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import +#import +#import "MPSwizzler.h" +#import "MPUITableViewBinding.h" + +@implementation MPUITableViewBinding + ++ (NSString *)typeName +{ + return @"ui_table_view"; +} + ++ (MPEventBinding *)bindingWithJSONObject:(NSDictionary *)object +{ + NSString *path = object[@"path"]; + if (![path isKindOfClass:[NSString class]] || path.length < 1) { + NSLog(@"must supply a view path to bind by"); + return nil; + } + + NSString *eventName = object[@"event_name"]; + if (![eventName isKindOfClass:[NSString class]] || eventName.length < 1 ) { + NSLog(@"binding requires an event name"); + return nil; + } + + Class tableDelegate = NSClassFromString(object[@"table_delegate"]); + if (!tableDelegate) { + NSLog(@"binding requires a table_delegate class"); + return nil; + } + + return [[MPUITableViewBinding alloc] initWithEventName:eventName + onPath:path + withDelegate:tableDelegate]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" ++ (MPEventBinding *)bindngWithJSONObject:(NSDictionary *)object +{ + return [self bindingWithJSONObject:object]; +} +#pragma clang diagnostic pop + +- (instancetype)initWithEventName:(NSString *)eventName onPath:(NSString *)path +{ + return [self initWithEventName:eventName onPath:path withDelegate:nil]; +} + +- (instancetype)initWithEventName:(NSString *)eventName onPath:(NSString *)path withDelegate:(Class)delegateClass +{ + if (self = [super initWithEventName:eventName onPath:path]) { + [self setSwizzleClass:delegateClass]; + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"UITableView Event Tracking: '%@' for '%@'", [self eventName], [self path]]; +} + + +#pragma mark -- Executing Actions + +- (void)execute +{ + if (!self.running && self.swizzleClass != nil) { + void (^block)(id, SEL, id, id) = ^(id view, SEL command, UITableView *tableView, NSIndexPath *indexPath) { + NSObject *root = [UIApplication sharedApplication].keyWindow.rootViewController; + // select targets based off path + if (tableView && [self.path isLeafSelected:tableView fromRoot:root]) { + UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; + NSString *label = (cell && cell.textLabel && cell.textLabel.text) ? cell.textLabel.text : @""; + [[self class] track:[self eventName] + properties:@{ + @"Cell Index": [NSString stringWithFormat: @"%ld", (unsigned long)indexPath.row], + @"Cell Section": [NSString stringWithFormat: @"%ld", (unsigned long)indexPath.section], + @"Cell Label": label + }]; + } + }; + + [MPSwizzler swizzleSelector:@selector(tableView:didSelectRowAtIndexPath:) + onClass:self.swizzleClass + withBlock:block + named:self.name]; + self.running = true; + } +} + +- (void)stop +{ + if (self.running && self.swizzleClass != nil) { + [MPSwizzler unswizzleSelector:@selector(tableView:didSelectRowAtIndexPath:) + onClass:self.swizzleClass + named:self.name]; + self.running = false; + } +} + +#pragma mark -- Helper Methods + +- (UITableView *)parentTableView:(UIView *)cell { + // iterate up the view hierarchy to find the table containing this cell/view + UIView *aView = cell.superview; + while (aView != nil) { + if ([aView isKindOfClass:[UITableView class]]) { + return (UITableView *)aView; + } + aView = aView.superview; + } + return nil; // this view is not within a tableView +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPValueTransformers.h b/ios/Pods/Mixpanel/Mixpanel/MPValueTransformers.h new file mode 100644 index 0000000..336e27e --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPValueTransformers.h @@ -0,0 +1,94 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@interface MPPassThroughValueTransformer : NSValueTransformer + +@end + +@interface MPBOOLToNSNumberValueTransformer : NSValueTransformer + +@end + +@interface MPCATransform3DToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPCGAffineTransformToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPCGColorRefToNSStringValueTransformer : NSValueTransformer + +@end + +@interface MPCGPointToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPCGRectToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPCGSizeToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPNSAttributedStringToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPUIColorToNSStringValueTransformer : NSValueTransformer + +@end + +@interface MPUIEdgeInsetsToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPUIFontToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPUIImageToNSDictionaryValueTransformer : NSValueTransformer + +@end + +@interface MPNSNumberToCGFloatValueTransformer : NSValueTransformer + +@end + +__unused static id transformValue(id value, NSString *toType) +{ + assert(value != nil); + + if ([value isKindOfClass:[NSClassFromString(toType) class]]) { + return [[NSValueTransformer valueTransformerForName:@"MPPassThroughValueTransformer"] transformedValue:value]; + } + + NSString *fromType = nil; + NSArray *validTypes = @[[NSString class], [NSNumber class], [NSDictionary class], [NSArray class], [NSNull class]]; + for (Class c in validTypes) { + if ([value isKindOfClass:c]) { + fromType = NSStringFromClass(c); + break; + } + } + + assert(fromType != nil); + NSValueTransformer *transformer = nil; + NSString *forwardTransformerName = [NSString stringWithFormat:@"MP%@To%@ValueTransformer", fromType, toType]; + transformer = [NSValueTransformer valueTransformerForName:forwardTransformerName]; + if (transformer) { + return [transformer transformedValue:value]; + } + + NSString *reverseTransformerName = [NSString stringWithFormat:@"MP%@To%@ValueTransformer", toType, fromType]; + transformer = [NSValueTransformer valueTransformerForName:reverseTransformerName]; + if (transformer && [[transformer class] allowsReverseTransformation]) { + return [transformer reverseTransformedValue:value]; + } + + return [[NSValueTransformer valueTransformerForName:@"MPPassThroughValueTransformer"] transformedValue:value]; +} diff --git a/ios/Pods/Mixpanel/Mixpanel/MPVariant.h b/ios/Pods/Mixpanel/Mixpanel/MPVariant.h new file mode 100644 index 0000000..e6ea8e6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPVariant.h @@ -0,0 +1,105 @@ +// +// MPVariant.h +// HelloMixpanel +// +// Created by Alex Hofsteede on 28/4/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import + +@interface MPVariant : NSObject + +@property (nonatomic) NSUInteger ID; +@property (nonatomic) NSUInteger experimentID; + +/*! + @property + + @abstract + Whether this specific variant is currently running on the device. + + @discussion + This property will not be restored on unarchive, as the variant will need + to be run again once the app is restarted. + */ +@property (nonatomic, readonly) BOOL running; + +/*! + @property + + @abstract + Whether the variant should not run anymore. + + @discussion + Variants are marked as finished when we no longer see them in a decide response. + They will continue running (ie their changes will be visible) until the next + time the app starts. +*/ +@property (nonatomic, readonly) BOOL finished; + ++ (MPVariant *)variantWithJSONObject:(NSDictionary *)object; + +- (void)addActionsFromJSONObject:(NSArray *)actions andExecute:(BOOL)exec; +- (void)addActionFromJSONObject:(NSDictionary *)object andExecute:(BOOL)exec; +- (void)addTweaksFromJSONObject:(NSArray *)tweaks andExecute:(BOOL)exec; +- (void)addTweakFromJSONObject:(NSDictionary *)object andExecute:(BOOL)exec; +- (void)removeActionWithName:(NSString *)name; + +/*! + @method + + @abstract + Executes the variant, including all of its actions and tweaks. + + @discussion + This immediately applies the changes associated with this variant. + */ +- (void)execute; + +/*! + @method + + @abstract + Stops the variant, including all of its actions and tweaks. + + @discussion + This immediately applies the reverse of this variant. including + reversing all actions and tweaks to their original values. + */ +- (void)stop; + +/*! + @method + + @abstract + Sets the finished flag on this variant, does not take any actions. + + @discussion + The finished flag marks this variant as one that should not be run + anymore the next time the app opens, but we leave it running so that + the UI doesn't change during the user session. + */ +- (void)finish; + +/*! + @method + + @abstract + Unsets the finished flag on this variant, does not take any actions. + + @discussion + If the finished flag is unset, the variant will continue to run the + next time the app starts. + */ +- (void)restart; + +@end + +@interface MPVariantAction : NSObject + +@end + +@interface MPVariantTweak : NSObject + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPVariant.m b/ios/Pods/Mixpanel/Mixpanel/MPVariant.m new file mode 100644 index 0000000..9cb4315 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPVariant.m @@ -0,0 +1,760 @@ +// +// MPVariant.m +// HelloMixpanel +// +// Created by Alex Hofsteede on 28/4/14. +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +#import "MPLogger.h" +#import "MPObjectSelector.h" +#import "MPSwizzler.h" +#import "MPTweak.h" +#import "MPTweakStore.h" +#import "MPValueTransformers.h" +#import "MPVariant.h" + +@interface MPVariant () + + @property (nonatomic, strong) NSMutableOrderedSet *actions; + @property (nonatomic, strong) NSMutableArray *tweaks; + +@end + +@interface MPVariantAction () + +@property (nonatomic, strong) NSString *name; + +@property (nonatomic, strong) MPObjectSelector *path; +@property (nonatomic, assign) SEL selector; +@property (nonatomic, strong) NSArray *args; +@property (nonatomic, strong) NSArray *original; +@property (nonatomic, assign) BOOL cacheOriginal; + +@property (nonatomic, assign) BOOL swizzle; +@property (nonatomic, assign) Class swizzleClass; +@property (nonatomic, assign) SEL swizzleSelector; + +@property (nonatomic, copy) NSHashTable *appliedTo; + ++ (MPVariantAction *)actionWithJSONObject:(NSDictionary *)object; +- (instancetype)initWithName:(NSString *)name + path:(MPObjectSelector *)path + selector:(SEL)selector + args:(NSArray *)args + cacheOriginal:(BOOL)cacheOriginal + original:(NSArray *)original + swizzle:(BOOL)swizzle + swizzleClass:(Class)swizzleClass + swizzleSelector:(SEL)swizzleSelector; + +- (void)execute; +- (void)stop; + +@end + +#pragma mark - + +@interface MPVariantTweak () + +@property (nonatomic, strong) NSString *name; +@property (nonatomic, strong) NSString *encoding; +@property (nonatomic, strong) MPTweakValue value; + ++ (MPVariantTweak *)tweakWithJSONObject:(NSDictionary *)object; +- (instancetype)initWithName:(NSString *)name + encoding:(NSString *)encoding + value:(MPTweakValue)value; +- (void)execute; +- (void)stop; + +@end + +#pragma mark - + +@implementation MPVariant + +#pragma mark Constructing Variants + ++ (MPVariant *)variantWithJSONObject:(NSDictionary *)object { + + NSNumber *ID = object[@"id"]; + if (!([ID isKindOfClass:[NSNumber class]] && ID.integerValue > 0)) { + MPLogError(@"invalid variant id: %@", ID); + return nil; + } + + NSNumber *experimentID = object[@"experiment_id"]; + if (!([experimentID isKindOfClass:[NSNumber class]] && experimentID.integerValue > 0)) { + MPLogError(@"invalid experiment id: %@", experimentID); + return nil; + } + + NSArray *actions = object[@"actions"]; + if (![actions isKindOfClass:[NSArray class]]) { + MPLogError(@"variant requires an array of actions"); + return nil; + } + + NSArray *tweaks = object[@"tweaks"]; + if (![tweaks isKindOfClass:[NSArray class]]) { + MPLogError(@"variant requires an array of tweaks"); + return nil; + } + + return [[MPVariant alloc] initWithID:ID.unsignedIntegerValue + experimentID:experimentID.unsignedIntegerValue + actions:actions + tweaks:tweaks]; +} + +- (instancetype)init +{ + return [self initWithID:0 experimentID:0 actions:nil tweaks:nil]; +} + +- (instancetype)initWithID:(NSUInteger)ID experimentID:(NSUInteger)experimentID actions:(NSArray *)actions tweaks:(NSArray *)tweaks +{ + if (self = [super init]) { + self.ID = ID; + self.experimentID = experimentID; + self.actions = [NSMutableOrderedSet orderedSet]; + self.tweaks = [NSMutableArray array]; + [self addTweaksFromJSONObject:tweaks andExecute:NO]; + [self addActionsFromJSONObject:actions andExecute:NO]; + _finished = NO; + _running = NO; + } + return self; +} + +#pragma mark NSCoding + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super init]) { + self.ID = [(NSNumber *)[aDecoder decodeObjectForKey:@"ID"] unsignedLongValue]; + self.experimentID = [(NSNumber *)[aDecoder decodeObjectForKey:@"experimentID"] unsignedLongValue]; + self.actions = [aDecoder decodeObjectForKey:@"actions"]; + self.tweaks = [aDecoder decodeObjectForKey:@"tweaks"]; + _finished = [(NSNumber *)[aDecoder decodeObjectForKey:@"finished"] boolValue]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:@(_ID) forKey:@"ID"]; + [aCoder encodeObject:@(_experimentID) forKey:@"experimentID"]; + [aCoder encodeObject:_actions forKey:@"actions"]; + [aCoder encodeObject:_tweaks forKey:@"tweaks"]; + [aCoder encodeObject:@(_finished) forKey:@"finished"]; +} + +#pragma mark Actions + +- (void)addActionsFromJSONObject:(NSArray *)actions andExecute:(BOOL)exec +{ + for (NSDictionary *object in actions) { + [self addActionFromJSONObject:object andExecute:exec]; + } +} + +- (void)addActionFromJSONObject:(NSDictionary *)object andExecute:(BOOL)exec +{ + MPVariantAction *action = [MPVariantAction actionWithJSONObject:object]; + if (action) { + // Remove any action already in use for this name + [self.actions removeObject:action]; + [self.actions addObject:action]; + if (exec) { + [action execute]; + } + } +} + +- (void)removeActionWithName:(NSString *)name +{ + for (MPVariantAction *action in self.actions) { + if ([action.name isEqualToString:name]) { + [action stop]; + [self.actions removeObject:action]; + break; + } + } +} + +#pragma mark Tweaks + +- (void)addTweaksFromJSONObject:(NSArray *)tweaks andExecute:(BOOL)exec +{ + for (NSDictionary *object in tweaks) { + [self addTweakFromJSONObject:object andExecute:exec]; + } +} + +- (void)addTweakFromJSONObject:(NSDictionary *)object andExecute:(BOOL)exec +{ + MPVariantTweak *tweak = [MPVariantTweak tweakWithJSONObject:object]; + if (tweak) { + [self.tweaks addObject:tweak]; + if (exec) { + [tweak execute]; + } + } +} + +#pragma mark Execution + +- (void)execute { + if (!self.running && !self.finished) { + for (MPVariantTweak *tweak in self.tweaks) { + [tweak execute]; + } + for (MPVariantAction *action in self.actions) { + [action execute]; + } + _running = YES; + } +} + +- (void)stop { + for (MPVariantAction *action in self.actions) { + [action stop]; + } + for (MPVariantTweak *tweak in self.tweaks) { + [tweak stop]; + } + _running = NO; +} + +- (void)finish { + [self stop]; + _finished = YES; +} + +- (void)restart { + _finished = NO; +} + +#pragma mark Equality + +- (BOOL)isEqualToVariant:(MPVariant *)variant +{ + return self.ID == variant.ID; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[MPVariant class]]) { + return NO; + } + + return [self isEqualToVariant:(MPVariant *)object]; +} + +- (NSUInteger)hash +{ + return self.ID; +} + +@end + +#pragma mark - + +@implementation MPVariantAction + +/* + A map of setter selectors to getters. If we have an action that attempts + to call the setter, we first cache the value returned from the getter + */ +static NSMapTable *gettersForSetters; +/* + A map of UIViews to UIImages. The UIImage is the original image for each + view before this VariantAction changed it, so we can quickly switch back + to it if we need to stop this action. We cache the original for every + view we apply to, as they may all have different original images. The view + is weakly held, so if the view is deallocated for any reason, it will disappear + from this map along with the cached original image for it. +*/ +static NSMapTable *originalCache; + ++ (void)load +{ + gettersForSetters = [[NSMapTable alloc] initWithKeyOptions:(NSPointerFunctionsOpaqueMemory|NSPointerFunctionsOpaquePersonality) valueOptions:(NSPointerFunctionsOpaqueMemory|NSPointerFunctionsOpaquePersonality) capacity:2]; + [gettersForSetters setObject:MAPTABLE_ID(NSSelectorFromString(@"imageForState:")) forKey:MAPTABLE_ID(NSSelectorFromString(@"setImage:forState:"))]; + [gettersForSetters setObject:MAPTABLE_ID(NSSelectorFromString(@"image")) forKey:MAPTABLE_ID(NSSelectorFromString(@"setImage:"))]; + [gettersForSetters setObject:MAPTABLE_ID(NSSelectorFromString(@"backgroundImageForState:")) forKey:MAPTABLE_ID(NSSelectorFromString(@"setBackgroundImage:forState:"))]; + + originalCache = [NSMapTable mapTableWithKeyOptions:(NSMapTableWeakMemory|NSMapTableObjectPointerPersonality) + valueOptions:(NSMapTableStrongMemory|NSMapTableObjectPointerPersonality)]; +} + ++ (MPVariantAction *)actionWithJSONObject:(NSDictionary *)object +{ + // Required parameters + MPObjectSelector *path = [MPObjectSelector objectSelectorWithString:object[@"path"]]; + if (!path) { + MPLogError(@"invalid action path: %@", object[@"path"]); + return nil; + } + + SEL selector = NSSelectorFromString(object[@"selector"]); + if (selector == (SEL)0) { + MPLogError(@"invalid action selector: %@", object[@"selector"]); + return nil; + } + + NSArray *args = object[@"args"]; + if (![args isKindOfClass:[NSArray class]]) { + MPLogError(@"invalid action arguments: %@", args); + return nil; + } + + // Optional parameters + BOOL cacheOriginal = !object[@"cacheOriginal"] || [object[@"swizzle"] boolValue]; + NSArray *original = [object[@"original"] isKindOfClass:[NSArray class]] ? object[@"original"] : nil; + NSString *name = object[@"name"]; + BOOL swizzle = !object[@"swizzle"] || [object[@"swizzle"] boolValue]; + Class swizzleClass = NSClassFromString(object[@"swizzleClass"]); + SEL swizzleSelector = NSSelectorFromString(object[@"swizzleSelector"]); + + return [[MPVariantAction alloc] initWithName:name + path:path + selector:selector + args:args + cacheOriginal:cacheOriginal + original:original + swizzle:swizzle + swizzleClass:swizzleClass + swizzleSelector:swizzleSelector]; +} + +- (instancetype)init +{ + [NSException raise:@"NotSupported" format:@"Please call initWithName: path: selector: args: original: swizzle: swizzleClass: swizzleSelector:"]; + return nil; +} + +- (instancetype)initWithName:(NSString *)name + path:(MPObjectSelector *)path + selector:(SEL)selector + args:(NSArray *)args + cacheOriginal:(BOOL)cacheOriginal + original:(NSArray *)original + swizzle:(BOOL)swizzle + swizzleClass:(Class)swizzleClass + swizzleSelector:(SEL)swizzleSelector +{ + if ((self = [super init])) { + self.path = path; + self.selector = selector; + self.args = args; + self.original = original; + self.swizzle = swizzle; + self.cacheOriginal = cacheOriginal; + + if (!name) { + name = [NSUUID UUID].UUIDString; + } + self.name = name; + + if (!swizzleClass) { + swizzleClass = [path selectedClass]; + } + if (!swizzleClass) { + swizzleClass = [UIView class]; + } + self.swizzleClass = swizzleClass; + + if (!swizzleSelector) { + BOOL shouldUseLayoutSubviews = NO; + NSArray *classesToUseLayoutSubviews = @[[UITableViewCell class], [UINavigationBar class]]; + for (Class klass in classesToUseLayoutSubviews) { + if ([self.swizzleClass isSubclassOfClass:klass] || + [self.path pathContainsObjectOfClass:klass]) { + shouldUseLayoutSubviews = YES; + break; + } + } + if (shouldUseLayoutSubviews) { + swizzleSelector = NSSelectorFromString(@"layoutSubviews"); + } else { + swizzleSelector = NSSelectorFromString(@"didMoveToWindow"); + } + } + self.swizzleSelector = swizzleSelector; + + self.appliedTo = [NSHashTable hashTableWithOptions:(NSHashTableWeakMemory|NSHashTableObjectPointerPersonality)]; + } + return self; +} + +#pragma mark NSCoding + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super init]) { + self.name = [aDecoder decodeObjectForKey:@"name"]; + + self.path = [MPObjectSelector objectSelectorWithString:[aDecoder decodeObjectForKey:@"path"]]; + self.selector = NSSelectorFromString([aDecoder decodeObjectForKey:@"selector"]); + self.args = [aDecoder decodeObjectForKey:@"args"]; + self.original = [aDecoder decodeObjectForKey:@"original"]; + + self.swizzle = [(NSNumber *)[aDecoder decodeObjectForKey:@"swizzle"] boolValue]; + self.swizzleClass = NSClassFromString([aDecoder decodeObjectForKey:@"swizzleClass"]); + self.swizzleSelector = NSSelectorFromString([aDecoder decodeObjectForKey:@"swizzleSelector"]); + + self.appliedTo = [NSHashTable hashTableWithOptions:(NSHashTableWeakMemory|NSHashTableObjectPointerPersonality)]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:_name forKey:@"name"]; + + [aCoder encodeObject:_path.string forKey:@"path"]; + [aCoder encodeObject:NSStringFromSelector(_selector) forKey:@"selector"]; + [aCoder encodeObject:_args forKey:@"args"]; + [aCoder encodeObject:_original forKey:@"original"]; + + [aCoder encodeObject:@(_swizzle) forKey:@"swizzle"]; + [aCoder encodeObject:NSStringFromClass(_swizzleClass) forKey:@"swizzleClass"]; + [aCoder encodeObject:NSStringFromSelector(_swizzleSelector) forKey:@"swizzleSelector"]; +} + +#pragma mark Executing Actions + +- (void)execute +{ + // Block to execute on swizzle + void (^executeBlock)(id, SEL) = ^(id view, SEL command){ + + if (self.cacheOriginal) { + [self cacheOriginalImage:view]; + } + + NSArray *invocations = [[self class] executeSelector:self.selector + withArgs:self.args + onPath:self.path + fromRoot:[UIApplication sharedApplication].keyWindow.rootViewController + toLeaf:view]; + + for (NSInvocation *invocation in invocations) { + [self.appliedTo addObject:invocation.target]; + } + }; + + // Execute once in case the view to be changed is already on screen. + executeBlock(nil, _cmd); + + // The block that is called on swizzle executes the executeBlock on the main queue to minimize time + // spent in the swizzle, and allow the newly added UI elements time to be initialized on screen. + void (^swizzleBlock)(id, SEL) = ^(id view, SEL command){ + dispatch_async(dispatch_get_main_queue(), ^{ executeBlock(view, command);}); + }; + + if (self.swizzle && self.swizzleClass != nil) { + // Swizzle the method needed to check for this object coming onscreen + [MPSwizzler swizzleSelector:self.swizzleSelector + onClass:self.swizzleClass + withBlock:swizzleBlock + named:self.name]; + } +} + +- (void)stop +{ + if (self.swizzle && self.swizzleClass != nil) { + // Stop this change from applying in future + [MPSwizzler unswizzleSelector:self.swizzleSelector + onClass:self.swizzleClass + named:self.name]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.original) { + // Undo the changes with the original values specified in the action + [[self class] executeSelector:self.selector withArgs:self.original onObjects:self.appliedTo.allObjects]; + } else if (self.cacheOriginal) { + // Or undo them from the local cache of original images + [self restoreCachedImage]; + } + + [self.appliedTo removeAllObjects]; + }); +} + +- (void)cacheOriginalImage:(id)view +{ + NSEnumerator *selectorEnum = [gettersForSetters keyEnumerator]; + SEL selector = nil, cacheSelector = nil; + while ((selector = (SEL)((__bridge void *)[selectorEnum nextObject]))) { + if (selector == self.selector) { + cacheSelector = (SEL)(__bridge void *)[gettersForSetters objectForKey:MAPTABLE_ID(selector)]; + break; + } + } + if (cacheSelector) { + NSArray *cacheInvocations = [[self class] executeSelector:cacheSelector + withArgs:self.args + onPath:self.path + fromRoot:[UIApplication sharedApplication].keyWindow.rootViewController + toLeaf:view]; + for (NSInvocation *invocation in cacheInvocations) { + if (![originalCache objectForKey:invocation.target]) { + // Retrieve the image through a void* and then + // __bridge cast to force a retain. If we populated + // originalImage directly from getReturnValue, it would + // not be correctly retained. + void *result; + [invocation getReturnValue:&result]; + UIImage *originalImage = (__bridge UIImage *)result; + [originalCache setObject:originalImage forKey:invocation.target]; + } + } + } +} + +- (void)restoreCachedImage +{ + for (NSObject *o in self.appliedTo.allObjects) { + id originalImage = [originalCache objectForKey:o]; + if (originalImage) { + NSMutableArray *originalArgs = [self.args mutableCopy]; + for (NSUInteger i = 0, n = originalArgs.count; i < n; i++) { + id originalArg = originalArgs[i]; + if ([originalArg isKindOfClass:[NSArray class]] && [originalArg[1] isEqual:@"UIImage"]) { + originalArgs[i] = @[originalImage, @"UIImage"]; + break; + } + } + [[self class] executeSelector:self.selector withArgs:originalArgs onObjects:@[o]]; + [originalCache removeObjectForKey:o]; + } + } +} + + +- (NSString *)description { + return [NSString stringWithFormat:@"Action: Change %@ on %@ matching %@ from %@ to %@", NSStringFromSelector(self.selector), NSStringFromClass(self.class), self.path.string, self.original ?: (self.cacheOriginal ? @"Cached Original" : nil), self.args]; +} + ++ (NSArray *)executeSelector:(SEL)selector withArgs:(NSArray *)args onPath:(MPObjectSelector *)path fromRoot:(NSObject *)root toLeaf:(NSObject *)leaf +{ + if (leaf) { + if ([path isLeafSelected:leaf fromRoot:root]) { + return [self executeSelector:selector withArgs:args onObjects:@[leaf]]; + } else { + return @[]; + } + } else { + return [self executeSelector:selector withArgs:args onObjects:[path selectFromRoot:root]]; + } +} + ++ (NSArray *)executeSelector:(SEL)selector withArgs:(NSArray *)args onObjects:(NSArray *)objects +{ + NSMutableArray *invocations = [NSMutableArray array]; + for (NSObject *o in objects) { + NSMethodSignature *signature = [o methodSignatureForSelector:selector]; + if (signature != nil) { + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + [invocation retainArguments]; + NSUInteger requiredArgs = signature.numberOfArguments - 2; + if (args.count >= requiredArgs) { + [invocation setSelector:selector]; + for (NSUInteger i = 0; i < requiredArgs; i++) { + NSArray *argTuple = args[i]; + // Ensure we only send strings to the transform method + if (![argTuple[1] isKindOfClass:[NSString class]]) continue; + + id arg = transformValue(argTuple[0], argTuple[1]); + + // Unpack NSValues to their base types. + if ([arg isKindOfClass:[NSValue class]]) { + const char *ctype = [(NSValue *)arg objCType]; + NSUInteger size; + NSGetSizeAndAlignment(ctype, &size, nil); + void *buf = malloc(size); + [(NSValue *)arg getValue:buf]; + [invocation setArgument:buf atIndex:(int)(i+2)]; + free(buf); + } else { + [invocation setArgument:(void *)&arg atIndex:(int)(i+2)]; + } + } + @try { + // This check is done to avoid moving and resizing UI components that you are not allowed to change. + if ([NSStringFromSelector(selector) isEqualToString:@"setFrame:"] && ![o isKindOfClass:[UINavigationBar class]]) { + ((UIView *)o).translatesAutoresizingMaskIntoConstraints = YES; + } + [invocation invokeWithTarget:o]; + } + @catch (NSException *exception) { + MPLogError(@"Exception during invocation: %@", exception); + } + [invocations addObject:invocation]; + } else { + MPLogError(@"Not enough args"); + } + } else { + MPLogError(@"No method found for %@", NSStringFromSelector(selector)); + } + } + return [invocations copy]; +} + + +#pragma mark Equality + +- (BOOL)isEqualToAction:(MPVariantAction *)action +{ + return [self.name isEqualToString:action.name]; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + if (![object isKindOfClass:[MPVariantAction class]]) { + return NO; + } + return [self isEqualToAction:(MPVariantAction *)object]; +} + +- (NSUInteger)hash +{ + return [self.name hash]; +} + +@end + +#pragma mark - + +@implementation MPVariantTweak + ++ (MPVariantTweak *)tweakWithJSONObject:(NSDictionary *)object +{ + // Required parameters + NSString *name = object[@"name"]; + if (![name isKindOfClass:[NSString class]]) { + MPLogError(@"invalid name: %@", name); + return nil; + } + + NSString *encoding = object[@"encoding"]; + if (![encoding isKindOfClass:[NSString class]]) { + MPLogError(@"invalid encoding: %@", encoding); + return nil; + } + + MPTweakValue value = object[@"value"]; + if (value == nil) { + MPLogError(@"invalid value: %@", value); + return nil; + } + + return [[MPVariantTweak alloc] initWithName:name + encoding:encoding + value:value]; +} + +- (instancetype)init +{ + [NSException raise:@"NotSupported" format:@"Please call initWithName:name encoding:encoding value:value"]; + return nil; + +} + +- (instancetype)initWithName:(NSString *)name + encoding:(NSString *)encoding + value:(MPTweakValue)value +{ + if ((self = [super init])) { + self.name = name; + self.encoding = encoding; + self.value = value; + } + return self; +} + +#pragma mark NSCoding + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + if (self = [super init]) { + self.name = [aDecoder decodeObjectForKey:@"name"]; + self.encoding = [aDecoder decodeObjectForKey:@"encoding"]; + self.value = [aDecoder decodeObjectForKey:@"value"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.name forKey:@"name"]; + [aCoder encodeObject:self.encoding forKey:@"encoding"]; + [aCoder encodeObject:self.value forKey:@"value"]; +} + +#pragma mark Executing Actions + +- (void)execute +{ + MPTweak *mpTweak = [[MPTweakStore sharedInstance] tweakWithName:self.name]; + if (mpTweak) { + //TODO, this may change, but for now sending an NSNull will revert the MPTweak back to its default. + if ([self.value isKindOfClass:[NSNull class]]) { + mpTweak.currentValue = mpTweak.defaultValue; + } else { + mpTweak.currentValue = self.value; + } + } +} + +- (void)stop +{ + MPTweak *mpTweak = [[MPTweakStore sharedInstance] tweakWithName:self.name]; + if (mpTweak) { + mpTweak.currentValue = mpTweak.defaultValue; + } +} + +- (NSString *)description { + return [NSString stringWithFormat:@"Tweak: %@ = %@", self.name, self.value]; +} + +#pragma mark Equality + +- (BOOL)isEqualToTweak:(MPVariantTweak *)tweak +{ + return [self.name isEqualToString:tweak.name]; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[MPVariantTweak class]]) { + return NO; + } + + return [self isEqualToTweak:(MPVariantTweak *)object]; +} + +- (NSUInteger)hash +{ + return self.name.hash; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPWebSocket.h b/ios/Pods/Mixpanel/Mixpanel/MPWebSocket.h new file mode 100644 index 0000000..abd2170 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPWebSocket.h @@ -0,0 +1,117 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. +// + +// Portions Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import + +typedef NS_ENUM(unsigned int, MPWebSocketReadyState) { + MPWebSocketStateConnecting = 0, + MPWebSocketStateOpen = 1, + MPWebSocketStateClosing = 2, + MPWebSocketStateClosed = 3, +}; + +@class MPWebSocket; + +extern NSString *const MPWebSocketErrorDomain; + +#pragma mark - MPWebSocketDelegate + +@protocol MPWebSocketDelegate; + +#pragma mark - MPWebSocket + +@interface MPWebSocket : NSObject + +@property (nonatomic, assign) id delegate; + +@property (nonatomic, readonly) MPWebSocketReadyState readyState; +@property (nonatomic, readonly, retain) NSURL *url; + +// This returns the negotiated protocol. +// It will be nil until after the handshake completes. +@property (nonatomic, readonly, copy) NSString *protocol; + +// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; +- (instancetype)initWithURLRequest:(NSURLRequest *)request; + +// Some helper constructors. +- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; +- (instancetype)initWithURL:(NSURL *)url; + +// Delegate queue will be dispatch_main_queue by default. +// You cannot set both OperationQueue and dispatch_queue. +- (void)setDelegateOperationQueue:(NSOperationQueue*) queue; +- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue; + +// By default, it will schedule itself on +[NSRunLoop mp_networkRunLoop] using defaultModes. +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; + +// MPWebSockets are intended for one-time-use only. Open should be called once and only once. +- (void)open; + +- (void)close; +- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; + +// Send a UTF8 String or Data. +- (void)send:(id)data; + +@end + +#pragma mark - MPWebSocketDelegate + +@protocol MPWebSocketDelegate + +// message will either be an NSString if the server is using text +// or NSData if the server is using binary. +- (void)webSocket:(MPWebSocket *)webSocket didReceiveMessage:(id)message; + +@optional + +- (void)webSocketDidOpen:(MPWebSocket *)webSocket; +- (void)webSocket:(MPWebSocket *)webSocket didFailWithError:(NSError *)error; +- (void)webSocket:(MPWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; + +@end + +#pragma mark - NSURLRequest (MPCertificateAdditions) + +@interface NSURLRequest (MPCertificateAdditions) + +@property (nonatomic, retain, readonly) NSArray *mp_SSLPinnedCertificates; + +@end + +#pragma mark - NSMutableURLRequest (MPCertificateAdditions) + +@interface NSMutableURLRequest (MPCertificateAdditions) + +@property (nonatomic, retain) NSArray *mp_SSLPinnedCertificates; + +@end + +#pragma mark - NSRunLoop (SRWebSocket) + +@interface NSRunLoop (MPWebSocket) + ++ (NSRunLoop *)mp_networkRunLoop; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MPWebSocket.m b/ios/Pods/Mixpanel/Mixpanel/MPWebSocket.m new file mode 100644 index 0000000..c4dada6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MPWebSocket.m @@ -0,0 +1,1787 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +// +// Portions Copyright 2012 Square Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "MPWebSocket.h" + +#if TARGET_OS_IPHONE +#define HAS_ICU +#endif + +#ifdef HAS_ICU + +#import + +#endif + +#if TARGET_OS_IPHONE + +#import + +#else + +#import + +#endif + +#import +#import +#import "MPLogger.h" + +#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE +#define mp_dispatch_retain(x) +#define mp_dispatch_release(x) +#define maybe_bridge(x) ((__bridge void *) x) +#else +#define mp_dispatch_retain(x) dispatch_retain(x) +#define mp_dispatch_release(x) dispatch_release(x) +#define maybe_bridge(x) (x) +#endif + +#if !__has_feature(objc_arc) +#error MPWebSocket must be compiled with ARC enabled +#endif + + +typedef NS_OPTIONS(unsigned int, MPOpCode) { + MPOpCodeTextFrame = 0x1, + MPOpCodeBinaryFrame = 0x2, + // 3-7 reserved. + MPOpCodeConnectionClose = 0x8, + MPOpCodePing = 0x9, + MPOpCodePong = 0xA, + // B-F reserved. +}; + +typedef NS_ENUM(unsigned int, MPStatusCode) { + MPStatusCodeNormal = 1000, + MPStatusCodeGoingAway = 1001, + MPStatusCodeProtocolError = 1002, + MPStatusCodeUnhandledType = 1003, + // 1004 reserved. + MPStatusNoStatusReceived = 1005, + // 1004-1006 reserved. + MPStatusCodeInvalidUTF8 = 1007, + MPStatusCodePolicyViolated = 1008, + MPStatusCodeMessageTooBig = 1009, +}; + +typedef struct { + BOOL fin; +// BOOL rsv1; +// BOOL rsv2; +// BOOL rsv3; + uint8_t opcode; + BOOL masked; + uint64_t payload_length; +} frame_header; + +static NSString *const MPWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +static inline int32_t validate_dispatch_data_partial_string(NSData *data); + +@interface NSData (MPWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding; + +@end + + +@interface NSString (MPWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding; + +@end + + +@interface NSURL (MPWebSocket) + +// The origin isn't really applicable for a native application. +// So instead, just map ws -> http and wss -> https. +- (NSString *)mp_origin; + +@end + + +@interface _MPRunLoopThread : NSThread + +@property (nonatomic, readonly) NSRunLoop *runLoop; + +@end + + +static NSData *newSHA1(const char *bytes, size_t length) { + uint8_t md[CC_SHA1_DIGEST_LENGTH]; + + CC_SHA1(bytes, (uint)length, md); + + return [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH]; +} + +@implementation NSData (MPWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding +{ + return [newSHA1(self.bytes, self.length) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; +} + +@end + + +@implementation NSString (MPWebSocket) + +- (NSString *)stringBySHA1ThenBase64Encoding +{ + return [newSHA1(self.UTF8String, self.length) base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; +} + +@end + +NSString *const MPWebSocketErrorDomain = @"com.mixpanel.error.WebSocket"; + +// Returns number of bytes consumed. Returning 0 means you didn't match. +// Sends bytes to callback handler; +typedef size_t (^stream_scanner)(NSData *collected_data); + +typedef void (^data_callback)(MPWebSocket *webSocket, NSData *data); + +@interface MPIOConsumer : NSObject { + stream_scanner _scanner; + data_callback _handler; + size_t _bytesNeeded; + BOOL _readToCurrentFrame; + BOOL _unmaskBytes; +} +@property (nonatomic, copy, readonly) stream_scanner consumer; +@property (nonatomic, copy, readonly) data_callback handler; +@property (nonatomic, assign) size_t bytesNeeded; +@property (nonatomic, assign, readonly) BOOL readToCurrentFrame; +@property (nonatomic, assign, readonly) BOOL unmaskBytes; + +@end + +// This class is not thread-safe, and is expected to always be run on the same queue. +@interface MPIOConsumerPool : NSObject + +- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize; + +- (MPIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +- (void)returnConsumer:(MPIOConsumer *)consumer; + +@end + +@interface MPWebSocket () + +- (void)_writeData:(NSData *)data; +- (void)_closeWithProtocolError:(NSString *)message; +- (void)_failWithError:(NSError *)error; + +- (void)_disconnect; + +- (void)_readFrameNew; +- (void)_readFrameContinue; + +- (void)_pumpScanner; + +- (void)_pumpWriting; + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; +- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; +- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; +- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; + +- (void)_sendFrameWithOpcode:(MPOpCode)opcode data:(id)data; + +- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; +- (void)_MP_commonInit; + +- (void)_initializeStreams; +- (void)_connect; + +@property (nonatomic) MPWebSocketReadyState readyState; + +@property (nonatomic) NSOperationQueue *delegateOperationQueue; +@property (nonatomic) dispatch_queue_t delegateDispatchQueue; + +@end + + +@implementation MPWebSocket { + NSInteger _webSocketVersion; + + NSOperationQueue *_delegateOperationQueue; + dispatch_queue_t _delegateDispatchQueue; + + dispatch_queue_t _workQueue; + NSMutableArray *_consumers; + + NSInputStream *_inputStream; + NSOutputStream *_outputStream; + + NSMutableData *_readBuffer; + NSUInteger _readBufferOffset; + + NSMutableData *_outputBuffer; + NSUInteger _outputBufferOffset; + + uint8_t _currentFrameOpcode; + size_t _currentFrameCount; + size_t _readOpCount; + uint32_t _currentStringScanPosition; + NSMutableData *_currentFrameData; + + NSString *_closeReason; + + NSString *_secKey; + + BOOL _pinnedCertFound; + + uint8_t _currentReadMaskKey[4]; + size_t _currentReadMaskOffset; + + BOOL _consumerStopped; + + BOOL _closeWhenFinishedWriting; + BOOL _failed; + + BOOL _secure; + NSURLRequest *_urlRequest; + + CFHTTPMessageRef _receivedHTTPHeaders; + + BOOL _sentClose; + BOOL _didFail; + BOOL _cleanupScheduled; + int _closeCode; + + BOOL _isPumping; + + NSMutableSet *_scheduledRunloops; + + // We use this to retain ourselves. + __strong MPWebSocket *_selfRetain; + + NSArray *_requestedProtocols; + MPIOConsumerPool *_consumerPool; +} + +@synthesize delegate = _delegate; +@synthesize url = _url; +@synthesize readyState = _readyState; +@synthesize protocol = _protocol; + +static __strong NSData *CRLFCRLF; + ++ (void)initialize; +{ + CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4]; +} + +- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols; +{ + self = [super init]; + if (self) { + assert(request.URL); + _url = request.URL; + _urlRequest = request; + + _requestedProtocols = [protocols copy]; + + [self _MP_commonInit]; + } + + return self; +} + +- (instancetype)initWithURLRequest:(NSURLRequest *)request; +{ + return [self initWithURLRequest:request protocols:nil]; +} + +- (instancetype)initWithURL:(NSURL *)url; +{ + return [self initWithURL:url protocols:nil]; +} + +- (instancetype)initWithURL:(NSURL *)url protocols:(NSArray *)protocols; +{ + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; + return [self initWithURLRequest:request protocols:protocols]; +} + +- (void)_MP_commonInit; +{ + + NSString *scheme = _url.scheme.lowercaseString; + assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]); + + if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) { + _secure = YES; + } + + _readyState = MPWebSocketStateConnecting; + _consumerStopped = YES; + _webSocketVersion = 13; + + _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); + + // Going to set a specific on the queue so we can validate we're on the work queue + dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL); + + _delegateDispatchQueue = dispatch_get_main_queue(); + mp_dispatch_retain(_delegateDispatchQueue); + + _readBuffer = [[NSMutableData alloc] init]; + _outputBuffer = [[NSMutableData alloc] init]; + + _currentFrameData = [[NSMutableData alloc] init]; + + _consumers = [NSMutableArray array]; + + _consumerPool = [[MPIOConsumerPool alloc] init]; + + _scheduledRunloops = [NSMutableSet set]; + + [self _initializeStreams]; + + // default handlers +} + +- (void)assertOnWorkQueue; +{ + assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue)); +} + +- (void)dealloc +{ + _inputStream.delegate = nil; + _outputStream.delegate = nil; + + [_inputStream close]; + [_outputStream close]; + + if (_workQueue) { + mp_dispatch_release(_workQueue); + _workQueue = NULL; + } + + if (_receivedHTTPHeaders) { + CFRelease(_receivedHTTPHeaders); + _receivedHTTPHeaders = NULL; + } + + if (_delegateDispatchQueue) { + mp_dispatch_release(_delegateDispatchQueue); + _delegateDispatchQueue = NULL; + } +} + +#ifndef NDEBUG + +- (void)setReadyState:(MPWebSocketReadyState)aReadyState; +{ + [self willChangeValueForKey:@"readyState"]; + assert(aReadyState > _readyState); + _readyState = aReadyState; + [self didChangeValueForKey:@"readyState"]; +} + +#endif + +- (void)open; +{ + assert(_url); + NSAssert(_readyState == MPWebSocketStateConnecting, @"Cannot call -(void)open on MPWebSocket more than once"); + + _selfRetain = self; + + [self _connect]; +} + +// Calls block on delegate queue +- (void)_performDelegateBlock:(dispatch_block_t)block; +{ + if (_delegateOperationQueue) { + [_delegateOperationQueue addOperationWithBlock:block]; + } else { + assert(_delegateDispatchQueue); + dispatch_async(_delegateDispatchQueue, block); + } +} + +- (void)setDelegateDispatchQueue:(dispatch_queue_t)queue; +{ + if (queue) { + mp_dispatch_retain(queue); + } + + if (_delegateDispatchQueue) { + mp_dispatch_release(_delegateDispatchQueue); + } + + _delegateDispatchQueue = queue; +} + +- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage; +{ + NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(httpMessage, CFSTR("Sec-WebSocket-Accept"))); + + if (acceptHeader == nil) { + return NO; + } + + NSString *concatenatedString = [_secKey stringByAppendingString:MPWebSocketAppendToSecKeyString]; + NSString *expectedAccept = [concatenatedString stringBySHA1ThenBase64Encoding]; + + return [acceptHeader isEqualToString:expectedAccept]; +} + +- (void)_HTTPHeadersDidFinish; +{ + NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders); + + if (responseCode >= 400) { + MPLogError(@"Request failed with response code %d", responseCode); + [self _failWithError:[NSError errorWithDomain:MPWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode]}]]; + return; + + } + + if (![self _checkHandshake:_receivedHTTPHeaders]) { + [self _failWithError:[NSError errorWithDomain:MPWebSocketErrorDomain code:2133 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Invalid Sec-WebSocket-Accept response"]}]]; + return; + } + + NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol"))); + if (negotiatedProtocol) { + // Make sure we requested the protocol + if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound) { + [self _failWithError:[NSError errorWithDomain:MPWebSocketErrorDomain code:2133 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Server specified Sec-WebSocket-Protocol that wasn't requested"]}]]; + return; + } + + _protocol = negotiatedProtocol; + } + + self.readyState = MPWebSocketStateOpen; + + if (!_didFail) { + [self _readFrameNew]; + } + + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) { + [self.delegate webSocketDidOpen:self]; + } + }]; +} + + +- (void)_readHTTPHeader; +{ + if (_receivedHTTPHeaders == NULL) { + _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO); + } + + [self _readUntilHeaderCompleteWithCallback:^(MPWebSocket *websocket, NSData *data) { + CFHTTPMessageAppendBytes(websocket->_receivedHTTPHeaders, (const UInt8 *)data.bytes, (CFIndex)data.length); + + if (CFHTTPMessageIsHeaderComplete(websocket->_receivedHTTPHeaders)) { + MPLogDebug(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(websocket->_receivedHTTPHeaders))); + [websocket _HTTPHeadersDidFinish]; + } else { + [websocket _readHTTPHeader]; + } + }]; +} + +- (void)didConnect +{ + MPLogInfo(@"Connected"); + CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1); + + // Set host first so it defaults + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host)); + + NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16]; + int result = SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes); + if (result != 0) { + MPLogError(@"Failed to generate random bytes with status: %d", result); + } + _secKey = [keyBytes base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + assert(_secKey.length == 24); + + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket")); + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade")); + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey); + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]); + + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.mp_origin); + + if (_requestedProtocols) { + CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol"), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]); + } + + [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj); + }]; + + NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request)); + + CFRelease(request); + + [self _writeData:message]; + [self _readHTTPHeader]; +} + +- (void)_initializeStreams; +{ + NSInteger port = _url.port.integerValue; + if (port == 0) { + if (!_secure) { + port = 80; + } else { + port = 443; + } + } + NSString *host = _url.host; + + CFReadStreamRef readStream = NULL; + CFWriteStreamRef writeStream = NULL; + + CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, (UInt32)port, &readStream, &writeStream); + + _outputStream = CFBridgingRelease(writeStream); + _inputStream = CFBridgingRelease(readStream); + + + if (_secure) { + NSMutableDictionary *SSLOptions = [NSMutableDictionary dictionary]; + + [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel]; + + // If we're using pinned certs, don't validate the certificate chain + if ([_urlRequest mp_SSLPinnedCertificates].count) { + [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; + } + +#if DEBUG + [SSLOptions setValue:@NO forKey:(__bridge id)kCFStreamSSLValidatesCertificateChain]; + MPLogDebug(@"SocketRocket: In debug mode. Allowing connection to any root cert"); +#endif + + [_outputStream setProperty:SSLOptions + forKey:(__bridge id)kCFStreamPropertySSLSettings]; + } + + _inputStream.delegate = self; + _outputStream.delegate = self; +} + +- (void)_connect; +{ + if (!_scheduledRunloops.count) { + [self scheduleInRunLoop:[NSRunLoop mp_networkRunLoop] forMode:NSDefaultRunLoopMode]; + } + + + [_outputStream open]; + [_inputStream open]; +} + +- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +{ + [_outputStream scheduleInRunLoop:aRunLoop forMode:mode]; + [_inputStream scheduleInRunLoop:aRunLoop forMode:mode]; + + [_scheduledRunloops addObject:@[aRunLoop, mode]]; +} + +- (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; +{ + [_outputStream removeFromRunLoop:aRunLoop forMode:mode]; + [_inputStream removeFromRunLoop:aRunLoop forMode:mode]; + + [_scheduledRunloops removeObject:@[aRunLoop, mode]]; +} + +- (void)close; +{ + [self closeWithCode:MPStatusCodeNormal reason:nil]; +} + +- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; +{ + assert(code); + dispatch_async(_workQueue, ^{ + if (self.readyState == MPWebSocketStateClosing || self.readyState == MPWebSocketStateClosed) { + return; + } + + BOOL wasConnecting = self.readyState == MPWebSocketStateConnecting; + + self.readyState = MPWebSocketStateClosing; + + MPLogDebug(@"Closing with code %d reason %@", code, reason); + + if (wasConnecting) { + [self _disconnect]; + return; + } + + size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize]; + NSData *payload = mutablePayload; + + ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code); + + if (reason) { + NSRange remainingRange = NSMakeRange(0, 0); + NSUInteger usedLength = 0; + + BOOL success = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) maxLength:payload.length - sizeof(uint16_t) usedLength:&usedLength encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRepresentation range:NSMakeRange(0, reason.length) remainingRange:&remainingRange]; + + assert(success); + assert(remainingRange.length == 0); + + if (usedLength != maxMsgSize) { + payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))]; + } + } + + [self _sendFrameWithOpcode:MPOpCodeConnectionClose data:payload]; + }); +} + +- (void)_closeWithProtocolError:(NSString *)message; +{ + // Need to shunt this on the _callbackQueue first to see if they received any messages + [self _performDelegateBlock:^{ + [self closeWithCode:MPStatusCodeProtocolError reason:message]; + dispatch_async(self->_workQueue, ^{ + [self _disconnect]; + }); + }]; +} + +- (void)_failWithError:(NSError *)error; +{ + dispatch_async(_workQueue, ^{ + if (self.readyState != MPWebSocketStateClosed) { + self->_failed = YES; + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) { + [self.delegate webSocket:self didFailWithError:error]; + } + }]; + + self.readyState = MPWebSocketStateClosed; + + MPLogError(@"Failing with error %@", error.localizedDescription); + + [self _disconnect]; + [self _scheduleCleanup]; + } + }); +} + +- (void)_writeData:(NSData *)data; +{ + [self assertOnWorkQueue]; + + if (_closeWhenFinishedWriting) { + return; + } + [_outputBuffer appendData:data]; + [self _pumpWriting]; +} + +- (void)send:(id)data; +{ + NSAssert(self.readyState != MPWebSocketStateConnecting, @"Invalid State: Cannot call send: until connection is open"); + // TODO: maybe not copy this for performance + data = [data copy]; + dispatch_async(_workQueue, ^{ + if ([data isKindOfClass:[NSString class]]) { + [self _sendFrameWithOpcode:MPOpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]]; + } else if ([data isKindOfClass:[NSData class]]) { + [self _sendFrameWithOpcode:MPOpCodeBinaryFrame data:data]; + } else if (data == nil) { + [self _sendFrameWithOpcode:MPOpCodeTextFrame data:data]; + } else { + assert(NO); + } + }); +} + +- (void)handlePing:(NSData *)pingData; +{ + // Need to pingpong this off _callbackQueue first to make sure messages happen in order + [self _performDelegateBlock:^{ + dispatch_async(self->_workQueue, ^{ + [self _sendFrameWithOpcode:MPOpCodePong data:pingData]; + }); + }]; +} + +- (void)handlePong; +{ + // NOOP +} + +- (void)_handleMessage:(id)message +{ + MPLogDebug(@"Received message"); + [self _performDelegateBlock:^{ + [self.delegate webSocket:self didReceiveMessage:message]; + }]; +} + + +static inline BOOL closeCodeIsValid(int closeCode) { + if (closeCode < 1000) { + return NO; + } + + if (closeCode >= 1000 && closeCode <= 1011) { + if (closeCode == 1004 || + closeCode == 1005 || + closeCode == 1006) { + return NO; + } + return YES; + } + + if (closeCode >= 3000 && closeCode <= 3999) { + return YES; + } + + if (closeCode >= 4000 && closeCode <= 4999) { + return YES; + } + + return NO; +} + +// Note from RFC: +// +// If there is a body, the first two +// bytes of the body MUST be a 2-byte unsigned integer (in network byte +// order) representing a status code with value /code/ defined in +// Section 7.4. Following the 2-byte integer the body MAY contain UTF-8 +// encoded data with value /reason/, the interpretation of which is not +// defined by this specification. + +- (void)handleCloseWithData:(NSData *)data; +{ + size_t dataSize = data.length; + __block uint16_t closeCode = 0; + + MPLogDebug(@"Received close frame"); + + if (dataSize == 1) { + // TODO handle error + [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"]; + return; + } else if (dataSize >= 2) { + [data getBytes:&closeCode length:sizeof(closeCode)]; + _closeCode = EndianU16_BtoN(closeCode); + if (!closeCodeIsValid(_closeCode)) { + [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot have close code of %d", _closeCode]]; + return; + } + if (dataSize > 2) { + _closeReason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding]; + if (!_closeReason) { + [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8"]; + return; + } + } + } else { + _closeCode = MPStatusNoStatusReceived; + } + + [self assertOnWorkQueue]; + + if (self.readyState == MPWebSocketStateOpen) { + [self closeWithCode:MPStatusCodeNormal reason:nil]; + } + dispatch_async(_workQueue, ^{ + [self _disconnect]; + }); +} + +- (void)_disconnect; +{ + [self assertOnWorkQueue]; + MPLogDebug(@"Trying to disconnect"); + _closeWhenFinishedWriting = YES; + [self _pumpWriting]; +} + +- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode; +{ + // Check that the current data is valid UTF8 + + BOOL isControlFrame = (opcode == MPOpCodePing || opcode == MPOpCodePong || opcode == MPOpCodeConnectionClose); + if (!isControlFrame) { + [self _readFrameNew]; + } else { + dispatch_async(_workQueue, ^{ + [self _readFrameContinue]; + }); + } + + switch (opcode) { + case MPOpCodeTextFrame: { + NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding]; + if (str == nil && frameData) { + [self closeWithCode:MPStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; + dispatch_async(_workQueue, ^{ + [self _disconnect]; + }); + + return; + } + [self _handleMessage:str]; + break; + } + case MPOpCodeBinaryFrame: + [self _handleMessage:[frameData copy]]; + break; + case MPOpCodeConnectionClose: + [self handleCloseWithData:frameData]; + break; + case MPOpCodePing: + [self handlePing:frameData]; + break; + case MPOpCodePong: + [self handlePong]; + break; + default: + [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]]; + // TODO: Handle invalid opcode + break; + } +} + +- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData; +{ + NSParameterAssert(frame_header.opcode != 0); + + if (self.readyState != MPWebSocketStateOpen) { + return; + } + + + BOOL isControlFrame = (frame_header.opcode == MPOpCodePing || frame_header.opcode == MPOpCodePong || frame_header.opcode == MPOpCodeConnectionClose); + + if (isControlFrame && !frame_header.fin) { + [self _closeWithProtocolError:@"Fragmented control frames not allowed"]; + return; + } + + if (isControlFrame && frame_header.payload_length >= 126) { + [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"]; + return; + } + + if (!isControlFrame) { + _currentFrameOpcode = frame_header.opcode; + _currentFrameCount += 1; + } + + if (frame_header.payload_length == 0) { + if (isControlFrame) { + [self _handleFrameWithData:curData opCode:frame_header.opcode]; + } else { + if (frame_header.fin) { + [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode]; + } else { + // TODO add assert that opcode is not a control; + [self _readFrameContinue]; + } + } + } else { + [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(MPWebSocket *websocket, NSData *newData) { + if (isControlFrame) { + [websocket _handleFrameWithData:newData opCode:frame_header.opcode]; + } else { + if (frame_header.fin) { + [websocket _handleFrameWithData:websocket->_currentFrameData opCode:frame_header.opcode]; + } else { + // TODO add assert that opcode is not a control; + [websocket _readFrameContinue]; + } + + } + } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked]; + } +} + +/* From RFC: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) | + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ + */ + +static const uint8_t MPFinMask = 0x80; +static const uint8_t MPOpCodeMask = 0x0F; +static const uint8_t MPRsvMask = 0x70; +static const uint8_t MPMaskMask = 0x80; +static const uint8_t MPPayloadLenMask = 0x7F; + + +- (void)_readFrameContinue; +{ + assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0)); + + [self _addConsumerWithDataLength:2 callback:^(MPWebSocket *websocket, NSData *data) { + __block frame_header header = { .fin = NO, .opcode = 0, .masked = NO, .payload_length = 0 }; + + const uint8_t *headerBuffer = data.bytes; + assert(data.length >= 2); + + if (headerBuffer[0] & MPRsvMask) { + [websocket _closeWithProtocolError:@"Server used RSV bits"]; + return; + } + + uint8_t receivedOpcode = (MPOpCodeMask & headerBuffer[0]); + + BOOL isControlFrame = (receivedOpcode == MPOpCodePing || receivedOpcode == MPOpCodePong || receivedOpcode == MPOpCodeConnectionClose); + + if (!isControlFrame && receivedOpcode != 0 && websocket->_currentFrameCount > 0) { + [websocket _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"]; + return; + } + + if (receivedOpcode == 0 && websocket->_currentFrameCount == 0) { + [websocket _closeWithProtocolError:@"cannot continue a message"]; + return; + } + + header.opcode = receivedOpcode == 0 ? websocket->_currentFrameOpcode : receivedOpcode; + + header.fin = !!(MPFinMask & headerBuffer[0]); + + + header.masked = !!(MPMaskMask & headerBuffer[1]); + header.payload_length = MPPayloadLenMask & headerBuffer[1]; + + if (header.masked) { + [websocket _closeWithProtocolError:@"Client must receive unmasked data"]; + } + + size_t extra_bytes_needed = header.masked ? sizeof(websocket->_currentReadMaskKey) : 0; + + if (header.payload_length == 126) { + extra_bytes_needed += sizeof(uint16_t); + } else if (header.payload_length == 127) { + extra_bytes_needed += sizeof(uint64_t); + } + + if (extra_bytes_needed == 0) { + [websocket _handleFrameHeader:header curData:websocket->_currentFrameData]; + } else { + [websocket _addConsumerWithDataLength:extra_bytes_needed callback:^(MPWebSocket *websocket2, NSData *data2) { + size_t mapped_size = data2.length; + const void *mapped_buffer = data2.bytes; + size_t offset = 0; + + if (header.payload_length == 126) { + assert(mapped_size >= sizeof(uint16_t)); + uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer)); + header.payload_length = newLen; + offset += sizeof(uint16_t); + } else if (header.payload_length == 127) { + assert(mapped_size >= sizeof(uint64_t)); + header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer)); + offset += sizeof(uint64_t); + } else { + assert(header.payload_length < 126 && header.payload_length >= 0); + } + + + if (header.masked) { + assert(mapped_size >= sizeof(websocket2->_currentReadMaskOffset) + offset); + memcpy(websocket2->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(websocket2->_currentReadMaskKey)); + } + + [websocket2 _handleFrameHeader:header curData:websocket2->_currentFrameData]; + } readToCurrentFrame:NO unmaskBytes:NO]; + } + } readToCurrentFrame:NO unmaskBytes:NO]; +} + +- (void)_readFrameNew; +{ + dispatch_async(_workQueue, ^{ + self->_currentFrameData.length = 0; + + self->_currentFrameOpcode = 0; + self->_currentFrameCount = 0; + self->_readOpCount = 0; + self->_currentStringScanPosition = 0; + + [self _readFrameContinue]; + }); +} + +- (void)_pumpWriting; +{ + [self assertOnWorkQueue]; + + NSUInteger dataLength = _outputBuffer.length; + if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) { + NSInteger bytesWritten = [_outputStream write:((const uint8_t *)_outputBuffer.bytes + _outputBufferOffset) maxLength:(dataLength - _outputBufferOffset)]; + if (bytesWritten == -1) { + [self _failWithError:[NSError errorWithDomain:MPWebSocketErrorDomain code:2145 userInfo:@{NSLocalizedDescriptionKey: @"Error writing to stream"}]]; + return; + } + + _outputBufferOffset += (NSUInteger) bytesWritten; + + if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) { + _outputBuffer = [[NSMutableData alloc] initWithBytes:((char *)_outputBuffer.bytes + _outputBufferOffset) length:(_outputBuffer.length - _outputBufferOffset)]; + _outputBufferOffset = 0; + } + } + + if (_closeWhenFinishedWriting && + _outputBuffer.length - _outputBufferOffset == 0 && + (_inputStream.streamStatus != NSStreamStatusNotOpen && + _inputStream.streamStatus != NSStreamStatusClosed) && + !_sentClose) { + _sentClose = YES; + + @synchronized(self) { + [_outputStream close]; + [_inputStream close]; + + + for (NSArray *runLoop in [_scheduledRunloops copy]) { + [self unscheduleFromRunLoop:runLoop[0] forMode:runLoop[1]]; + } + } + + if (!_failed) { + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { + [self.delegate webSocket:self didCloseWithCode:self->_closeCode reason:self->_closeReason wasClean:YES]; + } + }]; + } + + [self _scheduleCleanup]; + } +} + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback; +{ + [self assertOnWorkQueue]; + [self _addConsumerWithScanner:consumer callback:callback dataLength:0]; +} + +- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +{ + [self assertOnWorkQueue]; + assert(dataLength); + + [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]]; + [self _pumpScanner]; +} + +- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength; +{ + [self assertOnWorkQueue]; + [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]]; + [self _pumpScanner]; +} + +- (void)_scheduleCleanup +{ + @synchronized(self) { + if (_cleanupScheduled) { + return; + } + + _cleanupScheduled = YES; + + // Cleanup NSStream delegate's in the same RunLoop used by the streams themselves: + // This way we'll prevent race conditions between handleEvent and SRWebsocket's dealloc + NSTimer *timer = [NSTimer timerWithTimeInterval:0.f + target:self + selector:@selector(_cleanupSelfReference) + userInfo:nil + repeats:NO]; + + [[NSRunLoop mp_networkRunLoop] addTimer:timer + forMode:NSDefaultRunLoopMode]; + } +} + +- (void)_cleanupSelfReference +{ + @synchronized(self) { + // Remove the delegates for each stream so we don't fire any events on + // close or error + _inputStream.delegate = nil; + _outputStream.delegate = nil; + + // Close the streams, which will immediately remove them from the run + // loop. + [_inputStream close]; + [_outputStream close]; + } + + // Cleanup self reference in the same GCD queue as usual + dispatch_async(_workQueue, ^{ + self->_selfRetain = nil; + }); +} + + +static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'}; + +- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler; +{ + [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler]; +} + +- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler; +{ + // TODO optimize so this can continue from where we last searched + stream_scanner consumer = ^size_t(NSData *data) { + __block size_t found_size = 0; + __block size_t match_count = 0; + + size_t size = data.length; + const unsigned char *buffer = data.bytes; + for (size_t i = 0; i < size; i++ ) { + if (buffer[i] == ((const unsigned char *)bytes)[match_count]) { + match_count += 1; + if (match_count == length) { + found_size = i + 1; + break; + } + } else { + match_count = 0; + } + } + return found_size; + }; + [self _addConsumerWithScanner:consumer callback:dataHandler]; +} + + +// Returns true if did work +- (BOOL)_innerPumpScanner { + + BOOL didWork = NO; + + if (self.readyState >= MPWebSocketStateClosing) { + return didWork; + } + + if (!_consumers.count) { + return didWork; + } + + size_t curSize = _readBuffer.length - _readBufferOffset; + if (!curSize) { + return didWork; + } + + MPIOConsumer *consumer = _consumers[0]; + + size_t bytesNeeded = consumer.bytesNeeded; + + size_t foundSize = 0; + if (consumer.consumer) { + NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO]; + foundSize = consumer.consumer(tempView); + } else { + assert(consumer.bytesNeeded); + if (curSize >= bytesNeeded) { + foundSize = bytesNeeded; + } else if (consumer.readToCurrentFrame) { + foundSize = curSize; + } + } + + NSData *slice = nil; + if (consumer.readToCurrentFrame || foundSize) { + NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize); + slice = [_readBuffer subdataWithRange:sliceRange]; + + _readBufferOffset += foundSize; + + if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) { + _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0; + } + + if (consumer.unmaskBytes) { + NSMutableData *mutableSlice = [slice mutableCopy]; + + NSUInteger len = mutableSlice.length; + uint8_t *bytes = mutableSlice.mutableBytes; + + for (NSUInteger i = 0; i < len; i++) { + bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)]; + _currentReadMaskOffset += 1; + } + + slice = mutableSlice; + } + + if (consumer.readToCurrentFrame) { + [_currentFrameData appendData:slice]; + + _readOpCount += 1; + + if (_currentFrameOpcode == MPOpCodeTextFrame) { + // Validate UTF8 stuff. + size_t currentDataSize = _currentFrameData.length; + if (_currentFrameOpcode == MPOpCodeTextFrame && currentDataSize > 0) { + // TODO: Optimize the crap out of this. Don't really have to copy all the data each time + + size_t scanSize = currentDataSize - _currentStringScanPosition; + + NSData *scan_data = [_currentFrameData subdataWithRange:NSMakeRange(_currentStringScanPosition, scanSize)]; + int32_t valid_utf8_size = validate_dispatch_data_partial_string(scan_data); + + if (valid_utf8_size == -1) { + [self closeWithCode:MPStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"]; + dispatch_async(_workQueue, ^{ + [self _disconnect]; + }); + return didWork; + } else { + _currentStringScanPosition += (uint32_t)valid_utf8_size; + } + } + } + + consumer.bytesNeeded -= foundSize; + + if (consumer.bytesNeeded == 0) { + [_consumers removeObjectAtIndex:0]; + consumer.handler(self, nil); + [_consumerPool returnConsumer:consumer]; + didWork = YES; + } + } else if (foundSize) { + [_consumers removeObjectAtIndex:0]; + consumer.handler(self, slice); + [_consumerPool returnConsumer:consumer]; + didWork = YES; + } + } + return didWork; +} + +- (void)_pumpScanner; +{ + [self assertOnWorkQueue]; + + if (!_isPumping) { + _isPumping = YES; + } else { + return; + } + + while ([self _innerPumpScanner]) { + + } + + _isPumping = NO; +} + +//#define NOMASK + +static const size_t MPFrameHeaderOverhead = 32; + +- (void)_sendFrameWithOpcode:(MPOpCode)opcode data:(id)data; +{ + [self assertOnWorkQueue]; + + NSAssert(data == nil || [data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"Function expects nil, NSString or NSData"); + + size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [(NSData *)data length]; + + NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + MPFrameHeaderOverhead]; + if (!frame) { + [self closeWithCode:MPStatusCodeMessageTooBig reason:@"Message too big"]; + return; + } + uint8_t *frame_buffer = (uint8_t *)frame.mutableBytes; + + // set fin + frame_buffer[0] = MPFinMask | opcode; + + BOOL useMask = YES; +#ifdef NOMASK + useMask = NO; +#endif + + if (useMask) { + // set the mask and header + frame_buffer[1] |= MPMaskMask; + } + + size_t frame_buffer_size = 2; + + const uint8_t *unmasked_payload = NULL; + if ([data isKindOfClass:[NSData class]]) { + unmasked_payload = (uint8_t *)[data bytes]; + } else if ([data isKindOfClass:[NSString class]]) { + unmasked_payload = (const uint8_t *)[data UTF8String]; + } else { + assert(NO); + } + + if (payloadLength < 126) { + frame_buffer[1] |= payloadLength; + } else if (payloadLength <= UINT16_MAX) { + frame_buffer[1] |= 126; + *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint16_t)payloadLength); + frame_buffer_size += sizeof(uint16_t); + } else { + frame_buffer[1] |= 127; + *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint64_t)payloadLength); + frame_buffer_size += sizeof(uint64_t); + } + + if (!useMask) { + for (size_t i = 0; i < payloadLength; i++) { + frame_buffer[frame_buffer_size] = unmasked_payload[i]; + frame_buffer_size += 1; + } + } else { + uint8_t *mask_key = frame_buffer + frame_buffer_size; + int result = SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), mask_key); + if (result != 0) { + MPLogError(@"Failed to generate random bytes with status: %d", result); + } + frame_buffer_size += sizeof(uint32_t); + + // TODO: could probably optimize this with SIMD + for (size_t i = 0; i < payloadLength; i++) { + frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)]; + frame_buffer_size += 1; + } + } + + assert(frame_buffer_size <= frame.length); + frame.length = frame_buffer_size; + + [self _writeData:frame]; +} + +- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode; +{ + if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvailable || eventCode == NSStreamEventHasSpaceAvailable)) { + + NSArray *sslCerts = [_urlRequest mp_SSLPinnedCertificates]; + if (sslCerts) { + SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey:(__bridge id)kCFStreamPropertySSLPeerTrust]; + if (secTrust) { + NSInteger numCerts = SecTrustGetCertificateCount(secTrust); + for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) { + SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTrust, i); + NSData *certData = CFBridgingRelease(SecCertificateCopyData(cert)); + + for (id ref in sslCerts) { + SecCertificateRef trustedCert = (__bridge SecCertificateRef)ref; + NSData *trustedCertData = CFBridgingRelease(SecCertificateCopyData(trustedCert)); + + if ([trustedCertData isEqualToData:certData]) { + _pinnedCertFound = YES; + break; + } + } + } + } + + if (!_pinnedCertFound) { + dispatch_async(_workQueue, ^{ + [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Invalid server cert"]}]]; + }); + return; + } + } + } + + dispatch_async(_workQueue, ^{ + switch (eventCode) { + case NSStreamEventOpenCompleted: { + MPLogDebug(@"NSStreamEventOpenCompleted %@", aStream); + if (self.readyState >= MPWebSocketStateClosing) { + return; + } + assert(self->_readBuffer); + + if (self.readyState == MPWebSocketStateConnecting && aStream == self->_inputStream) { + [self didConnect]; + } + [self _pumpWriting]; + [self _pumpScanner]; + break; + } + + case NSStreamEventErrorOccurred: { + MPLogError(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStream streamError] copy]); + /// TODO specify error better! + [self _failWithError:aStream.streamError]; + self->_readBufferOffset = 0; + [self->_readBuffer setLength:0]; + break; + } + + case NSStreamEventEndEncountered: { + [self _pumpScanner]; + MPLogDebug(@"NSStreamEventEndEncountered %@", aStream); + if (aStream.streamError) { + [self _failWithError:aStream.streamError]; + } else { + if (self.readyState != MPWebSocketStateClosed) { + self.readyState = MPWebSocketStateClosed; + [self _scheduleCleanup]; + } + + if (!self->_sentClose && !self->_failed) { + self->_sentClose = YES; + // If we get closed in this state it's probably not clean because we should be sending this when we send messages + [self _performDelegateBlock:^{ + if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) { + [self.delegate webSocket:self didCloseWithCode:MPStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO]; + } + }]; + } + } + + break; + } + + case NSStreamEventHasBytesAvailable: { + MPLogDebug(@"NSStreamEventHasBytesAvailable %@", aStream); + const int bufferSize = 2048; + uint8_t buffer[bufferSize]; + + while (self->_inputStream.hasBytesAvailable) { + NSInteger bytes_read = [self->_inputStream read:buffer maxLength:bufferSize]; + + if (bytes_read > 0) { + [self->_readBuffer appendBytes:buffer length:(NSUInteger)bytes_read]; + } else if (bytes_read < 0) { + [self _failWithError:self->_inputStream.streamError]; + } + + if (bytes_read != bufferSize) { + break; + } + } + [self _pumpScanner]; + break; + } + + case NSStreamEventHasSpaceAvailable: { + MPLogDebug(@"NSStreamEventHasSpaceAvailable %@", aStream); + [self _pumpWriting]; + break; + } + + default: + MPLogDebug(@"(default) %@", aStream); + break; + } + }); +} + +@end + + +@implementation MPIOConsumer + +@synthesize bytesNeeded = _bytesNeeded; +@synthesize consumer = _scanner; +@synthesize handler = _handler; +@synthesize readToCurrentFrame = _readToCurrentFrame; +@synthesize unmaskBytes = _unmaskBytes; + +- (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +{ + _scanner = [scanner copy]; + _handler = [handler copy]; + _bytesNeeded = bytesNeeded; + _readToCurrentFrame = readToCurrentFrame; + _unmaskBytes = unmaskBytes; + assert(_scanner || _bytesNeeded); +} + + +@end + + +@implementation MPIOConsumerPool { + NSUInteger _poolSize; + NSMutableArray *_bufferedConsumers; +} + +- (instancetype)initWithBufferCapacity:(NSUInteger)poolSize; +{ + self = [super init]; + if (self) { + _poolSize = poolSize; + _bufferedConsumers = [NSMutableArray arrayWithCapacity:poolSize]; + } + return self; +} + +- (instancetype)init +{ + return [self initWithBufferCapacity:8]; +} + +- (MPIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes; +{ + MPIOConsumer *consumer = nil; + if (_bufferedConsumers.count) { + consumer = _bufferedConsumers.lastObject; + [_bufferedConsumers removeLastObject]; + } else { + consumer = [[MPIOConsumer alloc] init]; + } + + [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]; + + return consumer; +} + +- (void)returnConsumer:(MPIOConsumer *)consumer +{ + if (_bufferedConsumers.count < _poolSize) { + [_bufferedConsumers addObject:consumer]; + } +} + +@end + + +@implementation NSURLRequest (MPCertificateAdditions) + +- (NSArray *)mp_SSLPinnedCertificates; +{ + return [NSURLProtocol propertyForKey:@"mp_SSLPinnedCertificates" inRequest:self]; +} + +@end + +@implementation NSMutableURLRequest (MPCertificateAdditions) + +- (NSArray *)mp_SSLPinnedCertificates; +{ + return [NSURLProtocol propertyForKey:@"mp_SSLPinnedCertificates" inRequest:self]; +} + +- (void)setMp_SSLPinnedCertificates:(NSArray *)pinnedCertificates; +{ + [NSURLProtocol setProperty:pinnedCertificates forKey:@"mp_SSLPinnedCertificates" inRequest:self]; +} + +@end + +@implementation NSURL (MPWebSocket) + +- (NSString *)mp_origin; +{ + NSString *scheme = self.scheme.lowercaseString; + + if ([scheme isEqualToString:@"wss"]) { + scheme = @"https"; + } else if ([scheme isEqualToString:@"ws"]) { + scheme = @"http"; + } + + if (self.port) { + return [NSString stringWithFormat:@"%@://%@:%@/", scheme, self.host, self.port]; + } else { + return [NSString stringWithFormat:@"%@://%@/", scheme, self.host]; + } +} + +@end + +#ifdef HAS_ICU + +static inline int32_t validate_dispatch_data_partial_string(NSData *data) { + const void * contents = data.bytes; + long size = (long)data.length; + + const uint8_t *str = (const uint8_t *)contents; + + UChar32 codepoint = 1; + int32_t offset = 0; + int32_t lastOffset = 0; + while (offset < size && codepoint > 0) { + lastOffset = offset; + U8_NEXT(str, offset, size, codepoint); + } + + if (codepoint == -1) { + // Check to see if the last byte is valid or whether it was just continuing + if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) { + + size = -1; + } else { + uint8_t leadByte = str[lastOffset]; + U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte)); + + for (NSInteger i = lastOffset + 1; i < offset; i++) { + if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) { + size = -1; + } + } + + if (size != -1) { + size = lastOffset; + } + } + } + + if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:(NSUInteger)size encoding:NSUTF8StringEncoding freeWhenDone:NO]) { + size = -1; + } + + return (int32_t)size; +} + +#else + +// This is a hack, and probably not optimal +static inline int32_t validate_dispatch_data_partial_string(NSData *data) { + static const int maxCodepointSize = 3; + + for (NSInteger i = 0; i < maxCodepointSize; i++) { + NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO]; + if (str) { + return data.length - i; + } + } + + return -1; +} + +#endif + +static _MPRunLoopThread *networkThread = nil; +static NSRunLoop *networkRunLoop = nil; + +@implementation NSRunLoop (MPWebSocket) + ++ (NSRunLoop *)mp_networkRunLoop { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + networkThread = [[_MPRunLoopThread alloc] init]; + networkThread.name = @"com.mixpanel.WebSocket.NetworkThread"; + [networkThread start]; + networkRunLoop = networkThread.runLoop; + }); + + return networkRunLoop; +} + +@end + + +@implementation _MPRunLoopThread { + dispatch_group_t _waitGroup; +} + +@synthesize runLoop = _runLoop; + +- (void)dealloc +{ + mp_dispatch_release(_waitGroup); +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _waitGroup = dispatch_group_create(); + dispatch_group_enter(_waitGroup); + } + return self; +} + +- (void)main; +{ + @autoreleasepool { + _runLoop = [NSRunLoop currentRunLoop]; + dispatch_group_leave(_waitGroup); + + // Add an empty run loop source to prevent runloop from spinning. + CFRunLoopSourceContext sourceCtx = { + .version = 0, + .info = NULL, + .retain = NULL, + .release = NULL, + .copyDescription = NULL, + .equal = NULL, + .hash = NULL, + .schedule = NULL, + .cancel = NULL, + .perform = NULL + }; + CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); + CFRelease(source); + + while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) { + + } + assert(NO); + } +} + +- (NSRunLoop *)runLoop +{ + dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER); + return _runLoop; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.h b/ios/Pods/Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.h new file mode 100755 index 0000000..b05b6de --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.h @@ -0,0 +1,16 @@ +// +// Mixpanel+AutomaticTracks.h +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "Mixpanel.h" + +@interface Mixpanel (AutomaticTracks) + ++ (instancetype)sharedAutomatedInstance; ++ (void)setSharedAutomatedInstance:(Mixpanel *)instance; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.m b/ios/Pods/Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.m new file mode 100644 index 0000000..b682382 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/Mixpanel+AutomaticTracks.m @@ -0,0 +1,72 @@ +// +// Mixpanel+AutomaticTracks.m +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "Mixpanel+AutomaticTracks.h" +#import "UIApplication+AutomaticTracks.h" +#import "UIViewController+AutomaticTracks.h" +#import "NSNotificationCenter+AutomaticTracks.h" +#import "AutomaticTracksConstants.h" +#import "MPSwizzle.h" +#import "MPLogger.h" + +@implementation Mixpanel (AutomaticTracks) + +static Mixpanel *gSharedAutomatedInstance = nil; ++ (instancetype)sharedAutomatedInstance { + return gSharedAutomatedInstance; +} + ++ (void)setSharedAutomatedInstance:(Mixpanel *)instance { + gSharedAutomatedInstance = instance; + [self addSwizzles]; +} + ++ (void)addSwizzles { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + NSError *error = NULL; + + // Navigation + [UIViewController mp_swizzleMethod:@selector(viewDidAppear:) + withMethod:@selector(mp_viewDidAppear:) + error:&error]; + if (error) { + MPLogError(@"Failed to swizzle viewDidAppear: on UIViewController. Details: %@", error); + error = NULL; + } + + // Actions & Events + [UIApplication mp_swizzleMethod:@selector(sendAction:to:from:forEvent:) + withMethod:@selector(mp_sendAction:to:from:forEvent:) + error:&error]; + if (error) { + MPLogError(@"Failed to swizzle sendAction:to:from:forEvent: on UIAppplication. Details: %@", error); + error = NULL; + } + + // Notifications + [NSNotificationCenter mp_swizzleMethod:@selector(postNotification:) + withMethod:@selector(mp_postNotification:) + error:&error]; + if (error) { + MPLogError(@"Failed to swizzle postNotification: on NSNotificationCenter. Details: %@", error); + error = NULL; + } + + [NSNotificationCenter mp_swizzleMethod:@selector(postNotificationName:object:userInfo:) + withMethod:@selector(mp_postNotificationName:object:userInfo:) + error:&error]; + if (error) { + MPLogError(@"Failed to swizzle postNotificationName:object:userInfo: on NSNotificationCenter. Details: %@", error); + error = NULL; + } + }); +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/Mixpanel.h b/ios/Pods/Mixpanel/Mixpanel/Mixpanel.h new file mode 100644 index 0000000..b416018 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/Mixpanel.h @@ -0,0 +1,785 @@ +#import +#if !TARGET_OS_OSX +#import +#else +#import +#endif +#import "MixpanelPeople.h" + +#define MIXPANEL_FLUSH_IMMEDIATELY (defined(MIXPANEL_APP_EXTENSION) || defined(MIXPANEL_WATCHOS)) +#define MIXPANEL_NO_REACHABILITY_SUPPORT (defined(MIXPANEL_APP_EXTENSION) || defined(MIXPANEL_TVOS) || defined(MIXPANEL_WATCHOS) || defined(MIXPANEL_MACOS)) +#define MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT (defined(MIXPANEL_APP_EXTENSION) || defined(MIXPANEL_TVOS) || defined(MIXPANEL_WATCHOS) || defined(MIXPANEL_MACOS)) +#define MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT (defined(MIXPANEL_APP_EXTENSION) || defined(MIXPANEL_TVOS) || defined(MIXPANEL_WATCHOS) || defined(MIXPANEL_MACOS)) +#define MIXPANEL_NO_APP_LIFECYCLE_SUPPORT (defined(MIXPANEL_APP_EXTENSION) || defined(MIXPANEL_WATCHOS)) +#define MIXPANEL_NO_UIAPPLICATION_ACCESS (defined(MIXPANEL_APP_EXTENSION) || defined(MIXPANEL_WATCHOS) || defined(MIXPANEL_MACOS)) + +@class MixpanelPeople; +@protocol MixpanelDelegate; + +NS_ASSUME_NONNULL_BEGIN + +/*! + @class + Mixpanel API. + + @abstract + The primary interface for integrating Mixpanel with your app. + + @discussion + Use the Mixpanel class to set up your project and track events in Mixpanel + Engagement. It now also includes a people property for accessing + the Mixpanel People API. + +
+ // Initialize the API
+ Mixpanel *mixpanel = [Mixpanel sharedInstanceWithToken:@"YOUR API TOKEN"];
+
+ // Track an event in Mixpanel Engagement
+ [mixpanel track:@"Button Clicked"];
+
+ // Set properties on a user in Mixpanel People
+ [mixpanel identify:@"CURRENT USER DISTINCT ID"];
+ [mixpanel.people set:@"Plan" to:@"Premium"];
+ 
+ + For more advanced usage, please see the Mixpanel iPhone + Library Guide. + */ +@interface Mixpanel : NSObject + +#pragma mark Properties + +/*! + @property + + @abstract + Accessor to the Mixpanel People API object. + + @discussion + See the documentation for MixpanelDelegate below for more information. + */ +@property (atomic, readonly, strong) MixpanelPeople *people; + +/*! + @property + + @abstract + The distinct ID of the current user. + + @discussion + A distinct ID is a string that uniquely identifies one of your users. By default, + we'll use the device's advertisingIdentifier UUIDString, if that is not available + we'll use the device's identifierForVendor UUIDString, and finally if that + is not available we will generate a new random UUIDString. To change the + current distinct ID, use the identify: method. + */ +@property (atomic, readonly, copy) NSString *distinctId; + +/*! + @property + + @abstract + The alias of the current user. + + @discussion + An alias is another string that uniquely identifies one of your users. Typically, + this is the user ID from your database. By using an alias you can link pre- and + post-sign up activity as well as cross-platform activity under one distinct ID. + To set the alias use the createAlias:forDistinctID: method. + */ +@property (atomic, readonly, copy) NSString *alias; + +/*! + @property + + @abstract + The base URL used for Mixpanel API requests. + + @discussion + Useful if you need to proxy Mixpanel requests. Defaults to + https://api.mixpanel.com. + */ +@property (nonatomic, copy) NSString *serverURL; + +/*! + @property + + @abstract + Flush timer's interval. + + @discussion + Setting a flush interval of 0 will turn off the flush timer. + */ +@property (atomic) NSUInteger flushInterval; + +/*! + @property + + @abstract + Control whether the library should flush data to Mixpanel when the app + enters the background. + + @discussion + Defaults to YES. Only affects apps targeted at iOS 4.0, when background + task support was introduced, and later. + */ +@property (atomic) BOOL flushOnBackground; + +/*! + @property + + @abstract + Controls whether to show spinning network activity indicator when flushing + data to the Mixpanel servers. + + @discussion + Defaults to YES. + */ +@property (atomic) BOOL shouldManageNetworkActivityIndicator; + +/*! + @property + + @abstract + Controls whether to automatically check for notifications for the + currently identified user when the application becomes active. + + @discussion + Defaults to YES. Will fire a network request on + applicationDidBecomeActive to retrieve a list of valid notifications + for the currently identified user. + */ +@property (atomic) BOOL checkForNotificationsOnActive; + +/*! + @property + + @abstract + Controls whether to automatically check for A/B test variants for the + currently identified user when the application becomes active. + + @discussion + Defaults to YES. Will fire a network request on + applicationDidBecomeActive to retrieve a list of valid variants + for the currently identified user. + */ +@property (atomic) BOOL checkForVariantsOnActive; + +/*! + @property + + @abstract + Controls whether to automatically check for and show in-app notifications + for the currently identified user when the application becomes active. + + @discussion + Defaults to YES. + */ +@property (atomic) BOOL showNotificationOnActive; + +/*! + @property + + @abstract + Controls whether to automatically send the client IP Address as part of + event tracking. With an IP address, geo-location is possible down to neighborhoods + within a city, although the Mixpanel Dashboard will just show you city level location + specificity. For privacy reasons, you may be in a situation where you need to forego + effectively having access to such granular location information via the IP Address. + + @discussion + Defaults to YES. + */ +@property (atomic) BOOL useIPAddressForGeoLocation; + +/*! + @property + + @abstract + Controls whether to enable the visual test designer for A/B testing and codeless on mixpanel.com. + You will be unable to edit A/B tests and codeless events with this disabled, however *previously* + created A/B tests and codeless events will still be delivered. + + @discussion + Defaults to YES. + */ +@property (atomic) BOOL enableVisualABTestAndCodeless; + +/*! + @property + + @abstract + Controls whether to enable the run time debug logging at all levels. Note that the + Mixpanel SDK uses Apple System Logging to forward log messages to `STDERR`, this also + means that mixpanel logs are segmented by log level. Settings this to `YES` will enable + Mixpanel logging at the following levels: + + * Error - Something has failed + * Warning - Something is amiss and might fail if not corrected + * Info - The lowest priority that is normally logged, purely informational in nature + * Debug - Information useful only to developers, and normally not logged. + + + @discussion + Defaults to NO. + */ +@property (atomic) BOOL enableLogging; + +/*! + @property + + @abstract + Determines the time, in seconds, that a mini notification will remain on + the screen before automatically hiding itself. + + @discussion + Defaults to 6.0. + */ +@property (atomic) CGFloat miniNotificationPresentationTime; + +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT +/*! + @property + + @abstract + The minimum session duration (ms) that is tracked in automatic events. + + @discussion + The default value is 10000 (10 seconds). + */ +@property (atomic) UInt64 minimumSessionDuration; + +/*! + @property + + @abstract + The maximum session duration (ms) that is tracked in automatic events. + + @discussion + The default value is UINT64_MAX (no maximum session duration). + */ +@property (atomic) UInt64 maximumSessionDuration; +#endif + +/*! + @property + + @abstract + The a MixpanelDelegate object that can be used to assert fine-grain control + over Mixpanel network activity. + + @discussion + Using a delegate is optional. See the documentation for MixpanelDelegate + below for more information. + */ +@property (atomic, weak) id delegate; // allows fine grain control over uploading (optional) + +#pragma mark Tracking + +/*! + @method + + @abstract + Returns (and creates, if needed) a singleton instance of the API. + + @discussion + This method will return a singleton instance of the Mixpanel class for + you using the given project token. If an instance does not exist, this method will create + one using initWithToken:launchOptions:andFlushInterval:. If you only have one + instance in your project, you can use sharedInstance to retrieve it. + +
+ [Mixpanel sharedInstance] track:@"Something Happened"]];
+ 
+ + If you are going to use this singleton approach, + sharedInstanceWithToken: must be the first call to the + Mixpanel class, since it performs important initializations to + the API. + + @param apiToken your project token + */ ++ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken; + +/*! + @method + + @abstract + Initializes a singleton instance of the API, uses it to track launchOptions information, + and then returns it. + + @discussion + This is the preferred method for creating a sharedInstance with a mixpanel + like above. With the launchOptions parameter, Mixpanel can track referral + information created by push notifications. + + @param apiToken your project token + @param launchOptions your application delegate's launchOptions + + */ ++ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken launchOptions:(nullable NSDictionary *)launchOptions; + +/*! + @method + + @abstract + Returns a previously instantiated singleton instance of the API. + + @discussion + The API must be initialized with sharedInstanceWithToken: or + initWithToken:launchOptions:andFlushInterval before calling this class method. + This method will return nil if there are no instances created. If there is more than + one instace, it will return the first one that was created by using sharedInstanceWithToken: + or initWithToken:launchOptions:andFlushInterval:. + */ ++ (nullable Mixpanel *)sharedInstance; + +/*! + @method + + @abstract + Initializes an instance of the API with the given project token. + + @discussion + Creates and initializes a new API object. See also sharedInstanceWithToken:. + + @param apiToken your project token + @param launchOptions optional app delegate launchOptions + @param flushInterval interval to run background flushing + */ +- (instancetype)initWithToken:(NSString *)apiToken launchOptions:(nullable NSDictionary *)launchOptions andFlushInterval:(NSUInteger)flushInterval; + +/*! + @method + + @abstract + Initializes an instance of the API with the given project token. + + @discussion + Supports for the old initWithToken method format but really just passes + launchOptions to the above method as nil. + + @param apiToken your project token + @param flushInterval interval to run background flushing + */ +- (instancetype)initWithToken:(NSString *)apiToken andFlushInterval:(NSUInteger)flushInterval; + +/*! + @property + + @abstract + Sets the distinct ID of the current user. + + @discussion + As of version 2.3.1, Mixpanel will choose a default distinct ID based on + whether you are using the AdSupport.framework or not. + + If you are not using the AdSupport Framework (iAds), then we use the + [UIDevice currentDevice].identifierForVendor (IFV) string as the + default distinct ID. This ID will identify a user across all apps by the same + vendor, but cannot be used to link the same user across apps from different + vendors. + + If you are showing iAds in your application, you are allowed use the iOS ID + for Advertising (IFA) to identify users. If you have this framework in your + app, Mixpanel will use the IFA as the default distinct ID. If you have + AdSupport installed but still don't want to use the IFA, you can define the + MIXPANEL_NO_IFA preprocessor flag in your build settings, and + Mixpanel will use the IFV as the default distinct ID. + + If we are unable to get an IFA or IFV, we will fall back to generating a + random persistent UUID. + + For tracking events, you do not need to call identify: if you + want to use the default. However, Mixpanel People always requires an + explicit call to identify:. If calls are made to + set:, increment or other MixpanelPeople + methods prior to calling identify:, then they are queued up and + flushed once identify: is called. + + If you'd like to use the default distinct ID for Mixpanel People as well + (recommended), call identify: using the current distinct ID: + [mixpanel identify:mixpanel.distinctId]. + + @param distinctId string that uniquely identifies the current user + */ +- (void)identify:(NSString *)distinctId; + +/*! + @method + + @abstract + Tracks an event. + + @param event event name + */ +- (void)track:(NSString *)event; + +/*! + @method + + @abstract + Tracks an event with properties. + + @discussion + Properties will allow you to segment your events in your Mixpanel reports. + Property keys must be NSString objects and values must be + NSString, NSNumber, NSNull, + NSArray, NSDictionary, NSDate or + NSURL objects. If the event is being timed, the timer will + stop and be added as a property. + + @param event event name + @param properties properties dictionary + */ +- (void)track:(NSString *)event properties:(nullable NSDictionary *)properties; + + +/*! + @method + + @abstract + Track a push notification using its payload sent from Mixpanel. + + @discussion + To simplify user interaction tracking and a/b testing, Mixpanel + automatically sends IDs for the relevant notification and a/b variants + of each push. This method parses the standard payload and queues a + track call using this information. + + @param userInfo remote notification payload dictionary + */ +- (void)trackPushNotification:(NSDictionary *)userInfo; + + +/*! + @method + + @abstract + Registers super properties, overwriting ones that have already been set. + + @discussion + Super properties, once registered, are automatically sent as properties for + all event tracking calls. They save you having to maintain and add a common + set of properties to your events. Property keys must be NSString + objects and values must be NSString, NSNumber, + NSNull, NSArray, NSDictionary, + NSDate or NSURL objects. + + @param properties properties dictionary + */ +- (void)registerSuperProperties:(NSDictionary *)properties; + +/*! + @method + + @abstract + Registers super properties without overwriting ones that have already been + set. + + @discussion + Property keys must be NSString objects and values must be + NSString, NSNumber, NSNull, + NSArray, NSDictionary, NSDate or + NSURL objects. + + @param properties properties dictionary + */ +- (void)registerSuperPropertiesOnce:(NSDictionary *)properties; + +/*! + @method + + @abstract + Registers super properties without overwriting ones that have already been set + unless the existing value is equal to defaultValue. + + @discussion + Property keys must be NSString objects and values must be + NSString, NSNumber, NSNull, + NSArray, NSDictionary, NSDate or + NSURL objects. + + @param properties properties dictionary + @param defaultValue overwrite existing properties that have this value + */ +- (void)registerSuperPropertiesOnce:(NSDictionary *)properties defaultValue:(nullable id)defaultValue; + +/*! + @method + + @abstract + Removes a previously registered super property. + + @discussion + As an alternative to clearing all properties, unregistering specific super + properties prevents them from being recorded on future events. This operation + does not affect the value of other super properties. Any property name that is + not registered is ignored. + + Note that after removing a super property, events will show the attribute as + having the value undefined in Mixpanel until a new value is + registered. + + @param propertyName array of property name strings to remove + */ +- (void)unregisterSuperProperty:(NSString *)propertyName; + +/*! + @method + + @abstract + Clears all currently set super properties. + */ +- (void)clearSuperProperties; + +/*! + @method + + @abstract + Returns the currently set super properties. + */ +- (NSDictionary *)currentSuperProperties; + +/*! + @method + + @abstract + Starts a timer that will be stopped and added as a property when a + corresponding event is tracked. + + @discussion + This method is intended to be used in advance of events that have + a duration. For example, if a developer were to track an "Image Upload" event + she might want to also know how long the upload took. Calling this method + before the upload code would implicitly cause the track + call to record its duration. + +
+ // begin timing the image upload
+ [mixpanel timeEvent:@"Image Upload"];
+
+ // upload the image
+ [self uploadImageWithSuccessHandler:^{
+
+    // track the event
+    [mixpanel track:@"Image Upload"];
+ }];
+ 
+ + @param event a string, identical to the name of the event that will be tracked + + */ +- (void)timeEvent:(NSString *)event; + +/*! + @method + + @abstract + Clears all current event timers. + */ +- (void)clearTimedEvents; + +/*! + @method + + @abstract + Clears all stored properties and distinct IDs. Useful if your app's user logs out. + */ +- (void)reset; + +/*! + @method + + @abstract + Uploads queued data to the Mixpanel server. + + @discussion + By default, queued data is flushed to the Mixpanel servers every minute (the + default for flushInterval), and on background (since + flushOnBackground is on by default). You only need to call this + method manually if you want to force a flush at a particular moment. + */ +- (void)flush; + +/*! + @method + + @abstract + Calls flush, then optionally archives and calls a handler when finished. + + @discussion + When calling flush manually, it is sometimes important to verify + that the flush has finished before further action is taken. This is + especially important when the app is in the background and could be suspended + at any time if protocol is not followed. Delegate methods like + application:didReceiveRemoteNotification:fetchCompletionHandler: + are called when an app is brought to the background and require a handler to + be called when it finishes. + */ +- (void)flushWithCompletion:(nullable void (^)())handler; + +/*! + @method + + @abstract + Writes current project info, including distinct ID, super properties and pending event + and People record queues to disk. + + @discussion + This state will be recovered when the app is launched again if the Mixpanel + library is initialized with the same project token. You do not need to call + this method. The library listens for app state changes and handles + persisting data as needed. It can be useful in some special circumstances, + though, for example, if you'd like to track app crashes from main.m. + */ +- (void)archive; + +/*! + @method + + @abstract + Creates a distinct_id alias from alias to original id. + + @discussion + This method is used to map an identifier called an alias to the existing Mixpanel + distinct id. This causes all events and people requests sent with the alias to be + mapped back to the original distinct id. The recommended usage pattern is to call + both createAlias: and identify: when the user signs up, and only identify: (with + their new user ID) when they log in. This will keep your signup funnels working + correctly. + +
+ // This makes the current ID (an auto-generated GUID)
+ // and 'Alias' interchangeable distinct ids.
+ [mixpanel createAlias:@"Alias"
+    forDistinctID:mixpanel.distinctId];
+
+ // You must call identify if you haven't already
+ // (e.g., when your app launches).
+ [mixpanel identify:mixpanel.distinctId];
+
+ +@param alias the new distinct_id that should represent original +@param distinctID the old distinct_id that alias will be mapped to + */ +- (void)createAlias:(NSString *)alias forDistinctID:(NSString *)distinctID; + +- (NSString *)libVersion; ++ (NSString *)libVersion; + + +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT +#pragma mark - Mixpanel Notifications + +/*! + @method + + @abstract + Shows the notification of the given id. + + @discussion + You do not need to call this method on the main thread. + */ +- (void)showNotificationWithID:(NSUInteger)ID; + + +/*! + @method + + @abstract + Shows a notification with the given type if one is available. + + @discussion + You do not need to call this method on the main thread. + + @param type The type of notification to show, either @"mini", or @"takeover" + */ +- (void)showNotificationWithType:(NSString *)type; + +/*! + @method + + @abstract + Shows a notification if one is available. + + @discussion + You do not need to call this method on the main thread. + */ +- (void)showNotification; + +#pragma mark - Mixpanel A/B Testing + +/*! + @method + + @abstract + Join any experiments (A/B tests) that are available for the current user. + + @discussion + Mixpanel will check for A/B tests automatically when your app enters + the foreground. Call this method if you would like to to check for, + and join, any experiments are newly available for the current user. + + You do not need to call this method on the main thread. + */ +- (void)joinExperiments; + +/*! + @method + + @abstract + Join any experiments (A/B tests) that are available for the current user. + + @discussion + Same as joinExperiments but will fire the given callback after all experiments + have been loaded and applied. + */ +- (void)joinExperimentsWithCallback:(nullable void (^)())experimentsLoadedCallback; + +#endif // MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT + +#pragma mark - Deprecated +/*! + @property + + @abstract + Current user's name in Mixpanel Streams. + */ +@property (nullable, atomic, copy) NSString *nameTag __deprecated; // Deprecated in v3.0.1 + +@end + +/*! + @protocol + + @abstract + Delegate protocol for controlling the Mixpanel API's network behavior. + + @discussion + Creating a delegate for the Mixpanel object is entirely optional. It is only + necessary when you want full control over when data is uploaded to the server, + beyond simply calling stop: and start: before and after a particular block of + your code. + */ + +@protocol MixpanelDelegate + +@optional +/*! + @method + + @abstract + Asks the delegate if data should be uploaded to the server. + + @discussion + Return YES to upload now, NO to defer until later. + + @param mixpanel Mixpanel API instance + */ +- (BOOL)mixpanelWillFlush:(Mixpanel *)mixpanel; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Mixpanel/Mixpanel/Mixpanel.m b/ios/Pods/Mixpanel/Mixpanel/Mixpanel.m new file mode 100755 index 0000000..cce533a --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/Mixpanel.m @@ -0,0 +1,1841 @@ +#include +#include +#include +#include +#include + +#import "Mixpanel.h" +#import "MixpanelPrivate.h" +#import "MixpanelPeople.h" +#import "MixpanelPeoplePrivate.h" +#import "MPNetworkPrivate.h" + +#import "MPLogger.h" +#import "MPFoundation.h" + +#if defined(MIXPANEL_WATCHOS) +#import "MixpanelWatchProperties.h" +#import +#elif defined(MIXPANEL_MACOS) +#import +#endif + +#define VERSION @"3.1.5" + +@implementation Mixpanel + +static NSMutableDictionary *instances; +static NSString *defaultProjectToken; + ++ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken launchOptions:(NSDictionary *)launchOptions +{ + if (instances[apiToken]) { + return instances[apiToken]; + } + +#if defined(DEBUG) + const NSUInteger flushInterval = 1; +#else + const NSUInteger flushInterval = 60; +#endif + + return [[self alloc] initWithToken:apiToken launchOptions:launchOptions andFlushInterval:flushInterval]; +} + ++ (Mixpanel *)sharedInstanceWithToken:(NSString *)apiToken +{ + return [Mixpanel sharedInstanceWithToken:apiToken launchOptions:nil]; +} + ++ (nullable Mixpanel *)sharedInstance +{ + if (instances.count == 0) { + MPLogWarning(@"sharedInstance called before creating a Mixpanel instance"); + return nil; + } + + if (instances.count > 1) { + MPLogWarning([NSString stringWithFormat:@"sharedInstance called with multiple mixpanel instances. Using (the first) token %@", defaultProjectToken]); + } + + return instances[defaultProjectToken]; +} + +- (instancetype)init:(NSString *)apiToken +{ + if (self = [super init]) { + self.eventsQueue = [NSMutableArray array]; + self.peopleQueue = [NSMutableArray array]; + self.timedEvents = [NSMutableDictionary dictionary]; + self.shownNotifications = [NSMutableSet set]; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instances = [NSMutableDictionary dictionary]; + defaultProjectToken = apiToken; + }); + } + + return self; +} + +- (instancetype)initWithToken:(NSString *)apiToken launchOptions:(NSDictionary *)launchOptions andFlushInterval:(NSUInteger)flushInterval +{ + if (apiToken.length == 0) { + if (apiToken == nil) { + apiToken = @""; + } + MPLogWarning(@"%@ empty api token", self); + } + if (self = [self init:apiToken]) { +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT + // Install uncaught exception handlers first + [[MixpanelExceptionHandler sharedHandler] addMixpanelInstance:self]; +#endif +#if !MIXPANEL_NO_REACHABILITY_SUPPORT + self.telephonyInfo = [[CTTelephonyNetworkInfo alloc] init]; +#endif + self.apiToken = apiToken; + _flushInterval = flushInterval; + self.useIPAddressForGeoLocation = YES; + self.shouldManageNetworkActivityIndicator = YES; + self.flushOnBackground = YES; + + self.serverURL = @"https://api.mixpanel.com"; + self.switchboardURL = @"wss://switchboard.mixpanel.com"; + + self.showNotificationOnActive = YES; + self.checkForNotificationsOnActive = YES; + self.checkForVariantsOnActive = YES; + self.miniNotificationPresentationTime = 6.0; + + self.distinctId = [self defaultDistinctId]; + self.superProperties = [NSMutableDictionary dictionary]; + self.automaticProperties = [self collectAutomaticProperties]; + +#if !defined(MIXPANEL_WATCHOS) && !defined(MIXPANEL_MACOS) + self.taskId = UIBackgroundTaskInvalid; +#endif + NSString *label = [NSString stringWithFormat:@"com.mixpanel.%@.%p", apiToken, (void *)self]; + self.serialQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_SERIAL); + NSString *networkLabel = [label stringByAppendingString:@".network"]; + self.networkQueue = dispatch_queue_create([networkLabel UTF8String], DISPATCH_QUEUE_SERIAL); + +#if defined(DISABLE_MIXPANEL_AB_DESIGNER) // Deprecated in v3.0.1 + self.enableVisualABTestAndCodeless = NO; +#else + self.enableVisualABTestAndCodeless = YES; +#endif + + self.network = [[MPNetwork alloc] initWithServerURL:[NSURL URLWithString:self.serverURL] mixpanel:self]; + self.people = [[MixpanelPeople alloc] initWithMixpanel:self]; + [self setUpListeners]; + [self unarchive]; +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT + self.automaticEvents = [[AutomaticEvents alloc] init]; + self.automaticEvents.delegate = self; + [self.automaticEvents initializeEvents:self.people]; +#endif +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT + [self executeCachedVariants]; + [self executeCachedEventBindings]; + + NSDictionary *remoteNotification = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; + if (remoteNotification) { + [self trackPushNotification:remoteNotification event:@"$app_open"]; + } +#endif + instances[apiToken] = self; + } + return self; +} + +- (instancetype)initWithToken:(NSString *)apiToken andFlushInterval:(NSUInteger)flushInterval +{ + return [self initWithToken:apiToken launchOptions:nil andFlushInterval:flushInterval]; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + +#if !MIXPANEL_NO_REACHABILITY_SUPPORT + if (_reachability != NULL) { + if (!SCNetworkReachabilitySetCallback(_reachability, NULL, NULL)) { + MPLogError(@"%@ error unsetting reachability callback", self); + } + if (!SCNetworkReachabilitySetDispatchQueue(_reachability, NULL)) { + MPLogError(@"%@ error unsetting reachability dispatch queue", self); + } + CFRelease(_reachability); + _reachability = NULL; + } +#endif +} + +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT +- (void)setValidationEnabled:(BOOL)validationEnabled { + _validationEnabled = validationEnabled; + + if (_validationEnabled) { + [Mixpanel setSharedAutomatedInstance:self]; + } else { + [Mixpanel setSharedAutomatedInstance:nil]; + } +} +#endif + +- (BOOL)shouldManageNetworkActivityIndicator { + return self.network.shouldManageNetworkActivityIndicator; +} + +- (void)setShouldManageNetworkActivityIndicator:(BOOL)shouldManageNetworkActivityIndicator { + self.network.shouldManageNetworkActivityIndicator = shouldManageNetworkActivityIndicator; +} + +- (BOOL)useIPAddressForGeoLocation { + return self.network.useIPAddressForGeoLocation; +} + +- (void)setUseIPAddressForGeoLocation:(BOOL)useIPAddressForGeoLocation { + self.network.useIPAddressForGeoLocation = useIPAddressForGeoLocation; +} + +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT +- (UInt64)minimumSessionDuration { + return self.automaticEvents.minimumSessionDuration; +} + +- (void)setMinimumSessionDuration:(UInt64)minimumSessionDuration { + self.automaticEvents.minimumSessionDuration = minimumSessionDuration; +} + +- (UInt64)maximumSessionDuration { + return self.automaticEvents.maximumSessionDuration; +} + +- (void)setMaximumSessionDuration:(UInt64)maximumSessionDuration { + self.automaticEvents.maximumSessionDuration = maximumSessionDuration; +} +#endif + +#pragma mark - Tracking ++ (void)assertPropertyTypes:(NSDictionary *)properties +{ + [Mixpanel assertPropertyTypesInDictionary:properties depth:0]; +} + ++ (void)assertPropertyType:(id)propertyValue depth:(NSUInteger)depth +{ + // Note that @YES and @NO pass as instances of NSNumber class. + NSAssert([propertyValue isKindOfClass:[NSString class]] || + [propertyValue isKindOfClass:[NSNumber class]] || + [propertyValue isKindOfClass:[NSNull class]] || + [propertyValue isKindOfClass:[NSArray class]] || + [propertyValue isKindOfClass:[NSDictionary class]] || + [propertyValue isKindOfClass:[NSDate class]] || + [propertyValue isKindOfClass:[NSURL class]], + @"%@ property values must be NSString, NSNumber, NSNull, NSArray, NSDictionary, NSDate or NSURL. got: %@ %@", self, [propertyValue class], propertyValue); + +#ifdef DEBUG + if (depth == 3) { + MPLogWarning(@"Your properties are overly nested, specifically 3 or more levels deep. \ + Generally this is not recommended due to its complexity."); + } + if ([propertyValue isKindOfClass:[NSDictionary class]]) { + [Mixpanel assertPropertyTypesInDictionary:propertyValue depth:depth+1]; + } else if ([propertyValue isKindOfClass:[NSArray class]]) { + [Mixpanel assertPropertyTypesInArray:propertyValue depth:depth+1]; + } +#endif +} + ++ (void)assertPropertyTypesInDictionary:(NSDictionary *)properties depth:(NSUInteger)depth +{ + if([properties count] > 1000) { + MPLogWarning(@"You have an NSDictionary in your properties that is bigger than 1000 in size. \ + Generally this is not recommended due to its size."); + } + for (id key in properties) { + id value = properties[key]; + NSAssert([key isKindOfClass:[NSString class]], @"%@ property keys must be NSString. got: %@ %@", self, [key class], key); + [Mixpanel assertPropertyType:value depth:depth]; + } +} + ++ (void)assertPropertyTypesInArray:(NSArray *)arrayOfProperties depth:(NSUInteger)depth +{ + if([arrayOfProperties count] > 1000) { + MPLogWarning(@"You have an NSArray in your properties that is bigger than 1000 in size. \ + Generally this is not recommended due to its size."); + } + for (id value in arrayOfProperties) { + [Mixpanel assertPropertyType:value depth:depth]; + } +} + +- (NSString *)defaultDistinctId +{ + NSString *distinctId; +#if defined(MIXPANEL_MACOS) + distinctId = [self macOSIdentifier]; +#else + distinctId = [self IFA]; +#endif + +#if !defined(MIXPANEL_WATCHOS) && !defined(MIXPANEL_MACOS) + if (!distinctId && NSClassFromString(@"UIDevice")) { + distinctId = [[UIDevice currentDevice].identifierForVendor UUIDString]; + } +#endif + if (!distinctId) { + MPLogInfo(@"%@ error getting device identifier: falling back to uuid", self); + distinctId = [[NSUUID UUID] UUIDString]; + } + return distinctId; +} + +- (void)identify:(NSString *)distinctId +{ + if (distinctId.length == 0) { + MPLogWarning(@"%@ cannot identify blank distinct id: %@", self, distinctId); + return; + } + + dispatch_async(self.serialQueue, ^{ + // identify only changes the distinct id if it doesn't match either the existing or the alias; + // if it's new, blow away the alias as well. + if (![distinctId isEqualToString:self.alias]) { + if (![distinctId isEqualToString:self.distinctId]) { + self.alias = nil; + self.distinctId = distinctId; + } + self.people.distinctId = distinctId; + } + if (self.people.unidentifiedQueue.count > 0) { + for (NSMutableDictionary *r in self.people.unidentifiedQueue) { + r[@"$distinct_id"] = self.distinctId; + @synchronized (self) { + [self.peopleQueue addObject:r]; + } + } + [self.people.unidentifiedQueue removeAllObjects]; + [self archivePeople]; + } + [self archiveProperties]; + }); +#if MIXPANEL_FLUSH_IMMEDIATELY + [self flush]; +#endif +} + +- (void)createAlias:(NSString *)alias forDistinctID:(NSString *)distinctID +{ + if (alias.length == 0) { + MPLogError(@"%@ create alias called with empty alias: %@", self, alias); + return; + } + if (distinctID.length == 0) { + MPLogError(@"%@ create alias called with empty distinct id: %@", self, distinctID); + return; + } + if (![alias isEqualToString:distinctID]) { + dispatch_async(self.serialQueue, ^{ + self.alias = alias; + [self archiveProperties]; + }); + [self track:@"$create_alias" properties:@{ @"distinct_id": distinctID, @"alias": alias }]; + [self flush]; + } else { + MPLogWarning(@"alias: %@ matches distinctID: %@ - skipping api call.", alias, distinctID); + } +} + +- (void)track:(NSString *)event +{ + [self track:event properties:nil]; +} + +- (void)track:(NSString *)event properties:(NSDictionary *)properties +{ + if (event.length == 0) { + MPLogWarning(@"%@ mixpanel track called with empty event parameter. using 'mp_event'", self); + event = @"mp_event"; + } + +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT + // Safety check + BOOL isAutomaticTrack = [event isEqualToString:kAutomaticTrackName]; + if (isAutomaticTrack && !self.isValidationEnabled) return; +#endif + + properties = [properties copy]; + [Mixpanel assertPropertyTypes:properties]; + + NSTimeInterval epochInterval = [[NSDate date] timeIntervalSince1970]; + NSNumber *epochSeconds = @(round(epochInterval)); + dispatch_async(self.serialQueue, ^{ + NSNumber *eventStartTime = self.timedEvents[event]; + NSMutableDictionary *p = [NSMutableDictionary dictionaryWithDictionary:self.automaticProperties]; + p[@"token"] = self.apiToken; + p[@"time"] = epochSeconds; + if (eventStartTime) { + [self.timedEvents removeObjectForKey:event]; + p[@"$duration"] = @([[NSString stringWithFormat:@"%.3f", epochInterval - [eventStartTime doubleValue]] floatValue]); + } + if (self.distinctId) { + p[@"distinct_id"] = self.distinctId; + } + [p addEntriesFromDictionary:self.superProperties]; + if (properties) { + [p addEntriesFromDictionary:properties]; + } + +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT + if (self.validationEnabled) { + if (self.validationMode == AutomaticTrackModeCount) { + if (isAutomaticTrack) { + self.validationEventCount++; + } else { + if (self.validationEventCount > 0) { + p[@"$__c"] = @(self.validationEventCount); + self.validationEventCount = 0; + } + } + } + } +#endif + + NSDictionary *e = @{ @"event": event, @"properties": [NSDictionary dictionaryWithDictionary:p]} ; + MPLogInfo(@"%@ queueing event: %@", self, e); + @synchronized (self) { + [self.eventsQueue addObject:e]; + if (self.eventsQueue.count > 5000) { + [self.eventsQueue removeObjectAtIndex:0]; + } + } + + // Always archive + [self archiveEvents]; + }); +#if MIXPANEL_FLUSH_IMMEDIATELY + [self flush]; +#endif +} + + +- (void)trackPushNotification:(NSDictionary *)userInfo event:(NSString *)event +{ + MPLogInfo(@"%@ tracking push payload %@", self, userInfo); + + id rawMp = userInfo[@"mp"]; + if (rawMp) { + + NSDictionary *mpPayload = [rawMp isKindOfClass:[NSDictionary class]] ? rawMp : nil; + + if (mpPayload[@"m"] && mpPayload[@"c"]) { + [self track:event properties:@{@"campaign_id": mpPayload[@"c"], + @"message_id": mpPayload[@"m"], + @"message_type": @"push"}]; + } else { + MPLogInfo(@"%@ malformed mixpanel push payload %@", self, mpPayload); + } + } +} + +- (void)trackPushNotification:(NSDictionary *)userInfo +{ + [self trackPushNotification:userInfo event:@"$campaign_received"]; +} + +- (void)registerSuperProperties:(NSDictionary *)properties +{ + properties = [properties copy]; + [Mixpanel assertPropertyTypes:properties]; + dispatch_async(self.serialQueue, ^{ + NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties]; + [tmp addEntriesFromDictionary:properties]; + self.superProperties = [NSDictionary dictionaryWithDictionary:tmp]; + [self archiveProperties]; + }); +} + +- (void)registerSuperPropertiesOnce:(NSDictionary *)properties +{ + [self registerSuperPropertiesOnce:properties defaultValue:nil]; +} + +- (void)registerSuperPropertiesOnce:(NSDictionary *)properties defaultValue:(id)defaultValue +{ + properties = [properties copy]; + [Mixpanel assertPropertyTypes:properties]; + dispatch_async(self.serialQueue, ^{ + NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties]; + for (NSString *key in properties) { + id value = tmp[key]; + if (value == nil || [value isEqual:defaultValue]) { + tmp[key] = properties[key]; + } + } + self.superProperties = [NSDictionary dictionaryWithDictionary:tmp]; + [self archiveProperties]; + }); +} + +- (void)unregisterSuperProperty:(NSString *)propertyName +{ + dispatch_async(self.serialQueue, ^{ + NSMutableDictionary *tmp = [NSMutableDictionary dictionaryWithDictionary:self.superProperties]; + tmp[propertyName] = nil; + self.superProperties = [NSDictionary dictionaryWithDictionary:tmp]; + [self archiveProperties]; + }); +} + +- (void)clearSuperProperties +{ + dispatch_async(self.serialQueue, ^{ + self.superProperties = @{}; + [self archiveProperties]; + }); +} + +- (NSDictionary *)currentSuperProperties +{ + return [self.superProperties copy]; +} + +- (void)timeEvent:(NSString *)event +{ + NSNumber *startTime = @([[NSDate date] timeIntervalSince1970]); + + if (event.length == 0) { + MPLogError(@"Mixpanel cannot time an empty event"); + return; + } + dispatch_async(self.serialQueue, ^{ + self.timedEvents[event] = startTime; + }); +} + +- (void)clearTimedEvents +{ dispatch_async(self.serialQueue, ^{ + self.timedEvents = [NSMutableDictionary dictionary]; + }); +} + +- (void)reset +{ + [self flush]; + dispatch_async(self.serialQueue, ^{ + // wait for all current network requests to finish before resetting + dispatch_sync(self.networkQueue, ^{ return; }); + + self.distinctId = [self defaultDistinctId]; + self.superProperties = [NSMutableDictionary dictionary]; + self.people.distinctId = nil; + self.alias = nil; + self.people.unidentifiedQueue = [NSMutableArray array]; + self.eventsQueue = [NSMutableArray array]; + self.peopleQueue = [NSMutableArray array]; + self.timedEvents = [NSMutableDictionary dictionary]; + self.shownNotifications = [NSMutableSet set]; + self.decideResponseCached = NO; + self.variants = [NSSet set]; + self.eventBindings = [NSSet set]; +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT + [[MPTweakStore sharedInstance] reset]; +#endif + [self archive]; + }); +} + +- (void)dispatchOnNetworkQueue:(void (^)(void))dispatchBlock { + // so this looks stupid but to make [Mixpanel track]; [Mixpanel flush]; continue to have the track + // guaranteed to be part of the flush we need to make networkQueue stuff be dispatched on serialQueue + // first. still will allow serialQueue stuff to happen at the same time as networkQueue stuff just + // don't want to change track -> flush behavior that people may be relying on + dispatch_async(self.serialQueue, ^{ + dispatch_async(self.networkQueue, dispatchBlock); + }); +} + +#pragma mark - Network control +- (void)setServerURL:(NSString *)serverURL +{ + _serverURL = serverURL.copy; + self.network = [[MPNetwork alloc] initWithServerURL:[NSURL URLWithString:serverURL] mixpanel:self]; +} + +- (NSUInteger)flushInterval { + return _flushInterval; +} + +- (void)setFlushInterval:(NSUInteger)interval +{ + @synchronized (self) { + _flushInterval = interval; + } + [self flush]; + [self startFlushTimer]; +} + +- (void)startFlushTimer +{ + [self stopFlushTimer]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.flushInterval > 0) { + self.timer = [NSTimer scheduledTimerWithTimeInterval:self.flushInterval + target:self + selector:@selector(flush) + userInfo:nil + repeats:YES]; + MPLogInfo(@"%@ started flush timer: %@", self, self.timer); + } + }); +} + +- (void)stopFlushTimer +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.timer) { + [self.timer invalidate]; + MPLogInfo(@"%@ stopped flush timer: %@", self, self.timer); + self.timer = nil; + } + }); +} + +- (void)flush +{ + [self flushWithCompletion:nil]; +} + +- (void)flushWithCompletion:(void (^)())handler +{ + [self dispatchOnNetworkQueue:^{ + MPLogInfo(@"%@ flush starting", self); + + __strong id strongDelegate = self.delegate; + if (strongDelegate && [strongDelegate respondsToSelector:@selector(mixpanelWillFlush:)]) { + if (![strongDelegate mixpanelWillFlush:self]) { + MPLogInfo(@"%@ flush deferred by delegate", self); + return; + } + } + + [self.network flushEventQueue:self.eventsQueue]; + [self.network flushPeopleQueue:self.peopleQueue]; + + [self archive]; + + if (handler) { + dispatch_async(dispatch_get_main_queue(), handler); + } + + MPLogInfo(@"%@ flush complete", self); + }]; +} + +#pragma mark - Persistence +- (NSString *)filePathFor:(NSString *)data +{ + NSString *filename = [NSString stringWithFormat:@"mixpanel-%@-%@.plist", self.apiToken, data]; +#if !defined(MIXPANEL_TVOS) + return [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject] + stringByAppendingPathComponent:filename]; +#else + return [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] + stringByAppendingPathComponent:filename]; +#endif +} + +- (NSString *)eventsFilePath +{ + return [self filePathFor:@"events"]; +} + +- (NSString *)peopleFilePath +{ + return [self filePathFor:@"people"]; +} + +- (NSString *)propertiesFilePath +{ + return [self filePathFor:@"properties"]; +} + +- (NSString *)variantsFilePath +{ + return [self filePathFor:@"variants"]; +} + +- (NSString *)eventBindingsFilePath +{ + return [self filePathFor:@"event_bindings"]; +} + +- (void)archive +{ + [self archiveEvents]; + [self archivePeople]; + [self archiveProperties]; + [self archiveVariants]; + [self archiveEventBindings]; +} + +- (void)archiveEvents +{ + NSString *filePath = [self eventsFilePath]; + NSMutableArray *eventsQueueCopy; + @synchronized (self) { + eventsQueueCopy = [self.eventsQueue mutableCopy]; + } + MPLogInfo(@"%@ archiving events data to %@: %@", self, filePath, eventsQueueCopy); + if (![self archiveObject:eventsQueueCopy withFilePath:filePath]) { + MPLogError(@"%@ unable to archive event data", self); + } +} + +- (void)archivePeople +{ + NSString *filePath = [self peopleFilePath]; + NSMutableArray *peopleQueueCopy; + @synchronized (self) { + peopleQueueCopy = [self.peopleQueue mutableCopy]; + } + MPLogInfo(@"%@ archiving people data to %@: %@", self, filePath, peopleQueueCopy); + if (![self archiveObject:peopleQueueCopy withFilePath:filePath]) { + MPLogError(@"%@ unable to archive people data", self); + } +} + +- (void)archiveProperties +{ + NSString *filePath = [self propertiesFilePath]; + NSMutableDictionary *p = [NSMutableDictionary dictionary]; + [p setValue:self.distinctId forKey:@"distinctId"]; + [p setValue:self.alias forKey:@"alias"]; + [p setValue:self.superProperties forKey:@"superProperties"]; + [p setValue:self.people.distinctId forKey:@"peopleDistinctId"]; + [p setValue:self.people.unidentifiedQueue forKey:@"peopleUnidentifiedQueue"]; + [p setValue:self.shownNotifications forKey:@"shownNotifications"]; + [p setValue:self.timedEvents forKey:@"timedEvents"]; + [p setValue:self.automaticEventsEnabled forKey:@"automaticEvents"]; + MPLogInfo(@"%@ archiving properties data to %@: %@", self, filePath, p); + if (![self archiveObject:p withFilePath:filePath]) { + MPLogError(@"%@ unable to archive properties data", self); + } +} + +- (void)archiveVariants +{ + NSString *filePath = [self variantsFilePath]; + if (![self archiveObject:self.variants withFilePath:filePath]) { + MPLogError(@"%@ unable to archive variants data", self); + } +} + +- (void)archiveEventBindings +{ + NSString *filePath = [self eventBindingsFilePath]; + if (![self archiveObject:self.eventBindings withFilePath:filePath]) { + MPLogError(@"%@ unable to archive tracking events data", self); + } +} + +- (BOOL)archiveObject:(id)object withFilePath:(NSString *)filePath { + @try { + if (![NSKeyedArchiver archiveRootObject:object toFile:filePath]) { + return NO; + } + } @catch (NSException* exception) { + NSAssert(@"Got exception: %@, reason: %@. You can only send to Mixpanel values that inherit from NSObject and implement NSCoding.", exception.name, exception.reason); + return NO; + } + + [self addSkipBackupAttributeToItemAtPath:filePath]; + return YES; +} + +- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *)filePathString +{ + NSURL *URL = [NSURL fileURLWithPath: filePathString]; + assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]); + + NSError *error = nil; + BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES] + forKey: NSURLIsExcludedFromBackupKey error: &error]; + if (!success) { + NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error); + } + return success; +} + +- (void)unarchive +{ + [self unarchiveEvents]; + [self unarchivePeople]; + [self unarchiveProperties]; + [self unarchiveVariants]; + [self unarchiveEventBindings]; +} + ++ (nonnull id)unarchiveOrDefaultFromFile:(NSString *)filePath asClass:(Class)class +{ + return [self unarchiveFromFile:filePath asClass:class] ?: [class new]; +} + ++ (id)unarchiveFromFile:(NSString *)filePath asClass:(Class)class +{ + id unarchivedData = nil; + @try { + unarchivedData = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath]; + // this check is inside the try-catch as the unarchivedData may be a non-NSObject, not responding to `isKindOfClass:` or `respondsToSelector:` + if (![unarchivedData isKindOfClass:class]) { + unarchivedData = nil; + } + MPLogInfo(@"%@ unarchived data from %@: %@", self, filePath, unarchivedData); + } + @catch (NSException *exception) { + MPLogError(@"%@ unable to unarchive data in %@, starting fresh", self, filePath); + // Reset un archived data + unarchivedData = nil; + // Remove the (possibly) corrupt data from the disk + NSError *error = NULL; + BOOL removed = [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error]; + if (!removed) { + MPLogWarning(@"%@ unable to remove archived file at %@ - %@", self, filePath, error); + } + } + return unarchivedData; +} + +- (void)unarchiveEvents +{ + self.eventsQueue = (NSMutableArray *)[Mixpanel unarchiveOrDefaultFromFile:[self eventsFilePath] asClass:[NSMutableArray class]]; +} + +- (void)unarchivePeople +{ + self.peopleQueue = (NSMutableArray *)[Mixpanel unarchiveOrDefaultFromFile:[self peopleFilePath] asClass:[NSMutableArray class]]; +} + +- (void)unarchiveProperties +{ + NSDictionary *properties = (NSDictionary *)[Mixpanel unarchiveFromFile:[self propertiesFilePath] asClass:[NSDictionary class]]; + if (properties) { + self.distinctId = properties[@"distinctId"] ?: [self defaultDistinctId]; + self.alias = properties[@"alias"]; + self.superProperties = properties[@"superProperties"] ?: [NSMutableDictionary dictionary]; + self.people.distinctId = properties[@"peopleDistinctId"]; + self.people.unidentifiedQueue = properties[@"peopleUnidentifiedQueue"] ?: [NSMutableArray array]; + self.shownNotifications = properties[@"shownNotifications"] ?: [NSMutableSet set]; + self.variants = properties[@"variants"] ?: [NSSet set]; + self.eventBindings = properties[@"event_bindings"] ?: [NSSet set]; + self.timedEvents = properties[@"timedEvents"] ?: [NSMutableDictionary dictionary]; + self.automaticEventsEnabled = properties[@"automaticEvents"]; + } +} + +- (void)unarchiveVariants +{ + self.variants = (NSSet *)[Mixpanel unarchiveOrDefaultFromFile:[self variantsFilePath] asClass:[NSSet class]]; +} + +- (void)unarchiveEventBindings +{ + self.eventBindings = (NSSet *)[Mixpanel unarchiveOrDefaultFromFile:[self eventBindingsFilePath] asClass:[NSSet class]]; +} + +#pragma mark - Application Helpers + +- (NSString *)description +{ + return [NSString stringWithFormat:@"", (void *)self, self.apiToken]; +} + +- (NSString *)deviceModel +{ + NSString *results = nil; + size_t size; + sysctlbyname("hw.machine", NULL, &size, NULL, 0); + char answer[size]; + sysctlbyname("hw.machine", answer, &size, NULL, 0); + if (size) { + results = @(answer); + } else { + MPLogError(@"Failed fetch hw.machine from sysctl."); + } + return results; +} + +- (NSString *)watchModel +{ + NSString *model = nil; + Class WKInterfaceDeviceClass = NSClassFromString(@"WKInterfaceDevice"); + if (WKInterfaceDeviceClass) { + SEL currentDeviceSelector = NSSelectorFromString(@"currentDevice"); + id device = ((id (*)(id, SEL))[WKInterfaceDeviceClass methodForSelector:currentDeviceSelector])(WKInterfaceDeviceClass, currentDeviceSelector); + SEL screenBoundsSelector = NSSelectorFromString(@"screenBounds"); + if (device && [device respondsToSelector:screenBoundsSelector]) { + NSInvocation *screenBoundsInvocation = [NSInvocation invocationWithMethodSignature:[device methodSignatureForSelector:screenBoundsSelector]]; + [screenBoundsInvocation setSelector:screenBoundsSelector]; + [screenBoundsInvocation invokeWithTarget:device]; + CGRect screenBounds; + [screenBoundsInvocation getReturnValue:(void *)&screenBounds]; + if (screenBounds.size.width == 136.0f) { + model = @"Apple Watch 38mm"; + } else if (screenBounds.size.width == 156.0f) { + model = @"Apple Watch 42mm"; + } + } + } + return model; +} + +- (NSString *)IFA +{ + NSString *ifa = nil; +#if !defined(MIXPANEL_NO_IFA) + Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager"); + if (ASIdentifierManagerClass) { + SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager"); + id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector); + SEL advertisingTrackingEnabledSelector = NSSelectorFromString(@"isAdvertisingTrackingEnabled"); + BOOL isTrackingEnabled = ((BOOL (*)(id, SEL))[sharedManager methodForSelector:advertisingTrackingEnabledSelector])(sharedManager, advertisingTrackingEnabledSelector); + if (isTrackingEnabled) { + SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier"); + NSUUID *uuid = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector); + ifa = [uuid UUIDString]; + } + } +#endif + return ifa; +} + +#if defined(MIXPANEL_MACOS) +- (NSString *)macOSIdentifier { + io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching("IOPlatformExpertDevice")); + CFStringRef serialNumberAsCFString = NULL; + if (platformExpert) { + serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, + CFSTR(kIOPlatformSerialNumberKey), + kCFAllocatorDefault, 0); + IOObjectRelease(platformExpert); + } + NSString *serialNumberAsNSString = nil; + if (serialNumberAsCFString) { + serialNumberAsNSString = [NSString stringWithString:(__bridge NSString *)serialNumberAsCFString]; + CFRelease(serialNumberAsCFString); + } + return serialNumberAsNSString; +} +#endif + +- (void)setCurrentRadio +{ + dispatch_async(self.serialQueue, ^{ + NSMutableDictionary *properties = [self.automaticProperties mutableCopy]; + if (properties) { + properties[@"$radio"] = [self currentRadio]; + self.automaticProperties = [properties copy]; + } + }); +} + +- (NSString *)currentRadio +{ +#if !MIXPANEL_NO_REACHABILITY_SUPPORT + NSString *radio = _telephonyInfo.currentRadioAccessTechnology; + if (!radio) { + radio = @"None"; + } else if ([radio hasPrefix:@"CTRadioAccessTechnology"]) { + radio = [radio substringFromIndex:23]; + } + return radio; +#else + return @""; +#endif +} + +- (NSString *)libVersion +{ + return [Mixpanel libVersion]; +} + ++ (NSString *)libVersion +{ + return VERSION; +} + +- (NSDictionary *)collectDeviceProperties +{ +#if defined(MIXPANEL_WATCHOS) + return [MixpanelWatchProperties collectDeviceProperties]; +#elif defined(MIXPANEL_MACOS) + CGSize size = [NSScreen mainScreen].frame.size; + return @{ + @"$os": @"macOS", + @"$os_version": [NSProcessInfo processInfo].operatingSystemVersionString, + @"$screen_height": @((NSInteger)size.height), + @"$screen_width": @((NSInteger)size.width), + }; +#else + UIDevice *device = [UIDevice currentDevice]; + CGSize size = [UIScreen mainScreen].bounds.size; + return @{ + @"$os": [device systemName], + @"$os_version": [device systemVersion], + @"$screen_height": @((NSInteger)size.height), + @"$screen_width": @((NSInteger)size.width), + }; +#endif +} + +- (NSDictionary *)collectAutomaticProperties +{ + NSMutableDictionary *p = [NSMutableDictionary dictionary]; + NSString *deviceModel = [self deviceModel]; + + // Use setValue semantics to avoid adding keys where value can be nil. + [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"] forKey:@"$app_version"]; + [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] forKey:@"$app_release"]; + [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleVersion"] forKey:@"$app_build_number"]; + [p setValue:[[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] forKey:@"$app_version_string"]; + [p setValue:[self IFA] forKey:@"$ios_ifa"]; + +#if !MIXPANEL_NO_REACHABILITY_SUPPORT + CTCarrier *carrier = [self.telephonyInfo subscriberCellularProvider]; + [p setValue:carrier.carrierName forKey:@"$carrier"]; +#endif + + [p addEntriesFromDictionary:@{ + @"mp_lib": @"iphone", + @"$lib_version": [self libVersion], + @"$manufacturer": @"Apple", + @"$model": deviceModel, + @"mp_device_model": deviceModel, //legacy + }]; + [p addEntriesFromDictionary:[self collectDeviceProperties]]; + return [p copy]; +} + ++ (BOOL)inBackground +{ +#if !MIXPANEL_NO_UIAPPLICATION_ACCESS + return [UIApplication sharedApplication].applicationState == UIApplicationStateBackground; +#else + return NO; +#endif +} + +#pragma mark - UIApplication Events + +#if !defined(MIXPANEL_MACOS) +- (void)setUpListeners +{ +#if !MIXPANEL_NO_REACHABILITY_SUPPORT + // wifi reachability + if ((_reachability = SCNetworkReachabilityCreateWithName(NULL, "api.mixpanel.com")) != NULL) { + SCNetworkReachabilityContext context = {0, (__bridge void*)self, NULL, NULL, NULL}; + if (SCNetworkReachabilitySetCallback(_reachability, MixpanelReachabilityCallback, &context)) { + if (!SCNetworkReachabilitySetDispatchQueue(_reachability, self.serialQueue)) { + // cleanup callback if setting dispatch queue failed + SCNetworkReachabilitySetCallback(_reachability, NULL, NULL); + } + } + } + + // cellular info + [self setCurrentRadio]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(setCurrentRadio) + name:CTRadioAccessTechnologyDidChangeNotification + object:nil]; +#endif // MIXPANEL_NO_REACHABILITY_SUPPORT + +#if !MIXPANEL_NO_APP_LIFECYCLE_SUPPORT + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + // Application lifecycle events + [notificationCenter addObserver:self + selector:@selector(applicationWillTerminate:) + name:UIApplicationWillTerminateNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(applicationWillResignActive:) + name:UIApplicationWillResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(applicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(applicationDidEnterBackground:) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(applicationWillEnterForeground:) + name:UIApplicationWillEnterForegroundNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(appLinksNotificationRaised:) + name:@"com.parse.bolts.measurement_event" + object:nil]; +#endif // MIXPANEL_NO_APP_LIFECYCLE_SUPPORT + + [self initializeGestureRecognizer]; +} +#else +- (void)setUpListeners { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + + // Application lifecycle events + [notificationCenter addObserver:self + selector:@selector(applicationWillTerminate:) + name:NSApplicationWillTerminateNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(applicationWillResignActive:) + name:NSApplicationWillResignActiveNotification + object:nil]; + [notificationCenter addObserver:self + selector:@selector(applicationDidBecomeActive:) + name:NSApplicationDidBecomeActiveNotification + object:nil]; +} +#endif + +- (void) initializeGestureRecognizer { +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT + dispatch_async(dispatch_get_main_queue(), ^{ + self.testDesignerGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self + action:@selector(connectGestureRecognized:)]; + self.testDesignerGestureRecognizer.minimumPressDuration = 3; + self.testDesignerGestureRecognizer.cancelsTouchesInView = NO; +#if TARGET_IPHONE_SIMULATOR + self.testDesignerGestureRecognizer.numberOfTouchesRequired = 2; +#else + self.testDesignerGestureRecognizer.numberOfTouchesRequired = 4; +#endif + // because this is in a dispatch_async, if the user sets enableVisualABTestAndCodeless in the first run + // loop then this is initialized after that is set so we have to check here + self.testDesignerGestureRecognizer.enabled = self.enableVisualABTestAndCodeless; + [[UIApplication sharedApplication].keyWindow addGestureRecognizer:self.testDesignerGestureRecognizer]; + }); +#endif // MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT +} + +#if !MIXPANEL_NO_REACHABILITY_SUPPORT + +static void MixpanelReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) +{ + Mixpanel *mixpanel = (__bridge Mixpanel *)info; + if (mixpanel && [mixpanel isKindOfClass:[Mixpanel class]]) { + [mixpanel reachabilityChanged:flags]; + } +} + +- (void)reachabilityChanged:(SCNetworkReachabilityFlags)flags +{ + // this should be run in the serial queue. the reason we don't dispatch_async here + // is because it's only ever called by the reachability callback, which is already + // set to run on the serial queue. see SCNetworkReachabilitySetDispatchQueue in init + NSMutableDictionary *properties = [self.automaticProperties mutableCopy]; + if (properties) { + BOOL wifi = (flags & kSCNetworkReachabilityFlagsReachable) && !(flags & kSCNetworkReachabilityFlagsIsWWAN); + properties[@"$wifi"] = @(wifi); + self.automaticProperties = [properties copy]; + MPLogInfo(@"%@ reachability changed, wifi=%d", self, wifi); + } +} + +#endif // MIXPANEL_NO_REACHABILITY_SUPPORT + +#if !MIXPANEL_NO_APP_LIFECYCLE_SUPPORT + +- (void)applicationDidBecomeActive:(NSNotification *)notification +{ + MPLogInfo(@"%@ application did become active", self); + [self startFlushTimer]; + +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT + if (self.checkForNotificationsOnActive || self.checkForVariantsOnActive) { + + [self checkForDecideResponseWithCompletion:^(NSArray *notifications, NSSet *variants, NSSet *eventBindings) { + if (self.showNotificationOnActive && notifications.count > 0) { + [self showNotificationWithObject:notifications[0]]; + } + dispatch_sync(dispatch_get_main_queue(), ^{ + for (MPVariant *variant in variants) { + [variant execute]; + [self markVariantRun:variant]; + } + }); + + dispatch_sync(dispatch_get_main_queue(), ^{ + for (MPEventBinding *binding in eventBindings) { + [binding execute]; + } + }); + }]; + } +#endif // MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT +} + +- (void)applicationWillResignActive:(NSNotification *)notification +{ + MPLogInfo(@"%@ application will resign active", self); + [self stopFlushTimer]; + +#if defined(MIXPANEL_MACOS) + if (self.flushOnBackground) { + [self flush]; + } + + dispatch_async(_serialQueue, ^{ + [self archive]; + }); +#endif +} + +- (void)applicationWillTerminate:(NSNotification *)notification +{ + MPLogInfo(@"%@ application will terminate", self); + dispatch_async(_serialQueue, ^{ + [self archive]; + }); +} + +#if !defined(MIXPANEL_MACOS) +- (void)applicationDidEnterBackground:(NSNotification *)notification +{ + MPLogInfo(@"%@ did enter background", self); + __block UIBackgroundTaskIdentifier backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ + MPLogInfo(@"%@ flush %lu cut short", self, (unsigned long) backgroundTask); + [[UIApplication sharedApplication] endBackgroundTask:backgroundTask]; + self.taskId = UIBackgroundTaskInvalid; + }]; + self.taskId = backgroundTask; + MPLogInfo(@"%@ starting background cleanup task %lu", self, (unsigned long)self.taskId); + + dispatch_group_t bgGroup = dispatch_group_create(); + NSString *trackedKey = [NSString stringWithFormat:@"MPTracked:%@", self.apiToken]; + if (![[NSUserDefaults standardUserDefaults] boolForKey:trackedKey]) { + dispatch_group_enter(bgGroup); + NSString *requestData = [MPNetwork encodeArrayForAPI:@[@{@"event": @"Integration", @"properties": @{@"token": @"85053bf24bba75239b16a601d9387e17", @"mp_lib": @"iphone", @"distinct_id": self.apiToken}}]]; + NSString *postBody = [NSString stringWithFormat:@"ip=%d&data=%@", self.useIPAddressForGeoLocation, requestData]; + NSURLRequest *request = [self.network buildPostRequestForEndpoint:MPNetworkEndpointTrack andBody:postBody]; + [[[MPNetwork sharedURLSession] dataTaskWithRequest:request completionHandler:^(NSData *responseData, + NSURLResponse *urlResponse, + NSError *error) { + if (!error) { + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:trackedKey]; + } + dispatch_group_leave(bgGroup); + }] resume]; + } + + if (self.flushOnBackground) { + [self flush]; + } + + dispatch_group_enter(bgGroup); + dispatch_async(_serialQueue, ^{ + [self archive]; + self.decideResponseCached = NO; + dispatch_group_leave(bgGroup); + }); + + dispatch_group_notify(bgGroup, dispatch_get_main_queue(), ^{ + MPLogInfo(@"%@ ending background cleanup task %lu", self, (unsigned long)self.taskId); + if (self.taskId != UIBackgroundTaskInvalid) { + [[UIApplication sharedApplication] endBackgroundTask:self.taskId]; + self.taskId = UIBackgroundTaskInvalid; + } + }); +} + +- (void)applicationWillEnterForeground:(NSNotificationCenter *)notification +{ + MPLogInfo(@"%@ will enter foreground", self); + dispatch_async(self.serialQueue, ^{ + if (self.taskId != UIBackgroundTaskInvalid) { + [[UIApplication sharedApplication] endBackgroundTask:self.taskId]; + self.taskId = UIBackgroundTaskInvalid; + [self.network updateNetworkActivityIndicator:NO]; + } + }); +} + +- (void)appLinksNotificationRaised:(NSNotification *)notification +{ + NSDictionary *eventMap = @{@"al_nav_out": @"$al_nav_out", + @"al_nav_in": @"$al_nav_in", + @"al_ref_back_out": @"$al_ref_back_out" + }; + NSDictionary *userInfo = notification.userInfo; + if (userInfo[@"event_name"] && userInfo[@"event_args"] && eventMap[userInfo[@"event_name"]]) { + [self track:eventMap[userInfo[@"event_name"]] properties:userInfo[@"event_args"]]; + } +} +#endif // MIXPANEL_MACOS + +#endif // MIXPANEL_NO_APP_LIFECYCLE_SUPPORT + +#pragma mark - Logging +- (void)setEnableLogging:(BOOL)enableLogging { + gLoggingEnabled = enableLogging; + + if (gLoggingEnabled) { + asl_add_log_file(NULL, STDERR_FILENO); + asl_set_filter(NULL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + } else { + asl_remove_log_file(NULL, STDERR_FILENO); + } +} + +- (BOOL)enableLogging { + return gLoggingEnabled; +} + +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT + +#pragma mark - Decide + ++ (UIViewController *)topPresentedViewController +{ + UIViewController *controller = [UIApplication sharedApplication].keyWindow.rootViewController; + while (controller.presentedViewController) { + controller = controller.presentedViewController; + } + return controller; +} + ++ (BOOL)canPresentFromViewController:(UIViewController *)viewController +{ + if ([viewController isBeingPresented] || [viewController isBeingDismissed]) { + return NO; + } + + if ([viewController isKindOfClass:UIAlertController.class]) { + return NO; + } + + return YES; +} + +- (void)checkForDecideResponseWithCompletion:(void (^)(NSArray *notifications, NSSet *variants, NSSet *eventBindings))completion +{ + [self checkForDecideResponseWithCompletion:completion useCache:YES]; +} + +- (void)checkForDecideResponseWithCompletion:(void (^)(NSArray *notifications, NSSet *variants, NSSet *eventBindings))completion useCache:(BOOL)useCache +{ + [self dispatchOnNetworkQueue:^{ + NSMutableSet *newVariants = [NSMutableSet set]; + NSMutableSet *newEventBindings = [NSMutableSet set]; + __block BOOL hadError = NO; + + if (!useCache || !self.decideResponseCached) { + // Build a proper URL from our parameters + NSArray *queryItems = [MPNetwork buildDecideQueryForProperties:self.people.automaticPeopleProperties + withDistinctID:self.people.distinctId ?: self.distinctId + andToken:self.apiToken]; + + + // Build a network request from the URL + NSURLRequest *request = [self.network buildGetRequestForEndpoint:MPNetworkEndpointDecide + withQueryItems:queryItems]; + + // Send the network request + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [[[MPNetwork sharedURLSession] dataTaskWithRequest:request completionHandler:^(NSData *responseData, + NSURLResponse *urlResponse, + NSError *error) { + + if (error) { + MPLogError(@"%@ decide check http error: %@", self, error); + hadError = YES; + dispatch_semaphore_signal(semaphore); + return; + } + + // Handle network response + NSDictionary *object = [NSJSONSerialization JSONObjectWithData:responseData options:(NSJSONReadingOptions)0 error:&error]; + if (error) { + MPLogError(@"%@ decide check json error: %@, data: %@", self, error, [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]); + hadError = YES; + dispatch_semaphore_signal(semaphore); + return; + } + if (object[@"error"]) { + MPLogError(@"%@ decide check api error: %@", self, object[@"error"]); + hadError = YES; + dispatch_semaphore_signal(semaphore); + return; + } + + NSDictionary *config = object[@"config"]; + if (config && [config isKindOfClass:NSDictionary.class]) { + NSDictionary *validationConfig = config[@"ce"]; + if (validationConfig && [validationConfig isKindOfClass:NSDictionary.class]) { + self.validationEnabled = [validationConfig[@"enabled"] boolValue]; + + NSString *method = validationConfig[@"method"]; + if (method && [method isKindOfClass:NSString.class]) { + if ([method isEqualToString:@"count"]) { + self.validationMode = AutomaticTrackModeCount; + } + } + } + } + + id rawNotifications = object[@"notifications"]; + NSMutableArray *parsedNotifications = [NSMutableArray array]; + if ([rawNotifications isKindOfClass:[NSArray class]]) { + for (id obj in rawNotifications) { + MPNotification *notification = nil; + NSString *notificationType = obj[@"type"]; + if ([notificationType isEqualToString:MPNotificationTypeTakeover]) { + notification = [[MPTakeoverNotification alloc] initWithJSONObject:obj]; + } else if ([notificationType isEqualToString:MPNotificationTypeMini]) { + notification = [[MPMiniNotification alloc] initWithJSONObject:obj]; + } + + if (notification) { + [parsedNotifications addObject:notification]; + } + } + } else { + MPLogError(@"%@ in-app notifs check response format error: %@", self, object); + } + + id rawVariants = object[@"variants"]; + NSMutableSet *parsedVariants = [NSMutableSet set]; + if ([rawVariants isKindOfClass:[NSArray class]]) { + for (id obj in rawVariants) { + MPVariant *variant = [MPVariant variantWithJSONObject:obj]; + if (variant) { + [parsedVariants addObject:variant]; + } + } + } else { + MPLogError(@"%@ variants check response format error: %@", self, object); + } + + id rawAutomaticEvents = object[@"automatic_events"]; + if ([rawAutomaticEvents isKindOfClass:[NSNumber class]]) { + if (!self.automaticEventsEnabled || [self.automaticEventsEnabled boolValue] != [rawAutomaticEvents boolValue]) { + self.automaticEventsEnabled = rawAutomaticEvents; + [self archiveProperties]; + } + } + + // Variants that are already running (may or may not have been marked as finished). + NSSet *runningVariants = [NSSet setWithSet:[self.variants objectsPassingTest:^BOOL(MPVariant *var, BOOL *stop) { return var.running; }]]; + // Variants that are marked as finished, (may or may not be running still). + NSSet *finishedVariants = [NSSet setWithSet:[self.variants objectsPassingTest:^BOOL(MPVariant *var, BOOL *stop) { return var.finished; }]]; + // Variants that are running that should be marked finished. + NSMutableSet *toFinishVariants = [NSMutableSet setWithSet:runningVariants]; + [toFinishVariants minusSet:parsedVariants]; + // New variants that we just saw that are not already running. + [newVariants unionSet:parsedVariants]; + [newVariants minusSet:runningVariants]; + // Running variants that were marked finished, but have now started again. + NSMutableSet *restartVariants = [NSMutableSet setWithSet:parsedVariants]; + [restartVariants intersectSet:runningVariants]; + [restartVariants intersectSet:finishedVariants]; + // All variants that we still care about (stopped are thrown out) + NSMutableSet *allVariants = [NSMutableSet setWithSet:newVariants]; + [allVariants unionSet:runningVariants]; + + [restartVariants makeObjectsPerformSelector:NSSelectorFromString(@"restart")]; + [toFinishVariants makeObjectsPerformSelector:NSSelectorFromString(@"finish")]; + + id rawEventBindings = object[@"event_bindings"]; + NSMutableSet *parsedEventBindings = [NSMutableSet set]; + if ([rawEventBindings isKindOfClass:[NSArray class]]) { + for (id obj in rawEventBindings) { + MPEventBinding *binder = [MPEventBinding bindingWithJSONObject:obj]; + if (binder) { + [parsedEventBindings addObject:binder]; + } + } + } else { + MPLogDebug(@"%@ mp tracking events check response format error: %@", self, object); + } + + // Finished bindings are those which should no longer be run. + NSMutableSet *finishedEventBindings = [NSMutableSet setWithSet:self.eventBindings]; + [finishedEventBindings minusSet:parsedEventBindings]; + [finishedEventBindings makeObjectsPerformSelector:NSSelectorFromString(@"stop")]; + + // New bindings are those we are running for the first time. + [newEventBindings unionSet:parsedEventBindings]; + [newEventBindings minusSet:self.eventBindings]; + + NSMutableSet *allEventBindings = [self.eventBindings mutableCopy]; + [allEventBindings unionSet:newEventBindings]; + + self.notifications = [NSArray arrayWithArray:parsedNotifications]; + self.variants = [allVariants copy]; + self.eventBindings = [allEventBindings copy]; + + self.decideResponseCached = YES; + + dispatch_semaphore_signal(semaphore); + }] resume]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + + } else { + MPLogInfo(@"%@ decide cache found, skipping network request", self); + } + + if (hadError) { + if (completion) { + completion(nil, nil, nil); + } + } else { + NSArray *unseenNotifications = [self.notifications objectsAtIndexes:[self.notifications indexesOfObjectsPassingTest:^BOOL(MPNotification *obj, NSUInteger idx, BOOL *stop) { + return [self.shownNotifications member:@(obj.ID)] == nil; + }]]; + + MPLogInfo(@"%@ decide check found %lu available notifs out of %lu total: %@", self, (unsigned long)unseenNotifications.count, + (unsigned long)self.notifications.count, unseenNotifications); + MPLogInfo(@"%@ decide check found %lu variants: %@", self, (unsigned long)self.variants.count, self.variants); + MPLogInfo(@"%@ decide check found %lu tracking events: %@", self, (unsigned long)self.eventBindings.count, self.eventBindings); + + if (completion) { + completion(unseenNotifications, newVariants, newEventBindings); + } + } + }]; +} + +- (void)checkForNotificationsWithCompletion:(void (^)(NSArray *notifications))completion +{ + [self checkForDecideResponseWithCompletion:^(NSArray *notifications, NSSet *variants, NSSet *eventBindings) { + if (completion) { + completion(notifications); + } + } useCache:NO]; +} + +- (void)checkForVariantsWithCompletion:(void (^)(NSSet *variants))completion +{ + [self checkForDecideResponseWithCompletion:^(NSArray *notifications, NSSet *variants, NSSet *eventBindings) { + if (completion) { + completion(variants); + } + } useCache:NO]; +} + +#pragma mark - Mixpanel Notifications + +- (void)showNotification +{ + [self checkForNotificationsWithCompletion:^(NSArray *notifications) { + if (notifications.count > 0) { + [self showNotificationWithObject:notifications[0]]; + } + }]; +} + +- (void)showNotificationWithType:(NSString *)type +{ + [self checkForNotificationsWithCompletion:^(NSArray *notifications) { + if (type != nil) { + for (MPNotification *notification in notifications) { + if ([notification.type isEqualToString:type]) { + [self showNotificationWithObject:notification]; + break; + } + } + } + }]; +} + +- (void)showNotificationWithID:(NSUInteger)ID +{ + [self checkForNotificationsWithCompletion:^(NSArray *notifications) { + for (MPNotification *notification in notifications) { + if (notification.ID == ID) { + [self showNotificationWithObject:notification]; + break; + } + } + }]; +} + +- (void)showNotificationWithObject:(MPNotification *)notification +{ + NSData *image = notification.image; + + // if images fail to load, remove the notification from the queue + if (!image) { + NSMutableArray *notifications = [NSMutableArray arrayWithArray:_notifications]; + [notifications removeObject:notification]; + self.notifications = [NSArray arrayWithArray:notifications]; + return; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (self.currentlyShowingNotification) { + MPLogWarning(@"%@ already showing in-app notification: %@", self, self.currentlyShowingNotification); + } else { + self.currentlyShowingNotification = notification; + BOOL shown; + if ([notification.type isEqualToString:MPNotificationTypeMini]) { + shown = [self showMiniNotificationWithObject:(MPMiniNotification *)notification]; + } else { + shown = [self showTakeoverNotificationWithObject:(MPTakeoverNotification *)notification]; + } + + if (shown) { + [self markNotificationShown:notification]; + } else { + self.currentlyShowingNotification = nil; + } + } + }); +} + +- (BOOL)showTakeoverNotificationWithObject:(MPTakeoverNotification *)notification +{ + UIViewController *presentingViewController = [Mixpanel topPresentedViewController]; + + if ([[self class] canPresentFromViewController:presentingViewController]) { + MPTakeoverNotificationViewController *controller = [[MPTakeoverNotificationViewController alloc] init]; + controller.notification = notification; + controller.delegate = self; + [controller show]; + self.notificationViewController = controller; + + return YES; + } else { + return NO; + } +} + +- (BOOL)showMiniNotificationWithObject:(MPMiniNotification *)notification +{ + MPMiniNotificationViewController *controller = [[MPMiniNotificationViewController alloc] init]; + controller.notification = notification; + controller.delegate = self; + self.notificationViewController = controller; + + [controller show]; + + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.miniNotificationPresentationTime * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^{ + [self notificationController:controller wasDismissedWithCtaUrl:nil]; + }); + return YES; +} + +- (void)notificationController:(MPNotificationViewController *)controller wasDismissedWithCtaUrl:(NSURL *)ctaUrl +{ + if (controller == nil || self.currentlyShowingNotification != controller.notification) { + return; + } + + void (^completionBlock)() = ^void() { + self.currentlyShowingNotification = nil; + self.notificationViewController = nil; + }; + + if (ctaUrl) { + [controller hide:YES completion:^{ + MPLogInfo(@"%@ opening URL %@", self, ctaUrl); + + if (![[UIApplication sharedApplication] openURL:ctaUrl]) { + MPLogError(@"Mixpanel failed to open given URL: %@", ctaUrl); + } + + [self trackNotification:controller.notification event:@"$campaign_open"]; + completionBlock(); + }]; + } else { + [controller hide:YES completion:completionBlock]; + } +} + +- (void)trackNotification:(MPNotification *)notification event:(NSString *)event +{ + [self track:event properties:@{@"campaign_id": @(notification.ID), + @"message_id": @(notification.messageID), + @"message_type": @"inapp", + @"message_subtype": notification.type}]; +} + +- (void)markNotificationShown:(MPNotification *)notification +{ + MPLogInfo(@"%@ marking notification shown: %@, %@", self, @(notification.ID), _shownNotifications); + + [_shownNotifications addObject:@(notification.ID)]; + + NSDictionary *properties = @{ + @"$campaigns": @(notification.ID), + @"$notifications": @{ + @"campaign_id": @(notification.ID), + @"message_id": @(notification.messageID), + @"type": @"inapp", + @"time": [NSDate date] + } + }; + + [self.people append:properties]; + + [self trackNotification:notification event:@"$campaign_delivery"]; +} + +#pragma mark - Mixpanel A/B Testing and Codeless (Designer) +- (void)setEnableVisualABTestAndCodeless:(BOOL)enableVisualABTestAndCodeless { + _enableVisualABTestAndCodeless = enableVisualABTestAndCodeless; + + self.testDesignerGestureRecognizer.enabled = _enableVisualABTestAndCodeless; + if (!_enableVisualABTestAndCodeless) { + // Note that the connection will be closed and cleaned up properly in the dealloc method + [self.abtestDesignerConnection close]; + self.abtestDesignerConnection = nil; + } +} + +- (BOOL)enableVisualABTestAndCodeless { + return _enableVisualABTestAndCodeless; +} + +- (void)connectGestureRecognized:(id)sender +{ + if (!sender || ([sender isKindOfClass:[UIGestureRecognizer class]] && ((UIGestureRecognizer *)sender).state == UIGestureRecognizerStateBegan)) { + [self connectToABTestDesigner]; + } +} + +- (void)connectToABTestDesigner +{ + [self connectToABTestDesigner:NO]; +} + +- (void)connectToABTestDesigner:(BOOL)reconnect +{ + // Ignore the gesture if the AB test designer is disabled. + if (!self.enableVisualABTestAndCodeless) return; + + if ([self.abtestDesignerConnection isKindOfClass:[MPABTestDesignerConnection class]] && ((MPABTestDesignerConnection *)self.abtestDesignerConnection).connected) { + MPLogWarning(@"A/B test designer connection already exists"); + return; + } + static NSUInteger oldInterval; + NSString *designerURLString = [NSString stringWithFormat:@"%@/connect?key=%@&type=device", self.switchboardURL, self.apiToken]; + NSURL *designerURL = [NSURL URLWithString:designerURLString]; + __weak Mixpanel *weakSelf = self; + void (^connectCallback)(void) = ^{ + __strong Mixpanel *strongSelf = weakSelf; + oldInterval = strongSelf.flushInterval; + strongSelf.flushInterval = 1; + [UIApplication sharedApplication].idleTimerDisabled = YES; + if (strongSelf) { + for (MPVariant *variant in self.variants) { + [variant stop]; + } + for (MPEventBinding *binding in self.eventBindings) { + [binding stop]; + } + MPABTestDesignerConnection *connection = strongSelf.abtestDesignerConnection; + void (^block)(id, SEL, NSString*, id) = ^(id obj, SEL sel, NSString *event_name, id params) { + MPDesignerTrackMessage *message = [MPDesignerTrackMessage messageWithPayload:@{@"event_name": event_name}]; + [connection sendMessage:message]; + }; + + [MPSwizzler swizzleSelector:@selector(track:properties:) onClass:[Mixpanel class] withBlock:block named:@"track_properties"]; + } + }; + void (^disconnectCallback)(void) = ^{ + __strong Mixpanel *strongSelf = weakSelf; + strongSelf.flushInterval = oldInterval; + [UIApplication sharedApplication].idleTimerDisabled = NO; + if (strongSelf) { + for (MPVariant *variant in self.variants) { + [variant execute]; + } + for (MPEventBinding *binding in self.eventBindings) { + [binding execute]; + } + [MPSwizzler unswizzleSelector:@selector(track:properties:) onClass:[Mixpanel class] named:@"track_properties"]; + } + }; + self.abtestDesignerConnection = [[MPABTestDesignerConnection alloc] initWithURL:designerURL + keepTrying:reconnect + connectCallback:connectCallback + disconnectCallback:disconnectCallback]; +} + +#pragma mark - Mixpanel A/B Testing (Experiment) + +- (void)executeCachedVariants { + for (MPVariant *variant in self.variants) { + NSAssert(!variant.running, @"Variant should not be running at this point"); + [variant execute]; + } +} + +- (void)markVariantRun:(MPVariant *)variant +{ + MPLogInfo(@"%@ marking variant %@ shown for experiment %@", self, @(variant.ID), @(variant.experimentID)); + NSDictionary *shownVariant = @{@(variant.experimentID).stringValue: @(variant.ID)}; + if (self.people.distinctId) { + [self.people merge:@{@"$experiments": shownVariant}]; + } + + dispatch_async(self.serialQueue, ^{ + NSMutableDictionary *superProperties = [NSMutableDictionary dictionaryWithDictionary:self.superProperties]; + NSMutableDictionary *shownVariants = [NSMutableDictionary dictionaryWithDictionary:superProperties[@"$experiments"]]; + [shownVariants addEntriesFromDictionary:shownVariant]; + [superProperties addEntriesFromDictionary:@{@"$experiments": [shownVariants copy]}]; + self.superProperties = [superProperties copy]; + if ([Mixpanel inBackground]) { + [self archiveProperties]; + } + }); + + [self track:@"$experiment_started" properties:@{@"$experiment_id": @(variant.experimentID), @"$variant_id": @(variant.ID)}]; +} + +- (void)joinExperimentsWithCallback:(void(^)())experimentsLoadedCallback +{ + [self checkForVariantsWithCompletion:^(NSSet *newVariants) { + for (MPVariant *variant in newVariants) { + [variant execute]; + [self markVariantRun:variant]; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (experimentsLoadedCallback) { + experimentsLoadedCallback(); + } + }); + }]; +} + +- (void)joinExperiments +{ + [self joinExperimentsWithCallback:nil]; +} + +#pragma mark - Mixpanel Event Bindings + +- (void)executeCachedEventBindings { + for (id binding in self.eventBindings) { + if ([binding isKindOfClass:[MPEventBinding class]]) { + [binding execute]; + } + } +} + +#endif // MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MixpanelExceptionHandler.h b/ios/Pods/Mixpanel/Mixpanel/MixpanelExceptionHandler.h new file mode 100644 index 0000000..a5c2ee0 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MixpanelExceptionHandler.h @@ -0,0 +1,17 @@ +// +// MixpanelExceptionHandler.h +// HelloMixpanel +// +// Created by Sam Green on 7/28/15. +// Copyright (c) 2015 Mixpanel. All rights reserved. +// + +#import + +@class Mixpanel; + +@interface MixpanelExceptionHandler : NSObject + ++ (instancetype)sharedHandler; +- (void)addMixpanelInstance:(Mixpanel *)instance; +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MixpanelExceptionHandler.m b/ios/Pods/Mixpanel/Mixpanel/MixpanelExceptionHandler.m new file mode 100644 index 0000000..214f71a --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MixpanelExceptionHandler.m @@ -0,0 +1,181 @@ +// +// MixpanelExceptionHandler.m +// HelloMixpanel +// +// Created by Sam Green on 7/28/15. +// Copyright (c) 2015 Mixpanel. All rights reserved. +// + +#import "MixpanelExceptionHandler.h" +#import "Mixpanel.h" +#import "MixpanelPrivate.h" +#import "MPLogger.h" +#include +#include + +NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName"; +NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey"; +NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey"; + +volatile int32_t UncaughtExceptionCount = 0; +const int32_t UncaughtExceptionMaximum = 10; + +const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4; +const NSInteger UncaughtExceptionHandlerReportAddressCount = 5; + +@interface MixpanelExceptionHandler () + +@property (nonatomic) NSUncaughtExceptionHandler *defaultExceptionHandler; +@property (nonatomic, strong) NSHashTable *mixpanelInstances; + +@end + +@implementation MixpanelExceptionHandler + ++ (instancetype)sharedHandler { + static MixpanelExceptionHandler *gSharedHandler = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + gSharedHandler = [[MixpanelExceptionHandler alloc] init]; + }); + return gSharedHandler; +} + ++ (NSArray *)backtrace +{ + void* callstack[128]; + int frames = backtrace(callstack, 128); + char **strs = backtrace_symbols(callstack, frames); + + int i; + NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames]; + for ( + i = UncaughtExceptionHandlerSkipAddressCount; + i < UncaughtExceptionHandlerSkipAddressCount + + UncaughtExceptionHandlerReportAddressCount; + i++) + { + [backtrace addObject:[NSString stringWithUTF8String:strs[i]]]; + } + free(strs); + + return backtrace; +} + + +- (instancetype)init { + self = [super init]; + if (self) { + // Create a hash table of weak pointers to mixpanel instances + _mixpanelInstances = [NSHashTable weakObjectsHashTable]; + + // Save the existing exception handler + _defaultExceptionHandler = NSGetUncaughtExceptionHandler(); + // Install our handler + [self setupHandlers]; + } + return self; +} + +- (void)setupHandlers { + NSSetUncaughtExceptionHandler(&HandleException); + signal(SIGABRT, SignalHandler); + signal(SIGILL, SignalHandler); + signal(SIGSEGV, SignalHandler); + signal(SIGFPE, SignalHandler); + signal(SIGBUS, SignalHandler); + signal(SIGPIPE, SignalHandler); +} + +- (void)addMixpanelInstance:(Mixpanel *)instance { + NSParameterAssert(instance != nil); + + [self.mixpanelInstances addObject:instance]; +} + +void SignalHandler(int signal) +{ + int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); + if (exceptionCount > UncaughtExceptionMaximum) + { + return; + } + + NSMutableDictionary *userInfo = + [NSMutableDictionary + dictionaryWithObject:[NSNumber numberWithInt:signal] + forKey:UncaughtExceptionHandlerSignalKey]; + + NSArray *callStack = [MixpanelExceptionHandler backtrace]; + [userInfo + setObject:callStack + forKey:UncaughtExceptionHandlerAddressesKey]; + + [MixpanelExceptionHandler performSelectorOnMainThread:@selector(mp_handleUncaughtException:) + withObject:[NSException + exceptionWithName:UncaughtExceptionHandlerSignalExceptionName + reason: + [NSString stringWithFormat: + NSLocalizedString(@"Signal %d was raised.", nil), + signal] + userInfo: + [NSDictionary + dictionaryWithObject:[NSNumber numberWithInt:signal] + forKey:UncaughtExceptionHandlerSignalKey]] waitUntilDone:YES]; +} + +void HandleException(NSException *exception) +{ + int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount); + if (exceptionCount > UncaughtExceptionMaximum) + { + return; + } + + NSArray *callStack = [MixpanelExceptionHandler backtrace]; + NSMutableDictionary *userInfo = + [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]]; + [userInfo + setObject:callStack + forKey:UncaughtExceptionHandlerAddressesKey]; + + [MixpanelExceptionHandler performSelectorOnMainThread:@selector(mp_handleUncaughtException:) + withObject:[NSException + exceptionWithName:[exception name] + reason:[exception reason] + userInfo:userInfo] waitUntilDone:YES]; +} + + ++ (void) mp_handleUncaughtException:(NSException *)exception { + MixpanelExceptionHandler *handler = [MixpanelExceptionHandler sharedHandler]; + + // Archive the values for each Mixpanel instance + for (Mixpanel *instance in handler.mixpanelInstances) { + [instance archive]; + NSMutableDictionary *properties = [[NSMutableDictionary alloc] init]; + [properties setValue:[exception reason] forKey:@"$ae_crashed_reason"]; + [instance track:@"$ae_crashed" properties:properties]; + dispatch_sync(instance.serialQueue, ^{}); + } + + NSSetUncaughtExceptionHandler(NULL); + signal(SIGABRT, SIG_DFL); + signal(SIGILL, SIG_DFL); + signal(SIGSEGV, SIG_DFL); + signal(SIGFPE, SIG_DFL); + signal(SIGBUS, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + NSLog(@"Encountered an uncaught exception. All Mixpanel instances were archived."); + + NSLog(@"%@", [NSString stringWithFormat:@"Debug details follow:\n%@\n%@", + [exception reason], + [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]]); + if (handler.defaultExceptionHandler) { + // Ensure the existing handler gets called once we're finished + handler.defaultExceptionHandler(exception); + } +} + + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MixpanelPeople.h b/ios/Pods/Mixpanel/Mixpanel/MixpanelPeople.h new file mode 100644 index 0000000..4a19bbb --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MixpanelPeople.h @@ -0,0 +1,294 @@ +// +// MixpanelPeople.h +// Mixpanel +// +// Created by Sam Green on 6/16/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/*! + @class + Mixpanel People API. + + @abstract + Access to the Mixpanel People API, available as a property on the main + Mixpanel API. + + @discussion + You should not instantiate this object yourself. An instance of it will + be available as a property of the main Mixpanel object. Calls to Mixpanel + People methods will look like this: + +
+ [mixpanel.people increment:@"App Opens" by:[NSNumber numberWithInt:1]];
+ 
+ + Please note that the core Mixpanel and + MixpanelPeople classes share the identify: method. + The Mixpanel identify: affects the + distinct_id property of events sent by track: and + track:properties: and determines which Mixpanel People user + record will be updated by set:, increment: and other + MixpanelPeople methods. + + If you are going to set your own distinct IDs for core Mixpanel event + tracking, make sure to use the same distinct IDs when using Mixpanel + People. + */ +@interface MixpanelPeople : NSObject +/*! + @property + + @abstract + controls the $ignore_time property in any subsequent MixpanelPeople operation. + + If the $ignore_time property is present and true in your request, + Mixpanel will not automatically update the "Last Seen" property of the profile. + Otherwise, Mixpanel will add a "Last Seen" property associated with the + current time for all $set, $append, and $add operations + + @discussion + Defaults to NO. + */ +@property (atomic) BOOL ignoreTime; + +/*! + @method + + @abstract + Register the given device to receive push notifications. + + @discussion + This will associate the device token with the current user in Mixpanel People, + which will allow you to send push notifications to the user from the Mixpanel + People web interface. You should call this method with the NSData + token passed to + application:didRegisterForRemoteNotificationsWithDeviceToken:. + + @param deviceToken device token as returned application:didRegisterForRemoteNotificationsWithDeviceToken: + */ +- (void)addPushDeviceToken:(NSData *)deviceToken; + +/*! + @method + + @abstract + Unregister the given device to receive push notifications. + + @discussion + This will unset all of the push tokens saved to this people profile. This is useful + in conjunction with a call to `reset`, or when a user is logging out. + */ +- (void)removeAllPushDeviceTokens; + +/*! + @method + + @abstract + Unregister a specific device token from the ability to receive push notifications. + + @discussion + This will remove the provided push token saved to this people profile. This is useful + in conjunction with a call to `reset`, or when a user is logging out. + */ +- (void)removePushDeviceToken:(NSData *)deviceToken; + +/*! + @method + + @abstract + Set properties on the current user in Mixpanel People. + + @discussion + The properties will be set on the current user. The keys must be NSString + objects and the values should be NSString, NSNumber, NSArray, NSDate, or + NSNull objects. We use an NSAssert to enforce this type requirement. In + release mode, the assert is stripped out and we will silently convert + incorrect types to strings using [NSString stringWithFormat:@"%@", value]. You + can override the default the current project token and distinct ID by + including the special properties: $token and $distinct_id. If the existing + user record on the server already has a value for a given property, the old + value is overwritten. Other existing properties will not be affected. + +
+ // applies to both Mixpanel Engagement track: AND Mixpanel People set: and
+ // increment: calls
+ [mixpanel identify:distinctId];
+ 
+ + @param properties properties dictionary + + */ +- (void)set:(NSDictionary *)properties; + +/*! + @method + + @abstract + Convenience method for setting a single property in Mixpanel People. + + @discussion + Property keys must be NSString objects and values must be + NSString, NSNumber, NSNull, + NSArray, NSDictionary, NSDate or + NSURL objects. + + @param property property name + @param object property value + */ +- (void)set:(NSString *)property to:(id)object; + +/*! + @method + + @abstract + Set properties on the current user in Mixpanel People, but don't overwrite if + there is an existing value. + + @discussion + This method is identical to set: except it will only set + properties that are not already set. It is particularly useful for collecting + data about the user's initial experience and source, as well as dates + representing the first time something happened. + + @param properties properties dictionary + + */ +- (void)setOnce:(NSDictionary *)properties; + +/*! + @method + + @abstract + Remove a list of properties and their values from the current user's profile + in Mixpanel People. + + @discussion + The properties array must ony contain NSString names of properties. For properties + that don't exist there will be no effect. + + @param properties properties array + + */ +- (void)unset:(NSArray *)properties; + +/*! + @method + + @abstract + Increment the given numeric properties by the given values. + + @discussion + Property keys must be NSString names of numeric properties. A property is + numeric if its current value is a number. If a property does not exist, it + will be set to the increment amount. Property values must be NSNumber objects. + + @param properties properties dictionary + */ +- (void)increment:(NSDictionary *)properties; + +/*! + @method + + @abstract + Convenience method for incrementing a single numeric property by the specified + amount. + + @param property property name + @param amount amount to increment by + */ +- (void)increment:(NSString *)property by:(NSNumber *)amount; + +/*! + @method + + @abstract + Append values to list properties. + + @discussion + Property keys must be NSString objects and values must be + NSString, NSNumber, NSNull, + NSArray, NSDictionary, NSDate or + NSURL objects. + + @param properties mapping of list property names to values to append + */ +- (void)append:(NSDictionary *)properties; + +/*! + @method + + @abstract + Union list properties. + + @discussion + Property keys must be NSArray objects. + + @param properties mapping of list property names to lists to union + */ +- (void)union:(NSDictionary *)properties; + +/*! + @method + + @abstract + Remove list properties. + + @discussion + Property keys must be NSString objects and values must be + NSString, NSNumber, NSNull, + NSArray, NSDictionary, NSDate or + NSURL objects. + + @param properties mapping of list property names to values to remove + */ +- (void)remove:(NSDictionary *)properties; + +/*! + @method + + @abstract + Track money spent by the current user for revenue analytics. + + @param amount amount of revenue received + */ +- (void)trackCharge:(NSNumber *)amount; + +/*! + @method + + @abstract + Track money spent by the current user for revenue analytics and associate + properties with the charge. + + @discussion + Charge properties allow you segment on types of revenue. For instance, you + could record a product ID with each charge so that you could segment on it in + revenue analytics to see which products are generating the most revenue. + */ +- (void)trackCharge:(NSNumber *)amount withProperties:(nullable NSDictionary *)properties; + + +/*! + @method + + @abstract + Delete current user's revenue history. + */ +- (void)clearCharges; + +/*! + @method + + @abstract + Delete current user's record from Mixpanel People. + */ +- (void)deleteUser; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Mixpanel/Mixpanel/MixpanelPeople.m b/ios/Pods/Mixpanel/Mixpanel/MixpanelPeople.m new file mode 100644 index 0000000..6d693a4 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MixpanelPeople.m @@ -0,0 +1,278 @@ +// +// MixpanelPeople.m +// Mixpanel +// +// Created by Sam Green on 6/16/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "MixpanelPeople.h" +#import "MixpanelPeoplePrivate.h" +#import "Mixpanel.h" +#import "MixpanelPrivate.h" +#import "MPLogger.h" + +#if defined(MIXPANEL_WATCHOS) +#import "MixpanelWatchProperties.h" +#endif + +@implementation MixpanelPeople + +- (instancetype)initWithMixpanel:(Mixpanel *)mixpanel +{ + if (self = [self init]) { + self.mixpanel = mixpanel; + self.unidentifiedQueue = [NSMutableArray array]; + self.automaticPeopleProperties = [self collectAutomaticPeopleProperties]; + } + return self; +} + +- (NSString *)description +{ + __strong Mixpanel *strongMixpanel = self.mixpanel; + return [NSString stringWithFormat:@"", (void *)self, (strongMixpanel ? strongMixpanel.apiToken : @"")]; +} + +- (NSString *)deviceSystemVersion { +#if defined(MIXPANEL_WATCHOS) + return [MixpanelWatchProperties systemVersion]; +#elif defined(MIXPANEL_MACOS) + return [NSProcessInfo processInfo].operatingSystemVersionString; +#else + return [UIDevice currentDevice].systemVersion; +#endif +} + +- (NSDictionary *)collectAutomaticPeopleProperties +{ + NSMutableDictionary *p = [NSMutableDictionary dictionaryWithDictionary:@{ + @"$ios_version": [self deviceSystemVersion], + @"$ios_lib_version": [Mixpanel libVersion], + }]; + NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary; + if (infoDictionary[@"CFBundleVersion"]) { + p[@"$ios_app_version"] = infoDictionary[@"CFBundleVersion"]; + } + if (infoDictionary[@"CFBundleShortVersionString"]) { + p[@"$ios_app_release"] = infoDictionary[@"CFBundleShortVersionString"]; + } + __strong Mixpanel *strongMixpanel = self.mixpanel; + NSString *deviceModel = [strongMixpanel deviceModel]; + if (deviceModel) { + p[@"$ios_device_model"] = deviceModel; + } + +#if !defined(MIXPANEL_MACOS) + NSString *ifa = [strongMixpanel IFA]; + if (ifa) { + p[@"$ios_ifa"] = ifa; + } +#endif + return [p copy]; +} + +- (void)addPeopleRecordToQueueWithAction:(NSString *)action andProperties:(NSDictionary *)properties +{ + NSNumber *epochMilliseconds = @(round([[NSDate date] timeIntervalSince1970] * 1000)); + __strong Mixpanel *strongMixpanel = self.mixpanel; + if (strongMixpanel) { + properties = [properties copy]; + BOOL ignore_time = self.ignoreTime; + + dispatch_async(strongMixpanel.serialQueue, ^{ + NSMutableDictionary *r = [NSMutableDictionary dictionary]; + NSMutableDictionary *p = [NSMutableDictionary dictionary]; + r[@"$token"] = strongMixpanel.apiToken; + if (!r[@"$time"]) { + // milliseconds unix timestamp + r[@"$time"] = epochMilliseconds; + } + if (ignore_time) { + r[@"$ignore_time"] = @YES; + } + + if ([action isEqualToString:@"$unset"]) { + // $unset takes an array of property names which is supplied to this method + // in the properties parameter under the key "$properties" + r[action] = properties[@"$properties"]; + } else { + if ([action isEqualToString:@"$set"] || [action isEqualToString:@"$set_once"]) { + [p addEntriesFromDictionary:self.automaticPeopleProperties]; + } + [p addEntriesFromDictionary:properties]; + r[action] = [NSDictionary dictionaryWithDictionary:p]; + } + + if (self.distinctId) { + r[@"$distinct_id"] = self.distinctId; + MPLogInfo(@"%@ queueing people record: %@", strongMixpanel, r); + @synchronized (strongMixpanel) { + [strongMixpanel.peopleQueue addObject:r]; + if (strongMixpanel.peopleQueue.count > 500) { + [strongMixpanel.peopleQueue removeObjectAtIndex:0]; + } + } + } else { + MPLogInfo(@"%@ queueing unidentified people record: %@", self.mixpanel, r); + [self.unidentifiedQueue addObject:r]; + if (self.unidentifiedQueue.count > 500) { + [self.unidentifiedQueue removeObjectAtIndex:0]; + } + } + + [strongMixpanel archivePeople]; + }); +#if MIXPANEL_FLUSH_IMMEDIATELY + [strongMixpanel flush]; +#endif + } +} + ++ (NSString *)pushDeviceTokenToString:(NSData *)deviceToken +{ + const unsigned char *buffer = (const unsigned char *)deviceToken.bytes; + if (!buffer) { + return nil; + } + NSMutableString *hex = [NSMutableString stringWithCapacity:(deviceToken.length * 2)]; + for (NSUInteger i = 0; i < deviceToken.length; i++) { + [hex appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)buffer[i]]]; + } + return [hex copy]; +} + +#pragma mark - Public API + +- (void)addPushDeviceToken:(NSData *)deviceToken +{ + NSDictionary *properties = @{@"$ios_devices": @[[MixpanelPeople pushDeviceTokenToString:deviceToken]]}; + [self addPeopleRecordToQueueWithAction:@"$union" andProperties:properties]; +} + +- (void)removeAllPushDeviceTokens +{ + NSDictionary *properties = @{ @"$properties": @[@"$ios_devices"] }; + [self addPeopleRecordToQueueWithAction:@"$unset" andProperties:properties]; +} + +- (void)removePushDeviceToken:(NSData *)deviceToken +{ + NSDictionary *properties = @{@"$ios_devices": [MixpanelPeople pushDeviceTokenToString:deviceToken]}; + [self addPeopleRecordToQueueWithAction:@"$remove" andProperties:properties]; +} + +- (void)set:(NSDictionary *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + [Mixpanel assertPropertyTypes:properties]; + [self addPeopleRecordToQueueWithAction:@"$set" andProperties:properties]; +} + +- (void)set:(NSString *)property to:(id)object +{ + NSAssert(property != nil, @"property must not be nil"); + NSAssert(object != nil, @"object must not be nil"); + if (property == nil || object == nil) { + return; + } + [self set:@{property: object}]; +} + +- (void)setOnce:(NSDictionary *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + [Mixpanel assertPropertyTypes:properties]; + [self addPeopleRecordToQueueWithAction:@"$set_once" andProperties:properties]; +} + +- (void)unset:(NSArray *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + for (id __unused v in properties) { + NSAssert([v isKindOfClass:[NSString class]], + @"%@ unset property names should be NSString. found: %@", self, v); + } + // $unset takes an array but addPeopleRecordToQueueWithAction:andProperties takes an NSDictionary + // so the array is stored under the key "$properties" which the above method expects when action is $unset + [self addPeopleRecordToQueueWithAction:@"$unset" andProperties:@{@"$properties": properties}]; +} + +- (void)increment:(NSDictionary *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + for (id __unused v in properties.allValues) { + NSAssert([v isKindOfClass:[NSNumber class]], + @"%@ increment property values should be NSNumber. found: %@", self, v); + } + [self addPeopleRecordToQueueWithAction:@"$add" andProperties:properties]; +} + +- (void)increment:(NSString *)property by:(NSNumber *)amount +{ + NSAssert(property != nil, @"property must not be nil"); + NSAssert(amount != nil, @"amount must not be nil"); + if (property == nil || amount == nil) { + return; + } + [self increment:@{property: amount}]; +} + +- (void)append:(NSDictionary *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + [Mixpanel assertPropertyTypes:properties]; + [self addPeopleRecordToQueueWithAction:@"$append" andProperties:properties]; +} + +- (void)union:(NSDictionary *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + for (id __unused v in properties.allValues) { + NSAssert([v isKindOfClass:[NSArray class]], + @"%@ union property values should be NSArray. found: %@", self, v); + } + [self addPeopleRecordToQueueWithAction:@"$union" andProperties:properties]; +} + +- (void)remove:(NSDictionary *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + [Mixpanel assertPropertyTypes:properties]; + [self addPeopleRecordToQueueWithAction:@"$remove" andProperties:properties]; +} + +- (void)merge:(NSDictionary *)properties +{ + NSAssert(properties != nil, @"properties must not be nil"); + [self addPeopleRecordToQueueWithAction:@"$merge" andProperties:properties]; +} + +- (void)trackCharge:(NSNumber *)amount +{ + [self trackCharge:amount withProperties:nil]; +} + +- (void)trackCharge:(NSNumber *)amount withProperties:(NSDictionary *)properties +{ + NSAssert(amount != nil, @"amount must not be nil"); + if (amount != nil) { + NSMutableDictionary *txn = [NSMutableDictionary dictionaryWithObjectsAndKeys:amount, @"$amount", [NSDate date], @"$time", nil]; + if (properties) { + [txn addEntriesFromDictionary:properties]; + } + [self append:@{@"$transactions": txn}]; + } +} + +- (void)clearCharges +{ + [self set:@{@"$transactions": @[]}]; +} + +- (void)deleteUser +{ + [self addPeopleRecordToQueueWithAction:@"$delete" andProperties:@{}]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MixpanelPeoplePrivate.h b/ios/Pods/Mixpanel/Mixpanel/MixpanelPeoplePrivate.h new file mode 100644 index 0000000..2c6d0dd --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MixpanelPeoplePrivate.h @@ -0,0 +1,22 @@ +// +// MixpanelPeoplePrivate.h +// Mixpanel +// +// Created by Sam Green on 6/16/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// +#import + +@class Mixpanel; + +@interface MixpanelPeople () + +@property (nonatomic, weak) Mixpanel *mixpanel; +@property (nonatomic, strong) NSMutableArray *unidentifiedQueue; +@property (nonatomic, copy) NSString *distinctId; +@property (nonatomic, strong) NSDictionary *automaticPeopleProperties; + +- (instancetype)initWithMixpanel:(Mixpanel *)mixpanel; +- (void)merge:(NSDictionary *)properties; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/MixpanelPrivate.h b/ios/Pods/Mixpanel/Mixpanel/MixpanelPrivate.h new file mode 100644 index 0000000..dfad50c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/MixpanelPrivate.h @@ -0,0 +1,123 @@ +// +// MixpanelPrivate.h +// Mixpanel +// +// Created by Sam Green on 6/16/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "Mixpanel.h" +#import "MPNetwork.h" + +#if TARGET_OS_IPHONE +#if !MIXPANEL_NO_REACHABILITY_SUPPORT +#import +#import +#import +#endif + +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT +#import "Mixpanel+AutomaticTracks.h" +#import "AutomaticTracksConstants.h" +#import "AutomaticEvents.h" +#import "MixpanelExceptionHandler.h" +#endif +#endif + +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT +#import "MPResources.h" +#import "MPABTestDesignerConnection.h" +#import "UIView+MPHelpers.h" +#import "MPDesignerEventBindingMessage.h" +#import "MPDesignerSessionCollection.h" +#import "MPEventBinding.h" +#import "MPNotification.h" +#import "MPTakeoverNotification.h" +#import "MPMiniNotification.h" +#import "MPNotificationViewController.h" +#import "MPSwizzler.h" +#import "MPTweakStore.h" +#import "MPVariant.h" +#import "MPWebSocket.h" +#endif + +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT +@interface Mixpanel () +#else +@interface Mixpanel () +#endif +{ + NSUInteger _flushInterval; + BOOL _enableVisualABTestAndCodeless; +} + +#if !MIXPANEL_NO_REACHABILITY_SUPPORT +@property (nonatomic, assign) SCNetworkReachabilityRef reachability; +@property (nonatomic, strong) CTTelephonyNetworkInfo *telephonyInfo; +#endif + +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT +@property (nonatomic, strong) UILongPressGestureRecognizer *testDesignerGestureRecognizer; +@property (nonatomic, strong) MPABTestDesignerConnection *abtestDesignerConnection; +#endif + +#if !MIXPANEL_NO_AUTOMATIC_EVENTS_SUPPORT +@property (nonatomic) AutomaticTrackMode validationMode; +@property (nonatomic) NSUInteger validationEventCount; +@property (nonatomic, getter=isValidationEnabled) BOOL validationEnabled; +@property (atomic, strong) AutomaticEvents *automaticEvents; +#endif + +#if !defined(MIXPANEL_WATCHOS) && !defined(MIXPANEL_MACOS) +@property (nonatomic, assign) UIBackgroundTaskIdentifier taskId; +@property (nonatomic, strong) UIViewController *notificationViewController; +#endif + +// re-declare internally as readwrite +@property (atomic, strong) MixpanelPeople *people; +@property (atomic, strong) MPNetwork *network; +@property (atomic, copy) NSString *distinctId; +@property (atomic, copy) NSString *alias; + +@property (nonatomic, copy) NSString *apiToken; +@property (atomic, strong) NSDictionary *superProperties; +@property (atomic, strong) NSDictionary *automaticProperties; +@property (nonatomic, strong) NSTimer *timer; +@property (nonatomic, strong) NSMutableArray *eventsQueue; +@property (nonatomic, strong) NSMutableArray *peopleQueue; +@property (nonatomic) dispatch_queue_t serialQueue; +@property (nonatomic) dispatch_queue_t networkQueue; +@property (nonatomic, strong) NSMutableDictionary *timedEvents; + +@property (nonatomic) BOOL decideResponseCached; +@property (nonatomic, strong) NSNumber *automaticEventsEnabled; +@property (nonatomic, strong) NSArray *notifications; +@property (nonatomic, strong) id currentlyShowingNotification; +@property (nonatomic, strong) NSMutableSet *shownNotifications; + +@property (nonatomic, strong) NSSet *variants; +@property (nonatomic, strong) NSSet *eventBindings; + +@property (atomic, copy) NSString *switchboardURL; + ++ (void)assertPropertyTypes:(NSDictionary *)properties; + +- (NSString *)deviceModel; +- (NSString *)IFA; + +- (void)archivePeople; +- (NSString *)defaultDistinctId; +- (void)archive; +- (NSString *)eventsFilePath; +- (NSString *)peopleFilePath; +- (NSString *)propertiesFilePath; + +#if !MIXPANEL_NO_NOTIFICATION_AB_TEST_SUPPORT +- (void)showNotificationWithObject:(MPNotification *)notification; +- (void)markVariantRun:(MPVariant *)variant; +- (void)checkForDecideResponseWithCompletion:(void (^)(NSArray *notifications, NSSet *variants, NSSet *eventBindings))completion; +- (void)checkForDecideResponseWithCompletion:(void (^)(NSArray *notifications, NSSet *variants, NSSet *eventBindings))completion useCache:(BOOL)useCache; +#endif + +@end + diff --git a/ios/Pods/Mixpanel/Mixpanel/NSInvocation+MPHelpers.h b/ios/Pods/Mixpanel/Mixpanel/NSInvocation+MPHelpers.h new file mode 100644 index 0000000..1c105c9 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/NSInvocation+MPHelpers.h @@ -0,0 +1,11 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import + +@interface NSInvocation (MPHelpers) + +- (void)mp_setArgumentsFromArray:(NSArray *)argumentArray; +- (id)mp_returnValue; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/NSInvocation+MPHelpers.m b/ios/Pods/Mixpanel/Mixpanel/NSInvocation+MPHelpers.m new file mode 100644 index 0000000..1ad4dd3 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/NSInvocation+MPHelpers.m @@ -0,0 +1,188 @@ +// +// Copyright (c) 2014 Mixpanel. All rights reserved. + +#import +#import +#import "MPLogger.h" +#import "NSInvocation+MPHelpers.h" + +typedef union { + char _chr; + unsigned char _uchr; + short _sht; + unsigned short _usht; + int _int; + unsigned int _uint; + long _lng; + unsigned long _ulng; + long long _lng_lng; + unsigned long long _ulng_lng; + float _flt; + double _dbl; + _Bool _bool; +} MPObjCNumericTypes; + +static void MPFree(void *p) +{ + if (p) { + free(p); + } +} + +static void *MPAllocBufferForObjCType(const char *objCType) +{ + void *buffer = NULL; + + NSUInteger size, alignment; + NSGetSizeAndAlignment(objCType, &size, &alignment); + + int result = posix_memalign(&buffer, MAX(sizeof(void *), alignment), size); + if (result != 0) { + MPLogError(@"Error allocating aligned memory: %s", strerror(result)); + } + + if (buffer) { + memset(buffer, 0, size); + } + + return buffer; +} + +@implementation NSInvocation (MPHelpers) + +- (void)mp_setArgument:(id)argumentValue atIndex:(NSUInteger)index +{ + const char *argumentType = [self.methodSignature getArgumentTypeAtIndex:index]; + + if ([argumentValue isKindOfClass:[NSNumber class]] && strlen(argumentType) == 1) { + // Deal with NSNumber instances (converting to primitive numbers) + NSNumber *numberArgument = argumentValue; + + MPObjCNumericTypes arg; + switch (argumentType[0]) + { + case _C_CHR: arg._chr = numberArgument.charValue; break; + case _C_UCHR: arg._uchr = numberArgument.unsignedCharValue; break; + case _C_SHT: arg._sht = numberArgument.shortValue; break; + case _C_USHT: arg._usht = numberArgument.unsignedShortValue; break; + case _C_INT: arg._int = numberArgument.intValue; break; + case _C_UINT: arg._uint = numberArgument.unsignedIntValue; break; + case _C_LNG: arg._lng = numberArgument.longValue; break; + case _C_ULNG: arg._ulng = numberArgument.unsignedLongValue; break; + case _C_LNG_LNG: arg._lng_lng = numberArgument.longLongValue; break; + case _C_ULNG_LNG: arg._ulng_lng = numberArgument.unsignedLongLongValue; break; + case _C_FLT: arg._flt = numberArgument.floatValue; break; + case _C_DBL: arg._dbl = numberArgument.doubleValue; break; + case _C_BOOL: arg._bool = numberArgument.boolValue; break; + default: + NSAssert(NO, @"Currently unsupported argument type!"); + } + + [self setArgument:&arg atIndex:(NSInteger)index]; + } + else if ([argumentValue isKindOfClass:[NSValue class]]) + { + NSValue *valueArgument = argumentValue; + + NSAssert2(strcmp(valueArgument.objCType, argumentType) == 0, @"Objective-C type mismatch (%s != %s)!", valueArgument.objCType, argumentType); + + void *buffer = MPAllocBufferForObjCType(valueArgument.objCType); + + [valueArgument getValue:buffer]; + + [self setArgument:&buffer atIndex:(NSInteger)index]; + + MPFree(buffer); + } else { + switch (argumentType[0]) + { + case _C_ID: + { + [self setArgument:&argumentValue atIndex:(NSInteger)index]; + break; + } + case _C_SEL: + { + SEL sel = NSSelectorFromString(argumentValue); + [self setArgument:&sel atIndex:(NSInteger)index]; + break; + } + default: + NSAssert(NO, @"Currently unsupported argument type!"); + } + } +} + +- (void)mp_setArgumentsFromArray:(NSArray *)argumentArray +{ + NSParameterAssert(argumentArray.count == (self.methodSignature.numberOfArguments - 2)); + + NSUInteger i = 0; + for (id argument in argumentArray) { + NSUInteger argumentIndex = 2 + i; + [self mp_setArgument:argument atIndex:argumentIndex]; + ++i; + } +} + +- (id)mp_returnValue +{ + __strong id returnValue = nil; + + NSMethodSignature *methodSignature = self.methodSignature; + + const char *objCType = methodSignature.methodReturnType; + void *buffer = MPAllocBufferForObjCType(objCType); + + [self getReturnValue:buffer]; + + if (strlen(objCType) == 1) { + switch (objCType[0]) + { + case _C_CHR: returnValue = @(*((char *)buffer)); break; + case _C_UCHR: returnValue = @(*((unsigned char *)buffer)); break; + case _C_SHT: returnValue = @(*((short *)buffer)); break; + case _C_USHT: returnValue = @(*((unsigned short *)buffer)); break; + case _C_INT: returnValue = @(*((int *)buffer)); break; + case _C_UINT: returnValue = @(*((unsigned int *)buffer)); break; + case _C_LNG: returnValue = @(*((long *)buffer)); break; + case _C_ULNG: returnValue = @(*((unsigned long*)buffer)); break; + case _C_LNG_LNG: returnValue = @(*((long long *)buffer)); break; + case _C_ULNG_LNG: returnValue = @(*((unsigned long long*)buffer)); break; + case _C_FLT: returnValue = @(*((float *)buffer)); break; + case _C_DBL: returnValue = @(*((double *)buffer)); break; + case _C_BOOL: returnValue = @(*((_Bool *)buffer)); break; + case _C_ID: returnValue = *((__unsafe_unretained id *)buffer); break; + case _C_SEL: returnValue = NSStringFromSelector(*((SEL *)buffer)); break; + default: + NSAssert1(NO, @"Unhandled return type: %s", objCType); + break; + } + } else { + switch (objCType[0]) + { + case _C_STRUCT_B: returnValue = [NSValue valueWithBytes:buffer objCType:objCType]; break; + case _C_PTR: + { + CFTypeRef cfTypeRef = *(CFTypeRef *)buffer; + if ((strcmp(objCType, @encode(CGImageRef)) == 0 && CFGetTypeID(cfTypeRef) == CGImageGetTypeID()) || + (strcmp(objCType, @encode(CGColorRef)) == 0 && CFGetTypeID(cfTypeRef) == CGColorGetTypeID())) + { + returnValue = (__bridge id)cfTypeRef; + } else { + NSAssert(NO, @"Currently unsupported return type!"); + } + break; + } + default: + NSAssert1(NO, @"Unhandled return type: %s", objCType); + break; + } + } + + MPFree(buffer); + + return returnValue; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.h b/ios/Pods/Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.h new file mode 100644 index 0000000..8c22aa8 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.h @@ -0,0 +1,23 @@ +// +// NSNotificationCenter+AutomaticTracks.h +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSNotificationCenter (AutomaticTracks) + +- (void)mp_postNotification:(NSNotification *)notification; + +- (void)mp_postNotificationName:(NSString *)name + object:(nullable id)object + userInfo:(nullable NSDictionary *)info; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.m b/ios/Pods/Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.m new file mode 100644 index 0000000..68b45ae --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/NSNotificationCenter+AutomaticTracks.m @@ -0,0 +1,47 @@ +// +// NSNotificationCenter+AutomaticTracks.m +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "NSNotificationCenter+AutomaticTracks.h" +#import "Mixpanel+AutomaticTracks.h" +#import "AutomaticTracksConstants.h" + +@implementation NSNotificationCenter (AutomaticTracks) + +- (void)mp_postNotification:(NSNotification *)notification { + if ([NSNotificationCenter shouldTrackNotificationNamed:notification.name]) { + [[Mixpanel sharedAutomatedInstance] track:kAutomaticTrackName]; + } + + [self mp_postNotification:notification]; +} + +- (void)mp_postNotificationName:(NSString *)name + object:(nullable id)object + userInfo:(nullable NSDictionary *)info { + if ([NSNotificationCenter shouldTrackNotificationNamed:name]) { + [[Mixpanel sharedAutomatedInstance] track:kAutomaticTrackName]; + } + + [self mp_postNotificationName:name object:object userInfo:info]; +} + ++ (BOOL)shouldTrackNotificationNamed:(NSString *)name { + // iOS spams notifications. We're whitelisting for now. + NSArray *names = @[ + // UITextField Editing + UITextFieldTextDidEndEditingNotification, + + // UIApplication Lifecycle + UIApplicationDidFinishLaunchingNotification, + UIApplicationDidEnterBackgroundNotification, + UIApplicationDidBecomeActiveNotification ]; + NSSet *whiteListedNotificationNames = [NSSet setWithArray:names]; + return [whiteListedNotificationNames containsObject:name]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIApplication+AutomaticTracks.h b/ios/Pods/Mixpanel/Mixpanel/UIApplication+AutomaticTracks.h new file mode 100644 index 0000000..ba7fe73 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIApplication+AutomaticTracks.h @@ -0,0 +1,22 @@ +// +// UIApplication+AutomaticTracks.h +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIApplication (AutomaticTracks) + +- (BOOL)mp_sendAction:(SEL)action + to:(nullable id)to + from:(nullable id)from + forEvent:(nullable UIEvent *)event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Mixpanel/Mixpanel/UIApplication+AutomaticTracks.m b/ios/Pods/Mixpanel/Mixpanel/UIApplication+AutomaticTracks.m new file mode 100644 index 0000000..3ec28a6 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIApplication+AutomaticTracks.m @@ -0,0 +1,20 @@ +// +// UIApplication+AutomaticTracks.m +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "UIApplication+AutomaticTracks.h" +#import "Mixpanel+AutomaticTracks.h" +#import "AutomaticTracksConstants.h" + +@implementation UIApplication (AutomaticTracks) + +- (BOOL)mp_sendAction:(SEL)action to:(id)to from:(id)from forEvent:(UIEvent *)event { + [[Mixpanel sharedAutomatedInstance] track:kAutomaticTrackName]; + return [self mp_sendAction:action to:to from:from forEvent:event]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIColor+MPColor.h b/ios/Pods/Mixpanel/Mixpanel/UIColor+MPColor.h new file mode 100644 index 0000000..d0c508f --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIColor+MPColor.h @@ -0,0 +1,14 @@ +#import + +@interface UIColor (MPColor) + ++ (UIColor *)mp_applicationPrimaryColor; ++ (UIColor *)mp_lightEffectColor; ++ (UIColor *)mp_extraLightEffectColor; ++ (UIColor *)mp_darkEffectColor; + ++ (UIColor *)mp_colorFromRGB:(NSUInteger)rgbValue; +- (UIColor *)mp_colorAddColor:(UIColor *)overlay; +- (UIColor *)colorWithSaturationComponent:(CGFloat) saturation; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIColor+MPColor.m b/ios/Pods/Mixpanel/Mixpanel/UIColor+MPColor.m new file mode 100644 index 0000000..285567e --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIColor+MPColor.m @@ -0,0 +1,81 @@ +#import "UIColor+MPColor.h" + +@implementation UIColor (MPColor) + ++ (UIColor *)mp_applicationPrimaryColor { + // First try and find the color of the UINavigationBar of the top UINavigationController that is showing now. + UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; + UINavigationController *topNavigationController = nil; + + do { + if ([rootViewController isKindOfClass:[UINavigationController class]]) { + topNavigationController = (UINavigationController *)rootViewController; + } else if (rootViewController.navigationController) { + topNavigationController = rootViewController.navigationController; + } + } while ((rootViewController = rootViewController.presentedViewController)); + + UIColor *color = [topNavigationController navigationBar].barTintColor; + + // Then try and use the UINavigationBar default color for the app + if (!color) { + color = [UINavigationBar appearance].barTintColor; + } + + // Or the UITabBar default color + if (!color) { + color = [UITabBar appearance].barTintColor; + } + + return color; +} + ++ (UIColor *)mp_lightEffectColor { + return [UIColor colorWithWhite:1.0f alpha:0.3f]; +} + ++ (UIColor *)mp_extraLightEffectColor { + return [UIColor colorWithWhite:0.97f alpha:0.82f]; +} + ++ (UIColor *)mp_darkEffectColor { + return [UIColor colorWithWhite:0.11f alpha:0.73f]; +} + ++ (UIColor *)mp_colorFromRGB:(NSUInteger)rgbValue { + return [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:((float)((rgbValue & 0xFF000000) >> 24))/255.0]; +} + +- (UIColor *)mp_colorAddColor:(UIColor *)overlay { + CGFloat bgR = 0; + CGFloat bgG = 0; + CGFloat bgB = 0; + CGFloat bgA = 0; + + CGFloat fgR = 0; + CGFloat fgG = 0; + CGFloat fgB = 0; + CGFloat fgA = 0; + + + [self getRed:&bgR green: &bgG blue: &bgB alpha: &bgA]; + [overlay getRed:&fgR green: &fgG blue: &fgB alpha: &fgA]; + + CGFloat r = fgA * fgR + (1 - fgA) * bgR; + CGFloat g = fgA * fgG + (1 - fgA) * bgG; + CGFloat b = fgA * fgB + (1 - fgA) * bgB; + + return [UIColor colorWithRed:r green:g blue:b alpha:1.0]; + +} + +- (UIColor *)colorWithSaturationComponent:(CGFloat) saturation { + UIColor *newColor; + CGFloat h, s, b, a; + if ([self getHue:&h saturation:&s brightness:&b alpha:&a]) { + newColor = [UIColor colorWithHue:h saturation:saturation brightness:b alpha:a]; + } + return newColor; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIImage+MPAverageColor.h b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPAverageColor.h new file mode 100644 index 0000000..3092beb --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPAverageColor.h @@ -0,0 +1,8 @@ +#import + +@interface UIImage (MPAverageColor) + +- (UIColor *)mp_averageColor; +- (UIColor *)mp_importantColor; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIImage+MPAverageColor.m b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPAverageColor.m new file mode 100644 index 0000000..affba3b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPAverageColor.m @@ -0,0 +1,84 @@ +#import "UIImage+MPAverageColor.h" + +@implementation UIImage (MPAverageColor) + +- (UIColor *)mp_averageColor +{ + UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0.f); + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGContextSetInterpolationQuality(ctx, kCGInterpolationMedium); + [self drawInRect:CGRectMake(0, 0, 1, 1) blendMode:kCGBlendModeCopy alpha:1]; + + uint8_t *data = CGBitmapContextGetData(ctx); + UIColor *color = [UIColor colorWithRed:data[2] / 255.0f + green:data[1] / 255.0f + blue:data[0] / 255.0f + alpha:1]; + UIGraphicsEndImageContext(); + + return color; +} + +- (UIColor *)mp_importantColor { + static const size_t kImageStartRow = 40; + static const size_t kNumberOfRows = 124; + static const size_t kNumberOfHexColors = 262144; + + const size_t kImageWidth = CGImageGetWidth(self.CGImage); + const size_t kImageHeight = CGImageGetHeight(self.CGImage); + + const size_t kBytesPerPixel = CGImageGetBitsPerPixel(self.CGImage) / 8; + const size_t kBytesPerRow = CGImageGetBytesPerRow(self.CGImage); + + // Don't calculate + if (kImageHeight < kImageStartRow + kNumberOfRows) { + return [self mp_averageColor]; + } + + CFDataRef imageData = CGDataProviderCopyData(CGImageGetDataProvider(self.CGImage)); + const uint8_t *imageDataBuffer = CFDataGetBytePtr(imageData); + + char colorIndices[kNumberOfHexColors]; + memset(colorIndices, 0, sizeof(char) * kNumberOfHexColors); + + for (size_t rowIndex = kImageStartRow; rowIndex < kImageStartRow + kNumberOfRows; rowIndex++) { + const uint8_t *row = imageDataBuffer + kBytesPerRow * rowIndex; + for (size_t column = 0; column < kImageWidth; column++) { + const uint8_t red = row[0]; + const uint8_t green = row[1]; + const uint8_t blue = row[2]; + + const int hexColor = (red >> 2) + ((green >> 2) << 6) + ((blue >> 2) << 12); + BOOL validHexColor = (0 < hexColor && hexColor < (int)kNumberOfHexColors - 1); + if (validHexColor) { + + BOOL notTooBright = (red + green + blue < 255 + 255 + 200); + if (notTooBright) { + + BOOL notGrayScale = (red != blue && blue != green && green != red); + if (notGrayScale) { + colorIndices[hexColor]++; + } + } + } + row += kBytesPerPixel; + } + } + + NSUInteger index = 0; + char max = 0; + for (NSUInteger i = 0; i < kNumberOfHexColors; i++) { + if (colorIndices[i] > max) { + max = colorIndices[i]; + index = i; + } + } + + return [UIColor colorWithRed:(((index & 63) << 2) + 3) / 255.0f + green:(((index >> 4) & 252) + 3) / 255.0f + blue:(((index >> 10) & 252) + 3) / 255.0f + alpha:1]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIImage+MPImageEffects.h b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPImageEffects.h new file mode 100644 index 0000000..707e8e3 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPImageEffects.h @@ -0,0 +1,105 @@ +/* + File: UIImage+MPImageEffects.h + Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how to use vImage to efficiently calculate a blur. + Version: 1.0 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2013 Apple Inc. All Rights Reserved. + + + Copyright © 2013 Apple Inc. All rights reserved. + WWDC 2013 License + + NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013 + Session. Please refer to the applicable WWDC 2013 Session for further + information. + + IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and + your use, installation, modification or redistribution of this Apple + software constitutes acceptance of these terms. If you do not agree with + these terms, please do not use, install, modify or redistribute this + Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple + Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES + NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE + IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + EA1002 + 5/3/2013 + */ + +@interface UIImage (MPImageEffects) + +- (UIImage *)mp_applyLightEffect; +- (UIImage *)mp_applyExtraLightEffect; +- (UIImage *)mp_applyDarkEffect; +- (UIImage *)mp_applyTintEffectWithColor:(UIColor *)tintColor; + +- (UIImage *)mp_applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIImage+MPImageEffects.m b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPImageEffects.m new file mode 100644 index 0000000..891a832 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIImage+MPImageEffects.m @@ -0,0 +1,276 @@ +/* + File: UIImage+MPImageEffects.m + Abstract: This is a category of UIImage that adds methods to apply blur and tint effects to an image. This is the code you’ll want to look out to find out how to use vImage to efficiently calculate a blur. + Version: 1.0 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Inc. ("Apple") in consideration of your agreement to the following + terms, and your use, installation, modification or redistribution of + this Apple software constitutes acceptance of these terms. If you do + not agree with these terms, please do not use, install, modify or + redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright (C) 2013 Apple Inc. All Rights Reserved. + + + Copyright © 2013 Apple Inc. All rights reserved. + WWDC 2013 License + + NOTE: This Apple Software was supplied by Apple as part of a WWDC 2013 + Session. Please refer to the applicable WWDC 2013 Session for further + information. + + IMPORTANT: This Apple software is supplied to you by Apple Inc. + ("Apple") in consideration of your agreement to the following terms, and + your use, installation, modification or redistribution of this Apple + software constitutes acceptance of these terms. If you do not agree with + these terms, please do not use, install, modify or redistribute this + Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a non-exclusive license, under + Apple's copyrights in this original Apple software (the "Apple + Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Inc. may + be used to endorse or promote products derived from the Apple Software + without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or + implied, are granted by Apple herein, including but not limited to any + patent rights that may be infringed by your derivative works or by other + works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES + NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE + IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + EA1002 + 5/3/2013 + */ + +#import +#import +#import "MPLogger.h" +#import "UIColor+MPColor.h" +#import "UIImage+MPImageEffects.h" + +@implementation UIImage (MPImageEffects) + + +- (UIImage *)mp_applyLightEffect +{ + UIColor *tintColor = [UIColor mp_lightEffectColor]; + return [self mp_applyBlurWithRadius:30.0f tintColor:tintColor saturationDeltaFactor:1.8f maskImage:nil]; +} + + +- (UIImage *)mp_applyExtraLightEffect +{ + UIColor *tintColor = [UIColor mp_extraLightEffectColor]; + return [self mp_applyBlurWithRadius:20.0f tintColor:tintColor saturationDeltaFactor:1.8f maskImage:nil]; +} + + +- (UIImage *)mp_applyDarkEffect +{ + UIColor *tintColor = [UIColor mp_darkEffectColor]; + return [self mp_applyBlurWithRadius:20.0f tintColor:tintColor saturationDeltaFactor:1.8f maskImage:nil]; +} + + +- (UIImage *)mp_applyTintEffectWithColor:(UIColor *)tintColor +{ + const CGFloat EffectColorAlpha = 0.6f; + UIColor *effectColor = tintColor; + size_t componentCount = CGColorGetNumberOfComponents(tintColor.CGColor); + if (componentCount == 2) { + CGFloat b; + if ([tintColor getWhite:&b alpha:NULL]) { + effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha]; + } + } else { + CGFloat r, g, b; + if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) { + effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha]; + } + } + return [self mp_applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1 maskImage:nil]; +} + + +- (UIImage *)mp_applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage +{ + // Check pre-conditions. + if (self.size.width < 1 || self.size.height < 1) { + MPLogError(@"invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self); + return nil; + } + if (!self.CGImage) { + MPLogError(@"image must be backed by a CGImage: %@", self); + return nil; + } + if (maskImage && !maskImage.CGImage) { + MPLogError(@"maskImage must be backed by a CGImage: %@", maskImage); + return nil; + } + + CGRect imageRect = { CGPointZero, self.size }; + UIImage *effectImage = self; + + BOOL hasBlur = blurRadius > __FLT_EPSILON__; + BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__; + if (hasBlur || hasSaturationChange) { + UIGraphicsBeginImageContextWithOptions(self.size, NO, [UIScreen mainScreen].scale); + CGContextRef effectInContext = UIGraphicsGetCurrentContext(); + CGContextScaleCTM(effectInContext, 1, -1); + CGContextTranslateCTM(effectInContext, 0, -self.size.height); + CGContextDrawImage(effectInContext, imageRect, self.CGImage); + + vImage_Buffer effectInBuffer; + effectInBuffer.data = CGBitmapContextGetData(effectInContext); + effectInBuffer.width = CGBitmapContextGetWidth(effectInContext); + effectInBuffer.height = CGBitmapContextGetHeight(effectInContext); + effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext); + + UIGraphicsBeginImageContextWithOptions(self.size, NO, [UIScreen mainScreen].scale); + CGContextRef effectOutContext = UIGraphicsGetCurrentContext(); + vImage_Buffer effectOutBuffer; + effectOutBuffer.data = CGBitmapContextGetData(effectOutContext); + effectOutBuffer.width = CGBitmapContextGetWidth(effectOutContext); + effectOutBuffer.height = CGBitmapContextGetHeight(effectOutContext); + effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext); + + if (hasBlur) { + // A description of how to compute the box kernel width from the Gaussian + // radius (aka standard deviation) appears in the SVG spec: + // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement + // + // For larger values of 's' (s >= 2.0), an approximation can be used: Three + // successive box-blurs build a piece-wise quadratic convolution kernel, which + // approximates the Gaussian kernel to within roughly 3%. + // + // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) + // + // ... if d is odd, use three box-blurs of size 'd', centered on the output pixel. + // + CGFloat inputRadius = blurRadius * [UIScreen mainScreen].scale; + uint32_t radius = (uint32_t)floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5); + if (radius % 2 != 1) { + radius += 1; // force radius to be odd so that the three box-blur methodology works. + } + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); + vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend); + } + BOOL effectImageBuffersAreSwapped = NO; + if (hasSaturationChange) { + CGFloat s = saturationDeltaFactor; + CGFloat floatingPointSaturationMatrix[] = { + 0.0722f + 0.9278f * s, 0.0722f - 0.0722f * s, 0.0722f - 0.0722f * s, 0, + 0.7152f - 0.7152f * s, 0.7152f + 0.2848f * s, 0.7152f - 0.7152f * s, 0, + 0.2126f - 0.2126f * s, 0.2126f - 0.2126f * s, 0.2126f + 0.7873f * s, 0, + 0, 0, 0, 1, + }; + const int32_t divisor = 256; + NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix) / sizeof(floatingPointSaturationMatrix[0]); + int16_t saturationMatrix[matrixSize]; + for (NSUInteger i = 0; i < matrixSize; ++i) { + saturationMatrix[i] = (int16_t)round(floatingPointSaturationMatrix[i] * divisor); + } + if (hasBlur) { + vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); + effectImageBuffersAreSwapped = YES; + } else { + vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); + } + } + if (!effectImageBuffersAreSwapped) + effectImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + if (effectImageBuffersAreSwapped) + effectImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + + // Set up output context. + UIGraphicsBeginImageContextWithOptions(self.size, NO, [UIScreen mainScreen].scale); + CGContextRef outputContext = UIGraphicsGetCurrentContext(); + CGContextScaleCTM(outputContext, 1, -1); + CGContextTranslateCTM(outputContext, 0, -self.size.height); + + // Draw base image. + CGContextDrawImage(outputContext, imageRect, self.CGImage); + + // Draw effect image. + if (hasBlur) { + CGContextSaveGState(outputContext); + if (maskImage) { + CGContextClipToMask(outputContext, imageRect, maskImage.CGImage); + } + CGContextDrawImage(outputContext, imageRect, effectImage.CGImage); + CGContextRestoreGState(outputContext); + } + + // Add in color tint. + if (tintColor) { + CGContextSaveGState(outputContext); + CGContextSetFillColorWithColor(outputContext, tintColor.CGColor); + CGContextFillRect(outputContext, imageRect); + CGContextRestoreGState(outputContext); + } + + // Output image is ready. + UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return outputImage; +} + + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIView+MPHelpers.h b/ios/Pods/Mixpanel/Mixpanel/UIView+MPHelpers.h new file mode 100644 index 0000000..5ddd29c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIView+MPHelpers.h @@ -0,0 +1,12 @@ +#import +#import + +@interface UIView (MPHelpers) + +- (UIImage *)mp_snapshotImage; +- (UIImage *)mp_snapshotForBlur; +- (int)mp_fingerprintVersion; +- (NSString *)mp_text; + +@end + diff --git a/ios/Pods/Mixpanel/Mixpanel/UIView+MPHelpers.m b/ios/Pods/Mixpanel/Mixpanel/UIView+MPHelpers.m new file mode 100755 index 0000000..f2be82c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIView+MPHelpers.m @@ -0,0 +1,208 @@ +#import +#import +#import +#import "UIView+MPHelpers.h" +#import "MPLogger.h" + +// NB If you add any more fingerprint methods, increment this. +#define MP_FINGERPRINT_VERSION 1 + +@implementation UIView (MPHelpers) + +- (int)mp_fingerprintVersion { + return MP_FINGERPRINT_VERSION; +} + +- (UIImage *)mp_snapshotImage { + UIImage *image = nil; + CGSize size = self.layer.bounds.size; + UIGraphicsBeginImageContext(size); + + @try { + [self drawViewHierarchyInRect:CGRectMake(0.0f, 0.0f, size.width, size.height) afterScreenUpdates:YES]; + image = UIGraphicsGetImageFromCurrentImageContext(); + } @catch (NSException *exception) { + MPLogError(@"exception getting snapshot image %@ for view %@", exception, self); + } + + UIGraphicsEndImageContext(); + + return image; +} + +- (UIImage *)mp_snapshotForBlur { + UIImage *image = [self mp_snapshotImage]; + // hack, helps with colors when blurring + NSData *imageData = UIImageJPEGRepresentation(image, 1); // convert to jpeg + return [UIImage imageWithData:imageData]; +} + +- (NSArray *)mp_targetActions { + NSMutableArray *targetActions = [NSMutableArray array]; + if ([self isKindOfClass:[UIControl class]]) { + for (id target in [(UIControl *)(self) allTargets]) { + UIControlEvents allEvents = UIControlEventAllTouchEvents | UIControlEventAllEditingEvents; + for (NSUInteger e = 0; (allEvents >> e) > 0; e++) { + UIControlEvents event = allEvents & (0x01 << e); + if (event) { + NSArray *actions = [(UIControl *)(self) actionsForTarget:target forControlEvent:event]; + NSArray *ignoreActions = @[@"preVerify:forEvent:", @"execute:forEvent:"]; + for (NSString *action in actions) { + if ([ignoreActions indexOfObject:action] == NSNotFound) + { + [targetActions addObject:[NSString stringWithFormat:@"%lu/%@", (unsigned long)event, action]]; + } + } + } + } + } + } + return [targetActions copy]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" +// Set by a userDefinedRuntimeAttr in the MPTagNibs.rb script +- (void)setMixpanelViewId:(id)object +{ + objc_setAssociatedObject(self, @selector(mixpanelViewId), [object copy], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSString *)mp_viewId +{ + return objc_getAssociatedObject(self, @selector(mixpanelViewId)); +} +#pragma clang diagnostic pop + +- (NSString *)mp_controllerVariable +{ + NSString *result = nil; + if ([self isKindOfClass:[UIControl class]]) { + UIResponder *responder = [self nextResponder]; + while (responder && ![responder isKindOfClass:[UIViewController class]]) { + responder = [responder nextResponder]; + } + if (responder) { + uint count; + Ivar *ivars = class_copyIvarList([responder class], &count); + for (uint i = 0; i < count; i++) { + Ivar ivar = ivars[i]; + if (ivar_getTypeEncoding(ivar)[0] == '@' && object_getIvar(responder, ivar) == self) { + result = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding]; + break; + } + } + free(ivars); + } + } + return result; +} + +/* + Creates a short string which is a fingerprint of a UIButton's image property. + It does this by downsampling the image to 8x8 and then downsampling the resulting + 32bit pixel data to 8 bit. This should allow us to select images that are identical or + almost identical in appearance without having to compare the whole image. + + Returns a base64 encoded string representing an 8x8 bitmap of 8 bit rgba data + (2 bits per component). + */ +- (NSString *)mp_imageFingerprint +{ + NSString *result = nil; + UIImage *originalImage = nil; + if ([self isKindOfClass:[UIButton class]]) { + originalImage = [((UIButton *)self) imageForState:UIControlStateNormal]; + } else if ([NSStringFromClass([self.superview class]) isEqual:@"UITabBarButton"] && [self respondsToSelector:@selector(image)]) { + originalImage = (UIImage *)[self performSelector:@selector(image)]; + } + + if (originalImage) { + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + uint32_t data32[64]; + uint8_t data4[32]; + CGContextRef context = CGBitmapContextCreate(data32, 8, 8, 8, 8*4, space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Little); + CGContextSetAllowsAntialiasing(context, NO); + CGContextClearRect(context, CGRectMake(0, 0, 8, 8)); + CGContextSetInterpolationQuality(context, kCGInterpolationNone); + CGContextDrawImage(context, CGRectMake(0,0,8,8), [originalImage CGImage]); + CGColorSpaceRelease(space); + CGContextRelease(context); + for (int i = 0; i < 32; i++) { + int j = 2*i; + int k = 2*i + 1; + data4[i] = (((data32[j] & 0x80000000) >> 24) | ((data32[j] & 0x800000) >> 17) | ((data32[j] & 0x8000) >> 10) | ((data32[j] & 0x80) >> 3) | + ((data32[k] & 0x80000000) >> 28) | ((data32[k] & 0x800000) >> 21) | ((data32[k] & 0x8000) >> 14) | ((data32[k] & 0x80) >> 7)); + } + result = [[NSData dataWithBytes:data4 length:32] base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0]; + } + return result; +} + +- (NSString *)mp_text +{ + NSString *text = nil; + SEL titleSelector = @selector(title); + if ([self isKindOfClass:[UILabel class]]) { + text = ((UILabel *)self).text; + } else if ([self isKindOfClass:[UIButton class]]) { + text = [((UIButton *)self) titleForState:UIControlStateNormal]; + } else if ([self respondsToSelector:titleSelector]) { + IMP titleImp = [self methodForSelector:titleSelector]; + void *(*func)(id, SEL) = (void *(*)(id, SEL))titleImp; + id title = (__bridge id)func(self, titleSelector); + if ([title isKindOfClass:[NSString class]]) { + text = title; + } + } + return text; +} + +static NSString *mp_encryptHelper(id input) +{ + NSString *SALT = @"1l0v3c4a8s4n018cl3d93kxled3kcle3j19384jdo2dk3"; + NSMutableString *encryptedStuff = nil; + if ([input isKindOfClass:[NSString class]]) { + NSData *data = [[input stringByAppendingString:SALT] dataUsingEncoding:NSASCIIStringEncoding]; + uint8_t digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(data.bytes, (CC_LONG)data.length, digest); + encryptedStuff = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { + [encryptedStuff appendFormat:@"%02x", digest[i]]; + } + } + return encryptedStuff; +} + +#pragma mark - Aliases for compatibility +- (NSString *)mp_varA +{ + return mp_encryptHelper([self mp_viewId]); +} + +- (NSString *)mp_varB +{ + return mp_encryptHelper([self mp_controllerVariable]); +} + +- (NSString *)mp_varC +{ + return mp_encryptHelper([self mp_imageFingerprint]); +} + +- (NSArray *)mp_varSetD +{ + NSArray *targetActions = [self mp_targetActions]; + NSMutableArray *encryptedActions = [NSMutableArray array]; + for (id targetAction in targetActions) { + [encryptedActions addObject:mp_encryptHelper(targetAction)]; + } + return encryptedActions; +} + +- (NSString *)mp_varE +{ + return mp_encryptHelper([self mp_text]); +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/UIViewController+AutomaticTracks.h b/ios/Pods/Mixpanel/Mixpanel/UIViewController+AutomaticTracks.h new file mode 100644 index 0000000..d0bcf8c --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIViewController+AutomaticTracks.h @@ -0,0 +1,19 @@ +// +// UIViewController+AutomaticTracks.h +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIViewController (AutomaticTracks) + +- (void)mp_viewDidAppear:(BOOL)animated; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Pods/Mixpanel/Mixpanel/UIViewController+AutomaticTracks.m b/ios/Pods/Mixpanel/Mixpanel/UIViewController+AutomaticTracks.m new file mode 100644 index 0000000..6f400aa --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/UIViewController+AutomaticTracks.m @@ -0,0 +1,40 @@ +// +// UIViewController+AutomaticTracks.m +// HelloMixpanel +// +// Created by Sam Green on 2/23/16. +// Copyright © 2016 Mixpanel. All rights reserved. +// + +#import "UIViewController+AutomaticTracks.h" +#import "Mixpanel+AutomaticTracks.h" +#import "AutomaticTracksConstants.h" + +@implementation UIViewController (AutomaticTracks) + +- (void)mp_viewDidAppear:(BOOL)animated { + if ([self shouldTrackClass:self.class]) { + [[Mixpanel sharedAutomatedInstance] track:kAutomaticTrackName]; + } + [self mp_viewDidAppear:animated]; +} + +- (BOOL)shouldTrackClass:(Class)aClass { + static NSSet *blacklistedClasses = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSArray *blacklistedClassNames = @[ @"UICompatibilityInputViewController", + @"UIKeyboardCandidateGridCollectionViewController", + @"UIInputWindowController", + @"UICompatibilityInputViewController" ]; + NSMutableSet *transformedClasses = [NSMutableSet setWithCapacity:blacklistedClassNames.count]; + for (NSString *className in blacklistedClassNames) { + [transformedClasses addObject:NSClassFromString(className)]; + } + blacklistedClasses = [transformedClasses copy]; + }); + + return ![blacklistedClasses containsObject:aClass]; +} + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/_MPTweakBindObserver.h b/ios/Pods/Mixpanel/Mixpanel/_MPTweakBindObserver.h new file mode 100644 index 0000000..a9afc10 --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/_MPTweakBindObserver.h @@ -0,0 +1,40 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@class MPTweak; + +/** + @abstract Block to call when an update is observed. + @param object The object that the observer is attached to. + */ +typedef void (^_MPTweakBindObserverBlock)(id object); + +/** + @abstract Observes a tweak to issue bind updates. + @discussion This is an implementation detail of {@ref MPTweakBind}. + */ +@interface _MPTweakBindObserver : NSObject + +/** + @abstract Designated initializer. + @param tweak The tweak to observe. + @param block The block to call on change. + @return A new bind observer. +*/ +- (instancetype)initWithTweak:(MPTweak *)tweak block:(_MPTweakBindObserverBlock)block; + +/** + @abstract Attaches to an object and deallocates with it. + @discussion Useful to create a limited lifetime for the observer. + */ +- (void)attachToObject:(id)object; + +@end diff --git a/ios/Pods/Mixpanel/Mixpanel/_MPTweakBindObserver.m b/ios/Pods/Mixpanel/Mixpanel/_MPTweakBindObserver.m new file mode 100644 index 0000000..a57773b --- /dev/null +++ b/ios/Pods/Mixpanel/Mixpanel/_MPTweakBindObserver.m @@ -0,0 +1,57 @@ +/** + Copyright (c) 2014-present, Facebook, Inc. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import "_MPTweakBindObserver.h" +#import "MPTweak.h" + +@interface _MPTweakBindObserver () + +@end + +@implementation _MPTweakBindObserver { + MPTweak *_tweak; + _MPTweakBindObserverBlock _block; + __weak id _object; +} + +- (instancetype)initWithTweak:(MPTweak *)tweak block:(_MPTweakBindObserverBlock)block +{ + if ((self = [super init])) { + NSAssert(tweak != nil, @"tweak is required"); + NSAssert(block != NULL, @"block is required"); + + _tweak = tweak; + _block = block; + + [tweak addObserver:self]; + } + + return self; +} + +- (void)tweakDidChange:(MPTweak *)tweak +{ + __attribute__((objc_precise_lifetime)) id strongObject = _object; + + if (strongObject != nil) { + _block(strongObject); + } +} + +- (void)attachToObject:(id)object +{ + NSAssert(_object == nil, @"can only attach to an object once"); + NSAssert(object != nil, @"object is required"); + + _object = object; + objc_setAssociatedObject(object, (__bridge void *)self, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end diff --git a/ios/Pods/Mixpanel/README.md b/ios/Pods/Mixpanel/README.md new file mode 100644 index 0000000..31bdb17 --- /dev/null +++ b/ios/Pods/Mixpanel/README.md @@ -0,0 +1,115 @@ +[![Build Status](https://travis-ci.org/mixpanel/mixpanel-iphone.svg?branch=yolo-travis-ci)](https://travis-ci.org/mixpanel/mixpanel-iphone) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/mixpanel/mixpanel-iphone.svg)](http://isitmaintained.com/project/mixpanel/mixpanel-iphone "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/mixpanel/mixpanel-iphone.svg)](http://isitmaintained.com/project/mixpanel/mixpanel-iphone "Percentage of issues still open") +[![CocoaPods Version](http://img.shields.io/cocoapods/v/Mixpanel.svg?style=flat)](https://mixpanel.com) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Apache License](http://img.shields.io/cocoapods/l/Mixpanel.svg?style=flat)](https://mixpanel.com) + +# Table of Contents + + + +- [Introduction](#introduction) +- [Installation](#installation) + - [CocoaPods](#cocoapods) + - [Carthage](#carthage) + - [Manual Installation](#manual-installation) +- [Integrate](#integrate) +- [Start tracking](#start-tracking) + + + + +# Introduction + +The Mixpanel library for iOS is an open source project, and we'd love to see your contributions! We'd also love for you to come and work with us! Check out https://mixpanel.com/jobs/#openings for details. + +If you are using Swift, we recommend our **[Swift Library](https://github.com/mixpanel/mixpanel-swift)**. + + +# Installation + + +## CocoaPods + +Mixpanel supports `CocoaPods` for easy installation. +To Install, see our **[full documentation »](https://mixpanel.com/help/reference/ios)** + +#### iOS, tvOS, watchOS, macOS: +`pod 'Mixpanel'` +#### App Extension: +`pod 'Mixpanel-AppExtension'` + + +## Carthage + +Mixpanel also supports `Carthage` to package your dependencies as a framework. +Check out the **[Carthage docs »](https://github.com/Carthage/Carthage)** for more info. + +To integrate Mixpanel into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "mixpanel/mixpanel-iphone" +``` + +Run `carthage update` to build the framework and drag the built `Mixpanel.framework` into your Xcode project. + + +## Manual Installation + +To help users stay up to date with the latests version of our iOS SDK, we always recommend integrating our SDK via CocoaPods, which simplifies version updates and dependency management. However, there are cases where users can't use CocoaPods. Not to worry, just follow these manual installation steps and you'll be all set. + +### Step 1: Add as a Submodule + +Add Mixpanel as a submodule to your local git repo like so: + +``` +git submodule add git@github.com:mixpanel/mixpanel-iphone.git +``` + +Now the Mixpanel project and its files should be in your project folder! + +### Step 2: Add the SDK to your app! + +Add the "Mixpanel" folder from the "mixpanel-iphone" to your Xcode project's folder: + +![alt text](http://images.mxpnl.com/blog/2014-09-24%2000:56:07.905215-SprityBird_and_mixpanel-iphone.png) + +And drag and drop the Mixpanel folder into your Xcode Project Workspace: + +![alt text](http://images.mxpnl.com/blog/2014-09-24%2001:08:51.474250-AppDelegate_m_and_SprityBird.png) + +### Step 3: Import All dependencies + +Add all dependencies of the Mixpanel SDK to your app. The full list of necessary frameworks and libraries on lines 16-17 in the "Mixpanel.podspec" file in the "mixpanel-iphone" directory: + +![alt text](http://images.mxpnl.com/blog/2014-09-24%2001:32:27.445697-1__vim_and_spritybird_and_Mixpanel_-_Agent_and_spritybird.png) + + +# Integrate + +Import "Mixpanel.h" into AppDelegate.m, and initialize Mixpanel within `application:didFinishLaunchingWithOptions:` + +![alt text](http://images.mxpnl.com/blog/2014-09-24%2001:19:19.598858-AppDelegate_m.png) + +```objective-c +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [Mixpanel sharedInstanceWithToken:MIXPANEL_TOKEN]; +} +``` + +You initialize your Mixpanel instance with the token provided to you on mixpanel.com. + + +# Start tracking + +Tracking your first event is as easy as adding `track:` or `track:properties:` anywhere after initializing Mixpanel. + +```objective-c +[[Mixpanel sharedInstance] track:@"Event name"]; +[[Mixpanel sharedInstance] track:@"Event name" properties:@{@"Prop name": @"Prop value"}]; +``` + +You're done! You've successfully integrated the Mixpanel SDK into your app. To stay up to speed on important SDK releases and updates watch our iPhone repository on [Github](https://github.com/mixpanel/mixpanel-iphone). + +Have any questions? Reach out to [support@mixpanel.com](mailto:support@mixpanel.com) to speak to someone smart, quickly. diff --git a/ios/Pods/Pods.xcodeproj/project.pbxproj b/ios/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..df08e4f --- /dev/null +++ b/ios/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1207 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 019DA9AFF7AD762DB36C973136CFF95F /* MPObjectSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 70444506DB97ECAE02284C5CCCCB127F /* MPObjectSerializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 025AA447227A5189FA2658978804F1BF /* MPTypeDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = BD912927F984990CF05ABAC888D505D8 /* MPTypeDescription.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0323DA0FBAAE7344AFD5FFFDD3B959E6 /* MPDesignerEventBindingResponseMesssage.m in Sources */ = {isa = PBXBuildFile; fileRef = 72542460AC948DA8E4DCAC875F1BB596 /* MPDesignerEventBindingResponseMesssage.m */; }; + 039D17EA329CD07E9CA00426C22C11D1 /* MPResources.m in Sources */ = {isa = PBXBuildFile; fileRef = C8F2BA672D9F764823F1D2D1FD4AB350 /* MPResources.m */; }; + 04CACD9CC6A5539A6DA398DBEF799C94 /* UIColor+MPColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 00BD4E4891029D1E20ACD52DC0AC66E6 /* UIColor+MPColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0515EF2970C61A6A740FBA30A3A032C0 /* MPCGAffineTransformToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D4131908AE7A20F9333418F938CB1C /* MPCGAffineTransformToNSDictionaryValueTransformer.m */; }; + 0639B11DA968E39D41578ED8227DD82B /* UIImage+MPImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = FD44723997BA78DD92FB2A9E8041E5D1 /* UIImage+MPImageEffects.m */; }; + 07B28FC8C927D50B3CEF98649CFAE97E /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 845F8B0167B64D32EC3C83E2E34407FF /* CoreTelephony.framework */; }; + 09EA92DCCB5E280292B8AB2F143D655D /* MPTweakInline.h in Headers */ = {isa = PBXBuildFile; fileRef = 8A4B0C0EE3AAF1C9C7D6EB2D37D5A90E /* MPTweakInline.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0C38F12D9D89216965A19E14DAB5B06B /* MPCGSizeToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 90627B78BCBF044B18FD2C44F93E327C /* MPCGSizeToNSDictionaryValueTransformer.m */; }; + 0D38AF6918CAB15CF5C2FE6F9E8EE0A8 /* MPEnumDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B065F8367154F6B056EA7251BD65D1B /* MPEnumDescription.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 102EFF1FE40C7859E56C15830B87B00F /* MPUIEdgeInsetsToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D5CD044CF8DF9B720E9AD425F3514EDE /* MPUIEdgeInsetsToNSDictionaryValueTransformer.m */; }; + 10B61E82FA540273C243441633D032A7 /* MPWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F612930E064B62CA915CD1A190E5DC87 /* MPWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1AC1EBF06E787D287687C7872D9CF2C9 /* MPUIControlBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = C0924CBF800FD5A4C50440D1FED88E16 /* MPUIControlBinding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 1AFB6959F3D81DB2F0B7BB93E2F808DD /* MPUIControlBinding.m in Sources */ = {isa = PBXBuildFile; fileRef = D2A23A77C5B0DE316B102434E5D02499 /* MPUIControlBinding.m */; }; + 1B8618631BC84715AAAADF61BF5E376D /* MPAbstractABTestDesignerMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DE7D352124FA106364ABA0930F46D9A /* MPAbstractABTestDesignerMessage.m */; }; + 1D520D15E7AB72946EED04C6AB498080 /* UIImage+MPAverageColor.m in Sources */ = {isa = PBXBuildFile; fileRef = D1A7BC937C51BDB7D5E26C58128F255E /* UIImage+MPAverageColor.m */; }; + 1EE5BE2F1FC19AFBAD5C6EC787348BA8 /* MPEventBinding.m in Sources */ = {isa = PBXBuildFile; fileRef = E57E9E509F0CAD6C1B003EE3E26264FF /* MPEventBinding.m */; }; + 228E840F38959CA09A83D012B15938AB /* MPUIColorToNSStringValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FA16B40234415D66ADF8105DDEB6A8A /* MPUIColorToNSStringValueTransformer.m */; }; + 22902EAD35AA212A60CFF896511665E0 /* MPUITableViewBinding.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A422E780D2A48005F4F238B2F4314A7 /* MPUITableViewBinding.m */; }; + 26A28F3B7C721A57574BEB3F7FF136C2 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A3F2B8269D39DCD7C64A7FBCA009C1D /* CoreGraphics.framework */; }; + 274E2E3E3BDF4B87F6D10127DDC9FE11 /* MPABTestDesignerTweakResponseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0098C6FBE49E8C95D1450F10D1C141FB /* MPABTestDesignerTweakResponseMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 287D3BC697378E3A42674DEFA7F23DDF /* MPTweakInlineInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 48539D92FDB1B300D114BD020BE6ACB7 /* MPTweakInlineInternal.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2CEF794744F0AD55EABD90922F7B2B6F /* MPObjectIdentifierProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D9F44C91360B97BDA3CCECD4797C689 /* MPObjectIdentifierProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2DD20F27E9AEAED87E08C576B0920DC6 /* MPEventBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = 063B2C733B1887F1BDC833AD5F17FD4F /* MPEventBinding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2DE1661CF0B17C71A03B259CE00FF3DC /* MPTakeoverNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = CA6D7225C521B37136B62E78C1E15F35 /* MPTakeoverNotification.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 2EE13CF048A199943D2DDAF64AE17BBD /* MPAbstractABTestDesignerMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 6F9372302B10412D06A0B5E3A38F4C10 /* MPAbstractABTestDesignerMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 300609334B3680E91DC1636B4EF18079 /* MPNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = ED63EE3EC61744E7339A3AEF636C5FD3 /* MPNotification.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 30A722D0808CA7EEEF59DD1B890AC85C /* Pods-T7Chicken-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FC21782E539B7AB4E8558918F2D53191 /* Pods-T7Chicken-dummy.m */; }; + 32A4ED58EA2C3935C1E38C6963D7A652 /* MPSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = 2547602FD9CCDE9FF3E18AFA6E229314 /* MPSwizzler.m */; }; + 335D56D6BAC467A9F70ADB024C761779 /* MPABTestDesignerChangeRequestMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 655076B11228FF69369D8BA0CB27BE52 /* MPABTestDesignerChangeRequestMessage.m */; }; + 3659CBA339A215E680F56A79451DC9B3 /* MPNotificationButton.h in Headers */ = {isa = PBXBuildFile; fileRef = B694C8BE1B01C69E7C06E4E4E7649993 /* MPNotificationButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 372ED8281BE2261F2DED2599B2C82684 /* MPObjectSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F8436DD8EEBE2AFC1FA739C6FBAB2D4 /* MPObjectSerializer.m */; }; + 37772BA5872DE168A3AB6636AEF22EBA /* MPABTestDesignerDeviceInfoResponseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E8C8C258F6CA922437378866BD5C43 /* MPABTestDesignerDeviceInfoResponseMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 379F7A271B9E80D3F10C718972C087A5 /* MPApplicationStateSerializer.h in Headers */ = {isa = PBXBuildFile; fileRef = 773E3EE5400D07856AE311CC25A76090 /* MPApplicationStateSerializer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3E9A16E4E391F1A35EBF0FB8BBF7E1A6 /* _MPTweakBindObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 7566914A06505062CAAC356EE828C649 /* _MPTweakBindObserver.m */; }; + 3F4C2E18C33CB86B9E761E014E74C3A7 /* MPNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 38A6F66AE93021C25C69FE4C266DAC81 /* MPNotification.m */; }; + 415E8A80F508F24A251B957E00FBB27D /* MPLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 784EF79DC3D6331442E885CE4BE2FA7A /* MPLogger.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4335B34A8772E7B137D6077D45C76210 /* UIImage+MPImageEffects.h in Headers */ = {isa = PBXBuildFile; fileRef = FFE56D520884524B65571806182F7007 /* UIImage+MPImageEffects.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 453EE0A47359BCC2B10F3C07139158CD /* MPABTestDesignerSnapshotResponseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = A638B33BB9B0477EDDEAB2D1379B8396 /* MPABTestDesignerSnapshotResponseMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 473004B25142F65D28BD66BF4A937CF8 /* MPSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = 9CFEDE9D7C47E4416C9E83AEEE504006 /* MPSwizzle.m */; }; + 47980FF6FA059D0BE1A2A888EC4A0CD0 /* MPABTestDesignerDeviceInfoRequestMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 603E259486AD174C1DFB2DC78E8F1C1B /* MPABTestDesignerDeviceInfoRequestMessage.m */; }; + 4A0E8099B4514122C292FCE7330F05ED /* MPABTestDesignerMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F211693547D48FBBD99671F9A76F7ED /* MPABTestDesignerMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4ABEA142F069D14B83C2951FC9C59CE3 /* MPClassDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A0DB21BDF7E9B679AF3BA1E9BBEEECB /* MPClassDescription.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4AD15A531EFB023ABA992A51209CC519 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71186B3CA0D00FD73E8FD4C4FA0704F8 /* Foundation.framework */; }; + 4C2C9328B91ED677B59340D4A8576A7A /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E3C47467D4FAB4C0C812AFBE2AE99824 /* Accelerate.framework */; }; + 4E14CAF48DFBC64805467C3B1CBFA8E8 /* MPABTestDesignerDisconnectMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = AAEDCAEC8AFE7834F8D5464E160EB0C7 /* MPABTestDesignerDisconnectMessage.m */; }; + 4ED9DE269A6355AAD66C0E59C88E19EF /* MPNetwork.h in Headers */ = {isa = PBXBuildFile; fileRef = 05C9EA836E92AFB59436EC83FBF0402E /* MPNetwork.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4F58DB168DF83438C7066B42A8CD66C6 /* Mixpanel-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C5C9B441827E4EFDB3650259021E644 /* Mixpanel-dummy.m */; }; + 50B45EEC148650DB4A0DCFFD17E1FC85 /* MPTypeDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 9895AFE3F53886DB084616C99AF0CC3F /* MPTypeDescription.m */; }; + 51ABAD5F6B5D8461A5255011A7B77506 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71186B3CA0D00FD73E8FD4C4FA0704F8 /* Foundation.framework */; }; + 5362EEAABF289A862293F4AD5741901B /* UIViewController+AutomaticTracks.h in Headers */ = {isa = PBXBuildFile; fileRef = E94F5B43228D25F36A6514EF0F8649F0 /* UIViewController+AutomaticTracks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5457A607B063A5DB74CE0025FAD2CAB3 /* MPSequenceGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = D8CC28A2106D1E76AEEFCE47C3DDFD1E /* MPSequenceGenerator.m */; }; + 5574079F1EE65CDE2F3A0AC652CE407B /* MPObjectSerializerConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = B53AE7B40D02F7380EA6A04C1923AC70 /* MPObjectSerializerConfig.m */; }; + 575B280E1083912CCEAC601D69321B1F /* MPFoundation.h in Headers */ = {isa = PBXBuildFile; fileRef = 55649A64FA930E5CFD3AD6693E947D84 /* MPFoundation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 59298EED2003397D652D31BD4D3F937E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B719C21022B032FDDB1AB473149C3DC4 /* UIKit.framework */; }; + 5C60C4BF0674C817853B98C0403CAB9A /* MPVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = FBF2D10E12078A447678BAB98302318D /* MPVariant.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5C8BBF47FE3D699A1953C414EFEB53B7 /* UIView+MPHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = FED696F5A118A83AD0A304EA058D5F1C /* UIView+MPHelpers.m */; }; + 5D38DD68E197A4C89DEB12A4605D7F35 /* UIApplication+AutomaticTracks.h in Headers */ = {isa = PBXBuildFile; fileRef = DEBB170C91EE81BE8E9486A6750B93BC /* UIApplication+AutomaticTracks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5DAAAF3584CFD045CCA82FF1AFE85A15 /* MixpanelPeople.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D51920BD13637A05EACEC2552823452 /* MixpanelPeople.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5E45229D0D16AF9DCBA5BD2E9C83D46E /* MPABTestDesignerChangeResponseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 27221A1E04FDD51B5FA3F3E295B1FBA1 /* MPABTestDesignerChangeResponseMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 607DC86D27849C98095C91B240BA9068 /* MPABTestDesignerSnapshotRequestMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A756B9412EF39145CC604B342E7A072 /* MPABTestDesignerSnapshotRequestMessage.m */; }; + 61C1CD4E850C9CF008209D3AAD24C61D /* UIApplication+AutomaticTracks.m in Sources */ = {isa = PBXBuildFile; fileRef = 88770AB53F75BA220C5AF6AD05D09609 /* UIApplication+AutomaticTracks.m */; }; + 6226A885414058D0422A4108F54E927F /* MPNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = 080DC77574F362D808C4BC1295315E7B /* MPNetwork.m */; }; + 6328D3E9AD2CCF920DE8F8FADF54C919 /* Pods-T7ChickenTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 44E01A6EE48A0F19B91800A4FEAB622A /* Pods-T7ChickenTests-dummy.m */; }; + 65BEC4F06937A00C182C85DFD0C4F6CD /* MPTweakInline.m in Sources */ = {isa = PBXBuildFile; fileRef = E229782F104BF790213642B67F1D4298 /* MPTweakInline.m */; }; + 66DB9AC523548D7012CDF4848C726A54 /* MPNetworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = D31B5FB59EDF59BB514907DB57D6E8B4 /* MPNetworkPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 68A01D2DE9A849C525D932F23EC1AAF4 /* MPObjectSerializerConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 71956E54102D9EB87A438106001B5C67 /* MPObjectSerializerConfig.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 69814D03B24DFFB3DC019DB8C0785F69 /* MPNSAttributedStringToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = AF06B4CD829B7CD1531C9CCD6D858EC6 /* MPNSAttributedStringToNSDictionaryValueTransformer.m */; }; + 6AD15CC328CA1ABC324D510DA805016B /* MPTweak.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C241251EF3735A3DD315386B285C6B1 /* MPTweak.m */; }; + 6E7014E449CFC1C44F2C963E46467373 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BC128BB06115DCBBF25335548036364 /* QuartzCore.framework */; }; + 6E795DCE13FE0DADDD03534CBD7B7D66 /* MPPropertyDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 241FD1D28C18571694D4C55B11F02C85 /* MPPropertyDescription.m */; }; + 717789C37AE40935A2F2340070236318 /* UIView+MPHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BEA3E4E9A87A6A7D987F182380F6A7C /* UIView+MPHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 73C90F7FCECAFA11C5C5FBB6308D94FE /* MPNSNumberToCGFloatValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9503D8480738151CD6B30C2EC9F6298B /* MPNSNumberToCGFloatValueTransformer.m */; }; + 74ED21F48A5B4802FFABE793EBA98AF9 /* MPABTestDesignerTweakResponseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = F55C478EE6577CB6E8844E9010B8DE60 /* MPABTestDesignerTweakResponseMessage.m */; }; + 7735C5E428DE99B271B2F8B6773B94E7 /* NSNotificationCenter+AutomaticTracks.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B080F3D4FBEAB9071026C65F014E45C /* NSNotificationCenter+AutomaticTracks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 77DD0357ACB027E7388F1BBB9626A1EF /* MPObjectSelector.h in Headers */ = {isa = PBXBuildFile; fileRef = D63D572109015E7A9771C0BF61E41834 /* MPObjectSelector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 781E75D0443B26CCC9CFF2B1F43E99C3 /* AutomaticEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 495336079BC64FCF52BE6639F85DCA09 /* AutomaticEvents.m */; }; + 782B20406FEB0C04DF5CA0BD69765471 /* MPABTestDesignerDeviceInfoRequestMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D73D2EB3C330723A5465175C8CFEF1D /* MPABTestDesignerDeviceInfoRequestMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7D4B7992CDE6D56FC20446DADD810E92 /* MPApplicationStateSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = 89CE7AE30E368B45D023E6A8339B13DD /* MPApplicationStateSerializer.m */; }; + 7E44A7D0CAF7DDF78096A8251D1440D0 /* MPABTestDesignerChangeRequestMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 6747D7636E5B2AE038E2D8CF32E91CA8 /* MPABTestDesignerChangeRequestMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7EA96ACDB0CF35334A7FA2AAB44ECD42 /* MPWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C75C97AA13697777772CAB4492A7255 /* MPWebSocket.m */; }; + 8134B5B4507867151F4DF03E46675752 /* MPUITableViewBinding.h in Headers */ = {isa = PBXBuildFile; fileRef = BD1951D684625282975D3F4437B3B0E4 /* MPUITableViewBinding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 826BE6E699EA1752951FFCDADD42F575 /* NSInvocation+MPHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BC312EB8AE9323065CAB166BA652380 /* NSInvocation+MPHelpers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 841D5C185810F513B4CFABCEBEF19E94 /* MPSequenceGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = E291A22751011402F5C8CA49E92B39A8 /* MPSequenceGenerator.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 868F5819ED156770487F5EF1D393A02F /* MPABTestDesignerSnapshotRequestMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F99C9FFC8BF29FE61C2CE89826675DB /* MPABTestDesignerSnapshotRequestMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8750DCA1D1830C4D0D124AAC01215FD7 /* MPPassThroughValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = A974035D9F638A6C273C4F9B4CA905E6 /* MPPassThroughValueTransformer.m */; }; + 87D1D1677E78E1DDB077A356D93928A4 /* AutomaticEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C825D678914AE352C431669F592E918 /* AutomaticEvents.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8C338B9AF1EE6A2A408179A8F26272F7 /* MPDesignerTrackMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C91230158E956866DAD8522E3D166D36 /* MPDesignerTrackMessage.m */; }; + 8D7FA0C6B2A39556F7AF4E8AF9601855 /* MPTweakStore.m in Sources */ = {isa = PBXBuildFile; fileRef = BAABAFBD61155787287A6E3F86C660E2 /* MPTweakStore.m */; }; + 8E7827100ED66EA4595A78FE8C05F1CF /* MPUIImageToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = ECBE74E38586159316E7313D4EBC4735 /* MPUIImageToNSDictionaryValueTransformer.m */; }; + 8FAD1B42EBB691CDA835DC724C2882B6 /* MPABTestDesignerDisconnectMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = FEDE1C79E251E590A31CA9146559CFA8 /* MPABTestDesignerDisconnectMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9002AEFAE8E5B351E0FD4347B904C665 /* UIColor+MPColor.m in Sources */ = {isa = PBXBuildFile; fileRef = A073ABE9E4CD983137859E96A2B3702F /* UIColor+MPColor.m */; }; + 919A90D376CC7BD9DC6D7B4A14AB1F90 /* MPCGRectToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 0290E0A6569BD0FBED77C2F2EE92A9BC /* MPCGRectToNSDictionaryValueTransformer.m */; }; + 938E8C3E347896268CFB4BCA6C1BB31E /* MPABTestDesignerChangeResponseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3762D2701AC35BCEABEEFFDC7E360EB6 /* MPABTestDesignerChangeResponseMessage.m */; }; + 94C82BCE756B481DFC7C132AD0FC0610 /* AutomaticTracksConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = E77980A3EA7F735EFBE73610C8E2F6F8 /* AutomaticTracksConstants.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 95C596BF88C2858A194068429FA42BFB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71186B3CA0D00FD73E8FD4C4FA0704F8 /* Foundation.framework */; }; + 95D8A5900E58012F0088E5DF70FFD9EA /* Mixpanel.m in Sources */ = {isa = PBXBuildFile; fileRef = E86BDFA03FE2116AB7B8320D5BC388B2 /* Mixpanel.m */; }; + 971FCC4664D3CCD1475934290AB5C03E /* MPUIFontToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 0300B9B7936A50EB3E0BA7D6C75451ED /* MPUIFontToNSDictionaryValueTransformer.m */; }; + 975575EE716FD4E7CC2EB3D66B768528 /* MPABTestDesignerClearRequestMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 06FB14F9B197EDC27B80034715EB9864 /* MPABTestDesignerClearRequestMessage.m */; }; + 996B0FC2FEE5D45591E3FD419ED52408 /* MPNotificationButton.m in Sources */ = {isa = PBXBuildFile; fileRef = E5E9C747F79968077370666202A888C5 /* MPNotificationButton.m */; }; + 9A44E71E987748D9F59F81C6ED9400DA /* MixpanelPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 289BAF04AE2F97684046F85815C83C00 /* MixpanelPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9B0E237649862B9F8EE50AD624204278 /* MPMiniNotification.h in Headers */ = {isa = PBXBuildFile; fileRef = FD0BFDA381D45CAE312DD81010E3DBE7 /* MPMiniNotification.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9BA1C3C5912D4EC5C84E501E842E211C /* MPObjectSerializerContext.h in Headers */ = {isa = PBXBuildFile; fileRef = D7BC9F0586F0DB0659D1D0A33AC87B72 /* MPObjectSerializerContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9E1A820AA82A013DB6FBB9346B86B97A /* Mixpanel+AutomaticTracks.h in Headers */ = {isa = PBXBuildFile; fileRef = 683ABFFCDAB185558CADD80FE1239056 /* Mixpanel+AutomaticTracks.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A12281415A955A0417BE173564D080BE /* MPCATransform3DToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = A58CC11EAF63E6D048C0847FC6B0BFFA /* MPCATransform3DToNSDictionaryValueTransformer.m */; }; + A2B1013F5A4DA5A1531E04F1684215A8 /* MPTweakStore.h in Headers */ = {isa = PBXBuildFile; fileRef = B20D7FED2792635CABB46680D6741F76 /* MPTweakStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A53D5382C3C7009196E43AA6E1514A94 /* MPTweak.h in Headers */ = {isa = PBXBuildFile; fileRef = D4F8734CA45A859AB891CC61B66E61EA /* MPTweak.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A624600D79F23645D01F9E1A0FE77CD2 /* MPDesignerSessionCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AD154AFE252EBDB35402851C1D92213 /* MPDesignerSessionCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A6CE9F8280BEB215D3A44F6440D0685C /* _MPTweakBindObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = C670C5732EC303843E558778014D3600 /* _MPTweakBindObserver.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A823F29982D34744B604B05AF2EC7C7A /* UIImage+MPAverageColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 52BE2F2678A867E81ADC0A16A3AB5874 /* UIImage+MPAverageColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A8AFD6DBD6A48A7FAF10EF9485942937 /* MPABTestDesignerSnapshotResponseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D777206689D272ABB6CB8797FEB2C740 /* MPABTestDesignerSnapshotResponseMessage.m */; }; + A9CE54CF3B45B21D69D607B4CE14C274 /* MPValueTransformers.h in Headers */ = {isa = PBXBuildFile; fileRef = 27FA1D8D74ADF5333CB52B04B782FAA6 /* MPValueTransformers.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B07C9CC6C6604D24805EF6E1D6ED4121 /* MPNotificationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F878A5BEF282075BCFC2102FF1B96D1 /* MPNotificationViewController.m */; }; + B4EF9487F2894147C046611645A478F3 /* MPClassDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = B3A2EC70B9D72682673923E594428352 /* MPClassDescription.m */; }; + B580DE0C97CB869D5FDB979C03B363EF /* MPABTestDesignerConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = E848DD65E9223346BBE17BF874237E36 /* MPABTestDesignerConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B6A4F39F19E90CDD3907C6C0FE744887 /* NSInvocation+MPHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 95B96F308485E749A29BE0EB435DF0C7 /* NSInvocation+MPHelpers.m */; }; + B7FE9F2196535479F4347EB90241B86F /* MPBOOLToNSNumberValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 2555FC294A173BDDB498EFDAC13E8CB2 /* MPBOOLToNSNumberValueTransformer.m */; }; + B9BFF3B4ED344EFDFD3366BBA9A0A7A0 /* MPABTestDesignerConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 00023AE52FA8214E0BC2AC2DC386D6D4 /* MPABTestDesignerConnection.m */; }; + BD12E1B3FD591F1CD68513C65A4ECA1B /* MPABTestDesignerTweakRequestMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F354C404F6094664BB64E6706D5AF8F /* MPABTestDesignerTweakRequestMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BEA6A3301CC2DE5AC955AB4008C7E18F /* MPNotificationViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 2657F835647A8CF76E80DC55354EAE48 /* MPNotificationViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C486651766A41FB730EB2576C1A158FD /* MPResources.h in Headers */ = {isa = PBXBuildFile; fileRef = 954BB41EB65E3C98BE314CB6CE950D72 /* MPResources.h */; settings = {ATTRIBUTES = (Public, ); }; }; + C4B4B6BEAFA136B0E38963824A96728C /* MixpanelPeoplePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A1DD790C19B315A12311414C50557E79 /* MixpanelPeoplePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; + C9043E973E99D45F1C61088E377A0631 /* MPTakeoverNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1DABD0CCB460BD2836CA89EDC3626F /* MPTakeoverNotification.m */; }; + CA58F26DE44409353126E957F22CACEF /* MixpanelExceptionHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = B4F04D8E29CFF16E5FD2862D7A0DBD53 /* MixpanelExceptionHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CD554242CB48F0284E67F5CF954A22F8 /* MPABTestDesignerClearResponseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = D1AD8C944FB55C561E634202F9248D2D /* MPABTestDesignerClearResponseMessage.m */; }; + CE47032AEB24B03F7861F7C3CA5F2EA7 /* MPDesignerEventBindingRequestMesssage.m in Sources */ = {isa = PBXBuildFile; fileRef = B239D2F597F82A0A72EBD2CEFB828E43 /* MPDesignerEventBindingRequestMesssage.m */; }; + D3EBF2524DDEE49B6BED770B1987CA16 /* NSNotificationCenter+AutomaticTracks.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AA4D3364D2E311DF0E1F0ED9663E5B1 /* NSNotificationCenter+AutomaticTracks.m */; }; + D4452A116DEE2D75EF4E8941DA0C24C1 /* MPCGColorRefToNSStringValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 512D4BCAED57A02F1B8B193A793FF8E9 /* MPCGColorRefToNSStringValueTransformer.m */; }; + D581B576DD9A007A620B6BA890C3EF5B /* MPABTestDesignerDeviceInfoResponseMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D142DCC88F666E172F2A1F3A3B9FB0A /* MPABTestDesignerDeviceInfoResponseMessage.m */; }; + D8A362A92CCFF852DCF8C90A52CB5344 /* MPABTestDesignerClearRequestMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 35074F112EA51F005340798731DF1B08 /* MPABTestDesignerClearRequestMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D95A2B984F9BD7006822A4AE94715C7D /* MPSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = C7D838CBAC290780FB26966C8F3D5EAA /* MPSwizzle.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DF2E08AE349C49A8803B29448B1950BD /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 919005EA6727D6B78BD6D56646162573 /* SystemConfiguration.framework */; }; + E2CB6EC6E265778C2D1FC500CA9A1137 /* MPPropertyDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BEFC7939F87E4608FC9EFD214E8E52F /* MPPropertyDescription.h */; settings = {ATTRIBUTES = (Public, ); }; }; + E54F09E224971D710E7FCD0590750BBC /* MixpanelExceptionHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = C92A1DE2A35144C207368567F710908D /* MixpanelExceptionHandler.m */; }; + E6A85C69C316D8D063FDC1E1A777A9A1 /* Mixpanel+AutomaticTracks.m in Sources */ = {isa = PBXBuildFile; fileRef = 14E2F99AA58929BCC4E1214E1536F053 /* Mixpanel+AutomaticTracks.m */; }; + EACD728FD7A71D3F1FC92ADA091DE0DB /* MPMiniNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 633E2AEB882FE7FD0AF6D9E1E10D6277 /* MPMiniNotification.m */; }; + EC5E0557D5A71E2342869AF26FC6515E /* MPObjectIdentityProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = C37E375FBF42423790C267884E919475 /* MPObjectIdentityProvider.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC615574B939F8521E203D8A74E71CAE /* MPDesignerEventBindingMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 850AA3A3758B223E2A368B0C4150E6D4 /* MPDesignerEventBindingMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC9FAA65E5931EDA4DBD452612B60E78 /* UIViewController+AutomaticTracks.m in Sources */ = {isa = PBXBuildFile; fileRef = 732A2D1E7852F17E649284435CACCD0B /* UIViewController+AutomaticTracks.m */; }; + ECB8597C5FF13C74F1E911312DD9B061 /* Mixpanel.h in Headers */ = {isa = PBXBuildFile; fileRef = 40E7AC14D29B6981B4F1E4F94C9E678C /* Mixpanel.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EE4AC3A7F7F43C328169A10179F838B2 /* MPEnumDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = 12636F6ECA86EAA8B6A454480AE89A34 /* MPEnumDescription.m */; }; + EED1C8967352BC1E48ECFED69EA2AEFB /* MPVariant.m in Sources */ = {isa = PBXBuildFile; fileRef = E4CAA50D3A6C55998865A4C20040411F /* MPVariant.m */; }; + EF0FB38F4552636AF7623B4565B8C0E2 /* MPABTestDesignerClearResponseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 2175E18D8C406FFAD6002A371489B9DD /* MPABTestDesignerClearResponseMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EF17E92F1DA32380830FBD4A7259505F /* MPCGPointToNSDictionaryValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 825631778F26F9900D0ECE9F8E48FE8D /* MPCGPointToNSDictionaryValueTransformer.m */; }; + EF407E62499417FDA55484AEF41E1114 /* MixpanelPeople.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC0A91C458F7FF2C2734320DD3DECC3 /* MixpanelPeople.m */; }; + F26144BA5BDD712D6BFAB84F28D33B38 /* MPObjectIdentityProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 52F014A0CF4CC9A7CF29A701614CD06E /* MPObjectIdentityProvider.m */; }; + F348F490291E3FC8EBBBBE75F402A502 /* MPABTestDesignerTweakRequestMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = A0DD9D98088DB5EB933F8EB6B2896C45 /* MPABTestDesignerTweakRequestMessage.m */; }; + FC9689DFF612C6C2D6A12A9AB5559676 /* MPObjectSerializerContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 24924571A2E4F72E07F127D000B26114 /* MPObjectSerializerContext.m */; }; + FD8DF770C7A271F8124A1E0A8F1AD685 /* MPSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = FC51B12E5421BD0A41628AB20D99D336 /* MPSwizzler.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FF0E57706E1E76D51730624F22DCFF2E /* MPObjectSelector.m in Sources */ = {isa = PBXBuildFile; fileRef = 6108BA444669F2D0D833E971534BA42B /* MPObjectSelector.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7AD899F0E482F35F29484E72789FFDAD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 324CF72F163C07B591556D1AD2956697; + remoteInfo = Mixpanel; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00023AE52FA8214E0BC2AC2DC386D6D4 /* MPABTestDesignerConnection.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerConnection.m; path = Mixpanel/MPABTestDesignerConnection.m; sourceTree = ""; }; + 0098C6FBE49E8C95D1450F10D1C141FB /* MPABTestDesignerTweakResponseMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerTweakResponseMessage.h; path = Mixpanel/MPABTestDesignerTweakResponseMessage.h; sourceTree = ""; }; + 00BD4E4891029D1E20ACD52DC0AC66E6 /* UIColor+MPColor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIColor+MPColor.h"; path = "Mixpanel/UIColor+MPColor.h"; sourceTree = ""; }; + 0290E0A6569BD0FBED77C2F2EE92A9BC /* MPCGRectToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPCGRectToNSDictionaryValueTransformer.m; path = Mixpanel/MPCGRectToNSDictionaryValueTransformer.m; sourceTree = ""; }; + 0300B9B7936A50EB3E0BA7D6C75451ED /* MPUIFontToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPUIFontToNSDictionaryValueTransformer.m; path = Mixpanel/MPUIFontToNSDictionaryValueTransformer.m; sourceTree = ""; }; + 05C9EA836E92AFB59436EC83FBF0402E /* MPNetwork.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPNetwork.h; path = Mixpanel/MPNetwork.h; sourceTree = ""; }; + 05D9381F8DC7C911CA707CA4512E74B3 /* Pods-T7Chicken-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-T7Chicken-acknowledgements.plist"; sourceTree = ""; }; + 063B2C733B1887F1BDC833AD5F17FD4F /* MPEventBinding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPEventBinding.h; path = Mixpanel/MPEventBinding.h; sourceTree = ""; }; + 06FB14F9B197EDC27B80034715EB9864 /* MPABTestDesignerClearRequestMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerClearRequestMessage.m; path = Mixpanel/MPABTestDesignerClearRequestMessage.m; sourceTree = ""; }; + 080DC77574F362D808C4BC1295315E7B /* MPNetwork.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPNetwork.m; path = Mixpanel/MPNetwork.m; sourceTree = ""; }; + 0A0DB21BDF7E9B679AF3BA1E9BBEEECB /* MPClassDescription.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPClassDescription.h; path = Mixpanel/MPClassDescription.h; sourceTree = ""; }; + 0A6D5D7163A0EB054104F6286025C17E /* Pods-T7Chicken-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-T7Chicken-resources.sh"; sourceTree = ""; }; + 0C5C9B441827E4EFDB3650259021E644 /* Mixpanel-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Mixpanel-dummy.m"; sourceTree = ""; }; + 0CD4FFFDF97BD7A88BF9B1F1339F338E /* libMixpanel.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libMixpanel.a; path = libMixpanel.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 0D1DABD0CCB460BD2836CA89EDC3626F /* MPTakeoverNotification.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPTakeoverNotification.m; path = Mixpanel/MPTakeoverNotification.m; sourceTree = ""; }; + 0E72151D46CB438491157DE80A8306A3 /* MPTakeoverNotificationViewController~ipad.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = "MPTakeoverNotificationViewController~ipad.xib"; path = "Mixpanel/MPTakeoverNotificationViewController~ipad.xib"; sourceTree = ""; }; + 12636F6ECA86EAA8B6A454480AE89A34 /* MPEnumDescription.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPEnumDescription.m; path = Mixpanel/MPEnumDescription.m; sourceTree = ""; }; + 14E2F99AA58929BCC4E1214E1536F053 /* Mixpanel+AutomaticTracks.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "Mixpanel+AutomaticTracks.m"; path = "Mixpanel/Mixpanel+AutomaticTracks.m"; sourceTree = ""; }; + 195E329161DAD8B5FE0371464178FD09 /* MPArrowLeft@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "MPArrowLeft@2x.png"; path = "Mixpanel/Images/MPArrowLeft@2x.png"; sourceTree = ""; }; + 1AD154AFE252EBDB35402851C1D92213 /* MPDesignerSessionCollection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPDesignerSessionCollection.h; path = Mixpanel/MPDesignerSessionCollection.h; sourceTree = ""; }; + 1F905CC3729CF6F206FEA49FB01B1023 /* MPTakeoverNotificationViewController~iphonelandscape.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = "MPTakeoverNotificationViewController~iphonelandscape.xib"; path = "Mixpanel/MPTakeoverNotificationViewController~iphonelandscape.xib"; sourceTree = ""; }; + 1F99C9FFC8BF29FE61C2CE89826675DB /* MPABTestDesignerSnapshotRequestMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerSnapshotRequestMessage.h; path = Mixpanel/MPABTestDesignerSnapshotRequestMessage.h; sourceTree = ""; }; + 2175E18D8C406FFAD6002A371489B9DD /* MPABTestDesignerClearResponseMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerClearResponseMessage.h; path = Mixpanel/MPABTestDesignerClearResponseMessage.h; sourceTree = ""; }; + 241FD1D28C18571694D4C55B11F02C85 /* MPPropertyDescription.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPPropertyDescription.m; path = Mixpanel/MPPropertyDescription.m; sourceTree = ""; }; + 24924571A2E4F72E07F127D000B26114 /* MPObjectSerializerContext.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPObjectSerializerContext.m; path = Mixpanel/MPObjectSerializerContext.m; sourceTree = ""; }; + 2547602FD9CCDE9FF3E18AFA6E229314 /* MPSwizzler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPSwizzler.m; path = Mixpanel/MPSwizzler.m; sourceTree = ""; }; + 2555FC294A173BDDB498EFDAC13E8CB2 /* MPBOOLToNSNumberValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPBOOLToNSNumberValueTransformer.m; path = Mixpanel/MPBOOLToNSNumberValueTransformer.m; sourceTree = ""; }; + 2657F835647A8CF76E80DC55354EAE48 /* MPNotificationViewController.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPNotificationViewController.h; path = Mixpanel/MPNotificationViewController.h; sourceTree = ""; }; + 27221A1E04FDD51B5FA3F3E295B1FBA1 /* MPABTestDesignerChangeResponseMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerChangeResponseMessage.h; path = Mixpanel/MPABTestDesignerChangeResponseMessage.h; sourceTree = ""; }; + 27FA1D8D74ADF5333CB52B04B782FAA6 /* MPValueTransformers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPValueTransformers.h; path = Mixpanel/MPValueTransformers.h; sourceTree = ""; }; + 282A2457E3FE97FE7112D4CD7C89556D /* MPTakeoverNotificationViewController~iphoneportrait.xib */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = file.xib; name = "MPTakeoverNotificationViewController~iphoneportrait.xib"; path = "Mixpanel/MPTakeoverNotificationViewController~iphoneportrait.xib"; sourceTree = ""; }; + 289BAF04AE2F97684046F85815C83C00 /* MixpanelPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MixpanelPrivate.h; path = Mixpanel/MixpanelPrivate.h; sourceTree = ""; }; + 2A756B9412EF39145CC604B342E7A072 /* MPABTestDesignerSnapshotRequestMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerSnapshotRequestMessage.m; path = Mixpanel/MPABTestDesignerSnapshotRequestMessage.m; sourceTree = ""; }; + 2B080F3D4FBEAB9071026C65F014E45C /* NSNotificationCenter+AutomaticTracks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSNotificationCenter+AutomaticTracks.h"; path = "Mixpanel/NSNotificationCenter+AutomaticTracks.h"; sourceTree = ""; }; + 2D5527C0C221D173E1371D423908996E /* libPods-T7ChickenTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-T7ChickenTests.a"; path = "libPods-T7ChickenTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2FE46A174CEDFA510C180B5990E4AA8B /* libPods-T7Chicken.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-T7Chicken.a"; path = "libPods-T7Chicken.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 314BF80596CA1F6CF4FA2B454D6333DC /* Pods-T7ChickenTests-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-T7ChickenTests-resources.sh"; sourceTree = ""; }; + 35074F112EA51F005340798731DF1B08 /* MPABTestDesignerClearRequestMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerClearRequestMessage.h; path = Mixpanel/MPABTestDesignerClearRequestMessage.h; sourceTree = ""; }; + 3762D2701AC35BCEABEEFFDC7E360EB6 /* MPABTestDesignerChangeResponseMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerChangeResponseMessage.m; path = Mixpanel/MPABTestDesignerChangeResponseMessage.m; sourceTree = ""; }; + 38A6F66AE93021C25C69FE4C266DAC81 /* MPNotification.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPNotification.m; path = Mixpanel/MPNotification.m; sourceTree = ""; }; + 3BEFC7939F87E4608FC9EFD214E8E52F /* MPPropertyDescription.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPPropertyDescription.h; path = Mixpanel/MPPropertyDescription.h; sourceTree = ""; }; + 3C75C97AA13697777772CAB4492A7255 /* MPWebSocket.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPWebSocket.m; path = Mixpanel/MPWebSocket.m; sourceTree = ""; }; + 3D142DCC88F666E172F2A1F3A3B9FB0A /* MPABTestDesignerDeviceInfoResponseMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerDeviceInfoResponseMessage.m; path = Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.m; sourceTree = ""; }; + 40E7AC14D29B6981B4F1E4F94C9E678C /* Mixpanel.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = Mixpanel.h; path = Mixpanel/Mixpanel.h; sourceTree = ""; }; + 445A64A188D0BE3FCA97DCD96FC0FFE9 /* Pods-T7ChickenTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-T7ChickenTests-frameworks.sh"; sourceTree = ""; }; + 44E01A6EE48A0F19B91800A4FEAB622A /* Pods-T7ChickenTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-T7ChickenTests-dummy.m"; sourceTree = ""; }; + 4684DE7A164B1CC2B13B37A4086B8EBB /* Pods-T7Chicken-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-T7Chicken-acknowledgements.markdown"; sourceTree = ""; }; + 48539D92FDB1B300D114BD020BE6ACB7 /* MPTweakInlineInternal.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPTweakInlineInternal.h; path = Mixpanel/MPTweakInlineInternal.h; sourceTree = ""; }; + 495336079BC64FCF52BE6639F85DCA09 /* AutomaticEvents.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = AutomaticEvents.m; path = Mixpanel/AutomaticEvents.m; sourceTree = ""; }; + 4B065F8367154F6B056EA7251BD65D1B /* MPEnumDescription.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPEnumDescription.h; path = Mixpanel/MPEnumDescription.h; sourceTree = ""; }; + 4BEA3E4E9A87A6A7D987F182380F6A7C /* UIView+MPHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIView+MPHelpers.h"; path = "Mixpanel/UIView+MPHelpers.h"; sourceTree = ""; }; + 4D51920BD13637A05EACEC2552823452 /* MixpanelPeople.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MixpanelPeople.h; path = Mixpanel/MixpanelPeople.h; sourceTree = ""; }; + 4D9F44C91360B97BDA3CCECD4797C689 /* MPObjectIdentifierProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPObjectIdentifierProvider.h; path = Mixpanel/MPObjectIdentifierProvider.h; sourceTree = ""; }; + 4DE7D352124FA106364ABA0930F46D9A /* MPAbstractABTestDesignerMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPAbstractABTestDesignerMessage.m; path = Mixpanel/MPAbstractABTestDesignerMessage.m; sourceTree = ""; }; + 4F354C404F6094664BB64E6706D5AF8F /* MPABTestDesignerTweakRequestMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerTweakRequestMessage.h; path = Mixpanel/MPABTestDesignerTweakRequestMessage.h; sourceTree = ""; }; + 512D4BCAED57A02F1B8B193A793FF8E9 /* MPCGColorRefToNSStringValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPCGColorRefToNSStringValueTransformer.m; path = Mixpanel/MPCGColorRefToNSStringValueTransformer.m; sourceTree = ""; }; + 52BE2F2678A867E81ADC0A16A3AB5874 /* UIImage+MPAverageColor.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+MPAverageColor.h"; path = "Mixpanel/UIImage+MPAverageColor.h"; sourceTree = ""; }; + 52F014A0CF4CC9A7CF29A701614CD06E /* MPObjectIdentityProvider.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPObjectIdentityProvider.m; path = Mixpanel/MPObjectIdentityProvider.m; sourceTree = ""; }; + 53740A940112BB3679D1F8F281F6E422 /* MPLogo@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "MPLogo@2x.png"; path = "Mixpanel/Images/MPLogo@2x.png"; sourceTree = ""; }; + 55649A64FA930E5CFD3AD6693E947D84 /* MPFoundation.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPFoundation.h; path = Mixpanel/MPFoundation.h; sourceTree = ""; }; + 5737663F2FB8377664A80ED06E7B1CAF /* Pods-T7ChickenTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-T7ChickenTests.release.xcconfig"; sourceTree = ""; }; + 5AA4D3364D2E311DF0E1F0ED9663E5B1 /* NSNotificationCenter+AutomaticTracks.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSNotificationCenter+AutomaticTracks.m"; path = "Mixpanel/NSNotificationCenter+AutomaticTracks.m"; sourceTree = ""; }; + 5BC128BB06115DCBBF25335548036364 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; + 5BC312EB8AE9323065CAB166BA652380 /* NSInvocation+MPHelpers.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "NSInvocation+MPHelpers.h"; path = "Mixpanel/NSInvocation+MPHelpers.h"; sourceTree = ""; }; + 5D73D2EB3C330723A5465175C8CFEF1D /* MPABTestDesignerDeviceInfoRequestMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerDeviceInfoRequestMessage.h; path = Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.h; sourceTree = ""; }; + 5F8436DD8EEBE2AFC1FA739C6FBAB2D4 /* MPObjectSerializer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPObjectSerializer.m; path = Mixpanel/MPObjectSerializer.m; sourceTree = ""; }; + 603E259486AD174C1DFB2DC78E8F1C1B /* MPABTestDesignerDeviceInfoRequestMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerDeviceInfoRequestMessage.m; path = Mixpanel/MPABTestDesignerDeviceInfoRequestMessage.m; sourceTree = ""; }; + 6108BA444669F2D0D833E971534BA42B /* MPObjectSelector.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPObjectSelector.m; path = Mixpanel/MPObjectSelector.m; sourceTree = ""; }; + 612524F64E44A0B408F6589091F9F31D /* Pods-T7ChickenTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-T7ChickenTests-acknowledgements.markdown"; sourceTree = ""; }; + 62753BB53DCBA85D5EA0F982D919261A /* MPLogo.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = MPLogo.png; path = Mixpanel/Images/MPLogo.png; sourceTree = ""; }; + 633E2AEB882FE7FD0AF6D9E1E10D6277 /* MPMiniNotification.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPMiniNotification.m; path = Mixpanel/MPMiniNotification.m; sourceTree = ""; }; + 6438AE51C47DEEAAA2FE5E3EDCBD9AB7 /* MPCheckmark@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "MPCheckmark@2x.png"; path = "Mixpanel/Images/MPCheckmark@2x.png"; sourceTree = ""; }; + 655076B11228FF69369D8BA0CB27BE52 /* MPABTestDesignerChangeRequestMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerChangeRequestMessage.m; path = Mixpanel/MPABTestDesignerChangeRequestMessage.m; sourceTree = ""; }; + 6747D7636E5B2AE038E2D8CF32E91CA8 /* MPABTestDesignerChangeRequestMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerChangeRequestMessage.h; path = Mixpanel/MPABTestDesignerChangeRequestMessage.h; sourceTree = ""; }; + 67D09ED4675412E9464A1051215C6797 /* MPCheckmark.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = MPCheckmark.png; path = Mixpanel/Images/MPCheckmark.png; sourceTree = ""; }; + 683ABFFCDAB185558CADD80FE1239056 /* Mixpanel+AutomaticTracks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "Mixpanel+AutomaticTracks.h"; path = "Mixpanel/Mixpanel+AutomaticTracks.h"; sourceTree = ""; }; + 6AC38CAF66743432E4D28F37D0AD86E1 /* MPArrowRight@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "MPArrowRight@2x.png"; path = "Mixpanel/Images/MPArrowRight@2x.png"; sourceTree = ""; }; + 6E6554C79879AC1FB7A59B3C3E0FDBD0 /* MPCloseButton@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "MPCloseButton@2x.png"; path = "Mixpanel/Images/MPCloseButton@2x.png"; sourceTree = ""; }; + 6F9372302B10412D06A0B5E3A38F4C10 /* MPAbstractABTestDesignerMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPAbstractABTestDesignerMessage.h; path = Mixpanel/MPAbstractABTestDesignerMessage.h; sourceTree = ""; }; + 6FF5380941A8931D875DBE2200EDD7F2 /* Pods-T7Chicken-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-T7Chicken-frameworks.sh"; sourceTree = ""; }; + 70444506DB97ECAE02284C5CCCCB127F /* MPObjectSerializer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPObjectSerializer.h; path = Mixpanel/MPObjectSerializer.h; sourceTree = ""; }; + 71186B3CA0D00FD73E8FD4C4FA0704F8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 717C2ABD8C02755E8FB4E7192612518B /* Pods-T7Chicken.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-T7Chicken.debug.xcconfig"; sourceTree = ""; }; + 71956E54102D9EB87A438106001B5C67 /* MPObjectSerializerConfig.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPObjectSerializerConfig.h; path = Mixpanel/MPObjectSerializerConfig.h; sourceTree = ""; }; + 72542460AC948DA8E4DCAC875F1BB596 /* MPDesignerEventBindingResponseMesssage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPDesignerEventBindingResponseMesssage.m; path = Mixpanel/MPDesignerEventBindingResponseMesssage.m; sourceTree = ""; }; + 732A2D1E7852F17E649284435CACCD0B /* UIViewController+AutomaticTracks.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+AutomaticTracks.m"; path = "Mixpanel/UIViewController+AutomaticTracks.m"; sourceTree = ""; }; + 7566914A06505062CAAC356EE828C649 /* _MPTweakBindObserver.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = _MPTweakBindObserver.m; path = Mixpanel/_MPTweakBindObserver.m; sourceTree = ""; }; + 756BAB8FAE467B21D41FB4F4FB730551 /* Pods-T7Chicken.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-T7Chicken.release.xcconfig"; sourceTree = ""; }; + 773E3EE5400D07856AE311CC25A76090 /* MPApplicationStateSerializer.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPApplicationStateSerializer.h; path = Mixpanel/MPApplicationStateSerializer.h; sourceTree = ""; }; + 784EF79DC3D6331442E885CE4BE2FA7A /* MPLogger.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPLogger.h; path = Mixpanel/MPLogger.h; sourceTree = ""; }; + 825631778F26F9900D0ECE9F8E48FE8D /* MPCGPointToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPCGPointToNSDictionaryValueTransformer.m; path = Mixpanel/MPCGPointToNSDictionaryValueTransformer.m; sourceTree = ""; }; + 8347985818F92CBABBB7CF35337CFA2B /* MPCloseButton.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = MPCloseButton.png; path = Mixpanel/Images/MPCloseButton.png; sourceTree = ""; }; + 845F8B0167B64D32EC3C83E2E34407FF /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/CoreTelephony.framework; sourceTree = DEVELOPER_DIR; }; + 850AA3A3758B223E2A368B0C4150E6D4 /* MPDesignerEventBindingMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPDesignerEventBindingMessage.h; path = Mixpanel/MPDesignerEventBindingMessage.h; sourceTree = ""; }; + 86E8C8C258F6CA922437378866BD5C43 /* MPABTestDesignerDeviceInfoResponseMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerDeviceInfoResponseMessage.h; path = Mixpanel/MPABTestDesignerDeviceInfoResponseMessage.h; sourceTree = ""; }; + 86EA6C022E954AB4D0DC8E04303BA151 /* MPArrowLeft.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = MPArrowLeft.png; path = Mixpanel/Images/MPArrowLeft.png; sourceTree = ""; }; + 88770AB53F75BA220C5AF6AD05D09609 /* UIApplication+AutomaticTracks.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+AutomaticTracks.m"; path = "Mixpanel/UIApplication+AutomaticTracks.m"; sourceTree = ""; }; + 89CE7AE30E368B45D023E6A8339B13DD /* MPApplicationStateSerializer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPApplicationStateSerializer.m; path = Mixpanel/MPApplicationStateSerializer.m; sourceTree = ""; }; + 8A4B0C0EE3AAF1C9C7D6EB2D37D5A90E /* MPTweakInline.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPTweakInline.h; path = Mixpanel/MPTweakInline.h; sourceTree = ""; }; + 8F878A5BEF282075BCFC2102FF1B96D1 /* MPNotificationViewController.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPNotificationViewController.m; path = Mixpanel/MPNotificationViewController.m; sourceTree = ""; }; + 90627B78BCBF044B18FD2C44F93E327C /* MPCGSizeToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPCGSizeToNSDictionaryValueTransformer.m; path = Mixpanel/MPCGSizeToNSDictionaryValueTransformer.m; sourceTree = ""; }; + 919005EA6727D6B78BD6D56646162573 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/SystemConfiguration.framework; sourceTree = DEVELOPER_DIR; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9503D8480738151CD6B30C2EC9F6298B /* MPNSNumberToCGFloatValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPNSNumberToCGFloatValueTransformer.m; path = Mixpanel/MPNSNumberToCGFloatValueTransformer.m; sourceTree = ""; }; + 954BB41EB65E3C98BE314CB6CE950D72 /* MPResources.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPResources.h; path = Mixpanel/MPResources.h; sourceTree = ""; }; + 95B96F308485E749A29BE0EB435DF0C7 /* NSInvocation+MPHelpers.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "NSInvocation+MPHelpers.m"; path = "Mixpanel/NSInvocation+MPHelpers.m"; sourceTree = ""; }; + 9895AFE3F53886DB084616C99AF0CC3F /* MPTypeDescription.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPTypeDescription.m; path = Mixpanel/MPTypeDescription.m; sourceTree = ""; }; + 98D4131908AE7A20F9333418F938CB1C /* MPCGAffineTransformToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPCGAffineTransformToNSDictionaryValueTransformer.m; path = Mixpanel/MPCGAffineTransformToNSDictionaryValueTransformer.m; sourceTree = ""; }; + 9A3F2B8269D39DCD7C64A7FBCA009C1D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + 9A422E780D2A48005F4F238B2F4314A7 /* MPUITableViewBinding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPUITableViewBinding.m; path = Mixpanel/MPUITableViewBinding.m; sourceTree = ""; }; + 9C241251EF3735A3DD315386B285C6B1 /* MPTweak.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPTweak.m; path = Mixpanel/MPTweak.m; sourceTree = ""; }; + 9C825D678914AE352C431669F592E918 /* AutomaticEvents.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AutomaticEvents.h; path = Mixpanel/AutomaticEvents.h; sourceTree = ""; }; + 9CFEDE9D7C47E4416C9E83AEEE504006 /* MPSwizzle.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPSwizzle.m; path = Mixpanel/MPSwizzle.m; sourceTree = ""; }; + 9F211693547D48FBBD99671F9A76F7ED /* MPABTestDesignerMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerMessage.h; path = Mixpanel/MPABTestDesignerMessage.h; sourceTree = ""; }; + 9FA16B40234415D66ADF8105DDEB6A8A /* MPUIColorToNSStringValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPUIColorToNSStringValueTransformer.m; path = Mixpanel/MPUIColorToNSStringValueTransformer.m; sourceTree = ""; }; + A073ABE9E4CD983137859E96A2B3702F /* UIColor+MPColor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIColor+MPColor.m"; path = "Mixpanel/UIColor+MPColor.m"; sourceTree = ""; }; + A0DD9D98088DB5EB933F8EB6B2896C45 /* MPABTestDesignerTweakRequestMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerTweakRequestMessage.m; path = Mixpanel/MPABTestDesignerTweakRequestMessage.m; sourceTree = ""; }; + A1DD790C19B315A12311414C50557E79 /* MixpanelPeoplePrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MixpanelPeoplePrivate.h; path = Mixpanel/MixpanelPeoplePrivate.h; sourceTree = ""; }; + A58CC11EAF63E6D048C0847FC6B0BFFA /* MPCATransform3DToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPCATransform3DToNSDictionaryValueTransformer.m; path = Mixpanel/MPCATransform3DToNSDictionaryValueTransformer.m; sourceTree = ""; }; + A638B33BB9B0477EDDEAB2D1379B8396 /* MPABTestDesignerSnapshotResponseMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerSnapshotResponseMessage.h; path = Mixpanel/MPABTestDesignerSnapshotResponseMessage.h; sourceTree = ""; }; + A8E305274F4826B282FCFC6B5E3E7720 /* MPArrowRight.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = MPArrowRight.png; path = Mixpanel/Images/MPArrowRight.png; sourceTree = ""; }; + A974035D9F638A6C273C4F9B4CA905E6 /* MPPassThroughValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPPassThroughValueTransformer.m; path = Mixpanel/MPPassThroughValueTransformer.m; sourceTree = ""; }; + AAEDCAEC8AFE7834F8D5464E160EB0C7 /* MPABTestDesignerDisconnectMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerDisconnectMessage.m; path = Mixpanel/MPABTestDesignerDisconnectMessage.m; sourceTree = ""; }; + ACC0A91C458F7FF2C2734320DD3DECC3 /* MixpanelPeople.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MixpanelPeople.m; path = Mixpanel/MixpanelPeople.m; sourceTree = ""; }; + AF06B4CD829B7CD1531C9CCD6D858EC6 /* MPNSAttributedStringToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPNSAttributedStringToNSDictionaryValueTransformer.m; path = Mixpanel/MPNSAttributedStringToNSDictionaryValueTransformer.m; sourceTree = ""; }; + B1B72068C63BB352B542A164622AEAA4 /* MPDismissKeyboard@2x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "MPDismissKeyboard@2x.png"; path = "Mixpanel/Images/MPDismissKeyboard@2x.png"; sourceTree = ""; }; + B20D7FED2792635CABB46680D6741F76 /* MPTweakStore.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPTweakStore.h; path = Mixpanel/MPTweakStore.h; sourceTree = ""; }; + B239D2F597F82A0A72EBD2CEFB828E43 /* MPDesignerEventBindingRequestMesssage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPDesignerEventBindingRequestMesssage.m; path = Mixpanel/MPDesignerEventBindingRequestMesssage.m; sourceTree = ""; }; + B3A2EC70B9D72682673923E594428352 /* MPClassDescription.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPClassDescription.m; path = Mixpanel/MPClassDescription.m; sourceTree = ""; }; + B4F04D8E29CFF16E5FD2862D7A0DBD53 /* MixpanelExceptionHandler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MixpanelExceptionHandler.h; path = Mixpanel/MixpanelExceptionHandler.h; sourceTree = ""; }; + B53AE7B40D02F7380EA6A04C1923AC70 /* MPObjectSerializerConfig.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPObjectSerializerConfig.m; path = Mixpanel/MPObjectSerializerConfig.m; sourceTree = ""; }; + B694C8BE1B01C69E7C06E4E4E7649993 /* MPNotificationButton.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPNotificationButton.h; path = Mixpanel/MPNotificationButton.h; sourceTree = ""; }; + B6C00B57F215B338C8DF4D0F21274984 /* MPCloseButton@3x.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "MPCloseButton@3x.png"; path = "Mixpanel/Images/MPCloseButton@3x.png"; sourceTree = ""; }; + B719C21022B032FDDB1AB473149C3DC4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + BAABAFBD61155787287A6E3F86C660E2 /* MPTweakStore.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPTweakStore.m; path = Mixpanel/MPTweakStore.m; sourceTree = ""; }; + BD1951D684625282975D3F4437B3B0E4 /* MPUITableViewBinding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPUITableViewBinding.h; path = Mixpanel/MPUITableViewBinding.h; sourceTree = ""; }; + BD71BB594B30DA593E2833783E5584F6 /* Mixpanel-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Mixpanel-prefix.pch"; sourceTree = ""; }; + BD912927F984990CF05ABAC888D505D8 /* MPTypeDescription.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPTypeDescription.h; path = Mixpanel/MPTypeDescription.h; sourceTree = ""; }; + C0924CBF800FD5A4C50440D1FED88E16 /* MPUIControlBinding.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPUIControlBinding.h; path = Mixpanel/MPUIControlBinding.h; sourceTree = ""; }; + C37E375FBF42423790C267884E919475 /* MPObjectIdentityProvider.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPObjectIdentityProvider.h; path = Mixpanel/MPObjectIdentityProvider.h; sourceTree = ""; }; + C40DABBE05D5A548752435CACC2A27BA /* Pods-T7ChickenTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-T7ChickenTests-acknowledgements.plist"; sourceTree = ""; }; + C670C5732EC303843E558778014D3600 /* _MPTweakBindObserver.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = _MPTweakBindObserver.h; path = Mixpanel/_MPTweakBindObserver.h; sourceTree = ""; }; + C6FC56685B4D4D29D070D1D604793881 /* MPDismissKeyboard.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = MPDismissKeyboard.png; path = Mixpanel/Images/MPDismissKeyboard.png; sourceTree = ""; }; + C7D838CBAC290780FB26966C8F3D5EAA /* MPSwizzle.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPSwizzle.h; path = Mixpanel/MPSwizzle.h; sourceTree = ""; }; + C8F2BA672D9F764823F1D2D1FD4AB350 /* MPResources.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPResources.m; path = Mixpanel/MPResources.m; sourceTree = ""; }; + C91230158E956866DAD8522E3D166D36 /* MPDesignerTrackMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPDesignerTrackMessage.m; path = Mixpanel/MPDesignerTrackMessage.m; sourceTree = ""; }; + C92A1DE2A35144C207368567F710908D /* MixpanelExceptionHandler.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MixpanelExceptionHandler.m; path = Mixpanel/MixpanelExceptionHandler.m; sourceTree = ""; }; + CA6D7225C521B37136B62E78C1E15F35 /* MPTakeoverNotification.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPTakeoverNotification.h; path = Mixpanel/MPTakeoverNotification.h; sourceTree = ""; }; + D1A7BC937C51BDB7D5E26C58128F255E /* UIImage+MPAverageColor.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+MPAverageColor.m"; path = "Mixpanel/UIImage+MPAverageColor.m"; sourceTree = ""; }; + D1AD8C944FB55C561E634202F9248D2D /* MPABTestDesignerClearResponseMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerClearResponseMessage.m; path = Mixpanel/MPABTestDesignerClearResponseMessage.m; sourceTree = ""; }; + D2A23A77C5B0DE316B102434E5D02499 /* MPUIControlBinding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPUIControlBinding.m; path = Mixpanel/MPUIControlBinding.m; sourceTree = ""; }; + D31B5FB59EDF59BB514907DB57D6E8B4 /* MPNetworkPrivate.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPNetworkPrivate.h; path = Mixpanel/MPNetworkPrivate.h; sourceTree = ""; }; + D4F8734CA45A859AB891CC61B66E61EA /* MPTweak.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPTweak.h; path = Mixpanel/MPTweak.h; sourceTree = ""; }; + D5036A539FA57EC1EDBAF1782E680024 /* Mixpanel.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Mixpanel.xcconfig; sourceTree = ""; }; + D5CD044CF8DF9B720E9AD425F3514EDE /* MPUIEdgeInsetsToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPUIEdgeInsetsToNSDictionaryValueTransformer.m; path = Mixpanel/MPUIEdgeInsetsToNSDictionaryValueTransformer.m; sourceTree = ""; }; + D63D572109015E7A9771C0BF61E41834 /* MPObjectSelector.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPObjectSelector.h; path = Mixpanel/MPObjectSelector.h; sourceTree = ""; }; + D777206689D272ABB6CB8797FEB2C740 /* MPABTestDesignerSnapshotResponseMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerSnapshotResponseMessage.m; path = Mixpanel/MPABTestDesignerSnapshotResponseMessage.m; sourceTree = ""; }; + D7BC9F0586F0DB0659D1D0A33AC87B72 /* MPObjectSerializerContext.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPObjectSerializerContext.h; path = Mixpanel/MPObjectSerializerContext.h; sourceTree = ""; }; + D8CC28A2106D1E76AEEFCE47C3DDFD1E /* MPSequenceGenerator.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPSequenceGenerator.m; path = Mixpanel/MPSequenceGenerator.m; sourceTree = ""; }; + DEBB170C91EE81BE8E9486A6750B93BC /* UIApplication+AutomaticTracks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIApplication+AutomaticTracks.h"; path = "Mixpanel/UIApplication+AutomaticTracks.h"; sourceTree = ""; }; + E229782F104BF790213642B67F1D4298 /* MPTweakInline.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPTweakInline.m; path = Mixpanel/MPTweakInline.m; sourceTree = ""; }; + E291A22751011402F5C8CA49E92B39A8 /* MPSequenceGenerator.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPSequenceGenerator.h; path = Mixpanel/MPSequenceGenerator.h; sourceTree = ""; }; + E3C47467D4FAB4C0C812AFBE2AE99824 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Accelerate.framework; sourceTree = DEVELOPER_DIR; }; + E4CAA50D3A6C55998865A4C20040411F /* MPVariant.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPVariant.m; path = Mixpanel/MPVariant.m; sourceTree = ""; }; + E57E9E509F0CAD6C1B003EE3E26264FF /* MPEventBinding.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPEventBinding.m; path = Mixpanel/MPEventBinding.m; sourceTree = ""; }; + E5E9C747F79968077370666202A888C5 /* MPNotificationButton.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPNotificationButton.m; path = Mixpanel/MPNotificationButton.m; sourceTree = ""; }; + E77980A3EA7F735EFBE73610C8E2F6F8 /* AutomaticTracksConstants.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = AutomaticTracksConstants.h; path = Mixpanel/AutomaticTracksConstants.h; sourceTree = ""; }; + E848DD65E9223346BBE17BF874237E36 /* MPABTestDesignerConnection.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerConnection.h; path = Mixpanel/MPABTestDesignerConnection.h; sourceTree = ""; }; + E86BDFA03FE2116AB7B8320D5BC388B2 /* Mixpanel.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = Mixpanel.m; path = Mixpanel/Mixpanel.m; sourceTree = ""; }; + E94F5B43228D25F36A6514EF0F8649F0 /* UIViewController+AutomaticTracks.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIViewController+AutomaticTracks.h"; path = "Mixpanel/UIViewController+AutomaticTracks.h"; sourceTree = ""; }; + ECBE74E38586159316E7313D4EBC4735 /* MPUIImageToNSDictionaryValueTransformer.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPUIImageToNSDictionaryValueTransformer.m; path = Mixpanel/MPUIImageToNSDictionaryValueTransformer.m; sourceTree = ""; }; + ED63EE3EC61744E7339A3AEF636C5FD3 /* MPNotification.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPNotification.h; path = Mixpanel/MPNotification.h; sourceTree = ""; }; + EEBD63BF04C8534B06212C52EA5F1389 /* Pods-T7ChickenTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-T7ChickenTests.debug.xcconfig"; sourceTree = ""; }; + F2C2BC02095916C110A5DEF47D7155F1 /* placeholder-image.png */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = image.png; name = "placeholder-image.png"; path = "Mixpanel/Images/placeholder-image.png"; sourceTree = ""; }; + F55C478EE6577CB6E8844E9010B8DE60 /* MPABTestDesignerTweakResponseMessage.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = MPABTestDesignerTweakResponseMessage.m; path = Mixpanel/MPABTestDesignerTweakResponseMessage.m; sourceTree = ""; }; + F612930E064B62CA915CD1A190E5DC87 /* MPWebSocket.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPWebSocket.h; path = Mixpanel/MPWebSocket.h; sourceTree = ""; }; + FBF2D10E12078A447678BAB98302318D /* MPVariant.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPVariant.h; path = Mixpanel/MPVariant.h; sourceTree = ""; }; + FC21782E539B7AB4E8558918F2D53191 /* Pods-T7Chicken-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-T7Chicken-dummy.m"; sourceTree = ""; }; + FC51B12E5421BD0A41628AB20D99D336 /* MPSwizzler.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPSwizzler.h; path = Mixpanel/MPSwizzler.h; sourceTree = ""; }; + FD0BFDA381D45CAE312DD81010E3DBE7 /* MPMiniNotification.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPMiniNotification.h; path = Mixpanel/MPMiniNotification.h; sourceTree = ""; }; + FD44723997BA78DD92FB2A9E8041E5D1 /* UIImage+MPImageEffects.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIImage+MPImageEffects.m"; path = "Mixpanel/UIImage+MPImageEffects.m"; sourceTree = ""; }; + FED696F5A118A83AD0A304EA058D5F1C /* UIView+MPHelpers.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; name = "UIView+MPHelpers.m"; path = "Mixpanel/UIView+MPHelpers.m"; sourceTree = ""; }; + FEDE1C79E251E590A31CA9146559CFA8 /* MPABTestDesignerDisconnectMessage.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MPABTestDesignerDisconnectMessage.h; path = Mixpanel/MPABTestDesignerDisconnectMessage.h; sourceTree = ""; }; + FFE56D520884524B65571806182F7007 /* UIImage+MPImageEffects.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = "UIImage+MPImageEffects.h"; path = "Mixpanel/UIImage+MPImageEffects.h"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7CD8347EE4BC62356DA051D9107A25D5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4AD15A531EFB023ABA992A51209CC519 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D560D228B3FE26E31B880F9ACF54462F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 95C596BF88C2858A194068429FA42BFB /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D76573A1CC6E2E947AFDFB5164E98C9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C2C9328B91ED677B59340D4A8576A7A /* Accelerate.framework in Frameworks */, + 26A28F3B7C721A57574BEB3F7FF136C2 /* CoreGraphics.framework in Frameworks */, + 07B28FC8C927D50B3CEF98649CFAE97E /* CoreTelephony.framework in Frameworks */, + 51ABAD5F6B5D8461A5255011A7B77506 /* Foundation.framework in Frameworks */, + 6E7014E449CFC1C44F2C963E46467373 /* QuartzCore.framework in Frameworks */, + DF2E08AE349C49A8803B29448B1950BD /* SystemConfiguration.framework in Frameworks */, + 59298EED2003397D652D31BD4D3F937E /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08C439CEA415A7213A0B7C18F9D15B96 /* Products */ = { + isa = PBXGroup; + children = ( + 0CD4FFFDF97BD7A88BF9B1F1339F338E /* libMixpanel.a */, + 2FE46A174CEDFA510C180B5990E4AA8B /* libPods-T7Chicken.a */, + 2D5527C0C221D173E1371D423908996E /* libPods-T7ChickenTests.a */, + ); + name = Products; + sourceTree = ""; + }; + 15C5A64CF98D85193B8264B2F9253ED6 /* Pods */ = { + isa = PBXGroup; + children = ( + E152715C51AA6839F3FC19ECA4302032 /* Mixpanel */, + ); + name = Pods; + sourceTree = ""; + }; + 2AA5DA946C15C05C0492C4CDAC5DE5BC /* Resources */ = { + isa = PBXGroup; + children = ( + 86EA6C022E954AB4D0DC8E04303BA151 /* MPArrowLeft.png */, + 195E329161DAD8B5FE0371464178FD09 /* MPArrowLeft@2x.png */, + A8E305274F4826B282FCFC6B5E3E7720 /* MPArrowRight.png */, + 6AC38CAF66743432E4D28F37D0AD86E1 /* MPArrowRight@2x.png */, + 67D09ED4675412E9464A1051215C6797 /* MPCheckmark.png */, + 6438AE51C47DEEAAA2FE5E3EDCBD9AB7 /* MPCheckmark@2x.png */, + 8347985818F92CBABBB7CF35337CFA2B /* MPCloseButton.png */, + 6E6554C79879AC1FB7A59B3C3E0FDBD0 /* MPCloseButton@2x.png */, + B6C00B57F215B338C8DF4D0F21274984 /* MPCloseButton@3x.png */, + C6FC56685B4D4D29D070D1D604793881 /* MPDismissKeyboard.png */, + B1B72068C63BB352B542A164622AEAA4 /* MPDismissKeyboard@2x.png */, + 62753BB53DCBA85D5EA0F982D919261A /* MPLogo.png */, + 53740A940112BB3679D1F8F281F6E422 /* MPLogo@2x.png */, + 0E72151D46CB438491157DE80A8306A3 /* MPTakeoverNotificationViewController~ipad.xib */, + 1F905CC3729CF6F206FEA49FB01B1023 /* MPTakeoverNotificationViewController~iphonelandscape.xib */, + 282A2457E3FE97FE7112D4CD7C89556D /* MPTakeoverNotificationViewController~iphoneportrait.xib */, + F2C2BC02095916C110A5DEF47D7155F1 /* placeholder-image.png */, + ); + name = Resources; + sourceTree = ""; + }; + 3652949AF6080C4B376EE8EE5F2BCFDC /* iOS */ = { + isa = PBXGroup; + children = ( + E3C47467D4FAB4C0C812AFBE2AE99824 /* Accelerate.framework */, + 9A3F2B8269D39DCD7C64A7FBCA009C1D /* CoreGraphics.framework */, + 845F8B0167B64D32EC3C83E2E34407FF /* CoreTelephony.framework */, + 71186B3CA0D00FD73E8FD4C4FA0704F8 /* Foundation.framework */, + 5BC128BB06115DCBBF25335548036364 /* QuartzCore.framework */, + 919005EA6727D6B78BD6D56646162573 /* SystemConfiguration.framework */, + B719C21022B032FDDB1AB473149C3DC4 /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + F4CDA5FA9197A41E0081E84F932906EB /* Frameworks */, + 15C5A64CF98D85193B8264B2F9253ED6 /* Pods */, + 08C439CEA415A7213A0B7C18F9D15B96 /* Products */, + B3868756C096D1C4446809961D6D0F99 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 948EC73BFF1326DC4265D9148CD8E3D6 /* Pods-T7Chicken */ = { + isa = PBXGroup; + children = ( + 4684DE7A164B1CC2B13B37A4086B8EBB /* Pods-T7Chicken-acknowledgements.markdown */, + 05D9381F8DC7C911CA707CA4512E74B3 /* Pods-T7Chicken-acknowledgements.plist */, + FC21782E539B7AB4E8558918F2D53191 /* Pods-T7Chicken-dummy.m */, + 6FF5380941A8931D875DBE2200EDD7F2 /* Pods-T7Chicken-frameworks.sh */, + 0A6D5D7163A0EB054104F6286025C17E /* Pods-T7Chicken-resources.sh */, + 717C2ABD8C02755E8FB4E7192612518B /* Pods-T7Chicken.debug.xcconfig */, + 756BAB8FAE467B21D41FB4F4FB730551 /* Pods-T7Chicken.release.xcconfig */, + ); + name = "Pods-T7Chicken"; + path = "Target Support Files/Pods-T7Chicken"; + sourceTree = ""; + }; + B3868756C096D1C4446809961D6D0F99 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 948EC73BFF1326DC4265D9148CD8E3D6 /* Pods-T7Chicken */, + F1278DC2C6644DD4A148E0DB046742E0 /* Pods-T7ChickenTests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + E152715C51AA6839F3FC19ECA4302032 /* Mixpanel */ = { + isa = PBXGroup; + children = ( + C670C5732EC303843E558778014D3600 /* _MPTweakBindObserver.h */, + 7566914A06505062CAAC356EE828C649 /* _MPTweakBindObserver.m */, + 9C825D678914AE352C431669F592E918 /* AutomaticEvents.h */, + 495336079BC64FCF52BE6639F85DCA09 /* AutomaticEvents.m */, + E77980A3EA7F735EFBE73610C8E2F6F8 /* AutomaticTracksConstants.h */, + 40E7AC14D29B6981B4F1E4F94C9E678C /* Mixpanel.h */, + E86BDFA03FE2116AB7B8320D5BC388B2 /* Mixpanel.m */, + 683ABFFCDAB185558CADD80FE1239056 /* Mixpanel+AutomaticTracks.h */, + 14E2F99AA58929BCC4E1214E1536F053 /* Mixpanel+AutomaticTracks.m */, + B4F04D8E29CFF16E5FD2862D7A0DBD53 /* MixpanelExceptionHandler.h */, + C92A1DE2A35144C207368567F710908D /* MixpanelExceptionHandler.m */, + 4D51920BD13637A05EACEC2552823452 /* MixpanelPeople.h */, + ACC0A91C458F7FF2C2734320DD3DECC3 /* MixpanelPeople.m */, + A1DD790C19B315A12311414C50557E79 /* MixpanelPeoplePrivate.h */, + 289BAF04AE2F97684046F85815C83C00 /* MixpanelPrivate.h */, + 6F9372302B10412D06A0B5E3A38F4C10 /* MPAbstractABTestDesignerMessage.h */, + 4DE7D352124FA106364ABA0930F46D9A /* MPAbstractABTestDesignerMessage.m */, + 6747D7636E5B2AE038E2D8CF32E91CA8 /* MPABTestDesignerChangeRequestMessage.h */, + 655076B11228FF69369D8BA0CB27BE52 /* MPABTestDesignerChangeRequestMessage.m */, + 27221A1E04FDD51B5FA3F3E295B1FBA1 /* MPABTestDesignerChangeResponseMessage.h */, + 3762D2701AC35BCEABEEFFDC7E360EB6 /* MPABTestDesignerChangeResponseMessage.m */, + 35074F112EA51F005340798731DF1B08 /* MPABTestDesignerClearRequestMessage.h */, + 06FB14F9B197EDC27B80034715EB9864 /* MPABTestDesignerClearRequestMessage.m */, + 2175E18D8C406FFAD6002A371489B9DD /* MPABTestDesignerClearResponseMessage.h */, + D1AD8C944FB55C561E634202F9248D2D /* MPABTestDesignerClearResponseMessage.m */, + E848DD65E9223346BBE17BF874237E36 /* MPABTestDesignerConnection.h */, + 00023AE52FA8214E0BC2AC2DC386D6D4 /* MPABTestDesignerConnection.m */, + 5D73D2EB3C330723A5465175C8CFEF1D /* MPABTestDesignerDeviceInfoRequestMessage.h */, + 603E259486AD174C1DFB2DC78E8F1C1B /* MPABTestDesignerDeviceInfoRequestMessage.m */, + 86E8C8C258F6CA922437378866BD5C43 /* MPABTestDesignerDeviceInfoResponseMessage.h */, + 3D142DCC88F666E172F2A1F3A3B9FB0A /* MPABTestDesignerDeviceInfoResponseMessage.m */, + FEDE1C79E251E590A31CA9146559CFA8 /* MPABTestDesignerDisconnectMessage.h */, + AAEDCAEC8AFE7834F8D5464E160EB0C7 /* MPABTestDesignerDisconnectMessage.m */, + 9F211693547D48FBBD99671F9A76F7ED /* MPABTestDesignerMessage.h */, + 1F99C9FFC8BF29FE61C2CE89826675DB /* MPABTestDesignerSnapshotRequestMessage.h */, + 2A756B9412EF39145CC604B342E7A072 /* MPABTestDesignerSnapshotRequestMessage.m */, + A638B33BB9B0477EDDEAB2D1379B8396 /* MPABTestDesignerSnapshotResponseMessage.h */, + D777206689D272ABB6CB8797FEB2C740 /* MPABTestDesignerSnapshotResponseMessage.m */, + 4F354C404F6094664BB64E6706D5AF8F /* MPABTestDesignerTweakRequestMessage.h */, + A0DD9D98088DB5EB933F8EB6B2896C45 /* MPABTestDesignerTweakRequestMessage.m */, + 0098C6FBE49E8C95D1450F10D1C141FB /* MPABTestDesignerTweakResponseMessage.h */, + F55C478EE6577CB6E8844E9010B8DE60 /* MPABTestDesignerTweakResponseMessage.m */, + 773E3EE5400D07856AE311CC25A76090 /* MPApplicationStateSerializer.h */, + 89CE7AE30E368B45D023E6A8339B13DD /* MPApplicationStateSerializer.m */, + 2555FC294A173BDDB498EFDAC13E8CB2 /* MPBOOLToNSNumberValueTransformer.m */, + A58CC11EAF63E6D048C0847FC6B0BFFA /* MPCATransform3DToNSDictionaryValueTransformer.m */, + 98D4131908AE7A20F9333418F938CB1C /* MPCGAffineTransformToNSDictionaryValueTransformer.m */, + 512D4BCAED57A02F1B8B193A793FF8E9 /* MPCGColorRefToNSStringValueTransformer.m */, + 825631778F26F9900D0ECE9F8E48FE8D /* MPCGPointToNSDictionaryValueTransformer.m */, + 0290E0A6569BD0FBED77C2F2EE92A9BC /* MPCGRectToNSDictionaryValueTransformer.m */, + 90627B78BCBF044B18FD2C44F93E327C /* MPCGSizeToNSDictionaryValueTransformer.m */, + 0A0DB21BDF7E9B679AF3BA1E9BBEEECB /* MPClassDescription.h */, + B3A2EC70B9D72682673923E594428352 /* MPClassDescription.m */, + 850AA3A3758B223E2A368B0C4150E6D4 /* MPDesignerEventBindingMessage.h */, + B239D2F597F82A0A72EBD2CEFB828E43 /* MPDesignerEventBindingRequestMesssage.m */, + 72542460AC948DA8E4DCAC875F1BB596 /* MPDesignerEventBindingResponseMesssage.m */, + 1AD154AFE252EBDB35402851C1D92213 /* MPDesignerSessionCollection.h */, + C91230158E956866DAD8522E3D166D36 /* MPDesignerTrackMessage.m */, + 4B065F8367154F6B056EA7251BD65D1B /* MPEnumDescription.h */, + 12636F6ECA86EAA8B6A454480AE89A34 /* MPEnumDescription.m */, + 063B2C733B1887F1BDC833AD5F17FD4F /* MPEventBinding.h */, + E57E9E509F0CAD6C1B003EE3E26264FF /* MPEventBinding.m */, + 55649A64FA930E5CFD3AD6693E947D84 /* MPFoundation.h */, + 784EF79DC3D6331442E885CE4BE2FA7A /* MPLogger.h */, + FD0BFDA381D45CAE312DD81010E3DBE7 /* MPMiniNotification.h */, + 633E2AEB882FE7FD0AF6D9E1E10D6277 /* MPMiniNotification.m */, + 05C9EA836E92AFB59436EC83FBF0402E /* MPNetwork.h */, + 080DC77574F362D808C4BC1295315E7B /* MPNetwork.m */, + D31B5FB59EDF59BB514907DB57D6E8B4 /* MPNetworkPrivate.h */, + ED63EE3EC61744E7339A3AEF636C5FD3 /* MPNotification.h */, + 38A6F66AE93021C25C69FE4C266DAC81 /* MPNotification.m */, + B694C8BE1B01C69E7C06E4E4E7649993 /* MPNotificationButton.h */, + E5E9C747F79968077370666202A888C5 /* MPNotificationButton.m */, + 2657F835647A8CF76E80DC55354EAE48 /* MPNotificationViewController.h */, + 8F878A5BEF282075BCFC2102FF1B96D1 /* MPNotificationViewController.m */, + AF06B4CD829B7CD1531C9CCD6D858EC6 /* MPNSAttributedStringToNSDictionaryValueTransformer.m */, + 9503D8480738151CD6B30C2EC9F6298B /* MPNSNumberToCGFloatValueTransformer.m */, + 4D9F44C91360B97BDA3CCECD4797C689 /* MPObjectIdentifierProvider.h */, + C37E375FBF42423790C267884E919475 /* MPObjectIdentityProvider.h */, + 52F014A0CF4CC9A7CF29A701614CD06E /* MPObjectIdentityProvider.m */, + D63D572109015E7A9771C0BF61E41834 /* MPObjectSelector.h */, + 6108BA444669F2D0D833E971534BA42B /* MPObjectSelector.m */, + 70444506DB97ECAE02284C5CCCCB127F /* MPObjectSerializer.h */, + 5F8436DD8EEBE2AFC1FA739C6FBAB2D4 /* MPObjectSerializer.m */, + 71956E54102D9EB87A438106001B5C67 /* MPObjectSerializerConfig.h */, + B53AE7B40D02F7380EA6A04C1923AC70 /* MPObjectSerializerConfig.m */, + D7BC9F0586F0DB0659D1D0A33AC87B72 /* MPObjectSerializerContext.h */, + 24924571A2E4F72E07F127D000B26114 /* MPObjectSerializerContext.m */, + A974035D9F638A6C273C4F9B4CA905E6 /* MPPassThroughValueTransformer.m */, + 3BEFC7939F87E4608FC9EFD214E8E52F /* MPPropertyDescription.h */, + 241FD1D28C18571694D4C55B11F02C85 /* MPPropertyDescription.m */, + 954BB41EB65E3C98BE314CB6CE950D72 /* MPResources.h */, + C8F2BA672D9F764823F1D2D1FD4AB350 /* MPResources.m */, + E291A22751011402F5C8CA49E92B39A8 /* MPSequenceGenerator.h */, + D8CC28A2106D1E76AEEFCE47C3DDFD1E /* MPSequenceGenerator.m */, + C7D838CBAC290780FB26966C8F3D5EAA /* MPSwizzle.h */, + 9CFEDE9D7C47E4416C9E83AEEE504006 /* MPSwizzle.m */, + FC51B12E5421BD0A41628AB20D99D336 /* MPSwizzler.h */, + 2547602FD9CCDE9FF3E18AFA6E229314 /* MPSwizzler.m */, + CA6D7225C521B37136B62E78C1E15F35 /* MPTakeoverNotification.h */, + 0D1DABD0CCB460BD2836CA89EDC3626F /* MPTakeoverNotification.m */, + D4F8734CA45A859AB891CC61B66E61EA /* MPTweak.h */, + 9C241251EF3735A3DD315386B285C6B1 /* MPTweak.m */, + 8A4B0C0EE3AAF1C9C7D6EB2D37D5A90E /* MPTweakInline.h */, + E229782F104BF790213642B67F1D4298 /* MPTweakInline.m */, + 48539D92FDB1B300D114BD020BE6ACB7 /* MPTweakInlineInternal.h */, + B20D7FED2792635CABB46680D6741F76 /* MPTweakStore.h */, + BAABAFBD61155787287A6E3F86C660E2 /* MPTweakStore.m */, + BD912927F984990CF05ABAC888D505D8 /* MPTypeDescription.h */, + 9895AFE3F53886DB084616C99AF0CC3F /* MPTypeDescription.m */, + 9FA16B40234415D66ADF8105DDEB6A8A /* MPUIColorToNSStringValueTransformer.m */, + C0924CBF800FD5A4C50440D1FED88E16 /* MPUIControlBinding.h */, + D2A23A77C5B0DE316B102434E5D02499 /* MPUIControlBinding.m */, + D5CD044CF8DF9B720E9AD425F3514EDE /* MPUIEdgeInsetsToNSDictionaryValueTransformer.m */, + 0300B9B7936A50EB3E0BA7D6C75451ED /* MPUIFontToNSDictionaryValueTransformer.m */, + ECBE74E38586159316E7313D4EBC4735 /* MPUIImageToNSDictionaryValueTransformer.m */, + BD1951D684625282975D3F4437B3B0E4 /* MPUITableViewBinding.h */, + 9A422E780D2A48005F4F238B2F4314A7 /* MPUITableViewBinding.m */, + 27FA1D8D74ADF5333CB52B04B782FAA6 /* MPValueTransformers.h */, + FBF2D10E12078A447678BAB98302318D /* MPVariant.h */, + E4CAA50D3A6C55998865A4C20040411F /* MPVariant.m */, + F612930E064B62CA915CD1A190E5DC87 /* MPWebSocket.h */, + 3C75C97AA13697777772CAB4492A7255 /* MPWebSocket.m */, + 5BC312EB8AE9323065CAB166BA652380 /* NSInvocation+MPHelpers.h */, + 95B96F308485E749A29BE0EB435DF0C7 /* NSInvocation+MPHelpers.m */, + 2B080F3D4FBEAB9071026C65F014E45C /* NSNotificationCenter+AutomaticTracks.h */, + 5AA4D3364D2E311DF0E1F0ED9663E5B1 /* NSNotificationCenter+AutomaticTracks.m */, + DEBB170C91EE81BE8E9486A6750B93BC /* UIApplication+AutomaticTracks.h */, + 88770AB53F75BA220C5AF6AD05D09609 /* UIApplication+AutomaticTracks.m */, + 00BD4E4891029D1E20ACD52DC0AC66E6 /* UIColor+MPColor.h */, + A073ABE9E4CD983137859E96A2B3702F /* UIColor+MPColor.m */, + 52BE2F2678A867E81ADC0A16A3AB5874 /* UIImage+MPAverageColor.h */, + D1A7BC937C51BDB7D5E26C58128F255E /* UIImage+MPAverageColor.m */, + FFE56D520884524B65571806182F7007 /* UIImage+MPImageEffects.h */, + FD44723997BA78DD92FB2A9E8041E5D1 /* UIImage+MPImageEffects.m */, + 4BEA3E4E9A87A6A7D987F182380F6A7C /* UIView+MPHelpers.h */, + FED696F5A118A83AD0A304EA058D5F1C /* UIView+MPHelpers.m */, + E94F5B43228D25F36A6514EF0F8649F0 /* UIViewController+AutomaticTracks.h */, + 732A2D1E7852F17E649284435CACCD0B /* UIViewController+AutomaticTracks.m */, + 2AA5DA946C15C05C0492C4CDAC5DE5BC /* Resources */, + E8460A2AC5B5E01690D348C61BD4D8B1 /* Support Files */, + ); + name = Mixpanel; + path = Mixpanel; + sourceTree = ""; + }; + E8460A2AC5B5E01690D348C61BD4D8B1 /* Support Files */ = { + isa = PBXGroup; + children = ( + D5036A539FA57EC1EDBAF1782E680024 /* Mixpanel.xcconfig */, + 0C5C9B441827E4EFDB3650259021E644 /* Mixpanel-dummy.m */, + BD71BB594B30DA593E2833783E5584F6 /* Mixpanel-prefix.pch */, + ); + name = "Support Files"; + path = "../Target Support Files/Mixpanel"; + sourceTree = ""; + }; + F1278DC2C6644DD4A148E0DB046742E0 /* Pods-T7ChickenTests */ = { + isa = PBXGroup; + children = ( + 612524F64E44A0B408F6589091F9F31D /* Pods-T7ChickenTests-acknowledgements.markdown */, + C40DABBE05D5A548752435CACC2A27BA /* Pods-T7ChickenTests-acknowledgements.plist */, + 44E01A6EE48A0F19B91800A4FEAB622A /* Pods-T7ChickenTests-dummy.m */, + 445A64A188D0BE3FCA97DCD96FC0FFE9 /* Pods-T7ChickenTests-frameworks.sh */, + 314BF80596CA1F6CF4FA2B454D6333DC /* Pods-T7ChickenTests-resources.sh */, + EEBD63BF04C8534B06212C52EA5F1389 /* Pods-T7ChickenTests.debug.xcconfig */, + 5737663F2FB8377664A80ED06E7B1CAF /* Pods-T7ChickenTests.release.xcconfig */, + ); + name = "Pods-T7ChickenTests"; + path = "Target Support Files/Pods-T7ChickenTests"; + sourceTree = ""; + }; + F4CDA5FA9197A41E0081E84F932906EB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3652949AF6080C4B376EE8EE5F2BCFDC /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 66DAF109A1101861D3F4B5FB595CC341 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A6CE9F8280BEB215D3A44F6440D0685C /* _MPTweakBindObserver.h in Headers */, + 87D1D1677E78E1DDB077A356D93928A4 /* AutomaticEvents.h in Headers */, + 94C82BCE756B481DFC7C132AD0FC0610 /* AutomaticTracksConstants.h in Headers */, + 9E1A820AA82A013DB6FBB9346B86B97A /* Mixpanel+AutomaticTracks.h in Headers */, + ECB8597C5FF13C74F1E911312DD9B061 /* Mixpanel.h in Headers */, + CA58F26DE44409353126E957F22CACEF /* MixpanelExceptionHandler.h in Headers */, + 5DAAAF3584CFD045CCA82FF1AFE85A15 /* MixpanelPeople.h in Headers */, + C4B4B6BEAFA136B0E38963824A96728C /* MixpanelPeoplePrivate.h in Headers */, + 9A44E71E987748D9F59F81C6ED9400DA /* MixpanelPrivate.h in Headers */, + 2EE13CF048A199943D2DDAF64AE17BBD /* MPAbstractABTestDesignerMessage.h in Headers */, + 7E44A7D0CAF7DDF78096A8251D1440D0 /* MPABTestDesignerChangeRequestMessage.h in Headers */, + 5E45229D0D16AF9DCBA5BD2E9C83D46E /* MPABTestDesignerChangeResponseMessage.h in Headers */, + D8A362A92CCFF852DCF8C90A52CB5344 /* MPABTestDesignerClearRequestMessage.h in Headers */, + EF0FB38F4552636AF7623B4565B8C0E2 /* MPABTestDesignerClearResponseMessage.h in Headers */, + B580DE0C97CB869D5FDB979C03B363EF /* MPABTestDesignerConnection.h in Headers */, + 782B20406FEB0C04DF5CA0BD69765471 /* MPABTestDesignerDeviceInfoRequestMessage.h in Headers */, + 37772BA5872DE168A3AB6636AEF22EBA /* MPABTestDesignerDeviceInfoResponseMessage.h in Headers */, + 8FAD1B42EBB691CDA835DC724C2882B6 /* MPABTestDesignerDisconnectMessage.h in Headers */, + 4A0E8099B4514122C292FCE7330F05ED /* MPABTestDesignerMessage.h in Headers */, + 868F5819ED156770487F5EF1D393A02F /* MPABTestDesignerSnapshotRequestMessage.h in Headers */, + 453EE0A47359BCC2B10F3C07139158CD /* MPABTestDesignerSnapshotResponseMessage.h in Headers */, + BD12E1B3FD591F1CD68513C65A4ECA1B /* MPABTestDesignerTweakRequestMessage.h in Headers */, + 274E2E3E3BDF4B87F6D10127DDC9FE11 /* MPABTestDesignerTweakResponseMessage.h in Headers */, + 379F7A271B9E80D3F10C718972C087A5 /* MPApplicationStateSerializer.h in Headers */, + 4ABEA142F069D14B83C2951FC9C59CE3 /* MPClassDescription.h in Headers */, + EC615574B939F8521E203D8A74E71CAE /* MPDesignerEventBindingMessage.h in Headers */, + A624600D79F23645D01F9E1A0FE77CD2 /* MPDesignerSessionCollection.h in Headers */, + 0D38AF6918CAB15CF5C2FE6F9E8EE0A8 /* MPEnumDescription.h in Headers */, + 2DD20F27E9AEAED87E08C576B0920DC6 /* MPEventBinding.h in Headers */, + 575B280E1083912CCEAC601D69321B1F /* MPFoundation.h in Headers */, + 415E8A80F508F24A251B957E00FBB27D /* MPLogger.h in Headers */, + 9B0E237649862B9F8EE50AD624204278 /* MPMiniNotification.h in Headers */, + 4ED9DE269A6355AAD66C0E59C88E19EF /* MPNetwork.h in Headers */, + 66DB9AC523548D7012CDF4848C726A54 /* MPNetworkPrivate.h in Headers */, + 300609334B3680E91DC1636B4EF18079 /* MPNotification.h in Headers */, + 3659CBA339A215E680F56A79451DC9B3 /* MPNotificationButton.h in Headers */, + BEA6A3301CC2DE5AC955AB4008C7E18F /* MPNotificationViewController.h in Headers */, + 2CEF794744F0AD55EABD90922F7B2B6F /* MPObjectIdentifierProvider.h in Headers */, + EC5E0557D5A71E2342869AF26FC6515E /* MPObjectIdentityProvider.h in Headers */, + 77DD0357ACB027E7388F1BBB9626A1EF /* MPObjectSelector.h in Headers */, + 019DA9AFF7AD762DB36C973136CFF95F /* MPObjectSerializer.h in Headers */, + 68A01D2DE9A849C525D932F23EC1AAF4 /* MPObjectSerializerConfig.h in Headers */, + 9BA1C3C5912D4EC5C84E501E842E211C /* MPObjectSerializerContext.h in Headers */, + E2CB6EC6E265778C2D1FC500CA9A1137 /* MPPropertyDescription.h in Headers */, + C486651766A41FB730EB2576C1A158FD /* MPResources.h in Headers */, + 841D5C185810F513B4CFABCEBEF19E94 /* MPSequenceGenerator.h in Headers */, + D95A2B984F9BD7006822A4AE94715C7D /* MPSwizzle.h in Headers */, + FD8DF770C7A271F8124A1E0A8F1AD685 /* MPSwizzler.h in Headers */, + 2DE1661CF0B17C71A03B259CE00FF3DC /* MPTakeoverNotification.h in Headers */, + A53D5382C3C7009196E43AA6E1514A94 /* MPTweak.h in Headers */, + 09EA92DCCB5E280292B8AB2F143D655D /* MPTweakInline.h in Headers */, + 287D3BC697378E3A42674DEFA7F23DDF /* MPTweakInlineInternal.h in Headers */, + A2B1013F5A4DA5A1531E04F1684215A8 /* MPTweakStore.h in Headers */, + 025AA447227A5189FA2658978804F1BF /* MPTypeDescription.h in Headers */, + 1AC1EBF06E787D287687C7872D9CF2C9 /* MPUIControlBinding.h in Headers */, + 8134B5B4507867151F4DF03E46675752 /* MPUITableViewBinding.h in Headers */, + A9CE54CF3B45B21D69D607B4CE14C274 /* MPValueTransformers.h in Headers */, + 5C60C4BF0674C817853B98C0403CAB9A /* MPVariant.h in Headers */, + 10B61E82FA540273C243441633D032A7 /* MPWebSocket.h in Headers */, + 826BE6E699EA1752951FFCDADD42F575 /* NSInvocation+MPHelpers.h in Headers */, + 7735C5E428DE99B271B2F8B6773B94E7 /* NSNotificationCenter+AutomaticTracks.h in Headers */, + 5D38DD68E197A4C89DEB12A4605D7F35 /* UIApplication+AutomaticTracks.h in Headers */, + 04CACD9CC6A5539A6DA398DBEF799C94 /* UIColor+MPColor.h in Headers */, + A823F29982D34744B604B05AF2EC7C7A /* UIImage+MPAverageColor.h in Headers */, + 4335B34A8772E7B137D6077D45C76210 /* UIImage+MPImageEffects.h in Headers */, + 717789C37AE40935A2F2340070236318 /* UIView+MPHelpers.h in Headers */, + 5362EEAABF289A862293F4AD5741901B /* UIViewController+AutomaticTracks.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 16EF4A823B514A7A5E8EA36D4927047B /* Pods-T7Chicken */ = { + isa = PBXNativeTarget; + buildConfigurationList = D3DA597703C520E957220F544CCBB4BB /* Build configuration list for PBXNativeTarget "Pods-T7Chicken" */; + buildPhases = ( + 322EA406927833C9FEAEB72C620ED8C1 /* Sources */, + 7CD8347EE4BC62356DA051D9107A25D5 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + DA51F48B56B114FCC418E4E28E4441EA /* PBXTargetDependency */, + ); + name = "Pods-T7Chicken"; + productName = "Pods-T7Chicken"; + productReference = 2FE46A174CEDFA510C180B5990E4AA8B /* libPods-T7Chicken.a */; + productType = "com.apple.product-type.library.static"; + }; + 324CF72F163C07B591556D1AD2956697 /* Mixpanel */ = { + isa = PBXNativeTarget; + buildConfigurationList = 379E7AEA075F8C37E2AD316A03902EFB /* Build configuration list for PBXNativeTarget "Mixpanel" */; + buildPhases = ( + B6AA8FDD2FD643F9E5A8E0B6FB8591AD /* Sources */, + D76573A1CC6E2E947AFDFB5164E98C9A /* Frameworks */, + 66DAF109A1101861D3F4B5FB595CC341 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Mixpanel; + productName = Mixpanel; + productReference = 0CD4FFFDF97BD7A88BF9B1F1339F338E /* libMixpanel.a */; + productType = "com.apple.product-type.library.static"; + }; + C6C7D739FF03EE76A828AA50C144B79E /* Pods-T7ChickenTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F1ADD22D20D5C77ACEE3E75C1CF7C599 /* Build configuration list for PBXNativeTarget "Pods-T7ChickenTests" */; + buildPhases = ( + 404BCCA562484B60AA9197F7E81D59A0 /* Sources */, + D560D228B3FE26E31B880F9ACF54462F /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-T7ChickenTests"; + productName = "Pods-T7ChickenTests"; + productReference = 2D5527C0C221D173E1371D423908996E /* libPods-T7ChickenTests.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = 08C439CEA415A7213A0B7C18F9D15B96 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 324CF72F163C07B591556D1AD2956697 /* Mixpanel */, + 16EF4A823B514A7A5E8EA36D4927047B /* Pods-T7Chicken */, + C6C7D739FF03EE76A828AA50C144B79E /* Pods-T7ChickenTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 322EA406927833C9FEAEB72C620ED8C1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 30A722D0808CA7EEEF59DD1B890AC85C /* Pods-T7Chicken-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 404BCCA562484B60AA9197F7E81D59A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6328D3E9AD2CCF920DE8F8FADF54C919 /* Pods-T7ChickenTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B6AA8FDD2FD643F9E5A8E0B6FB8591AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3E9A16E4E391F1A35EBF0FB8BBF7E1A6 /* _MPTweakBindObserver.m in Sources */, + 781E75D0443B26CCC9CFF2B1F43E99C3 /* AutomaticEvents.m in Sources */, + E6A85C69C316D8D063FDC1E1A777A9A1 /* Mixpanel+AutomaticTracks.m in Sources */, + 4F58DB168DF83438C7066B42A8CD66C6 /* Mixpanel-dummy.m in Sources */, + 95D8A5900E58012F0088E5DF70FFD9EA /* Mixpanel.m in Sources */, + E54F09E224971D710E7FCD0590750BBC /* MixpanelExceptionHandler.m in Sources */, + EF407E62499417FDA55484AEF41E1114 /* MixpanelPeople.m in Sources */, + 1B8618631BC84715AAAADF61BF5E376D /* MPAbstractABTestDesignerMessage.m in Sources */, + 335D56D6BAC467A9F70ADB024C761779 /* MPABTestDesignerChangeRequestMessage.m in Sources */, + 938E8C3E347896268CFB4BCA6C1BB31E /* MPABTestDesignerChangeResponseMessage.m in Sources */, + 975575EE716FD4E7CC2EB3D66B768528 /* MPABTestDesignerClearRequestMessage.m in Sources */, + CD554242CB48F0284E67F5CF954A22F8 /* MPABTestDesignerClearResponseMessage.m in Sources */, + B9BFF3B4ED344EFDFD3366BBA9A0A7A0 /* MPABTestDesignerConnection.m in Sources */, + 47980FF6FA059D0BE1A2A888EC4A0CD0 /* MPABTestDesignerDeviceInfoRequestMessage.m in Sources */, + D581B576DD9A007A620B6BA890C3EF5B /* MPABTestDesignerDeviceInfoResponseMessage.m in Sources */, + 4E14CAF48DFBC64805467C3B1CBFA8E8 /* MPABTestDesignerDisconnectMessage.m in Sources */, + 607DC86D27849C98095C91B240BA9068 /* MPABTestDesignerSnapshotRequestMessage.m in Sources */, + A8AFD6DBD6A48A7FAF10EF9485942937 /* MPABTestDesignerSnapshotResponseMessage.m in Sources */, + F348F490291E3FC8EBBBBE75F402A502 /* MPABTestDesignerTweakRequestMessage.m in Sources */, + 74ED21F48A5B4802FFABE793EBA98AF9 /* MPABTestDesignerTweakResponseMessage.m in Sources */, + 7D4B7992CDE6D56FC20446DADD810E92 /* MPApplicationStateSerializer.m in Sources */, + B7FE9F2196535479F4347EB90241B86F /* MPBOOLToNSNumberValueTransformer.m in Sources */, + A12281415A955A0417BE173564D080BE /* MPCATransform3DToNSDictionaryValueTransformer.m in Sources */, + 0515EF2970C61A6A740FBA30A3A032C0 /* MPCGAffineTransformToNSDictionaryValueTransformer.m in Sources */, + D4452A116DEE2D75EF4E8941DA0C24C1 /* MPCGColorRefToNSStringValueTransformer.m in Sources */, + EF17E92F1DA32380830FBD4A7259505F /* MPCGPointToNSDictionaryValueTransformer.m in Sources */, + 919A90D376CC7BD9DC6D7B4A14AB1F90 /* MPCGRectToNSDictionaryValueTransformer.m in Sources */, + 0C38F12D9D89216965A19E14DAB5B06B /* MPCGSizeToNSDictionaryValueTransformer.m in Sources */, + B4EF9487F2894147C046611645A478F3 /* MPClassDescription.m in Sources */, + CE47032AEB24B03F7861F7C3CA5F2EA7 /* MPDesignerEventBindingRequestMesssage.m in Sources */, + 0323DA0FBAAE7344AFD5FFFDD3B959E6 /* MPDesignerEventBindingResponseMesssage.m in Sources */, + 8C338B9AF1EE6A2A408179A8F26272F7 /* MPDesignerTrackMessage.m in Sources */, + EE4AC3A7F7F43C328169A10179F838B2 /* MPEnumDescription.m in Sources */, + 1EE5BE2F1FC19AFBAD5C6EC787348BA8 /* MPEventBinding.m in Sources */, + EACD728FD7A71D3F1FC92ADA091DE0DB /* MPMiniNotification.m in Sources */, + 6226A885414058D0422A4108F54E927F /* MPNetwork.m in Sources */, + 3F4C2E18C33CB86B9E761E014E74C3A7 /* MPNotification.m in Sources */, + 996B0FC2FEE5D45591E3FD419ED52408 /* MPNotificationButton.m in Sources */, + B07C9CC6C6604D24805EF6E1D6ED4121 /* MPNotificationViewController.m in Sources */, + 69814D03B24DFFB3DC019DB8C0785F69 /* MPNSAttributedStringToNSDictionaryValueTransformer.m in Sources */, + 73C90F7FCECAFA11C5C5FBB6308D94FE /* MPNSNumberToCGFloatValueTransformer.m in Sources */, + F26144BA5BDD712D6BFAB84F28D33B38 /* MPObjectIdentityProvider.m in Sources */, + FF0E57706E1E76D51730624F22DCFF2E /* MPObjectSelector.m in Sources */, + 372ED8281BE2261F2DED2599B2C82684 /* MPObjectSerializer.m in Sources */, + 5574079F1EE65CDE2F3A0AC652CE407B /* MPObjectSerializerConfig.m in Sources */, + FC9689DFF612C6C2D6A12A9AB5559676 /* MPObjectSerializerContext.m in Sources */, + 8750DCA1D1830C4D0D124AAC01215FD7 /* MPPassThroughValueTransformer.m in Sources */, + 6E795DCE13FE0DADDD03534CBD7B7D66 /* MPPropertyDescription.m in Sources */, + 039D17EA329CD07E9CA00426C22C11D1 /* MPResources.m in Sources */, + 5457A607B063A5DB74CE0025FAD2CAB3 /* MPSequenceGenerator.m in Sources */, + 473004B25142F65D28BD66BF4A937CF8 /* MPSwizzle.m in Sources */, + 32A4ED58EA2C3935C1E38C6963D7A652 /* MPSwizzler.m in Sources */, + C9043E973E99D45F1C61088E377A0631 /* MPTakeoverNotification.m in Sources */, + 6AD15CC328CA1ABC324D510DA805016B /* MPTweak.m in Sources */, + 65BEC4F06937A00C182C85DFD0C4F6CD /* MPTweakInline.m in Sources */, + 8D7FA0C6B2A39556F7AF4E8AF9601855 /* MPTweakStore.m in Sources */, + 50B45EEC148650DB4A0DCFFD17E1FC85 /* MPTypeDescription.m in Sources */, + 228E840F38959CA09A83D012B15938AB /* MPUIColorToNSStringValueTransformer.m in Sources */, + 1AFB6959F3D81DB2F0B7BB93E2F808DD /* MPUIControlBinding.m in Sources */, + 102EFF1FE40C7859E56C15830B87B00F /* MPUIEdgeInsetsToNSDictionaryValueTransformer.m in Sources */, + 971FCC4664D3CCD1475934290AB5C03E /* MPUIFontToNSDictionaryValueTransformer.m in Sources */, + 8E7827100ED66EA4595A78FE8C05F1CF /* MPUIImageToNSDictionaryValueTransformer.m in Sources */, + 22902EAD35AA212A60CFF896511665E0 /* MPUITableViewBinding.m in Sources */, + EED1C8967352BC1E48ECFED69EA2AEFB /* MPVariant.m in Sources */, + 7EA96ACDB0CF35334A7FA2AAB44ECD42 /* MPWebSocket.m in Sources */, + B6A4F39F19E90CDD3907C6C0FE744887 /* NSInvocation+MPHelpers.m in Sources */, + D3EBF2524DDEE49B6BED770B1987CA16 /* NSNotificationCenter+AutomaticTracks.m in Sources */, + 61C1CD4E850C9CF008209D3AAD24C61D /* UIApplication+AutomaticTracks.m in Sources */, + 9002AEFAE8E5B351E0FD4347B904C665 /* UIColor+MPColor.m in Sources */, + 1D520D15E7AB72946EED04C6AB498080 /* UIImage+MPAverageColor.m in Sources */, + 0639B11DA968E39D41578ED8227DD82B /* UIImage+MPImageEffects.m in Sources */, + 5C8BBF47FE3D699A1953C414EFEB53B7 /* UIView+MPHelpers.m in Sources */, + EC9FAA65E5931EDA4DBD452612B60E78 /* UIViewController+AutomaticTracks.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + DA51F48B56B114FCC418E4E28E4441EA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Mixpanel; + target = 324CF72F163C07B591556D1AD2956697 /* Mixpanel */; + targetProxy = 7AD899F0E482F35F29484E72789FFDAD /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 29FA04A7E69761344621FC3AB716B6C1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 717C2ABD8C02755E8FB4E7192612518B /* Pods-T7Chicken.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 34FE9531DA9AF2820790339988D5FF41 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + 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_OBJC_ROOT_CLASS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7F50D7D83D8258F53A57EFBA71F8BFB5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5737663F2FB8377664A80ED06E7B1CAF /* Pods-T7ChickenTests.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 90D9E83C1D5DD7C70F5B761192DEBDFA /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EEBD63BF04C8534B06212C52EA5F1389 /* Pods-T7ChickenTests.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + BC85CC937ECF9D39CD4B44B142A9D7BE /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D5036A539FA57EC1EDBAF1782E680024 /* Mixpanel.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Mixpanel/Mixpanel-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + C104F7F091290C3D1E248192F07FE689 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + 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_OBJC_ROOT_CLASS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + ONLY_ACTIVE_ARCH = YES; + PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + D9F59519AA64C0FEB48B225B6B7B3EFC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 756BAB8FAE467B21D41FB4F4FB730551 /* Pods-T7Chicken.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MACH_O_TYPE = staticlib; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Release; + }; + F9ECF8ED8B71FD3619CC5ADD33E41AEC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D5036A539FA57EC1EDBAF1782E680024 /* Mixpanel.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREFIX_HEADER = "Target Support Files/Mixpanel/Mixpanel-prefix.pch"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PRIVATE_HEADERS_FOLDER_PATH = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + PUBLIC_HEADERS_FOLDER_PATH = ""; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C104F7F091290C3D1E248192F07FE689 /* Debug */, + 34FE9531DA9AF2820790339988D5FF41 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 379E7AEA075F8C37E2AD316A03902EFB /* Build configuration list for PBXNativeTarget "Mixpanel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9ECF8ED8B71FD3619CC5ADD33E41AEC /* Debug */, + BC85CC937ECF9D39CD4B44B142A9D7BE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D3DA597703C520E957220F544CCBB4BB /* Build configuration list for PBXNativeTarget "Pods-T7Chicken" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 29FA04A7E69761344621FC3AB716B6C1 /* Debug */, + D9F59519AA64C0FEB48B225B6B7B3EFC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F1ADD22D20D5C77ACEE3E75C1CF7C599 /* Build configuration list for PBXNativeTarget "Pods-T7ChickenTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 90D9E83C1D5DD7C70F5B761192DEBDFA /* Debug */, + 7F50D7D83D8258F53A57EFBA71F8BFB5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/ios/Pods/Target Support Files/Mixpanel/Mixpanel-dummy.m b/ios/Pods/Target Support Files/Mixpanel/Mixpanel-dummy.m new file mode 100644 index 0000000..c3c00bd --- /dev/null +++ b/ios/Pods/Target Support Files/Mixpanel/Mixpanel-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Mixpanel : NSObject +@end +@implementation PodsDummy_Mixpanel +@end diff --git a/ios/Pods/Target Support Files/Mixpanel/Mixpanel-prefix.pch b/ios/Pods/Target Support Files/Mixpanel/Mixpanel-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/ios/Pods/Target Support Files/Mixpanel/Mixpanel-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/Mixpanel/Mixpanel.xcconfig b/ios/Pods/Target Support Files/Mixpanel/Mixpanel.xcconfig new file mode 100644 index 0000000..1b868da --- /dev/null +++ b/ios/Pods/Target Support Files/Mixpanel/Mixpanel.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Mixpanel +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Mixpanel" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Mixpanel" +OTHER_LDFLAGS = -l"icucore" -framework "Accelerate" -framework "CoreGraphics" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "SystemConfiguration" -framework "UIKit" +PODS_BUILD_DIR = $BUILD_DIR +PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/Mixpanel +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-acknowledgements.markdown b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-acknowledgements.markdown new file mode 100644 index 0000000..e8f4b03 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-acknowledgements.markdown @@ -0,0 +1,266 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## Mixpanel + +Copyright 2013 Mixpanel, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this work except in compliance with the License. +You may obtain a copy of the License below, or 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. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +================================================================================ + +_MPTweakBindObserver.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +_MPTweakBindObserver.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweak.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweak.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInline.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInline.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInlineInternal.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakStore.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakStore.m Copyright (c) 2014, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation + +and/or other materials provided with the distribution. + +* Neither the name Facebook nor the names of its contributors may be used to + +endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Generated by CocoaPods - https://cocoapods.org diff --git a/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-acknowledgements.plist b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-acknowledgements.plist new file mode 100644 index 0000000..3cec2cf --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-acknowledgements.plist @@ -0,0 +1,298 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright 2013 Mixpanel, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this work except in compliance with the License. +You may obtain a copy of the License below, or 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. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +================================================================================ + +_MPTweakBindObserver.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +_MPTweakBindObserver.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweak.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweak.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInline.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInline.m Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakInlineInternal.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakStore.h Copyright (c) 2014, Facebook, Inc. All rights reserved. +MPTweakStore.m Copyright (c) 2014, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation + +and/or other materials provided with the distribution. + +* Neither the name Facebook nor the names of its contributors may be used to + +endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + License + Apache License, Version 2.0 + Title + Mixpanel + 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-T7Chicken/Pods-T7Chicken-dummy.m b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-dummy.m new file mode 100644 index 0000000..4f8f778 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_T7Chicken : NSObject +@end +@implementation PodsDummy_Pods_T7Chicken +@end diff --git a/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-frameworks.sh b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-frameworks.sh new file mode 100755 index 0000000..0f29f13 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-frameworks.sh @@ -0,0 +1,92 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +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 dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --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 +} + +# 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 file + archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" + stripped="" + for arch in $archs; do + if ! [[ "${VALID_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 +} + +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-resources.sh b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-resources.sh new file mode 100755 index 0000000..97a947b --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-resources.sh @@ -0,0 +1,140 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +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}" + 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}" + 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}" + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "$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\"" + 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\"" + 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\"" + 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" + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_resource "Mixpanel/Mixpanel/Images/MPArrowLeft.png" + install_resource "Mixpanel/Mixpanel/Images/MPArrowLeft@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPArrowRight.png" + install_resource "Mixpanel/Mixpanel/Images/MPArrowRight@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPCheckmark.png" + install_resource "Mixpanel/Mixpanel/Images/MPCheckmark@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPCloseButton.png" + install_resource "Mixpanel/Mixpanel/Images/MPCloseButton@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPCloseButton@3x.png" + install_resource "Mixpanel/Mixpanel/Images/MPDismissKeyboard.png" + install_resource "Mixpanel/Mixpanel/Images/MPDismissKeyboard@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPLogo.png" + install_resource "Mixpanel/Mixpanel/Images/MPLogo@2x.png" + install_resource "Mixpanel/Mixpanel/Images/placeholder-image.png" + install_resource "Mixpanel/Mixpanel/MPTakeoverNotificationViewController~ipad.xib" + install_resource "Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphonelandscape.xib" + install_resource "Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphoneportrait.xib" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_resource "Mixpanel/Mixpanel/Images/MPArrowLeft.png" + install_resource "Mixpanel/Mixpanel/Images/MPArrowLeft@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPArrowRight.png" + install_resource "Mixpanel/Mixpanel/Images/MPArrowRight@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPCheckmark.png" + install_resource "Mixpanel/Mixpanel/Images/MPCheckmark@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPCloseButton.png" + install_resource "Mixpanel/Mixpanel/Images/MPCloseButton@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPCloseButton@3x.png" + install_resource "Mixpanel/Mixpanel/Images/MPDismissKeyboard.png" + install_resource "Mixpanel/Mixpanel/Images/MPDismissKeyboard@2x.png" + install_resource "Mixpanel/Mixpanel/Images/MPLogo.png" + install_resource "Mixpanel/Mixpanel/Images/MPLogo@2x.png" + install_resource "Mixpanel/Mixpanel/Images/placeholder-image.png" + install_resource "Mixpanel/Mixpanel/MPTakeoverNotificationViewController~ipad.xib" + install_resource "Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphonelandscape.xib" + install_resource "Mixpanel/Mixpanel/MPTakeoverNotificationViewController~iphoneportrait.xib" +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" + + 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}" +fi diff --git a/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken.debug.xcconfig b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken.debug.xcconfig new file mode 100644 index 0000000..6a873a9 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken.debug.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Mixpanel" +LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Mixpanel" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Mixpanel" +OTHER_LDFLAGS = $(inherited) -ObjC -l"Mixpanel" -l"icucore" -framework "Accelerate" -framework "CoreGraphics" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "SystemConfiguration" -framework "UIKit" +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-T7Chicken/Pods-T7Chicken.release.xcconfig b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken.release.xcconfig new file mode 100644 index 0000000..6a873a9 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken.release.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Mixpanel" +LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Mixpanel" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Mixpanel" +OTHER_LDFLAGS = $(inherited) -ObjC -l"Mixpanel" -l"icucore" -framework "Accelerate" -framework "CoreGraphics" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "SystemConfiguration" -framework "UIKit" +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-T7ChickenTests/Pods-T7ChickenTests-acknowledgements.markdown b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-acknowledgements.markdown new file mode 100644 index 0000000..102af75 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-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-T7ChickenTests/Pods-T7ChickenTests-acknowledgements.plist b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-acknowledgements.plist new file mode 100644 index 0000000..7acbad1 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-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-T7ChickenTests/Pods-T7ChickenTests-dummy.m b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-dummy.m new file mode 100644 index 0000000..19141f8 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_T7ChickenTests : NSObject +@end +@implementation PodsDummy_Pods_T7ChickenTests +@end diff --git a/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-frameworks.sh b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-frameworks.sh new file mode 100755 index 0000000..0f29f13 --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-frameworks.sh @@ -0,0 +1,92 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +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 dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --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 +} + +# 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 file + archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" + stripped="" + for arch in $archs; do + if ! [[ "${VALID_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 +} + +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-resources.sh b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-resources.sh new file mode 100755 index 0000000..aed060f --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-resources.sh @@ -0,0 +1,102 @@ +#!/bin/sh +set -e + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +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}" + 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}" + 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}" + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "$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\"" + 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\"" + 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\"" + 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" + 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" + + 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}" +fi diff --git a/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests.debug.xcconfig b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests.debug.xcconfig new file mode 100644 index 0000000..f958bcc --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests.debug.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Mixpanel" +LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Mixpanel" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Mixpanel" +OTHER_LDFLAGS = $(inherited) -ObjC -l"icucore" -framework "Accelerate" -framework "CoreGraphics" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "SystemConfiguration" -framework "UIKit" +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-T7ChickenTests/Pods-T7ChickenTests.release.xcconfig b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests.release.xcconfig new file mode 100644 index 0000000..f958bcc --- /dev/null +++ b/ios/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests.release.xcconfig @@ -0,0 +1,9 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Mixpanel" +LIBRARY_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Mixpanel" +OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Mixpanel" +OTHER_LDFLAGS = $(inherited) -ObjC -l"icucore" -framework "Accelerate" -framework "CoreGraphics" -framework "CoreTelephony" -framework "Foundation" -framework "QuartzCore" -framework "SystemConfiguration" -framework "UIKit" +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/T7Chicken-tvOS/Info.plist b/ios/T7Chicken-tvOS/Info.plist new file mode 100644 index 0000000..2fb6a11 --- /dev/null +++ b/ios/T7Chicken-tvOS/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + + NSAppTransportSecurity + + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + + diff --git a/ios/t7ChickenNativeTests/Info.plist b/ios/T7Chicken-tvOSTests/Info.plist similarity index 93% rename from ios/t7ChickenNativeTests/Info.plist rename to ios/T7Chicken-tvOSTests/Info.plist index 4cfb33f..886825c 100644 --- a/ios/t7ChickenNativeTests/Info.plist +++ b/ios/T7Chicken-tvOSTests/Info.plist @@ -20,7 +20,5 @@ ???? CFBundleVersion 1 - LSApplicationCategoryType - diff --git a/ios/t7ChickenNative.xcodeproj/project.pbxproj b/ios/T7Chicken.xcodeproj/project.pbxproj similarity index 50% rename from ios/t7ChickenNative.xcodeproj/project.pbxproj rename to ios/T7Chicken.xcodeproj/project.pbxproj index 66d6d23..f2cba8a 100644 --- a/ios/t7ChickenNative.xcodeproj/project.pbxproj +++ b/ios/T7Chicken.xcodeproj/project.pbxproj @@ -5,13 +5,15 @@ }; objectVersion = 46; objects = { + /* Begin PBXBuildFile section */ 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; - 00E356F31AD99517003FC87E /* t7ChickenNativeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* t7ChickenNativeTests.m */; }; + 00E356F31AD99517003FC87E /* T7ChickenTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* T7ChickenTests.m */; }; + 063719DD1D1A420588081B9A /* Exo2-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6F9FD97F4E8049FE8616E8E1 /* Exo2-MediumItalic.ttf */; }; 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; @@ -21,26 +23,42 @@ 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 18CF69363163450191835AD3 /* Exo2-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3EFA7E1BF14E4EAFA55B8827 /* Exo2-ExtraBoldItalic.ttf */; }; + 199DBB9A1ACA41498E08A821 /* Exo2-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7F0CE15B5A67418184A2FCC0 /* Exo2-SemiBold.ttf */; }; + 1AD0F7797D764048828D8532 /* libRNDeviceInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C579DE6BA16044EC923F872D /* libRNDeviceInfo.a */; }; + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; + 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; }; + 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; }; + 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; }; + 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; }; + 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; }; + 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; }; + 2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3EA31DF850E9000B6D8A /* libReact.a */; }; + 2DCD954D1E0B4F2C00145EB5 /* T7ChickenTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* T7ChickenTests.m */; }; + 357F1B52A6AF43E38A4B78C5 /* Exo2-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 48FA2192E200418382FFCA95 /* Exo2-ExtraLight.ttf */; }; + 43EC743E4F6245BCB54DFFB2 /* Exo2-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D8F8344664F748AD939932FA /* Exo2-ExtraBold.ttf */; }; + 50020C498A684F23BEC89564 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D80924C15DB4E219923A9A5 /* libBVLinearGradient.a */; }; + 519FAB44F7A620D5726D1B3D /* libPods-T7Chicken.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C26E2B37A1FB04AE7C6C4D44 /* libPods-T7Chicken.a */; }; + 591E0BE4AA57417EB6EB331F /* Exo2-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DAA724B229B342BDA77AFD9A /* Exo2-Bold.ttf */; }; + 5AA816B455C14E33837A95C2 /* Exo2-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 25DE771A847E44149B7A3DB7 /* Exo2-Medium.ttf */; }; 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; + 741EC7F379D34C70A453DAA0 /* Exo2-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 97BCC27F18914094B252D2D1 /* Exo2-Italic.ttf */; }; + 74FD6E38D06E4DE8B8952B96 /* Exo2-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AB2C4934DB8E4797AE896306 /* Exo2-LightItalic.ttf */; }; + 7B896CBBA00942C4A758D13F /* Exo2-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0D3968EE547E4CFCB625F412 /* Exo2-Regular.ttf */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; - CB61777D36DA4614B14554EA /* Exo2-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7B001D53816447C494ABCD01 /* Exo2-Black.ttf */; }; - FAB143B8AD81431080AE4107 /* Exo2-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2006AAF75B0547BE98EC1ADF /* Exo2-BlackItalic.ttf */; }; - A795743AF548431495091976 /* Exo2-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 54C90A6DD41842C592818B80 /* Exo2-Bold.ttf */; }; - 380A3BE44E8A463793BC14AC /* Exo2-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1A22D30D691440AC97E630AF /* Exo2-BoldItalic.ttf */; }; - BEE22F8EE4DE4292AE69B01A /* Exo2-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 89D81ABC709243288FF6CB90 /* Exo2-ExtraBold.ttf */; }; - A8AEEBE721274A018E679C36 /* Exo2-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1D4897648B3249309F64552E /* Exo2-ExtraBoldItalic.ttf */; }; - A5412D5EF7BC4A5A9F4F3534 /* Exo2-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A0A5EABEBA9E407DBC595F8A /* Exo2-ExtraLight.ttf */; }; - 6E0B20D19C8D4051BB1B539C /* Exo2-ExtraLightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5D05C16B36C49C398BD76A2 /* Exo2-ExtraLightItalic.ttf */; }; - F251FE58378C4E7DA753C15C /* Exo2-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 056C73C96A2A49479874EE25 /* Exo2-Italic.ttf */; }; - D6832361ECB74D03ABA06D7A /* Exo2-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D0212DB1EB774D7BAB7ED1EE /* Exo2-Light.ttf */; }; - 6097370F16F5461CA2A09AD7 /* Exo2-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7F564CB758E14B308D6030D6 /* Exo2-LightItalic.ttf */; }; - 6F0213799AD0480DAF606A69 /* Exo2-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B644829F3E2F479CAFE246A4 /* Exo2-Medium.ttf */; }; - C20CA6A6B48B4BBCB1E53D24 /* Exo2-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 114ED7A3D9F447E4913FAE37 /* Exo2-MediumItalic.ttf */; }; - 8A6AA4313508496A8D160DDD /* Exo2-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4DFE2753FAB44CA0AD9A61B9 /* Exo2-Regular.ttf */; }; - 08DD513836224D5997F33841 /* Exo2-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1ED7FC45D5B6497A804BDEFD /* Exo2-SemiBold.ttf */; }; - 8376DA5B27C74746A8B8B37C /* Exo2-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 298EC6FB91444670AAF14CAA /* Exo2-SemiBoldItalic.ttf */; }; - C099C38A275B4E1B9105B6AA /* Exo2-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 28A8BC1FA1EA4B1DB4EE95AE /* Exo2-Thin.ttf */; }; - 34D1FE24A4244868B0163813 /* Exo2-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 15615BC905F0433EB564BE0C /* Exo2-ThinItalic.ttf */; }; + 9256CC59DFBE49DFAD09DA1B /* Exo2-ExtraLightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8C38E78E03754EB6AC4314DD /* Exo2-ExtraLightItalic.ttf */; }; + 9E86350384914572B59FD0B4 /* Exo2-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 213AA45619554BC595DA5E6F /* Exo2-BlackItalic.ttf */; }; + 9ED4AF9C83D616DF99665988 /* libPods-T7ChickenTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 112E06CE9AA3129505392CA0 /* libPods-T7ChickenTests.a */; }; + AA17AE58DB3E40DF8E781920 /* libRNMixpanel.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 50F3DCC6E2A440BCA1BAC466 /* libRNMixpanel.a */; }; + B61BE23868E8442A95AD0CB9 /* Exo2-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E7906C709AB744AFA7A36B79 /* Exo2-Black.ttf */; }; + B7D30E6430584EF6924365AC /* Exo2-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DC37EDF1DB4D43018F520D05 /* Exo2-Light.ttf */; }; + C4636EAB82F34DD0842D5383 /* Exo2-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = DD43283E36BB4267882D8E13 /* Exo2-BoldItalic.ttf */; }; + E0D9C375BAC84BA197167199 /* Exo2-SemiBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2CE07831A39E4070A15DC7A2 /* Exo2-SemiBoldItalic.ttf */; }; + FB025A20E1D94FC4ADF9A958 /* Exo2-ThinItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 834E0208D44148F8B7D2DAC1 /* Exo2-ThinItalic.ttf */; }; + FF186797B5C84F8390B5B0E0 /* Exo2-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 4BE7643AB504452EB2831CC5 /* Exo2-Thin.ttf */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -84,7 +102,7 @@ containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; proxyType = 1; remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = t7ChickenNative; + remoteInfo = T7Chicken; }; 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -107,6 +125,13 @@ remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; remoteInfo = React; }; + 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7; + remoteInfo = "T7Chicken-tvOS"; + }; 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; @@ -226,54 +251,26 @@ remoteGlobalIDString = 58B5119B1A9E6C1200147676; remoteInfo = RCTText; }; - 9D3F6E011E078EB900FDD43E /* PBXContainerItemProxy */ = { + 9D71A6681EE1D68100E3F2FB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + containerPortal = D043742885A24FE99A8BE744 /* RNDeviceInfo.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; - remoteInfo = "RCTImage-tvOS"; + remoteGlobalIDString = DA5891D81BA9A9FC002B4DB2; + remoteInfo = RNDeviceInfo; }; - 9D3F6E051E078EB900FDD43E /* PBXContainerItemProxy */ = { + 9D71A66D1EE1D68100E3F2FB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + containerPortal = A4DB4DB998124376BAF6BFF6 /* RNMixpanel.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 2D2A28471D9B043800D4039D; - remoteInfo = "RCTLinking-tvOS"; + remoteGlobalIDString = FDC64AF01BF1690D0044C1B4; + remoteInfo = RNMixpanel; }; - 9D3F6E091E078EB900FDD43E /* PBXContainerItemProxy */ = { + 9D71A6721EE1D68100E3F2FB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28541D9B044C00D4039D; - remoteInfo = "RCTNetwork-tvOS"; - }; - 9D3F6E0D1E078EB900FDD43E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + containerPortal = 9E210F7F3788465F9208243E /* BVLinearGradient.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 2D2A28611D9B046600D4039D; - remoteInfo = "RCTSettings-tvOS"; - }; - 9D3F6E111E078EB900FDD43E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A287B1D9B048500D4039D; - remoteInfo = "RCTText-tvOS"; - }; - 9D3F6E161E078EB900FDD43E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28881D9B049200D4039D; - remoteInfo = "RCTWebSocket-tvOS"; - }; - 9D3F6E1A1E078EB900FDD43E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 2D2A28131D9B038B00D4039D; - remoteInfo = "React-tvOS"; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = BVLinearGradient; }; /* End PBXContainerItemProxy section */ @@ -284,40 +281,54 @@ 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; - 00E356EE1AD99517003FC87E /* t7ChickenNativeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = t7ChickenNativeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356EE1AD99517003FC87E /* T7ChickenTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = T7ChickenTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 00E356F21AD99517003FC87E /* t7ChickenNativeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = t7ChickenNativeTests.m; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* T7ChickenTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = T7ChickenTests.m; sourceTree = ""; }; + 0D3968EE547E4CFCB625F412 /* Exo2-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-Regular.ttf"; path = "../src/fonts/Exo_2/Exo2-Regular.ttf"; sourceTree = ""; }; + 112E06CE9AA3129505392CA0 /* libPods-T7ChickenTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-T7ChickenTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* t7ChickenNative.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = t7ChickenNative.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = t7ChickenNative/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = t7ChickenNative/AppDelegate.m; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* T7Chicken.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = T7Chicken.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = T7Chicken/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = T7Chicken/AppDelegate.m; sourceTree = ""; }; 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = t7ChickenNative/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = t7ChickenNative/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = t7ChickenNative/main.m; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = T7Chicken/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = T7Chicken/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = T7Chicken/main.m; sourceTree = ""; }; 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 213AA45619554BC595DA5E6F /* Exo2-BlackItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-BlackItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-BlackItalic.ttf"; sourceTree = ""; }; + 25DE771A847E44149B7A3DB7 /* Exo2-Medium.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-Medium.ttf"; path = "../src/fonts/Exo_2/Exo2-Medium.ttf"; sourceTree = ""; }; + 2CE07831A39E4070A15DC7A2 /* Exo2-SemiBoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-SemiBoldItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-SemiBoldItalic.ttf"; sourceTree = ""; }; + 2D02E47B1E0B4A5D006451C7 /* T7Chicken-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "T7Chicken-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D02E4901E0B4A5D006451C7 /* T7Chicken-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "T7Chicken-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3EFA7E1BF14E4EAFA55B8827 /* Exo2-ExtraBoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-ExtraBoldItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraBoldItalic.ttf"; sourceTree = ""; }; + 48FA2192E200418382FFCA95 /* Exo2-ExtraLight.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-ExtraLight.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraLight.ttf"; sourceTree = ""; }; + 4BE7643AB504452EB2831CC5 /* Exo2-Thin.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-Thin.ttf"; path = "../src/fonts/Exo_2/Exo2-Thin.ttf"; sourceTree = ""; }; + 50F3DCC6E2A440BCA1BAC466 /* libRNMixpanel.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNMixpanel.a; sourceTree = ""; }; 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; + 6F9FD97F4E8049FE8616E8E1 /* Exo2-MediumItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-MediumItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-MediumItalic.ttf"; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 7F0CE15B5A67418184A2FCC0 /* Exo2-SemiBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-SemiBold.ttf"; path = "../src/fonts/Exo_2/Exo2-SemiBold.ttf"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; - 7B001D53816447C494ABCD01 /* Exo2-Black.ttf */ = {isa = PBXFileReference; name = "Exo2-Black.ttf"; path = "../src/fonts/Exo_2/Exo2-Black.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 2006AAF75B0547BE98EC1ADF /* Exo2-BlackItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-BlackItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-BlackItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 54C90A6DD41842C592818B80 /* Exo2-Bold.ttf */ = {isa = PBXFileReference; name = "Exo2-Bold.ttf"; path = "../src/fonts/Exo_2/Exo2-Bold.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 1A22D30D691440AC97E630AF /* Exo2-BoldItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-BoldItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-BoldItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 89D81ABC709243288FF6CB90 /* Exo2-ExtraBold.ttf */ = {isa = PBXFileReference; name = "Exo2-ExtraBold.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraBold.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 1D4897648B3249309F64552E /* Exo2-ExtraBoldItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-ExtraBoldItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraBoldItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - A0A5EABEBA9E407DBC595F8A /* Exo2-ExtraLight.ttf */ = {isa = PBXFileReference; name = "Exo2-ExtraLight.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraLight.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - D5D05C16B36C49C398BD76A2 /* Exo2-ExtraLightItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-ExtraLightItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraLightItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 056C73C96A2A49479874EE25 /* Exo2-Italic.ttf */ = {isa = PBXFileReference; name = "Exo2-Italic.ttf"; path = "../src/fonts/Exo_2/Exo2-Italic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - D0212DB1EB774D7BAB7ED1EE /* Exo2-Light.ttf */ = {isa = PBXFileReference; name = "Exo2-Light.ttf"; path = "../src/fonts/Exo_2/Exo2-Light.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 7F564CB758E14B308D6030D6 /* Exo2-LightItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-LightItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-LightItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - B644829F3E2F479CAFE246A4 /* Exo2-Medium.ttf */ = {isa = PBXFileReference; name = "Exo2-Medium.ttf"; path = "../src/fonts/Exo_2/Exo2-Medium.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 114ED7A3D9F447E4913FAE37 /* Exo2-MediumItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-MediumItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-MediumItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 4DFE2753FAB44CA0AD9A61B9 /* Exo2-Regular.ttf */ = {isa = PBXFileReference; name = "Exo2-Regular.ttf"; path = "../src/fonts/Exo_2/Exo2-Regular.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 1ED7FC45D5B6497A804BDEFD /* Exo2-SemiBold.ttf */ = {isa = PBXFileReference; name = "Exo2-SemiBold.ttf"; path = "../src/fonts/Exo_2/Exo2-SemiBold.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 298EC6FB91444670AAF14CAA /* Exo2-SemiBoldItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-SemiBoldItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-SemiBoldItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 28A8BC1FA1EA4B1DB4EE95AE /* Exo2-Thin.ttf */ = {isa = PBXFileReference; name = "Exo2-Thin.ttf"; path = "../src/fonts/Exo_2/Exo2-Thin.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; - 15615BC905F0433EB564BE0C /* Exo2-ThinItalic.ttf */ = {isa = PBXFileReference; name = "Exo2-ThinItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-ThinItalic.ttf"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; + 834E0208D44148F8B7D2DAC1 /* Exo2-ThinItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-ThinItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-ThinItalic.ttf"; sourceTree = ""; }; + 8C38E78E03754EB6AC4314DD /* Exo2-ExtraLightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-ExtraLightItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraLightItalic.ttf"; sourceTree = ""; }; + 97BCC27F18914094B252D2D1 /* Exo2-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-Italic.ttf"; path = "../src/fonts/Exo_2/Exo2-Italic.ttf"; sourceTree = ""; }; + 997C42AB4DA6CFD90DD1D05B /* Pods-T7Chicken.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-T7Chicken.debug.xcconfig"; path = "Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken.debug.xcconfig"; sourceTree = ""; }; + 9984E2C6B9D8C8CE74E5BB32 /* Pods-T7Chicken.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-T7Chicken.release.xcconfig"; path = "Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken.release.xcconfig"; sourceTree = ""; }; + 9D80924C15DB4E219923A9A5 /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = ""; }; + 9E210F7F3788465F9208243E /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = BVLinearGradient.xcodeproj; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = ""; }; + A4DB4DB998124376BAF6BFF6 /* RNMixpanel.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNMixpanel.xcodeproj; path = "../node_modules/react-native-mixpanel/RNMixpanel.xcodeproj"; sourceTree = ""; }; + AB2C4934DB8E4797AE896306 /* Exo2-LightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-LightItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-LightItalic.ttf"; sourceTree = ""; }; + B267FC03AB609E5B1237F3CF /* Pods-T7ChickenTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-T7ChickenTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests.release.xcconfig"; sourceTree = ""; }; + BAB33FD7BCE0279DE90315DA /* Pods-T7ChickenTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-T7ChickenTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests.debug.xcconfig"; sourceTree = ""; }; + C26E2B37A1FB04AE7C6C4D44 /* libPods-T7Chicken.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-T7Chicken.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + C579DE6BA16044EC923F872D /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNDeviceInfo.a; sourceTree = ""; }; + D043742885A24FE99A8BE744 /* RNDeviceInfo.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNDeviceInfo.xcodeproj; path = "../node_modules/react-native-device-info/RNDeviceInfo.xcodeproj"; sourceTree = ""; }; + D8F8344664F748AD939932FA /* Exo2-ExtraBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-ExtraBold.ttf"; path = "../src/fonts/Exo_2/Exo2-ExtraBold.ttf"; sourceTree = ""; }; + DAA724B229B342BDA77AFD9A /* Exo2-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-Bold.ttf"; path = "../src/fonts/Exo_2/Exo2-Bold.ttf"; sourceTree = ""; }; + DC37EDF1DB4D43018F520D05 /* Exo2-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-Light.ttf"; path = "../src/fonts/Exo_2/Exo2-Light.ttf"; sourceTree = ""; }; + DD43283E36BB4267882D8E13 /* Exo2-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-BoldItalic.ttf"; path = "../src/fonts/Exo_2/Exo2-BoldItalic.ttf"; sourceTree = ""; }; + E7906C709AB744AFA7A36B79 /* Exo2-Black.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Exo2-Black.ttf"; path = "../src/fonts/Exo_2/Exo2-Black.ttf"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -326,6 +337,7 @@ buildActionMask = 2147483647; files = ( 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */, + 9ED4AF9C83D616DF99665988 /* libPods-T7ChickenTests.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -333,8 +345,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */, + 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, @@ -344,6 +356,32 @@ 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, + 50020C498A684F23BEC89564 /* libBVLinearGradient.a in Frameworks */, + AA17AE58DB3E40DF8E781920 /* libRNMixpanel.a in Frameworks */, + 519FAB44F7A620D5726D1B3D /* libPods-T7Chicken.a in Frameworks */, + 1AD0F7797D764048828D8532 /* libRNDeviceInfo.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4781E0B4A5D006451C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4C91E0B4AEC006451C7 /* libReact.a in Frameworks */, + 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */, + 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */, + 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */, + 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */, + 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */, + 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */, + 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48D1E0B4A5D006451C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( ); runOnlyForDeploymentPostprocessing = 0; }; @@ -370,7 +408,6 @@ isa = PBXGroup; children = ( 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, - 9D3F6E021E078EB900FDD43E /* libRCTImage-tvOS.a */, 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */, ); name = Products; @@ -380,7 +417,6 @@ isa = PBXGroup; children = ( 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, - 9D3F6E0A1E078EB900FDD43E /* libRCTNetwork-tvOS.a */, 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */, ); name = Products; @@ -394,13 +430,13 @@ name = Products; sourceTree = ""; }; - 00E356EF1AD99517003FC87E /* t7ChickenNativeTests */ = { + 00E356EF1AD99517003FC87E /* T7ChickenTests */ = { isa = PBXGroup; children = ( - 00E356F21AD99517003FC87E /* t7ChickenNativeTests.m */, + 00E356F21AD99517003FC87E /* T7ChickenTests.m */, 00E356F01AD99517003FC87E /* Supporting Files */, ); - path = t7ChickenNativeTests; + path = T7ChickenTests; sourceTree = ""; }; 00E356F01AD99517003FC87E /* Supporting Files */ = { @@ -411,11 +447,21 @@ name = "Supporting Files"; sourceTree = ""; }; + 0B044DEE7DB7D16745C7E2A3 /* Pods */ = { + isa = PBXGroup; + children = ( + 997C42AB4DA6CFD90DD1D05B /* Pods-T7Chicken.debug.xcconfig */, + 9984E2C6B9D8C8CE74E5BB32 /* Pods-T7Chicken.release.xcconfig */, + BAB33FD7BCE0279DE90315DA /* Pods-T7ChickenTests.debug.xcconfig */, + B267FC03AB609E5B1237F3CF /* Pods-T7ChickenTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; 139105B71AF99BAD00B5F7CC /* Products */ = { isa = PBXGroup; children = ( 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, - 9D3F6E0E1E078EB900FDD43E /* libRCTSettings-tvOS.a */, 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */, ); name = Products; @@ -425,13 +471,12 @@ isa = PBXGroup; children = ( 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, - 9D3F6E171E078EB900FDD43E /* libRCTWebSocket-tvOS.a */, 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, ); name = Products; sourceTree = ""; }; - 13B07FAE1A68108700A75B9A /* t7ChickenNative */ = { + 13B07FAE1A68108700A75B9A /* T7Chicken */ = { isa = PBXGroup; children = ( 008F07F21AC5B25A0029DE68 /* main.jsbundle */, @@ -442,14 +487,13 @@ 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 13B07FB71A68108700A75B9A /* main.m */, ); - name = t7ChickenNative; + name = T7Chicken; sourceTree = ""; }; 146834001AC3E56700842450 /* Products */ = { isa = PBXGroup; children = ( 146834041AC3E56700842450 /* libReact.a */, - 9D3F6E1B1E078EB900FDD43E /* libReact-tvOS.a */, 3DAD3EA31DF850E9000B6D8A /* libReact.a */, 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, @@ -461,11 +505,36 @@ name = Products; sourceTree = ""; }; + 415DA1257808444D8800A504 /* Resources */ = { + isa = PBXGroup; + children = ( + E7906C709AB744AFA7A36B79 /* Exo2-Black.ttf */, + 213AA45619554BC595DA5E6F /* Exo2-BlackItalic.ttf */, + DAA724B229B342BDA77AFD9A /* Exo2-Bold.ttf */, + DD43283E36BB4267882D8E13 /* Exo2-BoldItalic.ttf */, + D8F8344664F748AD939932FA /* Exo2-ExtraBold.ttf */, + 3EFA7E1BF14E4EAFA55B8827 /* Exo2-ExtraBoldItalic.ttf */, + 48FA2192E200418382FFCA95 /* Exo2-ExtraLight.ttf */, + 8C38E78E03754EB6AC4314DD /* Exo2-ExtraLightItalic.ttf */, + 97BCC27F18914094B252D2D1 /* Exo2-Italic.ttf */, + DC37EDF1DB4D43018F520D05 /* Exo2-Light.ttf */, + AB2C4934DB8E4797AE896306 /* Exo2-LightItalic.ttf */, + 25DE771A847E44149B7A3DB7 /* Exo2-Medium.ttf */, + 6F9FD97F4E8049FE8616E8E1 /* Exo2-MediumItalic.ttf */, + 0D3968EE547E4CFCB625F412 /* Exo2-Regular.ttf */, + 7F0CE15B5A67418184A2FCC0 /* Exo2-SemiBold.ttf */, + 2CE07831A39E4070A15DC7A2 /* Exo2-SemiBoldItalic.ttf */, + 4BE7643AB504452EB2831CC5 /* Exo2-Thin.ttf */, + 834E0208D44148F8B7D2DAC1 /* Exo2-ThinItalic.ttf */, + ); + name = Resources; + sourceTree = ""; + }; 5E91572E1DD0AC6500FF2AA8 /* Products */ = { isa = PBXGroup; children = ( 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, - 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */, + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */, ); name = Products; sourceTree = ""; @@ -474,12 +543,20 @@ isa = PBXGroup; children = ( 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, - 9D3F6E061E078EB900FDD43E /* libRCTLinking-tvOS.a */, 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */, ); name = Products; sourceTree = ""; }; + 806DE3EED7B659BCE7AF332D /* Frameworks */ = { + isa = PBXGroup; + children = ( + C26E2B37A1FB04AE7C6C4D44 /* libPods-T7Chicken.a */, + 112E06CE9AA3129505392CA0 /* libPods-T7ChickenTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( @@ -494,6 +571,9 @@ 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, + 9E210F7F3788465F9208243E /* BVLinearGradient.xcodeproj */, + A4DB4DB998124376BAF6BFF6 /* RNMixpanel.xcodeproj */, + D043742885A24FE99A8BE744 /* RNDeviceInfo.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -502,7 +582,6 @@ isa = PBXGroup; children = ( 832341B51AAA6A8300B99B32 /* libRCTText.a */, - 9D3F6E121E078EB900FDD43E /* libRCTText-tvOS.a */, 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */, ); name = Products; @@ -511,11 +590,13 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( - 13B07FAE1A68108700A75B9A /* t7ChickenNative */, + 13B07FAE1A68108700A75B9A /* T7Chicken */, 832341AE1AAA6A7D00B99B32 /* Libraries */, - 00E356EF1AD99517003FC87E /* t7ChickenNativeTests */, + 00E356EF1AD99517003FC87E /* T7ChickenTests */, 83CBBA001A601CBA00E9B192 /* Products */, - 18323EFD7C4E43AB9D249CD2 /* Resources */, + 415DA1257808444D8800A504 /* Resources */, + 0B044DEE7DB7D16745C7E2A3 /* Pods */, + 806DE3EED7B659BCE7AF332D /* Frameworks */, ); indentWidth = 2; sourceTree = ""; @@ -524,77 +605,119 @@ 83CBBA001A601CBA00E9B192 /* Products */ = { isa = PBXGroup; children = ( - 13B07F961A680F5B00A75B9A /* t7ChickenNative.app */, - 00E356EE1AD99517003FC87E /* t7ChickenNativeTests.xctest */, + 13B07F961A680F5B00A75B9A /* T7Chicken.app */, + 00E356EE1AD99517003FC87E /* T7ChickenTests.xctest */, + 2D02E47B1E0B4A5D006451C7 /* T7Chicken-tvOS.app */, + 2D02E4901E0B4A5D006451C7 /* T7Chicken-tvOSTests.xctest */, ); name = Products; sourceTree = ""; }; - 18323EFD7C4E43AB9D249CD2 /* Resources */ = { + 9D71A64C1EE1D68100E3F2FB /* Products */ = { isa = PBXGroup; children = ( - 7B001D53816447C494ABCD01 /* Exo2-Black.ttf */, - 2006AAF75B0547BE98EC1ADF /* Exo2-BlackItalic.ttf */, - 54C90A6DD41842C592818B80 /* Exo2-Bold.ttf */, - 1A22D30D691440AC97E630AF /* Exo2-BoldItalic.ttf */, - 89D81ABC709243288FF6CB90 /* Exo2-ExtraBold.ttf */, - 1D4897648B3249309F64552E /* Exo2-ExtraBoldItalic.ttf */, - A0A5EABEBA9E407DBC595F8A /* Exo2-ExtraLight.ttf */, - D5D05C16B36C49C398BD76A2 /* Exo2-ExtraLightItalic.ttf */, - 056C73C96A2A49479874EE25 /* Exo2-Italic.ttf */, - D0212DB1EB774D7BAB7ED1EE /* Exo2-Light.ttf */, - 7F564CB758E14B308D6030D6 /* Exo2-LightItalic.ttf */, - B644829F3E2F479CAFE246A4 /* Exo2-Medium.ttf */, - 114ED7A3D9F447E4913FAE37 /* Exo2-MediumItalic.ttf */, - 4DFE2753FAB44CA0AD9A61B9 /* Exo2-Regular.ttf */, - 1ED7FC45D5B6497A804BDEFD /* Exo2-SemiBold.ttf */, - 298EC6FB91444670AAF14CAA /* Exo2-SemiBoldItalic.ttf */, - 28A8BC1FA1EA4B1DB4EE95AE /* Exo2-Thin.ttf */, - 15615BC905F0433EB564BE0C /* Exo2-ThinItalic.ttf */, + 9D71A6691EE1D68100E3F2FB /* libRNDeviceInfo.a */, ); - name = Resources; - path = ""; + name = Products; + sourceTree = ""; + }; + 9D71A66A1EE1D68100E3F2FB /* Products */ = { + isa = PBXGroup; + children = ( + 9D71A66E1EE1D68100E3F2FB /* libRNMixpanel.a */, + ); + name = Products; + sourceTree = ""; + }; + 9D71A66F1EE1D68100E3F2FB /* Products */ = { + isa = PBXGroup; + children = ( + 9D71A6731EE1D68100E3F2FB /* libBVLinearGradient.a */, + ); + name = Products; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 00E356ED1AD99517003FC87E /* t7ChickenNativeTests */ = { + 00E356ED1AD99517003FC87E /* T7ChickenTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "t7ChickenNativeTests" */; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "T7ChickenTests" */; buildPhases = ( + EACD4B8D16D4A91F88D76868 /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, + A7264236BE5797632D0F9670 /* [CP] Embed Pods Frameworks */, + C5D3DAA70F624C53CEA5BE61 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( 00E356F51AD99517003FC87E /* PBXTargetDependency */, ); - name = t7ChickenNativeTests; - productName = t7ChickenNativeTests; - productReference = 00E356EE1AD99517003FC87E /* t7ChickenNativeTests.xctest */; + name = T7ChickenTests; + productName = T7ChickenTests; + productReference = 00E356EE1AD99517003FC87E /* T7ChickenTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - 13B07F861A680F5B00A75B9A /* t7ChickenNative */ = { + 13B07F861A680F5B00A75B9A /* T7Chicken */ = { isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "t7ChickenNative" */; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "T7Chicken" */; buildPhases = ( + 361F0ACF80AB38819374632A /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + DD83FADF33082C7EBC4CAFA9 /* [CP] Embed Pods Frameworks */, + 5AA5AA762F691C10218C1914 /* [CP] Copy Pods Resources */, ); buildRules = ( ); dependencies = ( ); - name = t7ChickenNative; + name = T7Chicken; productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* t7ChickenNative.app */; + productReference = 13B07F961A680F5B00A75B9A /* T7Chicken.app */; + productType = "com.apple.product-type.application"; + }; + 2D02E47A1E0B4A5D006451C7 /* T7Chicken-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "T7Chicken-tvOS" */; + buildPhases = ( + 2D02E4771E0B4A5D006451C7 /* Sources */, + 2D02E4781E0B4A5D006451C7 /* Frameworks */, + 2D02E4791E0B4A5D006451C7 /* Resources */, + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "T7Chicken-tvOS"; + productName = "T7Chicken-tvOS"; + productReference = 2D02E47B1E0B4A5D006451C7 /* T7Chicken-tvOS.app */; productType = "com.apple.product-type.application"; }; + 2D02E48F1E0B4A5D006451C7 /* T7Chicken-tvOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "T7Chicken-tvOSTests" */; + buildPhases = ( + 2D02E48C1E0B4A5D006451C7 /* Sources */, + 2D02E48D1E0B4A5D006451C7 /* Frameworks */, + 2D02E48E1E0B4A5D006451C7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */, + ); + name = "T7Chicken-tvOSTests"; + productName = "T7Chicken-tvOSTests"; + productReference = 2D02E4901E0B4A5D006451C7 /* T7Chicken-tvOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -608,9 +731,21 @@ CreatedOnToolsVersion = 6.2; TestTargetID = 13B07F861A680F5B00A75B9A; }; + 13B07F861A680F5B00A75B9A = { + DevelopmentTeam = XPMM36ENSK; + }; + 2D02E47A1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + }; + 2D02E48F1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + TestTargetID = 2D02E47A1E0B4A5D006451C7; + }; }; }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "t7ChickenNative" */; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "T7Chicken" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -622,6 +757,10 @@ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 9D71A66F1EE1D68100E3F2FB /* Products */; + ProjectRef = 9E210F7F3788465F9208243E /* BVLinearGradient.xcodeproj */; + }, { ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; @@ -666,11 +805,21 @@ ProductGroup = 146834001AC3E56700842450 /* Products */; ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; }, + { + ProductGroup = 9D71A64C1EE1D68100E3F2FB /* Products */; + ProjectRef = D043742885A24FE99A8BE744 /* RNDeviceInfo.xcodeproj */; + }, + { + ProductGroup = 9D71A66A1EE1D68100E3F2FB /* Products */; + ProjectRef = A4DB4DB998124376BAF6BFF6 /* RNMixpanel.xcodeproj */; + }, ); projectRoot = ""; targets = ( - 13B07F861A680F5B00A75B9A /* t7ChickenNative */, - 00E356ED1AD99517003FC87E /* t7ChickenNativeTests */, + 13B07F861A680F5B00A75B9A /* T7Chicken */, + 00E356ED1AD99517003FC87E /* T7ChickenTests */, + 2D02E47A1E0B4A5D006451C7 /* T7Chicken-tvOS */, + 2D02E48F1E0B4A5D006451C7 /* T7Chicken-tvOSTests */, ); }; /* End PBXProject section */ @@ -830,10 +979,10 @@ remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */ = { + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libRCTAnimation-tvOS.a"; + path = libRCTAnimation.a; remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -851,53 +1000,25 @@ remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 9D3F6E021E078EB900FDD43E /* libRCTImage-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTImage-tvOS.a"; - remoteRef = 9D3F6E011E078EB900FDD43E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 9D3F6E061E078EB900FDD43E /* libRCTLinking-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTLinking-tvOS.a"; - remoteRef = 9D3F6E051E078EB900FDD43E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 9D3F6E0A1E078EB900FDD43E /* libRCTNetwork-tvOS.a */ = { + 9D71A6691EE1D68100E3F2FB /* libRNDeviceInfo.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libRCTNetwork-tvOS.a"; - remoteRef = 9D3F6E091E078EB900FDD43E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 9D3F6E0E1E078EB900FDD43E /* libRCTSettings-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTSettings-tvOS.a"; - remoteRef = 9D3F6E0D1E078EB900FDD43E /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 9D3F6E121E078EB900FDD43E /* libRCTText-tvOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = "libRCTText-tvOS.a"; - remoteRef = 9D3F6E111E078EB900FDD43E /* PBXContainerItemProxy */; + path = libRNDeviceInfo.a; + remoteRef = 9D71A6681EE1D68100E3F2FB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 9D3F6E171E078EB900FDD43E /* libRCTWebSocket-tvOS.a */ = { + 9D71A66E1EE1D68100E3F2FB /* libRNMixpanel.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libRCTWebSocket-tvOS.a"; - remoteRef = 9D3F6E161E078EB900FDD43E /* PBXContainerItemProxy */; + path = libRNMixpanel.a; + remoteRef = 9D71A66D1EE1D68100E3F2FB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 9D3F6E1B1E078EB900FDD43E /* libReact-tvOS.a */ = { + 9D71A6731EE1D68100E3F2FB /* libBVLinearGradient.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libReact-tvOS.a"; - remoteRef = 9D3F6E1A1E078EB900FDD43E /* PBXContainerItemProxy */; + path = libBVLinearGradient.a; + remoteRef = 9D71A6721EE1D68100E3F2FB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ @@ -916,24 +1037,39 @@ files = ( 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - CB61777D36DA4614B14554EA /* Exo2-Black.ttf in Resources */, - FAB143B8AD81431080AE4107 /* Exo2-BlackItalic.ttf in Resources */, - A795743AF548431495091976 /* Exo2-Bold.ttf in Resources */, - 380A3BE44E8A463793BC14AC /* Exo2-BoldItalic.ttf in Resources */, - BEE22F8EE4DE4292AE69B01A /* Exo2-ExtraBold.ttf in Resources */, - A8AEEBE721274A018E679C36 /* Exo2-ExtraBoldItalic.ttf in Resources */, - A5412D5EF7BC4A5A9F4F3534 /* Exo2-ExtraLight.ttf in Resources */, - 6E0B20D19C8D4051BB1B539C /* Exo2-ExtraLightItalic.ttf in Resources */, - F251FE58378C4E7DA753C15C /* Exo2-Italic.ttf in Resources */, - D6832361ECB74D03ABA06D7A /* Exo2-Light.ttf in Resources */, - 6097370F16F5461CA2A09AD7 /* Exo2-LightItalic.ttf in Resources */, - 6F0213799AD0480DAF606A69 /* Exo2-Medium.ttf in Resources */, - C20CA6A6B48B4BBCB1E53D24 /* Exo2-MediumItalic.ttf in Resources */, - 8A6AA4313508496A8D160DDD /* Exo2-Regular.ttf in Resources */, - 08DD513836224D5997F33841 /* Exo2-SemiBold.ttf in Resources */, - 8376DA5B27C74746A8B8B37C /* Exo2-SemiBoldItalic.ttf in Resources */, - C099C38A275B4E1B9105B6AA /* Exo2-Thin.ttf in Resources */, - 34D1FE24A4244868B0163813 /* Exo2-ThinItalic.ttf in Resources */, + B61BE23868E8442A95AD0CB9 /* Exo2-Black.ttf in Resources */, + 9E86350384914572B59FD0B4 /* Exo2-BlackItalic.ttf in Resources */, + 591E0BE4AA57417EB6EB331F /* Exo2-Bold.ttf in Resources */, + C4636EAB82F34DD0842D5383 /* Exo2-BoldItalic.ttf in Resources */, + 43EC743E4F6245BCB54DFFB2 /* Exo2-ExtraBold.ttf in Resources */, + 18CF69363163450191835AD3 /* Exo2-ExtraBoldItalic.ttf in Resources */, + 357F1B52A6AF43E38A4B78C5 /* Exo2-ExtraLight.ttf in Resources */, + 9256CC59DFBE49DFAD09DA1B /* Exo2-ExtraLightItalic.ttf in Resources */, + 741EC7F379D34C70A453DAA0 /* Exo2-Italic.ttf in Resources */, + B7D30E6430584EF6924365AC /* Exo2-Light.ttf in Resources */, + 74FD6E38D06E4DE8B8952B96 /* Exo2-LightItalic.ttf in Resources */, + 5AA816B455C14E33837A95C2 /* Exo2-Medium.ttf in Resources */, + 063719DD1D1A420588081B9A /* Exo2-MediumItalic.ttf in Resources */, + 7B896CBBA00942C4A758D13F /* Exo2-Regular.ttf in Resources */, + 199DBB9A1ACA41498E08A821 /* Exo2-SemiBold.ttf in Resources */, + E0D9C375BAC84BA197167199 /* Exo2-SemiBoldItalic.ttf in Resources */, + FF186797B5C84F8390B5B0E0 /* Exo2-Thin.ttf in Resources */, + FB025A20E1D94FC4ADF9A958 /* Exo2-ThinItalic.ttf in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4791E0B4A5D006451C7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48E1E0B4A5D006451C7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); runOnlyForDeploymentPostprocessing = 0; }; @@ -954,6 +1090,110 @@ shellPath = /bin/sh; shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; }; + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native Code And Images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; + }; + 361F0ACF80AB38819374632A /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 5AA5AA762F691C10218C1914 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + A7264236BE5797632D0F9670 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C5D3DAA70F624C53CEA5BE61 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-T7ChickenTests/Pods-T7ChickenTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + DD83FADF33082C7EBC4CAFA9 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-T7Chicken/Pods-T7Chicken-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + EACD4B8D16D4A91F88D76868 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -961,7 +1201,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 00E356F31AD99517003FC87E /* t7ChickenNativeTests.m in Sources */, + 00E356F31AD99517003FC87E /* T7ChickenTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -974,14 +1214,36 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 2D02E4771E0B4A5D006451C7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */, + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48C1E0B4A5D006451C7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2DCD954D1E0B4F2C00145EB5 /* T7ChickenTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* t7ChickenNative */; + target = 13B07F861A680F5B00A75B9A /* T7Chicken */; targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; }; + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2D02E47A1E0B4A5D006451C7 /* T7Chicken-tvOS */; + targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -991,7 +1253,7 @@ 13B07FB21A68108700A75B9A /* Base */, ); name = LaunchScreen.xib; - path = t7ChickenNative; + path = T7Chicken; sourceTree = ""; }; /* End PBXVariantGroup section */ @@ -999,68 +1261,250 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BAB33FD7BCE0279DE90315DA /* Pods-T7ChickenTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = t7ChickenNativeTests/Info.plist; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-mixpanel/RNMixpanel", + "$(SRCROOT)/../node_modules/react-native-device-info/RNDeviceInfo", + ); + INFOPLIST_FILE = T7ChickenTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/t7ChickenNative.app/t7ChickenNative"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/T7Chicken.app/T7Chicken"; }; name = Debug; }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B267FC03AB609E5B1237F3CF /* Pods-T7ChickenTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COPY_PHASE_STRIP = NO; - INFOPLIST_FILE = t7ChickenNativeTests/Info.plist; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-mixpanel/RNMixpanel", + "$(SRCROOT)/../node_modules/react-native-device-info/RNDeviceInfo", + ); + INFOPLIST_FILE = T7ChickenTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/t7ChickenNative.app/t7ChickenNative"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/T7Chicken.app/T7Chicken"; }; name = Release; }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 997C42AB4DA6CFD90DD1D05B /* Pods-T7Chicken.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; - INFOPLIST_FILE = t7ChickenNative/Info.plist; + DEVELOPMENT_TEAM = XPMM36ENSK; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-mixpanel/RNMixpanel", + "$(SRCROOT)/../node_modules/react-native-device-info/RNDeviceInfo", + ); + INFOPLIST_FILE = T7Chicken/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); - PRODUCT_NAME = t7ChickenNative; + PRODUCT_BUNDLE_IDENTIFIER = com.tekkenchicken; + PRODUCT_NAME = T7Chicken; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 9984E2C6B9D8C8CE74E5BB32 /* Pods-T7Chicken.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CURRENT_PROJECT_VERSION = 1; - INFOPLIST_FILE = t7ChickenNative/Info.plist; + DEVELOPMENT_TEAM = XPMM36ENSK; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-mixpanel/RNMixpanel", + "$(SRCROOT)/../node_modules/react-native-device-info/RNDeviceInfo", + ); + INFOPLIST_FILE = T7Chicken/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", "-lc++", ); - PRODUCT_NAME = t7ChickenNative; + PRODUCT_BUNDLE_IDENTIFIER = com.tekkenchicken; + PRODUCT_NAME = T7Chicken; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; + 2D02E4971E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-mixpanel/RNMixpanel", + "$(SRCROOT)/../node_modules/react-native-device-info/RNDeviceInfo", + ); + INFOPLIST_FILE = "T7Chicken-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.T7Chicken-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.2; + }; + name = Debug; + }; + 2D02E4981E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../node_modules/react-native-linear-gradient/BVLinearGradient", + "$(SRCROOT)/../node_modules/react-native-mixpanel/RNMixpanel", + "$(SRCROOT)/../node_modules/react-native-device-info/RNDeviceInfo", + ); + INFOPLIST_FILE = "T7Chicken-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.T7Chicken-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.2; + }; + name = Release; + }; + 2D02E4991E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "T7Chicken-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.T7Chicken-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/T7Chicken-tvOS.app/T7Chicken-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Debug; + }; + 2D02E49A1E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "T7Chicken-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + "\"$(SRCROOT)/$(TARGET_NAME)\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.T7Chicken-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/T7Chicken-tvOS.app/T7Chicken-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Release; + }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1119,7 +1563,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1140,7 +1584,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "t7ChickenNativeTests" */ = { + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "T7ChickenTests" */ = { isa = XCConfigurationList; buildConfigurations = ( 00E356F61AD99517003FC87E /* Debug */, @@ -1149,7 +1593,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "t7ChickenNative" */ = { + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "T7Chicken" */ = { isa = XCConfigurationList; buildConfigurations = ( 13B07F941A680F5B00A75B9A /* Debug */, @@ -1158,7 +1602,25 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "t7ChickenNative" */ = { + 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "T7Chicken-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4971E0B4A5E006451C7 /* Debug */, + 2D02E4981E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "T7Chicken-tvOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4991E0B4A5E006451C7 /* Debug */, + 2D02E49A1E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "T7Chicken" */ = { isa = XCConfigurationList; buildConfigurations = ( 83CBBA201A601CBA00E9B192 /* Debug */, diff --git a/ios/T7Chicken.xcodeproj/xcshareddata/xcschemes/T7Chicken-tvOS.xcscheme b/ios/T7Chicken.xcodeproj/xcshareddata/xcschemes/T7Chicken-tvOS.xcscheme new file mode 100644 index 0000000..ab7237a --- /dev/null +++ b/ios/T7Chicken.xcodeproj/xcshareddata/xcschemes/T7Chicken-tvOS.xcscheme @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/t7ChickenNative.xcodeproj/xcshareddata/xcschemes/t7ChickenNative.xcscheme b/ios/T7Chicken.xcodeproj/xcshareddata/xcschemes/T7Chicken.xcscheme similarity index 78% rename from ios/t7ChickenNative.xcodeproj/xcshareddata/xcschemes/t7ChickenNative.xcscheme rename to ios/T7Chicken.xcodeproj/xcshareddata/xcschemes/T7Chicken.xcscheme index 0551096..4fa3a95 100644 --- a/ios/t7ChickenNative.xcodeproj/xcshareddata/xcschemes/t7ChickenNative.xcscheme +++ b/ios/T7Chicken.xcodeproj/xcshareddata/xcschemes/T7Chicken.xcscheme @@ -29,9 +29,9 @@ + BuildableName = "T7Chicken.app" + BlueprintName = "T7Chicken" + ReferencedContainer = "container:T7Chicken.xcodeproj"> + BuildableName = "T7ChickenTests.xctest" + BlueprintName = "T7ChickenTests" + ReferencedContainer = "container:T7Chicken.xcodeproj"> @@ -61,9 +61,9 @@ + BuildableName = "T7ChickenTests.xctest" + BlueprintName = "T7ChickenTests" + ReferencedContainer = "container:T7Chicken.xcodeproj"> @@ -71,9 +71,9 @@ + BuildableName = "T7Chicken.app" + BlueprintName = "T7Chicken" + ReferencedContainer = "container:T7Chicken.xcodeproj"> @@ -94,9 +94,9 @@ + BuildableName = "T7Chicken.app" + BlueprintName = "T7Chicken" + ReferencedContainer = "container:T7Chicken.xcodeproj"> @@ -113,9 +113,9 @@ + BuildableName = "T7Chicken.app" + BlueprintName = "T7Chicken" + ReferencedContainer = "container:T7Chicken.xcodeproj"> diff --git a/ios/T7Chicken.xcworkspace/contents.xcworkspacedata b/ios/T7Chicken.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..fcaf2f4 --- /dev/null +++ b/ios/T7Chicken.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/t7ChickenNative/AppDelegate.h b/ios/T7Chicken/AppDelegate.h similarity index 100% rename from ios/t7ChickenNative/AppDelegate.h rename to ios/T7Chicken/AppDelegate.h diff --git a/ios/t7ChickenNative/AppDelegate.m b/ios/T7Chicken/AppDelegate.m similarity index 98% rename from ios/t7ChickenNative/AppDelegate.m rename to ios/T7Chicken/AppDelegate.m index b98895a..11a319d 100644 --- a/ios/t7ChickenNative/AppDelegate.m +++ b/ios/T7Chicken/AppDelegate.m @@ -21,7 +21,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"t7ChickenNative" + moduleName:@"T7Chicken" initialProperties:nil launchOptions:launchOptions]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; diff --git a/ios/t7ChickenNative/Base.lproj/LaunchScreen.xib b/ios/T7Chicken/Base.lproj/LaunchScreen.xib similarity index 93% rename from ios/t7ChickenNative/Base.lproj/LaunchScreen.xib rename to ios/T7Chicken/Base.lproj/LaunchScreen.xib index 7ab32cb..de79cf2 100644 --- a/ios/t7ChickenNative/Base.lproj/LaunchScreen.xib +++ b/ios/T7Chicken/Base.lproj/LaunchScreen.xib @@ -18,7 +18,7 @@ -