diff --git a/cfg/central-signing/ProductionSigning.xcconfig b/cfg/central-signing/ProductionSigning.xcconfig
new file mode 100644
index 00000000000..29251b2ceeb
--- /dev/null
+++ b/cfg/central-signing/ProductionSigning.xcconfig
@@ -0,0 +1,2 @@
+CODE_SIGN_IDENTITY = iPhone Distribution
+PROVISIONING_PROFILE_SPECIFIER = 6WQYJS4JUL/com sap sailing ios SAPTracker enterprise
diff --git a/cfg/central-signing/ProductionSigningExportOptions.plist b/cfg/central-signing/ProductionSigningExportOptions.plist
new file mode 100644
index 00000000000..67d0b3f9417
--- /dev/null
+++ b/cfg/central-signing/ProductionSigningExportOptions.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ method
+ enterprise
+ provisioningProfiles
+
+ com.sap.sailing.ios.SAPTracker.enterprise
+ com sap sailing ios SAPTracker enterprise
+
+
+
diff --git a/cfg/files2sign.json b/cfg/files2sign.json
index c403414dfd2..ed0af5de200 100644
--- a/cfg/files2sign.json
+++ b/cfg/files2sign.json
@@ -1,22 +1,22 @@
-{
- "ANDROID":[
- {
- "version": "1.4.63",
- "groupid": "com.sap.sailing",
- "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.racecommittee.app-unsigned.apk",
- "artifactid": "com.sap.sailing.racecommittee.app"
- },
- {
- "version": "1.4.63",
- "groupid": "com.sap.sailing",
- "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.tracking.app-unsigned.apk",
- "artifactid": "com.sap.sailing.android.tracking.app"
- },
- {
- "version": "1.4.63",
- "groupid": "com.sap.sailing",
- "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.buoy.positioning.app-unsigned.apk",
- "artifactid": "com.sap.sailing.android.buoy.positioning.app"
- }
- ]
-}
+{
+ "ANDROID":[
+ {
+ "version": "1.4.63",
+ "groupid": "com.sap.sailing",
+ "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.racecommittee.app-unsigned.apk",
+ "artifactid": "com.sap.sailing.racecommittee.app"
+ },
+ {
+ "version": "1.4.63",
+ "groupid": "com.sap.sailing",
+ "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.tracking.app-unsigned.apk",
+ "artifactid": "com.sap.sailing.android.tracking.app"
+ },
+ {
+ "version": "1.4.63",
+ "groupid": "com.sap.sailing",
+ "file": "src/java/com.sap.sailing.www/apps/com.sap.sailing.android.buoy.positioning.app-unsigned.apk",
+ "artifactid": "com.sap.sailing.android.buoy.positioning.app"
+ }
+ ]
+}
diff --git a/configuration/installPluginsForEclipse2019-03.sh b/configuration/installPluginsForEclipse2019-03.sh
old mode 100644
new mode 100755
index a7e620b9097..266d020cc8d
--- a/configuration/installPluginsForEclipse2019-03.sh
+++ b/configuration/installPluginsForEclipse2019-03.sh
@@ -36,15 +36,15 @@ installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.
echo "Installing EasyShell..."
installPlugins http://anb0s.github.io/EasyShell de.anbos.eclipse.easyshell.feature.feature.group
-echo "Installing BIRT charts (requirement for MAT)..."
-installPlugins https://download.eclipse.org/birt/update-site/photon-interim/ org.eclipse.birt.chart.feature.group
-
echo "Installing Memory Analyzer..."
installPlugins http://download.eclipse.org/releases/2019-03 org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
echo "Installing SAP JVM Tools (profiler) ..."
installPlugins https://tools.hana.ondemand.com/oxygen com.sap.jvm.profiling.feature.group
+echo "Installing UMLet ..."
+installPlugins https://www.umlet.com/umlet_latest/repository/ umlet-eclipse-feature.feature.group
+
echo "Installing javax.xml.bind for news feed polling (see https://stackoverflow.com/questions/52528693/eclipse-internal-error-polling-news-feeds)..."
installPlugins http://download.eclipse.org/tools/orbit/downloads/drops/R20180905201904/repository javax.xml.bind
diff --git a/configuration/installPluginsForEclipseMars.sh b/configuration/installPluginsForEclipseMars.sh
deleted file mode 100644
index 3399162b36e..00000000000
--- a/configuration/installPluginsForEclipseMars.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-
-# You need an installation of the Mars release of "Eclipse IDE for Eclipse Committers" matching you OS and JDK (32 vs 64 Bit):
-# http://www.eclipse.org/downloads/packages/eclipse-ide-eclipse-committers/marsr
-
-if [[ $1 == "" ]]; then
- echo "You need to specify the Eclipse installation directory"
- exit 1
-fi
-
-installPath="$1"
-
-installPlugins() {
- "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -installIU $2
- echo ""
-}
-
-updatePlugins() {
- "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -uninstallIU $2 -installIU $2
- echo ""
-}
-
-# Not necessary if using Eclipse for JEE developers
-echo "Installing webtools (WTP/WST/JPT) and m2e that are required by the GWT plugin..."
-installPlugins http://download.eclipse.org/releases/mars org.eclipse.wst.web_ui.feature.feature.group,org.eclipse.jst.web_ui.feature.feature.group,org.eclipse.wst.xml_ui.feature.feature.group,org.eclipse.jpt.common.feature.feature.group,org.eclipse.jpt.jpa.feature.feature.group,org.eclipse.m2e.feature.feature.group,org.eclipse.m2e.wtp.feature.feature.group
-
-echo "Installing GWT plugin..."
-installPlugins http://storage.googleapis.com/gwt-eclipse-plugin/release com.google.gdt.eclipse.suite.e44.feature.feature.group
-
-echo "Installing Android Tools..."
-installPlugins https://dl-ssl.google.com/android/eclipse com.android.ide.eclipse.adt.feature.feature.group
-
-echo "Installing GWT SDM debug bridge..."
-installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.group
-
-echo "Installing EasyShell..."
-installPlugins http://anb0s.github.io/EasyShell com.tetrade.eclipse.plugins.easyshell.feature.feature.group
-
-echo "Installing BIRT charts (requirement for MAT)..."
-installPlugins http://download.eclipse.org/birt/update-site/4.6 org.eclipse.birt.chart.feature.group
-
-echo "Installing Memory Analyzer..."
-installPlugins http://download.eclipse.org/mat/1.6/update-site/ org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
-
-echo "Installing latest version of Code Recommenders ..."
-updatePlugins http://download.eclipse.org/recommenders/updates/stable/ org.eclipse.recommenders.rcp.feature.feature.group,org.eclipse.recommenders.mylyn.rcp.feature.feature.group,org.eclipse.recommenders.snipmatch.rcp.feature.feature.group
-
-echo "Installing latest version of EGit ..."
-updatePlugins http://download.eclipse.org/egit/updates org.eclipse.jgit.feature.group,org.eclipse.jgit.http.apache.feature.group,org.eclipse.egit.feature.group,org.eclipse.egit.mylyn.feature.group
-
-echo "Installing latest version of Mylyn ..."
-updatePlugins http://download.eclipse.org/mylyn/releases/latest org.eclipse.mylyn_feature.feature.group,org.eclipse.mylyn.bugzilla_feature.feature.group,org.eclipse.mylyn.builds.feature.group,org.eclipse.mylyn.commons.feature.group,org.eclipse.mylyn.commons.identity.feature.group,org.eclipse.mylyn.commons.notifications.feature.group,org.eclipse.mylyn.commons.repositories.feature.group,org.eclipse.mylyn.commons.repositories.http.feature.group,org.eclipse.mylyn.context_feature.feature.group,org.eclipse.mylyn.discovery.feature.group,org.eclipse.mylyn.gerrit.feature.feature.group,org.eclipse.mylyn.git.feature.group,org.eclipse.mylyn.hudson.feature.group,org.eclipse.mylyn.java_feature.feature.group,org.eclipse.mylyn.monitor.feature.group,org.eclipse.mylyn.reviews.feature.feature.group,org.eclipse.mylyn.team_feature.feature.group,org.eclipse.mylyn.versions.feature.group,org.eclipse.mylyn.wikitext_feature.feature.group
-
-echo "Installation completed!"
diff --git a/configuration/installPluginsForEclipseNeon.sh b/configuration/installPluginsForEclipseNeon.sh
deleted file mode 100644
index 3e1d30fd860..00000000000
--- a/configuration/installPluginsForEclipseNeon.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-
-# You need an installation of the Neon SR2 release of "Eclipse IDE for Eclipse Committers" matching you OS and JDK (32 vs 64 Bit):
-# http://www.eclipse.org/downloads/packages/eclipse-ide-eclipse-committers/neon2
-
-if [[ $1 == "" ]]; then
- echo "You need to specify the Eclipse installation directory"
- exit 1
-fi
-
-installPath="$1"
-
-installPlugins() {
- "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -installIU $2
- echo ""
-}
-
-updatePlugins() {
- "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -uninstallIU $2 -installIU $2
- echo ""
-}
-
-# Not necessary if using Eclipse for JEE developers
-echo "Installing webtools (WTP/WST/JPT) and m2e that are required by the GWT plugin..."
-installPlugins http://download.eclipse.org/releases/neon org.eclipse.wst.web_ui.feature.feature.group,org.eclipse.jst.web_ui.feature.feature.group,org.eclipse.wst.xml_ui.feature.feature.group,org.eclipse.jpt.common.feature.feature.group,org.eclipse.jpt.jpa.feature.feature.group,org.eclipse.m2e.feature.feature.group,org.eclipse.m2e.wtp.feature.feature.group
-
-echo "Installing GWT plugin..."
-installPlugins http://storage.googleapis.com/gwt-eclipse-plugin/release com.google.gdt.eclipse.suite.e44.feature.feature.group
-
-echo "Installing Android Tools..."
-installPlugins https://dl-ssl.google.com/android/eclipse com.android.ide.eclipse.adt.feature.feature.group
-
-echo "Installing GWT SDM debug bridge..."
-installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.group
-
-echo "Installing EasyShell..."
-installPlugins http://anb0s.github.io/EasyShell de.anbos.eclipse.easyshell.feature.feature.group
-
-echo "Installing BIRT charts (requirement for MAT)..."
-installPlugins http://download.eclipse.org/birt/update-site/4.6 org.eclipse.birt.chart.feature.group
-
-echo "Installing Memory Analyzer..."
-installPlugins http://download.eclipse.org/mat/1.6/update-site/ org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
-
-echo "Installing latest version of Code Recommenders (Neon release version has bugs)..."
-updatePlugins http://download.eclipse.org/recommenders/updates/stable/ org.eclipse.recommenders.rcp.feature.feature.group,org.eclipse.recommenders.mylyn.rcp.feature.feature.group,org.eclipse.recommenders.snipmatch.rcp.feature.feature.group,org.eclipse.recommenders.news.rcp.feature.feature.group
-
-echo "Installing latest version of EGit (Neon release version has bugs)..."
-updatePlugins http://download.eclipse.org/egit/updates org.eclipse.jgit.feature.group,org.eclipse.jgit.http.apache.feature.group,org.eclipse.egit.feature.group,org.eclipse.egit.mylyn.feature.group
-
-echo "Installing latest version of Mylyn ..."
-updatePlugins http://download.eclipse.org/mylyn/releases/latest org.eclipse.mylyn_feature.feature.group,org.eclipse.mylyn.bugzilla_feature.feature.group,org.eclipse.mylyn.builds.feature.group,org.eclipse.mylyn.commons.feature.group,org.eclipse.mylyn.commons.identity.feature.group,org.eclipse.mylyn.commons.notifications.feature.group,org.eclipse.mylyn.commons.repositories.feature.group,org.eclipse.mylyn.commons.repositories.http.feature.group,org.eclipse.mylyn.context_feature.feature.group,org.eclipse.mylyn.discovery.feature.group,org.eclipse.mylyn.gerrit.feature.feature.group,org.eclipse.mylyn.git.feature.group,org.eclipse.mylyn.hudson.feature.group,org.eclipse.mylyn.java_feature.feature.group,org.eclipse.mylyn.monitor.feature.group,org.eclipse.mylyn.reviews.feature.feature.group,org.eclipse.mylyn.team_feature.feature.group,org.eclipse.mylyn.versions.feature.group,org.eclipse.mylyn.wikitext_feature.feature.group
-
-echo "Installing SAP JVM Tools (profiler) ..."
-installPlugins https://tools.hana.ondemand.com/neon com.sap.jvm.profiling.feature.group
-
-echo "Installation completed!"
diff --git a/configuration/installPluginsForEclipseOxygen.sh b/configuration/installPluginsForEclipseOxygen.sh
deleted file mode 100644
index 9c263371ac1..00000000000
--- a/configuration/installPluginsForEclipseOxygen.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-
-# You need an installation of the Oxygen SR2 release of "Eclipse IDE for Eclipse Committers" matching you OS and JDK (32 vs 64 Bit):
-# http://www.eclipse.org/downloads/packages/eclipse-ide-eclipse-committers/oxygen2
-
-if [[ $1 == "" ]]; then
- echo "You need to specify the Eclipse installation directory"
- exit 1
-fi
-
-installPath="$1"
-
-installPlugins() {
- "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -installIU $2
- echo ""
-}
-
-updatePlugins() {
- "$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -uninstallIU $2 -installIU $2
- echo ""
-}
-
-# Not necessary if using Eclipse for JEE developers
-echo "Installing webtools (WTP/WST/JPT) and m2e that are required by the GWT plugin..."
-installPlugins http://download.eclipse.org/releases/oxygen org.eclipse.wst.web_ui.feature.feature.group,org.eclipse.jst.web_ui.feature.feature.group,org.eclipse.wst.xml_ui.feature.feature.group,org.eclipse.jpt.common.feature.feature.group,org.eclipse.jpt.jpa.feature.feature.group,org.eclipse.m2e.feature.feature.group,org.eclipse.m2e.wtp.feature.feature.group
-
-echo "Installing GWT plugin..."
-installPlugins http://storage.googleapis.com/gwt-eclipse-plugin/v3/release com.gwtplugins.eclipse.suite.v3.feature.feature.group
-
-echo "Installing Android Tools..."
-installPlugins https://dl-ssl.google.com/android/eclipse com.android.ide.eclipse.adt.feature.feature.group
-
-echo "Installing GWT SDM debug bridge..."
-installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.group
-
-echo "Installing EasyShell..."
-installPlugins http://anb0s.github.io/EasyShell de.anbos.eclipse.easyshell.feature.feature.group
-
-echo "Installing BIRT charts (requirement for MAT)..."
-installPlugins http://download.eclipse.org/birt/update-site/4.6 org.eclipse.birt.chart.feature.group
-
-echo "Installing Memory Analyzer..."
-installPlugins http://download.eclipse.org/releases/oxygen org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
-
-echo "Installing latest version of Code Recommenders..."
-updatePlugins http://download.eclipse.org/recommenders/updates/stable/ org.eclipse.recommenders.rcp.feature.feature.group,org.eclipse.recommenders.mylyn.rcp.feature.feature.group,org.eclipse.recommenders.snipmatch.rcp.feature.feature.group,org.eclipse.recommenders.news.rcp.feature.feature.group
-
-echo "Installing latest version of EGit..."
-updatePlugins http://download.eclipse.org/egit/updates org.eclipse.jgit.feature.group,org.eclipse.jgit.http.apache.feature.group,org.eclipse.egit.feature.group,org.eclipse.egit.mylyn.feature.group,org.eclipse.egit.gitflow.feature.feature.group
-
-echo "Installing latest version of Mylyn ..."
-updatePlugins http://download.eclipse.org/mylyn/releases/latest org.eclipse.mylyn_feature.feature.group,org.eclipse.mylyn.bugzilla_feature.feature.group,org.eclipse.mylyn.builds.feature.group,org.eclipse.mylyn.commons.feature.group,org.eclipse.mylyn.commons.identity.feature.group,org.eclipse.mylyn.commons.notifications.feature.group,org.eclipse.mylyn.commons.repositories.feature.group,org.eclipse.mylyn.commons.repositories.http.feature.group,org.eclipse.mylyn.context_feature.feature.group,org.eclipse.mylyn.discovery.feature.group,org.eclipse.mylyn.gerrit.feature.feature.group,org.eclipse.mylyn.git.feature.group,org.eclipse.mylyn.hudson.feature.group,org.eclipse.mylyn.java_feature.feature.group,org.eclipse.mylyn.monitor.feature.group,org.eclipse.mylyn.reviews.feature.feature.group,org.eclipse.mylyn.team_feature.feature.group,org.eclipse.mylyn.versions.feature.group,org.eclipse.mylyn.wikitext_feature.feature.group
-
-echo "Installing SAP JVM Tools (profiler) ..."
-installPlugins https://tools.hana.ondemand.com/oxygen com.sap.jvm.profiling.feature.group
-
-echo "Installation completed!"
diff --git a/ios/SAPTracker/src/xcode/SAPTracker.xcodeproj/project.pbxproj b/ios/SAPTracker/src/xcode/SAPTracker.xcodeproj/project.pbxproj
old mode 100755
new mode 100644
index 19507b37c1c..39b01ad2ea5
--- a/ios/SAPTracker/src/xcode/SAPTracker.xcodeproj/project.pbxproj
+++ b/ios/SAPTracker/src/xcode/SAPTracker.xcodeproj/project.pbxproj
@@ -7,6 +7,65 @@
objects = {
/* Begin PBXBuildFile section */
+ 10080BAA220C5DD200C6E0A2 /* BNCError.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B29220C5DD200C6E0A2 /* BNCError.m */; };
+ 10080BAB220C5DD200C6E0A2 /* BNCEncodingUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B2B220C5DD200C6E0A2 /* BNCEncodingUtils.m */; };
+ 10080BAC220C5DD200C6E0A2 /* BranchUniversalObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B2C220C5DD200C6E0A2 /* BranchUniversalObject.m */; };
+ 10080BAD220C5DD200C6E0A2 /* BNCApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B2D220C5DD200C6E0A2 /* BNCApplication.m */; };
+ 10080BAE220C5DD200C6E0A2 /* NSMutableDictionary+Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B2E220C5DD200C6E0A2 /* NSMutableDictionary+Branch.m */; };
+ 10080BAF220C5DD200C6E0A2 /* BNCDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B32220C5DD200C6E0A2 /* BNCDebug.m */; };
+ 10080BB0220C5DD200C6E0A2 /* BNCDeepLinkViewControllerInstance.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B34220C5DD200C6E0A2 /* BNCDeepLinkViewControllerInstance.m */; };
+ 10080BB1220C5DD200C6E0A2 /* BNCContentDiscoveryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B35220C5DD200C6E0A2 /* BNCContentDiscoveryManager.m */; };
+ 10080BB2220C5DD200C6E0A2 /* Branch+Validator.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B36220C5DD200C6E0A2 /* Branch+Validator.m */; };
+ 10080BB3220C5DD200C6E0A2 /* NSString+Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B38220C5DD200C6E0A2 /* NSString+Branch.m */; };
+ 10080BB4220C5DD200C6E0A2 /* BNCSystemObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B39220C5DD200C6E0A2 /* BNCSystemObserver.m */; };
+ 10080BB5220C5DD200C6E0A2 /* BNCCommerceEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B3A220C5DD200C6E0A2 /* BNCCommerceEvent.m */; };
+ 10080BB6220C5DD200C6E0A2 /* BNCThreads.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B3C220C5DD200C6E0A2 /* BNCThreads.m */; };
+ 10080BB7220C5DD200C6E0A2 /* BNCPreferenceHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B3F220C5DD200C6E0A2 /* BNCPreferenceHelper.m */; };
+ 10080BB8220C5DD200C6E0A2 /* BNCServerRequestQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B45220C5DD200C6E0A2 /* BNCServerRequestQueue.m */; };
+ 10080BB9220C5DD200C6E0A2 /* BNCServerRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B46220C5DD200C6E0A2 /* BNCServerRequest.m */; };
+ 10080BBA220C5DD200C6E0A2 /* BNCNetworkService.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B48220C5DD200C6E0A2 /* BNCNetworkService.m */; };
+ 10080BBB220C5DD200C6E0A2 /* BNCServerInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B49220C5DD200C6E0A2 /* BNCServerInterface.m */; };
+ 10080BBC220C5DD200C6E0A2 /* BranchLogoutRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B4C220C5DD200C6E0A2 /* BranchLogoutRequest.m */; };
+ 10080BBD220C5DD200C6E0A2 /* BranchInstallRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B4D220C5DD200C6E0A2 /* BranchInstallRequest.m */; };
+ 10080BBE220C5DD200C6E0A2 /* BranchSetIdentityRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B4F220C5DD200C6E0A2 /* BranchSetIdentityRequest.m */; };
+ 10080BBF220C5DD200C6E0A2 /* BranchUserCompletedActionRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B50220C5DD200C6E0A2 /* BranchUserCompletedActionRequest.m */; };
+ 10080BC0220C5DD200C6E0A2 /* BranchRegisterViewRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B51220C5DD200C6E0A2 /* BranchRegisterViewRequest.m */; };
+ 10080BC1220C5DD200C6E0A2 /* BranchCloseRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B52220C5DD200C6E0A2 /* BranchCloseRequest.m */; };
+ 10080BC2220C5DD200C6E0A2 /* BranchSpotlightUrlRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B54220C5DD200C6E0A2 /* BranchSpotlightUrlRequest.m */; };
+ 10080BC3220C5DD200C6E0A2 /* BranchLoadRewardsRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B55220C5DD200C6E0A2 /* BranchLoadRewardsRequest.m */; };
+ 10080BC4220C5DD200C6E0A2 /* BranchCreditHistoryRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B5C220C5DD200C6E0A2 /* BranchCreditHistoryRequest.m */; };
+ 10080BC5220C5DD200C6E0A2 /* BranchRedeemRewardsRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B5F220C5DD200C6E0A2 /* BranchRedeemRewardsRequest.m */; };
+ 10080BC6220C5DD200C6E0A2 /* BranchShortUrlRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B62220C5DD200C6E0A2 /* BranchShortUrlRequest.m */; };
+ 10080BC7220C5DD200C6E0A2 /* BranchOpenRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B63220C5DD200C6E0A2 /* BranchOpenRequest.m */; };
+ 10080BC8220C5DD200C6E0A2 /* BranchShortUrlSyncRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B64220C5DD200C6E0A2 /* BranchShortUrlSyncRequest.m */; };
+ 10080BC9220C5DD200C6E0A2 /* BNCServerResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B68220C5DD200C6E0A2 /* BNCServerResponse.m */; };
+ 10080BCA220C5DD200C6E0A2 /* BNCConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B6B220C5DD200C6E0A2 /* BNCConfig.m */; };
+ 10080BCB220C5DD200C6E0A2 /* BranchConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B6C220C5DD200C6E0A2 /* BranchConstants.m */; };
+ 10080BCC220C5DD200C6E0A2 /* BranchContentDiscoveryManifest.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B6E220C5DD200C6E0A2 /* BranchContentDiscoveryManifest.m */; };
+ 10080BCD220C5DD200C6E0A2 /* BNCLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B6F220C5DD200C6E0A2 /* BNCLog.m */; };
+ 10080BCE220C5DD200C6E0A2 /* BranchContentDiscoverer.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B70220C5DD200C6E0A2 /* BranchContentDiscoverer.m */; };
+ 10080BCF220C5DD200C6E0A2 /* BNCDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B71220C5DD200C6E0A2 /* BNCDeviceInfo.m */; };
+ 10080BD0220C5DD200C6E0A2 /* BNCFabricAnswers.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B73220C5DD200C6E0A2 /* BNCFabricAnswers.m */; };
+ 10080BD1220C5DD200C6E0A2 /* BNCURLBlackList.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B75220C5DD200C6E0A2 /* BNCURLBlackList.m */; };
+ 10080BD2220C5DD200C6E0A2 /* BNCCrashlyticsWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B77220C5DD200C6E0A2 /* BNCCrashlyticsWrapper.m */; };
+ 10080BD3220C5DD200C6E0A2 /* BranchCSSearchableItemAttributeSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B7A220C5DD200C6E0A2 /* BranchCSSearchableItemAttributeSet.m */; };
+ 10080BD4220C5DD200C6E0A2 /* BNCStrongMatchHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B7B220C5DD200C6E0A2 /* BNCStrongMatchHelper.m */; };
+ 10080BD5220C5DD200C6E0A2 /* BranchActivityItemProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B7C220C5DD200C6E0A2 /* BranchActivityItemProvider.m */; };
+ 10080BD6220C5DD200C6E0A2 /* BNCKeyChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B81220C5DD200C6E0A2 /* BNCKeyChain.m */; };
+ 10080BD7220C5DD200C6E0A2 /* BranchDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B83220C5DD200C6E0A2 /* BranchDelegate.m */; };
+ 10080BD8220C5DD200C6E0A2 /* BNCLinkData.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B84220C5DD200C6E0A2 /* BNCLinkData.m */; };
+ 10080BD9220C5DD200C6E0A2 /* Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B85220C5DD200C6E0A2 /* Branch.m */; };
+ 10080BDA220C5DD200C6E0A2 /* BNCAvailability.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B86220C5DD200C6E0A2 /* BNCAvailability.m */; };
+ 10080BDB220C5DD200C6E0A2 /* BranchView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B8B220C5DD200C6E0A2 /* BranchView.m */; };
+ 10080BDC220C5DD200C6E0A2 /* BranchContentPathProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B8D220C5DD200C6E0A2 /* BranchContentPathProperties.m */; };
+ 10080BDD220C5DD200C6E0A2 /* BranchLinkProperties.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B8F220C5DD200C6E0A2 /* BranchLinkProperties.m */; };
+ 10080BDE220C5DD200C6E0A2 /* BNCLinkCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B94220C5DD200C6E0A2 /* BNCLinkCache.m */; };
+ 10080BDF220C5DD200C6E0A2 /* BNCSpotlightService.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B9A220C5DD200C6E0A2 /* BNCSpotlightService.m */; };
+ 10080BE0220C5DD200C6E0A2 /* BranchShareLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B9B220C5DD200C6E0A2 /* BranchShareLink.m */; };
+ 10080BE1220C5DD200C6E0A2 /* UIViewController+Branch.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B9C220C5DD200C6E0A2 /* UIViewController+Branch.m */; };
+ 10080BE2220C5DD200C6E0A2 /* BranchEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080B9E220C5DD200C6E0A2 /* BranchEvent.m */; };
+ 10080BE3220C5DD200C6E0A2 /* BNCLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080BA0220C5DD200C6E0A2 /* BNCLocalization.m */; };
+ 10080BE4220C5DD200C6E0A2 /* BranchViewHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 10080BA2220C5DD200C6E0A2 /* BranchViewHandler.m */; };
64FDEBDC20D12DCF0086C2F4 /* AFAutoPurgingImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 64FDEBDA20D12DCE0086C2F4 /* AFAutoPurgingImageCache.m */; };
64FDEBDD20D12DCF0086C2F4 /* AFImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 64FDEBDB20D12DCE0086C2F4 /* AFImageDownloader.m */; };
7F0658FD19F7D6700077FF95 /* ScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0658FC19F7D6700077FF95 /* ScanViewController.swift */; };
@@ -307,8 +366,6 @@
B3150BB320FF453D006D742A /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3150BB220FF453D006D742A /* MobileCoreServices.framework */; };
B3150BB520FF4548006D742A /* CoreSpotlight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3150BB420FF4548006D742A /* CoreSpotlight.framework */; };
B3150BB720FF454D006D742A /* iAd.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3150BB620FF454D006D742A /* iAd.framework */; };
- B31EE1A320FF915E0097AA40 /* Branch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3150BAA20FF4518006D742A /* Branch.framework */; };
- B31EE1A420FF915E0097AA40 /* Branch.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B3150BAA20FF4518006D742A /* Branch.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C8150BA51DCC6FDB0087C9C2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C8150BA71DCC6FDB0087C9C2 /* InfoPlist.strings */; };
C8289F3A1F386D8300A76953 /* SignUpRequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8289F391F386D8300A76953 /* SignUpRequestManager.swift */; };
C843D47F1EBC54A300250F01 /* CompetitorCheckIn+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = C843D4791EBC54A300250F01 /* CompetitorCheckIn+CoreDataProperties.swift */; };
@@ -374,7 +431,6 @@
C89C5FD01D06CF46002AAAD2 /* TrackingViewModeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89C5FCF1D06CF46002AAAD2 /* TrackingViewModeCell.swift */; };
C8A4400B1C18153600DF70A0 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = C8A4400A1C18153600DF70A0 /* Settings.bundle */; };
C8B3B9BC1F39E64400B3B6BA /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B3B9BB1F39E64400B3B6BA /* Keychain.swift */; };
- C8B3B9C01F3C3B4400B3B6BA /* SignUp.strings in Resources */ = {isa = PBXBuildFile; fileRef = C8B3B9BF1F3C3B4400B3B6BA /* SignUp.strings */; };
C8B3B9C21F3C6B3900B3B6BA /* SignUpTranslation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8B3B9C11F3C6B3900B3B6BA /* SignUpTranslation.swift */; };
C8BBAC791C52649E004CA551 /* LicenseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BBAC781C52649E004CA551 /* LicenseViewController.swift */; };
C8BFCCC41D51C53500FC1A27 /* TeamData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8BFCCC31D51C53500FC1A27 /* TeamData.swift */; };
@@ -428,21 +484,136 @@
};
/* End PBXContainerItemProxy section */
-/* Begin PBXCopyFilesBuildPhase section */
- B31EE1A520FF915E0097AA40 /* Embed Frameworks */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = "";
- dstSubfolderSpec = 10;
- files = (
- B31EE1A420FF915E0097AA40 /* Branch.framework in Embed Frameworks */,
- );
- name = "Embed Frameworks";
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXCopyFilesBuildPhase section */
-
/* Begin PBXFileReference section */
+ 10080B27220C5DD200C6E0A2 /* BNCLinkData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCLinkData.h; sourceTree = ""; };
+ 10080B28220C5DD200C6E0A2 /* BranchDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchDelegate.h; sourceTree = ""; };
+ 10080B29220C5DD200C6E0A2 /* BNCError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCError.m; sourceTree = ""; };
+ 10080B2A220C5DD200C6E0A2 /* BNCKeyChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCKeyChain.h; sourceTree = ""; };
+ 10080B2B220C5DD200C6E0A2 /* BNCEncodingUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCEncodingUtils.m; sourceTree = ""; };
+ 10080B2C220C5DD200C6E0A2 /* BranchUniversalObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchUniversalObject.m; sourceTree = ""; };
+ 10080B2D220C5DD200C6E0A2 /* BNCApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCApplication.m; sourceTree = ""; };
+ 10080B2E220C5DD200C6E0A2 /* NSMutableDictionary+Branch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableDictionary+Branch.m"; sourceTree = ""; };
+ 10080B2F220C5DD200C6E0A2 /* BranchActivityItemProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchActivityItemProvider.h; sourceTree = ""; };
+ 10080B30220C5DD200C6E0A2 /* BNCStrongMatchHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCStrongMatchHelper.h; sourceTree = ""; };
+ 10080B31220C5DD200C6E0A2 /* BranchCSSearchableItemAttributeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchCSSearchableItemAttributeSet.h; sourceTree = ""; };
+ 10080B32220C5DD200C6E0A2 /* BNCDebug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCDebug.m; sourceTree = ""; };
+ 10080B33220C5DD200C6E0A2 /* BranchLinkProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchLinkProperties.h; sourceTree = ""; };
+ 10080B34220C5DD200C6E0A2 /* BNCDeepLinkViewControllerInstance.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCDeepLinkViewControllerInstance.m; sourceTree = ""; };
+ 10080B35220C5DD200C6E0A2 /* BNCContentDiscoveryManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCContentDiscoveryManager.m; sourceTree = ""; };
+ 10080B36220C5DD200C6E0A2 /* Branch+Validator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Branch+Validator.m"; sourceTree = ""; };
+ 10080B37220C5DD200C6E0A2 /* BranchContentPathProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchContentPathProperties.h; sourceTree = ""; };
+ 10080B38220C5DD200C6E0A2 /* NSString+Branch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Branch.m"; sourceTree = ""; };
+ 10080B39220C5DD200C6E0A2 /* BNCSystemObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCSystemObserver.m; sourceTree = ""; };
+ 10080B3A220C5DD200C6E0A2 /* BNCCommerceEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCCommerceEvent.m; sourceTree = ""; };
+ 10080B3B220C5DD200C6E0A2 /* BranchView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchView.h; sourceTree = ""; };
+ 10080B3C220C5DD200C6E0A2 /* BNCThreads.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCThreads.m; sourceTree = ""; };
+ 10080B3D220C5DD200C6E0A2 /* BNCAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCAvailability.h; sourceTree = ""; };
+ 10080B3E220C5DD200C6E0A2 /* Branch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Branch.h; sourceTree = ""; };
+ 10080B3F220C5DD200C6E0A2 /* BNCPreferenceHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCPreferenceHelper.m; sourceTree = ""; };
+ 10080B40220C5DD200C6E0A2 /* BNCCallbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCCallbacks.h; sourceTree = ""; };
+ 10080B41220C5DD200C6E0A2 /* UIViewController+Branch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Branch.h"; sourceTree = ""; };
+ 10080B42220C5DD200C6E0A2 /* BranchShareLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchShareLink.h; sourceTree = ""; };
+ 10080B43220C5DD200C6E0A2 /* BNCFieldDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCFieldDefines.h; sourceTree = ""; };
+ 10080B45220C5DD200C6E0A2 /* BNCServerRequestQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCServerRequestQueue.m; sourceTree = ""; };
+ 10080B46220C5DD200C6E0A2 /* BNCServerRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCServerRequest.m; sourceTree = ""; };
+ 10080B47220C5DD200C6E0A2 /* BNCServerResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCServerResponse.h; sourceTree = ""; };
+ 10080B48220C5DD200C6E0A2 /* BNCNetworkService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCNetworkService.m; sourceTree = ""; };
+ 10080B49220C5DD200C6E0A2 /* BNCServerInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCServerInterface.m; sourceTree = ""; };
+ 10080B4A220C5DD200C6E0A2 /* BNCNetworkServiceProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCNetworkServiceProtocol.h; sourceTree = ""; };
+ 10080B4C220C5DD200C6E0A2 /* BranchLogoutRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchLogoutRequest.m; sourceTree = ""; };
+ 10080B4D220C5DD200C6E0A2 /* BranchInstallRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchInstallRequest.m; sourceTree = ""; };
+ 10080B4E220C5DD200C6E0A2 /* BranchCreditHistoryRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchCreditHistoryRequest.h; sourceTree = ""; };
+ 10080B4F220C5DD200C6E0A2 /* BranchSetIdentityRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchSetIdentityRequest.m; sourceTree = ""; };
+ 10080B50220C5DD200C6E0A2 /* BranchUserCompletedActionRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchUserCompletedActionRequest.m; sourceTree = ""; };
+ 10080B51220C5DD200C6E0A2 /* BranchRegisterViewRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchRegisterViewRequest.m; sourceTree = ""; };
+ 10080B52220C5DD200C6E0A2 /* BranchCloseRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchCloseRequest.m; sourceTree = ""; };
+ 10080B53220C5DD200C6E0A2 /* BranchRedeemRewardsRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchRedeemRewardsRequest.h; sourceTree = ""; };
+ 10080B54220C5DD200C6E0A2 /* BranchSpotlightUrlRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchSpotlightUrlRequest.m; sourceTree = ""; };
+ 10080B55220C5DD200C6E0A2 /* BranchLoadRewardsRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchLoadRewardsRequest.m; sourceTree = ""; };
+ 10080B56220C5DD200C6E0A2 /* BranchShortUrlSyncRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchShortUrlSyncRequest.h; sourceTree = ""; };
+ 10080B57220C5DD200C6E0A2 /* BranchOpenRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchOpenRequest.h; sourceTree = ""; };
+ 10080B58220C5DD200C6E0A2 /* BranchShortUrlRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchShortUrlRequest.h; sourceTree = ""; };
+ 10080B59220C5DD200C6E0A2 /* BranchLogoutRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchLogoutRequest.h; sourceTree = ""; };
+ 10080B5A220C5DD200C6E0A2 /* BranchUserCompletedActionRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchUserCompletedActionRequest.h; sourceTree = ""; };
+ 10080B5B220C5DD200C6E0A2 /* BranchRegisterViewRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchRegisterViewRequest.h; sourceTree = ""; };
+ 10080B5C220C5DD200C6E0A2 /* BranchCreditHistoryRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchCreditHistoryRequest.m; sourceTree = ""; };
+ 10080B5D220C5DD200C6E0A2 /* BranchSetIdentityRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchSetIdentityRequest.h; sourceTree = ""; };
+ 10080B5E220C5DD200C6E0A2 /* BranchInstallRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchInstallRequest.h; sourceTree = ""; };
+ 10080B5F220C5DD200C6E0A2 /* BranchRedeemRewardsRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchRedeemRewardsRequest.m; sourceTree = ""; };
+ 10080B60220C5DD200C6E0A2 /* BranchSpotlightUrlRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchSpotlightUrlRequest.h; sourceTree = ""; };
+ 10080B61220C5DD200C6E0A2 /* BranchCloseRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchCloseRequest.h; sourceTree = ""; };
+ 10080B62220C5DD200C6E0A2 /* BranchShortUrlRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchShortUrlRequest.m; sourceTree = ""; };
+ 10080B63220C5DD200C6E0A2 /* BranchOpenRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchOpenRequest.m; sourceTree = ""; };
+ 10080B64220C5DD200C6E0A2 /* BranchShortUrlSyncRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchShortUrlSyncRequest.m; sourceTree = ""; };
+ 10080B65220C5DD200C6E0A2 /* BranchLoadRewardsRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchLoadRewardsRequest.h; sourceTree = ""; };
+ 10080B66220C5DD200C6E0A2 /* BNCServerRequestQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCServerRequestQueue.h; sourceTree = ""; };
+ 10080B67220C5DD200C6E0A2 /* BNCServerRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCServerRequest.h; sourceTree = ""; };
+ 10080B68220C5DD200C6E0A2 /* BNCServerResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCServerResponse.m; sourceTree = ""; };
+ 10080B69220C5DD200C6E0A2 /* BNCServerInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCServerInterface.h; sourceTree = ""; };
+ 10080B6A220C5DD200C6E0A2 /* BNCNetworkService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCNetworkService.h; sourceTree = ""; };
+ 10080B6B220C5DD200C6E0A2 /* BNCConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCConfig.m; sourceTree = ""; };
+ 10080B6C220C5DD200C6E0A2 /* BranchConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchConstants.m; sourceTree = ""; };
+ 10080B6D220C5DD200C6E0A2 /* BNCSpotlightService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCSpotlightService.h; sourceTree = ""; };
+ 10080B6E220C5DD200C6E0A2 /* BranchContentDiscoveryManifest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchContentDiscoveryManifest.m; sourceTree = ""; };
+ 10080B6F220C5DD200C6E0A2 /* BNCLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCLog.m; sourceTree = ""; };
+ 10080B70220C5DD200C6E0A2 /* BranchContentDiscoverer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchContentDiscoverer.m; sourceTree = ""; };
+ 10080B71220C5DD200C6E0A2 /* BNCDeviceInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCDeviceInfo.m; sourceTree = ""; };
+ 10080B72220C5DD200C6E0A2 /* BNCLinkCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCLinkCache.h; sourceTree = ""; };
+ 10080B73220C5DD200C6E0A2 /* BNCFabricAnswers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCFabricAnswers.m; sourceTree = ""; };
+ 10080B74220C5DD200C6E0A2 /* BranchViewHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchViewHandler.h; sourceTree = ""; };
+ 10080B75220C5DD200C6E0A2 /* BNCURLBlackList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCURLBlackList.m; sourceTree = ""; };
+ 10080B76220C5DD200C6E0A2 /* BNCLocalization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCLocalization.h; sourceTree = ""; };
+ 10080B77220C5DD200C6E0A2 /* BNCCrashlyticsWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCCrashlyticsWrapper.m; sourceTree = ""; };
+ 10080B78220C5DD200C6E0A2 /* BranchEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchEvent.h; sourceTree = ""; };
+ 10080B79220C5DD200C6E0A2 /* BNCDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCDebug.h; sourceTree = ""; };
+ 10080B7A220C5DD200C6E0A2 /* BranchCSSearchableItemAttributeSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchCSSearchableItemAttributeSet.m; sourceTree = ""; };
+ 10080B7B220C5DD200C6E0A2 /* BNCStrongMatchHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCStrongMatchHelper.m; sourceTree = ""; };
+ 10080B7C220C5DD200C6E0A2 /* BranchActivityItemProvider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchActivityItemProvider.m; sourceTree = ""; };
+ 10080B7D220C5DD200C6E0A2 /* BNCApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCApplication.h; sourceTree = ""; };
+ 10080B7E220C5DD200C6E0A2 /* NSMutableDictionary+Branch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableDictionary+Branch.h"; sourceTree = ""; };
+ 10080B7F220C5DD200C6E0A2 /* BranchUniversalObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchUniversalObject.h; sourceTree = ""; };
+ 10080B80220C5DD200C6E0A2 /* BNCEncodingUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCEncodingUtils.h; sourceTree = ""; };
+ 10080B81220C5DD200C6E0A2 /* BNCKeyChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCKeyChain.m; sourceTree = ""; };
+ 10080B82220C5DD200C6E0A2 /* BNCError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCError.h; sourceTree = ""; };
+ 10080B83220C5DD200C6E0A2 /* BranchDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchDelegate.m; sourceTree = ""; };
+ 10080B84220C5DD200C6E0A2 /* BNCLinkData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCLinkData.m; sourceTree = ""; };
+ 10080B85220C5DD200C6E0A2 /* Branch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Branch.m; sourceTree = ""; };
+ 10080B86220C5DD200C6E0A2 /* BNCAvailability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCAvailability.m; sourceTree = ""; };
+ 10080B87220C5DD200C6E0A2 /* BranchDeepLinkingController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchDeepLinkingController.h; sourceTree = ""; };
+ 10080B88220C5DD200C6E0A2 /* BNCThreads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCThreads.h; sourceTree = ""; };
+ 10080B89220C5DD200C6E0A2 /* BNCSystemObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCSystemObserver.h; sourceTree = ""; };
+ 10080B8A220C5DD200C6E0A2 /* BNCCommerceEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCCommerceEvent.h; sourceTree = ""; };
+ 10080B8B220C5DD200C6E0A2 /* BranchView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchView.m; sourceTree = ""; };
+ 10080B8C220C5DD200C6E0A2 /* NSString+Branch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Branch.h"; sourceTree = ""; };
+ 10080B8D220C5DD200C6E0A2 /* BranchContentPathProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchContentPathProperties.m; sourceTree = ""; };
+ 10080B8E220C5DD200C6E0A2 /* Branch+Validator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Branch+Validator.h"; sourceTree = ""; };
+ 10080B8F220C5DD200C6E0A2 /* BranchLinkProperties.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchLinkProperties.m; sourceTree = ""; };
+ 10080B90220C5DD200C6E0A2 /* BNCDeepLinkViewControllerInstance.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCDeepLinkViewControllerInstance.h; sourceTree = ""; };
+ 10080B91220C5DD200C6E0A2 /* BNCContentDiscoveryManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCContentDiscoveryManager.h; sourceTree = ""; };
+ 10080B92220C5DD200C6E0A2 /* BNCFabricAnswers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCFabricAnswers.h; sourceTree = ""; };
+ 10080B93220C5DD200C6E0A2 /* BNCDeviceInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCDeviceInfo.h; sourceTree = ""; };
+ 10080B94220C5DD200C6E0A2 /* BNCLinkCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCLinkCache.m; sourceTree = ""; };
+ 10080B95220C5DD200C6E0A2 /* BNCLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCLog.h; sourceTree = ""; };
+ 10080B96220C5DD200C6E0A2 /* BranchContentDiscoverer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchContentDiscoverer.h; sourceTree = ""; };
+ 10080B97220C5DD200C6E0A2 /* BranchContentDiscoveryManifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchContentDiscoveryManifest.h; sourceTree = ""; };
+ 10080B98220C5DD200C6E0A2 /* BNCConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCConfig.h; sourceTree = ""; };
+ 10080B99220C5DD200C6E0A2 /* BranchConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BranchConstants.h; sourceTree = ""; };
+ 10080B9A220C5DD200C6E0A2 /* BNCSpotlightService.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCSpotlightService.m; sourceTree = ""; };
+ 10080B9B220C5DD200C6E0A2 /* BranchShareLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchShareLink.m; sourceTree = ""; };
+ 10080B9C220C5DD200C6E0A2 /* UIViewController+Branch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Branch.m"; sourceTree = ""; };
+ 10080B9D220C5DD200C6E0A2 /* BNCPreferenceHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCPreferenceHelper.h; sourceTree = ""; };
+ 10080B9E220C5DD200C6E0A2 /* BranchEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchEvent.m; sourceTree = ""; };
+ 10080B9F220C5DD200C6E0A2 /* BNCCrashlyticsWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCCrashlyticsWrapper.h; sourceTree = ""; };
+ 10080BA0220C5DD200C6E0A2 /* BNCLocalization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BNCLocalization.m; sourceTree = ""; };
+ 10080BA1220C5DD200C6E0A2 /* BNCURLBlackList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BNCURLBlackList.h; sourceTree = ""; };
+ 10080BA2220C5DD200C6E0A2 /* BranchViewHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BranchViewHandler.m; sourceTree = ""; };
+ 10080BA4220C5DD200C6E0A2 /* ANSCompatibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ANSCompatibility.h; sourceTree = ""; };
+ 10080BA5220C5DD200C6E0A2 /* Fabric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Fabric.h; sourceTree = ""; };
+ 10080BA6220C5DD200C6E0A2 /* Answers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Answers.h; sourceTree = ""; };
+ 10080BA7220C5DD200C6E0A2 /* FABKitProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABKitProtocol.h; sourceTree = ""; };
+ 10080BA8220C5DD200C6E0A2 /* Fabric+FABKits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Fabric+FABKits.h"; sourceTree = ""; };
+ 10080BA9220C5DD200C6E0A2 /* FABAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FABAttributes.h; sourceTree = ""; };
+ 10C76537224BA455003254D8 /* CoreData 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "CoreData 5.xcdatamodel"; sourceTree = ""; };
64FDEBD220D12C390086C2F4 /* AFImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFImageDownloader.h; sourceTree = ""; };
64FDEBD320D12C390086C2F4 /* AFAutoPurgingImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFAutoPurgingImageCache.h; sourceTree = ""; };
64FDEBDA20D12DCE0086C2F4 /* AFAutoPurgingImageCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AFAutoPurgingImageCache.m; sourceTree = ""; };
@@ -743,7 +914,6 @@
9E95C3D51C723AB30016632B /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; };
9ECBDDAD1C9C4F0300F76AEB /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; };
B3150B2920FE3280006D742A /* SAPTracker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SAPTracker.entitlements; sourceTree = ""; };
- B3150BAA20FF4518006D742A /* Branch.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Branch.framework; path = SAPTracker/Libraries/Branch.framework; sourceTree = ""; };
B3150BAE20FF452C006D742A /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; };
B3150BB020FF4532006D742A /* SafariServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariServices.framework; path = System/Library/Frameworks/SafariServices.framework; sourceTree = SDKROOT; };
B3150BB220FF453D006D742A /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
@@ -818,7 +988,6 @@
C89C5FCF1D06CF46002AAAD2 /* TrackingViewModeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingViewModeCell.swift; sourceTree = ""; };
C8A4400A1C18153600DF70A0 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; };
C8B3B9BB1F39E64400B3B6BA /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; };
- C8B3B9BF1F3C3B4400B3B6BA /* SignUp.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = SignUp.strings; sourceTree = ""; };
C8B3B9C11F3C6B3900B3B6BA /* SignUpTranslation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignUpTranslation.swift; sourceTree = ""; };
C8BBAC781C52649E004CA551 /* LicenseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LicenseViewController.swift; sourceTree = ""; };
C8BFCCC31D51C53500FC1A27 /* TeamData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TeamData.swift; sourceTree = ""; };
@@ -894,7 +1063,6 @@
files = (
B3150BB720FF454D006D742A /* iAd.framework in Frameworks */,
B3150BB520FF4548006D742A /* CoreSpotlight.framework in Frameworks */,
- B31EE1A320FF915E0097AA40 /* Branch.framework in Frameworks */,
B3150BB320FF453D006D742A /* MobileCoreServices.framework in Frameworks */,
B3150BB120FF4532006D742A /* SafariServices.framework in Frameworks */,
B3150BAF20FF452C006D742A /* AdSupport.framework in Frameworks */,
@@ -915,10 +1083,176 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 10080B25220C5DD200C6E0A2 /* Branch-SDK */ = {
+ isa = PBXGroup;
+ children = (
+ 10080B26220C5DD200C6E0A2 /* Branch-SDK */,
+ 10080BA3220C5DD200C6E0A2 /* Fabric */,
+ );
+ path = "Branch-SDK";
+ sourceTree = "";
+ };
+ 10080B26220C5DD200C6E0A2 /* Branch-SDK */ = {
+ isa = PBXGroup;
+ children = (
+ 10080B27220C5DD200C6E0A2 /* BNCLinkData.h */,
+ 10080B28220C5DD200C6E0A2 /* BranchDelegate.h */,
+ 10080B29220C5DD200C6E0A2 /* BNCError.m */,
+ 10080B2A220C5DD200C6E0A2 /* BNCKeyChain.h */,
+ 10080B2B220C5DD200C6E0A2 /* BNCEncodingUtils.m */,
+ 10080B2C220C5DD200C6E0A2 /* BranchUniversalObject.m */,
+ 10080B2D220C5DD200C6E0A2 /* BNCApplication.m */,
+ 10080B2E220C5DD200C6E0A2 /* NSMutableDictionary+Branch.m */,
+ 10080B2F220C5DD200C6E0A2 /* BranchActivityItemProvider.h */,
+ 10080B30220C5DD200C6E0A2 /* BNCStrongMatchHelper.h */,
+ 10080B31220C5DD200C6E0A2 /* BranchCSSearchableItemAttributeSet.h */,
+ 10080B32220C5DD200C6E0A2 /* BNCDebug.m */,
+ 10080B33220C5DD200C6E0A2 /* BranchLinkProperties.h */,
+ 10080B34220C5DD200C6E0A2 /* BNCDeepLinkViewControllerInstance.m */,
+ 10080B35220C5DD200C6E0A2 /* BNCContentDiscoveryManager.m */,
+ 10080B36220C5DD200C6E0A2 /* Branch+Validator.m */,
+ 10080B37220C5DD200C6E0A2 /* BranchContentPathProperties.h */,
+ 10080B38220C5DD200C6E0A2 /* NSString+Branch.m */,
+ 10080B39220C5DD200C6E0A2 /* BNCSystemObserver.m */,
+ 10080B3A220C5DD200C6E0A2 /* BNCCommerceEvent.m */,
+ 10080B3B220C5DD200C6E0A2 /* BranchView.h */,
+ 10080B3C220C5DD200C6E0A2 /* BNCThreads.m */,
+ 10080B3D220C5DD200C6E0A2 /* BNCAvailability.h */,
+ 10080B3E220C5DD200C6E0A2 /* Branch.h */,
+ 10080B3F220C5DD200C6E0A2 /* BNCPreferenceHelper.m */,
+ 10080B40220C5DD200C6E0A2 /* BNCCallbacks.h */,
+ 10080B41220C5DD200C6E0A2 /* UIViewController+Branch.h */,
+ 10080B42220C5DD200C6E0A2 /* BranchShareLink.h */,
+ 10080B43220C5DD200C6E0A2 /* BNCFieldDefines.h */,
+ 10080B44220C5DD200C6E0A2 /* Networking */,
+ 10080B6B220C5DD200C6E0A2 /* BNCConfig.m */,
+ 10080B6C220C5DD200C6E0A2 /* BranchConstants.m */,
+ 10080B6D220C5DD200C6E0A2 /* BNCSpotlightService.h */,
+ 10080B6E220C5DD200C6E0A2 /* BranchContentDiscoveryManifest.m */,
+ 10080B6F220C5DD200C6E0A2 /* BNCLog.m */,
+ 10080B70220C5DD200C6E0A2 /* BranchContentDiscoverer.m */,
+ 10080B71220C5DD200C6E0A2 /* BNCDeviceInfo.m */,
+ 10080B72220C5DD200C6E0A2 /* BNCLinkCache.h */,
+ 10080B73220C5DD200C6E0A2 /* BNCFabricAnswers.m */,
+ 10080B74220C5DD200C6E0A2 /* BranchViewHandler.h */,
+ 10080B75220C5DD200C6E0A2 /* BNCURLBlackList.m */,
+ 10080B76220C5DD200C6E0A2 /* BNCLocalization.h */,
+ 10080B77220C5DD200C6E0A2 /* BNCCrashlyticsWrapper.m */,
+ 10080B78220C5DD200C6E0A2 /* BranchEvent.h */,
+ 10080B79220C5DD200C6E0A2 /* BNCDebug.h */,
+ 10080B7A220C5DD200C6E0A2 /* BranchCSSearchableItemAttributeSet.m */,
+ 10080B7B220C5DD200C6E0A2 /* BNCStrongMatchHelper.m */,
+ 10080B7C220C5DD200C6E0A2 /* BranchActivityItemProvider.m */,
+ 10080B7D220C5DD200C6E0A2 /* BNCApplication.h */,
+ 10080B7E220C5DD200C6E0A2 /* NSMutableDictionary+Branch.h */,
+ 10080B7F220C5DD200C6E0A2 /* BranchUniversalObject.h */,
+ 10080B80220C5DD200C6E0A2 /* BNCEncodingUtils.h */,
+ 10080B81220C5DD200C6E0A2 /* BNCKeyChain.m */,
+ 10080B82220C5DD200C6E0A2 /* BNCError.h */,
+ 10080B83220C5DD200C6E0A2 /* BranchDelegate.m */,
+ 10080B84220C5DD200C6E0A2 /* BNCLinkData.m */,
+ 10080B85220C5DD200C6E0A2 /* Branch.m */,
+ 10080B86220C5DD200C6E0A2 /* BNCAvailability.m */,
+ 10080B87220C5DD200C6E0A2 /* BranchDeepLinkingController.h */,
+ 10080B88220C5DD200C6E0A2 /* BNCThreads.h */,
+ 10080B89220C5DD200C6E0A2 /* BNCSystemObserver.h */,
+ 10080B8A220C5DD200C6E0A2 /* BNCCommerceEvent.h */,
+ 10080B8B220C5DD200C6E0A2 /* BranchView.m */,
+ 10080B8C220C5DD200C6E0A2 /* NSString+Branch.h */,
+ 10080B8D220C5DD200C6E0A2 /* BranchContentPathProperties.m */,
+ 10080B8E220C5DD200C6E0A2 /* Branch+Validator.h */,
+ 10080B8F220C5DD200C6E0A2 /* BranchLinkProperties.m */,
+ 10080B90220C5DD200C6E0A2 /* BNCDeepLinkViewControllerInstance.h */,
+ 10080B91220C5DD200C6E0A2 /* BNCContentDiscoveryManager.h */,
+ 10080B92220C5DD200C6E0A2 /* BNCFabricAnswers.h */,
+ 10080B93220C5DD200C6E0A2 /* BNCDeviceInfo.h */,
+ 10080B94220C5DD200C6E0A2 /* BNCLinkCache.m */,
+ 10080B95220C5DD200C6E0A2 /* BNCLog.h */,
+ 10080B96220C5DD200C6E0A2 /* BranchContentDiscoverer.h */,
+ 10080B97220C5DD200C6E0A2 /* BranchContentDiscoveryManifest.h */,
+ 10080B98220C5DD200C6E0A2 /* BNCConfig.h */,
+ 10080B99220C5DD200C6E0A2 /* BranchConstants.h */,
+ 10080B9A220C5DD200C6E0A2 /* BNCSpotlightService.m */,
+ 10080B9B220C5DD200C6E0A2 /* BranchShareLink.m */,
+ 10080B9C220C5DD200C6E0A2 /* UIViewController+Branch.m */,
+ 10080B9D220C5DD200C6E0A2 /* BNCPreferenceHelper.h */,
+ 10080B9E220C5DD200C6E0A2 /* BranchEvent.m */,
+ 10080B9F220C5DD200C6E0A2 /* BNCCrashlyticsWrapper.h */,
+ 10080BA0220C5DD200C6E0A2 /* BNCLocalization.m */,
+ 10080BA1220C5DD200C6E0A2 /* BNCURLBlackList.h */,
+ 10080BA2220C5DD200C6E0A2 /* BranchViewHandler.m */,
+ );
+ path = "Branch-SDK";
+ sourceTree = "";
+ };
+ 10080B44220C5DD200C6E0A2 /* Networking */ = {
+ isa = PBXGroup;
+ children = (
+ 10080B45220C5DD200C6E0A2 /* BNCServerRequestQueue.m */,
+ 10080B46220C5DD200C6E0A2 /* BNCServerRequest.m */,
+ 10080B47220C5DD200C6E0A2 /* BNCServerResponse.h */,
+ 10080B48220C5DD200C6E0A2 /* BNCNetworkService.m */,
+ 10080B49220C5DD200C6E0A2 /* BNCServerInterface.m */,
+ 10080B4A220C5DD200C6E0A2 /* BNCNetworkServiceProtocol.h */,
+ 10080B4B220C5DD200C6E0A2 /* Requests */,
+ 10080B66220C5DD200C6E0A2 /* BNCServerRequestQueue.h */,
+ 10080B67220C5DD200C6E0A2 /* BNCServerRequest.h */,
+ 10080B68220C5DD200C6E0A2 /* BNCServerResponse.m */,
+ 10080B69220C5DD200C6E0A2 /* BNCServerInterface.h */,
+ 10080B6A220C5DD200C6E0A2 /* BNCNetworkService.h */,
+ );
+ path = Networking;
+ sourceTree = "";
+ };
+ 10080B4B220C5DD200C6E0A2 /* Requests */ = {
+ isa = PBXGroup;
+ children = (
+ 10080B4C220C5DD200C6E0A2 /* BranchLogoutRequest.m */,
+ 10080B4D220C5DD200C6E0A2 /* BranchInstallRequest.m */,
+ 10080B4E220C5DD200C6E0A2 /* BranchCreditHistoryRequest.h */,
+ 10080B4F220C5DD200C6E0A2 /* BranchSetIdentityRequest.m */,
+ 10080B50220C5DD200C6E0A2 /* BranchUserCompletedActionRequest.m */,
+ 10080B51220C5DD200C6E0A2 /* BranchRegisterViewRequest.m */,
+ 10080B52220C5DD200C6E0A2 /* BranchCloseRequest.m */,
+ 10080B53220C5DD200C6E0A2 /* BranchRedeemRewardsRequest.h */,
+ 10080B54220C5DD200C6E0A2 /* BranchSpotlightUrlRequest.m */,
+ 10080B55220C5DD200C6E0A2 /* BranchLoadRewardsRequest.m */,
+ 10080B56220C5DD200C6E0A2 /* BranchShortUrlSyncRequest.h */,
+ 10080B57220C5DD200C6E0A2 /* BranchOpenRequest.h */,
+ 10080B58220C5DD200C6E0A2 /* BranchShortUrlRequest.h */,
+ 10080B59220C5DD200C6E0A2 /* BranchLogoutRequest.h */,
+ 10080B5A220C5DD200C6E0A2 /* BranchUserCompletedActionRequest.h */,
+ 10080B5B220C5DD200C6E0A2 /* BranchRegisterViewRequest.h */,
+ 10080B5C220C5DD200C6E0A2 /* BranchCreditHistoryRequest.m */,
+ 10080B5D220C5DD200C6E0A2 /* BranchSetIdentityRequest.h */,
+ 10080B5E220C5DD200C6E0A2 /* BranchInstallRequest.h */,
+ 10080B5F220C5DD200C6E0A2 /* BranchRedeemRewardsRequest.m */,
+ 10080B60220C5DD200C6E0A2 /* BranchSpotlightUrlRequest.h */,
+ 10080B61220C5DD200C6E0A2 /* BranchCloseRequest.h */,
+ 10080B62220C5DD200C6E0A2 /* BranchShortUrlRequest.m */,
+ 10080B63220C5DD200C6E0A2 /* BranchOpenRequest.m */,
+ 10080B64220C5DD200C6E0A2 /* BranchShortUrlSyncRequest.m */,
+ 10080B65220C5DD200C6E0A2 /* BranchLoadRewardsRequest.h */,
+ );
+ path = Requests;
+ sourceTree = "";
+ };
+ 10080BA3220C5DD200C6E0A2 /* Fabric */ = {
+ isa = PBXGroup;
+ children = (
+ 10080BA4220C5DD200C6E0A2 /* ANSCompatibility.h */,
+ 10080BA5220C5DD200C6E0A2 /* Fabric.h */,
+ 10080BA6220C5DD200C6E0A2 /* Answers.h */,
+ 10080BA7220C5DD200C6E0A2 /* FABKitProtocol.h */,
+ 10080BA8220C5DD200C6E0A2 /* Fabric+FABKits.h */,
+ 10080BA9220C5DD200C6E0A2 /* FABAttributes.h */,
+ );
+ path = Fabric;
+ sourceTree = "";
+ };
7AC6457280AEA3EC18F5E846 /* Frameworks */ = {
isa = PBXGroup;
children = (
- B3150BAA20FF4518006D742A /* Branch.framework */,
B3150BB620FF454D006D742A /* iAd.framework */,
B3150BB420FF4548006D742A /* CoreSpotlight.framework */,
B3150BB220FF453D006D742A /* MobileCoreServices.framework */,
@@ -1292,7 +1626,6 @@
9ECBDDAD1C9C4F0300F76AEB /* LaunchScreen.storyboard */,
C8D5E81C1D479FA800DA0D61 /* Main.storyboard */,
C85DB8241F30B680009559CE /* SignUp.storyboard */,
- C8B3B9BF1F3C3B4400B3B6BA /* SignUp.strings */,
9E95C3D51C723AB30016632B /* Constants.swift */,
7FD9D3021A1236080058FEAD /* Extensions.swift */,
C8B3B9C11F3C6B3900B3B6BA /* SignUpTranslation.swift */,
@@ -1589,6 +1922,7 @@
C8F71EA31D8691F700826DE4 /* Libraries */ = {
isa = PBXGroup;
children = (
+ 10080B25220C5DD200C6E0A2 /* Branch-SDK */,
C8BFCC1B1D50F17800FC1A27 /* SMTWiFiStatus */,
C8F71EA41D8691F700826DE4 /* AFNetworking */,
C8F71EB81D8691F700826DE4 /* SVProgressHUD */,
@@ -1682,8 +2016,7 @@
7F83F14C19F154F000C53328 /* Sources */,
7F83F14D19F154F000C53328 /* Frameworks */,
7F83F14E19F154F000C53328 /* Resources */,
- 7FF3E4BE1A1DFCF20012F1FB /* ShellScript */,
- B31EE1A520FF915E0097AA40 /* Embed Frameworks */,
+ 7FF3E4BE1A1DFCF20012F1FB /* Run Script */,
);
buildRules = (
);
@@ -1723,9 +2056,8 @@
TargetAttributes = {
7F83F14F19F154F000C53328 = {
CreatedOnToolsVersion = 6.0.1;
- DevelopmentTeam = F9U235EA4J;
LastSwiftMigration = 0820;
- ProvisioningStyle = Automatic;
+ ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.SafariKeychain = {
enabled = 1;
@@ -1826,7 +2158,6 @@
7F14A1681A13B9A6001C1ECB /* GA.png in Resources */,
7F14A10C1A13B9A6001C1ECB /* _british-antarctic-territory.png in Resources */,
7F14A1771A13B9A6001C1ECB /* GW.png in Resources */,
- C8B3B9C01F3C3B4400B3B6BA /* SignUp.strings in Resources */,
7F14A1811A13B9A6001C1ECB /* IE.png in Resources */,
7F14A14D1A13B9A6001C1ECB /* CR.png in Resources */,
7F14A1991A13B9A6001C1ECB /* LB.png in Resources */,
@@ -2054,18 +2385,19 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 7FF3E4BE1A1DFCF20012F1FB /* ShellScript */ = {
+ 7FF3E4BE1A1DFCF20012F1FB /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
+ name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "FILE=\"${SRCROOT}/HockeySDK-iOS/BuildAgent\"\nif [ -f \"$FILE\" ]; then\n\"$FILE\"\nfi";
+ shellScript = "FILE=\"${SRCROOT}/HockeySDK-iOS/BuildAgent\"\nif [ -f \"$FILE\" ]; then\n\"$FILE\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */
@@ -2075,14 +2407,19 @@
buildActionMask = 2147483647;
files = (
64FDEBDC20D12DCF0086C2F4 /* AFAutoPurgingImageCache.m in Sources */,
+ 10080BCF220C5DD200C6E0A2 /* BNCDeviceInfo.m in Sources */,
64FDEBDD20D12DCF0086C2F4 /* AFImageDownloader.m in Sources */,
C867BD091F4B0274007E78E0 /* TrainingCompetitorViewController.swift in Sources */,
C85DB82A1F31AAFD009559CE /* LoginViewController.swift in Sources */,
7F1875B31A10F1B800C25BE0 /* TrackingViewController.swift in Sources */,
+ 10080BCE220C5DD200C6E0A2 /* BranchContentDiscoverer.m in Sources */,
+ 10080BC8220C5DD200C6E0A2 /* BranchShortUrlSyncRequest.m in Sources */,
C8739E081D2AA2A500A89F3C /* Leaderboard+CoreDataProperties.swift in Sources */,
C8739E121D2BF14F00A89F3C /* EventData.swift in Sources */,
+ 10080BAD220C5DD200C6E0A2 /* BNCApplication.m in Sources */,
C843D4881EBCADDC00250F01 /* CompetitorSessionController.swift in Sources */,
C880646E2087DA1F005FBE57 /* BoatClassData.swift in Sources */,
+ 10080BC6220C5DD200C6E0A2 /* BranchShortUrlRequest.m in Sources */,
C891887A2057F55100E37CC3 /* BoatSessionController.swift in Sources */,
C8F71EE31D8691F700826DE4 /* SVIndefiniteAnimatedView.m in Sources */,
C8739E0F1D2BEE7900A89F3C /* SessionController.swift in Sources */,
@@ -2091,14 +2428,23 @@
7F8B8D431A373D4400C34B88 /* CourseViewController.swift in Sources */,
7FAFA7951A126B5700D230F1 /* AboutViewController.swift in Sources */,
C8739E161D2BF17900A89F3C /* CompetitorData.swift in Sources */,
+ 10080BAB220C5DD200C6E0A2 /* BNCEncodingUtils.m in Sources */,
C8289F3A1F386D8300A76953 /* SignUpRequestManager.swift in Sources */,
+ 10080BD7220C5DD200C6E0A2 /* BranchDelegate.m in Sources */,
+ 10080BCC220C5DD200C6E0A2 /* BranchContentDiscoveryManifest.m in Sources */,
+ 10080BB5220C5DD200C6E0A2 /* BNCCommerceEvent.m in Sources */,
+ 10080BE0220C5DD200C6E0A2 /* BranchShareLink.m in Sources */,
C8739E0C1D2AA2A500A89F3C /* Event+CoreDataProperties.swift in Sources */,
C8739E071D2AA2A500A89F3C /* GPSFix.swift in Sources */,
C8E0BD0D1F39ADAD002FCCA5 /* KeychainPasswordItem.swift in Sources */,
C891887C20580A4100E37CC3 /* TrainingBoatViewController.swift in Sources */,
C87A74E11F4EE570003DE396 /* TrainingRequestManager.swift in Sources */,
+ 10080BB0220C5DD200C6E0A2 /* BNCDeepLinkViewControllerInstance.m in Sources */,
+ 10080BB7220C5DD200C6E0A2 /* BNCPreferenceHelper.m in Sources */,
C89188762057F36300E37CC3 /* BoatSessionViewController.swift in Sources */,
C843D4811EBC54A300250F01 /* MarkCheckIn+CoreDataProperties.swift in Sources */,
+ 10080BB6220C5DD200C6E0A2 /* BNCThreads.m in Sources */,
+ 10080BAE220C5DD200C6E0A2 /* NSMutableDictionary+Branch.m in Sources */,
C843D532201F3050003725F0 /* NewCheckInViewController.swift in Sources */,
C8F71EEE1D8691F700826DE4 /* UIProgressView+AFNetworking.m in Sources */,
C8CF22A91F62780C00A01E0A /* CheckInDataCollector.swift in Sources */,
@@ -2108,26 +2454,44 @@
C89C5FD01D06CF46002AAAD2 /* TrackingViewModeCell.swift in Sources */,
C8FF5B7C1EB8CF2C00CF9764 /* MarkData.swift in Sources */,
C88F6D311FFBC35F00A616BE /* ErrorHelper.swift in Sources */,
+ 10080BE3220C5DD200C6E0A2 /* BNCLocalization.m in Sources */,
+ 10080BC7220C5DD200C6E0A2 /* BranchOpenRequest.m in Sources */,
+ 10080BC3220C5DD200C6E0A2 /* BranchLoadRewardsRequest.m in Sources */,
C8F71EEA1D8691F700826DE4 /* UIActivityIndicatorView+AFNetworking.m in Sources */,
7F1B66F91A0CDCF600904A41 /* BatteryManager.swift in Sources */,
+ 10080BE1220C5DD200C6E0A2 /* UIViewController+Branch.m in Sources */,
+ 10080BCB220C5DD200C6E0A2 /* BranchConstants.m in Sources */,
7FA6551A19F6661900A1B92D /* CoreDataManager.swift in Sources */,
C8F71EE71D8691F700826DE4 /* SVRadialGradientLayer.m in Sources */,
C852BAA21CF5E1EA0093B0C5 /* CheckInRequestManager.swift in Sources */,
C8F71EE01D8691F700826DE4 /* AFURLRequestSerialization.m in Sources */,
+ 10080BCD220C5DD200C6E0A2 /* BNCLog.m in Sources */,
C8BBAC791C52649E004CA551 /* LicenseViewController.swift in Sources */,
C8BFCCC41D51C53500FC1A27 /* TeamData.swift in Sources */,
+ 10080BD4220C5DD200C6E0A2 /* BNCStrongMatchHelper.m in Sources */,
+ 10080BE2220C5DD200C6E0A2 /* BranchEvent.m in Sources */,
C843D4911EC0A93600250F01 /* CoreData1-3.xcmappingmodel in Sources */,
C89188742057F32300E37CC3 /* RegattaBoatViewController.swift in Sources */,
+ 10080BCA220C5DD200C6E0A2 /* BNCConfig.m in Sources */,
+ 10080BBF220C5DD200C6E0A2 /* BranchUserCompletedActionRequest.m in Sources */,
+ 10080BC2220C5DD200C6E0A2 /* BranchSpotlightUrlRequest.m in Sources */,
+ 10080BC1220C5DD200C6E0A2 /* BranchCloseRequest.m in Sources */,
+ 10080BC4220C5DD200C6E0A2 /* BranchCreditHistoryRequest.m in Sources */,
C88F6D2B1FEAC4E800A616BE /* LeaderboardAutoCourseData.swift in Sources */,
7F23F0881A1A3DEF00F03F1C /* CheckInController.swift in Sources */,
+ 10080BD2220C5DD200C6E0A2 /* BNCCrashlyticsWrapper.m in Sources */,
C843D47F1EBC54A300250F01 /* CompetitorCheckIn+CoreDataProperties.swift in Sources */,
C8F71EDC1D8691F700826DE4 /* AFHTTPSessionManager.m in Sources */,
C8F71EE91D8691F700826DE4 /* AFNetworkActivityIndicatorManager.m in Sources */,
C84936291D47645F000F30E8 /* CheckInTableViewCell.swift in Sources */,
C891886E2057DB4A00E37CC3 /* BoatData.swift in Sources */,
+ 10080BC5220C5DD200C6E0A2 /* BranchRedeemRewardsRequest.m in Sources */,
7F2C605A19F160E400C32D93 /* LocationManager.swift in Sources */,
+ 10080BD0220C5DD200C6E0A2 /* BNCFabricAnswers.m in Sources */,
C89C5FCC1D06CF0C002AAAD2 /* TrackingViewGPSAccuracyCell.swift in Sources */,
+ 10080BAF220C5DD200C6E0A2 /* BNCDebug.m in Sources */,
C89C5FCA1D06CECD002AAAD2 /* TrackingViewStatusCell.swift in Sources */,
+ 10080BDD220C5DD200C6E0A2 /* BranchLinkProperties.m in Sources */,
C843D4861EBC927800250F01 /* RegattaMarkViewController.swift in Sources */,
C8F71EDE1D8691F700826DE4 /* AFSecurityPolicy.m in Sources */,
7F8B8D3F1A37356C00C34B88 /* TrackingPageViewController.swift in Sources */,
@@ -2139,47 +2503,71 @@
C89188722057E7D700E37CC3 /* BoatCheckIn+CoreDataProperties.swift in Sources */,
C843D4841EBC54A300250F01 /* CheckIn.swift in Sources */,
C8BFCCCC1D523C5D00FC1A27 /* TitleView.swift in Sources */,
+ 10080BBC220C5DD200C6E0A2 /* BranchLogoutRequest.m in Sources */,
C8CF22C01F6926E500A01E0A /* CompetitorSessionViewController.swift in Sources */,
+ 10080BB8220C5DD200C6E0A2 /* BNCServerRequestQueue.m in Sources */,
C8F71EF31D86A15F00826DE4 /* SMTWiFiStatus.m in Sources */,
7F8CC2081A10D47A00C56D7F /* SettingsViewController.swift in Sources */,
C8739E0D1D2AA2A500A89F3C /* Event.swift in Sources */,
C87A74EC1F5068D0003DE396 /* CreateTrainingData.swift in Sources */,
C8CF22A31F6194C600A01E0A /* RegattaCheckInController.swift in Sources */,
+ 10080BDB220C5DD200C6E0A2 /* BranchView.m in Sources */,
7F8B8D411A373B0200C34B88 /* SpeedViewController.swift in Sources */,
+ 10080BB3220C5DD200C6E0A2 /* NSString+Branch.m in Sources */,
9E95C3D61C723AB30016632B /* Constants.swift in Sources */,
C8CF22A51F61965C00A01E0A /* TrainingCheckInController.swift in Sources */,
C89188782057F44200E37CC3 /* BoatViewController.swift in Sources */,
C8F71EF01D8691F700826DE4 /* UIWebView+AFNetworking.m in Sources */,
+ 10080BDC220C5DD200C6E0A2 /* BranchContentPathProperties.m in Sources */,
+ 10080BAA220C5DD200C6E0A2 /* BNCError.m in Sources */,
+ 10080BB1220C5DD200C6E0A2 /* BNCContentDiscoveryManager.m in Sources */,
+ 10080BDA220C5DD200C6E0A2 /* BNCAvailability.m in Sources */,
C87A74E81F504EBB003DE396 /* CreateEventData.swift in Sources */,
7FD9D3031A1236080058FEAD /* Extensions.swift in Sources */,
C8739E091D2AA2A500A89F3C /* Leaderboard.swift in Sources */,
+ 10080BD1220C5DD200C6E0A2 /* BNCURLBlackList.m in Sources */,
C8B3B9BC1F39E64400B3B6BA /* Keychain.swift in Sources */,
C8739E141D2BF16300A89F3C /* LeaderboardData.swift in Sources */,
C89C5FC51D06A289002AAAD2 /* GradientView.swift in Sources */,
C8F71EEF1D8691F700826DE4 /* UIRefreshControl+AFNetworking.m in Sources */,
C8F71EE21D8691F700826DE4 /* AFURLSessionManager.m in Sources */,
C843D4821EBC54A300250F01 /* MarkCheckIn.swift in Sources */,
+ 10080BD5220C5DD200C6E0A2 /* BranchActivityItemProvider.m in Sources */,
C8F71EDD1D8691F700826DE4 /* AFNetworkReachabilityManager.m in Sources */,
+ 10080BB4220C5DD200C6E0A2 /* BNCSystemObserver.m in Sources */,
C843D4801EBC54A300250F01 /* CompetitorCheckIn.swift in Sources */,
C8CF22C41F692B7700A01E0A /* MarkViewController.swift in Sources */,
C852BAAB1CF736680093B0C5 /* Preferences.swift in Sources */,
+ 10080BC9220C5DD200C6E0A2 /* BNCServerResponse.m in Sources */,
+ 10080BE4220C5DD200C6E0A2 /* BranchViewHandler.m in Sources */,
+ 10080BB2220C5DD200C6E0A2 /* Branch+Validator.m in Sources */,
+ 10080BDE220C5DD200C6E0A2 /* BNCLinkCache.m in Sources */,
C843D48A1EBCADF500250F01 /* MarkSessionController.swift in Sources */,
+ 10080BBA220C5DD200C6E0A2 /* BNCNetworkService.m in Sources */,
C8CF22C21F692B5300A01E0A /* MarkSessionViewController.swift in Sources */,
7F0658FD19F7D6700077FF95 /* ScanViewController.swift in Sources */,
+ 10080BD6220C5DD200C6E0A2 /* BNCKeyChain.m in Sources */,
C843D4831EBC54A300250F01 /* CheckIn+CoreDataProperties.swift in Sources */,
C8F71EEC1D8691F700826DE4 /* UIButton+AFNetworking.m in Sources */,
+ 10080BD3220C5DD200C6E0A2 /* BranchCSSearchableItemAttributeSet.m in Sources */,
C867BD0B1F4B0290007E78E0 /* CreateTrainingViewController.swift in Sources */,
C87A74E31F4F0E34003DE396 /* TrainingController.swift in Sources */,
+ 10080BD9220C5DD200C6E0A2 /* Branch.m in Sources */,
+ 10080BBE220C5DD200C6E0A2 /* BranchSetIdentityRequest.m in Sources */,
C8CF22C61F694EB900A01E0A /* TrainingViewController.swift in Sources */,
C8F1B1E11D488B6D004C3379 /* Translation.swift in Sources */,
C8F71EE81D8691F700826DE4 /* TSMarkdownParser.m in Sources */,
C8739E061D2AA2A500A89F3C /* GPSFix+CoreDataProperties.swift in Sources */,
7F193A8F1A39ACF500D71773 /* TimerViewController.swift in Sources */,
C88F6D2F1FEACD7400A616BE /* RegattaRaceCourseData.swift in Sources */,
+ 10080BB9220C5DD200C6E0A2 /* BNCServerRequest.m in Sources */,
C8739E181D2BF2DC00A89F3C /* BaseData.swift in Sources */,
C8CF22B41F67C4D800A01E0A /* RegattaRaceColumnAddData.swift in Sources */,
7F83F15619F154F000C53328 /* AppDelegate.swift in Sources */,
+ 10080BD8220C5DD200C6E0A2 /* BNCLinkData.m in Sources */,
C8B3B9C21F3C6B3900B3B6BA /* SignUpTranslation.swift in Sources */,
+ 10080BC0220C5DD200C6E0A2 /* BranchRegisterViewRequest.m in Sources */,
+ 10080BAC220C5DD200C6E0A2 /* BranchUniversalObject.m in Sources */,
7F950A911A35E967003C8C5C /* LeaderboardViewController.swift in Sources */,
C8F71EED1D8691F700826DE4 /* UIImageView+AFNetworking.m in Sources */,
C88F6D2D1FEACC5700A616BE /* LeaderboardGroupData.swift in Sources */,
@@ -2189,9 +2577,12 @@
C85DB8281F31AAE0009559CE /* SignUpViewController.swift in Sources */,
C89C5FCE1D06CF26002AAAD2 /* TrackingViewGPSFixesCell.swift in Sources */,
C8F71EE11D8691F700826DE4 /* AFURLResponseSerialization.m in Sources */,
+ 10080BBD220C5DD200C6E0A2 /* BranchInstallRequest.m in Sources */,
C8F71EE61D8691F700826DE4 /* SVProgressHUD.m in Sources */,
7F0658FF19F7EE910077FF95 /* CheckInData.swift in Sources */,
7F193A911A39E54600D71773 /* GPSAccuracyView.swift in Sources */,
+ 10080BDF220C5DD200C6E0A2 /* BNCSpotlightService.m in Sources */,
+ 10080BBB220C5DD200C6E0A2 /* BNCServerInterface.m in Sources */,
C8E0BD0A1F39A08B002FCCA5 /* FormularViewController.swift in Sources */,
C8CF22BE1F69187800A01E0A /* CompetitorViewController.swift in Sources */,
C8F71EE41D8691F700826DE4 /* SVProgressAnimatedView.m in Sources */,
@@ -2335,8 +2726,8 @@
CODE_SIGN_ENTITLEMENTS = SAPTracker/SAPTracker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CODE_SIGN_STYLE = Automatic;
- DEVELOPMENT_TEAM = F9U235EA4J;
+ CODE_SIGN_STYLE = Manual;
+ DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/SAPTracker/Libraries",
@@ -2346,7 +2737,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_SWIFT_FLAGS = "-D DEBUG";
- PRODUCT_BUNDLE_IDENTIFIER = com.sap.sailing.ios.SAPTracker.dev;
+ PRODUCT_BUNDLE_IDENTIFIER = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2363,8 +2754,8 @@
CODE_SIGN_ENTITLEMENTS = SAPTracker/SAPTracker.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CODE_SIGN_STYLE = Automatic;
- DEVELOPMENT_TEAM = F9U235EA4J;
+ CODE_SIGN_STYLE = Manual;
+ DEVELOPMENT_TEAM = "";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/SAPTracker/Libraries",
@@ -2374,7 +2765,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_SWIFT_FLAGS = "";
- PRODUCT_BUNDLE_IDENTIFIER = com.sap.sailing.ios.SAPTracker.dev;
+ PRODUCT_BUNDLE_IDENTIFIER = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2390,6 +2781,7 @@
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_IDENTITY = "iPhone Distribution";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
+ DEVELOPMENT_TEAM = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@@ -2411,6 +2803,7 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_IDENTITY = "iPhone Distribution";
+ DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = SAPTrackerTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.sap.sailing.ios.$(PRODUCT_NAME:rfc1034identifier)";
@@ -2459,12 +2852,13 @@
7F44095919F65B5B0021BF22 /* CoreData.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
+ 10C76537224BA455003254D8 /* CoreData 5.xcdatamodel */,
C843D522201F1F17003725F0 /* CoreData 4.xcdatamodel */,
C8D938B11EB9AFDB00B1B4BC /* CoreData 3.xcdatamodel */,
C8739D9A1D25317100A89F3C /* CoreData 2.xcdatamodel */,
7F44095A19F65B5B0021BF22 /* CoreData.xcdatamodel */,
);
- currentVersion = C843D522201F1F17003725F0 /* CoreData 4.xcdatamodel */;
+ currentVersion = 10C76537224BA455003254D8 /* CoreData 5.xcdatamodel */;
path = CoreData.xcdatamodeld;
sourceTree = "";
versionGroupType = wrapper.xcdatamodel;
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/AppDelegate.swift b/ios/SAPTracker/src/xcode/SAPTracker/AppDelegate.swift
index e0afe41d5cc..6ff21e2abb8 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/AppDelegate.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/AppDelegate.swift
@@ -8,7 +8,7 @@
import UIKit
import CoreData
-import Branch
+// Branch.io Version: 0.25.11
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -41,7 +41,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
setup()
Branch.getInstance().initSession(launchOptions: launchOptions) { (params, error) in
guard let data = params as? [String: AnyObject] else { return }
- if let checkinUrl = data["checkin_url"] as? String {
+ if let checkinUrl = data["checkinUrl"] as? String {
Preferences.newCheckInURL = checkinUrl.removingPercentEncoding
}
}
@@ -50,7 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
let branchQueryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems
- if (branchQueryItems?.first(where: {$0.name == "checkin_url"})) != nil {
+ if (branchQueryItems?.first(where: {$0.name == "checkinUrl"})) != nil {
Branch.getInstance().application(app, open: url, options: options)
} else {
Preferences.newCheckInURL = urlStringForDeeplink(url: url)
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/InfoPlist.strings b/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/InfoPlist.strings
index 0faec172b97..0878a6aa745 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/InfoPlist.strings
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/InfoPlist.strings
@@ -1,11 +1,12 @@
-//
-// InfoPlist.strings
-// SAPTracker
-//
-// Created by Raimund Wege on 04.11.16.
-// Copyright © 2016 com.sap.sailing. All rights reserved.
-//
+/*
+ InfoPlist.strings
+ SAPTracker
+
+ Created by Raimund Wege on 04.11.16.
+ Copyright © 2016 com.sap.sailing. All rights reserved.
+*/
+"NSLocationAlwaysUsageDescription" = "In order to track your entire race your phone needs to continue tracking also when you switch off or lock the screen which helps save battery and avoids unintentionally stopping the tracking process.";
"NSLocationWhenInUseUsageDescription" = "In order to track your entire race your phone needs to continue tracking also when you switch off or lock the screen which helps save battery and avoids unintentionally stopping the tracking process.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "In order to track your entire race your phone needs to continue tracking also when you switch off or lock the screen which helps save battery and avoids unintentionally stopping the tracking process.";
"NSCameraUsageDescription" = "The camera is required to perform a check-in using QR code.";
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/Localizable.strings b/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/Localizable.strings
index 9a7c34ec360..721f4b8d9d2 100644
Binary files a/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/Localizable.strings and b/ios/SAPTracker/src/xcode/SAPTracker/Base.lproj/Localizable.strings differ
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CheckIn+CoreDataProperties.swift b/ios/SAPTracker/src/xcode/SAPTracker/CheckIn+CoreDataProperties.swift
index b450c45676a..d033d1cf574 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CheckIn+CoreDataProperties.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CheckIn+CoreDataProperties.swift
@@ -20,5 +20,6 @@ extension CheckIn {
@NSManaged var event: Event
@NSManaged var gpsFixes: NSSet?
@NSManaged var leaderboard: Leaderboard
+ @NSManaged var secret: String?
}
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CheckIn.swift b/ios/SAPTracker/src/xcode/SAPTracker/CheckIn.swift
index 06c46182e45..10dea2d61ab 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CheckIn.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CheckIn.swift
@@ -17,6 +17,7 @@ class CheckIn: NSManagedObject {
serverURL = checkInData.serverURL
event.updateWithEventData(eventData: checkInData.eventData)
leaderboard.updateWithLeaderboardData(leaderboardData: checkInData.leaderboardData)
+ secret = checkInData.secret
}
func eventURL() -> URL? {
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CheckInController.swift b/ios/SAPTracker/src/xcode/SAPTracker/CheckInController.swift
index 17f05d12c53..5bcc37c9f1d 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CheckInController.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CheckInController.swift
@@ -139,7 +139,8 @@ class CheckInController : NSObject {
failure: @escaping (_ error: Error) -> Void)
{
SVProgressHUD.show()
- let checkInRequestManager = CheckInRequestManager(baseURLString: checkInData.serverURL)
+ let checkInRequestManager = CheckInRequestManager(baseURLString: checkInData.serverURL,
+ secret: checkInData.secret)
checkInRequestManager.postCheckIn(checkInData: checkInData, success: { [weak self] () in
SVProgressHUD.popActivity()
self?.postCheckInSuccess(checkInData: checkInData, success: success)
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CheckInData.swift b/ios/SAPTracker/src/xcode/SAPTracker/CheckInData.swift
index cefa62b2082..f667dd7fff2 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CheckInData.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CheckInData.swift
@@ -16,6 +16,7 @@ class CheckInData: NSObject {
static let LeaderboardName = "leaderboard_name"
static let CompetitorID = "competitor_id"
static let MarkID = "mark_id"
+ static let Secret = "secret"
}
public enum CheckInDataType {
@@ -31,6 +32,7 @@ class CheckInData: NSObject {
let competitorID: String?
let markID: String?
let isTraining: Bool
+ let secret: String?
let type: CheckInDataType
var eventData = EventData()
@@ -44,7 +46,8 @@ class CheckInData: NSObject {
eventID: String,
leaderboardName: String,
competitorID: String,
- isTraining: Bool)
+ isTraining: Bool,
+ secret: String? = nil)
{
self.boatID = nil
self.competitorID = competitorID
@@ -53,6 +56,7 @@ class CheckInData: NSObject {
self.markID = nil
self.serverURL = serverURL
self.isTraining = isTraining
+ self.secret = secret
self.type = CheckInDataType.competitor
super.init()
}
@@ -61,7 +65,8 @@ class CheckInData: NSObject {
eventID: String,
leaderboardName: String,
markID: String,
- isTraining: Bool)
+ isTraining: Bool,
+ secret: String? = nil)
{
self.boatID = nil
self.competitorID = nil
@@ -70,6 +75,7 @@ class CheckInData: NSObject {
self.markID = markID
self.serverURL = serverURL
self.isTraining = isTraining
+ self.secret = secret
type = CheckInDataType.mark
super.init()
}
@@ -78,7 +84,8 @@ class CheckInData: NSObject {
eventID: String,
leaderboardName: String,
boatID: String,
- isTraining: Bool)
+ isTraining: Bool,
+ secret: String? = nil)
{
self.boatID = boatID
self.competitorID = nil
@@ -87,6 +94,7 @@ class CheckInData: NSObject {
self.markID = nil
self.serverURL = serverURL
self.isTraining = isTraining
+ self.secret = secret
type = CheckInDataType.boat
super.init()
}
@@ -97,7 +105,8 @@ class CheckInData: NSObject {
eventID: boatCheckIn.event.eventID,
leaderboardName: boatCheckIn.leaderboard.name,
boatID: boatCheckIn.boatID,
- isTraining: boatCheckIn.isTraining.boolValue
+ isTraining: boatCheckIn.isTraining.boolValue,
+ secret: boatCheckIn.secret
)
}
@@ -107,7 +116,8 @@ class CheckInData: NSObject {
eventID: competitorCheckIn.event.eventID,
leaderboardName: competitorCheckIn.leaderboard.name,
competitorID: competitorCheckIn.competitorID,
- isTraining: competitorCheckIn.isTraining.boolValue
+ isTraining: competitorCheckIn.isTraining.boolValue,
+ secret: competitorCheckIn.secret
)
}
@@ -117,7 +127,8 @@ class CheckInData: NSObject {
eventID: markCheckIn.event.eventID,
leaderboardName: markCheckIn.leaderboard.name,
markID: markCheckIn.markID,
- isTraining: markCheckIn.isTraining.boolValue
+ isTraining: markCheckIn.isTraining.boolValue,
+ secret: markCheckIn.secret
)
}
@@ -125,7 +136,7 @@ class CheckInData: NSObject {
// handle the case where a user uses the app to scan a QR code containing a branch.io deeplink
let branchQueryItems = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems
- let checkinUrl = branchQueryItems?.first(where: {$0.name == "checkin_url"}).map { URL(string: $0.value!)! }
+ let checkinUrl = branchQueryItems?.first(where: {$0.name == "checkinUrl"}).map { URL(string: $0.value!)! }
let unwrappedUrl = checkinUrl ?? url
@@ -142,13 +153,16 @@ class CheckInData: NSObject {
// Get check-in items
guard let eventID = CheckInData.queryItemValue(queryItems: queryItems, itemName: ItemNames.EventID) else { return nil }
guard let leaderboardName = CheckInData.queryItemValue(queryItems: queryItems, itemName: ItemNames.LeaderboardName) else { return nil }
+ let secret = CheckInData.queryItemValue(queryItems: queryItems, itemName: ItemNames.Secret)
+
if let competitorID = CheckInData.queryItemValue(queryItems: queryItems, itemName: ItemNames.CompetitorID) {
self.init(
serverURL: serverURL,
eventID: eventID,
leaderboardName: leaderboardName,
competitorID: competitorID,
- isTraining: false
+ isTraining: false,
+ secret: secret
)
} else if let markID = CheckInData.queryItemValue(queryItems: queryItems, itemName: ItemNames.MarkID) {
self.init(
@@ -156,7 +170,8 @@ class CheckInData: NSObject {
eventID: eventID,
leaderboardName: leaderboardName,
markID: markID,
- isTraining: false
+ isTraining: false,
+ secret: secret
)
} else if let boatID = CheckInData.queryItemValue(queryItems: queryItems, itemName: ItemNames.BoatID) {
self.init(
@@ -164,7 +179,8 @@ class CheckInData: NSObject {
eventID: eventID,
leaderboardName: leaderboardName,
boatID: boatID,
- isTraining: false
+ isTraining: false,
+ secret: secret
)
} else {
logError(name: "\(#function)", error: "unknown check-in type")
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CheckInDataCollector.swift b/ios/SAPTracker/src/xcode/SAPTracker/CheckInDataCollector.swift
index 08d36df6695..e5fd89a8182 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CheckInDataCollector.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CheckInDataCollector.swift
@@ -26,7 +26,7 @@ class CheckInDataCollector: NSObject {
fileprivate let checkInRequestManager: CheckInRequestManager
init(checkInData: CheckInData) {
- checkInRequestManager = CheckInRequestManager(baseURLString: checkInData.serverURL)
+ checkInRequestManager = CheckInRequestManager(baseURLString: checkInData.serverURL, secret: checkInData.secret)
super.init()
}
@@ -92,7 +92,7 @@ class CheckInDataCollector: NSObject {
success: @escaping (_ collector: CheckInData) -> Void,
failure: @escaping (_ error: Error) -> Void)
{
- checkInRequestManager.getBoat(boatID: boatID, success: { (boatData) in
+ checkInRequestManager.getBoat(boatID: boatID, leaderboardName: collector.leaderboardName, success: { (boatData) in
collector.boatData = boatData
success(collector)
}) { error in
@@ -108,7 +108,7 @@ class CheckInDataCollector: NSObject {
success: @escaping (_ collector: CheckInData) -> Void,
failure: @escaping (_ error: Error) -> Void)
{
- checkInRequestManager.getCompetitor(competitorID: competitorID, success: { (competitorData) in
+ checkInRequestManager.getCompetitor(competitorID: competitorID, leaderboardName: collector.leaderboardName, success: { (competitorData) in
collector.competitorData = competitorData
self.getCompetitorDataSuccess(
collector: collector,
@@ -127,7 +127,7 @@ class CheckInDataCollector: NSObject {
success: @escaping (_ collector: CheckInData) -> Void,
failure: @escaping (_ error: Error) -> Void)
{
- checkInRequestManager.getTeamImageURL(competitorID: competitorID, result: { (imageURL) in
+ checkInRequestManager.getTeamImageURL(competitorID: competitorID, leaderboardName: collector.leaderboardName, result: { (imageURL) in
collector.teamImageURL = imageURL
success(collector)
})
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CheckInRequestManager.swift b/ios/SAPTracker/src/xcode/SAPTracker/CheckInRequestManager.swift
index 7bbb816a88a..f611862c2d7 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CheckInRequestManager.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CheckInRequestManager.swift
@@ -71,8 +71,10 @@ class CheckInRequestManager: NSObject {
static let FixesSpeed = "speed"
static let FixesTimestamp = "timestamp"
static let FromMillis = "fromMillis"
+ static let LeaderboardName = "leaderboardName"
static let MarkID = "markId"
static let PushDeviceID = "pushDeviceId"
+ static let Secret = "secret"
static let ToMillis = "toMillis"
}
@@ -84,12 +86,14 @@ class CheckInRequestManager: NSObject {
static let IOS = "iOS"
}
+ private let secret: String?
let baseURLString: String
let manager: AFHTTPSessionManager
let sessionManager: AFURLSessionManager
- init(baseURLString: String = "") {
+ init(baseURLString: String = "", secret: String? = nil) {
self.baseURLString = baseURLString
+ self.secret = secret
manager = AFHTTPSessionManager(baseURL: URL(string: baseURLString))
manager.requestSerializer = AFJSONRequestSerializer() as AFHTTPRequestSerializer
manager.requestSerializer.timeoutInterval = Application.RequestTimeout
@@ -108,9 +112,15 @@ class CheckInRequestManager: NSObject {
{
let encodedEventID = eventID.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
let urlString = "\(basePathString)/events/\(encodedEventID)"
+ let parameter: [String:String]?
+ switch self.secret {
+ case .some(let secret): parameter = [BodyKeys.Secret:secret]
+ case .none: parameter = nil
+ }
+
manager.get(
urlString,
- parameters: nil,
+ parameters: parameter,
progress: nil,
success: { (requestOperation, responseObject) in self.getEventSuccess(responseObject: responseObject, success: success) },
failure: { (requestOperation, error) in self.getEventFailure(error: error, failure: failure) }
@@ -136,9 +146,15 @@ class CheckInRequestManager: NSObject {
{
let encodedLeaderboardName = leaderboardName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
let urlString = "\(basePathString)/leaderboards/\(encodedLeaderboardName)"
+ let parameter: [String:String]?
+ switch self.secret {
+ case .some(let secret): parameter = [BodyKeys.Secret:secret]
+ case .none: parameter = nil
+ }
+
manager.get(
urlString,
- parameters: nil,
+ parameters: parameter,
progress: nil,
success: { (requestOperation, responseObject) in self.getLeaderboardSuccess(responseObject: responseObject, success: success) },
failure: { (requestOperation, error) in self.getLeaderboardFailure(error: error, failure: failure) }
@@ -159,14 +175,21 @@ class CheckInRequestManager: NSObject {
func getBoat(
boatID: String,
+ leaderboardName: String,
success: @escaping (_ boatData: BoatData) -> Void,
failure: @escaping (_ error: Error) -> Void)
{
let encodedBoatID = boatID.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed) ?? ""
let urlString = "\(basePathString)/boats/\(encodedBoatID)"
+ var parameters = [String:String]()
+ parameters[BodyKeys.LeaderboardName] = leaderboardName
+ if let secret = secret {
+ parameters[BodyKeys.Secret] = secret
+ }
+
manager.get(
urlString,
- parameters: nil,
+ parameters: parameters,
progress: nil,
success: { (requestOperation, responseObject) in self.getBoatSuccess(responseObject: responseObject, success: success) },
failure: { (requestOperation, error) in self.getBoatFailure(error: error, failure: failure) }
@@ -187,14 +210,21 @@ class CheckInRequestManager: NSObject {
func getCompetitor(
competitorID: String,
+ leaderboardName: String,
success: @escaping (_ competitorData: CompetitorData) -> Void,
failure: @escaping (_ error: Error) -> Void)
{
let encodedCompetitorID = competitorID.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed) ?? ""
let urlString = "\(basePathString)/competitors/\(encodedCompetitorID)"
+ var parameters = [String:String]()
+ parameters[BodyKeys.LeaderboardName] = leaderboardName
+ if let secret = secret {
+ parameters[BodyKeys.Secret] = secret
+ }
+
manager.get(
urlString,
- parameters: nil,
+ parameters: parameters,
progress: nil,
success: { (requestOperation, responseObject) in self.getCompetitorSuccess(responseObject: responseObject, success: success) },
failure: { (requestOperation, error) in self.getCompetitorFailure(error: error, failure: failure) }
@@ -222,9 +252,15 @@ class CheckInRequestManager: NSObject {
let encodedLeaderboardName = leaderboardName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? ""
let encodedMarkID = markID.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed) ?? ""
let urlString = "\(basePathString)/leaderboards/\(encodedLeaderboardName)/marks/\(encodedMarkID)"
+ let parameter: [String:String]?
+ switch self.secret {
+ case .some(let secret): parameter = [BodyKeys.Secret:secret]
+ case .none: parameter = nil
+ }
+
manager.get(
urlString,
- parameters: nil,
+ parameters: parameter,
progress: nil,
success: { (requestOperation, responseObject) in self.getMarkSuccess(responseObject: responseObject, success: success) },
failure: { (requestOperation, error) in self.getMarkFailure(error: error, failure: failure) }
@@ -245,14 +281,20 @@ class CheckInRequestManager: NSObject {
func getTeam(
competitorID: String,
+ leaderboardName: String,
success: @escaping (_ teamData: TeamData) -> Void,
failure: @escaping (_ error: Error) -> Void)
{
let encodedCompetitorID = competitorID.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed) ?? ""
let urlString = "\(basePathString)/competitors/\(encodedCompetitorID)/team"
+ var parameters = [String:String]()
+ parameters[BodyKeys.LeaderboardName] = leaderboardName
+ if let secret = secret {
+ parameters[BodyKeys.Secret] = secret
+ }
manager.get(
urlString,
- parameters: nil,
+ parameters: parameters,
progress: nil,
success: { (requestOperation, responseObject) in self.getTeamSuccess(responseObject: responseObject, success: success) },
failure: { (requestOperation, error) in self.getTeamFailure(error: error, failure: failure) }
@@ -269,9 +311,10 @@ class CheckInRequestManager: NSObject {
failure(CheckInRequestManagerError.getTeamFailed)
}
- func getTeamImageURL(competitorID: String, result: @escaping (_ imageURL: String?) -> Void) {
+ func getTeamImageURL(competitorID: String, leaderboardName: String, result: @escaping (_ imageURL: String?) -> Void) {
getTeam(
competitorID: competitorID,
+ leaderboardName: leaderboardName,
success: { (teamData) in result(teamData.imageURL) },
failure: { (error) in result(nil) } // No image but that's ok
)
@@ -290,6 +333,9 @@ class CheckInRequestManager: NSObject {
body[BodyKeys.DeviceUUID] = Preferences.uuid as AnyObject?
body[BodyKeys.PushDeviceID] = "" as AnyObject?
body[BodyKeys.FromMillis] = millisSince1970()
+ if let secret = checkInData.secret {
+ body[BodyKeys.Secret] = secret as AnyObject?
+ }
switch checkInData.type {
case .boat:
body[BodyKeys.BoatID] = checkInData.boatData.boatID as AnyObject?
@@ -334,6 +380,9 @@ class CheckInRequestManager: NSObject {
var body = [String: AnyObject]()
body[BodyKeys.DeviceUUID] = Preferences.uuid as AnyObject?
body[BodyKeys.ToMillis] = millisSince1970()
+ if let secret = checkIn.secret {
+ body[BodyKeys.Secret] = secret as AnyObject?
+ }
if let competitorCheckIn = checkIn as? CompetitorCheckIn {
body[BodyKeys.CompetitorID] = competitorCheckIn.competitorID as AnyObject?
} else if let markCheckIn = checkIn as? MarkCheckIn {
@@ -409,15 +458,26 @@ class CheckInRequestManager: NSObject {
func postTeamImageData(
imageData: Data,
- competitorID: String,
+ competitorCheckIn: CompetitorCheckIn,
success: @escaping (_ teamImageURL: String) -> Void,
failure: @escaping (_ error: Error) -> Void)
{
- guard let teamImageURL = URL(string: "\(baseURLString)\(basePathString)/competitors/\(competitorID)/team/image") else {
+ let competitorID = competitorCheckIn.competitorID
+ let leaderboardName = competitorCheckIn.leaderboard.name
+
+ guard var components = URLComponents(string: "\(baseURLString)\(basePathString)/competitors/\(competitorID)/team/image") else {
failure(CheckInRequestManagerError.teamImageURLIsInvalid)
return
}
-
+ components.queryItems = [URLQueryItem(name: BodyKeys.LeaderboardName, value: leaderboardName)]
+ if let secret = competitorCheckIn.secret {
+ components.queryItems?.append(URLQueryItem(name: BodyKeys.Secret, value: secret))
+ }
+ guard let teamImageURL = components.url else {
+ failure(CheckInRequestManagerError.teamImageURLIsInvalid)
+ return
+ }
+
postTeamImageData(
imageData: imageData,
teamImageURL: teamImageURL,
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CompetitorSessionController.swift b/ios/SAPTracker/src/xcode/SAPTracker/CompetitorSessionController.swift
index 68653c67bfe..68f59205371 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CompetitorSessionController.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CompetitorSessionController.swift
@@ -14,13 +14,13 @@ class CompetitorSessionController: SessionController {
func postTeamImageData(
imageData: Data,
- competitorID: String,
+ competitorCheckIn: CompetitorCheckIn,
success: @escaping (_ teamImageURL: String) -> Void,
failure: @escaping (_ error: Error) -> Void
) {
checkInRequestManager.postTeamImageData(
imageData: imageData,
- competitorID: competitorID,
+ competitorCheckIn: competitorCheckIn,
success: success,
failure: failure
)
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CompetitorViewController.swift b/ios/SAPTracker/src/xcode/SAPTracker/CompetitorViewController.swift
index 4b52115539e..f5d05a59f44 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CompetitorViewController.swift
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CompetitorViewController.swift
@@ -218,7 +218,7 @@ extension CompetitorViewController: UIImagePickerControllerDelegate {
fileprivate func uploadTeamImageData(imageData: Data!) {
SVProgressHUD.show()
- competitorSessionController.postTeamImageData(imageData: imageData, competitorID: competitorCheckIn.competitorID, success: { (teamImageURL) in
+ competitorSessionController.postTeamImageData(imageData: imageData, competitorCheckIn: competitorCheckIn, success: { (teamImageURL) in
SVProgressHUD.popActivity()
self.uploadTeamImageDataSuccess(teamImageURL: teamImageURL)
}) { (error) in
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CoreData.xcdatamodeld/.xccurrentversion b/ios/SAPTracker/src/xcode/SAPTracker/CoreData.xcdatamodeld/.xccurrentversion
index 1d5ea989f87..2145ac780f8 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/CoreData.xcdatamodeld/.xccurrentversion
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CoreData.xcdatamodeld/.xccurrentversion
@@ -3,6 +3,6 @@
_XCCurrentVersionName
- CoreData 4.xcdatamodel
+ CoreData 5.xcdatamodel
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/CoreData.xcdatamodeld/CoreData 5.xcdatamodel/contents b/ios/SAPTracker/src/xcode/SAPTracker/CoreData.xcdatamodeld/CoreData 5.xcdatamodel/contents
new file mode 100644
index 00000000000..b3a24e8bb98
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/CoreData.xcdatamodeld/CoreData 5.xcdatamodel/contents
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Images.xcassets/AppIcon.appiconset/1024x0w.jpg b/ios/SAPTracker/src/xcode/SAPTracker/Images.xcassets/AppIcon.appiconset/1024x0w.jpg
new file mode 100644
index 00000000000..c7122b563a2
Binary files /dev/null and b/ios/SAPTracker/src/xcode/SAPTracker/Images.xcassets/AppIcon.appiconset/1024x0w.jpg differ
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/SAPTracker/src/xcode/SAPTracker/Images.xcassets/AppIcon.appiconset/Contents.json
index 140d558a03a..b77d112031f 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/Images.xcassets/AppIcon.appiconset/Contents.json
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -99,8 +99,9 @@
"scale" : "2x"
},
{
- "idiom" : "ios-marketing",
"size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "1024x0w.jpg",
"scale" : "1x"
}
],
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Info.plist b/ios/SAPTracker/src/xcode/SAPTracker/Info.plist
index 3e18d436e87..1988ae2d2e5 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/Info.plist
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Info.plist
@@ -9,7 +9,7 @@
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
+ com.sap.sailing.ios.SAPTracker
CFBundleInfoDictionaryVersion
6.0
CFBundleName
@@ -28,14 +28,6 @@
comsapsailingtracker
-
- CFBundleTypeRole
- Editor
- CFBundleURLSchemes
-
- prefs
-
-
CFBundleVersion
2.0.0-rc2
@@ -46,12 +38,6 @@
NSAllowsArbitraryLoads
- NSLocationAlwaysUsageDescription
-
- branch_app_domain
- sailinsight-app.sapsailing.com
- branch_key
- key_live_ges90RzIjjgM0vg8wbTtYcdmtCeYJMAZ
UIBackgroundModes
fetch
@@ -78,5 +64,9 @@
UIViewControllerBasedStatusBarAppearance
+ branch_app_domain
+ sailinsight-app.sapsailing.com
+ branch_key
+ key_live_ges90RzIjjgM0vg8wbTtYcdmtCeYJMAZ
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCApplication.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCApplication.h
new file mode 100644
index 00000000000..52f88e84730
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCApplication.h
@@ -0,0 +1,54 @@
+/**
+ @file BNCApplication.h
+ @package Branch-SDK
+ @brief Current application and extension info.
+
+ @author Edward Smith
+ @date January 8, 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+@interface BNCApplication : NSObject
+
+/// A reference to the current running application.
++ (BNCApplication*_Nonnull) currentApplication;
+
+/// The bundle identifier of the current
+@property (atomic, readonly) NSString*_Nullable bundleID;
+
+/// The bundle display name from the info plist.
+@property (atomic, readonly) NSString*_Nullable displayName;
+
+/// The bundle short display name from the info plist.
+@property (atomic, readonly) NSString*_Nullable shortDisplayName;
+
+/// The short version ID as is typically shown to the user, like in iTunes or the app store.
+@property (atomic, readonly) NSString*_Nullable displayVersionString;
+
+/// The version ID for developers use.
+@property (atomic, readonly) NSString*_Nullable versionString;
+
+/// The creation date of the current executable.
+@property (atomic, readonly) NSDate*_Nullable currentBuildDate;
+
+/// The creating date of the exectuble the first time it was recorded by Branch.
+@property (atomic, readonly) NSDate*_Nullable firstInstallBuildDate;
+
+/// The date this app was installed on this device.
+@property (atomic, readonly) NSDate*_Nullable currentInstallDate;
+
+/// The date this app was first installed on this device.
+@property (atomic, readonly) NSDate*_Nullable firstInstallDate;
+
+/// Returns a dictionary of device / identity pairs.
+@property (atomic, readonly) NSDictionary*_Nonnull deviceKeyIdentityValueDictionary;
+
+/// The team identifier for the app.
+@property (atomic, readonly) NSString*_Nullable teamID;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCApplication.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCApplication.m
new file mode 100644
index 00000000000..f9adcfbd43f
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCApplication.m
@@ -0,0 +1,169 @@
+/**
+ @file BNCApplication.m
+ @package Branch-SDK
+ @brief Current application and extension info.
+
+ @author Edward Smith
+ @date January 8, 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#import "BNCApplication.h"
+#import "BNCLog.h"
+#import "BNCKeyChain.h"
+
+static NSString*const kBranchKeychainService = @"BranchKeychainService";
+static NSString*const kBranchKeychainDevicesKey = @"BranchKeychainDevices";
+static NSString*const kBranchKeychainFirstBuildKey = @"BranchKeychainFirstBuild";
+static NSString*const kBranchKeychainFirstInstalldKey = @"BranchKeychainFirstInstall";
+
+#pragma mark - BNCApplication
+
+@implementation BNCApplication
+
++ (BNCApplication*) currentApplication {
+ static BNCApplication *bnc_currentApplication = nil;
+ static dispatch_once_t onceToken = 0;
+ dispatch_once(&onceToken, ^{
+ bnc_currentApplication = [BNCApplication createCurrentApplication];
+ });
+ return bnc_currentApplication;
+}
+
++ (BNCApplication*) createCurrentApplication {
+ BNCApplication *application = [[BNCApplication alloc] init];
+ if (!application) return application;
+ NSDictionary *info = [NSBundle mainBundle].infoDictionary;
+
+ application->_bundleID = [NSBundle mainBundle].bundleIdentifier;
+ application->_displayName = info[@"CFBundleDisplayName"];
+ application->_shortDisplayName = info[@"CFBundleName"];
+
+ application->_displayVersionString = info[@"CFBundleShortVersionString"];
+ application->_versionString = info[@"CFBundleVersion"];
+
+ application->_firstInstallBuildDate = [BNCApplication firstInstallBuildDate];
+ application->_currentBuildDate = [BNCApplication currentBuildDate];
+
+ application->_firstInstallDate = [BNCApplication firstInstallDate];
+ application->_currentInstallDate = [BNCApplication currentInstallDate];
+
+ NSString*group = [BNCKeyChain securityAccessGroup];
+ if (group) {
+ NSRange range = [group rangeOfString:@"."];
+ if (range.location != NSNotFound) {
+ application->_teamID = [[group substringToIndex:range.location] copy];
+ }
+ }
+
+ return application;
+}
+
++ (NSDate*) currentBuildDate {
+ NSURL *appURL = nil;
+ NSURL *bundleURL = [NSBundle mainBundle].bundleURL;
+ NSDictionary *info = [NSBundle mainBundle].infoDictionary;
+ NSString *appName = info[(__bridge NSString*)kCFBundleExecutableKey];
+ if (appName.length > 0 && bundleURL) {
+ appURL = [bundleURL URLByAppendingPathComponent:appName];
+ } else {
+ NSString *path = [[NSProcessInfo processInfo].arguments firstObject];
+ if (path) appURL = [NSURL fileURLWithPath:path];
+ }
+ if (appURL == nil)
+ return nil;
+
+ NSError *error = nil;
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSDictionary *attributes = [fileManager attributesOfItemAtPath:appURL.path error:&error];
+ if (error) {
+ BNCLogError(@"Can't get build date: %@.", error);
+ return nil;
+ }
+ NSDate * buildDate = [attributes fileCreationDate];
+ if (buildDate == nil || [buildDate timeIntervalSince1970] <= 0.0) {
+ BNCLogError(@"Invalid build date: %@.", buildDate);
+ }
+ return buildDate;
+}
+
++ (NSDate*) firstInstallBuildDate {
+ NSError *error = nil;
+ NSDate *firstBuildDate =
+ [BNCKeyChain retrieveValueForService:kBranchKeychainService
+ key:kBranchKeychainFirstBuildKey
+ error:&error];
+ if (firstBuildDate)
+ return firstBuildDate;
+
+ firstBuildDate = [self currentBuildDate];
+ error = [BNCKeyChain storeValue:firstBuildDate
+ forService:kBranchKeychainService
+ key:kBranchKeychainFirstBuildKey
+ cloudAccessGroup:nil];
+ if (error) BNCLogError(@"Keychain store: %@.", error);
+ return firstBuildDate;
+}
+
++ (NSDate*) currentInstallDate {
+ NSError *error = nil;
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSURL *libraryURL =
+ [[fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask] firstObject];
+ NSDictionary *attributes = [fileManager attributesOfItemAtPath:libraryURL.path error:&error];
+ if (error) {
+ BNCLogError(@"Can't get library date: %@.", error);
+ return nil;
+ }
+ NSDate *installDate = [attributes fileCreationDate];
+ if (installDate == nil || [installDate timeIntervalSince1970] <= 0.0) {
+ BNCLogError(@"Invalid install date.");
+ }
+ return installDate;
+}
+
++ (NSDate*) firstInstallDate {
+ NSError *error = nil;
+ NSDate* firstInstallDate =
+ [BNCKeyChain retrieveValueForService:kBranchKeychainService
+ key:kBranchKeychainFirstInstalldKey
+ error:&error];
+ if (firstInstallDate)
+ return firstInstallDate;
+
+ firstInstallDate = [self currentInstallDate];
+ error = [BNCKeyChain storeValue:firstInstallDate
+ forService:kBranchKeychainService
+ key:kBranchKeychainFirstInstalldKey
+ cloudAccessGroup:nil];
+ if (error) BNCLogError(@"Keychain store: %@.", error);
+ return firstInstallDate;
+}
+
+- (NSDictionary*) deviceKeyIdentityValueDictionary {
+ @synchronized (self.class) {
+ NSError *error = nil;
+ NSDictionary *deviceDictionary =
+ [BNCKeyChain retrieveValueForService:kBranchKeychainService
+ key:kBranchKeychainDevicesKey
+ error:&error];
+ if (error) BNCLogWarning(@"While retrieving deviceKeyIdentityValueDictionary: %@.", error);
+ if (!deviceDictionary) deviceDictionary = @{};
+ return deviceDictionary;
+ }
+}
+
+@end
+
+@implementation BNCApplication (BNCTest)
+
+- (void) setAppOriginalInstallDate:(NSDate*)originalInstallDate
+ firstInstallDate:(NSDate*)firstInstallDate
+ lastUpdateDate:(NSDate*)lastUpdateDate {
+ self->_currentInstallDate = firstInstallDate; // latest_install_time
+ self->_firstInstallDate = originalInstallDate; // first_install_time
+ self->_currentBuildDate = lastUpdateDate; // lastest_update_time
+}
+
+@end
+
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCAvailability.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCAvailability.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCAvailability.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCAvailability.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCAvailability.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCAvailability.m
new file mode 100644
index 00000000000..7dbf548d752
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCAvailability.m
@@ -0,0 +1,14 @@
+//
+// BNCAvailability.m
+// Branch-SDK
+//
+// Created by Edward on 10/26/16.
+// Copyright © 2016 Branch Metrics. All rights reserved.
+//
+
+#import "BNCAvailability.h"
+
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < 100000
+// Usually nothing goes here.
+#endif
+
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCCallbacks.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCallbacks.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCCallbacks.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCallbacks.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCCommerceEvent.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCommerceEvent.h
similarity index 99%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCCommerceEvent.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCommerceEvent.h
index 224d4334de6..e69bd4182d1 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCCommerceEvent.h
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCommerceEvent.h
@@ -263,7 +263,7 @@ NSArray*_Nonnull BNCCurrencyAllCurrencies(void);
@end
-@interface BranchCommerceEventRequest : BNCServerRequest
+@interface BranchCommerceEventRequest : BNCServerRequest
- (instancetype _Nonnull) initWithCommerceEvent:(BNCCommerceEvent*_Nonnull)commerceEvent
metadata:(NSDictionary*_Nullable)dictionary
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCommerceEvent.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCommerceEvent.m
new file mode 100644
index 00000000000..5ed23483f6a
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCommerceEvent.m
@@ -0,0 +1,622 @@
+//
+// BNCCommerceEvent.h
+// Branch-SDK
+//
+// Created by Edward Smith on 12/14/16.
+// Copyright (c) 2016 Branch Metrics. All rights reserved.
+//
+
+#import "BNCCommerceEvent.h"
+#import "BranchConstants.h"
+#import "BNCLog.h"
+
+#pragma mark BNCProductCategory
+
+BNCProductCategory BNCProductCategoryAnimalSupplies = @"Animals & Pet Supplies";
+BNCProductCategory BNCProductCategoryApparel = @"Apparel & Accessories";
+BNCProductCategory BNCProductCategoryArtsEntertainment = @"Arts & Entertainment";
+BNCProductCategory BNCProductCategoryBabyToddler = @"Baby & Toddler";
+BNCProductCategory BNCProductCategoryBusinessIndustrial = @"Business & Industrial";
+BNCProductCategory BNCProductCategoryCamerasOptics = @"Cameras & Optics";
+BNCProductCategory BNCProductCategoryElectronics = @"Electronics";
+BNCProductCategory BNCProductCategoryFoodBeverageTobacco = @"Food, Beverages & Tobacco";
+BNCProductCategory BNCProductCategoryFurniture = @"Furniture";
+BNCProductCategory BNCProductCategoryHardware = @"Hardware";
+BNCProductCategory BNCProductCategoryHealthBeauty = @"Health & Beauty";
+BNCProductCategory BNCProductCategoryHomeGarden = @"Home & Garden";
+BNCProductCategory BNCProductCategoryLuggageBags = @"Luggage & Bags";
+BNCProductCategory BNCProductCategoryMature = @"Mature";
+BNCProductCategory BNCProductCategoryMedia = @"Media";
+BNCProductCategory BNCProductCategoryOfficeSupplies = @"Office Supplies";
+BNCProductCategory BNCProductCategoryReligious = @"Religious & Ceremonial";
+BNCProductCategory BNCProductCategorySoftware = @"Software";
+BNCProductCategory BNCProductCategorySportingGoods = @"Sporting Goods";
+BNCProductCategory BNCProductCategoryToysGames = @"Toys & Games";
+BNCProductCategory BNCProductCategoryVehiclesParts = @"Vehicles & Parts";
+
+NSArray* BNCProductCategoryAllCategories(void) {
+ return @[
+ BNCProductCategoryAnimalSupplies,
+ BNCProductCategoryApparel,
+ BNCProductCategoryArtsEntertainment,
+ BNCProductCategoryBabyToddler,
+ BNCProductCategoryBusinessIndustrial,
+ BNCProductCategoryCamerasOptics,
+ BNCProductCategoryElectronics,
+ BNCProductCategoryFoodBeverageTobacco,
+ BNCProductCategoryFurniture,
+ BNCProductCategoryHardware,
+ BNCProductCategoryHealthBeauty,
+ BNCProductCategoryHomeGarden,
+ BNCProductCategoryLuggageBags,
+ BNCProductCategoryMature,
+ BNCProductCategoryMedia,
+ BNCProductCategoryOfficeSupplies,
+ BNCProductCategoryReligious,
+ BNCProductCategorySoftware,
+ BNCProductCategorySportingGoods,
+ BNCProductCategoryToysGames,
+ BNCProductCategoryVehiclesParts,
+ ];
+}
+
+#pragma mark - BNCCurrency
+
+BNCCurrency BNCCurrencyAED = @"AED";
+BNCCurrency BNCCurrencyAFN = @"AFN";
+BNCCurrency BNCCurrencyALL = @"ALL";
+BNCCurrency BNCCurrencyAMD = @"AMD";
+BNCCurrency BNCCurrencyANG = @"ANG";
+BNCCurrency BNCCurrencyAOA = @"AOA";
+BNCCurrency BNCCurrencyARS = @"ARS";
+BNCCurrency BNCCurrencyAUD = @"AUD";
+BNCCurrency BNCCurrencyAWG = @"AWG";
+BNCCurrency BNCCurrencyAZN = @"AZN";
+BNCCurrency BNCCurrencyBAM = @"BAM";
+BNCCurrency BNCCurrencyBBD = @"BBD";
+
+BNCCurrency BNCCurrencyBDT = @"BDT";
+BNCCurrency BNCCurrencyBGN = @"BGN";
+BNCCurrency BNCCurrencyBHD = @"BHD";
+BNCCurrency BNCCurrencyBIF = @"BIF";
+BNCCurrency BNCCurrencyBMD = @"BMD";
+BNCCurrency BNCCurrencyBND = @"BND";
+BNCCurrency BNCCurrencyBOB = @"BOB";
+BNCCurrency BNCCurrencyBOV = @"BOV";
+BNCCurrency BNCCurrencyBRL = @"BRL";
+BNCCurrency BNCCurrencyBSD = @"BSD";
+BNCCurrency BNCCurrencyBTN = @"BTN";
+BNCCurrency BNCCurrencyBWP = @"BWP";
+
+BNCCurrency BNCCurrencyBYN = @"BYN";
+BNCCurrency BNCCurrencyBYR = @"BYR";
+BNCCurrency BNCCurrencyBZD = @"BZD";
+BNCCurrency BNCCurrencyCAD = @"CAD";
+BNCCurrency BNCCurrencyCDF = @"CDF";
+BNCCurrency BNCCurrencyCHE = @"CHE";
+BNCCurrency BNCCurrencyCHF = @"CHF";
+BNCCurrency BNCCurrencyCHW = @"CHW";
+BNCCurrency BNCCurrencyCLF = @"CLF";
+BNCCurrency BNCCurrencyCLP = @"CLP";
+BNCCurrency BNCCurrencyCNY = @"CNY";
+BNCCurrency BNCCurrencyCOP = @"COP";
+
+BNCCurrency BNCCurrencyCOU = @"COU";
+BNCCurrency BNCCurrencyCRC = @"CRC";
+BNCCurrency BNCCurrencyCUC = @"CUC";
+BNCCurrency BNCCurrencyCUP = @"CUP";
+BNCCurrency BNCCurrencyCVE = @"CVE";
+BNCCurrency BNCCurrencyCZK = @"CZK";
+BNCCurrency BNCCurrencyDJF = @"DJF";
+BNCCurrency BNCCurrencyDKK = @"DKK";
+BNCCurrency BNCCurrencyDOP = @"DOP";
+BNCCurrency BNCCurrencyDZD = @"DZD";
+BNCCurrency BNCCurrencyEGP = @"EGP";
+BNCCurrency BNCCurrencyERN = @"ERN";
+
+BNCCurrency BNCCurrencyETB = @"ETB";
+BNCCurrency BNCCurrencyEUR = @"EUR";
+BNCCurrency BNCCurrencyFJD = @"FJD";
+BNCCurrency BNCCurrencyFKP = @"FKP";
+BNCCurrency BNCCurrencyGBP = @"GBP";
+BNCCurrency BNCCurrencyGEL = @"GEL";
+BNCCurrency BNCCurrencyGHS = @"GHS";
+BNCCurrency BNCCurrencyGIP = @"GIP";
+BNCCurrency BNCCurrencyGMD = @"GMD";
+BNCCurrency BNCCurrencyGNF = @"GNF";
+BNCCurrency BNCCurrencyGTQ = @"GTQ";
+BNCCurrency BNCCurrencyGYD = @"GYD";
+
+BNCCurrency BNCCurrencyHKD = @"HKD";
+BNCCurrency BNCCurrencyHNL = @"HNL";
+BNCCurrency BNCCurrencyHRK = @"HRK";
+BNCCurrency BNCCurrencyHTG = @"HTG";
+BNCCurrency BNCCurrencyHUF = @"HUF";
+BNCCurrency BNCCurrencyIDR = @"IDR";
+BNCCurrency BNCCurrencyILS = @"ILS";
+BNCCurrency BNCCurrencyINR = @"INR";
+BNCCurrency BNCCurrencyIQD = @"IQD";
+BNCCurrency BNCCurrencyIRR = @"IRR";
+BNCCurrency BNCCurrencyISK = @"ISK";
+BNCCurrency BNCCurrencyJMD = @"JMD";
+
+BNCCurrency BNCCurrencyJOD = @"JOD";
+BNCCurrency BNCCurrencyJPY = @"JPY";
+BNCCurrency BNCCurrencyKES = @"KES";
+BNCCurrency BNCCurrencyKGS = @"KGS";
+BNCCurrency BNCCurrencyKHR = @"KHR";
+BNCCurrency BNCCurrencyKMF = @"KMF";
+BNCCurrency BNCCurrencyKPW = @"KPW";
+BNCCurrency BNCCurrencyKRW = @"KRW";
+BNCCurrency BNCCurrencyKWD = @"KWD";
+BNCCurrency BNCCurrencyKYD = @"KYD";
+BNCCurrency BNCCurrencyKZT = @"KZT";
+BNCCurrency BNCCurrencyLAK = @"LAK";
+
+BNCCurrency BNCCurrencyLBP = @"LBP";
+BNCCurrency BNCCurrencyLKR = @"LKR";
+BNCCurrency BNCCurrencyLRD = @"LRD";
+BNCCurrency BNCCurrencyLSL = @"LSL";
+BNCCurrency BNCCurrencyLYD = @"LYD";
+BNCCurrency BNCCurrencyMAD = @"MAD";
+BNCCurrency BNCCurrencyMDL = @"MDL";
+BNCCurrency BNCCurrencyMGA = @"MGA";
+BNCCurrency BNCCurrencyMKD = @"MKD";
+BNCCurrency BNCCurrencyMMK = @"MMK";
+BNCCurrency BNCCurrencyMNT = @"MNT";
+BNCCurrency BNCCurrencyMOP = @"MOP";
+
+BNCCurrency BNCCurrencyMRO = @"MRO";
+BNCCurrency BNCCurrencyMUR = @"MUR";
+BNCCurrency BNCCurrencyMVR = @"MVR";
+BNCCurrency BNCCurrencyMWK = @"MWK";
+BNCCurrency BNCCurrencyMXN = @"MXN";
+BNCCurrency BNCCurrencyMXV = @"MXV";
+BNCCurrency BNCCurrencyMYR = @"MYR";
+BNCCurrency BNCCurrencyMZN = @"MZN";
+BNCCurrency BNCCurrencyNAD = @"NAD";
+BNCCurrency BNCCurrencyNGN = @"NGN";
+BNCCurrency BNCCurrencyNIO = @"NIO";
+BNCCurrency BNCCurrencyNOK = @"NOK";
+
+BNCCurrency BNCCurrencyNPR = @"NPR";
+BNCCurrency BNCCurrencyNZD = @"NZD";
+BNCCurrency BNCCurrencyOMR = @"OMR";
+BNCCurrency BNCCurrencyPAB = @"PAB";
+BNCCurrency BNCCurrencyPEN = @"PEN";
+BNCCurrency BNCCurrencyPGK = @"PGK";
+BNCCurrency BNCCurrencyPHP = @"PHP";
+BNCCurrency BNCCurrencyPKR = @"PKR";
+BNCCurrency BNCCurrencyPLN = @"PLN";
+BNCCurrency BNCCurrencyPYG = @"PYG";
+BNCCurrency BNCCurrencyQAR = @"QAR";
+BNCCurrency BNCCurrencyRON = @"RON";
+
+BNCCurrency BNCCurrencyRSD = @"RSD";
+BNCCurrency BNCCurrencyRUB = @"RUB";
+BNCCurrency BNCCurrencyRWF = @"RWF";
+BNCCurrency BNCCurrencySAR = @"SAR";
+BNCCurrency BNCCurrencySBD = @"SBD";
+BNCCurrency BNCCurrencySCR = @"SCR";
+BNCCurrency BNCCurrencySDG = @"SDG";
+BNCCurrency BNCCurrencySEK = @"SEK";
+BNCCurrency BNCCurrencySGD = @"SGD";
+BNCCurrency BNCCurrencySHP = @"SHP";
+BNCCurrency BNCCurrencySLL = @"SLL";
+BNCCurrency BNCCurrencySOS = @"SOS";
+
+BNCCurrency BNCCurrencySRD = @"SRD";
+BNCCurrency BNCCurrencySSP = @"SSP";
+BNCCurrency BNCCurrencySTD = @"STD";
+BNCCurrency BNCCurrencySYP = @"SYP";
+BNCCurrency BNCCurrencySZL = @"SZL";
+BNCCurrency BNCCurrencyTHB = @"THB";
+BNCCurrency BNCCurrencyTJS = @"TJS";
+BNCCurrency BNCCurrencyTMT = @"TMT";
+BNCCurrency BNCCurrencyTND = @"TND";
+BNCCurrency BNCCurrencyTOP = @"TOP";
+BNCCurrency BNCCurrencyTRY = @"TRY";
+BNCCurrency BNCCurrencyTTD = @"TTD";
+
+BNCCurrency BNCCurrencyTWD = @"TWD";
+BNCCurrency BNCCurrencyTZS = @"TZS";
+BNCCurrency BNCCurrencyUAH = @"UAH";
+BNCCurrency BNCCurrencyUGX = @"UGX";
+BNCCurrency BNCCurrencyUSD = @"USD";
+BNCCurrency BNCCurrencyUSN = @"USN";
+BNCCurrency BNCCurrencyUYI = @"UYI";
+BNCCurrency BNCCurrencyUYU = @"UYU";
+BNCCurrency BNCCurrencyUZS = @"UZS";
+BNCCurrency BNCCurrencyVEF = @"VEF";
+BNCCurrency BNCCurrencyVND = @"VND";
+BNCCurrency BNCCurrencyVUV = @"VUV";
+
+BNCCurrency BNCCurrencyWST = @"WST";
+BNCCurrency BNCCurrencyXAF = @"XAF";
+BNCCurrency BNCCurrencyXAG = @"XAG";
+BNCCurrency BNCCurrencyXAU = @"XAU";
+BNCCurrency BNCCurrencyXBA = @"XBA";
+BNCCurrency BNCCurrencyXBB = @"XBB";
+BNCCurrency BNCCurrencyXBC = @"XBC";
+BNCCurrency BNCCurrencyXBD = @"XBD";
+BNCCurrency BNCCurrencyXCD = @"XCD";
+BNCCurrency BNCCurrencyXDR = @"XDR";
+BNCCurrency BNCCurrencyXFU = @"XFU";
+BNCCurrency BNCCurrencyXOF = @"XOF";
+
+BNCCurrency BNCCurrencyXPD = @"XPD";
+BNCCurrency BNCCurrencyXPF = @"XPF";
+BNCCurrency BNCCurrencyXPT = @"XPT";
+BNCCurrency BNCCurrencyXSU = @"XSU";
+BNCCurrency BNCCurrencyXTS = @"XTS";
+BNCCurrency BNCCurrencyXUA = @"XUA";
+BNCCurrency BNCCurrencyXXX = @"XXX";
+BNCCurrency BNCCurrencyYER = @"YER";
+BNCCurrency BNCCurrencyZAR = @"ZAR";
+BNCCurrency BNCCurrencyZMW = @"ZMW";
+
+NSArray* BNCCurrencyAllCurrencies(void) {
+ return @[
+ BNCCurrencyAED,
+ BNCCurrencyAFN,
+ BNCCurrencyALL,
+ BNCCurrencyAMD,
+ BNCCurrencyANG,
+ BNCCurrencyAOA,
+ BNCCurrencyARS,
+ BNCCurrencyAUD,
+ BNCCurrencyAWG,
+ BNCCurrencyAZN,
+ BNCCurrencyBAM,
+ BNCCurrencyBBD,
+
+ BNCCurrencyBDT,
+ BNCCurrencyBGN,
+ BNCCurrencyBHD,
+ BNCCurrencyBIF,
+ BNCCurrencyBMD,
+ BNCCurrencyBND,
+ BNCCurrencyBOB,
+ BNCCurrencyBOV,
+ BNCCurrencyBRL,
+ BNCCurrencyBSD,
+ BNCCurrencyBTN,
+ BNCCurrencyBWP,
+
+ BNCCurrencyBYN,
+ BNCCurrencyBYR,
+ BNCCurrencyBZD,
+ BNCCurrencyCAD,
+ BNCCurrencyCDF,
+ BNCCurrencyCHE,
+ BNCCurrencyCHF,
+ BNCCurrencyCHW,
+ BNCCurrencyCLF,
+ BNCCurrencyCLP,
+ BNCCurrencyCNY,
+ BNCCurrencyCOP,
+
+ BNCCurrencyCOU,
+ BNCCurrencyCRC,
+ BNCCurrencyCUC,
+ BNCCurrencyCUP,
+ BNCCurrencyCVE,
+ BNCCurrencyCZK,
+ BNCCurrencyDJF,
+ BNCCurrencyDKK,
+ BNCCurrencyDOP,
+ BNCCurrencyDZD,
+ BNCCurrencyEGP,
+ BNCCurrencyERN,
+
+ BNCCurrencyETB,
+ BNCCurrencyEUR,
+ BNCCurrencyFJD,
+ BNCCurrencyFKP,
+ BNCCurrencyGBP,
+ BNCCurrencyGEL,
+ BNCCurrencyGHS,
+ BNCCurrencyGIP,
+ BNCCurrencyGMD,
+ BNCCurrencyGNF,
+ BNCCurrencyGTQ,
+ BNCCurrencyGYD,
+
+ BNCCurrencyHKD,
+ BNCCurrencyHNL,
+ BNCCurrencyHRK,
+ BNCCurrencyHTG,
+ BNCCurrencyHUF,
+ BNCCurrencyIDR,
+ BNCCurrencyILS,
+ BNCCurrencyINR,
+ BNCCurrencyIQD,
+ BNCCurrencyIRR,
+ BNCCurrencyISK,
+ BNCCurrencyJMD,
+
+ BNCCurrencyJOD,
+ BNCCurrencyJPY,
+ BNCCurrencyKES,
+ BNCCurrencyKGS,
+ BNCCurrencyKHR,
+ BNCCurrencyKMF,
+ BNCCurrencyKPW,
+ BNCCurrencyKRW,
+ BNCCurrencyKWD,
+ BNCCurrencyKYD,
+ BNCCurrencyKZT,
+ BNCCurrencyLAK,
+
+ BNCCurrencyLBP,
+ BNCCurrencyLKR,
+ BNCCurrencyLRD,
+ BNCCurrencyLSL,
+ BNCCurrencyLYD,
+ BNCCurrencyMAD,
+ BNCCurrencyMDL,
+ BNCCurrencyMGA,
+ BNCCurrencyMKD,
+ BNCCurrencyMMK,
+ BNCCurrencyMNT,
+ BNCCurrencyMOP,
+
+ BNCCurrencyMRO,
+ BNCCurrencyMUR,
+ BNCCurrencyMVR,
+ BNCCurrencyMWK,
+ BNCCurrencyMXN,
+ BNCCurrencyMXV,
+ BNCCurrencyMYR,
+ BNCCurrencyMZN,
+ BNCCurrencyNAD,
+ BNCCurrencyNGN,
+ BNCCurrencyNIO,
+ BNCCurrencyNOK,
+
+ BNCCurrencyNPR,
+ BNCCurrencyNZD,
+ BNCCurrencyOMR,
+ BNCCurrencyPAB,
+ BNCCurrencyPEN,
+ BNCCurrencyPGK,
+ BNCCurrencyPHP,
+ BNCCurrencyPKR,
+ BNCCurrencyPLN,
+ BNCCurrencyPYG,
+ BNCCurrencyQAR,
+ BNCCurrencyRON,
+
+ BNCCurrencyRSD,
+ BNCCurrencyRUB,
+ BNCCurrencyRWF,
+ BNCCurrencySAR,
+ BNCCurrencySBD,
+ BNCCurrencySCR,
+ BNCCurrencySDG,
+ BNCCurrencySEK,
+ BNCCurrencySGD,
+ BNCCurrencySHP,
+ BNCCurrencySLL,
+ BNCCurrencySOS,
+
+ BNCCurrencySRD,
+ BNCCurrencySSP,
+ BNCCurrencySTD,
+ BNCCurrencySYP,
+ BNCCurrencySZL,
+ BNCCurrencyTHB,
+ BNCCurrencyTJS,
+ BNCCurrencyTMT,
+ BNCCurrencyTND,
+ BNCCurrencyTOP,
+ BNCCurrencyTRY,
+ BNCCurrencyTTD,
+
+ BNCCurrencyTWD,
+ BNCCurrencyTZS,
+ BNCCurrencyUAH,
+ BNCCurrencyUGX,
+ BNCCurrencyUSD,
+ BNCCurrencyUSN,
+ BNCCurrencyUYI,
+ BNCCurrencyUYU,
+ BNCCurrencyUZS,
+ BNCCurrencyVEF,
+ BNCCurrencyVND,
+ BNCCurrencyVUV,
+
+ BNCCurrencyWST,
+ BNCCurrencyXAF,
+ BNCCurrencyXAG,
+ BNCCurrencyXAU,
+ BNCCurrencyXBA,
+ BNCCurrencyXBB,
+ BNCCurrencyXBC,
+ BNCCurrencyXBD,
+ BNCCurrencyXCD,
+ BNCCurrencyXDR,
+ BNCCurrencyXFU,
+ BNCCurrencyXOF,
+
+ BNCCurrencyXPD,
+ BNCCurrencyXPF,
+ BNCCurrencyXPT,
+ BNCCurrencyXSU,
+ BNCCurrencyXTS,
+ BNCCurrencyXUA,
+ BNCCurrencyXXX,
+ BNCCurrencyYER,
+ BNCCurrencyZAR,
+ BNCCurrencyZMW,
+ ];
+}
+
+#pragma mark - BNCProduct
+
+@implementation BNCProduct
+
+- (NSMutableDictionary*) dictionary {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ #define assign(x) \
+ do { if (self.x != nil) { dictionary[@#x] = self.x; } } while (0)
+
+ assign(sku);
+ assign(name);
+ assign(price);
+ assign(quantity);
+ assign(brand);
+ assign(category);
+ assign(variant);
+
+ #undef assign
+
+ return dictionary;
+}
+
+- (NSString*) description {
+ return [NSString stringWithFormat:
+ @"Name: %@ Sku: %@ Price: %@ Quantity: %@ Brand: %@ Category: %@ Variant: %@",
+ self.name,
+ self.sku,
+ self.price,
+ self.quantity,
+ self.brand,
+ self.category,
+ self.variant];
+}
+
+@end
+
+#pragma mark - BNCCommerceEvent
+
+@implementation BNCCommerceEvent : NSObject
+
+- (NSDictionary*) dictionary {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
+
+ #define assign(x) \
+ do { if (self.x) { dictionary[@#x] = self.x; } } while (0)
+
+ assign(revenue);
+ assign(currency);
+ if (self.transactionID) {
+ dictionary[@"transaction_id"] = self.transactionID;
+ }
+ assign(shipping);
+ assign(tax);
+ assign(coupon);
+ assign(affiliation);
+
+ NSMutableArray *products = [NSMutableArray arrayWithCapacity:self.products.count];
+ for (BNCProduct *product in self.products) {
+ NSDictionary * d = [product dictionary];
+ if (d) [products addObject:d];
+ }
+ dictionary[@"products"] = products;
+
+ #undef assign
+
+ return dictionary;
+}
+
+- (NSString*) description {
+ return [NSString stringWithFormat:
+ @"Revenue: %@ Currency: %@ TxID: %@ Shipping: %@ Tax: %@ Coupon: %@ Affl: %@ Products: %lu",
+ self.revenue,
+ self.currency,
+ self.transactionID,
+ self.shipping,
+ self.tax,
+ self.coupon,
+ self.affiliation,
+ (unsigned long) self.products.count];
+}
+
+@end
+
+#pragma mark - BranchCommerceEventRequest
+
+@interface BranchCommerceEventRequest ()
+@property (strong) NSDictionary *commerceDictionary;
+@property (strong) NSDictionary *metadata;
+@property (copy) void (^completion)(NSDictionary* response, NSError* error);
+@end
+
+@implementation BranchCommerceEventRequest
+
+- (instancetype) initWithCommerceEvent:(BNCCommerceEvent*)commerceEvent
+ metadata:(NSDictionary*)metadata
+ completion:(void (^)(NSDictionary* response, NSError* error))completion {
+ self = [super init];
+ if (!self) return self;
+
+ if ([commerceEvent.revenue isEqualToNumber:[NSDecimalNumber numberWithDouble:0.0]]) {
+ BNCLogWarning(@"Sending a commerce event with zero revenue.");
+ }
+
+ self.commerceDictionary = [commerceEvent dictionary];
+ self.metadata = metadata;
+ self.completion = completion;
+ return self;
+}
+
+- (void)makeRequest:(BNCServerInterface *)serverInterface
+ key:(NSString *)key callback:(BNCServerCallback)callback {
+
+ BNCPreferenceHelper *preferenceHelper = [BNCPreferenceHelper preferenceHelper];
+
+ NSMutableDictionary *params = [NSMutableDictionary dictionary];
+ params[BRANCH_REQUEST_KEY_ACTION] = @"purchase";
+ params[BRANCH_REQUEST_KEY_DEVICE_FINGERPRINT_ID] = preferenceHelper.deviceFingerprintID;
+ params[BRANCH_REQUEST_KEY_BRANCH_IDENTITY] = preferenceHelper.identityID;
+ params[BRANCH_REQUEST_KEY_SESSION_ID] = preferenceHelper.sessionID;
+
+ if (self.metadata)
+ params[@"metadata"] = self.metadata;
+ if (self.commerceDictionary)
+ params[@"commerce_data"] = self.commerceDictionary;
+ if (preferenceHelper.limitFacebookTracking)
+ params[@"limit_facebook_tracking"] = (__bridge NSNumber*) kCFBooleanTrue;
+
+ NSString *URL = [preferenceHelper getAPIURL:BRANCH_REQUEST_ENDPOINT_USER_COMPLETED_ACTION];
+ [serverInterface postRequest:params
+ url:URL
+ key:key
+ callback:callback];
+}
+
+- (void)processResponse:(BNCServerResponse*)response
+ error:(NSError*)error {
+
+ NSDictionary *dictionary =
+ ([response.data isKindOfClass:[NSDictionary class]])
+ ? (NSDictionary*) response.data
+ : nil;
+
+ if (self.completion)
+ self.completion(dictionary, error);
+}
+
+#pragma mark BranchCommerceEventRequest NSSecureCoding
+
+- (instancetype)initWithCoder:(NSCoder *)decoder {
+ self = [super initWithCoder:decoder];
+ if (!self) return self;
+ self.commerceDictionary = [decoder decodeObjectOfClass:NSDictionary.class forKey:@"commerceDictionary"];
+ self.metadata = [decoder decodeObjectOfClass:NSDictionary.class forKey:@"metaData"];
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ [super encodeWithCoder:coder];
+ [coder encodeObject:self.commerceDictionary forKey:@"commerceDictionary"];
+ [coder encodeObject:self.metadata forKey:@"metadata"];
+}
+
++ (BOOL) supportsSecureCoding {
+ return YES;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCConfig.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCConfig.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCConfig.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCConfig.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCConfig.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCConfig.m
new file mode 100644
index 00000000000..5fe0d05d8f5
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCConfig.m
@@ -0,0 +1,15 @@
+//
+// BNCConfig.c
+// Branch-SDK
+//
+// Created by Edward Smith on 12/12/16.
+// Copyright © 2016 Branch Metrics. All rights reserved.
+//
+
+#include "BNCConfig.h"
+
+NSString * const BNC_API_BASE_URL = @"https://api2.branch.io";
+BOOL const BNC_API_PINNED = NO;
+NSString * const BNC_API_VERSION = @"v1";
+NSString * const BNC_LINK_URL = @"https://bnc.lt";
+NSString * const BNC_SDK_VERSION = @"0.25.11";
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCContentDiscoveryManager.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCContentDiscoveryManager.h
new file mode 100644
index 00000000000..ac696ca4de9
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCContentDiscoveryManager.h
@@ -0,0 +1,62 @@
+//
+// BNCContentDiscoveryManager.h
+// Branch-TestBed
+//
+// Created by Graham Mueller on 7/17/15.
+// Copyright © 2015 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+#import "BNCCallbacks.h"
+
+@interface BNCContentDiscoveryManager : NSObject
+
+- (NSString *)spotlightIdentifierFromActivity:(NSUserActivity *)userActivity;
+- (NSString *)standardSpotlightIdentifierFromActivity:(NSUserActivity *)userActivity;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description callback:(callbackWithUrl)callback;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable callback:(callbackWithUrl)callback;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type callback:(callbackWithUrl)callback;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type thumbnailUrl:(NSURL *)thumbnailUrl callback:(callbackWithUrl)callback;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type thumbnailUrl:(NSURL *)thumbnailUrl keywords:(NSSet *)keywords callback:(callbackWithUrl)callback;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type thumbnailUrl:(NSURL *)thumbnailUrl keywords:(NSSet *)keywords;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type thumbnailUrl:(NSURL *)thumbnailUrl keywords:(NSSet *)keywords userInfo:(NSDictionary *)userInfo;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable thumbnailUrl:(NSURL *)thumbnailUrl userInfo:(NSDictionary *)userInfo;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable thumbnailUrl:(NSURL *)thumbnailUrl keywords:(NSSet *)keywords userInfo:(NSDictionary *)userInfo;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type thumbnailUrl:(NSURL *)thumbnailUrl keywords:(NSSet *)keywords userInfo:(NSDictionary *)userInfo callback:(callbackWithUrl)callback;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type thumbnailUrl:(NSURL *)thumbnailUrl keywords:(NSSet *)keywords userInfo:(NSDictionary *)userInfo expirationDate:(NSDate *)expirationDate callback:(callbackWithUrl)callback;
+
+- (void)indexContentWithTitle:(NSString *)title description:(NSString *)description canonicalId:(NSString *)canonicalId publiclyIndexable:(BOOL)publiclyIndexable type:(NSString *)type thumbnailUrl:(NSURL *)thumbnailUrl keywords:(NSSet *)keywords userInfo:(NSDictionary *)userInfo expirationDate:(NSDate *)expirationDate callback:(callbackWithUrl)callback;
+
+
+/* This one has a different callback, which includes the spotlightIdentifier, and requires a different signature
+ It cannot be part of the stack of method signatures above, because of the different callback type.*/
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ canonicalId:(NSString *)canonicalId
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo
+ expirationDate:(NSDate *)expirationDate
+ callback:(callbackWithUrl)callback
+ spotlightCallback:(callbackWithUrlAndSpotlightIdentifier)spotlightCallback;
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCContentDiscoveryManager.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCContentDiscoveryManager.m
new file mode 100644
index 00000000000..01f5181a09e
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCContentDiscoveryManager.m
@@ -0,0 +1,406 @@
+//
+// BNCContentDiscoveryManager.m
+// Branch-TestBed
+//
+// Created by Graham Mueller on 7/17/15.
+// Copyright © 2015 Branch Metrics. All rights reserved.
+//
+
+#import "BNCContentDiscoveryManager.h"
+#import "Branch.h"
+#import "BranchConstants.h"
+#import "BNCSpotlightService.h"
+
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
+ #if __has_feature(modules)
+ @import MobileCoreServices;
+ #else
+ #import
+ #endif
+#endif
+
+static NSString* const kUTTypeGeneric = @"public.content";
+
+#ifndef CSSearchableItemActivityIdentifier
+#define CSSearchableItemActivityIdentifier @"kCSSearchableItemActivityIdentifier"
+#endif
+
+@interface BNCContentDiscoveryManager (){
+ dispatch_queue_t _workQueue;
+}
+
+@property (strong, readonly) dispatch_queue_t workQueue;
+@property (strong, atomic) BNCSpotlightService* spotlight;
+
+@end
+
+@implementation BNCContentDiscoveryManager
+
+
+- (id) init {
+ self = [super init];
+
+ if (self) {
+ self.spotlight = [[BNCSpotlightService alloc] init];
+ }
+ return self;
+}
+
+#pragma mark - Launch handling
+
+- (NSString *)spotlightIdentifierFromActivity:(NSUserActivity *)userActivity {
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
+ // If it has our prefix, then the link identifier is just the last piece of the identifier.
+ NSString *activityIdentifier = userActivity.userInfo[CSSearchableItemActivityIdentifier];
+ BOOL isBranchIdentifier = [activityIdentifier hasPrefix:BRANCH_SPOTLIGHT_PREFIX];
+ if (isBranchIdentifier) {
+ return activityIdentifier;
+ }
+#endif
+
+ return nil;
+}
+
+- (NSString *)standardSpotlightIdentifierFromActivity:(NSUserActivity *)userActivity {
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
+ if (userActivity.userInfo[CSSearchableItemActivityIdentifier]) {
+ return userActivity.userInfo[CSSearchableItemActivityIdentifier];
+ }
+#endif
+
+ return nil;
+}
+
+#pragma mark - Content Indexing
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:NO
+ type:(NSString *)kUTTypeGeneric
+ thumbnailUrl:nil
+ keywords:nil
+ userInfo:nil
+ expirationDate:nil
+ callback:NULL];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:NO
+ type:(NSString *)kUTTypeGeneric
+ thumbnailUrl:nil
+ keywords:nil
+ userInfo:nil
+ expirationDate:nil
+ callback:callback];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:(NSString *)kUTTypeGeneric
+ thumbnailUrl:nil
+ keywords:nil userInfo:nil
+ expirationDate:nil
+ callback:callback];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:nil
+ keywords:nil
+ userInfo:nil
+ expirationDate:nil
+ callback: callback];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:nil
+ userInfo:nil
+ expirationDate:nil
+ callback:callback];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:nil
+ expirationDate:nil
+ callback:callback];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:nil
+ expirationDate:nil
+ callback:NULL];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:userInfo
+ expirationDate:nil
+ callback:NULL];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ userInfo:(NSDictionary *)userInfo {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:kUTTypeGeneric
+ thumbnailUrl:thumbnailUrl
+ keywords:nil
+ userInfo:userInfo
+ expirationDate:nil
+ callback:NULL];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo {
+ [self indexContentWithTitle:title
+ description:description
+ publiclyIndexable:publiclyIndexable
+ type:kUTTypeGeneric
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:userInfo
+ expirationDate:nil
+ callback:NULL];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo
+ expirationDate:(NSDate*)expirationDate
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ canonicalId:nil
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:userInfo
+ expirationDate:nil
+ callback:callback
+ spotlightCallback:nil];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ canonicalId:(NSString *)canonicalId
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo
+ expirationDate:(NSDate*)expirationDate
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ canonicalId:canonicalId
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:userInfo
+ expirationDate:nil
+ callback:callback
+ spotlightCallback:nil];
+}
+
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo
+ callback:(callbackWithUrl)callback {
+ [self indexContentWithTitle:title
+ description:description
+ canonicalId:nil
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:userInfo
+ expirationDate:nil
+ callback:callback
+ spotlightCallback:nil];
+}
+
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo
+ spotlightCallback:(callbackWithUrlAndSpotlightIdentifier)spotlightCallback {
+ [self indexContentWithTitle:title
+ description:description
+ canonicalId:nil
+ publiclyIndexable:publiclyIndexable
+ type:type
+ thumbnailUrl:thumbnailUrl
+ keywords:keywords
+ userInfo:userInfo
+ expirationDate:nil
+ callback:nil
+ spotlightCallback:spotlightCallback];
+}
+
+-(void) indexObject:(BranchUniversalObject *)universalObject
+ onCompletion:(void (^)(BranchUniversalObject *, NSString*, NSError *))completion {
+
+ [self indexContentWithTitle:universalObject.title
+ description:universalObject.description
+ canonicalId:universalObject.canonicalUrl
+ publiclyIndexable:universalObject.locallyIndex
+ type:universalObject.contentMetadata.contentSchema
+ thumbnailUrl:[NSURL URLWithString: universalObject.imageUrl]
+ keywords:[NSSet setWithArray:universalObject.keywords]
+ userInfo:universalObject.contentMetadata.customMetadata expirationDate:nil
+ callback:nil
+ spotlightCallback:^(NSString * _Nullable url, NSString * _Nullable spotlightIdentifier, NSError * _Nullable error) {
+
+ if (error) {
+ completion(universalObject,url,error);
+ } else {
+ completion(universalObject,url,error);
+ }
+ }];
+}
+
+//This is the final one, which figures out which callback to use, if any
+// The simpler callbackWithURL overrides spotlightCallback, so don't send both
+- (void)indexContentWithTitle:(NSString *)title
+ description:(NSString *)description
+ canonicalId:(NSString *)canonicalId
+ publiclyIndexable:(BOOL)publiclyIndexable
+ type:(NSString *)type
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ keywords:(NSSet *)keywords
+ userInfo:(NSDictionary *)userInfo
+ expirationDate:(NSDate *)expirationDate
+ callback:(callbackWithUrl)callback
+ spotlightCallback:(callbackWithUrlAndSpotlightIdentifier)spotlightCallback {
+
+ if (!userInfo) userInfo = @{};
+ NSMutableDictionary *customData = [NSMutableDictionary dictionaryWithDictionary:userInfo];
+ if (!customData) customData = [NSMutableDictionary dictionaryWithDictionary:@{}];
+
+ BNCSpotlightService* spotlightService = [[BNCSpotlightService alloc] init];
+
+ BranchUniversalObject *universalObject = [[BranchUniversalObject alloc] initWithTitle:title];
+ [universalObject setContentDescription:description];
+ [universalObject setCanonicalUrl:canonicalId];
+ [universalObject setLocallyIndex:publiclyIndexable];
+ [universalObject.contentMetadata setContentSchema:type];
+ [universalObject setImageUrl:[thumbnailUrl absoluteString]];
+ [universalObject setKeywords:[keywords allObjects]];
+ [universalObject.contentMetadata setCustomMetadata:customData];
+ [universalObject setExpirationDate:expirationDate];
+
+ if(publiclyIndexable) {
+ [spotlightService indexWithBranchUniversalObject:universalObject
+ linkProperties:nil
+ callback:^(BranchUniversalObject * _Nullable universalObject,
+ NSString * _Nullable url,
+ NSError * _Nullable error) {
+ if (callback) {
+ callback(url, error);
+ }
+ else if (spotlightCallback) {
+ spotlightCallback(url, url, error);
+ }
+ }];
+
+ } else {
+ [spotlightService indexWithBranchUniversalObject:universalObject
+ linkProperties:nil
+ callback:^(BranchUniversalObject * _Nullable universalObject,
+ NSString * _Nullable url,
+ NSError * _Nullable error) {
+ if (callback) {
+ callback(url, error);
+ }
+ else if (spotlightCallback) {
+ spotlightCallback(url, url, error);
+ }
+ }];
+ }
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCrashlyticsWrapper.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCrashlyticsWrapper.h
new file mode 100644
index 00000000000..c5fc8245a27
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCrashlyticsWrapper.h
@@ -0,0 +1,34 @@
+//
+// BNCCrashlyticsWrapper.h
+// Branch.framework
+//
+// Created by Jimmy Dee on 7/18/17.
+// Copyright © 2017 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+/**
+ * Convenience class to dynamically wrap the Crashlytics SDK
+ * if present. If it is not present, everything here is a no-op.
+ */
+@interface BNCCrashlyticsWrapper : NSObject
+
+/// Reference to the Crashlytics.sharedInstance or nil.
+@property (nonatomic, nullable, readonly) id crashlytics;
+
+/// Convenience method to create new instances
++ (instancetype _Nonnull)wrapper;
+
+/**
+ * Use these methods to set key values in a Crashlytics report.
+ */
+- (void)setObjectValue:(id _Nullable)value forKey:(NSString * _Nonnull)key;
+- (void)setIntValue:(int)value forKey:(NSString * _Nonnull)key;
+- (void)setBoolValue:(BOOL)value forKey:(NSString * _Nonnull)key;
+- (void)setFloatValue:(float)value forKey:(NSString * _Nonnull)key;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCrashlyticsWrapper.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCrashlyticsWrapper.m
new file mode 100644
index 00000000000..7046ba6ec25
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCCrashlyticsWrapper.m
@@ -0,0 +1,71 @@
+//
+// BNCCrashlyticsReportingHelper.m
+// Branch.framework
+//
+// Created by Jimmy Dee on 7/18/17.
+// Copyright © 2017 Branch Metrics. All rights reserved.
+//
+
+#import "BNCCrashlyticsWrapper.h"
+
+@interface BNCCrashlyticsWrapper()
+@property (nonatomic, nullable) id crashlytics;
+@end
+
+@implementation BNCCrashlyticsWrapper
+
++ (id)sharedInstance
+{
+ // This just exists so that sharedInstance is not an unknown selector.
+ return nil;
+}
+
++ (instancetype)wrapper
+{
+ return [[self alloc] init];
+}
+
+- (instancetype)init
+{
+ self = [super init];
+ if (self) {
+ // Dynamically obtain Crashlytics.sharedInstance if the Crashlytics SDK is linked.
+ Class Crashlytics = NSClassFromString(@"Crashlytics");
+ if ([Crashlytics respondsToSelector:@selector(sharedInstance)]) {
+ id crashlyticsInstance = [Crashlytics sharedInstance];
+ if ([crashlyticsInstance isKindOfClass:Crashlytics] &&
+ [crashlyticsInstance respondsToSelector:@selector(setObjectValue:forKey:)] &&
+ [crashlyticsInstance respondsToSelector:@selector(setBoolValue:forKey:)] &&
+ [crashlyticsInstance respondsToSelector:@selector(setFloatValue:forKey:)] &&
+ [crashlyticsInstance respondsToSelector:@selector(setIntValue:forKey:)])
+ _crashlytics = crashlyticsInstance;
+ }
+ }
+ return self;
+}
+
+- (void)setObjectValue:(id)value forKey:(NSString *)key
+{
+ if (!self.crashlytics) return;
+ [self.crashlytics setObjectValue:value forKey:key];
+}
+
+- (void)setIntValue:(int)value forKey:(NSString *)key
+{
+ if (!self.crashlytics) return;
+ [self.crashlytics setIntValue:value forKey:key];
+}
+
+- (void)setFloatValue:(float)value forKey:(NSString *)key
+{
+ if (!self.crashlytics) return;
+ [self.crashlytics setFloatValue:value forKey:key];
+}
+
+- (void)setBoolValue:(BOOL)value forKey:(NSString *)key
+{
+ if (!self.crashlytics) return;
+ [self.crashlytics setBoolValue:value forKey:key];
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCDebug.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDebug.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCDebug.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDebug.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDebug.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDebug.m
new file mode 100644
index 00000000000..6a8732c8ed2
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDebug.m
@@ -0,0 +1,225 @@
+/**
+ @file BNCDebug.m
+ @package Branch-SDK
+ @brief Debugging Support.
+
+ @author Edward Smith
+ @date October 2016
+ @copyright Copyright © 2016 Branch. All rights reserved.
+*/
+
+#import "BNCDebug.h"
+#if __has_feature(modules)
+@import Darwin.sys.sysctl;
+@import ObjectiveC.runtime;
+#else
+#import
+#import
+#endif
+
+BOOL BNCDebuggerIsAttached() {
+ // From an Apple tech note that I've lost --EB Smith
+
+ // Returns true if the current process is being debugged (either
+ // running under the debugger or has a debugger attached post facto).
+
+ int junk;
+ int mib[4];
+ struct kinfo_proc info;
+ size_t size;
+
+ // Initialize the flags so that, if sysctl fails for some bizarre
+ // reason, we get a predictable result.
+
+ info.kp_proc.p_flag = 0;
+
+ // Initialize mib, which tells sysctl the info we want, in this case
+ // we're looking for information about a specific process ID.
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ // Call sysctl.
+
+ size = sizeof(info);
+ junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
+ if (junk != 0) {
+ NSLog(@"Program error in BNCDebuggerIsAttached. Junk != 0!");
+ }
+
+ // We're being debugged if the P_TRACED flag is set.
+
+ return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+}
+
+NSString * _Nonnull BNCDebugStringFromObject(id _Nullable instance) {
+ // Dump the class --
+
+ if (!instance) return @"Object is nil.\n";
+
+ const char* superclassname = "";
+ Class class = object_getClass(instance);
+ Class superclass = class_getSuperclass(class);
+ if (superclass) superclassname = class_getName(superclass);
+ if (!superclassname) superclassname = "";
+
+ NSMutableString *result = [NSMutableString stringWithCapacity:512];
+ if (class_isMetaClass(class)) {
+ [result appendFormat:@"\nClass %p is class '%s' of class '%s':\n",
+ (void*)instance, class_getName(class), superclassname];
+ class = instance;
+ } else {
+ [result appendFormat:@"\nInstance %p is of class '%s' of class '%s':\n",
+ (void*)instance, class_getName(class), superclassname];
+ }
+
+ // Ivars --
+
+ #define isTypeOf(encoding, type) \
+ (strncmp(encoding, @encode(type), strlen(encoding)) == 0)
+
+ #define AppendValueOfType(type, format) \
+ if (isTypeOf(encoding, type)) { \
+ if (ivarPtr) { \
+ [result appendFormat:@"\tIvar '%s' type '%s' value '"format"'.\n", \
+ ivarName, #type, *((type*)ivarPtr)]; \
+ } else { \
+ [result appendFormat:@"\tIvar '%s' type '%s'.\n", \
+ ivarName, #type]; \
+ } \
+ } else
+
+ uint count = 0;
+ Ivar *ivars = class_copyIvarList(class, &count);
+ for (uint i = 0; ivars && i < count; ++i) {
+ const char* encoding = ivar_getTypeEncoding(ivars[i]);
+ const char* ivarName = ivar_getName(ivars[i]);
+ const void* ivarPtr = nil;
+ if (class == instance) {
+ // instance is a class, so there aren't any ivar values.
+ } else if (encoding[0] == '@' || encoding[0] == '#') {
+ ivarPtr = (__bridge void*) object_getIvar(instance, ivars[i]);
+ } else {
+ ivarPtr = (void*) (((__bridge void*)instance) + ivar_getOffset(ivars[i]));
+ }
+
+ if (encoding[0] == '@') {
+ if (ivarPtr)
+ [result appendFormat:@"\tIvar '%s' type '%@' value '%@'.\n",
+ ivarName, NSStringFromClass(((__bridge id)ivarPtr).class), ivarPtr];
+ else {
+ NSString *className = [NSString stringWithFormat:@"%s", encoding];
+ if ([className hasPrefix:@"@\""])
+ className = [className substringFromIndex:2];
+ if ([className hasSuffix:@"\""])
+ className = [className substringToIndex:className.length-1];
+ [result appendFormat:@"\tIvar '%s' type '%@'.\n", ivarName, className];
+ }
+ } else if (isTypeOf(encoding, Class)) {
+ if (ivarPtr)
+ [result appendFormat:@"\tIvar '%s' type 'class' value '%@'.\n",
+ ivarName, NSStringFromClass((__bridge Class _Nonnull)(ivarPtr))];
+ else
+ [result appendFormat:@"\tIvar '%s' type 'class'.\n",
+ ivarName];
+ } else if (isTypeOf(encoding, char*)) {
+ if (ivarPtr)
+ [result appendFormat:@"\tIvar '%s' type 'char*' value '%s'.\n",
+ ivarName, *(char**)ivarPtr];
+ else
+ [result appendFormat:@"\tIvar '%s' type 'char*'.\n",
+ ivarName];
+ } else if (isTypeOf(encoding, BOOL)) {
+ if (ivarPtr)
+ [result appendFormat:@"\tIvar '%s' type 'BOOL' value '%s'.\n",
+ ivarName, (*(BOOL*)ivarPtr)?"YES":"NO"];
+ else
+ [result appendFormat:@"\tIvar '%s' type 'BOOL'.\n",
+ ivarName];
+ } else if (isTypeOf(encoding, int)) {
+ if (ivarPtr)
+ [result appendFormat:@"\tIvar '%s' type '%s' value '%d'.\n",
+ ivarName, "int", *((int*)ivarPtr)];
+ else
+ [result appendFormat:@"\tIvar '%s' type '%s'.\n",
+ ivarName, "int"];
+ } else
+ // clang-format off
+ AppendValueOfType(float, "%f")
+ AppendValueOfType(double, "%f")
+ AppendValueOfType(long double, "%Lf")
+ AppendValueOfType(char, "%c")
+ AppendValueOfType(int, "%d")
+ AppendValueOfType(short, "%hd")
+ AppendValueOfType(long, "%ld")
+ AppendValueOfType(long long, "%lld")
+ AppendValueOfType(unsigned char, "%c")
+ AppendValueOfType(unsigned int, "%u")
+ AppendValueOfType(unsigned short, "%hu")
+ AppendValueOfType(unsigned long, "%lu")
+ AppendValueOfType(unsigned long long, "%llu")
+ [result appendFormat:@"\tIvar '%s' type '%s' (un-handled type).\n",
+ ivarName,
+ encoding];
+ // clang-format off
+ }
+ if (ivars) free(ivars);
+
+ #undef AppendValueOfType
+ #undef isTypeOf
+
+ // Properties --
+
+ count = 0;
+ objc_property_t *properties = class_copyPropertyList(class, &count);
+ for (int i = 0; properties && i < count; ++i)
+ [result appendFormat:@"\tProperty name: '%s'.\n", property_getName(properties[i])];
+ if (properties) free(properties);
+
+ // Class methods --
+
+ count = 0;
+ Method *methods = class_copyMethodList(object_getClass(class), &count);
+ for (int i = 0; i < count; ++i)
+ [result appendFormat:@"\tClass method name: '%s'.\n",
+ sel_getName(method_getName(methods[i]))];
+ if (methods) free(methods);
+
+ // Instance methods --
+
+ count = 0;
+ methods = class_copyMethodList(class, &count);
+ for (int i = 0; i < count; ++i)
+ [result appendFormat:@"\tMethod name: '%s'.\n",
+ sel_getName(method_getName(methods[i]))];
+ if (methods) free(methods);
+
+ return result;
+}
+
+NSArray * _Nonnull BNCDebugArrayOfReqisteredClasses() {
+ // Add all loaded classes to the NSString array:
+
+ int numClasses = 0;
+ Class * classes = NULL;
+
+ numClasses = objc_getClassList(NULL, 0);
+ if (numClasses <= 0) return @[];
+
+ classes = (__unsafe_unretained Class*) malloc(sizeof(Class) * numClasses);
+ numClasses = objc_getClassList(classes, numClasses);
+
+ NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity:numClasses];
+
+ Class *class = classes;
+ for (int i = 0; i < numClasses; ++i) {
+ NSString* s = NSStringFromClass(*class);
+ if (s) [result addObject:s];
+ ++class;
+ }
+
+ free(classes);
+ return result;
+}
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeepLinkViewControllerInstance.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeepLinkViewControllerInstance.h
new file mode 100644
index 00000000000..d0e82aff0f6
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeepLinkViewControllerInstance.h
@@ -0,0 +1,14 @@
+//
+// BNCDeepLinkViewControllerInstance.h
+// Branch-SDK
+//
+// Created by Parth Kalavadia on 5/15/17.
+// Copyright © 2017 Parth Kalavadia. All rights reserved.
+//
+
+#import "BranchDeepLinkingController.h"
+
+@interface BNCDeepLinkViewControllerInstance : NSObject
+@property (strong, nonatomic)UIViewController* viewController;
+@property (assign)BNCViewControllerPresentationOption option;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeepLinkViewControllerInstance.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeepLinkViewControllerInstance.m
new file mode 100644
index 00000000000..1f13676e71f
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeepLinkViewControllerInstance.m
@@ -0,0 +1,12 @@
+//
+// BNCDeepLinkViewControllerInstance.m
+// Branch-SDK
+//
+// Created by Parth Kalavadia on 5/15/17.
+// Copyright © 2017 Parth Kalavadia. All rights reserved.
+//
+
+#import "BNCDeepLinkViewControllerInstance.h"
+
+@implementation BNCDeepLinkViewControllerInstance
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeviceInfo.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeviceInfo.h
new file mode 100644
index 00000000000..15942560f76
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeviceInfo.h
@@ -0,0 +1,50 @@
+//
+// BNCDeviceInfo.h
+// Branch-TestBed
+//
+// Created by Sojan P.R. on 3/22/16.
+// Copyright © 2016 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+@import CoreGraphics;
+#else
+#import
+#import
+#endif
+
+@interface BNCDeviceInfo : NSObject
+
+//---------Properties-------------//
+@property (atomic, copy, readonly) NSString *hardwareId;
+@property (atomic, copy, readonly) NSString *hardwareIdType;
+@property (atomic, readonly) BOOL isRealHardwareId;
+@property (atomic, copy, readonly) NSString *vendorId; //!< VendorId can be nil initially and non-nil later.
+@property (atomic, copy, readonly) NSString *brandName;
+@property (atomic, copy, readonly) NSString *modelName;
+@property (atomic, copy, readonly) NSString *osName;
+@property (atomic, copy, readonly) NSString *osVersion;
+@property (atomic, copy, readonly) NSNumber *screenWidth;
+@property (atomic, copy, readonly) NSNumber *screenHeight;
+@property (atomic, readonly) BOOL isAdTrackingEnabled;
+
+@property (atomic, copy, readonly) NSString *extensionType;
+@property (atomic, copy, readonly) NSString *branchSDKVersion;
+@property (atomic, copy, readonly) NSString *applicationVersion;
+@property (atomic, assign, readonly) CGFloat screenScale;
+@property (atomic, copy, readonly) NSString* adId;
+@property (atomic, assign, readonly) BOOL unidentifiedDevice;
+
+@property (atomic, copy, readonly) NSString* country; //!< The iso2 Country name (us, in,etc).
+@property (atomic, copy, readonly) NSString* language; //!< The iso2 language code (en, ml).
+@property (atomic, copy, readonly) NSString* browserUserAgent; //!< Simple user agent string.
+@property (atomic, copy, readonly) NSString* localIPAddress; //!< The current local IP address.
+@property (atomic, copy, readonly) NSArray *allIPAddresses; //!< All local IP addresses.
+//----------Methods----------------//
++ (BNCDeviceInfo *)getInstance;
++ (NSString*) userAgentString; // Warning: Has an implied lock on main thread on first call.
++ (NSString*) systemBuildVersion;
+
+- (NSDictionary*) v2dictionary;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeviceInfo.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeviceInfo.m
new file mode 100644
index 00000000000..07fb12bb5c2
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCDeviceInfo.m
@@ -0,0 +1,473 @@
+//
+// BNCDeviceInfo.m
+// Branch-TestBed
+//
+// Created by Sojan P.R. on 3/22/16.
+// Copyright © 2016 Branch Metrics. All rights reserved.
+//
+
+#import "BNCDeviceInfo.h"
+#import "BNCPreferenceHelper.h"
+#import "BNCSystemObserver.h"
+#import "BNCAvailability.h"
+#import "BNCLog.h"
+#import "BNCConfig.h"
+#import "BNCPreferenceHelper.h"
+
+#if __has_feature(modules)
+@import UIKit;
+#else
+#import
+#endif
+#import // @import not available in Xcode 7
+#import
+#import
+#import
+#import
+
+// Forward declare this for older versions of iOS
+@interface NSLocale (BranchAvailability)
+- (NSString*) countryCode;
+- (NSString*) languageCode;
+@end
+
+#pragma mark BRNNetworkInfo
+
+typedef NS_ENUM(NSInteger, BNCNetworkAddressType) {
+ BNCNetworkAddressTypeUnknown = 0,
+ BNCNetworkAddressTypeIPv4,
+ BNCNetworkAddressTypeIPv6
+};
+
+@interface BNCNetworkInterface : NSObject
+
++ (NSArray*) currentInterfaces;
+
+@property (nonatomic, strong) NSString *interfaceName;
+@property (nonatomic, assign) BNCNetworkAddressType addressType;
+@property (nonatomic, strong) NSString *address;
+@end
+
+@implementation BNCNetworkInterface
+
++ (NSArray*) currentInterfaces {
+
+ struct ifaddrs *interfaces = NULL;
+ NSMutableArray *currentInterfaces = [NSMutableArray arrayWithCapacity:8];
+
+ // Retrieve the current interfaces - returns 0 on success
+
+ if (getifaddrs(&interfaces) != 0) {
+ int e = errno;
+ BNCLogError(@"Can't read ip address: (%d): %s.", e, strerror(e));
+ goto exit;
+ }
+
+ // Loop through linked list of interfaces --
+
+ struct ifaddrs *interface = NULL;
+ for(interface=interfaces; interface; interface=interface->ifa_next) {
+ // BNCLogDebugSDK(@"Found %s: %x.", interface->ifa_name, interface->ifa_flags);
+
+ // Check the state: IFF_RUNNING, IFF_UP, IFF_LOOPBACK, etc.
+ if ((interface->ifa_flags & IFF_UP) &&
+ (interface->ifa_flags & IFF_RUNNING) &&
+ !(interface->ifa_flags & IFF_LOOPBACK)) {
+ } else {
+ continue;
+ }
+
+ // TODO: Check ifdata too.
+ // struct if_data *ifdata = interface->ifa_data;
+
+ const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
+ if (!addr) continue;
+
+ BNCNetworkAddressType type = BNCNetworkAddressTypeUnknown;
+ char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
+
+ if (addr->sin_family == AF_INET) {
+ if (inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN))
+ type = BNCNetworkAddressTypeIPv4;
+ }
+ else
+ if (addr->sin_family == AF_INET6) {
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
+ if (inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN))
+ type = BNCNetworkAddressTypeIPv6;
+ }
+ else {
+ continue;
+ }
+
+ NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
+ if (name && type != BNCNetworkAddressTypeUnknown) {
+ BNCNetworkInterface *interface = [BNCNetworkInterface new];
+ interface.interfaceName = name;
+ interface.addressType = type;
+ interface.address = [NSString stringWithUTF8String:addrBuf];
+ [currentInterfaces addObject:interface];
+ }
+ }
+
+exit:
+ if (interfaces) freeifaddrs(interfaces);
+ return currentInterfaces;
+}
+
+- (NSString*) description {
+ return [NSString stringWithFormat:@"<%@ %p %@ %@>",
+ NSStringFromClass(self.class),
+ self,
+ self.interfaceName,
+ self.address
+ ];
+}
+
+@end
+
+#pragma mark - BNCDeviceInfo
+
+@interface BNCDeviceInfo()
+@end
+
+
+@implementation BNCDeviceInfo {
+ NSString *_vendorId;
+ NSString *_localIPAddress;
+}
+
++ (BNCDeviceInfo *)getInstance {
+ static BNCDeviceInfo *bnc_deviceInfo = 0;
+ static dispatch_once_t onceToken = 0;
+ dispatch_once(&onceToken, ^{
+ bnc_deviceInfo = [[BNCDeviceInfo alloc] init];
+ });
+ return bnc_deviceInfo;
+}
+
+- (id)init {
+ self = [super init];
+ if (!self) return self;
+
+ BNCPreferenceHelper *preferenceHelper = [BNCPreferenceHelper preferenceHelper];
+ BOOL isRealHardwareId;
+ NSString *hardwareIdType;
+ NSString *hardwareId =
+ [BNCSystemObserver getUniqueHardwareId:&isRealHardwareId
+ isDebug:preferenceHelper.isDebug
+ andType:&hardwareIdType];
+ if (hardwareId) {
+ _hardwareId = hardwareId.copy;
+ _isRealHardwareId = isRealHardwareId;
+ _hardwareIdType = hardwareIdType.copy;
+ }
+
+ _brandName = [BNCSystemObserver getBrand].copy;
+ _modelName = [BNCSystemObserver getModel].copy;
+ _osName = [BNCSystemObserver getOS].copy;
+ _osVersion = [BNCSystemObserver getOSVersion].copy;
+ _screenWidth = [BNCSystemObserver getScreenWidth].copy;
+ _screenHeight = [BNCSystemObserver getScreenHeight].copy;
+ _isAdTrackingEnabled = [BNCSystemObserver adTrackingSafe];
+
+ _country = [BNCDeviceInfo bnc_country].copy;
+ _language = [BNCDeviceInfo bnc_language].copy;
+ _browserUserAgent = [BNCDeviceInfo userAgentString].copy;
+ _extensionType = self.class.extensionType.copy;
+ _branchSDKVersion = [NSString stringWithFormat:@"ios%@", BNC_SDK_VERSION];
+ _applicationVersion = [NSBundle mainBundle].infoDictionary[@"CFBundleShortVersionString"];
+ if (!_applicationVersion.length)
+ _applicationVersion = [NSBundle mainBundle].infoDictionary[@"CFBundleVersionKey"];
+ _screenScale = [UIScreen mainScreen].scale;
+ _adId = [BNCSystemObserver getAdId];
+
+ return self;
+}
+
++ (NSString*_Nonnull) extensionType {
+ NSString *result = @"FULL_APP";
+ NSString *extensionType = [NSBundle mainBundle].infoDictionary[@"NSExtension"][@"NSExtensionPointIdentifier"];
+ if ([extensionType isEqualToString:@"com.apple.identitylookup.message-filter"]) {
+ result = @"IMESSAGE_APP";
+ }
+ return result;
+}
+
+- (NSString *)vendorId {
+ @synchronized (self) {
+ if (_vendorId) return _vendorId;
+ /*
+ * https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor
+ * BNCSystemObserver.getVendorId is based on UIDevice.identifierForVendor. Note from the
+ * docs above:
+ *
+ * If the value is nil, wait and get the value again later. This happens, for example,
+ * after the device has been restarted but before the user has unlocked the device.
+ *
+ * It's not clear if that specific example scenario would apply to opening Branch links,
+ * but this lazy initialization is probably safer.
+ */
+ _vendorId = [BNCSystemObserver getVendorId].copy;
+ return _vendorId;
+ }
+}
+
+- (BOOL) unidentifiedDevice {
+ return (self.vendorId == nil && self.adId == nil);
+}
+
+- (NSString*) localIPAddress { // For 'local_ip' server field.
+ @synchronized (self) {
+ NSArray*interfaces = [BNCNetworkInterface currentInterfaces];
+ for (BNCNetworkInterface *interface in interfaces) {
+ if (interface.addressType == BNCNetworkAddressTypeIPv4)
+ return interface.address;
+ }
+ return nil;
+ }
+}
+
+- (NSArray*) allIPAddresses {
+ NSMutableArray *array = [NSMutableArray new];
+ for (BNCNetworkInterface *inf in [BNCNetworkInterface currentInterfaces]) {
+ [array addObject:inf.description];
+ }
+ return array;
+}
+
++ (NSString*) bnc_country {
+
+ NSString *country = nil;
+ #define returnIfValidCountry() \
+ if ([country isKindOfClass:[NSString class]] && country.length) { \
+ return country; \
+ } else { \
+ country = nil; \
+ }
+
+ // Should work on iOS 10
+ NSLocale *currentLocale = [NSLocale currentLocale];
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wpartial-availability"
+ if ([currentLocale respondsToSelector:@selector(countryCode)]) {
+ country = [currentLocale countryCode];
+ }
+ #pragma clang diagnostic pop
+ returnIfValidCountry();
+
+ // Should work on iOS 9
+ NSString *rawLanguage = [[NSLocale preferredLanguages] firstObject];
+ NSDictionary *languageDictionary = [NSLocale componentsFromLocaleIdentifier:rawLanguage];
+ country = [languageDictionary objectForKey:@"kCFLocaleCountryCodeKey"];
+ returnIfValidCountry();
+
+ // Should work on iOS 8 and below.
+ //NSString* language = [[NSLocale preferredLanguages] firstObject];
+ NSString *rawLocale = currentLocale.localeIdentifier;
+ NSRange range = [rawLocale rangeOfString:@"_"];
+ if (range.location != NSNotFound) {
+ range = NSMakeRange(range.location+1, rawLocale.length-range.location-1);
+ country = [rawLocale substringWithRange:range];
+ }
+ returnIfValidCountry();
+
+ #undef returnIfValidCountry
+
+ return nil;
+}
+
++ (NSString*) bnc_language {
+
+ NSString *language = nil;
+ #define returnIfValidLanguage() \
+ if ([language isKindOfClass:[NSString class]] && language.length) { \
+ return language; \
+ } else { \
+ language = nil; \
+ } \
+
+ // Should work on iOS 10
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wpartial-availability"
+ NSLocale *currentLocale = [NSLocale currentLocale];
+ if ([currentLocale respondsToSelector:@selector(languageCode)]) {
+ language = [currentLocale languageCode];
+ }
+ #pragma clang diagnostic pop
+ returnIfValidLanguage();
+
+ // Should work on iOS 9
+ NSString *rawLanguage = [[NSLocale preferredLanguages] firstObject];
+ NSDictionary *languageDictionary = [NSLocale componentsFromLocaleIdentifier:rawLanguage];
+ language = [languageDictionary objectForKey:@"kCFLocaleLanguageCodeKey"];
+ returnIfValidLanguage();
+
+ // Should work on iOS 8 and below.
+ language = [[NSLocale preferredLanguages] firstObject];
+ returnIfValidLanguage();
+
+ #undef returnIfValidLanguage
+
+ return nil;
+}
+
++ (NSString*) systemBuildVersion {
+ int mib[2] = { CTL_KERN, KERN_OSVERSION };
+ u_int namelen = sizeof(mib) / sizeof(mib[0]);
+
+ // Get the size for the buffer --
+
+ size_t bufferSize = 0;
+ sysctl(mib, namelen, NULL, &bufferSize, NULL, 0);
+ if (bufferSize <= 0) return nil;
+
+ u_char buildBuffer[bufferSize];
+ int result = sysctl(mib, namelen, buildBuffer, &bufferSize, NULL, 0);
+
+ NSString *version = nil;
+ if (result >= 0) {
+ version = [[NSString alloc]
+ initWithBytes:buildBuffer
+ length:bufferSize-1
+ encoding:NSUTF8StringEncoding];
+ }
+ return version;
+}
+
++ (NSString*) userAgentString {
+
+ static NSString* brn_browserUserAgentString = nil;
+
+ void (^setBrowserUserAgent)(void) = ^() {
+ @synchronized (self) {
+ if (!brn_browserUserAgentString) {
+ brn_browserUserAgentString =
+ [[[UIWebView alloc]
+ initWithFrame:CGRectZero]
+ stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
+ BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
+ preferences.browserUserAgentString = brn_browserUserAgentString;
+ preferences.lastSystemBuildVersion = self.systemBuildVersion;
+ BNCLogDebugSDK(@"userAgentString: '%@'.", brn_browserUserAgentString);
+ }
+ }
+ };
+
+ NSString* (^browserUserAgent)(void) = ^ NSString* () {
+ @synchronized (self) {
+ return brn_browserUserAgentString;
+ }
+ };
+
+ @synchronized (self) {
+ // We only get the string once per app run:
+
+ if (brn_browserUserAgentString)
+ return brn_browserUserAgentString;
+
+ // Did we cache it?
+
+ BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
+ if (preferences.browserUserAgentString &&
+ preferences.lastSystemBuildVersion &&
+ [preferences.lastSystemBuildVersion isEqualToString:self.systemBuildVersion]) {
+ brn_browserUserAgentString = [preferences.browserUserAgentString copy];
+ return brn_browserUserAgentString;
+ }
+
+ // Make sure this executes on the main thread.
+ // Uses an implied lock through dispatch_queues: This can deadlock if mis-used!
+
+ if (NSThread.isMainThread) {
+ setBrowserUserAgent();
+ return brn_browserUserAgentString;
+ }
+
+ }
+
+ // Different case for iOS 7.0:
+ if ([UIDevice currentDevice].systemVersion.doubleValue < 8.0) {
+ BNCLogDebugSDK(@"Getting iOS 7 UserAgent.");
+ dispatch_sync(dispatch_get_main_queue(), ^ {
+ setBrowserUserAgent();
+ });
+ BNCLogDebugSDK(@"Got iOS 7 UserAgent.");
+ return browserUserAgent();
+ }
+
+ // Wait and yield to prevent deadlock:
+ int retries = 10;
+ int64_t timeoutDelta = (dispatch_time_t)((long double)NSEC_PER_SEC * (long double)0.100);
+ while (!browserUserAgent() && retries > 0) {
+
+ dispatch_block_t agentBlock = dispatch_block_create_with_qos_class(
+ DISPATCH_BLOCK_DETACHED | DISPATCH_BLOCK_ENFORCE_QOS_CLASS,
+ QOS_CLASS_USER_INTERACTIVE,
+ 0, ^ {
+ BNCLogDebugSDK(@"Will set userAgent.");
+ setBrowserUserAgent();
+ BNCLogDebugSDK(@"Did set userAgent.");
+ });
+ dispatch_async(dispatch_get_main_queue(), agentBlock);
+
+ dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, timeoutDelta);
+ dispatch_block_wait(agentBlock, timeoutTime);
+ retries--;
+ }
+ BNCLogDebugSDK(@"Retries: %d", 10-retries);
+
+ return browserUserAgent();
+}
+
+- (NSDictionary*) v2dictionary {
+ NSMutableDictionary *dictionary = [NSMutableDictionary new];
+
+ #define BNCFieldDefinesDictionaryFromSelf
+ #include "BNCFieldDefines.h"
+
+ addString(osName, os);
+ addString(osVersion, os_version);
+ addString(extensionType, environment);
+ addString(vendorId, idfv);
+ if ([BNCPreferenceHelper preferenceHelper].isDebug) {
+ dictionary[@"unidentified_device"] = (__bridge NSNumber*) kCFBooleanTrue;
+ } else {
+ addString(adId, idfa);
+ }
+ addString(browserUserAgent, user_agent);
+ addString(country, country);
+ addString(language, language);
+ addString(brandName, brand);
+ addString(applicationVersion, app_version);
+ addString(modelName, model);
+ addDouble(screenScale, screen_dpi);
+ addNumber(screenHeight, screen_height);
+ addNumber(screenWidth, screen_width);
+ addBoolean(unidentifiedDevice, unidentified_device);
+ addString(localIPAddress, local_ip);
+
+ #include "BNCFieldDefines.h"
+
+ if (!self.isAdTrackingEnabled)
+ dictionary[@"limit_ad_tracking"] = (__bridge NSNumber*) kCFBooleanTrue;
+
+ NSString *s = nil;
+ BNCPreferenceHelper *preferences = [BNCPreferenceHelper preferenceHelper];
+
+ s = preferences.userIdentity;
+ if (s.length) dictionary[@"developer_identity"] = s;
+
+ s = preferences.deviceFingerprintID;
+ if (s.length) dictionary[@"device_fingerprint_id"] = s;
+
+ if (preferences.limitFacebookTracking)
+ dictionary[@"limit_facebook_tracking"] = (__bridge NSNumber*) kCFBooleanTrue;
+
+ dictionary[@"sdk"] = @"ios";
+ dictionary[@"sdk_version"] = BNC_SDK_VERSION;
+
+ return dictionary;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCEncodingUtils.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCEncodingUtils.h
new file mode 100644
index 00000000000..34ecf5b5e8a
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCEncodingUtils.h
@@ -0,0 +1,67 @@
+//
+// BNCEncodingUtils.h
+// Branch
+//
+// Created by Graham Mueller on 3/31/15.
+// Copyright (c) 2015 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+#pragma mark BNCWireFormat
+
+extern NSDate* BNCDateFromWireFormat(id object);
+extern NSNumber* BNCWireFormatFromDate(NSDate *date);
+extern NSNumber* BNCWireFormatFromBool(BOOL b);
+
+extern NSString* BNCStringFromWireFormat(id object);
+extern NSString* BNCWireFormatFromString(NSString *string);
+
+#pragma mark - BNCKeyValue
+
+@interface BNCKeyValue : NSObject
+
++ (BNCKeyValue*) key:(NSString*)key value:(NSString*)value;
+- (NSString*) description;
+
+@property (nonatomic, strong) NSString* key;
+@property (nonatomic, strong) NSString* value;
+
+@end
+
+#pragma mark - BNCEncodingUtils
+
+@interface BNCEncodingUtils : NSObject
+
++ (NSString *)base64EncodeStringToString:(NSString *)strData;
++ (NSString *)base64DecodeStringToString:(NSString *)strData;
++ (NSString *)base64EncodeData:(NSData *)objData;
++ (NSData *)base64DecodeString:(NSString *)strBase64;
+
++ (NSString *)md5Encode:(NSString *)input;
+
++ (NSString *)urlEncodedString:(NSString *)string;
++ (NSString *)encodeArrayToJsonString:(NSArray *)dictionary;
++ (NSString *)encodeDictionaryToJsonString:(NSDictionary *)dictionary;
++ (NSData *)encodeDictionaryToJsonData:(NSDictionary *)dictionary;
+
++ (NSString*) stringByPercentDecodingString:(NSString*)string;
++ (NSString*) stringByPercentEncodingStringForQuery:(NSString *)string;
+
++ (NSString *)sanitizedStringFromString:(NSString *)dirtyString;
++ (NSDictionary *)decodeJsonDataToDictionary:(NSData *)jsonData;
++ (NSDictionary *)decodeJsonStringToDictionary:(NSString *)jsonString;
+
++ (NSDictionary *)decodeQueryStringToDictionary:(NSString *)queryString;
++ (NSString *)encodeDictionaryToQueryString:(NSDictionary *)dictionary;
+
++ (NSString *) hexStringFromData:(NSData*)data;
++ (NSData *) dataFromHexString:(NSString*)string;
+
++ (NSArray*) queryItems:(NSURL*)URL;
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCEncodingUtils.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCEncodingUtils.m
new file mode 100644
index 00000000000..98fd978b378
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCEncodingUtils.m
@@ -0,0 +1,522 @@
+//
+// BNCEncodingUtils.m
+// Branch
+//
+// Created by Graham Mueller on 3/31/15.
+// Copyright (c) 2015 Branch Metrics. All rights reserved.
+//
+
+#import "BNCEncodingUtils.h"
+#import "BNCPreferenceHelper.h"
+#import "BNCLog.h"
+#import
+
+#pragma mark BNCWireFormat
+
+NSDate* BNCDateFromWireFormat(id object) {
+ NSDate *date = nil;
+ if ([object respondsToSelector:@selector(doubleValue)]) {
+ NSTimeInterval t = [object doubleValue];
+ date = [NSDate dateWithTimeIntervalSince1970:t/1000.0];
+ }
+ return date;
+}
+
+NSNumber* BNCWireFormatFromDate(NSDate *date) {
+ NSNumber *number = nil;
+ NSTimeInterval t = [date timeIntervalSince1970];
+ if (date && t != 0.0 ) {
+ number = [NSNumber numberWithLongLong:(long long)(t*1000.0)];
+ }
+ return number;
+}
+
+NSNumber* BNCWireFormatFromBool(BOOL b) {
+ return (b) ? (__bridge NSNumber*) kCFBooleanTrue : nil;
+}
+
+NSString* BNCStringFromWireFormat(id object) {
+ if ([object isKindOfClass:NSString.class])
+ return object;
+ else
+ if ([object respondsToSelector:@selector(stringValue)])
+ return [object stringValue];
+ else
+ if ([object respondsToSelector:@selector(description)])
+ return [object description];
+ return nil;
+}
+
+NSString* BNCWireFormatFromString(NSString *string) {
+ return string;
+}
+
+#pragma mark - BNCKeyValue
+
+@implementation BNCKeyValue
+
++ (BNCKeyValue*) key:(NSString*)key value:(NSString*)value {
+ BNCKeyValue *kv = [[BNCKeyValue alloc] init];
+ kv.key = key;
+ kv.value = value;
+ return kv;
+}
+
+- (NSString*) description {
+ return [NSString stringWithFormat:@"<%@, %@>", self.key, self.value];
+}
+
+- (BOOL) isEqual:(id)rawObject {
+ BNCKeyValue *object = rawObject;
+ return
+ [object isKindOfClass:[BNCKeyValue class]] &&
+ [self.key isEqualToString:object.key] &&
+ [self.value isEqualToString:object.value]
+ ;
+}
+
+@end
+
+#pragma mark - BNCEncodingUtils
+
+@implementation BNCEncodingUtils
+
+#pragma mark - Base 64 Encoding
+
++ (NSString *)base64EncodeStringToString:(NSString *)strData {
+ return [self base64EncodeData:[strData dataUsingEncoding:NSUTF8StringEncoding]];
+}
+
++ (NSString *)base64DecodeStringToString:(NSString *)strData {
+ NSData* data =[BNCEncodingUtils base64DecodeString:strData];
+ if (data == nil) {
+ return nil;
+ }
+ return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+}
+
++ (NSString *)base64EncodeData:(NSData *)data {
+ if (data) return [data base64EncodedStringWithOptions:0];
+ return @"";
+}
+
++ (NSData *)base64DecodeString:(NSString *)strBase64 {
+ if (strBase64) return [[NSData alloc] initWithBase64EncodedString:strBase64 options:0];
+ return nil;
+}
+
+#pragma mark - MD5 methods
+
++ (NSString *)md5Encode:(NSString *)input {
+ if (!input) {
+ return @"";
+ }
+
+ const char *cStr = [input UTF8String];
+ unsigned char digest[CC_MD5_DIGEST_LENGTH];
+ CC_MD5(cStr, (CC_LONG)strlen(cStr), digest);
+
+ NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
+
+ for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
+ [output appendFormat:@"%02x", digest[i]];
+ }
+
+ return output;
+}
+
+
+#pragma mark - Param Encoding methods
+
++ (NSString *)iso8601StringFromDate:(NSDate *)date {
+ static NSDateFormatter *dateFormatter;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ dateFormatter = [[NSDateFormatter alloc] init];
+ [dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; // POSIX to avoid weird issues
+ [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
+ });
+
+ return [dateFormatter stringFromDate:date];
+}
+
++ (NSString *)sanitizedStringFromString:(NSString *)dirtyString {
+ NSString *dirtyCopy = [dirtyString copy]; // dirtyString seems to get dealloc'ed sometimes. Make a copy.
+ NSString *cleanString = [[[[[[[[dirtyCopy
+ stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]
+ stringByReplacingOccurrencesOfString:@"\b" withString:@"\\b"]
+ stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"]
+ stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]
+ stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"]
+ stringByReplacingOccurrencesOfString:@"\t" withString:@"\\t"]
+ stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]
+ stringByReplacingOccurrencesOfString:@"`" withString:@"'"];
+ return cleanString;
+}
+
++ (NSData *)encodeDictionaryToJsonData:(NSDictionary *)dictionary {
+ NSString *jsonString = [BNCEncodingUtils encodeDictionaryToJsonString:dictionary];
+ NSUInteger length = [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+ return [NSData dataWithBytes:[jsonString UTF8String] length:length];
+}
+
++ (NSString *)encodeDictionaryToJsonString:(NSDictionary *)dictionary {
+ NSMutableString *encodedDictionary = [[NSMutableString alloc] initWithString:@"{"];
+ for (NSString *key in dictionary) {
+ NSString *value = nil;
+ BOOL string = YES;
+
+ id obj = dictionary[key];
+ if ([obj isKindOfClass:[NSString class]]) {
+ value = [BNCEncodingUtils sanitizedStringFromString:obj];
+ }
+ else if ([obj isKindOfClass:[NSURL class]]) {
+ value = [obj absoluteString];
+ }
+ else if ([obj isKindOfClass:[NSDate class]]) {
+ value = [BNCEncodingUtils iso8601StringFromDate:obj];
+ }
+ else if ([obj isKindOfClass:[NSArray class]]) {
+ value = [BNCEncodingUtils encodeArrayToJsonString:obj];
+ string = NO;
+ }
+ else if ([obj isKindOfClass:[NSDictionary class]] || [obj isKindOfClass:[NSMutableDictionary class]]) {
+ value = [BNCEncodingUtils encodeDictionaryToJsonString:obj];
+ string = NO;
+ }
+ else if ([obj isKindOfClass:[NSNumber class]]) {
+ string = NO;
+ if (obj == (id)kCFBooleanFalse)
+ value = @"false";
+ else
+ if (obj == (id)kCFBooleanTrue)
+ value = @"true";
+ else
+ value = [obj stringValue];
+ }
+ else if ([obj isKindOfClass:[NSNull class]]) {
+ value = @"null";
+ string = NO;
+ }
+ else {
+ // If this type is not a known type, don't attempt to encode it.
+ BNCLogError(@"Cannot encode value for key %@. The value is not an accepted type.", key);
+ continue;
+ }
+
+ [encodedDictionary appendFormat:@"\"%@\":", [BNCEncodingUtils sanitizedStringFromString:key]];
+
+ // If this is a "string" object, wrap it in quotes
+ if (string) {
+ [encodedDictionary appendFormat:@"\"%@\",", value];
+ }
+ // Otherwise, just add the raw value after the colon
+ else {
+ [encodedDictionary appendFormat:@"%@,", value];
+ }
+ }
+
+ if (encodedDictionary.length > 1) {
+ [encodedDictionary deleteCharactersInRange:NSMakeRange([encodedDictionary length] - 1, 1)];
+ }
+
+ [encodedDictionary appendString:@"}"];
+
+ BNCLogDebugSDK(@"Encoded dictionary: %@.", encodedDictionary);
+ return encodedDictionary;
+}
+
++ (NSString *)encodeArrayToJsonString:(NSArray *)array {
+ // Empty array
+ if (![array count]) {
+ return @"[]";
+ }
+
+ NSMutableString *encodedArray = [[NSMutableString alloc] initWithString:@"["];
+ for (id obj in array) {
+ NSString *value = nil;
+ BOOL string = YES;
+
+ if ([obj isKindOfClass:[NSString class]]) {
+ value = [BNCEncodingUtils sanitizedStringFromString:obj];
+ }
+ else if ([obj isKindOfClass:[NSURL class]]) {
+ value = [obj absoluteString];
+ }
+ else if ([obj isKindOfClass:[NSDate class]]) {
+ value = [BNCEncodingUtils iso8601StringFromDate:obj];
+ }
+ else if ([obj isKindOfClass:[NSArray class]]) {
+ value = [BNCEncodingUtils encodeArrayToJsonString:obj];
+ string = NO;
+ }
+ else if ([obj isKindOfClass:[NSDictionary class]] || [obj isKindOfClass:[NSMutableDictionary class]]) {
+ value = [BNCEncodingUtils encodeDictionaryToJsonString:obj];
+ string = NO;
+ }
+ else if ([obj isKindOfClass:[NSNumber class]]) {
+ value = [obj stringValue];
+ string = NO;
+ }
+ else if ([obj isKindOfClass:[NSNull class]]) {
+ value = @"null";
+ string = NO;
+ }
+ else {
+ // If this type is not a known type, don't attempt to encode it.
+ BNCLogError(@"Cannot encode value %@. The value is not an accepted type.", obj);
+ continue;
+ }
+
+ // If this is a "string" object, wrap it in quotes
+ if (string) {
+ [encodedArray appendFormat:@"\"%@\",", value];
+ }
+ // Otherwise, just add the raw value after the colon
+ else {
+ [encodedArray appendFormat:@"%@,", value];
+ }
+ }
+
+ // Delete the trailing comma
+ [encodedArray deleteCharactersInRange:NSMakeRange([encodedArray length] - 1, 1)];
+ [encodedArray appendString:@"]"];
+
+ BNCLogDebugSDK(@"Encoded array: %@.", encodedArray);
+ return encodedArray;
+}
+
++ (NSString *)urlEncodedString:(NSString *)string {
+ NSMutableCharacterSet *charSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
+ [charSet removeCharactersInString:@"!*'\"();:@&=+$,/?%#[]% "];
+ return [string stringByAddingPercentEncodingWithAllowedCharacters:charSet];
+}
+
++ (NSString *)encodeDictionaryToQueryString:(NSDictionary *)dictionary {
+ NSMutableString *queryString = [[NSMutableString alloc] initWithString:@"?"];
+
+ for (NSString *key in [dictionary allKeys]) {
+ // No empty keys, please.
+ if (key.length) {
+ id obj = dictionary[key];
+ NSString *value;
+
+ if ([obj isKindOfClass:[NSString class]]) {
+ value = [BNCEncodingUtils urlEncodedString:obj];
+ }
+ else if ([obj isKindOfClass:[NSURL class]]) {
+ value = [BNCEncodingUtils urlEncodedString:[obj absoluteString]];
+ }
+ else if ([obj isKindOfClass:[NSDate class]]) {
+ value = [BNCEncodingUtils iso8601StringFromDate:obj];
+ }
+ else if ([obj isKindOfClass:[NSNumber class]]) {
+ value = [obj stringValue];
+ }
+ else {
+ // If this type is not a known type, don't attempt to encode it.
+ BNCLogError(@"Cannot encode value %@. The value is not an accepted type.", obj);
+ continue;
+ }
+
+ [queryString appendFormat:@"%@=%@&", [BNCEncodingUtils urlEncodedString:key], value];
+ }
+ }
+
+ // Delete last character (either trailing & or ? if no params present)
+ [queryString deleteCharactersInRange:NSMakeRange(queryString.length - 1, 1)];
+
+ return queryString;
+}
+
++ (NSString*) stringByPercentDecodingString:(NSString *)string {
+ return [string stringByRemovingPercentEncoding];
+}
+
++ (NSString*) stringByPercentEncodingStringForQuery:(NSString *)string {
+ return [string stringByAddingPercentEncodingWithAllowedCharacters:
+ [NSCharacterSet URLQueryAllowedCharacterSet]];
+}
+
+#pragma mark - Param Decoding Methods
+
++ (NSDictionary *)decodeJsonDataToDictionary:(NSData *)jsonData {
+ NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+
+ return [BNCEncodingUtils decodeJsonStringToDictionary:jsonString];
+}
+
++ (NSDictionary *)decodeJsonStringToDictionary:(NSString *)jsonString {
+ // Just a basic decode, easy enough
+ NSData *tempData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
+ if (!tempData) {
+ return @{};
+ }
+
+ NSDictionary *plainDecodedDictionary = [NSJSONSerialization JSONObjectWithData:tempData options:NSJSONReadingMutableContainers error:nil];
+ if (plainDecodedDictionary) {
+ return plainDecodedDictionary;
+ }
+
+ // If the first decode failed, it could be because the data was encoded. Try decoding first.
+ NSString *decodedVersion = [BNCEncodingUtils base64DecodeStringToString:jsonString];
+ tempData = [decodedVersion dataUsingEncoding:NSUTF8StringEncoding];
+ if (!tempData) {
+ return @{};
+ }
+
+ NSDictionary *base64DecodedDictionary = [NSJSONSerialization JSONObjectWithData:tempData options:NSJSONReadingMutableContainers error:nil];
+ if (base64DecodedDictionary) {
+ return base64DecodedDictionary;
+ }
+
+ // Apparently this data was not parsible into a dictionary, so we'll just return an empty one
+ return @{};
+}
+
++ (NSDictionary *)decodeQueryStringToDictionary:(NSString *)queryString {
+ NSArray *pairs = [queryString componentsSeparatedByString:@"&"];
+ NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
+
+ for (NSString *pair in pairs) {
+ NSArray *kv = [pair componentsSeparatedByString:@"="];
+ if (kv.count > 1) { // If this key has a value (so, not foo&bar=...)
+ NSString *key = kv[0];
+ NSString *val;
+
+ //Pre iOS 7, stringByReplacingPercentEscapesUsingEncoding was deprecated in iOS 9
+ if (NSFoundationVersionNumber < NSFoundationVersionNumber_iOS_7_0) {
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ val = [kv[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ #pragma clang diagnostic pop
+
+ } else { //iOS 7 and later
+ val = [kv[1] stringByRemovingPercentEncoding]; // uses the default UTF-8 encoding, introduced in iOS 7
+ }
+
+ // Don't add empty items
+ if (val.length) {
+ params[key] = val;
+ }
+ }
+ }
+
+ return params;
+}
+
+#pragma mark - Hex Strings
+
++ (NSString *) hexStringFromData:(NSData*)data {
+
+ NSUInteger bytesCount = data.length;
+ if (bytesCount <= 0) return @"";
+
+ const char *hexChars = "0123456789ABCDEF";
+ const char *dataBuffer = data.bytes;
+ char *chars = malloc(sizeof(char) * (bytesCount * 2 + 1));
+ if (!chars) return @"";
+ char *s = chars;
+ for (unsigned i = 0; i < bytesCount; ++i) {
+ *s++ = hexChars[((*dataBuffer & 0xF0) >> 4)];
+ *s++ = hexChars[(*dataBuffer & 0x0F)];
+ dataBuffer++;
+ }
+ *s = '\0';
+
+ NSString *hexString = [NSString stringWithUTF8String:chars];
+ if (chars) free(chars);
+ return hexString;
+}
+
++ (NSData *) dataFromHexString:(NSString*)string {
+ if (!string) return nil;
+
+ NSData *data = nil;
+ NSData *inputData = [string dataUsingEncoding:NSUTF8StringEncoding];
+
+ size_t length = (inputData.length+1)/2;
+ uint8_t *bytes = malloc(length);
+ uint8_t *b = bytes;
+ if (!bytes) goto exit;
+
+ int highValue = -1;
+ const uint8_t *p = (const uint8_t*) [inputData bytes];
+ for (NSUInteger i = 0; i < inputData.length; ++i) {
+ int value;
+ if (*p >= '0' && *p <= '9')
+ value = *p - '0';
+ else
+ if (*p >= 'A' && *p <= 'F')
+ value = *p - 'A' + 10;
+ else
+ if (*p >= 'a' && *p <= 'f')
+ value = *p - 'a' + 10;
+ else
+ if (isspace(*p)) {
+ p++;
+ continue;
+ } else {
+ goto exit; // Invalid character.
+ }
+
+ if (highValue == -1) {
+ highValue = value;
+ } else {
+ *b++ = (highValue << 4) | value;
+ highValue = -1;
+ }
+ p++;
+ }
+
+ // If highValue != -1 then we got an odd number of hex values, which is an error.
+ if (highValue == -1)
+ data = [NSData dataWithBytes:bytes length:b-bytes];
+
+exit:
+ if (bytes) {
+ BNCLogAssert((size_t)(b-bytes)<=length);
+ free(bytes);
+ }
+ return data;
+}
+
+#pragma mark - URL QueryItems
+
++ (NSArray*) queryItems:(NSURL*)URL {
+ NSMutableArray* keyValues = [NSMutableArray new];
+ if (!URL) return keyValues;
+
+ NSArray *queryItems = [[URL query] componentsSeparatedByString:@"&"];
+ for (NSString* itemPair in queryItems) {
+
+ BNCKeyValue *keyValue = [BNCKeyValue new];
+ NSRange range = [itemPair rangeOfString:@"="];
+ if (range.location == NSNotFound) {
+ if (itemPair.length)
+ keyValue.key = itemPair;
+ } else {
+ keyValue.key = [itemPair substringWithRange:NSMakeRange(0, range.location)];
+ NSRange r = NSMakeRange(range.location+1, itemPair.length-range.location-1);
+ if (r.length > 0)
+ keyValue.value = [itemPair substringWithRange:r];
+ }
+
+ keyValue.key = [BNCEncodingUtils stringByPercentDecodingString:keyValue.key];
+ keyValue.key = [keyValue.key stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ keyValue.value = [BNCEncodingUtils stringByPercentDecodingString:keyValue.value];
+ keyValue.value = [keyValue.value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
+
+ if (keyValue.key.length || keyValue.value.length) {
+ if (keyValue.key == nil) keyValue.key = @"";
+ if (keyValue.value == nil) keyValue.value = @"";
+ [keyValues addObject:keyValue];
+ }
+ }
+
+ return keyValues;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCError.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCError.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCError.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCError.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCError.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCError.m
new file mode 100644
index 00000000000..703f1302f75
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCError.m
@@ -0,0 +1,126 @@
+/**
+ @file BNCError.m
+ @package Branch-SDK
+ @brief Branch errors.
+
+ @author Qinwei Gong
+ @date November 2014
+ @copyright Copyright © 2014 Branch. All rights reserved.
+*/
+
+#import "BNCError.h"
+#import "BNCLocalization.h"
+
+NSString * const BNCErrorDomain = @"io.branch.sdk.error";
+
+__attribute__((constructor)) void BNCForceNSErrorCategoryToLoad() {
+ // Nothing here, but forces linker to load the category.
+}
+
+@implementation NSError (Branch)
+
++ (NSString*) messageForCode:(BNCErrorCode)code {
+
+ // The order is important!
+
+ static NSString* const messages[] = {
+
+ // BNCInitError
+ @"The Branch user session has not been initialized.",
+
+ // BNCDuplicateResourceError
+ @"A resource with this identifier already exists.",
+
+ // BNCRedeemCreditsError
+ @"You're trying to redeem more credits than are available. Have you loaded rewards?",
+
+ // BNCBadRequestError
+ @"The network request was invalid.",
+
+ // BNCServerProblemError
+ @"Trouble reaching the Branch servers, please try again shortly.",
+
+ // BNCNilLogError
+ @"Can't log error messages because the logger is set to nil.",
+
+ // BNCVersionError
+ @"Incompatible version.",
+
+ // BNCNetworkServiceInterfaceError
+ @"The underlying network service does not conform to the BNCNetworkOperationProtocol.",
+
+ // BNCInvalidNetworkPublicKeyError
+ @"Public key is not an SecKeyRef type.",
+
+ // BNCContentIdentifierError
+ @"A canonical identifier or title are required to uniquely identify content.",
+
+ // BNCSpotlightNotAvailableError
+ @"The Core Spotlight indexing service is not available on this device.",
+
+ // BNCSpotlightTitleError
+ @"Spotlight indexing requires a title.",
+
+ // BNCRedeemZeroCreditsError
+ @"Can't redeem zero credits.",
+
+ // BNCSpotlightIdentifierError
+ @"The Spotlight identifier is required to remove indexing from spotlight.",
+
+ //BNCSpotlightPublicIndexError
+ @"Spotlight cannot remove publicly indexed content.",
+
+ //BNCTrackingDisabledError
+ @"User tracking is disabled."
+ };
+
+ #define _countof(array) (sizeof(array)/sizeof(array[0]))
+
+ // Sanity check
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wunreachable-code"
+ if (_countof(messages) != (BNCHighestError - BNCInitError)) {
+ [NSException raise:NSInternalInconsistencyException format:@"Branch error message count is wrong."];
+ return @"Branch encountered an error.";
+ }
+ #pragma clang diagnostic pop
+
+ if (code < BNCInitError || code >= BNCHighestError)
+ return @"Branch encountered an error.";
+
+ return messages[code - BNCInitError];
+}
+
++ (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode
+ error:(NSError*)error
+ localizedMessage:(NSString*_Nullable)message {
+
+ NSMutableDictionary *userInfo = [NSMutableDictionary new];
+
+ NSString *localizedString = BNCLocalizedString([self messageForCode:errorCode]);
+ if (localizedString) userInfo[NSLocalizedDescriptionKey] = localizedString;
+ if (message) {
+ userInfo[NSLocalizedFailureReasonErrorKey] = message;
+ }
+ if (error) {
+ userInfo[NSUnderlyingErrorKey] = error;
+ if (!userInfo[NSLocalizedFailureReasonErrorKey] && error.localizedDescription)
+ userInfo[NSLocalizedFailureReasonErrorKey] = error.localizedDescription;
+ }
+
+ return [NSError errorWithDomain:BNCErrorDomain code:errorCode userInfo:userInfo];
+}
+
++ (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode {
+ return [NSError branchErrorWithCode:errorCode error:nil localizedMessage:nil];
+}
+
++ (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode error:(NSError*_Nullable)error {
+ return [NSError branchErrorWithCode:errorCode error:error localizedMessage:nil];
+}
+
++ (NSError*_Nonnull) branchErrorWithCode:(BNCErrorCode)errorCode localizedMessage:(NSString*_Nullable)message {
+ return [NSError branchErrorWithCode:errorCode error:nil localizedMessage:message];
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFabricAnswers.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFabricAnswers.h
new file mode 100644
index 00000000000..c1e88e3305f
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFabricAnswers.h
@@ -0,0 +1,20 @@
+//
+// BNCFabricAnswers.h
+// Branch-TestBed
+//
+// Created by Ahmed Nawar on 6/2/16.
+// Copyright © 2016 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+@interface BNCFabricAnswers : NSObject
+
++ (NSDictionary*) branchConfigurationDictionary;
++ (void)sendEventWithName:(NSString*)name andAttributes:(NSDictionary*)attributes;
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFabricAnswers.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFabricAnswers.m
new file mode 100644
index 00000000000..63076828535
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFabricAnswers.m
@@ -0,0 +1,71 @@
+//
+// BNCFabricAnswers.m
+// Branch-TestBed
+//
+// Created by Ahmed Nawar on 6/2/16.
+// Copyright © 2016 Branch Metrics. All rights reserved.
+//
+
+#import "Branch.h"
+#import "BNCFabricAnswers.h"
+#import "BNCPreferenceHelper.h"
+#import "../Fabric/Answers.h"
+#import "../Fabric/Fabric+FABKits.h"
+
+@implementation BNCFabricAnswers
+
++ (void)sendEventWithName:(NSString *)name andAttributes:(NSDictionary *)attributes {
+ ANSLogCustomEvent(name, [self prepareBranchDataForAnswers:attributes]);
+}
+
++ (NSDictionary *)prepareBranchDataForAnswers:(NSDictionary *)dictionary {
+ NSMutableDictionary *temp = [[NSMutableDictionary alloc] init];
+
+ for (NSString *key in dictionary.allKeys) {
+ if ([key hasPrefix:@"+"] || ([key hasPrefix:@"$"] && ![key isEqualToString:@"$identity_id"])) {
+ // ignore because this data is not found on the ShareSheet
+ continue;
+ } else if ([dictionary[key] isKindOfClass:[NSArray class]]) {
+ // flatten arrays, special treatement for ~tags
+ NSString *aKey;
+ if ([key hasPrefix:@"~"])
+ aKey = [key substringFromIndex:1];
+ else
+ aKey = key;
+ NSArray *valuesArray = dictionary[key];
+ for (NSUInteger i = 0; i < valuesArray.count; ++i) {
+ temp[[NSString stringWithFormat:@"%@.%lu", aKey, (unsigned long)i]] = valuesArray[i];
+ }
+ } else if ([key hasPrefix:@"~"]) {
+ // strip tildes ~
+ temp[[key substringFromIndex:1]] = dictionary[key];
+ } else if ([key isEqualToString:@"$identity_id"]) {
+ temp[@"referring_branch_identity"] = dictionary[key];
+ }
+ }
+
+ [[BNCPreferenceHelper preferenceHelper] synchronize];
+ NSString *identity = [[BNCPreferenceHelper preferenceHelper].identityID copy];
+ if (identity) {
+ temp[@"branch_identity"] = identity;
+ }
+
+ return temp;
+}
+
++ (NSDictionary*) branchConfigurationDictionary {
+ Class fabric = NSClassFromString(@"Fabric");
+ if ([fabric respondsToSelector:@selector(configurationDictionaryForKitClass:)]) {
+
+ // The name of this key was specified in the account-creation API integration
+ NSString * const BNC_BRANCH_FABRIC_APP_KEY_KEY = @"branch_key";
+
+ NSDictionary *configDictionary = [fabric configurationDictionaryForKitClass:[Branch class]];
+ NSDictionary *dictionary = [configDictionary objectForKey:BNC_BRANCH_FABRIC_APP_KEY_KEY];
+
+ return dictionary;
+ }
+ return nil;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFieldDefines.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFieldDefines.h
new file mode 100644
index 00000000000..50f7eea8514
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCFieldDefines.h
@@ -0,0 +1,186 @@
+//
+// BNCFieldDefines.h
+// Branch-TestBed
+//
+// Created by edward on 8/17/17.
+// Copyright © 2017 Branch Metrics. All rights reserved.
+//
+
+#if defined(addString) // --------------------------------------------------------------------------
+
+ // Already defined so undefine them:
+
+ #undef addString
+ #undef addDate
+ #undef addDouble
+ #undef addBoolean
+ #undef addDecimal
+ #undef addNumber
+ #undef addInteger
+ #undef addStringifiedDictionary
+ #undef addStringArray
+ #undef addDictionary
+ #undef BNCFieldDefinesObjectFromDictionary
+ #undef BNCFieldDefinesDictionaryFromSelf
+
+#elif defined(BNCFieldDefinesObjectFromDictionary) // ----------------------------------------------
+
+ #define addString(field, name) { \
+ NSString *string = dictionary[@#name]; \
+ if ([string isKindOfClass:[NSString class]]) { \
+ object.field = string; \
+ } \
+ }
+
+ #define addDate(field, name) { \
+ NSNumber *number = dictionary[@#name]; \
+ if ([number isKindOfClass:[NSNumber class]] || \
+ [number isKindOfClass:[NSString class]]) { \
+ NSTimeInterval t = [number doubleValue]; \
+ if (t) object.field = [NSDate dateWithTimeIntervalSince1970:t/1000.0]; \
+ } \
+ }
+
+ #define addDouble(field, name) { \
+ NSNumber *number = dictionary[@#name]; \
+ if ([number isKindOfClass:[NSNumber class]] || \
+ [number isKindOfClass:[NSString class]]) { \
+ object.field = [number doubleValue]; \
+ } \
+ }
+
+ #define addBoolean(field, name) { \
+ NSNumber *number = dictionary[@#name]; \
+ if ([number isKindOfClass:[NSNumber class]] || \
+ [number isKindOfClass:[NSString class]]) { \
+ object.field = [number boolValue]; \
+ } \
+ }
+
+ #define addDecimal(field, name) { \
+ NSString *string = dictionary[@#name]; \
+ if ([string isKindOfClass:[NSNumber class]]) \
+ string = [string description]; \
+ if ([string isKindOfClass:[NSString class]]) { \
+ object.field = [NSDecimalNumber decimalNumberWithString:string]; \
+ } \
+ }
+
+ #define addNumber(field, name) { \
+ NSNumber *number = dictionary[@#name]; \
+ if ([number isKindOfClass:[NSString class]]) \
+ number = [number doubleValue]; \
+ if ([number isKindOfClass:[NSNumber class]]) { \
+ object.field = number; \
+ } \
+ }
+
+ #define addInteger(field, name) { \
+ NSNumber *number = dictionary[@#name]; \
+ if ([number isKindOfClass:[NSNumber class]] || \
+ [number isKindOfClass:[NSString class]]) { \
+ object.field = [number integerValue]; \
+ } \
+ }
+
+ #define addStringifiedDictionary(field, name) { \
+ NSString *string = dictionary[@#name]; \
+ if ([string isKindOfClass:[NSString class]]) { \
+ NSDictionary *d = [BNCEncodingUtils decodeJsonStringToDictionary:string]; \
+ object.field = [NSMutableDictionary dictionaryWithDictionary:d]; \
+ } \
+ }
+
+ #define addStringArray(field, name) { \
+ NSArray *a = dictionary[@#name]; \
+ if ([a isKindOfClass:[NSArray class]]) { \
+ NSMutableArray *newArray = [NSMutableArray array]; \
+ for (NSString *s in a) { \
+ if ([s isKindOfClass:[NSString class]]) { \
+ [newArray addObject:s]; \
+ } \
+ } \
+ object.field = newArray; \
+ } else if ([a isKindOfClass:[NSString class]]) { \
+ object.field = [NSMutableArray arrayWithObject:a]; \
+ } else { \
+ object.field = (id) [NSMutableArray new]; \
+ } \
+ }
+
+ #define addDictionary(field, name) { \
+ NSDictionary *d = dictionary[@#name]; \
+ if ([d isKindOfClass:[NSDictionary class]]) { \
+ object.field = [NSMutableDictionary dictionaryWithDictionary:d]; \
+ } \
+ }
+
+#elif defined(BNCFieldDefinesDictionaryFromSelf) // ----------------------------------------------
+
+ #define addString(field, name) { \
+ if (self.field.length) { \
+ dictionary[@#name] = self.field; \
+ } \
+ }
+
+ #define addDate(field, name) { \
+ if (self.field) { \
+ NSTimeInterval t = self.field.timeIntervalSince1970; \
+ dictionary[@#name] = [NSNumber numberWithLongLong:(long long)(t*1000.0)]; \
+ } \
+ }
+
+ #define addDouble(field, name) { \
+ if (self.field != 0.0) { \
+ dictionary[@#name] = [NSNumber numberWithDouble:self.field]; \
+ } \
+ }
+
+ #define addBoolean(field, name) { \
+ if (self.field) { \
+ dictionary[@#name] = (__bridge NSNumber*) kCFBooleanTrue; \
+ } \
+ }
+
+ #define addDecimal(field, name) { \
+ if (self.field) { \
+ dictionary[@#name] = self.field; \
+ } \
+ }
+
+ #define addNumber(field, name) { \
+ if (self.field != nil) { \
+ dictionary[@#name] = self.field; \
+ } \
+ }
+
+ #define addInteger(field, name) { \
+ if (self.field != 0) { \
+ dictionary[@#name] = [NSNumber numberWithInteger:self.field]; \
+ } \
+ }
+
+ #define addStringifiedDictionary(field, name) { \
+ if (self.field.count) { \
+ NSString *string = [BNCEncodingUtils encodeDictionaryToJsonString:self.field]; \
+ dictionary[@#name] = string; \
+ } \
+ }
+
+ #define addStringArray(field, name) { \
+ if (self.field.count) { \
+ dictionary[@#name] = self.field; \
+ } \
+ }
+
+ #define addDictionary(field, name) { \
+ if (self.field.count) { \
+ dictionary[@#name] = self.field; \
+ } \
+ }
+
+#else
+
+//#error "Define either 'BNCFieldDefinesDictionaryFromObject' or 'BNCFieldDefinesObjectFromDictionary'.
+
+#endif
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCKeyChain.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCKeyChain.h
new file mode 100644
index 00000000000..fe14d76d133
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCKeyChain.h
@@ -0,0 +1,64 @@
+/**
+ @file BNCKeyChain.h
+ @package Branch-SDK
+ @brief Simple access routines for secure keychain storage.
+
+ @author Edward Smith
+ @date January 8, 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+@interface BNCKeyChain : NSObject
+
+/**
+ @brief Remove a value for a service and key. Optionally removes all keys and values for a service.
+
+ @param service The name of the service under which to store the key.
+ @param key The key to remove the value from. If `nil` is passed, all keys and values are removed for that service.
+ @return Returns an `NSError` if an error occurs.
+*/
++ (NSError*_Nullable) removeValuesForService:(NSString*_Nullable)service
+ key:(NSString*_Nullable)key;
+
+/**
+ @brief Returns a value for the passed service and key.
+
+ @param service The name of the service that the value is stored under.
+ @param key The key that the value is stored under.
+ @param error If an error occurs, and `error` is a pointer to an error pointer, the error is returned here.
+ @return Returns the value stored under `service` and `key`, or `nil` if none found.
+*/
++ (id _Nullable) retrieveValueForService:(NSString*_Nonnull)service
+ key:(NSString*_Nonnull)key
+ error:(NSError*_Nullable __autoreleasing *_Nullable)error;
+
+/**
+ @brief Returns an array of all items found in the keychain for an app.
+
+ @param error If an error occurs, the error is returned in `error` if it is not `NULL`.
+ @return Returns an array of the items stored in the keychain or `nil`.
+*/
++ (NSArray*_Nullable) retieveAllValuesWithError:(NSError*_Nullable __autoreleasing *_Nullable)error;
+
+/**
+ @brief Stores an item in the keychain.
+
+ @param service The service name to store the item under.
+ @param key The key to store the item under.
+ @param accessGroup The iCloud security access group for sharing the item. Specify `nil` if item should not be shared.
+ @return Returns an error if an error occurs.
+ */
++ (NSError*_Nullable) storeValue:(id _Nonnull)value
+ forService:(NSString*_Nonnull)service
+ key:(NSString*_Nonnull)key
+ cloudAccessGroup:(NSString*_Nullable)accessGroup;
+
+/// The default security access group for the app.
++ (NSString*_Nullable) securityAccessGroup;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCKeyChain.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCKeyChain.m
new file mode 100644
index 00000000000..44a3f651887
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCKeyChain.m
@@ -0,0 +1,250 @@
+/**
+ @file BNCKeyChain.m
+ @package Branch-SDK
+ @brief Simple access routines for secure keychain storage.
+
+ @author Edward Smith
+ @date January 8, 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#import "BNCKeyChain.h"
+#import "BNCLog.h"
+
+// Apple Keychain Reference:
+// https://developer.apple.com/library/content/documentation/Conceptual/
+// keychainServConcepts/02concepts/concepts.html#//apple_ref/doc/uid/TP30000897-CH204-SW1
+//
+// To translate security errors to text from the command line use: `security error -34018`
+
+#pragma mark SecCopyErrorMessageString
+
+#if TARGET_OS_OSX
+
+//#pragma clang link undefined _SecCopyErrorMessageString // -Wl,-U,_SecCopyErrorMessageString
+extern CFStringRef SecCopyErrorMessageString(OSStatus status, void *reserved)
+ __attribute__((weak_import));
+
+#else
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+CFStringRef SecCopyErrorMessageString(OSStatus status, void *reserved) {
+ return CFSTR("Sec OSStatus error.");
+}
+#pragma clang diagnostic pop
+
+#endif
+
+#pragma mark - BNCKeyChain
+
+@implementation BNCKeyChain
+
++ (NSError*) errorWithKey:(NSString*)key OSStatus:(OSStatus)status {
+ // Security errors are defined in Security/SecBase.h
+ if (status == errSecSuccess) return nil;
+ NSString *reason = nil;
+ NSString *description =
+ [NSString stringWithFormat:@"Security error with key '%@': code %ld.", key, (long) status];
+
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wtautological-compare"
+ #pragma clang diagnostic ignored "-Wpartial-availability"
+ if (SecCopyErrorMessageString != NULL)
+ reason = (__bridge_transfer NSString*) SecCopyErrorMessageString(status, NULL);
+ #pragma clang diagnostic pop
+
+ if (!reason)
+ reason = @"Sec OSStatus error.";
+
+ NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:@{
+ NSLocalizedDescriptionKey: description,
+ NSLocalizedFailureReasonErrorKey: reason
+ }];
+ return error;
+}
+
++ (NSArray*) retieveAllValuesWithError:(NSError**)error {
+ if (error) *error = nil;
+ NSDictionary* dictionary = @{
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
+ (__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,
+ (__bridge id)kSecAttrSynchronizable: (__bridge id)kSecAttrSynchronizableAny,
+ (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitAll
+ };
+ CFTypeRef valueData = NULL;
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dictionary, &valueData);
+ if (status == errSecItemNotFound) status = 0;
+ if (status) {
+ NSError *localError = [self errorWithKey:@"" OSStatus:status];
+ BNCLogDebugSDK(@"Can't retrieve key: %@.", localError);
+ if (error) *error = localError;
+ if (valueData) CFRelease(valueData);
+ return nil;
+ }
+ NSMutableArray *array = nil;
+ if ([((__bridge NSArray*)valueData) isKindOfClass:[NSArray class]]) {
+ NSArray *dataArray = (__bridge NSArray*) valueData;
+ array = [NSMutableArray new];
+ for (NSData* data in dataArray) {
+ id value = nil;
+ @try {
+ value = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+ }
+ @catch (id) {
+ value = nil;
+ }
+ if (value)
+ [array addObject:value];
+ else
+ if (data)
+ [array addObject:data];
+ }
+ }
+ if (valueData)
+ CFRelease(valueData);
+ return array;
+}
+
++ (id) retrieveValueForService:(NSString*)service key:(NSString*)key error:(NSError**)error {
+ if (error) *error = nil;
+ if (service == nil || key == nil) {
+ NSError *localError = [self errorWithKey:key OSStatus:errSecParam];
+ if (error) *error = localError;
+ return nil;
+ }
+
+ NSDictionary* dictionary = @{
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
+ (__bridge id)kSecAttrService: service,
+ (__bridge id)kSecAttrAccount: key,
+ (__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,
+ (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
+ (__bridge id)kSecAttrSynchronizable: (__bridge id)kSecAttrSynchronizableAny
+ };
+ CFDataRef valueData = NULL;
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dictionary, (CFTypeRef *)&valueData);
+ if (status != errSecSuccess) {
+ NSError *localError = [self errorWithKey:key OSStatus:status];
+ BNCLogDebugSDK(@"Can't retrieve key: %@.", localError);
+ if (error) *error = localError;
+ if (valueData) CFRelease(valueData);
+ return nil;
+ }
+ id value = nil;
+ if (valueData) {
+ @try {
+ value = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData*)valueData];
+ }
+ @catch (id) {
+ value = nil;
+ NSError *localError = [self errorWithKey:key OSStatus:errSecDecode];
+ if (error) *error = localError;
+ }
+ CFRelease(valueData);
+ }
+ return value;
+}
+
++ (NSError*) storeValue:(id)value
+ forService:(NSString*)service
+ key:(NSString*)key
+ cloudAccessGroup:(NSString*)accessGroup {
+
+ if (value == nil || service == nil || key == nil)
+ return [self errorWithKey:key OSStatus:errSecParam];
+
+ NSData* valueData = nil;
+ @try {
+ valueData = [NSKeyedArchiver archivedDataWithRootObject:value];
+ }
+ @catch(id) {
+ valueData = nil;
+ }
+ if (!valueData) {
+ NSError *error = [NSError errorWithDomain:NSCocoaErrorDomain
+ code:NSPropertyListWriteStreamError userInfo:nil];
+ return error;
+ }
+ NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:@{
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
+ (__bridge id)kSecAttrService: service,
+ (__bridge id)kSecAttrAccount: key,
+ (__bridge id)kSecAttrSynchronizable: (__bridge id)kSecAttrSynchronizableAny
+ }];
+ OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dictionary);
+ if (status != errSecSuccess && status != errSecItemNotFound) {
+ NSError *error = [self errorWithKey:key OSStatus:status];
+ BNCLogDebugSDK(@"Can't clear to store key: %@.", error);
+ }
+
+ dictionary[(__bridge id)kSecValueData] = valueData;
+ dictionary[(__bridge id)kSecAttrIsInvisible] = (__bridge id)kCFBooleanTrue;
+ dictionary[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlock;
+
+ if (accessGroup.length) {
+ dictionary[(__bridge id)kSecAttrAccessGroup] = accessGroup;
+ dictionary[(__bridge id)kSecAttrSynchronizable] = (__bridge id) kCFBooleanTrue;
+ } else {
+ dictionary[(__bridge id)kSecAttrSynchronizable] = (__bridge id) kCFBooleanFalse;
+ }
+ status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
+ if (status) {
+ NSError *error = [self errorWithKey:key OSStatus:status];
+ BNCLogDebugSDK(@"Can't store key: %@.", error);
+ return error;
+ }
+ return nil;
+}
+
++ (NSError*) removeValuesForService:(NSString*)service key:(NSString*)key {
+ NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithDictionary:@{
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
+ (__bridge id)kSecAttrSynchronizable: (__bridge id)kSecAttrSynchronizableAny
+ }];
+ if (service) dictionary[(__bridge id)kSecAttrService] = service;
+ if (key) dictionary[(__bridge id)kSecAttrAccount] = key;
+
+ OSStatus status = SecItemDelete((__bridge CFDictionaryRef)dictionary);
+ if (status == errSecItemNotFound) status = errSecSuccess;
+ if (status) {
+ NSError *error = [self errorWithKey:key OSStatus:status];
+ BNCLogDebugSDK(@"Can't remove key: %@.", error);
+ return error;
+ }
+ return nil;
+}
+
++ (NSString*_Nullable) securityAccessGroup {
+ // https://stackoverflow.com/questions/11726672/access-app-identifier-prefix-programmatically
+ @synchronized(self) {
+ static NSString*_securityAccessGroup = nil;
+ if (_securityAccessGroup) return _securityAccessGroup;
+
+ // First store a value:
+ NSError*error = [self storeValue:@"Value" forService:@"BranchKeychainService" key:@"Temp" cloudAccessGroup:nil];
+ if (error) BNCLogDebugSDK(@"Error storing temp value: %@.", error);
+
+ NSDictionary* dictionary = @{
+ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
+ (__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue,
+ (__bridge id)kSecAttrSynchronizable: (__bridge id)kSecAttrSynchronizableAny,
+ (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne
+ };
+ CFDictionaryRef resultDictionary = NULL;
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)dictionary, (CFTypeRef*)&resultDictionary);
+ if (status == errSecItemNotFound) return nil;
+ if (status != errSecSuccess) {
+ BNCLogDebugSDK(@"Get securityAccessGroup returned(%ld): %@.",
+ (long) status, [self errorWithKey:nil OSStatus:status]);
+ return nil;
+ }
+ NSString*group =
+ [(__bridge NSDictionary *)resultDictionary objectForKey:(__bridge NSString *)kSecAttrAccessGroup];
+ if (group.length > 0) _securityAccessGroup = [group copy];
+ CFRelease(resultDictionary);
+ return _securityAccessGroup;
+ }
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCLinkCache.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkCache.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCLinkCache.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkCache.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkCache.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkCache.m
new file mode 100644
index 00000000000..252b5781979
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkCache.m
@@ -0,0 +1,45 @@
+//
+// BNCLinkCache.m
+// Branch-SDK
+//
+// Created by Qinwei Gong on 1/23/15.
+// Copyright (c) 2015 Branch Metrics. All rights reserved.
+//
+
+
+#import "BNCLinkCache.h"
+
+
+@interface BNCLinkCache ()
+@property (nonatomic, strong) NSMutableDictionary *cache;
+@end
+
+
+@implementation BNCLinkCache
+
+- (id)init {
+ if ((self = [super init])) {
+ self.cache = [[NSMutableDictionary alloc] init];
+ }
+ return self;
+}
+
+- (void)setObject:(NSString *)anObject forKey:(BNCLinkData *)aKey {
+ @synchronized (self) {
+ self.cache[@([aKey hash])] = anObject;
+ }
+}
+
+- (NSString *)objectForKey:(BNCLinkData *)aKey {
+ @synchronized (self) {
+ return self.cache[@([aKey hash])];
+ }
+}
+
+- (void) clear {
+ @synchronized (self) {
+ [self.cache removeAllObjects];
+ }
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCLinkData.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkData.h
similarity index 94%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCLinkData.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkData.h
index d2488702c80..c092409bc33 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCLinkData.h
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkData.h
@@ -17,7 +17,7 @@ typedef NS_ENUM(NSUInteger, BranchLinkType) {
BranchLinkTypeOneTimeUse = 1
};
-@interface BNCLinkData : NSObject
+@interface BNCLinkData : NSObject
@property (strong, nonatomic) NSMutableDictionary *data;
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkData.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkData.m
new file mode 100644
index 00000000000..0a37194cdcf
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLinkData.m
@@ -0,0 +1,193 @@
+//
+// BNCLinkData.m
+// Branch-SDK
+//
+// Created by Qinwei Gong on 1/22/15.
+// Copyright (c) 2015 Branch Metrics. All rights reserved.
+//
+
+
+#import "BNCLinkData.h"
+#import "BNCEncodingUtils.h"
+#import "BranchConstants.h"
+
+
+@interface BNCLinkData ()
+@property (strong, nonatomic) NSArray *tags;
+@property (strong, nonatomic) NSString *alias;
+@property (strong, nonatomic) NSString *channel;
+@property (strong, nonatomic) NSString *feature;
+@property (strong, nonatomic) NSString *stage;
+@property (strong, nonatomic) NSString *campaign;
+@property (strong, nonatomic) NSDictionary *params;
+@property (strong, nonatomic) NSString *ignoreUAString;
+@property (assign, nonatomic) BranchLinkType type;
+@property (assign, nonatomic) NSUInteger duration;
+@end
+
+
+@implementation BNCLinkData
+
+- (id)init {
+ if ((self = [super init])) {
+ self.data = [[NSMutableDictionary alloc] init];
+ self.data[@"source"] = @"ios";
+ }
+ return self;
+}
+
+- (void)setupTags:(NSArray *)tags {
+ if (tags) {
+ _tags = tags;
+
+ self.data[BRANCH_REQUEST_KEY_URL_TAGS] = tags;
+ }
+}
+
+- (void)setupAlias:(NSString *)alias {
+ if (alias) {
+ _alias = alias;
+
+ self.data[BRANCH_REQUEST_KEY_URL_ALIAS] = alias;
+ }
+}
+
+- (void)setupType:(BranchLinkType)type {
+ if (type) {
+ _type = type;
+
+ self.data[BRANCH_REQUEST_KEY_URL_LINK_TYPE] = @(type);
+ }
+}
+
+- (void)setupMatchDuration:(NSUInteger)duration {
+ if (duration > 0) {
+ _duration = duration;
+
+ self.data[BRANCH_REQUEST_KEY_URL_DURATION] = @(duration);
+ }
+}
+
+- (void)setupChannel:(NSString *)channel {
+ if (channel) {
+ _channel = channel;
+
+ self.data[BRANCH_REQUEST_KEY_URL_CHANNEL] = channel;
+ }
+}
+
+- (void)setupFeature:(NSString *)feature {
+ if (feature) {
+ _feature = feature;
+
+ self.data[BRANCH_REQUEST_KEY_URL_FEATURE] = feature;
+ }
+}
+
+- (void)setupStage:(NSString *)stage {
+ if (stage) {
+ _stage = stage;
+
+ self.data[BRANCH_REQUEST_KEY_URL_STAGE] = stage;
+ }
+}
+
+- (void)setupCampaign:(NSString *)campaign {
+ if (campaign) {
+ _campaign = campaign;
+
+ self.data[BRANCH_REQUEST_KEY_URL_CAMPAIGN] = campaign;
+ }
+}
+
+- (void)setupIgnoreUAString:(NSString *)ignoreUAString {
+ if (ignoreUAString) {
+ _ignoreUAString = ignoreUAString;
+
+ self.data[BRANCH_REQUEST_KEY_URL_IGNORE_UA_STRING] = ignoreUAString;
+ }
+}
+
+- (void)setupParams:(NSDictionary *)params {
+ if (params) {
+ _params = params;
+
+ self.data[BRANCH_REQUEST_KEY_URL_DATA] = params;
+ }
+}
+
+- (NSUInteger)hash {
+ NSUInteger result = 1;
+ NSUInteger prime = 19;
+
+ NSString *encodedParams = [BNCEncodingUtils encodeDictionaryToJsonString:self.params];
+ result = prime * result + self.type;
+ result = prime * result + [[BNCEncodingUtils md5Encode:self.alias] hash];
+ result = prime * result + [[BNCEncodingUtils md5Encode:self.channel] hash];
+ result = prime * result + [[BNCEncodingUtils md5Encode:self.feature] hash];
+ result = prime * result + [[BNCEncodingUtils md5Encode:self.stage] hash];
+ result = prime * result + [[BNCEncodingUtils md5Encode:self.campaign] hash];
+ result = prime * result + [[BNCEncodingUtils md5Encode:encodedParams] hash];
+ result = prime * result + self.duration;
+
+ for (NSString *tag in self.tags) {
+ result = prime * result + [[BNCEncodingUtils md5Encode:tag] hash];
+ }
+
+ return result;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder {
+ if (self.tags) {
+ [coder encodeObject:self.tags forKey:BRANCH_REQUEST_KEY_URL_TAGS];
+ }
+ if (self.alias) {
+ [coder encodeObject:self.alias forKey:BRANCH_REQUEST_KEY_URL_ALIAS];
+ }
+ if (self.type) {
+ [coder encodeObject:@(self.type) forKey:BRANCH_REQUEST_KEY_URL_LINK_TYPE];
+ }
+ if (self.channel) {
+ [coder encodeObject:self.channel forKey:BRANCH_REQUEST_KEY_URL_CHANNEL];
+ }
+ if (self.feature) {
+ [coder encodeObject:self.feature forKey:BRANCH_REQUEST_KEY_URL_FEATURE];
+ }
+ if (self.stage) {
+ [coder encodeObject:self.stage forKey:BRANCH_REQUEST_KEY_URL_STAGE];
+ }
+ if (self.campaign) {
+ [coder encodeObject:self.campaign forKey:BRANCH_REQUEST_KEY_URL_CAMPAIGN];
+ }
+ if (self.params) {
+ NSString *encodedParams = [BNCEncodingUtils encodeDictionaryToJsonString:self.params];
+ [coder encodeObject:encodedParams forKey:BRANCH_REQUEST_KEY_URL_DATA];
+ }
+ if (self.duration > 0) {
+ [coder encodeObject:@(self.duration) forKey:BRANCH_REQUEST_KEY_URL_DURATION];
+ }
+}
+
+- (id)initWithCoder:(NSCoder *)coder {
+ if ((self = [super init])) {
+ self.tags = [coder decodeObjectOfClass:NSArray.class forKey:BRANCH_REQUEST_KEY_URL_TAGS];
+ self.alias = [coder decodeObjectOfClass:NSString.class forKey:BRANCH_REQUEST_KEY_URL_ALIAS];
+ self.type = [[coder decodeObjectOfClass:NSNumber.class forKey:BRANCH_REQUEST_KEY_URL_LINK_TYPE] integerValue];
+ self.channel = [coder decodeObjectOfClass:NSString.class forKey:BRANCH_REQUEST_KEY_URL_CHANNEL];
+ self.feature = [coder decodeObjectOfClass:NSString.class forKey:BRANCH_REQUEST_KEY_URL_FEATURE];
+ self.stage = [coder decodeObjectOfClass:NSString.class forKey:BRANCH_REQUEST_KEY_URL_STAGE];
+ self.campaign = [coder decodeObjectOfClass:NSString.class forKey:BRANCH_REQUEST_KEY_URL_CAMPAIGN];
+ self.duration = [[coder decodeObjectOfClass:NSNumber.class forKey:BRANCH_REQUEST_KEY_URL_DURATION] integerValue];
+
+ NSString *encodedParams = [coder decodeObjectOfClass:NSString.class forKey:BRANCH_REQUEST_KEY_URL_DATA];
+ self.params = [BNCEncodingUtils decodeJsonStringToDictionary:encodedParams];
+ }
+
+ return self;
+}
+
++ (BOOL) supportsSecureCoding {
+ return YES;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLocalization.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLocalization.h
new file mode 100644
index 00000000000..35c5dbf3d68
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLocalization.h
@@ -0,0 +1,50 @@
+/**
+ @file BNCLocalization.h
+ @package Branch-SDK
+ @brief Branch string localizations.
+
+ @author Parth Kalavadia
+ @date July 2017
+ @copyright Copyright © 2017 Branch. All rights reserved.
+*/
+
+/**
+ @discusion
+
+ # BNCLocalization
+
+ Since the Branch SDK can be shipped as a static library, it can't use the standard Apple
+ string localization mechanism.
+
+ Use this class to localize the few user-facing string resources the Branch SDK has.
+*/
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface BNCLocalization : NSObject
+
++ (instancetype) shared;
++ (NSString*) applicationLanguage;
++ (NSDictionary*) languageDictionaries;
+- (NSString*) localizeString:(NSString*)string;
+
+/// Set to an empty string or nil to reset the current language.
+@property (copy, atomic) NSString* currentLanguage;
+@property (strong, atomic, readonly) NSDictionary *currentLanguageDictionary;
+@end
+
+#pragma mark Convenience Functions
+
+static inline NSString* BNCLocalizedString(NSString*const string) {
+ return [[BNCLocalization shared] localizeString:string];
+}
+
+extern NSString* BNCLocalizedFormattedString(NSString*const format, ...) NS_FORMAT_FUNCTION(1,2);
+
+NS_ASSUME_NONNULL_END
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLocalization.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLocalization.m
new file mode 100644
index 00000000000..cb38170faaa
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLocalization.m
@@ -0,0 +1,295 @@
+/**
+ @file BNCLocalization.m
+ @package Branch-SDK
+ @brief Branch string localizations.
+
+ @author Parth Kalavadia
+ @date July 2017
+ @copyright Copyright © 2017 Branch. All rights reserved.
+*/
+
+#import "BNCLocalization.h"
+#import "BNCLog.h"
+
+#pragma mark Convenience Functions
+
+NSString* _Nonnull BNCLocalizedFormattedString(NSString* _Nonnull const format, ...) {
+ NSString *string = @"";
+ if (format) {
+ va_list args;
+ va_start(args, format);
+ string = [[NSString alloc] initWithFormat:format arguments:args];
+ va_end(args);
+ }
+ return string;
+}
+
+#pragma mark BNCLocalization
+
+@interface BNCLocalization () {
+ NSString *_currentLanguage;
+}
+@end
+
+@implementation BNCLocalization
+
++ (instancetype) shared {
+ static BNCLocalization *bnc_shared = 0;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ bnc_shared = [[BNCLocalization alloc] init];
+ });
+ return bnc_shared;
+}
+
++ (NSString*_Nonnull) applicationLanguage {
+ NSString* lang = [[[NSBundle mainBundle] preferredLocalizations] firstObject];
+ return lang ? lang : @"";
+}
+
+- (instancetype) init {
+ self = [super init];
+ if (!self) return self;
+ self.currentLanguage = [self.class applicationLanguage];
+ return self;
+}
+
+- (NSString*_Nonnull) currentLanguage {
+ @synchronized (self) {
+ return _currentLanguage;
+ }
+}
+
+- (void) setCurrentLanguage:(NSString*)language {
+ @synchronized (self) {
+
+ if (!language.length) {
+ language = [self.class applicationLanguage];
+ }
+
+ NSRange range =
+ [language rangeOfCharacterFromSet:
+ [[NSCharacterSet letterCharacterSet] invertedSet]];
+ if (range.location != NSNotFound)
+ language = [language substringToIndex:range.location];
+ language = [language lowercaseString];
+
+ _currentLanguageDictionary = self.class.languageDictionaries[language];
+ if (_currentLanguageDictionary) {
+ _currentLanguage = language.copy;
+ } else {
+ _currentLanguage = @"en";
+ _currentLanguageDictionary = [BNCLocalization en_localized];
+ }
+ }
+}
+
+- (NSString*_Nonnull) localizeString:(NSString *)string {
+ if (!string) return @"";
+
+ NSString *localized = self.currentLanguageDictionary[string];
+ if (localized) return localized;
+
+ BNCLogWarning(
+ @"Branch is missing the localization missing for language '%@' string '%@'.",
+ self.currentLanguage, string);
+
+ localized = self.class.languageDictionaries[@"en"][string];
+ if (localized) return localized;
+
+ return string;
+}
+
++(NSDictionary*_Nonnull) languageDictionaries {
+ NSDictionary* languages = @{
+ @"en": [BNCLocalization en_localized],
+ @"ru": [BNCLocalization ru_localized],
+ };
+ return languages;
+}
+
++ (NSDictionary*_Nonnull) en_localized {
+ NSDictionary* en_dict = @{
+
+ // BNCInitError
+ @"The Branch user session has not been initialized.":
+ @"The Branch user session has not been initialized.",
+
+ // BNCDuplicateResourceError
+ @"A resource with this identifier already exists.":
+ @"A resource with this identifier already exists.",
+
+ // BNCRedeemCreditsError
+ @"You're trying to redeem more credits than are available. Have you loaded rewards?":
+ @"You're trying to redeem more credits than are available. Have you loaded rewards?",
+
+ // BNCBadRequestError
+ @"The network request was invalid.":
+ @"The network request was invalid.",
+
+ // BNCServerProblemError
+ @"Trouble reaching the Branch servers, please try again shortly.":
+ @"Trouble reaching the Branch servers, please try again shortly.",
+
+ // BNCNilLogError
+ @"Can't log error messages because the logger is set to nil.":
+ @"Can't log error messages because the logger is set to nil.",
+
+ // BNCVersionError
+ @"Incompatible version.":
+ @"Incompatible version.",
+
+ // BNCNetworkServiceInterfaceError
+ @"The underlying network service does not conform to the BNCNetworkOperationProtocol.":
+ @"The underlying network service does not conform to the BNCNetworkOperationProtocol.",
+
+ // BNCInvalidNetworkPublicKeyError
+ @"Public key is not an SecKeyRef type.":
+ @"Public key is not an SecKeyRef type.",
+
+ // BNCContentIdentifierError
+ @"A canonical identifier or title are required to uniquely identify content.":
+ @"A canonical identifier or title are required to uniquely identify content.",
+
+ // BNCSpotlightNotAvailableError
+ @"The Core Spotlight indexing service is not available on this device.":
+ @"The Core Spotlight indexing service is not available on this device.",
+
+ // BNCSpotlightTitleError
+ @"Spotlight indexing requires a title.":
+ @"Spotlight indexing requires a title.",
+
+ // BNCRedeemZeroCreditsError
+ @"Can't redeem zero credits.":
+ @"Can't redeem zero credits.",
+
+ // Unknown error
+ @"Branch encountered an error.":
+ @"Branch encountered an error.",
+
+ // Network provider error messages
+ @"A network operation instance is expected to be returned by the networkOperationWithURLRequest:completion: method.":
+ @"A network operation instance is expected to be returned by the networkOperationWithURLRequest:completion: method.",
+
+ @"Network operation of class '%@' does not conform to the BNCNetworkOperationProtocol.":
+ @"Network operation of class '%@' does not conform to the BNCNetworkOperationProtocol.",
+
+ @"The network operation start date is not set. The Branch SDK expects the network operation start date to be set by the network provider.":
+ @"The network operation start date is not set. The Branch SDK expects the network operation start date to be set by the network provider.",
+
+ @"The network operation timeout date is not set. The Branch SDK expects the network operation timeout date to be set by the network provider.":
+ @"The network operation timeout date is not set. The Branch SDK expects the network operation timeout date to be set by the network provider.",
+
+ @"The network operation request is not set. The Branch SDK expects the network operation request to be set by the network provider.":
+ @"The network operation request is not set. The Branch SDK expects the network operation request to be set by the network provider.",
+
+ // Other errors
+ @"The request was invalid.":
+ @"The request was invalid.",
+
+ @"Could not register view.":
+ @"Could not register view.",
+
+ @"Could not generate a URL.":
+ @"Could not generate a URL.",
+
+ @"User tracking is disabled.":
+ @"User tracking is disabled."
+ };
+ return en_dict;
+}
+
++ (NSDictionary*_Nonnull) ru_localized {
+
+ NSDictionary* ru_dict = @{
+
+ // BNCInitError
+ @"The Branch user session has not been initialized.":
+ @"Сессия Branch не была инициализирована.",
+
+ // BNCDuplicateResourceError
+ @"A resource with this identifier already exists.":
+ @"Ресурс с таким идентификатором уже существует.",
+
+ // BNCRedeemCreditsError
+ @"You're trying to redeem more credits than are available. Have you loaded rewards?":
+ @"Вы пытаетесь применить больше кредитов, чем доступно. Были ли загружены награды?",
+
+ // BNCBadRequestError
+ @"The network request was invalid.":
+ @"Неправильный сетевой запрос.",
+
+ // BNCServerProblemError
+ @"Trouble reaching the Branch servers, please try again shortly.":
+ @"Проблемы с соединением с серверами Branch, попробуйте повторить операцию позже.",
+
+ // BNCNilLogError
+ @"Can't log error messages because the logger is set to nil.":
+ @"Невозможно записать ошибку, так как значение логгера nil.",
+
+ // BNCVersionError
+ @"Incompatible version.":
+ @"Несовместимая версия.",
+
+ // BNCNetworkServiceInterfaceError
+ @"The underlying network service does not conform to the BNCNetworkOperationProtocol.":
+ @"Используемый сетевой сервис не соблюдает протокол BNCNetworkOperationProtocol.",
+
+ // BNCInvalidNetworkPublicKeyError
+ @"Public key is not an SecKeyRef type.":
+ @"Публичный ключ неверного типа. Ожидаемый тип: SecKeyRef.",
+
+ // BNCContentIdentifierError
+ @"A canonical identifier or title are required to uniquely identify content.":
+ @"Канонический идентификатор или название необходимы для идентификации контента.",
+
+ // BNCSpotlightNotAvailableError
+ @"The Core Spotlight indexing service is not available on this device.":
+ @"Сервис Core Spotlight Indexing не доступен на этом устройстве.",
+
+ // BNCSpotlightTitleError
+ @"Spotlight indexing requires a title.":
+ @"Название – необходимый параметр для Spotlight Indexing",
+
+ // BNCRedeemZeroCreditsError
+ @"Can't redeem zero credits.":
+ @"Нельзя применить ноль кредитов.",
+
+ // Unknown error
+ @"Branch encountered an error.":
+ @"Неизвестная ошибка в Branch.",
+
+ // Network provider error messages
+ @"A network operation instance is expected to be returned by the networkOperationWithURLRequest:completion: method.":
+ @"Объект сетевого оператора должен быть возвращен после запроса к методу networkOperationWithURLRequest:completion:.",
+
+ @"Network operation of class '%@' does not conform to the BNCNetworkOperationProtocol.":
+ @"Сетевая операция класса '%@' не соответствует протоколу BNCNetworkOperationProtocol.",
+
+ @"The network operation start date is not set. The Branch SDK expects the network operation start date to be set by the network provider.":
+ @"Дата старта сетевого запроса не была установлена. SDK Branch ожидает, что дату старта сетевого запроса назначит сетевой провайдер.",
+
+ @"The network operation timeout date is not set. The Branch SDK expects the network operation timeout date to be set by the network provider.":
+ @"Дата тайм-аута сетевого запроса не была назначена. SDK Branch ожидает, что дату тайм-аута сетевого запроса назначит сетевой провайдер.",
+
+ @"The network operation request is not set. The Branch SDK expects the network operation request to be set by the network provider.":
+ @"Сетевой запрос не был назначен. SDK Branch ожидает, что сетевой запрос назначит сетевой провайдер.",
+
+ // Other errors
+ @"The request was invalid.":
+ @"Неверный запрос.",
+
+ @"Could not register view.":
+ @"Не получилось зарегистрировать view.",
+
+ @"Could not generate a URL.":
+ @"Не получилось сгенерировать URL.",
+
+ @"User tracking is disbabled.":
+ @"Трекинг пользователя отключен.",
+ };
+
+ return ru_dict;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCLog.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLog.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCLog.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLog.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLog.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLog.m
new file mode 100644
index 00000000000..ab3905c2184
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCLog.m
@@ -0,0 +1,673 @@
+/**
+ @file BNCLog.m
+ @package Branch-SDK
+ @brief Simple logging functions.
+
+ @author Edward Smith
+ @date October 2016
+ @copyright Copyright © 2016 Branch. All rights reserved.
+*/
+
+#import "BNCLog.h"
+#import // import not available in Xcode 7
+
+#define _countof(array) (sizeof(array)/sizeof(array[0]))
+static NSNumber *bnc_LogIsInitialized = nil;
+
+// All log synchronization and globals are coordinated through the bnc_LogQueue.
+static dispatch_queue_t bnc_LogQueue = nil;
+
+static off_t bnc_LogOffset = 0;
+static off_t bnc_LogOffsetMax = 100;
+static off_t bnc_LogRecordSize = 1024;
+static BNCLogOutputFunctionPtr bnc_LoggingFunction = nil; // Default to just NSLog output.
+static BNCLogFlushFunctionPtr bnc_LogFlushFunction = nil;
+static NSDateFormatter *bnc_LogDateFormatter = nil;
+
+// A fallback attempt at logging if an error occurs in BNCLog.
+// BNCLog can't log itself, but if an error occurs it uses this simple define:
+extern void BNCLogInternalErrorFunction(int linenumber, NSString*format, ...);
+void BNCLogInternalErrorFunction(int linenumber, NSString*format, ...) {
+
+ va_list args;
+ va_start(args, format);
+ NSString* message = [[NSString alloc] initWithFormat:format arguments:args];
+ va_end(args);
+
+ NSLog(@"[branch.io] BNCLog.m (%d) Log error: %@", linenumber, message);
+}
+
+#define BNCLogInternalError(...) \
+ BNCLogInternalErrorFunction(__LINE__, __VA_ARGS__)
+
+
+inline static void BNCLogInitializeClient_Internal() {
+ BNCLogInitialize();
+ BNCLogClientInitializeFunctionPtr initFunction = BNCLogSetClientInitializeFunction(NULL);
+ if (initFunction) {
+ initFunction();
+ }
+}
+
+#pragma mark - Default Output Functions
+
+static int bnc_LogDescriptor = -1;
+
+void BNCLogFunctionOutputToStdOut(
+ NSDate*_Nonnull timestamp,
+ BNCLogLevel level,
+ NSString *_Nullable message
+ ) {
+ NSData *data = [message dataUsingEncoding:NSNEXTSTEPStringEncoding];
+ if (!data) data = [@"" dataUsingEncoding:NSNEXTSTEPStringEncoding];
+ long n = write(STDOUT_FILENO, data.bytes, data.length);
+ if (n < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't write log message (%d): %s.", e, strerror(e));
+ }
+ write(STDOUT_FILENO, "\n ", sizeof('\n'));
+}
+
+void BNCLogFunctionOutputToStdErr(
+ NSDate*_Nonnull timestamp,
+ BNCLogLevel level,
+ NSString *_Nullable message
+ ) {
+ NSData *data = [message dataUsingEncoding:NSNEXTSTEPStringEncoding];
+ if (!data) data = [@"" dataUsingEncoding:NSNEXTSTEPStringEncoding];
+ long n = write(STDERR_FILENO, data.bytes, data.length);
+ if (n < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't write log message (%d): %s.", e, strerror(e));
+ }
+ write(STDERR_FILENO, "\n ", sizeof('\n'));
+}
+
+void BNCLogFunctionOutputToFileDescriptor(
+ NSDate*_Nonnull timestamp,
+ BNCLogLevel level,
+ NSString *_Nullable message
+ ) {
+ // Pad length to even characters
+ if (!message) message = @"";
+ NSString *string = [NSString stringWithFormat:@"%@\n", message];
+ NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
+ if ((data.length & 1) == 1) {
+ string = [NSString stringWithFormat:@"%@ \n", message];
+ data = [string dataUsingEncoding:NSUTF8StringEncoding];
+ }
+ if ((data.length & 1) != 0) {
+ BNCLogInternalError(@"Writing un-even bytes!");
+ }
+ long n = write(bnc_LogDescriptor, data.bytes, data.length);
+ if (n < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't write log message (%d): %s.", e, strerror(e));
+ }
+}
+
+void BNCLogFlushFileDescriptor() {
+ if (bnc_LogDescriptor >= 0) {
+ fsync(bnc_LogDescriptor);
+ }
+}
+
+void BNCLogSetOutputToURL_Interal(NSURL *_Nullable url) {
+ if (url == nil) return;
+ bnc_LogDescriptor = open(
+ url.path.UTF8String,
+ O_RDWR|O_CREAT|O_APPEND,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
+ );
+ if (bnc_LogDescriptor < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't open log file '%@'.", url);
+ BNCLogInternalError(@"Can't open log file (%d): %s.", e, strerror(e));
+ return;
+ }
+ bnc_LoggingFunction = BNCLogFunctionOutputToFileDescriptor;
+ bnc_LogFlushFunction = BNCLogFlushFileDescriptor;
+}
+
+void BNCLogSetOutputToURL(NSURL *_Nullable url) {
+ dispatch_sync(bnc_LogQueue, ^{
+ if (bnc_LogFlushFunction)
+ bnc_LogFlushFunction();
+ if (bnc_LogDescriptor >= 0) {
+ close(bnc_LogDescriptor);
+ bnc_LogDescriptor = -1;
+ }
+ BNCLogSetOutputToURL_Interal(url);
+ });
+}
+
+#pragma mark - Record Wrap Output File Functions
+
+void BNCLogRecordWrapWrite(NSDate*_Nonnull timestamp, BNCLogLevel level, NSString *_Nullable message) {
+
+ NSString * string = [NSString stringWithFormat:@"%@ %ld %@",
+ [bnc_LogDateFormatter stringFromDate:timestamp], (long) level, message];
+ NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
+
+ char buffer[bnc_LogRecordSize];
+ memset(buffer, ' ', sizeof(buffer));
+ buffer[sizeof(buffer)-1] = '\n';
+ long len = MIN(stringData.length, sizeof(buffer)-1);
+ memcpy(buffer, stringData.bytes, len);
+
+ off_t n = write(bnc_LogDescriptor, buffer, sizeof(buffer));
+ if (n < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't write log message (%d): %s.", e, strerror(e));
+ }
+ bnc_LogOffset++;
+ if (bnc_LogOffset >= bnc_LogOffsetMax) {
+ bnc_LogOffset = 0;
+ n = lseek(bnc_LogDescriptor, 0, SEEK_SET);
+ if (n < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't seek in log (%d): %s.", e, strerror(e));
+ }
+ }
+}
+
+void BNCLogRecordWrapFlush() {
+ if (bnc_LogDescriptor >= 0) {
+ fsync(bnc_LogDescriptor);
+ }
+}
+
+BOOL BNCLogRecordWrapOpenURL_Internal(NSURL *url, long maxRecords, long recordSize) {
+ if (url == nil) return NO;
+ bnc_LogOffsetMax = MAX(1, maxRecords);
+ bnc_LogRecordSize = MAX(64, recordSize);
+ if ((bnc_LogRecordSize & 1) != 0) {
+ // Can't have odd-length records.
+ bnc_LogRecordSize++;
+ }
+ bnc_LogDescriptor = open(
+ url.path.UTF8String,
+ O_RDWR|O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
+ );
+ if (bnc_LogDescriptor < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't open log file '%@'.", url);
+ BNCLogInternalError(@"Can't open log file (%d): %s.", e, strerror(e));
+ return NO;
+ }
+ bnc_LoggingFunction = BNCLogRecordWrapWrite;
+ bnc_LogFlushFunction = BNCLogRecordWrapFlush;
+
+ // Truncate the file if the file size > max file size.
+
+ off_t fileOffset = 0;
+ off_t maxSz = bnc_LogOffsetMax * bnc_LogRecordSize;
+ off_t sz = lseek(bnc_LogDescriptor, 0, SEEK_END);
+ if (sz < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't seek in log (%d): %s.", e, strerror(e));
+ } else if (sz > maxSz) {
+ fileOffset = ftruncate(bnc_LogDescriptor, maxSz);
+ if (fileOffset < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't truncate log (%d): %s.", e, strerror(e));
+ }
+ }
+ lseek(bnc_LogDescriptor, 0, SEEK_SET);
+
+ // Read the records until the oldest record is found --
+
+ off_t oldestOffset = 0;
+ NSDate * oldestDate = [NSDate distantFuture];
+ NSDate * lastDate = [NSDate distantFuture];
+
+ off_t offset = 0;
+ char buffer[bnc_LogRecordSize];
+ ssize_t bytesRead = read(bnc_LogDescriptor, &buffer, sizeof(buffer));
+ while ((unsigned long) bytesRead == sizeof(buffer)) {
+ NSString *dateString =
+ [[NSString alloc] initWithBytes:buffer length:27 encoding:NSUTF8StringEncoding];
+ NSDate *date = [bnc_LogDateFormatter dateFromString:dateString];
+ if ([date compare:oldestDate] < 0 ||
+ [date compare:lastDate] < 0) {
+ oldestOffset = offset;
+ oldestDate = date;
+ }
+ offset++;
+ if (date) lastDate = date;
+ bytesRead = read(bnc_LogDescriptor, &buffer, sizeof(buffer));
+ }
+ if (offset < bnc_LogOffsetMax)
+ bnc_LogOffset = offset;
+ else
+ if (oldestOffset >= bnc_LogOffsetMax)
+ bnc_LogOffset = 0;
+ else
+ bnc_LogOffset = oldestOffset;
+ sz = lseek(bnc_LogDescriptor, bnc_LogOffset*bnc_LogRecordSize, SEEK_SET);
+ if (sz < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't seek in log (%d): %s.", e, strerror(e));
+ }
+ return YES;
+}
+
+BOOL BNCLogRecordWrapOpenURL(NSURL *url, long maxRecords, long recordSize) {
+ __block BOOL result = NO;
+ dispatch_sync(bnc_LogQueue, ^{
+ if (bnc_LogFlushFunction)
+ bnc_LogFlushFunction();
+ if (bnc_LogDescriptor >= 0) {
+ close(bnc_LogDescriptor);
+ bnc_LogDescriptor = -1;
+ }
+ bnc_LoggingFunction = NULL;
+ bnc_LogFlushFunction = NULL;
+ result = BNCLogRecordWrapOpenURL_Internal(url, maxRecords, recordSize);
+ });
+ return result;
+}
+
+void BNCLogSetOutputToURLRecordWrapSize(NSURL *_Nullable url, long maxRecords, long recordSize) {
+ BNCLogRecordWrapOpenURL(url, maxRecords, recordSize);
+}
+
+void BNCLogSetOutputToURLRecordWrap(NSURL *_Nullable url, long maxRecords) {
+ BNCLogSetOutputToURLRecordWrapSize(url, maxRecords, 1024);
+}
+
+#pragma mark - Byte Wrap Output File Functions
+
+void BNCLogByteWrapWrite(NSDate*_Nonnull timestamp, BNCLogLevel level, NSString *_Nullable message) {
+
+ NSString * string = [NSString stringWithFormat:@"%@ %ld %@\n",
+ [bnc_LogDateFormatter stringFromDate:timestamp], (long) level, message];
+ NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
+
+ if ((stringData.length & 1) != 0) {
+ string = [NSString stringWithFormat:@"%@ %ld %@ \n",
+ [bnc_LogDateFormatter stringFromDate:timestamp], (long) level, message];
+ stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
+ }
+
+ // Truncate the file if the file size > max file size.
+
+ if ((bnc_LogOffset + stringData.length) > bnc_LogOffsetMax) {
+ long n = ftruncate(bnc_LogDescriptor, bnc_LogOffset);
+ if (n < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't truncate log (%d): %s.", e, strerror(e));
+ }
+ lseek(bnc_LogDescriptor, 0, SEEK_SET);
+ bnc_LogOffset = 0;
+ }
+
+ long n = write(bnc_LogDescriptor, stringData.bytes, stringData.length);
+ if (n < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't write log message (%d): %s.", e, strerror(e));
+ } else {
+ bnc_LogOffset += n;
+ }
+}
+
+void BNCLogByteWrapFlush() {
+ if (bnc_LogDescriptor >= 0) {
+ fsync(bnc_LogDescriptor);
+ }
+}
+
+NSString *BNCLogByteWrapReadNextRecord() {
+
+ char *buffer = NULL;
+ long bufferSize = 0;
+ off_t originalOffset = lseek(bnc_LogDescriptor, 0, SEEK_CUR);
+ if (originalOffset < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't find offset in log file (%d): %s.", e, strerror(e));
+ goto error_exit;
+ }
+
+ do {
+
+ bufferSize += 1024;
+ if (buffer) free(buffer);
+ buffer = malloc(bufferSize);
+ if (!buffer) {
+ BNCLogInternalError(@"Can't allocate a buffer of %ld bytes.", bufferSize);
+ goto error_exit;
+ }
+
+ off_t newOffset = lseek(bnc_LogDescriptor, originalOffset, SEEK_SET);
+ if (newOffset < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't seek in log file (%d): %s.", e, strerror(e));
+ goto error_exit;
+ }
+ ssize_t bytesRead = read(bnc_LogDescriptor, buffer, bufferSize);
+ if (bytesRead == 0) {
+ goto error_exit;
+ } else if (bytesRead < 0) {
+ int e = errno;
+ if (e != EOF) {
+ BNCLogInternalError(@"Can't read log message (%d): %s.", e, strerror(e));
+ }
+ goto error_exit;
+ }
+
+ char* p = buffer;
+ intptr_t endByte = bytesRead;
+ while ( (p-buffer) < endByte && *p != '\n') {
+ p++;
+ }
+ if ((p-buffer) < endByte && *p == '\n') {
+ intptr_t offset = (p-buffer)+1;
+ NSString *result =
+ [[NSString alloc]
+ initWithBytes:buffer length:offset encoding:NSUTF8StringEncoding];
+ bnc_LogOffset = originalOffset + offset;
+ newOffset = lseek(bnc_LogDescriptor, bnc_LogOffset, SEEK_SET);
+ if (newOffset < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't seek in log file (%d): %s.", e, strerror(e));
+ }
+ free(buffer);
+ return result;
+ }
+
+ } while (bufferSize < 1024*20);
+
+error_exit:
+ if (buffer) free(buffer);
+ return nil;
+}
+
+BOOL BNCLogByteWrapOpenURL_Internal(NSURL *url, long maxBytes) {
+ if (url == nil) return NO;
+ bnc_LogOffsetMax = MAX(256, maxBytes);
+ bnc_LogDescriptor = open(
+ url.path.UTF8String,
+ O_RDWR|O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
+ );
+ if (bnc_LogDescriptor < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't open log file '%@'.", url);
+ BNCLogInternalError(@"Can't open log file (%d): %s.", e, strerror(e));
+ return NO;
+ }
+ bnc_LoggingFunction = BNCLogByteWrapWrite;
+ bnc_LogFlushFunction = BNCLogByteWrapFlush;
+
+ // Truncate the file if the file size > max file size.
+
+ off_t newOffset = 0;
+ off_t maxSz = bnc_LogOffsetMax;
+ off_t sz = lseek(bnc_LogDescriptor, 0, SEEK_END);
+ if (sz < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't seek in log (%d): %s.", e, strerror(e));
+ } else if (sz > maxSz) {
+ newOffset = ftruncate(bnc_LogDescriptor, maxSz);
+ if (newOffset < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't truncate log (%d): %s.", e, strerror(e));
+ }
+ }
+ bnc_LogOffset = 0;
+ lseek(bnc_LogDescriptor, bnc_LogOffset, SEEK_SET);
+
+ // Read the records until the oldest record is found --
+
+ BOOL logDidWrap = NO;
+ off_t wrapOffset = 0;
+
+ off_t lastOffset = 0;
+ NSDate *lastDate = [NSDate distantPast];
+
+ NSString *record = BNCLogByteWrapReadNextRecord();
+ while (record) {
+ NSDate *date = nil;
+ if (record.length >= 27) {
+ NSString *dateString = [record substringWithRange:NSMakeRange(0, 27)];
+ date = [bnc_LogDateFormatter dateFromString:dateString];
+ }
+ if (!date || [date compare:lastDate] < 0) {
+ wrapOffset = lastOffset;
+ logDidWrap = YES;
+ }
+ lastDate = date ?: [NSDate distantPast];
+ lastOffset = bnc_LogOffset;
+ record = BNCLogByteWrapReadNextRecord();
+ }
+ if (logDidWrap) {
+ bnc_LogOffset = wrapOffset;
+ } else if (bnc_LogOffset >= bnc_LogOffsetMax)
+ bnc_LogOffset = 0;
+ newOffset = lseek(bnc_LogDescriptor, bnc_LogOffset, SEEK_SET);
+ if (newOffset < 0) {
+ int e = errno;
+ BNCLogInternalError(@"Can't seek in log (%d): %s.", e, strerror(e));
+ }
+ return YES;
+}
+
+void BNCLogSetOutputToURLByteWrap(NSURL *_Nullable URL, long maxBytes) {
+ __block BOOL result = NO;
+ dispatch_sync(bnc_LogQueue, ^{
+ if (bnc_LogFlushFunction)
+ bnc_LogFlushFunction();
+ if (bnc_LogDescriptor >= 0) {
+ close(bnc_LogDescriptor);
+ bnc_LogDescriptor = -1;
+ }
+ result = BNCLogByteWrapOpenURL_Internal(URL, maxBytes);
+ });
+}
+
+#pragma mark - Log Message Severity
+
+static BNCLogLevel bnc_LogDisplayLevel = BNCLogLevelWarning;
+
+BNCLogLevel BNCLogDisplayLevel() {
+ BNCLogInitializeClient_Internal();
+ __block BNCLogLevel level = BNCLogLevelAll;
+ dispatch_sync(bnc_LogQueue, ^{
+ level = bnc_LogDisplayLevel;
+ });
+ return level;
+}
+
+void BNCLogSetDisplayLevel(BNCLogLevel level) {
+ BNCLogInitializeClient_Internal();
+ dispatch_async(bnc_LogQueue, ^{
+ bnc_LogDisplayLevel = level;
+ });
+}
+
+static NSString*const bnc_logLevelStrings[] = {
+ @"BNCLogLevelAll",
+ @"BNCLogLevelBreakPoint",
+ @"BNCLogLevelDebug",
+ @"BNCLogLevelWarning",
+ @"BNCLogLevelError",
+ @"BNCLogLevelAssert",
+ @"BNCLogLevelLog",
+ @"BNCLogLevelNone",
+ @"BNCLogLevelMax"
+};
+
+NSString* BNCLogStringFromLogLevel(BNCLogLevel level) {
+ level = MAX(MIN(level, BNCLogLevelMax), 0);
+ return bnc_logLevelStrings[level];
+}
+
+BNCLogLevel BNCLogLevelFromString(NSString*string) {
+ if (!string) return BNCLogLevelNone;
+ for (NSUInteger i = 0; i < _countof(bnc_logLevelStrings); ++i) {
+ if ([bnc_logLevelStrings[i] isEqualToString:string]) {
+ return i;
+ }
+ }
+ if ([string isEqualToString:@"BNCLogLevelDebugSDK"]) {
+ return BNCLogLevelDebugSDK;
+ }
+ return BNCLogLevelNone;
+}
+
+#pragma mark - Client Initialization Function
+
+static _Atomic(BNCLogClientInitializeFunctionPtr) bnc_LogClientInitializeFunctionPtr = (BNCLogClientInitializeFunctionPtr) 0;
+
+extern BNCLogClientInitializeFunctionPtr _Null_unspecified BNCLogSetClientInitializeFunction(
+ BNCLogClientInitializeFunctionPtr _Nullable clientInitializationFunction
+ ) {
+ BNCLogClientInitializeFunctionPtr lastPtr =
+ atomic_exchange(&bnc_LogClientInitializeFunctionPtr, clientInitializationFunction);
+ return lastPtr;
+}
+
+#pragma mark - Break Points
+
+static BOOL bnc_LogBreakPointsAreEnabled = NO;
+
+BOOL BNCLogBreakPointsAreEnabled() {
+ __block BOOL enabled = NO;
+ dispatch_sync(bnc_LogQueue, ^{
+ enabled = bnc_LogBreakPointsAreEnabled;
+ });
+ return enabled;
+}
+
+void BNCLogSetBreakPointsEnabled(BOOL enabled) {
+ dispatch_async(bnc_LogQueue, ^{
+ bnc_LogBreakPointsAreEnabled = enabled;
+ });
+}
+
+#pragma mark - Log Functions
+
+BNCLogOutputFunctionPtr _Nullable BNCLogOutputFunction() {
+ __block BNCLogOutputFunctionPtr ptr = NULL;
+ dispatch_sync(bnc_LogQueue, ^{
+ ptr = bnc_LoggingFunction;
+ });
+ return ptr;
+}
+
+void BNCLogCloseLogFile() {
+ dispatch_sync(bnc_LogQueue, ^{
+ if (bnc_LogFlushFunction)
+ bnc_LogFlushFunction();
+ if (bnc_LogDescriptor >= 0) {
+ close(bnc_LogDescriptor);
+ bnc_LogDescriptor = -1;
+ }
+ bnc_LogFlushFunction = NULL;
+ bnc_LoggingFunction = NULL;
+ });
+}
+
+void BNCLogSetOutputFunction(BNCLogOutputFunctionPtr _Nullable logFunction) {
+ dispatch_async(bnc_LogQueue, ^{
+ bnc_LoggingFunction = logFunction;
+ });
+}
+
+BNCLogFlushFunctionPtr BNCLogFlushFunction() {
+ __block BNCLogFlushFunctionPtr ptr = NULL;
+ dispatch_sync(bnc_LogQueue, ^{
+ ptr = bnc_LogFlushFunction;
+ });
+ return ptr;
+}
+
+void BNCLogSetFlushFunction(BNCLogFlushFunctionPtr flushFunction) {
+ dispatch_async(bnc_LogQueue, ^{
+ bnc_LogFlushFunction = flushFunction;
+ });
+}
+
+#pragma mark - BNCLogInternal
+
+void BNCLogWriteMessageFormat(
+ BNCLogLevel logLevel,
+ const char *_Nullable file,
+ int32_t lineNumber,
+ NSString *_Nullable message,
+ ...
+ ) {
+ BNCLogInitializeClient_Internal();
+ if (!file) file = "";
+ if (!message) message = @"";
+ if (![message isKindOfClass:[NSString class]]) {
+ message = [NSString stringWithFormat:@"0x%016llx <%@> %@",
+ (uint64_t) message, message.class, message.description];
+ }
+
+ NSString* filename =
+ [[NSString stringWithCString:file encoding:NSMacOSRomanStringEncoding]
+ lastPathComponent];
+
+ NSString * const logLevels[BNCLogLevelMax] = {
+ @"DebugSDK",
+ @"Break",
+ @"Debug",
+ @"Warning",
+ @"Error",
+ @"Assert",
+ @"Log",
+ @"None",
+ };
+
+ logLevel = MAX(MIN(logLevel, BNCLogLevelMax-1), 0);
+ NSString *levelString = logLevels[logLevel];
+
+ va_list args;
+ va_start(args, message);
+ NSString* m = [[NSString alloc] initWithFormat:message arguments:args];
+ NSString* s = [NSString stringWithFormat:
+ @"[branch.io] %@(%d) %@: %@", filename, lineNumber, levelString, m];
+ va_end(args);
+
+ dispatch_async(bnc_LogQueue, ^{
+ if (logLevel >= bnc_LogDisplayLevel) {
+ NSLog(@"%@", s); // Upgrade this to unified logging when we can.
+ }
+ if (bnc_LoggingFunction)
+ bnc_LoggingFunction([NSDate date], logLevel, s);
+ });
+}
+
+void BNCLogWriteMessage(
+ BNCLogLevel logLevel,
+ NSString *_Nonnull file,
+ int32_t lineNumber,
+ NSString *_Nonnull message
+ ) {
+ BNCLogWriteMessageFormat(logLevel, file.UTF8String, lineNumber, @"%@", message);
+}
+
+void BNCLogFlushMessages() {
+ dispatch_sync(bnc_LogQueue, ^{
+ if (bnc_LogFlushFunction)
+ bnc_LogFlushFunction();
+ });
+}
+
+#pragma mark - BNCLogInitialize
+
+__attribute__((constructor)) void BNCLogInitialize(void) {
+ static dispatch_once_t onceToken = 0;
+ dispatch_once(&onceToken, ^ {
+ bnc_LogQueue = dispatch_queue_create("io.branch.sdk.log", DISPATCH_QUEUE_SERIAL);
+
+ bnc_LogDateFormatter = [[NSDateFormatter alloc] init];
+ bnc_LogDateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
+ bnc_LogDateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSSSSX";
+ bnc_LogDateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
+
+ bnc_LogIsInitialized = @(YES);
+ });
+}
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCPreferenceHelper.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCPreferenceHelper.h
similarity index 99%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCPreferenceHelper.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCPreferenceHelper.h
index 949cde7cb4e..215aec636e3 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BNCPreferenceHelper.h
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCPreferenceHelper.h
@@ -94,5 +94,6 @@ NSURL* /* _Nonnull */ BNCURLForBranchDirectory(void);
- (NSMutableString*) sanitizedMutableBaseURL:(NSString*)baseUrl;
- (void) synchronize; // Flushes preference queue to persistence.
++ (void) clearAll;
@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCPreferenceHelper.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCPreferenceHelper.m
new file mode 100644
index 00000000000..83b6786a4f7
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCPreferenceHelper.m
@@ -0,0 +1,959 @@
+//
+// BNCPreferenceHelper.m
+// Branch-SDK
+//
+// Created by Alex Austin on 6/6/14.
+// Copyright (c) 2014 Branch Metrics. All rights reserved.
+//
+
+#import "BNCPreferenceHelper.h"
+#import "BNCEncodingUtils.h"
+#import "BNCConfig.h"
+#import "Branch.h"
+#import "BNCLog.h"
+#import "BNCFabricAnswers.h"
+#import "BranchConstants.h"
+#import "NSString+Branch.h"
+
+static const NSTimeInterval DEFAULT_TIMEOUT = 5.5;
+static const NSTimeInterval DEFAULT_RETRY_INTERVAL = 0;
+static const NSInteger DEFAULT_RETRY_COUNT = 3;
+
+static NSString * const BRANCH_PREFS_FILE = @"BNCPreferences";
+
+static NSString * const BRANCH_PREFS_KEY_APP_VERSION = @"bnc_app_version";
+static NSString * const BRANCH_PREFS_KEY_LAST_RUN_BRANCH_KEY = @"bnc_last_run_branch_key";
+static NSString * const BRANCH_PREFS_KEY_LAST_STRONG_MATCH_DATE = @"bnc_strong_match_created_date";
+static NSString * const BRANCH_PREFS_KEY_DEVICE_FINGERPRINT_ID = @"bnc_device_fingerprint_id";
+static NSString * const BRANCH_PREFS_KEY_SESSION_ID = @"bnc_session_id";
+static NSString * const BRANCH_PREFS_KEY_IDENTITY_ID = @"bnc_identity_id";
+static NSString * const BRANCH_PREFS_KEY_IDENTITY = @"bnc_identity";
+static NSString * const BRANCH_PREFS_KEY_CHECKED_FACEBOOK_APP_LINKS = @"bnc_checked_fb_app_links";
+static NSString * const BRANCH_PREFS_KEY_CHECKED_APPLE_SEARCH_ADS = @"bnc_checked_apple_search_ads";
+static NSString * const BRANCH_PREFS_KEY_APPLE_SEARCH_ADS_INFO = @"bnc_apple_search_ads_info";
+static NSString * const BRANCH_PREFS_KEY_LINK_CLICK_IDENTIFIER = @"bnc_link_click_identifier";
+static NSString * const BRANCH_PREFS_KEY_SPOTLIGHT_IDENTIFIER = @"bnc_spotlight_identifier";
+static NSString * const BRANCH_PREFS_KEY_UNIVERSAL_LINK_URL = @"bnc_universal_link_url";
+static NSString * const BRANCH_PREFS_KEY_SESSION_PARAMS = @"bnc_session_params";
+static NSString * const BRANCH_PREFS_KEY_INSTALL_PARAMS = @"bnc_install_params";
+static NSString * const BRANCH_PREFS_KEY_USER_URL = @"bnc_user_url";
+static NSString * const BRANCH_PREFS_KEY_BRANCH_UNIVERSAL_LINK_DOMAINS = @"branch_universal_link_domains";
+
+static NSString * const BRANCH_PREFS_KEY_CREDITS = @"bnc_credits";
+static NSString * const BRANCH_PREFS_KEY_CREDIT_BASE = @"bnc_credit_base_";
+
+static NSString * const BRANCH_PREFS_KEY_BRANCH_VIEW_USAGE_CNT = @"bnc_branch_view_usage_cnt_";
+static NSString * const BRANCH_PREFS_KEY_ANALYTICAL_DATA = @"bnc_branch_analytical_data";
+static NSString * const BRANCH_PREFS_KEY_ANALYTICS_MANIFEST = @"bnc_branch_analytics_manifest";
+
+@interface BNCPreferenceHelper () {
+ NSOperationQueue *_persistPrefsQueue;
+ NSString *_lastSystemBuildVersion;
+ NSString *_browserUserAgentString;
+ NSString *_branchAPIURL;
+ NSString *_referringURL;
+}
+
+@property (strong, nonatomic) NSMutableDictionary *persistenceDict;
+@property (strong, nonatomic) NSMutableDictionary *creditsDictionary;
+@property (strong, nonatomic) NSMutableDictionary *requestMetadataDictionary;
+@property (strong, nonatomic) NSMutableDictionary *instrumentationDictionary;
+@end
+
+@implementation BNCPreferenceHelper
+
+@synthesize
+ lastRunBranchKey = _lastRunBranchKey,
+ appVersion = _appVersion,
+ deviceFingerprintID = _deviceFingerprintID,
+ sessionID = _sessionID,
+ spotlightIdentifier = _spotlightIdentifier,
+ identityID = _identityID,
+ linkClickIdentifier = _linkClickIdentifier,
+ userUrl = _userUrl,
+ userIdentity = _userIdentity,
+ sessionParams = _sessionParams,
+ installParams = _installParams,
+ universalLinkUrl = _universalLinkUrl,
+ externalIntentURI = _externalIntentURI,
+ isDebug = _isDebug,
+ shouldWaitForInit = _shouldWaitForInit,
+ retryCount = _retryCount,
+ retryInterval = _retryInterval,
+ timeout = _timeout,
+ lastStrongMatchDate = _lastStrongMatchDate,
+ checkedFacebookAppLinks = _checkedFacebookAppLinks,
+ checkedAppleSearchAdAttribution = _checkedAppleSearchAdAttribution,
+ appleSearchAdDetails = _appleSearchAdDetails,
+ requestMetadataDictionary = _requestMetadataDictionary,
+ instrumentationDictionary = _instrumentationDictionary;
+
++ (BNCPreferenceHelper *)preferenceHelper {
+ static BNCPreferenceHelper *preferenceHelper;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ preferenceHelper = [[BNCPreferenceHelper alloc] init];
+ });
+
+ return preferenceHelper;
+}
+
+- (id)init {
+ self = [super init];
+ if (!self) return self;
+
+ _timeout = DEFAULT_TIMEOUT;
+ _retryCount = DEFAULT_RETRY_COUNT;
+ _retryInterval = DEFAULT_RETRY_INTERVAL;
+ _isDebug = NO;
+ _persistPrefsQueue = [[NSOperationQueue alloc] init];
+ _persistPrefsQueue.maxConcurrentOperationCount = 1;
+
+ return self;
+}
+
++ (BNCPreferenceHelper *)getInstance {
+ static BNCPreferenceHelper *preferenceHelper;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ preferenceHelper = [[BNCPreferenceHelper alloc] init];
+ });
+
+ return preferenceHelper;
+}
+
+- (void) synchronize {
+ [_persistPrefsQueue waitUntilAllOperationsAreFinished];
+}
+
+- (void) dealloc {
+ [self synchronize];
+}
+
+#pragma mark - API methods
+
+- (void) setBranchAPIURL:(NSString*)branchAPIURL_ {
+ @synchronized (self) {
+ _branchAPIURL = [branchAPIURL_ copy];
+ }
+}
+
+- (NSString*) branchAPIURL {
+ @synchronized (self) {
+ if (!_branchAPIURL) {
+ _branchAPIURL = [BNC_API_BASE_URL copy];
+ }
+ return _branchAPIURL;
+ }
+}
+
+- (NSString *)getAPIBaseURL {
+ @synchronized (self) {
+ return [NSString stringWithFormat:@"%@/%@/", self.branchAPIURL, BNC_API_VERSION];
+ }
+}
+
+- (NSString *)getAPIURL:(NSString *) endpoint {
+ return [[self getAPIBaseURL] stringByAppendingString:endpoint];
+}
+
+- (NSString *)getEndpointFromURL:(NSString *)url {
+ NSString *APIBase = self.branchAPIURL;
+ if ([url hasPrefix:APIBase]) {
+ NSUInteger index = APIBase.length;
+ return [url substringFromIndex:index];
+ }
+ return @"";
+}
+
+#pragma mark - Preference Storage
+
+- (NSString *)lastRunBranchKey {
+ if (!_lastRunBranchKey) {
+ _lastRunBranchKey = [self readStringFromDefaults:BRANCH_PREFS_KEY_LAST_RUN_BRANCH_KEY];
+ }
+ return _lastRunBranchKey;
+}
+
+- (void)setLastRunBranchKey:(NSString *)lastRunBranchKey {
+ if (![_lastRunBranchKey isEqualToString:lastRunBranchKey]) {
+ _lastRunBranchKey = lastRunBranchKey;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_LAST_RUN_BRANCH_KEY value:lastRunBranchKey];
+ }
+}
+
+- (NSDate *)lastStrongMatchDate {
+ if (!_lastStrongMatchDate) {
+ _lastStrongMatchDate = (NSDate *)[self readObjectFromDefaults:BRANCH_PREFS_KEY_LAST_STRONG_MATCH_DATE];
+ }
+ return _lastStrongMatchDate;
+}
+
+- (void)setLastStrongMatchDate:(NSDate *)lastStrongMatchDate {
+ if (lastStrongMatchDate == nil || ![_lastStrongMatchDate isEqualToDate:lastStrongMatchDate]) {
+ _lastStrongMatchDate = lastStrongMatchDate;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_LAST_STRONG_MATCH_DATE value:lastStrongMatchDate];
+ }
+}
+
+- (NSString *)appVersion {
+ if (!_appVersion) {
+ _appVersion = [self readStringFromDefaults:BRANCH_PREFS_KEY_APP_VERSION];
+ }
+ return _appVersion;
+}
+
+- (void)setAppVersion:(NSString *)appVersion {
+ if (![_appVersion isEqualToString:appVersion]) {
+ _appVersion = appVersion;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_APP_VERSION value:appVersion];
+ }
+}
+
+- (NSString *)deviceFingerprintID {
+ if (!_deviceFingerprintID) {
+ _deviceFingerprintID = [self readStringFromDefaults:BRANCH_PREFS_KEY_DEVICE_FINGERPRINT_ID];
+ }
+
+ return _deviceFingerprintID;
+}
+
+- (void)setDeviceFingerprintID:(NSString *)deviceFingerprintID {
+ if (deviceFingerprintID == nil || ![_deviceFingerprintID isEqualToString:deviceFingerprintID]) {
+ _deviceFingerprintID = deviceFingerprintID;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_DEVICE_FINGERPRINT_ID value:deviceFingerprintID];
+ }
+}
+
+- (NSString *)sessionID {
+ if (!_sessionID) {
+ _sessionID = [self readStringFromDefaults:BRANCH_PREFS_KEY_SESSION_ID];
+ }
+
+ return _sessionID;
+}
+
+- (void)setSessionID:(NSString *)sessionID {
+ if (sessionID == nil || ![_sessionID isEqualToString:sessionID]) {
+ _sessionID = sessionID;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_SESSION_ID value:sessionID];
+ }
+}
+
+- (NSString *)identityID {
+ return [self readStringFromDefaults:BRANCH_PREFS_KEY_IDENTITY_ID];
+}
+
+- (void)setIdentityID:(NSString *)identityID {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_IDENTITY_ID value:identityID];
+}
+
+- (NSString *)userIdentity {
+ return [self readStringFromDefaults:BRANCH_PREFS_KEY_IDENTITY];
+}
+
+- (void)setUserIdentity:(NSString *)userIdentity {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_IDENTITY value:userIdentity];
+}
+
+- (NSString *)linkClickIdentifier {
+ return [self readStringFromDefaults:BRANCH_PREFS_KEY_LINK_CLICK_IDENTIFIER];
+}
+
+- (void)setLinkClickIdentifier:(NSString *)linkClickIdentifier {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_LINK_CLICK_IDENTIFIER value:linkClickIdentifier];
+}
+
+- (NSString *)spotlightIdentifier {
+ return [self readStringFromDefaults:BRANCH_PREFS_KEY_SPOTLIGHT_IDENTIFIER];
+}
+
+- (void)setSpotlightIdentifier:(NSString *)spotlightIdentifier {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_SPOTLIGHT_IDENTIFIER value:spotlightIdentifier];
+}
+
+- (NSString *)externalIntentURI {
+ @synchronized(self) {
+ if (!_externalIntentURI) {
+ _externalIntentURI = [self readStringFromDefaults:BRANCH_REQUEST_KEY_EXTERNAL_INTENT_URI];
+ }
+ return _externalIntentURI;
+ }
+}
+
+- (void)setExternalIntentURI:(NSString *)externalIntentURI {
+ @synchronized(self) {
+ if (externalIntentURI == nil || ![_externalIntentURI isEqualToString:externalIntentURI]) {
+ _externalIntentURI = externalIntentURI;
+ [self writeObjectToDefaults:BRANCH_REQUEST_KEY_EXTERNAL_INTENT_URI value:externalIntentURI];
+ }
+ }
+}
+
+- (NSString*) referringURL {
+ @synchronized (self) {
+ if (!_referringURL) _referringURL = [self readStringFromDefaults:@"referringURL"];
+ return _referringURL;
+ }
+}
+
+- (void) setReferringURL:(NSString *)referringURL {
+ @synchronized (self) {
+ _referringURL = [referringURL copy];
+ [self writeObjectToDefaults:@"referringURL" value:_referringURL];
+ }
+}
+
+- (NSString *)universalLinkUrl {
+ return [self readStringFromDefaults:BRANCH_PREFS_KEY_UNIVERSAL_LINK_URL];
+}
+
+- (void)setUniversalLinkUrl:(NSString *)universalLinkUrl {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_UNIVERSAL_LINK_URL value:universalLinkUrl];
+}
+
+- (NSString *)sessionParams {
+ @synchronized (self) {
+ if (!_sessionParams) {
+ _sessionParams = [self readStringFromDefaults:BRANCH_PREFS_KEY_SESSION_PARAMS];
+ }
+ return _sessionParams;
+ }
+}
+
+- (void)setSessionParams:(NSString *)sessionParams {
+ @synchronized (self) {
+ if (sessionParams == nil || ![_sessionParams isEqualToString:sessionParams]) {
+ _sessionParams = sessionParams;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_SESSION_PARAMS value:sessionParams];
+ }
+ }
+}
+
+- (NSString *)installParams {
+ @synchronized(self) {
+ if (!_installParams) {
+ id installParamsFromCache = [self readStringFromDefaults:BRANCH_PREFS_KEY_INSTALL_PARAMS];
+ if ([installParamsFromCache isKindOfClass:[NSString class]]) {
+ _installParams = [self readStringFromDefaults:BRANCH_PREFS_KEY_INSTALL_PARAMS];
+ }
+ else if ([installParamsFromCache isKindOfClass:[NSDictionary class]]) {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_INSTALL_PARAMS value:nil];
+ }
+ }
+ return _installParams;
+ }
+}
+
+- (void)setInstallParams:(NSString *)installParams {
+ @synchronized(self) {
+ if ([installParams isKindOfClass:[NSDictionary class]]) {
+ _installParams = [BNCEncodingUtils encodeDictionaryToJsonString:(NSDictionary *)installParams];
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_INSTALL_PARAMS value:_installParams];
+ return;
+ }
+ if (installParams == nil || ![_installParams isEqualToString:installParams]) {
+ _installParams = installParams;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_INSTALL_PARAMS value:installParams];
+ }
+ }
+}
+
+- (void) setAppleSearchAdDetails:(NSDictionary*)details {
+ if (details == nil || [details isKindOfClass:[NSDictionary class]]) {
+ _appleSearchAdDetails = details;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_APPLE_SEARCH_ADS_INFO value:details];
+ }
+}
+
+- (NSDictionary*) appleSearchAdDetails {
+ if (!_appleSearchAdDetails) {
+ _appleSearchAdDetails = (NSDictionary *) [self readObjectFromDefaults:BRANCH_PREFS_KEY_APPLE_SEARCH_ADS_INFO];
+ }
+ return [_appleSearchAdDetails isKindOfClass:[NSDictionary class]] ? _appleSearchAdDetails : nil;
+}
+
+- (void) setAppleSearchAdNeedsSend:(BOOL)appleSearchAdNeedsSend {
+ [self writeBoolToDefaults:@"_appleSearchAdNeedsSend" value:appleSearchAdNeedsSend];
+}
+
+- (BOOL) appleSearchAdNeedsSend {
+ return [self readBoolFromDefaults:@"_appleSearchAdNeedsSend"];
+}
+
+- (NSString*) lastSystemBuildVersion {
+ if (!_lastSystemBuildVersion) {
+ _lastSystemBuildVersion = [self readStringFromDefaults:@"_lastSystemBuildVersion"];
+ }
+ return _lastSystemBuildVersion;
+}
+
+- (void) setLastSystemBuildVersion:(NSString *)lastSystemBuildVersion {
+ if (![_lastSystemBuildVersion isEqualToString:lastSystemBuildVersion]) {
+ _lastSystemBuildVersion = lastSystemBuildVersion;
+ [self writeObjectToDefaults:@"_lastSystemBuildVersion" value:_lastSystemBuildVersion];
+ }
+}
+
+- (NSString*) browserUserAgentString {
+ if (!_browserUserAgentString) {
+ _browserUserAgentString = [self readStringFromDefaults:@"_browserUserAgentString"];
+ }
+ return _browserUserAgentString;
+}
+
+- (void) setBrowserUserAgentString:(NSString *)browserUserAgentString {
+ if (![_browserUserAgentString isEqualToString:browserUserAgentString]) {
+ _browserUserAgentString = browserUserAgentString;
+ [self writeObjectToDefaults:@"_browserUserAgentString" value:_browserUserAgentString];
+ }
+}
+
+- (NSString *)userUrl {
+ if (!_userUrl) {
+ _userUrl = [self readStringFromDefaults:BRANCH_PREFS_KEY_USER_URL];
+ }
+
+ return _userUrl;
+}
+
+- (void)setUserUrl:(NSString *)userUrl {
+ if (![_userUrl isEqualToString:userUrl]) {
+ _userUrl = userUrl;
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_USER_URL value:userUrl];
+ }
+}
+
+- (NSMutableString*) sanitizedMutableBaseURL:(NSString*)baseUrl_ {
+ NSMutableString *baseUrl = [baseUrl_ mutableCopy];
+ if (self.trackingDisabled) {
+ NSString *id_string = [NSString stringWithFormat:@"%%24identity_id=%@", self.identityID];
+ NSRange range = [baseUrl rangeOfString:id_string];
+ if (range.location != NSNotFound) [baseUrl replaceCharactersInRange:range withString:@""];
+ } else
+ if ([baseUrl hasSuffix:@"&"] || [baseUrl hasSuffix:@"?"]) {
+ } else
+ if ([baseUrl bnc_containsString:@"?"]) {
+ [baseUrl appendString:@"&"];
+ }
+ else {
+ [baseUrl appendString:@"?"];
+ }
+ return baseUrl;
+}
+
+- (BOOL)checkedAppleSearchAdAttribution {
+ _checkedAppleSearchAdAttribution = [self readBoolFromDefaults:BRANCH_PREFS_KEY_CHECKED_APPLE_SEARCH_ADS];
+ return _checkedAppleSearchAdAttribution;
+}
+
+- (void)setCheckedAppleSearchAdAttribution:(BOOL)checked {
+ _checkedAppleSearchAdAttribution = checked;
+ [self writeBoolToDefaults:BRANCH_PREFS_KEY_CHECKED_APPLE_SEARCH_ADS value:checked];
+}
+
+
+- (BOOL)checkedFacebookAppLinks {
+ _checkedFacebookAppLinks = [self readBoolFromDefaults:BRANCH_PREFS_KEY_CHECKED_FACEBOOK_APP_LINKS];
+ return _checkedFacebookAppLinks;
+}
+
+- (void)setCheckedFacebookAppLinks:(BOOL)checked {
+ _checkedFacebookAppLinks = checked;
+ [self writeBoolToDefaults:BRANCH_PREFS_KEY_CHECKED_FACEBOOK_APP_LINKS value:checked];
+}
+
+- (void)clearUserCreditsAndCounts {
+ self.creditsDictionary = [[NSMutableDictionary alloc] init];
+}
+
+- (id)getBranchUniversalLinkDomains {
+ return [[[NSBundle mainBundle] infoDictionary] objectForKey:BRANCH_PREFS_KEY_BRANCH_UNIVERSAL_LINK_DOMAINS];
+}
+
+- (NSMutableDictionary *)requestMetadataDictionary {
+ if (!_requestMetadataDictionary) {
+ _requestMetadataDictionary = [NSMutableDictionary dictionary];
+ }
+ return _requestMetadataDictionary;
+}
+
+- (void)setRequestMetadataKey:(NSString *)key value:(NSObject *)value {
+ if (!key) {
+ return;
+ }
+ if ([self.requestMetadataDictionary objectForKey:key] && !value) {
+ [self.requestMetadataDictionary removeObjectForKey:key];
+ }
+ else if (value) {
+ [self.requestMetadataDictionary setObject:value forKey:key];
+ }
+}
+
+- (NSMutableDictionary *)instrumentationDictionary {
+ @synchronized (self) {
+ if (!_instrumentationDictionary) {
+ _instrumentationDictionary = [NSMutableDictionary dictionary];
+ }
+ return _instrumentationDictionary;
+ }
+}
+
+- (void)addInstrumentationDictionaryKey:(NSString *)key value:(NSString *)value {
+ @synchronized (self) {
+ if (key && value) {
+ [self.instrumentationDictionary setObject:value forKey:key];
+ }
+ }
+}
+
+- (void)clearInstrumentationDictionary {
+ @synchronized (self) {
+ NSArray *keys = [_instrumentationDictionary allKeys];
+ for (NSUInteger i = 0 ; i < [keys count]; i++) {
+ [_instrumentationDictionary removeObjectForKey:keys[i]];
+ }
+ }
+}
+
+- (BOOL) limitFacebookTracking {
+ @synchronized (self) {
+ return [self readBoolFromDefaults:@"_limitFacebookTracking"];
+ }
+}
+
+- (void) setLimitFacebookTracking:(BOOL)limitFacebookTracking {
+ @synchronized (self) {
+ [self writeBoolToDefaults:@"_limitFacebookTracking" value:limitFacebookTracking];
+ }
+}
+
+- (NSDate*) previousAppBuildDate {
+ @synchronized (self) {
+ NSDate *date = (NSDate*) [self readObjectFromDefaults:@"_previousAppBuildDate"];
+ if ([date isKindOfClass:[NSDate class]]) return date;
+ return nil;
+ }
+}
+
+- (void) setPreviousAppBuildDate:(NSDate*)date {
+ @synchronized (self) {
+ if (date == nil || [date isKindOfClass:[NSDate class]])
+ [self writeObjectToDefaults:@"_previousAppBuildDate" value:date];
+ }
+}
+
+- (NSArray*) URLBlackList {
+ @synchronized(self) {
+ id a = [self readObjectFromDefaults:@"URLBlackList"];
+ if ([a isKindOfClass:NSArray.class]) return a;
+ return nil;
+ }
+}
+
+- (void) setURLBlackList:(NSArray *)URLBlackList {
+ @synchronized(self) {
+ [self writeObjectToDefaults:@"URLBlackList" value:URLBlackList];
+ }
+}
+
+- (NSInteger) URLBlackListVersion {
+ @synchronized(self) {
+ return [self readIntegerFromDefaults:@"URLBlackListVersion"];
+ }
+}
+
+- (void) setURLBlackListVersion:(NSInteger)URLBlackListVersion {
+ @synchronized(self) {
+ [self writeIntegerToDefaults:@"URLBlackListVersion" value:URLBlackListVersion];
+ }
+}
+
+- (BOOL) blacklistURLOpen {
+ @synchronized(self) {
+ return [self readBoolFromDefaults:@"blacklistURLOpen"];
+ }
+}
+
+- (void) setBlacklistURLOpen:(BOOL)value {
+ @synchronized(self) {
+ [self writeBoolToDefaults:@"blacklistURLOpen" value:value];
+ }
+}
+
+
+- (BOOL) trackingDisabled {
+ @synchronized(self) {
+ NSNumber *b = (id) [self readObjectFromDefaults:@"trackingDisabled"];
+ if ([b isKindOfClass:NSNumber.class]) return [b boolValue];
+ return false;
+ }
+}
+
+- (void) setTrackingDisabled:(BOOL)disabled {
+ @synchronized(self) {
+ NSNumber *b = [NSNumber numberWithBool:disabled];
+ [self writeObjectToDefaults:@"trackingDisabled" value:b];
+ if (disabled) [self clearTrackingInformation];
+ }
+}
+
+- (void) clearTrackingInformation {
+ @synchronized(self) {
+ /* Don't clear these:
+ self.deviceFingerprintID = nil;
+ self.userIdentity = nil;
+ self.identityID = nil;
+ */
+ self.sessionID = nil;
+ self.linkClickIdentifier = nil;
+ self.spotlightIdentifier = nil;
+ self.referringURL = nil;
+ self.universalLinkUrl = nil;
+ self.installParams = nil;
+ self.appleSearchAdDetails = nil;
+ self.appleSearchAdNeedsSend = NO;
+ self.sessionParams = nil;
+ self.externalIntentURI = nil;
+ self.savedAnalyticsData = nil;
+ self.previousAppBuildDate = nil;
+ self.requestMetadataDictionary = nil;
+ self.lastStrongMatchDate = nil;
+ }
+}
+
+#pragma mark - Credit Storage
+
+- (NSMutableDictionary *)creditsDictionary {
+ if (!_creditsDictionary) {
+ _creditsDictionary = [[self readObjectFromDefaults:BRANCH_PREFS_KEY_CREDITS] mutableCopy];
+
+ if (!_creditsDictionary) {
+ _creditsDictionary = [[NSMutableDictionary alloc] init];
+ }
+ }
+
+ return _creditsDictionary;
+}
+
+- (void)setCreditCount:(NSInteger)count {
+ [self setCreditCount:count forBucket:@"default"];
+}
+
+- (void)setCreditCount:(NSInteger)count forBucket:(NSString *)bucket {
+ self.creditsDictionary[[BRANCH_PREFS_KEY_CREDIT_BASE stringByAppendingString:bucket]] = @(count);
+
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_CREDITS value:self.creditsDictionary];
+}
+
+- (void)removeCreditCountForBucket:(NSString *)bucket {
+ NSMutableDictionary *dictToWrite = self.creditsDictionary;
+ [dictToWrite removeObjectForKey:[BRANCH_PREFS_KEY_CREDIT_BASE stringByAppendingString:bucket]];
+
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_CREDITS value:self.creditsDictionary];
+}
+
+- (NSDictionary *)getCreditDictionary {
+ NSMutableDictionary *returnDictionary = [[NSMutableDictionary alloc] init];
+ for(NSString *key in self.creditsDictionary) {
+ NSString *cleanKey = [key stringByReplacingOccurrencesOfString:BRANCH_PREFS_KEY_CREDIT_BASE
+ withString:@""];
+ returnDictionary[cleanKey] = self.creditsDictionary[key];
+ }
+ return returnDictionary;
+}
+
+- (NSInteger)getCreditCount {
+ return [self getCreditCountForBucket:@"default"];
+}
+
+- (NSInteger)getCreditCountForBucket:(NSString *)bucket {
+ return [self.creditsDictionary[[BRANCH_PREFS_KEY_CREDIT_BASE stringByAppendingString:bucket]] integerValue];
+}
+
+- (void)clearUserCredits {
+ self.creditsDictionary = [[NSMutableDictionary alloc] init];
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_CREDITS value:self.creditsDictionary];
+}
+
+#pragma mark - Count Storage
+
+- (void)updateBranchViewCount:(NSString *)branchViewID {
+ NSInteger currentCount = [self getBranchViewCount:branchViewID] + 1;
+ [self writeObjectToDefaults:[BRANCH_PREFS_KEY_BRANCH_VIEW_USAGE_CNT stringByAppendingString:branchViewID] value:@(currentCount)];
+}
+
+- (NSInteger)getBranchViewCount:(NSString *)branchViewID {
+ NSInteger count = [self readIntegerFromDefaults:[BRANCH_PREFS_KEY_BRANCH_VIEW_USAGE_CNT stringByAppendingString:branchViewID]];
+ if (count == NSNotFound){
+ count = 0;
+ }
+ return count;
+}
+
+- (void)saveBranchAnalyticsData:(NSDictionary *)analyticsData {
+ if (_sessionID) {
+ if (!_savedAnalyticsData) {
+ _savedAnalyticsData = [self getBranchAnalyticsData];
+ }
+ NSMutableArray *viewDataArray = [_savedAnalyticsData objectForKey:_sessionID];
+ if (!viewDataArray) {
+ viewDataArray = [[NSMutableArray alloc] init];
+ [_savedAnalyticsData setObject:viewDataArray forKey:_sessionID];
+ }
+ [viewDataArray addObject:analyticsData];
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_ANALYTICAL_DATA value:_savedAnalyticsData];
+ }
+}
+
+- (void)clearBranchAnalyticsData {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_ANALYTICAL_DATA value:nil];
+ _savedAnalyticsData = nil;
+}
+
+- (NSMutableDictionary *)getBranchAnalyticsData {
+ NSMutableDictionary *analyticsDataObj = _savedAnalyticsData;
+ if (!analyticsDataObj) {
+ analyticsDataObj = (NSMutableDictionary *)[self readObjectFromDefaults:BRANCH_PREFS_KEY_ANALYTICAL_DATA];
+ if (!analyticsDataObj) {
+ analyticsDataObj = [[NSMutableDictionary alloc] init];
+ }
+ }
+ return analyticsDataObj;
+}
+
+- (void)saveContentAnalyticsManifest:(NSDictionary *)cdManifest {
+ [self writeObjectToDefaults:BRANCH_PREFS_KEY_ANALYTICS_MANIFEST value:cdManifest];
+}
+
+- (NSDictionary *)getContentAnalyticsManifest {
+ return (NSDictionary *)[self readObjectFromDefaults:BRANCH_PREFS_KEY_ANALYTICS_MANIFEST];
+}
+
+#pragma mark - Writing To Persistence
+
+- (void)writeIntegerToDefaults:(NSString *)key value:(NSInteger)value {
+ [self writeObjectToDefaults:key value:@(value)];
+}
+
+- (void)writeBoolToDefaults:(NSString *)key value:(BOOL)value {
+ [self writeObjectToDefaults:key value:@(value)];
+}
+
+- (void)writeObjectToDefaults:(NSString *)key value:(NSObject *)value {
+ @synchronized (self) {
+ if (value) {
+ self.persistenceDict[key] = value;
+ }
+ else {
+ [self.persistenceDict removeObjectForKey:key];
+ }
+ [self persistPrefsToDisk];
+ }
+}
+
+- (void)persistPrefsToDisk {
+ @synchronized (self) {
+ if (!self.persistenceDict) return;
+ NSData *data = nil;
+ @try {
+ data = [NSKeyedArchiver archivedDataWithRootObject:self.persistenceDict];
+ }
+ @catch (id exception) {
+ data = nil;
+ BNCLogWarning(@"Exception creating preferences data: %@.", exception);
+ }
+ if (!data) {
+ BNCLogWarning(@"Can't create preferences data.");
+ return;
+ }
+ NSURL *prefsURL = [self.class.URLForPrefsFile copy];
+ NSBlockOperation *newPersistOp = [NSBlockOperation blockOperationWithBlock:^ {
+ NSError *error = nil;
+ [data writeToURL:prefsURL options:NSDataWritingAtomic error:&error];
+ if (error) {
+ BNCLogWarning(@"Failed to persist preferences: %@.", error);
+ }
+ }];
+ [_persistPrefsQueue addOperation:newPersistOp];
+ }
+}
+
++ (void) clearAll {
+ NSURL *prefsURL = [self.URLForPrefsFile copy];
+ if (prefsURL) [[NSFileManager defaultManager] removeItemAtURL:prefsURL error:nil];
+}
+
+#pragma mark - Reading From Persistence
+
+- (NSMutableDictionary *)persistenceDict {
+ @synchronized(self) {
+ if (!_persistenceDict) {
+ NSDictionary *persistenceDict = nil;
+ @try {
+ NSError *error = nil;
+ NSData *data = [NSData dataWithContentsOfURL:self.class.URLForPrefsFile
+ options:0 error:&error];
+ if (!error && data)
+ persistenceDict = [NSKeyedUnarchiver unarchiveObjectWithData:data];
+ }
+ @catch (NSException*) {
+ BNCLogWarning(@"Failed to load preferences from storage.");
+ }
+
+ if ([persistenceDict isKindOfClass:[NSDictionary class]])
+ _persistenceDict = [persistenceDict mutableCopy];
+ else
+ _persistenceDict = [[NSMutableDictionary alloc] init];
+ }
+ return _persistenceDict;
+ }
+}
+
+- (NSObject *)readObjectFromDefaults:(NSString *)key {
+ @synchronized(self) {
+ NSObject *obj = self.persistenceDict[key];
+ return obj;
+ }
+}
+
+- (NSString *)readStringFromDefaults:(NSString *)key {
+ @synchronized(self) {
+ id str = self.persistenceDict[key];
+ if ([str isKindOfClass:[NSNumber class]]) {
+ str = [str stringValue];
+ }
+ return str;
+ }
+}
+
+- (BOOL)readBoolFromDefaults:(NSString *)key {
+ @synchronized(self) {
+ BOOL boo = [self.persistenceDict[key] boolValue];
+ return boo;
+ }
+}
+
+- (NSInteger)readIntegerFromDefaults:(NSString *)key {
+ @synchronized(self) {
+ NSNumber *number = self.persistenceDict[key];
+ if (number != nil) {
+ return [number integerValue];
+ }
+ return NSNotFound;
+ }
+}
+
+#pragma mark - Preferences File URL
+
++ (NSString *)prefsFile_deprecated {
+ NSString * path =
+ [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
+ firstObject]
+ stringByAppendingPathComponent:BRANCH_PREFS_FILE];
+ return path;
+}
+
++ (NSURL* _Nonnull) URLForPrefsFile {
+ NSURL *URL = BNCURLForBranchDirectory();
+ URL = [URL URLByAppendingPathComponent:BRANCH_PREFS_FILE isDirectory:NO];
+ return URL;
+}
+
++ (void) moveOldPrefsFile {
+ NSString* oldPath = self.prefsFile_deprecated;
+ NSURL *oldURL = (oldPath) ? [NSURL fileURLWithPath:self.prefsFile_deprecated] : nil;
+ NSURL *newURL = [self URLForPrefsFile];
+
+ if (!oldURL || !newURL) { return; }
+
+ NSError *error = nil;
+ [[NSFileManager defaultManager]
+ moveItemAtURL:oldURL
+ toURL:newURL
+ error:&error];
+
+ if (error && error.code != NSFileNoSuchFileError) {
+ if (error.code == NSFileWriteFileExistsError) {
+ [[NSFileManager defaultManager]
+ removeItemAtURL:oldURL
+ error:&error];
+ } else {
+ BNCLogError(@"Can't move prefs file: %@.", error);
+ }
+ }
+}
+
++ (void) initialize {
+ if (self == [BNCPreferenceHelper self]) {
+ [self moveOldPrefsFile];
+ }
+}
+
+@end
+
+#pragma mark - BNCURLForBranchDirectory
+
+NSURL* _Null_unspecified BNCCreateDirectoryForBranchURLWithSearchPath_Unthreaded(NSSearchPathDirectory directory) {
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSArray *URLs = [fileManager URLsForDirectory:directory inDomains:NSUserDomainMask | NSLocalDomainMask];
+
+ for (NSURL *URL in URLs) {
+ NSError *error = nil;
+ NSURL *branchURL = [[NSURL alloc] initWithString:@"io.branch" relativeToURL:URL];
+ BOOL success =
+ [fileManager
+ createDirectoryAtURL:branchURL
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error];
+ if (success) {
+ return branchURL;
+ } else {
+ NSLog(@"[branch.io] Info: CreateBranchURL failed: %@ URL: %@.", error, branchURL);
+ }
+ }
+ return nil;
+}
+
+NSURL* _Nonnull BNCURLForBranchDirectory_Unthreaded() {
+ NSArray *kSearchDirectories = @[
+ @(NSApplicationSupportDirectory),
+ @(NSLibraryDirectory),
+ @(NSCachesDirectory),
+ @(NSDocumentDirectory),
+ ];
+
+ for (NSNumber *directory in kSearchDirectories) {
+ NSSearchPathDirectory directoryValue = [directory unsignedLongValue];
+ NSURL *URL = BNCCreateDirectoryForBranchURLWithSearchPath_Unthreaded(directoryValue);
+ if (URL) return URL;
+ }
+
+ // Worst case backup plan:
+ NSString *path = [@"~/Library/io.branch" stringByExpandingTildeInPath];
+ NSURL *branchURL = [NSURL fileURLWithPath:path isDirectory:YES];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ NSError *error = nil;
+ BOOL success =
+ [fileManager
+ createDirectoryAtURL:branchURL
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:&error];
+ if (!success) {
+ NSLog(@"[io.branch] Error: Worst case CreateBranchURL error was: %@ URL: %@.", error, branchURL);
+ }
+ return branchURL;
+}
+
+NSURL* _Nonnull BNCURLForBranchDirectory() {
+ static NSURL *urlForBranchDirectory = nil;
+ static dispatch_once_t onceToken = 0;
+ dispatch_once(&onceToken, ^ {
+ urlForBranchDirectory = BNCURLForBranchDirectory_Unthreaded();
+ });
+ return urlForBranchDirectory;
+}
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSpotlightService.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSpotlightService.h
new file mode 100644
index 00000000000..be3cac3515f
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSpotlightService.h
@@ -0,0 +1,38 @@
+//
+// BNCSpotlightService.h
+// Branch-SDK
+//
+// Created by Parth Kalavadia on 8/10/17.
+// Copyright © 2017 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+@class BranchUniversalObject, BranchLinkProperties;
+
+@interface BNCSpotlightService : NSObject
+
+//Indexing API
+//indexPublicaly
+- (void)indexWithBranchUniversalObject:(BranchUniversalObject* _Nonnull)universalObject
+ linkProperties:(BranchLinkProperties* _Nullable)linkProperties
+ callback:(void (^_Nullable)(BranchUniversalObject * _Nullable universalObject,
+ NSString* _Nullable url,
+ NSError * _Nullable error))completion;
+//indexWithBranchShareLink
+- (void)indexPrivatelyWithBranchUniversalObjects:(NSArray* _Nonnull)universalObjects
+ completion:(void (^_Nullable) (NSArray* _Nullable,
+ NSError* _Nullable))completion;
+
+//Remove indexing API
+- (void)removeSearchableItemsWithIdentifier:(NSString * _Nonnull)identifier
+ callback:(void (^_Nullable)(NSError * _Nullable error))completion;
+
+- (void)removeSearchableItemsWithIdentifiers:(NSArray *_Nonnull)identifiers
+ callback:(void (^_Nullable)(NSError * _Nullable error))completion;
+
+- (void)removeAllBranchSearchableItemsWithCallback:(void (^_Nullable)(NSError * _Nullable error))completion;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSpotlightService.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSpotlightService.m
new file mode 100644
index 00000000000..3ae6826cd3a
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSpotlightService.m
@@ -0,0 +1,510 @@
+//
+// BNCSpotlightService.m
+// Branch-SDK
+//
+// Created by Parth Kalavadia on 8/10/17.
+// Copyright © 2017 Branch Metrics. All rights reserved.
+//
+
+#import "BNCSpotlightService.h"
+#import "Branch.h"
+#import "BNCSystemObserver.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+
+#pragma mark Defensive Declarations
+
+#ifndef CSSearchableItemActivityIdentifier
+#define CSSearchableItemActivityIdentifier @"kCSSearchableItemActivityIdentifier"
+#endif
+
+@interface CSSearchableItemAttributeSetDummyClass : NSObject
+- (void) setKeywords:(NSArray*)keywords;
+- (void) setWeakRelatedUniqueIdentifier:(NSString*)uniqueIdentifier;
+@end
+
+static NSString* const kUTTypeGeneric = @"public.content";
+static NSString* const kDomainIdentifier = @"io.branch.sdk.spotlight";
+
+#pragma mark - BNCSpotlightService
+
+@interface BNCSpotlightService() {
+ dispatch_queue_t _workQueue;
+}
+@property (strong, nonatomic) NSMutableDictionary *userInfo;
+@property (strong, readonly) dispatch_queue_t workQueue;
+@end
+
+#pragma mark - BNCSpotlightService
+
+@implementation BNCSpotlightService
+
+- (void)indexWithBranchUniversalObject:(BranchUniversalObject* _Nonnull)universalObject
+ linkProperties:(BranchLinkProperties* _Nullable)linkProperties
+ callback:(void (^_Nullable)(BranchUniversalObject * _Nullable universalObject,
+ NSString* _Nullable url,
+ NSError * _Nullable error))completion {
+ if ([BNCSystemObserver getOSVersion].floatValue < 9.0) {
+ NSError *error = [NSError branchErrorWithCode:BNCSpotlightNotAvailableError];
+ if (completion) {
+ completion(universalObject,nil,error);
+ }
+ return;
+ }
+ if (!universalObject.title) {
+ NSError *error = [NSError branchErrorWithCode:BNCSpotlightTitleError];
+ if (completion) {
+ completion(universalObject,[BNCPreferenceHelper preferenceHelper].userUrl, error);
+ }
+ return;
+ }
+
+ BranchLinkProperties* spotlightLinkProperties;
+ if (linkProperties == nil) {
+ spotlightLinkProperties = [[BranchLinkProperties alloc] init];
+ }
+ [spotlightLinkProperties setFeature:BNCSpotlightFeature];
+
+ NSURL* thumbnailUrl = [NSURL URLWithString:universalObject.imageUrl];
+ BOOL thumbnailIsRemote = thumbnailUrl && ![thumbnailUrl isFileURL];
+
+ if (thumbnailIsRemote) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSData *thumbnailData = [NSData dataWithContentsOfURL:thumbnailUrl];
+ dispatch_async(dispatch_get_main_queue(), ^{
+
+ [self indexContentWithBranchUniversalObject:universalObject
+ linkProperty:linkProperties
+ thumbnailUrl:thumbnailUrl
+ thumbnailData:thumbnailData
+ callback:^(NSString * _Nullable url, NSError * _Nullable error) {
+ if (completion)
+ completion(universalObject,url,error);
+ }];
+ });
+ });
+ }
+ else {
+ [self indexContentWithBranchUniversalObject:universalObject
+ linkProperty:linkProperties
+ thumbnailUrl:thumbnailUrl
+ thumbnailData:nil
+ callback:^(NSString * _Nullable url, NSError * _Nullable error) {
+ if (completion)
+ completion(universalObject,url,error);
+ }];
+ }
+}
+
+- (void)indexContentWithBranchUniversalObject:(BranchUniversalObject*)universalObject
+ linkProperty:(BranchLinkProperties *)linkProperty
+ thumbnailUrl:(NSURL *)thumbnailUrl
+ thumbnailData:(NSData *)thumbnailData
+ callback:(void (^_Nullable)(NSString* _Nullable url, NSError * _Nullable error))completion {
+
+ if (!linkProperty) {
+ linkProperty = [[BranchLinkProperties alloc] init];
+ linkProperty.channel = @"Spotlight Search";
+ }
+
+ if (universalObject.locallyIndex) {
+ NSString *dynamicUrl = [universalObject getLongUrlWithChannel:nil
+ andTags:nil
+ andFeature:BNCSpotlightFeature
+ andStage:nil
+ andAlias:nil];
+
+ id attributes = [self attributeSetWithUniversalObject:universalObject
+ thumbnail:thumbnailData
+ url:dynamicUrl];
+ NSDictionary *indexingParams = @{@"title": universalObject.title,
+ @"url": dynamicUrl,
+ @"spotlightId": dynamicUrl,
+ @"userInfo": [universalObject.contentMetadata.customMetadata mutableCopy],
+ @"keywords": [NSSet setWithArray:universalObject.keywords],
+ @"attributeSet": attributes
+ };
+
+ [self indexUsingSearchableItem:indexingParams
+ thumbnailData:thumbnailData
+ callback:^(NSString * _Nullable url, NSError * _Nullable error) {
+ if (completion)
+ completion(url,error);
+ }];
+
+ } else {
+
+ __weak __typeof(self) weakSelf = self;
+ [universalObject getShortUrlWithLinkProperties:linkProperty
+ andCallback:^(NSString * _Nullable url, NSError * _Nullable error) {
+ __strong __typeof(self) strongSelf = weakSelf;
+ if (!strongSelf || !url || error) {
+ if (completion) completion([BNCPreferenceHelper preferenceHelper].userUrl, error);
+ return;
+ }
+ id attributes =
+ [strongSelf attributeSetWithUniversalObject:universalObject thumbnail:thumbnailData url:url];
+ NSMutableDictionary *indexingParams = [NSMutableDictionary new];
+ indexingParams[@"title"] = universalObject.title;
+ indexingParams[@"url"] = url;
+ indexingParams[@"spotlightId"] = url;
+ indexingParams[@"userInfo"] = [universalObject.contentMetadata.customMetadata mutableCopy];
+ indexingParams[@"keywords"] = [NSSet setWithArray:universalObject.keywords];
+ indexingParams[@"attributeSet"] = attributes;
+ [self indexUsingNSUserActivity:indexingParams];
+ if (completion) completion(url, nil);
+ }
+ ];
+
+ }
+}
+
+- (id)attributeSetWithUniversalObject:(BranchUniversalObject*)universalObject
+ thumbnail:(NSData*)thumbnailData
+ url:(NSString*)url {
+
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
+ NSString *type = universalObject.contentMetadata.contentSchema ?: (NSString *)kUTTypeGeneric;
+
+ id CSSearchableItemAttributeSetClass = NSClassFromString(@"CSSearchableItemAttributeSet");
+ if (!CSSearchableItemAttributeSetClass)
+ return nil;
+
+ id attributes = [CSSearchableItemAttributeSetClass alloc];
+ if (!attributes || ![attributes respondsToSelector:@selector(initWithItemContentType:)])
+ return nil;
+ attributes = [attributes initWithItemContentType:type];
+
+ #define safePerformSelector(_selector, parameter) { \
+ if (parameter != nil && [attributes respondsToSelector:@selector(_selector)]) { \
+ [attributes _selector parameter]; \
+ } \
+ }
+
+ safePerformSelector(setTitle:, universalObject.title);
+ safePerformSelector(setContentDescription:, universalObject.contentDescription);
+ NSURL* thumbnailUrl = [NSURL URLWithString:universalObject.imageUrl];
+ BOOL thumbnailIsRemote = thumbnailUrl && ![thumbnailUrl isFileURL];
+ if (!thumbnailIsRemote) {
+ safePerformSelector(setThumbnailURL:, thumbnailUrl);
+ }
+ safePerformSelector(setThumbnailData:, thumbnailData);
+ safePerformSelector(setContentURL:, [NSURL URLWithString:url]);
+ if (universalObject.keywords && [attributes respondsToSelector:@selector(setKeywords:)])
+ [((CSSearchableItemAttributeSetDummyClass*)attributes) setKeywords:universalObject.keywords];
+ safePerformSelector(setWeakRelatedUniqueIdentifier:, universalObject.canonicalIdentifier);
+
+ #undef safePerformSelector
+
+ return attributes;
+
+#else
+
+ return nil;
+
+#endif
+}
+
+- (void)indexPrivatelyWithBranchUniversalObjects:(NSArray* _Nonnull)universalObjects
+ completion:(void (^_Nullable) (NSArray * _Nullable universalObjects,
+ NSError * _Nullable error))completion {
+ BOOL isIndexingAvailable = NO;
+ Class CSSearchableIndexClass = NSClassFromString(@"CSSearchableIndex");
+ Class CSSearchableItemClass = NSClassFromString(@"CSSearchableItem");
+ SEL isIndexingAvailableSelector = NSSelectorFromString(@"isIndexingAvailable");
+ isIndexingAvailable =
+ ((BOOL (*)(id, SEL))[CSSearchableIndexClass methodForSelector:isIndexingAvailableSelector])
+ (CSSearchableIndexClass, isIndexingAvailableSelector);
+
+ #define IndexingNotAvailable() { \
+ NSError *error = [NSError branchErrorWithCode:BNCSpotlightNotAvailableError];\
+ if (completion) {\
+ completion(nil,error);\
+ }\
+ return;\
+ }
+
+ if (!isIndexingAvailable ||
+ !CSSearchableIndexClass ||
+ ![CSSearchableIndexClass respondsToSelector:@selector(defaultSearchableIndex)] ||
+ !CSSearchableItemClass) {
+ IndexingNotAvailable();
+ }
+ dispatch_group_t workGroup = dispatch_group_create();
+ NSMutableArray *searchableItems = [[NSMutableArray alloc] init];
+ NSMutableDictionary *mapSpotlightIdentifier =
+ [[NSMutableDictionary alloc] init];
+
+ for (BranchUniversalObject* universalObject in universalObjects) {
+ dispatch_group_enter(workGroup);
+ NSString* dynamicUrl = [universalObject getLongUrlWithChannel:nil
+ andTags:nil
+ andFeature:BNCSpotlightFeature
+ andStage:nil andAlias:nil];
+ if (!dynamicUrl) continue;
+ mapSpotlightIdentifier[dynamicUrl] = universalObject;
+ NSURL* thumbnailUrl = [NSURL URLWithString:universalObject.imageUrl];
+ BOOL thumbnailIsRemote = thumbnailUrl && ![thumbnailUrl isFileURL];
+ if (thumbnailIsRemote) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSData *thumbnailData = [NSData dataWithContentsOfURL:thumbnailUrl];
+ dispatch_async(dispatch_get_main_queue(), ^{
+
+ id attributes = [self attributeSetWithUniversalObject:universalObject
+ thumbnail:thumbnailData
+ url:dynamicUrl];
+
+ id item = [CSSearchableItemClass alloc];
+ item = [item initWithUniqueIdentifier:dynamicUrl
+ domainIdentifier:kDomainIdentifier
+ attributeSet:attributes];
+
+ [searchableItems addObject:item];
+ dispatch_group_leave(workGroup);
+ });
+ });
+ }
+ else {
+ id attributes = [self attributeSetWithUniversalObject:universalObject
+ thumbnail:nil
+ url:dynamicUrl];
+ id item = [CSSearchableItemClass alloc];
+ item = [item initWithUniqueIdentifier:dynamicUrl
+ domainIdentifier:kDomainIdentifier
+ attributeSet:attributes];
+
+ [searchableItems addObject:item];
+ dispatch_group_leave(workGroup);
+ }
+ }
+
+ dispatch_group_notify(workGroup, dispatch_get_main_queue(), ^{
+ id index = [CSSearchableIndexClass defaultSearchableIndex];
+
+ if (![index respondsToSelector:@selector(indexSearchableItems:completionHandler:)]) {
+ IndexingNotAvailable();
+ }
+
+ [index indexSearchableItems:searchableItems completionHandler:^(NSError * _Nullable error) {
+ if (!error) {
+ if (completion)
+ completion(universalObjects,nil);
+ }
+ else {
+ if (completion)
+ completion(nil,error);
+ }
+
+ }];
+ });
+ #undef IndexingNotAvailable
+}
+
+- (dispatch_queue_t) workQueue {
+ @synchronized (self) {
+ if (!_workQueue)
+ _workQueue = dispatch_queue_create("io.branch.sdk.spotlight.indexing", DISPATCH_QUEUE_CONCURRENT);
+ return _workQueue;
+ }
+}
+
+- (void)indexUsingNSUserActivity:(NSDictionary *)params {
+ self.userInfo = params[@"userInfo"];
+ self.userInfo[CSSearchableItemActivityIdentifier] = params[@"spotlightId"];
+ UIViewController *activeViewController = [UIViewController bnc_currentViewController];
+ if (!activeViewController) {
+ // if no view controller, don't index. Current use case: iMessage extensions
+ return;
+ }
+ NSString *uniqueIdentifier = [NSString stringWithFormat:@"io.branch.%@", [[NSBundle mainBundle] bundleIdentifier]];
+ // Can't create any weak references here to the userActivity, otherwise it will not index.
+ activeViewController.userActivity = [[NSUserActivity alloc] initWithActivityType:uniqueIdentifier];
+ activeViewController.userActivity.delegate = self;
+ activeViewController.userActivity.title = params[@"title"];
+ activeViewController.userActivity.webpageURL = [NSURL URLWithString:params[@"url"]];
+ activeViewController.userActivity.eligibleForSearch = YES;
+ activeViewController.userActivity.eligibleForPublicIndexing = YES; // TODO: Update with the new indexPublically.
+ activeViewController.userActivity.userInfo = self.userInfo; // This alone doesn't pass userInfo through
+ activeViewController.userActivity.requiredUserInfoKeys = [NSSet setWithArray:self.userInfo.allKeys]; // This along with the delegate method userActivityWillSave, however, seem to force the userInfo to come through.
+ activeViewController.userActivity.keywords = params[@"keywords"];
+ SEL setContentAttributeSetSelector = NSSelectorFromString(@"setContentAttributeSet:");
+ ((void (*)(id, SEL, id))[activeViewController.userActivity methodForSelector:setContentAttributeSetSelector])(activeViewController.userActivity, setContentAttributeSetSelector, params[@"attributeSet"]);
+
+ [activeViewController.userActivity becomeCurrent];
+
+}
+
+- (void)indexUsingSearchableItem:(NSDictionary*)indexingParam
+ thumbnailData:(NSData*)thumbnailData
+ callback:(void (^_Nullable)(NSString* _Nullable url, NSError * _Nullable error))completion {
+
+ BOOL isIndexingAvailable = NO;
+ Class CSSearchableIndexClass = NSClassFromString(@"CSSearchableIndex");
+ SEL isIndexingAvailableSelector = NSSelectorFromString(@"isIndexingAvailable");
+ isIndexingAvailable =
+ ((BOOL (*)(id, SEL))[CSSearchableIndexClass methodForSelector:isIndexingAvailableSelector])
+ (CSSearchableIndexClass, isIndexingAvailableSelector);
+
+ #define IndexingNotAvalable() { \
+ NSError *error = [NSError branchErrorWithCode:BNCSpotlightNotAvailableError];\
+ if (completion) {\
+ completion(nil,error);\
+ }\
+ return;\
+ }
+
+ if (!isIndexingAvailable) {
+ IndexingNotAvalable();
+ }
+
+ NSString *dynamicUrl = indexingParam[@"url"];
+ Class CSSearchableItemClass = NSClassFromString(@"CSSearchableItem");
+
+ if (!CSSearchableItemClass) {
+ IndexingNotAvalable();
+ }
+
+ id item = [CSSearchableItemClass alloc];
+
+ if (!item ||
+ ![item respondsToSelector:@selector(initWithUniqueIdentifier:domainIdentifier:attributeSet:)]) {
+ IndexingNotAvalable();
+ }
+
+ item = [item initWithUniqueIdentifier:indexingParam[@"url"]
+ domainIdentifier:kDomainIdentifier
+ attributeSet:indexingParam[@"attributeSet"]];
+
+ if (CSSearchableIndexClass &&
+ [CSSearchableIndexClass respondsToSelector:@selector(defaultSearchableIndex)]){
+ id defaultSearchableIndex = [CSSearchableIndexClass defaultSearchableIndex];
+ if ([defaultSearchableIndex respondsToSelector:@selector(indexSearchableItems:completionHandler:)]) {
+ [defaultSearchableIndex indexSearchableItems:@[item]
+ completionHandler: ^(NSError * __nullable error) {
+ NSString *url = error == nil?dynamicUrl:nil;
+ if (completion) {
+ completion(url, error);
+ }
+ }];
+ }
+ else {
+ IndexingNotAvalable();
+ }
+ }
+ else {
+ IndexingNotAvalable();
+ }
+ #undef IndexingNotAvalable
+}
+
+#pragma mark - UserActivity Delegate Methods
+
+- (void)userActivityWillSave:(NSUserActivity *)userActivity {
+ [userActivity addUserInfoEntriesFromDictionary:self.userInfo];
+}
+
+#pragma remove private content from spotlight indexed using Searchable Item
+
+- (void)removeSearchableItemsWithIdentifier:(NSString * _Nonnull)identifier
+ callback:(void (^_Nullable)(NSError * _Nullable error))completion {
+ if (identifier == nil) {
+ NSError *error = [NSError branchErrorWithCode:BNCSpotlightIdentifierError
+ localizedMessage:@"Spotlight indentifier not available"];
+ if (completion) completion(error);
+ return;
+ }
+ [self removeSearchableItemsWithIdentifiers:@[identifier] callback:^(NSError * _Nullable error) {
+ if (completion)
+ completion(error);
+ }];
+}
+
+- (void)removeSearchableItemsWithIdentifiers:(NSArray *_Nonnull)identifiers
+ callback:(void (^_Nullable)(NSError * _Nullable error))completion {
+
+ BOOL isIndexingAvailable = NO;
+ Class CSSearchableIndexClass = NSClassFromString(@"CSSearchableIndex");
+ SEL isIndexingAvailableSelector = NSSelectorFromString(@"isIndexingAvailable");
+ isIndexingAvailable =
+ ((BOOL (*)(id, SEL))[CSSearchableIndexClass methodForSelector:isIndexingAvailableSelector])
+ (CSSearchableIndexClass, isIndexingAvailableSelector);
+
+ #define IndexingNotAvalable() { \
+ NSError *error = [NSError branchErrorWithCode:BNCSpotlightNotAvailableError];\
+ if (completion) {\
+ completion(error);\
+ }\
+ return;\
+ }
+
+ if (!isIndexingAvailable) {
+ IndexingNotAvalable();
+ }
+ else {
+ if (CSSearchableIndexClass &&
+ [CSSearchableIndexClass respondsToSelector:@selector(defaultSearchableIndex)]){
+ id defaultSearchableIndex = [CSSearchableIndexClass defaultSearchableIndex];
+ if ([defaultSearchableIndex respondsToSelector:@selector(deleteSearchableItemsWithIdentifiers:completionHandler:)]) {
+ [defaultSearchableIndex deleteSearchableItemsWithIdentifiers:identifiers
+ completionHandler:^(NSError * _Nullable error) {
+ if (completion)
+ completion(error);
+ }];
+ }
+ else {
+ IndexingNotAvalable();
+ }
+ }
+ else {
+ IndexingNotAvalable();
+ }
+ }
+
+ #undef IndexingNotAvalable
+}
+
+- (void)removeAllBranchSearchableItemsWithCallback:(void (^_Nullable)(NSError * _Nullable error))completion {
+
+ BOOL isIndexingAvailable = NO;
+ Class CSSearchableIndexClass = NSClassFromString(@"CSSearchableIndex");
+ SEL isIndexingAvailableSelector = NSSelectorFromString(@"isIndexingAvailable");
+ isIndexingAvailable =
+ ((BOOL (*)(id, SEL))[CSSearchableIndexClass methodForSelector:isIndexingAvailableSelector])
+ (CSSearchableIndexClass, isIndexingAvailableSelector);
+
+ #define IndexingNotAvalable() { \
+ NSError *error = [NSError branchErrorWithCode:BNCSpotlightNotAvailableError];\
+ if (completion) {\
+ completion(error);\
+ }\
+ return;\
+ }
+
+ if (!isIndexingAvailable) {
+ IndexingNotAvalable();
+ }
+ else {
+ id CSSearchableIndexClass = NSClassFromString(@"CSSearchableIndex");
+ if (CSSearchableIndexClass &&
+ [CSSearchableIndexClass respondsToSelector:@selector(defaultSearchableIndex)]){
+ id defaultSearchableIndex = [CSSearchableIndexClass defaultSearchableIndex];
+ if ([defaultSearchableIndex respondsToSelector:
+ @selector(deleteSearchableItemsWithDomainIdentifiers:completionHandler:)]) {
+ [defaultSearchableIndex deleteSearchableItemsWithDomainIdentifiers:@[kDomainIdentifier]
+ completionHandler:^(NSError * _Nullable error) {
+ if (completion)
+ completion(error);
+ }];
+ }
+ else {
+ IndexingNotAvalable();
+ }
+ }
+ else {
+ IndexingNotAvalable();
+ }
+ }
+ #undef IndexingNotAvalable
+}
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCStrongMatchHelper.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCStrongMatchHelper.h
new file mode 100644
index 00000000000..74be6c7fff6
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCStrongMatchHelper.h
@@ -0,0 +1,23 @@
+//
+// BNCStrongMatchHelper.h
+// Branch-TestBed
+//
+// Created by Derrick Staten on 8/26/15.
+// Copyright © 2015 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+@interface BNCStrongMatchHelper : NSObject
+
++ (BNCStrongMatchHelper *)strongMatchHelper;
+- (void)createStrongMatchWithBranchKey:(NSString *)branchKey;
++ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey
+ redirectUrl:(NSString *)redirectUrl;
+
+@property (atomic, assign, readonly) BOOL shouldDelayInstallRequest;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCStrongMatchHelper.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCStrongMatchHelper.m
new file mode 100644
index 00000000000..63f77e8c3e9
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCStrongMatchHelper.m
@@ -0,0 +1,359 @@
+//
+// BNCStrongMatchHelper.m
+// Branch-TestBed
+//
+// Created by Derrick Staten on 8/26/15.
+// Copyright © 2015 Branch Metrics. All rights reserved.
+//
+
+
+#import "BNCStrongMatchHelper.h"
+#import "BNCConfig.h"
+#import "BNCPreferenceHelper.h"
+#import "BNCSystemObserver.h"
+#import "BranchConstants.h"
+#import "BNCLog.h"
+#import "UIViewController+Branch.h"
+#import
+
+
+#pragma mark BNCStrongMatchHelper iOS 8.0
+
+
+// This is a stub the class for older Xcode versions. These methods don't do anything.
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED < 90000 // iOS < 9.0
+
+@implementation BNCStrongMatchHelper
+
++ (BNCStrongMatchHelper *)strongMatchHelper {
+ return nil;
+}
+
+- (void)createStrongMatchWithBranchKey:(NSString *)branchKey {
+}
+
+- (BOOL)shouldDelayInstallRequest {
+ return NO;
+}
+
++ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey
+ redirectUrl:(NSString *)redirectUrl {
+ return nil;
+}
+
+@end
+
+
+#else // ------------------------------------------------------------------------------ iOS >= 9.0
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+
+#if __has_feature(modules)
+@import SafariServices;
+#else
+#import
+#endif
+
+
+#pragma mark - BNCMatchView
+
+
+@interface BNCMatchView : UIView
+@end
+
+
+@implementation BNCMatchView
+
+- (instancetype) initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ self.alpha = 0.0;
+ return self;
+}
+
+- (void) setAlpha:(CGFloat)alpha {
+ [super setAlpha:0.0];
+}
+
+- (CGFloat) alpha {
+ return 1.0;
+}
+
+@end
+
+
+#pragma mark - BNCMatchViewController
+
+
+// This is a class interface that will be dynamically subclassed from SFSafariViewController
+@interface BNCMatchViewController : UIViewController
+@end
+
+
+@implementation BNCMatchViewController
+
+- (instancetype) initWithURL:(NSURL*)URL {
+ self = [super init];
+ return self;
+}
+
+- (BOOL) canBecomeFirstResponder {
+ return NO;
+}
+
+- (BOOL) becomeFirstResponder {
+ return NO;
+}
+
+- (UIResponder*) nextResponder {
+ return nil;
+}
+
+- (void) setDelegate:(id)delegate {
+}
+
+@end
+
+
+#pragma mark - BNCStrongMatchHelper iOS 9.0
+
+
+@interface BNCStrongMatchHelper ()
+@property (assign, nonatomic) BOOL requestInProgress;
+@property (readwrite) BOOL shouldDelayInstallRequest;
+@property (strong, nonatomic) UIWindow *primaryWindow;
+@property (strong, nonatomic) BNCMatchView *matchView;
+@property (strong, nonatomic) BNCMatchViewController *matchViewController;
+@end
+
+
+@implementation BNCStrongMatchHelper
+
++ (BNCStrongMatchHelper *)strongMatchHelper {
+ static BNCStrongMatchHelper *strongMatchHelper;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ strongMatchHelper = [[BNCStrongMatchHelper alloc] init];
+ });
+
+ return strongMatchHelper;
+}
+
++ (NSURL *)getUrlForCookieBasedMatchingWithBranchKey:(NSString *)branchKey
+ redirectUrl:(NSString *)redirectUrl {
+ if (!branchKey || !self.cookiesAvailableInOS) {
+ return nil;
+ }
+
+ NSString *appDomainLinkURL;
+ id ret = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"branch_app_domain"];
+ if (ret) {
+ if ([ret isKindOfClass:[NSString class]]) {
+ appDomainLinkURL = ret;
+ if (!([appDomainLinkURL hasPrefix:@"https:"] || [appDomainLinkURL hasPrefix:@"http:"]))
+ appDomainLinkURL = [NSString stringWithFormat:@"https://%@", ret];
+ }
+ } else {
+ appDomainLinkURL = BNC_LINK_URL;
+ }
+ NSMutableString *urlString =
+ [[NSMutableString alloc] initWithFormat:@"%@/_strong_match?os=%@",
+ appDomainLinkURL, [BNCSystemObserver getOS]];
+
+ BNCPreferenceHelper *preferenceHelper = [BNCPreferenceHelper preferenceHelper];
+ BOOL isRealHardwareId;
+ NSString *hardwareIdType;
+ NSString *hardwareId =
+ [BNCSystemObserver getUniqueHardwareId:&isRealHardwareId
+ isDebug:preferenceHelper.isDebug andType:&hardwareIdType];
+ if (!hardwareId || !isRealHardwareId) {
+ BNCLogWarning(@"Cannot use cookie-based matching while setDebug is enabled.");
+ return nil;
+ }
+
+ [urlString appendFormat:@"&%@=%@", BRANCH_REQUEST_KEY_HARDWARE_ID, hardwareId];
+
+ if (preferenceHelper.deviceFingerprintID) {
+ [urlString appendFormat:@"&%@=%@",
+ BRANCH_REQUEST_KEY_DEVICE_FINGERPRINT_ID,
+ preferenceHelper.deviceFingerprintID];
+ }
+
+ if ([BNCSystemObserver getAppVersion]) {
+ [urlString appendFormat:@"&%@=%@",
+ BRANCH_REQUEST_KEY_APP_VERSION,
+ [BNCSystemObserver getAppVersion]];
+ }
+
+ [urlString appendFormat:@"&branch_key=%@", branchKey];
+ [urlString appendFormat:@"&sdk=ios%@", BNC_SDK_VERSION];
+
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+ if (redirectUrl) {
+ [urlString appendFormat:@"&redirect_url=%@",
+ [redirectUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ }
+
+ NSString *escapedURL =
+ [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ return [NSURL URLWithString:escapedURL];
+
+ #pragma clang diagnostic pop
+}
+
++ (BOOL)cookiesAvailableInOS {
+ return [UIDevice currentDevice].systemVersion.doubleValue < 11.0;
+}
+
+- (void)createStrongMatchWithBranchKey:(NSString *)branchKey {
+ if (!self.class.cookiesAvailableInOS) return;
+
+ @synchronized (self) {
+ if (self.requestInProgress) return;
+
+ NSInteger const ABOUT_30_DAYS_TIME_IN_SECONDS = 60 * 60 * 24 * 30;
+ NSDate *thirtyDaysAgo = [NSDate dateWithTimeIntervalSinceNow:-ABOUT_30_DAYS_TIME_IN_SECONDS];
+ NSDate *lastCheck = [BNCPreferenceHelper preferenceHelper].lastStrongMatchDate;
+ if ([lastCheck compare:thirtyDaysAgo] == NSOrderedDescending) return;
+
+ NSURL *strongMatchUrl =
+ [BNCStrongMatchHelper
+ getUrlForCookieBasedMatchingWithBranchKey:branchKey
+ redirectUrl:nil];
+ if (!strongMatchUrl) return;
+
+ self.requestInProgress = YES;
+ self.shouldDelayInstallRequest = YES;
+
+ // Must be on next run loop to avoid a warning
+ dispatch_async(dispatch_get_main_queue(), ^{
+
+ if (![self willLoadViewControllerWithURL:strongMatchUrl]) {
+ self.shouldDelayInstallRequest = NO;
+ self.requestInProgress = NO;
+ return;
+ }
+
+ // Give enough time for Safari to load the request (optimized for 3G)
+ dispatch_after(
+ dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)),
+ dispatch_get_main_queue(),
+ ^{ [self unloadViewController]; }
+ );
+ });
+ }
+}
+
+- (BOOL) subclass:(Class)subclass selector:(SEL)selector {
+ Class templateClass = objc_getClass("BNCMatchViewController");
+ if (!templateClass) return NO;
+
+ Method method = class_getInstanceMethod(templateClass, selector);
+ if (!method) return NO;
+
+ const char * typeEncoding = method_getTypeEncoding(method);
+ if (!typeEncoding) return NO;
+
+ IMP implementation = class_getMethodImplementation(templateClass, selector);
+ if (!implementation) return NO;
+
+ class_addMethod(subclass, selector, implementation, typeEncoding);
+ return YES;
+}
+
+- (BOOL) willLoadViewControllerWithURL:(NSURL*)matchURL {
+ if (self.primaryWindow) return NO;
+ if (!([matchURL.scheme isEqualToString:@"https"] || [matchURL.scheme isEqualToString:@"http"]))
+ return NO;
+
+ // Dynamically subclass the SFSafariViewController if available.
+ // This allows us to compile and link to an app that doesn't
+ // include SafariServices, but is also able to compile and link
+ // when it is.
+
+ Class SFSafariViewControllerClass = NSClassFromString(@"SFSafariViewController");
+ if (!SFSafariViewControllerClass) {
+ BNCLogWarning(@"cookieBasedMatching is enabled but SafariServices framework is not available.");
+ return NO;
+ }
+
+ Class BNCMatchViewControllerSubclass = NSClassFromString(@"BNCMatchViewController_Safari");
+ if (!BNCMatchViewControllerSubclass) {
+ // The class isn't registered. Create it:
+
+ BNCMatchViewControllerSubclass =
+ objc_allocateClassPair(SFSafariViewControllerClass, "BNCMatchViewController_Safari", 0);
+ if (!BNCMatchViewControllerSubclass) return NO;
+
+ BOOL fail = NO;
+ fail |= ![self subclass:BNCMatchViewControllerSubclass selector:@selector(becomeFirstResponder)];
+ fail |= ![self subclass:BNCMatchViewControllerSubclass selector:@selector(canBecomeFirstResponder)];
+ fail |= ![self subclass:BNCMatchViewControllerSubclass selector:@selector(nextResponder)];
+ if (fail) {
+ objc_disposeClassPair(BNCMatchViewControllerSubclass);
+ return NO;
+ }
+ objc_registerClassPair(BNCMatchViewControllerSubclass);
+ }
+
+ BNCLogDebugSDK(@"Safari is initializing.");
+ self.primaryWindow = [UIViewController bnc_currentWindow];
+
+ @try {
+ self.matchViewController = [[BNCMatchViewControllerSubclass alloc] initWithURL:matchURL];
+ }
+ @catch (id e) {
+ BNCLogError(@"Can't create BNCMatchViewController with URL '%@': %@.", matchURL, e);
+ self.matchViewController = nil;
+ }
+ if (!self.matchViewController) return NO;
+
+ self.matchViewController.delegate = self;
+ self.matchViewController.view.frame = self.primaryWindow.bounds;
+
+ self.matchView = [[BNCMatchView alloc] initWithFrame:self.primaryWindow.bounds];
+ self.matchView.alpha = 1.0;
+ [self.matchView addSubview:self.matchViewController.view];
+
+ UIViewController *rootViewController = [self.primaryWindow.rootViewController bnc_currentViewController];
+
+ [rootViewController addChildViewController:self.matchViewController];
+ UIView *parentView = rootViewController.view ?: self.primaryWindow;
+ [parentView insertSubview:self.matchView atIndex:0];
+
+ [self.matchViewController didMoveToParentViewController:rootViewController];
+
+ return YES;
+}
+
+- (void) unloadViewController {
+ BNCLogDebugSDK(@"Safari unloadViewController called.");
+
+ [self.matchViewController willMoveToParentViewController:nil];
+ [self.matchViewController.view removeFromSuperview];
+ [self.matchViewController removeFromParentViewController];
+ self.matchViewController.delegate = nil;
+ self.matchViewController = nil;
+
+ [self.matchView removeFromSuperview];
+ self.matchView = nil;
+
+ self.primaryWindow = nil;
+
+ [BNCPreferenceHelper preferenceHelper].lastStrongMatchDate = [NSDate date];
+ self.shouldDelayInstallRequest = NO;
+ self.requestInProgress = NO;
+}
+
+- (void)safariViewController:(SFSafariViewController *)controller
+ didCompleteInitialLoad:(BOOL)didLoadSuccessfully {
+ BNCLogDebugSDK(@"Safari did load. Success: %d.", didLoadSuccessfully);
+ [self unloadViewController];
+}
+
+@end
+
+#pragma clang diagnostic pop
+#endif // ------------------------------------------------------------------------------ iOS >= 9.0
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSystemObserver.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSystemObserver.h
new file mode 100644
index 00000000000..8fe1097db65
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSystemObserver.h
@@ -0,0 +1,35 @@
+//
+// BNCSystemObserver.h
+// Branch-SDK
+//
+// Created by Alex Austin on 6/5/14.
+// Copyright (c) 2014 Branch Metrics. All rights reserved.
+//
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+@interface BNCSystemObserver : NSObject
+
++ (NSString *)getUniqueHardwareId:(BOOL *)isReal
+ isDebug:(BOOL)debug
+ andType:(NSString *__autoreleasing*)type;
++ (NSString *)getVendorId;
++ (NSString *)getDefaultUriScheme;
++ (NSString *)getAppVersion;
++ (NSString *)getBundleID;
++ (NSString *)getTeamIdentifier;
++ (NSString *)getBrand;
++ (NSString *)getModel;
++ (NSString *)getOS;
++ (NSString *)getOSVersion;
++ (NSNumber *)getScreenWidth;
++ (NSNumber *)getScreenHeight;
++ (BOOL)isSimulator;
++ (BOOL)adTrackingSafe;
++ (NSString*) getAdId;
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSystemObserver.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSystemObserver.m
new file mode 100644
index 00000000000..655e8672549
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCSystemObserver.m
@@ -0,0 +1,172 @@
+//
+// BNCSystemObserver.m
+// Branch-SDK
+//
+// Created by Alex Austin on 6/5/14.
+// Copyright (c) 2014 Branch Metrics. All rights reserved.
+//
+
+#import "BNCPreferenceHelper.h"
+#import "BNCSystemObserver.h"
+#import "BNCLog.h"
+#if __has_feature(modules)
+@import UIKit;
+@import SystemConfiguration;
+@import Darwin.POSIX.sys.utsname;
+#else
+#import
+#import
+#import
+#endif
+
+@implementation BNCSystemObserver
+
++ (NSString *)getUniqueHardwareId:(BOOL *)isReal
+ isDebug:(BOOL)debug
+ andType:(NSString *__autoreleasing*)type {
+ NSString *uid = nil;
+ *isReal = YES;
+
+ if (!debug) {
+ uid = [self getAdId];
+ *type = @"idfa";
+ }
+
+ if (!uid && NSClassFromString(@"UIDevice") && !debug) {
+ uid = [[UIDevice currentDevice].identifierForVendor UUIDString];
+ *type = @"vendor_id";
+ }
+
+ if (!uid) {
+ uid = [[NSUUID UUID] UUIDString];
+ *type = @"random";
+ *isReal = NO;
+ }
+
+ return uid;
+}
+
++ (NSString*) getAdId {
+ NSString *uid = nil;
+
+ Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager");
+ if (ASIdentifierManagerClass) {
+ SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
+ id sharedManager =
+ ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])
+ (ASIdentifierManagerClass, sharedManagerSelector);
+ SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier");
+ NSUUID *uuid =
+ ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])
+ (sharedManager, advertisingIdentifierSelector);
+ uid = [uuid UUIDString];
+ // limit ad tracking is enabled. iOS 10+
+ if ([uid isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
+ uid = nil;
+ }
+ }
+
+ return uid;
+}
+
++ (NSString *)getVendorId {
+ NSString *vendorId = nil;
+
+ if (NSClassFromString(@"UIDevice")) {
+ vendorId = [[UIDevice currentDevice].identifierForVendor UUIDString];
+ }
+
+ return vendorId;
+}
+
++ (BOOL)adTrackingSafe {
+ Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager");
+ if (ASIdentifierManagerClass) {
+ SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
+ id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector);
+ SEL advertisingEnabledSelector = NSSelectorFromString(@"isAdvertisingTrackingEnabled");
+ BOOL enabled = ((BOOL (*)(id, SEL))[sharedManager methodForSelector:advertisingEnabledSelector])(sharedManager, advertisingEnabledSelector);
+ return enabled;
+ }
+ return YES;
+}
+
++ (NSString *)getDefaultUriScheme {
+ NSArray *urlTypes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"];
+
+ for (NSDictionary *urlType in urlTypes) {
+ NSArray *urlSchemes = [urlType objectForKey:@"CFBundleURLSchemes"];
+ for (NSString *uriScheme in urlSchemes) {
+ if ([uriScheme hasPrefix:@"fb"]) continue; // Facebook
+ if ([uriScheme hasPrefix:@"db"]) continue; // DB?
+ if ([uriScheme hasPrefix:@"twitterkit-"]) continue; // Twitter
+ if ([uriScheme hasPrefix:@"pdk"]) continue; // Pinterest
+ if ([uriScheme hasPrefix:@"pin"]) continue; // Pinterest
+ if ([uriScheme hasPrefix:@"com.googleusercontent.apps"]) continue; // Google
+
+ // Otherwise this must be it!
+ return uriScheme;
+ }
+ }
+ return nil;
+}
+
++ (NSString *)getAppVersion {
+ return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
+}
+
++ (NSString *)getBundleID {
+ return [[NSBundle mainBundle] bundleIdentifier];
+}
+
++ (NSString *)getTeamIdentifier {
+ NSString *teamWithDot = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppIdentifierPrefix"];
+ if (teamWithDot.length) {
+ return [teamWithDot substringToIndex:([teamWithDot length] - 1)];
+ }
+ return nil;
+}
+
++ (NSString *)getBrand {
+ return @"Apple";
+}
+
++ (NSString *)getModel {
+ struct utsname systemInfo;
+ uname(&systemInfo);
+
+ return [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
+}
+
++ (BOOL)isSimulator {
+ #if (TARGET_OS_SIMULATOR)
+ return YES;
+ #else
+ return NO;
+ #endif
+}
+
++ (NSString *)getOS {
+ return @"iOS";
+}
+
++ (NSString *)getOSVersion {
+ UIDevice *device = [UIDevice currentDevice];
+ return [device systemVersion];
+}
+
++ (NSNumber *)getScreenWidth {
+ UIScreen *mainScreen = [UIScreen mainScreen];
+ CGFloat scaleFactor = mainScreen.scale;
+ CGFloat width = mainScreen.bounds.size.width * scaleFactor;
+ return [NSNumber numberWithInteger:(NSInteger)width];
+}
+
++ (NSNumber *)getScreenHeight {
+ UIScreen *mainScreen = [UIScreen mainScreen];
+ CGFloat scaleFactor = mainScreen.scale;
+ CGFloat height = mainScreen.bounds.size.height * scaleFactor;
+ return [NSNumber numberWithInteger:(NSInteger)height];
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCThreads.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCThreads.h
new file mode 100644
index 00000000000..e164f631b93
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCThreads.h
@@ -0,0 +1,43 @@
+/**
+ @file BNCThreads.h
+ @package Branch-SDK
+ @brief Utilities for working with threads, queues, and blocks.
+
+ @author Edward Smith
+ @date May 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+///@group Blocks and Threads
+#pragma mark - Blocks and Threads
+
+static inline dispatch_time_t BNCDispatchTimeFromSeconds(NSTimeInterval seconds) {
+ return dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
+}
+
+static inline void BNCAfterSecondsPerformBlockOnMainThread(NSTimeInterval seconds, dispatch_block_t block) {
+ dispatch_after(BNCDispatchTimeFromSeconds(seconds), dispatch_get_main_queue(), block);
+}
+
+static inline void BNCPerformBlockOnMainThreadAsync(dispatch_block_t block) {
+ dispatch_async(dispatch_get_main_queue(), block);
+}
+
+static inline uint64_t BNCNanoSecondsFromTimeInterval(NSTimeInterval interval) {
+ return interval * ((NSTimeInterval) NSEC_PER_SEC);
+}
+
+static inline void BNCSleepForTimeInterval(NSTimeInterval seconds) {
+ double secPart = trunc(seconds);
+ double nanoPart = trunc((seconds - secPart) * ((double)NSEC_PER_SEC));
+ struct timespec sleepTime;
+ sleepTime.tv_sec = (__typeof(sleepTime.tv_sec)) secPart;
+ sleepTime.tv_nsec = (__typeof(sleepTime.tv_nsec)) nanoPart;
+ nanosleep(&sleepTime, NULL);
+}
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCThreads.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCThreads.m
new file mode 100644
index 00000000000..53d0aae5b77
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCThreads.m
@@ -0,0 +1,11 @@
+/**
+ @file BNCThreads.m
+ @package Branch-SDK
+ @brief Utilities for working with threads, queues, and blocks.
+
+ @author Edward Smith
+ @date May 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#import "BNCThreads.h"
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCURLBlackList.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCURLBlackList.h
new file mode 100644
index 00000000000..1c148d706ab
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCURLBlackList.h
@@ -0,0 +1,41 @@
+/**
+ @file BNCURLBlackList.h
+ @package Branch-SDK
+ @brief Manages a list of sensitive URLs such as login data that should not be handled by Branch.
+
+ @author Edward Smith
+ @date February 14, 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#if __has_feature(modules)
+@import Foundation;
+#else
+#import
+#endif
+
+@interface BNCURLBlackList : NSObject
+
+/**
+ @brief Checks if a given URL should be ignored (blacklisted).
+
+ @param url The URL to be checked.
+ @return Returns true if the provided URL should be ignored.
+*/
+- (BOOL) isBlackListedURL:(NSURL*_Nullable)url;
+
+/**
+ @brief Returns the pattern that matches a URL, if any.
+
+ @param url The URL to be checked.
+ @return Returns the pattern matching the URL or `nil` if no patterns match.
+*/
+- (NSString*_Nullable) blackListPatternMatchingURL:(NSURL*_Nullable)url;
+
+/// Refreshes the list of ignored URLs from the server.
+- (void) refreshBlackListFromServerWithCompletion:(void (^_Nullable) (NSError*_Nullable error, NSArray*_Nullable list))completion;
+
+/// Is YES if the listed has already been updated from the server.
+@property (assign, readonly) BOOL hasRefreshedBlackListFromServer;
+@property (strong) NSArray*_Nullable blackList;
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCURLBlackList.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCURLBlackList.m
new file mode 100644
index 00000000000..2f703726942
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BNCURLBlackList.m
@@ -0,0 +1,182 @@
+/**
+ @file BNCURLBlackList.m
+ @package Branch-SDK
+ @brief Manages a list of URLs that we should ignore.
+
+ @author Edward Smith
+ @date February 14, 2018
+ @copyright Copyright © 2018 Branch. All rights reserved.
+*/
+
+#import "BNCURLBlackList.h"
+#import "Branch.h"
+
+@interface BNCURLBlackList () {
+ NSArray*_blackList;
+}
+@property (strong) NSArray *blackListRegex;
+@property (assign) NSInteger blackListVersion;
+@property (strong) id networkService;
+@property (assign) BOOL hasRefreshedBlackListFromServer;
+@property (strong) NSError *error;
+@property (strong) NSURL *blackListJSONURL;
+@end
+
+@implementation BNCURLBlackList
+
+- (instancetype) init {
+ self = [super init];
+ if (!self) return self;
+
+ self.blackList = @[
+ @"^fb\\d+:",
+ @"^li\\d+:",
+ @"^pdk\\d+:",
+ @"^twitterkit-.*:",
+ @"^com\\.googleusercontent\\.apps\\.\\d+-.*:\\/oauth",
+ @"^(?i)(?!(http|https):).*(:|:.*\\b)(password|o?auth|o?auth.?token|access|access.?token)\\b",
+ @"^(?i)((http|https):\\/\\/).*[\\/|?|#].*\\b(password|o?auth|o?auth.?token|access|access.?token)\\b",
+ ];
+ self.blackListVersion = -1; // First time always refresh the list version, version 0.
+
+ NSArray *storedList = [BNCPreferenceHelper preferenceHelper].URLBlackList;
+ if (storedList.count > 0) {
+ self.blackList = storedList;
+ self.blackListVersion = [BNCPreferenceHelper preferenceHelper].URLBlackListVersion;
+ }
+
+ NSError *error = nil;
+ _blackListRegex = [self.class compileRegexArray:self.blackList error:&error];
+ self.error = error;
+
+ return self;
+}
+
+- (void) dealloc {
+ [self.networkService cancelAllOperations];
+ self.networkService = nil;
+}
+
+- (void) setBlackList:(NSArray *)blackList {
+ @synchronized (self) {
+ _blackList = blackList;
+ _blackListRegex = [self.class compileRegexArray:_blackList error:nil];
+ }
+}
+
+- (NSArray*) blackList {
+ @synchronized (self) {
+ return _blackList;
+ }
+}
+
++ (NSArray*) compileRegexArray:(NSArray*)blacklist
+ error:(NSError*_Nullable __autoreleasing *_Nullable)error_ {
+ if (error_) *error_ = nil;
+ NSMutableArray *array = [NSMutableArray new];
+ for (NSString *pattern in blacklist) {
+ NSError *error = nil;
+ NSRegularExpression *regex =
+ [NSRegularExpression regularExpressionWithPattern:pattern
+ options: NSRegularExpressionAnchorsMatchLines | NSRegularExpressionUseUnicodeWordBoundaries
+ error:&error];
+ if (error || !regex) {
+ BNCLogError(@"Invalid regular expression '%@': %@.", pattern, error);
+ if (error_ && !*error_) *error_ = error;
+ } else {
+ [array addObject:regex];
+ }
+ }
+ return array;
+}
+
+- (NSString*_Nullable) blackListPatternMatchingURL:(NSURL*_Nullable)url {
+ NSString *urlString = url.absoluteString;
+ if (urlString == nil || urlString.length <= 0) return nil;
+ NSRange range = NSMakeRange(0, urlString.length);
+
+ for (NSRegularExpression* regex in self.blackListRegex) {
+ NSUInteger matches = [regex numberOfMatchesInString:urlString options:0 range:range];
+ if (matches > 0) return regex.pattern;
+ }
+
+ return nil;
+}
+
+- (BOOL) isBlackListedURL:(NSURL *)url {
+ return ([self blackListPatternMatchingURL:url]) ? YES : NO;
+}
+
+- (void) refreshBlackListFromServer {
+ [self refreshBlackListFromServerWithCompletion:nil];
+}
+
+- (void) refreshBlackListFromServerWithCompletion:(void (^) (NSError*error, NSArray*list))completion {
+ @synchronized(self) {
+ if (self.hasRefreshedBlackListFromServer) {
+ if (completion) completion(self.error, self.blackList);
+ return;
+ }
+ self.hasRefreshedBlackListFromServer = YES;
+ }
+
+ self.error = nil;
+ NSString *urlString = [self.blackListJSONURL absoluteString];
+ if (!urlString) {
+ urlString = [NSString stringWithFormat:@"https://cdn.branch.io/sdk/uriskiplist_v%ld.json",
+ (long) self.blackListVersion+1];
+ }
+ NSMutableURLRequest *request =
+ [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]
+ cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
+ timeoutInterval:30.0];
+
+ self.networkService = [[Branch networkServiceClass] new];
+ id operation =
+ [self.networkService networkOperationWithURLRequest:request completion:
+ ^(id operation) {
+ [self processServerOperation:operation];
+ if (completion) completion(self.error, self.blackList);
+ [self.networkService cancelAllOperations];
+ self.networkService = nil;
+ }
+ ];
+ [operation start];
+}
+
+- (void) processServerOperation:(id)operation {
+ NSError *error = nil;
+ NSString *responseString = nil;
+ if (operation.responseData)
+ responseString = [[NSString alloc] initWithData:operation.responseData encoding:NSUTF8StringEncoding];
+ if (operation.response.statusCode == 404) {
+ BNCLogDebugSDK(@"No new BlackList refresh found.");
+ } else {
+ BNCLogDebugSDK(@"BlackList refresh result. Error: %@ status: %ld body:\n%@.",
+ operation.error, (long)operation.response.statusCode, responseString);
+ }
+ if (operation.error || operation.responseData == nil || operation.response.statusCode != 200) {
+ self.error = operation.error;
+ return;
+ }
+
+ NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:operation.responseData options:0 error:&error];
+ if (error) {
+ self.error = error;
+ BNCLogError(@"Can't parse JSON: %@.", error);
+ return;
+ }
+
+ NSArray *blackListURLs = dictionary[@"uri_skip_list"];
+ if (![blackListURLs isKindOfClass:NSArray.class]) return;
+
+ NSNumber *blackListVersion = dictionary[@"version"];
+ if (![blackListVersion isKindOfClass:NSNumber.class]) return;
+
+ self.blackList = blackListURLs;
+ self.blackListVersion = [blackListVersion longValue];
+ [BNCPreferenceHelper preferenceHelper].URLBlackList = self.blackList;
+ [BNCPreferenceHelper preferenceHelper].URLBlackListVersion = self.blackListVersion;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch+Validator.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch+Validator.h
new file mode 100644
index 00000000000..742e0f0a367
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch+Validator.h
@@ -0,0 +1,17 @@
+//
+// Branch+Validator.h
+// Branch
+//
+// Created by agrim on 12/18/17.
+// Copyright © 2017 Branch, Inc. All rights reserved.
+//
+
+#import "Branch.h"
+
+@interface Branch (Validator)
+- (void) validateSDKIntegrationCore;
+- (void) validateDeeplinkRouting:(NSDictionary *)params;
++ (NSString *) returnNonUniversalLink:(NSString *) referringLink;
+@end
+
+void BNCForceBranchValidatorCategoryToLoad(void);
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch+Validator.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch+Validator.m
new file mode 100644
index 00000000000..855a5ae1925
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch+Validator.m
@@ -0,0 +1,308 @@
+//
+// Branch+Validator.m
+// Branch
+//
+// Created by agrim on 12/18/17.
+// Copyright © 2017 Branch, Inc. All rights reserved.
+//
+
+#import "Branch+Validator.h"
+#import "BNCSystemObserver.h"
+#import "BranchConstants.h"
+#import "BNCApplication.h"
+#import "BNCEncodingUtils.h"
+
+void BNCForceBranchValidatorCategoryToLoad(void) {
+ // Empty body but forces loader to load the category.
+}
+
+static inline void BNCPerformBlockOnMainThreadAsync(dispatch_block_t block) {
+ dispatch_async(dispatch_get_main_queue(), block);
+}
+
+static inline dispatch_time_t BNCDispatchTimeFromSeconds(NSTimeInterval seconds) {
+ return dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
+}
+
+static inline void BNCAfterSecondsPerformBlockOnMainThread(NSTimeInterval seconds, dispatch_block_t block) {
+ dispatch_after(BNCDispatchTimeFromSeconds(seconds), dispatch_get_main_queue(), block);
+}
+
+#pragma mark - Branch (Validator)
+
+@implementation Branch (Validator)
+
+- (void)validateSDKIntegrationCore {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [self startValidation];
+ });
+}
+
+- (void) startValidation {
+ BNCPreferenceHelper *preferenceHelper = [BNCPreferenceHelper preferenceHelper];
+ NSString *endpoint =
+ [BRANCH_REQUEST_ENDPOINT_APP_LINK_SETTINGS stringByAppendingPathComponent:preferenceHelper.lastRunBranchKey];
+ [[[BNCServerInterface alloc] init]
+ getRequest:nil
+ url:[preferenceHelper getAPIURL:endpoint]
+ key:nil
+ callback:^ (BNCServerResponse *response, NSError *error) {
+ if (error) {
+ [self showAlertWithTitle:@"Error" message:error.localizedDescription];
+ } else {
+ [self validateIntegrationWithServerResponse:response];
+ }
+ }];
+}
+
+- (void) validateIntegrationWithServerResponse:(BNCServerResponse*)response {
+ NSString*passString = @"PASS";
+ NSString*errorString = @"ERROR";
+
+ // Decode the server message:
+ NSString*serverUriScheme = BNCStringFromWireFormat(response.data[@"ios_uri_scheme"]) ?: @"";
+ NSString*serverBundleID = BNCStringFromWireFormat(response.data[@"ios_bundle_id"]) ?: @"";
+ NSString*serverTeamID = BNCStringFromWireFormat(response.data[@"ios_team_id"]) ?: @"";
+
+ // Verify:
+ NSLog(@"** Initiating Branch integration verification **");
+ NSLog(@"-------------------------------------------------");
+
+ NSLog(@"------ Checking for URI scheme correctness ------");
+ NSString *clientUriScheme = [NSString stringWithFormat:@"%@%@", [BNCSystemObserver getDefaultUriScheme], @"://"];
+ NSString *uriScheme = [serverUriScheme isEqualToString:clientUriScheme] ? passString : errorString;
+ NSString *uriSchemeMessage =
+ [NSString stringWithFormat:@"%@: Dashboard Link Settings page '%@' compared to client side '%@'",
+ uriScheme, serverUriScheme, clientUriScheme];
+ NSLog(@"%@",uriSchemeMessage);
+ NSLog(@"-------------------------------------------------");
+
+ NSLog(@"-- Checking for bundle identifier correctness ---");
+ NSString *clientBundleIdentifier = [[NSBundle mainBundle] bundleIdentifier] ?: @"";
+ NSString *bundleIdentifier = [serverBundleID isEqualToString:clientBundleIdentifier] ? passString : errorString;
+ NSString *bundleIdentifierMessage =
+ [NSString stringWithFormat:@"%@: Dashboard Link Settings page '%@' compared to client side '%@'",
+ bundleIdentifier, serverBundleID, clientBundleIdentifier];
+ NSLog(@"%@",bundleIdentifierMessage);
+ NSLog(@"-------------------------------------------------");
+
+ NSLog(@"----- Checking for iOS Team ID correctness ------");
+ NSString *clientTeamId = [BNCApplication currentApplication].teamID ?: @"";
+ NSString *teamID = [serverTeamID isEqualToString:clientTeamId] ? passString : errorString;
+ NSString *teamIDMessage =
+ [NSString stringWithFormat:@"%@: Dashboard Link Settings page '%@' compared to client side '%@'",
+ teamID, serverTeamID, clientTeamId];
+ NSLog(@"%@",teamIDMessage);
+ NSLog(@"-------------------------------------------------");
+
+ if ([teamID isEqualToString:errorString] ||
+ [bundleIdentifier isEqualToString:errorString] ||
+ [uriScheme isEqualToString:errorString]) {
+ NSLog(@"%@: server side '%@' compared to client side '%@'.", errorString, serverTeamID, clientTeamId);
+ NSLog(@"To fix your Dashboard settings head over to https://branch.app.link/link-settings-page");
+ NSLog(@"If you see a null value on the client side, please temporarily add the following key-value pair to your plist: \n\tAppIdentifierPrefix$(AppIdentifierPrefix)\n-> then re-run this test.");
+ NSLog(@"-------------------------------------------------");
+ }
+
+ NSLog(@"-------------------------------------------------------------------------------------------------------------------");
+ NSLog(@"-----To test your deeplink routing append ?bnc_validate=true to any branch link and click it on your mobile device-----");
+ NSLog(@"-------------------------------------------------------------------------------------------------------------------");
+
+ BOOL testsFailed = NO;
+ NSString *kPassMark = @"✅\t";
+ NSString *kFailMark = @"❌\t";
+
+ // Build an alert string:
+ NSString *alertString = @"\n";
+ if (serverUriScheme.length && [serverUriScheme isEqualToString:clientUriScheme]) {
+ alertString = [alertString stringByAppendingFormat:@"%@URI Scheme matches:\n\t'%@'\n",
+ kPassMark, serverUriScheme];
+ } else {
+ testsFailed = YES;
+ alertString = [alertString stringByAppendingFormat:@"%@URI Scheme mismatch:\n\t'%@'\n",
+ kFailMark, serverUriScheme];
+ }
+
+ if ([serverBundleID isEqualToString:clientBundleIdentifier]) {
+ alertString = [alertString stringByAppendingFormat:@"%@App Bundle ID matches:\n\t'%@'\n",
+ kPassMark, serverBundleID];
+ } else {
+ testsFailed = YES;
+ alertString = [alertString stringByAppendingFormat:@"%@App Bundle ID mismatch:\n\t'%@'\n",
+ kFailMark, serverBundleID];
+ }
+
+ if ([serverTeamID isEqualToString:clientTeamId]) {
+ alertString = [alertString stringByAppendingFormat:@"%@Team ID matches:\n\t'%@'\n",
+ kPassMark, serverTeamID];
+ } else {
+ testsFailed = YES;
+ alertString = [alertString stringByAppendingFormat:@"%@Team ID mismatch:\n\t'%@'\n",
+ kFailMark, serverTeamID];
+ }
+
+ if (testsFailed) {
+ alertString = [alertString stringByAppendingString:@"\nFailed!\nCheck the log for details."];
+ } else {
+ alertString = [alertString stringByAppendingString:@"\nPassed!"];
+ }
+
+ NSMutableParagraphStyle *ps = [NSMutableParagraphStyle new];
+ ps.alignment = NSTextAlignmentLeft;
+ NSAttributedString *styledAlertString =
+ [[NSAttributedString alloc]
+ initWithString:alertString
+ attributes:@{
+ NSParagraphStyleAttributeName: ps
+ }];
+
+ BNCPerformBlockOnMainThreadAsync(^{
+ UIAlertController *alertController =
+ [UIAlertController alertControllerWithTitle:@"Branch Integration"
+ message:alertString
+ preferredStyle:UIAlertControllerStyleAlert];
+ if (testsFailed) {
+ [alertController
+ addAction:[UIAlertAction actionWithTitle:@"Bummer"
+ style:UIAlertActionStyleDefault
+ handler:nil]];
+ } else {
+ [alertController
+ addAction:[UIAlertAction actionWithTitle:@"Next Step"
+ style:UIAlertActionStyleDefault
+ handler:^ (UIAlertAction *action) { [self showNextStep]; }]];
+ }
+ [alertController setValue:styledAlertString forKey:@"attributedMessage"];
+ [[UIViewController bnc_currentViewController]
+ presentViewController:alertController
+ animated:YES
+ completion:nil];
+ });
+}
+
+- (void) showNextStep {
+ NSString *message =
+ @"\nGreat! Remove the 'validateSDKIntegration' line in your app.\n\n"
+ "Next check your deep link routing.\n\n"
+ "Append '?bnc_validate=true' to any of your app's Branch links and "
+ "click on it on your mobile device (not the Simulator!) to start the test.\n\n"
+ "For instance, to validate a link like:\n"
+ "https://.app.link/NdJ6nFzRbK\n\n"
+ "click on:\n"
+ "https://.app.link/NdJ6nFzRbK?bnc_validate=true";
+ NSLog(
+ @"\n----------------------------------------------------------------------------"
+ "\nBranch Integration Next Steps\n"
+ "\n"
+ "%@"
+ "\n----------------------------------------------------------------------------", message);
+ [self showAlertWithTitle:@"Next Step" message:message];
+}
+
+- (void) showAlertWithTitle:(NSString*)title message:(NSString*)message {
+ BNCPerformBlockOnMainThreadAsync(^{
+ UIAlertController *alertController =
+ [UIAlertController alertControllerWithTitle:title
+ message:message
+ preferredStyle:UIAlertControllerStyleAlert];
+ [alertController
+ addAction:[UIAlertAction actionWithTitle:@"OK"
+ style:UIAlertActionStyleDefault handler:nil]];
+ [[UIViewController bnc_currentViewController]
+ presentViewController:alertController
+ animated:YES
+ completion:nil];
+ });
+}
+
+- (void)returnToBrowserBasedOnReferringLink:(NSString *)referringLink
+ currentTest:(NSString *)currentTest
+ newTestVal:(NSString *)val {
+ // TODO: handling for missing ~referring_link
+ // TODO: test with short url where, say, t1=b is set in deep link data.
+ // If this logic fails then we'll need to generate a new short URL, which is sucky.
+ referringLink = [self.class returnNonUniversalLink:referringLink];
+ NSURLComponents *comp = [NSURLComponents componentsWithURL:[NSURL URLWithString:referringLink]
+ resolvingAgainstBaseURL:NO]; // TODO: Check iOS 8 support
+ NSArray *queryParams = [comp queryItems];
+ NSMutableArray *newQueryParams = [NSMutableArray array];
+ for (NSURLQueryItem *queryParam in queryParams) {
+ if (![queryParam.name isEqualToString:currentTest]) {
+ [newQueryParams addObject:queryParam];
+ }
+ }
+ [newQueryParams addObject:[NSURLQueryItem queryItemWithName:currentTest value:val]];
+ [newQueryParams addObject:[NSURLQueryItem queryItemWithName:@"validate" value:@"true"]];
+ comp.queryItems = newQueryParams;
+
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ Class applicationClass = NSClassFromString(@"UIApplication");
+ id sharedApplication = [applicationClass performSelector:@selector(sharedApplication)];
+ SEL openURL = @selector(openURL:);
+ if ([sharedApplication respondsToSelector:openURL])
+ [sharedApplication performSelector:openURL withObject:comp.URL];
+ #pragma clang diagnostic pop
+}
+
+- (void)validateDeeplinkRouting:(NSDictionary *)params {
+ BNCAfterSecondsPerformBlockOnMainThread(0.30, ^{
+ UIAlertController *alertController =
+ [UIAlertController
+ alertControllerWithTitle:@"Branch Deeplink Routing Support"
+ message:nil
+ preferredStyle:UIAlertControllerStyleAlert];
+
+ if ([params[@"+clicked_branch_link"] isEqualToNumber:@YES]) {
+ alertController.message =
+ @"Good news - we got link data. Now a question for you, astute developer: "
+ "did the app deep link to the specific piece of content you expected to see?";
+ // yes
+ [alertController addAction:[UIAlertAction
+ actionWithTitle:@"Yes" style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction * _Nonnull action) {
+ [self returnToBrowserBasedOnReferringLink:params[@"~referring_link"]
+ currentTest:params[@"ct"] newTestVal:@"g"];
+ }]];
+ // no
+ [alertController addAction:[UIAlertAction
+ actionWithTitle:@"No" style:UIAlertActionStyleDestructive
+ handler:^(UIAlertAction * _Nonnull action) {
+ [self returnToBrowserBasedOnReferringLink:params[@"~referring_link"]
+ currentTest:params[@"ct"] newTestVal:@"r"];
+ }]];
+ // cancel
+ [alertController addAction:[UIAlertAction
+ actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
+ }
+ else {
+ alertController.message =
+ @"Bummer. It seems like +clicked_branch_link is false - we didn't deep link. "
+ "Double check that the link you're clicking has the same branch_key that is being "
+ "used in your .plist file. Return to Safari when you're ready to test again.";
+ [alertController addAction:[UIAlertAction
+ actionWithTitle:@"Got it" style:UIAlertActionStyleDefault handler:nil]];
+ }
+ [[UIViewController bnc_currentViewController]
+ presentViewController:alertController animated:YES completion:nil];
+ });
+}
+
++ (NSString *) returnNonUniversalLink:(NSString *) referringLink {
+ // Appending /e/ to not treat this link as a Universal link
+ NSArray *lines = [referringLink componentsSeparatedByString: @"/"];
+ referringLink = @"";
+ for (int i = 0 ; i < [lines count]; i++) {
+ if(i != 2) {
+ referringLink = [referringLink stringByAppendingString:lines[i]];
+ referringLink = [referringLink stringByAppendingString:@"/"];
+ } else {
+ referringLink = [referringLink stringByAppendingString:lines[i]];
+ referringLink = [referringLink stringByAppendingString:@"/e/"];
+ }
+ }
+ referringLink = [referringLink stringByReplacingOccurrencesOfString:@"-alternate" withString:@""];
+ return referringLink;
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/Branch.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch.h
similarity index 97%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/Branch.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch.h
index 48d40628df3..add20b72673 100644
--- a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/Branch.h
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch.h
@@ -27,6 +27,7 @@
#import "BNCAvailability.h"
#import "BranchActivityItemProvider.h"
#import "BranchConstants.h"
+#import "BranchCSSearchableItemAttributeSet.h"
#import "BranchDeepLinkingController.h"
#import "BranchEvent.h"
#import "BranchLinkProperties.h"
@@ -66,7 +67,7 @@
Indicates this link is being used to trigger a deal, like a discounted rate.
`BRANCH_FEATURE_TAG_GIFT`
- Indicates this link is being used to sned a gift to another user.
+ Indicates this link is being used to send a gift to another user.
*/
extern NSString * const BRANCH_FEATURE_TAG_SHARE;
extern NSString * const BRANCH_FEATURE_TAG_REFERRAL;
@@ -224,9 +225,24 @@ typedef NS_ENUM(NSUInteger, BranchCreditHistoryOrder) {
You can only set the Branch key once per app run.
@param branchKey The Branch key to use.
+ @param error NSError will be set if Branch encounters a key error.
*/
++ (void) setBranchKey:(NSString*)branchKey error:(NSError **)error;
+
+/**
+ Directly sets the Branch key to be used. Branch usually reads the Branch key from your app's
+ Info.plist file which is recommended and more convenient. But the Branch key can also be set
+ with this method. See the documentation at
+ https://dev.branch.io/getting-started/sdk-integration-guide/guide/ios/#configure-xcode-project
+ for information about configuring your app with Branch keys.
+
+ You can only set the Branch key once per app run. Any errors are logged.
+
+ @param branchKey The Branch key to use.
+ */
+ (void) setBranchKey:(NSString*)branchKey;
+
/// @return Returns the current Branch key.
+ (NSString*) branchKey;
@@ -461,7 +477,11 @@ typedef NS_ENUM(NSUInteger, BranchCreditHistoryOrder) {
/**
Call this method from inside your app delegate's `application:openURL:sourceApplication:annotation:`
- method with the so that Branch can open the passed URL.
+ method so that Branch can open the passed URL. This method is for pre-iOS 9 compatibility: If you don't need
+ pre-iOS 9 compatibility, override your app delegate's `application:openURL:options:` method instead and use
+ the Branch `application:openURL:options:` to open the URL.
+
+ @warning Pre-iOS 9 compatibility only.
@param application The application that was passed to your app delegate.
@param url The URL that was passed to your app delegate.
@@ -475,12 +495,10 @@ typedef NS_ENUM(NSUInteger, BranchCreditHistoryOrder) {
annotation:(id)annotation;
/**
- Call this method from inside your app delegate's `application:openURL:options:`
- method with the so that Branch can open the passed URL.
+ Call this method from inside your app delegate's `application:openURL:options:` method so that Branch can
+ open the passed URL.
- This method is functionally the same as calling the Branch method
- `application:openURL:sourceApplication:annotation:`. This method matches the new Apple appDelegate
- method for convenience.
+ This is the preferred Branch method to call inside your `application:openURL:options:` method.
@param application The application that was passed to your app delegate.
@param url The URL that was passed to your app delegate.
@@ -1631,7 +1649,23 @@ typedef NS_ENUM(NSUInteger, BranchCreditHistoryOrder) {
- (void) sendServerRequest:(BNCServerRequest*)request;
- (void) sendServerRequestWithoutSession:(BNCServerRequest*)request;
+/**
+ This is the block that is called each time a new Branch session is started. It is automatically set
+ when Branch is initialized with `initSessionWithLaunchOptions:andRegisterDeepLinkHandler`.
+ */
+@property (copy, nonatomic) void(^ sessionInitWithParamsCallback) (NSDictionary * params, NSError * error);
+
+/**
+ This is the block that is called each time a new Branch session is started. It is automatically set
+ when Branch is initialized with `initSessionWithLaunchOptions:andRegisterDeepLinkHandlerUsingBranchUniversalObject`.
+
+ The difference with this callback from `sessionInitWithParamsCallback` is that it is called with a
+ BranchUniversalObject.
+ */
+@property (copy, nonatomic) void (^ sessionInitWithBranchUniversalObjectCallback)
+ (BranchUniversalObject * universalObject, BranchLinkProperties * linkProperties, NSError * error);
+
// Read-only property exposed for unit testing.
-@property (strong, readonly) BNCServerInterface *serverInterface;
+@property (strong, readonly) BNCServerInterface* serverInterface;
- (void) clearNetworkQueue;
@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch.m
new file mode 100644
index 00000000000..3da67fd55a4
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/Branch.m
@@ -0,0 +1,2531 @@
+//
+// Branch_SDK.m
+// Branch-SDK
+//
+// Created by Alex Austin on 6/5/14.
+// Copyright (c) 2014 Branch Metrics. All rights reserved.
+//
+
+#import "Branch.h"
+#import "BNCContentDiscoveryManager.h"
+#import "BNCCrashlyticsWrapper.h"
+#import "BNCDeepLinkViewControllerInstance.h"
+#import "BNCEncodingUtils.h"
+#import "BNCFabricAnswers.h"
+#import "BNCLinkData.h"
+#import "BNCNetworkService.h"
+#import "BNCPreferenceHelper.h"
+#import "BNCServerRequest.h"
+#import "BNCServerRequestQueue.h"
+#import "BNCServerResponse.h"
+#import "BNCStrongMatchHelper.h"
+#import "BNCSystemObserver.h"
+#import "BranchCloseRequest.h"
+#import "BranchConstants.h"
+#import "BranchContentDiscoverer.h"
+#import "BranchCreditHistoryRequest.h"
+#import "BranchInstallRequest.h"
+#import "BranchLoadRewardsRequest.h"
+#import "BranchLogoutRequest.h"
+#import "BranchOpenRequest.h"
+#import "BranchRedeemRewardsRequest.h"
+#import "BranchSetIdentityRequest.h"
+#import "BranchShortUrlRequest.h"
+#import "BranchShortUrlSyncRequest.h"
+#import "BranchSpotlightUrlRequest.h"
+#import "BranchUniversalObject.h"
+#import "BranchUserCompletedActionRequest.h"
+#import "NSMutableDictionary+Branch.h"
+#import "NSString+Branch.h"
+#import "Branch+Validator.h"
+#import "BNCSpotlightService.h"
+#import "../Fabric/FABKitProtocol.h" // Fabric
+#import "BNCApplication.h"
+#import "BNCURLBlackList.h"
+#import
+
+NSString * const BRANCH_FEATURE_TAG_SHARE = @"share";
+NSString * const BRANCH_FEATURE_TAG_REFERRAL = @"referral";
+NSString * const BRANCH_FEATURE_TAG_INVITE = @"invite";
+NSString * const BRANCH_FEATURE_TAG_DEAL = @"deal";
+NSString * const BRANCH_FEATURE_TAG_GIFT = @"gift";
+
+NSString * const BRANCH_INIT_KEY_CHANNEL = @"~channel";
+NSString * const BRANCH_INIT_KEY_FEATURE = @"~feature";
+NSString * const BRANCH_INIT_KEY_TAGS = @"~tags";
+NSString * const BRANCH_INIT_KEY_CAMPAIGN = @"~campaign";
+NSString * const BRANCH_INIT_KEY_STAGE = @"~stage";
+NSString * const BRANCH_INIT_KEY_CREATION_SOURCE = @"~creation_source";
+NSString * const BRANCH_INIT_KEY_REFERRER = @"+referrer";
+NSString * const BRANCH_INIT_KEY_PHONE_NUMBER = @"+phone_number";
+NSString * const BRANCH_INIT_KEY_IS_FIRST_SESSION = @"+is_first_session";
+NSString * const BRANCH_INIT_KEY_CLICKED_BRANCH_LINK = @"+clicked_branch_link";
+static NSString * const BRANCH_PUSH_NOTIFICATION_PAYLOAD_KEY = @"branch";
+
+NSString * const BNCCanonicalIdList = @"$canonical_identifier_list";
+NSString * const BNCPurchaseAmount = @"$amount";
+NSString * const BNCPurchaseCurrency = @"$currency";
+NSString * const BNCRegisterViewEvent = @"View";
+NSString * const BNCAddToWishlistEvent = @"Add to Wishlist";
+NSString * const BNCAddToCartEvent = @"Add to Cart";
+NSString * const BNCPurchaseInitiatedEvent = @"Purchase Started";
+NSString * const BNCPurchasedEvent = @"Purchased";
+NSString * const BNCShareInitiatedEvent = @"Share Started";
+NSString * const BNCShareCompletedEvent = @"Share Completed";
+
+static NSString * const BNCLogLevelKey = @"io.branch.sdk.BNCLogLevel";
+NSString * const BNCSpotlightFeature = @"spotlight";
+
+#ifndef CSSearchableItemActivityIdentifier
+#define CSSearchableItemActivityIdentifier @"kCSSearchableItemActivityIdentifier"
+#endif
+
+static inline dispatch_time_t BNCDispatchTimeFromSeconds(NSTimeInterval seconds) {
+ return dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
+}
+
+static inline void BNCAfterSecondsPerformBlock(NSTimeInterval seconds, dispatch_block_t block) {
+ dispatch_after(BNCDispatchTimeFromSeconds(seconds), dispatch_get_main_queue(), block);
+}
+
+#pragma mark - Load Categories
+
+void ForceCategoriesToLoad(void);
+void ForceCategoriesToLoad(void) {
+ BNCForceNSErrorCategoryToLoad();
+ BNCForceNSStringCategoryToLoad();
+ BNCForceNSMutableDictionaryCategoryToLoad();
+ BNCForceBranchValidatorCategoryToLoad();
+ BNCForceUIViewControllerCategoryToLoad();
+}
+
+#pragma mark - BranchLink
+
+@implementation BranchLink
+
++ (BranchLink*) linkWithUniversalObject:(BranchUniversalObject*)universalObject
+ properties:(BranchLinkProperties*)linkProperties {
+ BranchLink *link = [[BranchLink alloc] init];
+ link.universalObject = universalObject;
+ link.linkProperties = linkProperties;
+ return link;
+}
+
+@end
+
+#pragma mark - Branch
+
+typedef NS_ENUM(NSInteger, BNCInitStatus) {
+ BNCInitStatusUninitialized = 0,
+ BNCInitStatusInitializing,
+ BNCInitStatusInitialized
+};
+
+@interface Branch() {
+ NSInteger _networkCount;
+ BNCURLBlackList *_userURLBlackList;
+}
+
+@property (strong, nonatomic) BNCServerInterface *serverInterface;
+@property (strong, nonatomic) BNCServerRequestQueue *requestQueue;
+@property (strong, nonatomic) dispatch_semaphore_t processing_sema;
+@property (assign, atomic) NSInteger networkCount;
+@property (assign, nonatomic) NSInteger asyncRequestCount;
+@property (assign, nonatomic) BNCInitStatus initializationStatus;
+@property (assign, nonatomic) BOOL shouldCallSessionInitCallback;
+@property (assign, nonatomic) BOOL shouldAutomaticallyDeepLink;
+@property (strong, nonatomic) BNCLinkCache *linkCache;
+@property (strong, nonatomic) BNCPreferenceHelper *preferenceHelper;
+@property (strong, nonatomic) BNCContentDiscoveryManager *contentDiscoveryManager;
+@property (strong, nonatomic) NSMutableDictionary *deepLinkControllers;
+@property (weak, nonatomic) UIViewController *deepLinkPresentingController;
+@property (assign, nonatomic) BOOL useCookieBasedMatching;
+@property (strong, nonatomic) NSDictionary *deepLinkDebugParams;
+@property (assign, nonatomic) BOOL accountForFacebookSDK;
+@property (strong, nonatomic) id FBSDKAppLinkUtility;
+@property (assign, nonatomic) BOOL delayForAppleAds;
+@property (assign, nonatomic) BOOL searchAdsDebugMode;
+@property (strong, nonatomic) NSMutableArray *whiteListedSchemeList;
+@property (strong, nonatomic) BNCURLBlackList *URLBlackList;
+@end
+
+@implementation Branch
+
+#pragma mark - Public methods
+
+#pragma mark - GetInstance methods
+
+static NSURL* bnc_logURL = nil;
+
++ (void) openLog {
+ // Initialize the log
+ @synchronized (self) {
+ if (bnc_logURL) {
+ #if defined(BNCKeepLogfiles)
+ BNCLogSetOutputToURLByteWrap(bnc_logURL, 102400);
+ #else
+ BNCLogSetOutputFunction(NULL);
+ #endif
+ } else {
+ BNCLogInitialize();
+ BNCLogSetDisplayLevel(BNCLogLevelAll);
+ bnc_logURL = BNCURLForBranchDirectory();
+ bnc_logURL = [[NSURL alloc] initWithString:@"Branch.log" relativeToURL:bnc_logURL];
+ #if defined(BNCKeepLogfiles)
+ BNCLogSetOutputToURLByteWrap(bnc_logURL, 102400);
+ #else
+ BNCLogSetOutputFunction(NULL);
+ if (bnc_logURL)
+ [[NSFileManager defaultManager] removeItemAtURL:bnc_logURL error:nil];
+ #endif
+ BNCLogSetDisplayLevel(BNCLogLevelWarning); // Default
+
+ // Try loading from the Info.plist
+ NSString *logLevelString = [[NSBundle mainBundle] infoDictionary][@"BranchLogLevel"];
+ if ([logLevelString isKindOfClass:[NSString class]]) {
+ BNCLogLevel logLevel = BNCLogLevelFromString(logLevelString);
+ BNCLogSetDisplayLevel(logLevel);
+ }
+
+ // Try loading from user defaults
+ NSNumber *logLevel = [[NSUserDefaults standardUserDefaults] objectForKey:BNCLogLevelKey];
+ if ([logLevel isKindOfClass:[NSNumber class]]) {
+ BNCLogSetDisplayLevel([logLevel integerValue]);
+ }
+
+ BNCLog(@"Branch version %@ started at %@.", BNC_SDK_VERSION, [NSDate date]);
+ }
+ }
+}
+
++ (void) closeLog {
+ BNCLogCloseLogFile();
+}
+
+void BranchClassInitializeLog(void);
+void BranchClassInitializeLog(void) {
+ [Branch openLog];
+}
+
++ (void) load {
+ static dispatch_once_t onceToken = 0;
+ dispatch_once(&onceToken, ^{
+ BNCLogSetClientInitializeFunction(BranchClassInitializeLog);
+ });
+}
+
++ (Branch *) getTestInstance {
+ Branch.useTestBranchKey = YES;
+ return Branch.getInstance;
+}
+
++ (Branch *)getInstance {
+ return [Branch getInstanceInternal:self.class.branchKey returnNilIfNoCurrentInstance:NO];
+}
+
++ (Branch *)getInstance:(NSString *)branchKey {
+ self.branchKey = branchKey;
+ return [Branch getInstanceInternal:self.branchKey returnNilIfNoCurrentInstance:NO];
+}
+
+- (id)initWithInterface:(BNCServerInterface *)interface
+ queue:(BNCServerRequestQueue *)queue
+ cache:(BNCLinkCache *)cache
+ preferenceHelper:(BNCPreferenceHelper *)preferenceHelper
+ key:(NSString *)key {
+
+ self = [super init];
+ if (!self) return self;
+
+ // Initialize instance variables
+
+ _serverInterface = interface;
+ _serverInterface.preferenceHelper = preferenceHelper;
+ _requestQueue = queue;
+ _linkCache = cache;
+ _preferenceHelper = preferenceHelper;
+
+ _contentDiscoveryManager = [[BNCContentDiscoveryManager alloc] init];
+ _initializationStatus = BNCInitStatusUninitialized;
+ _shouldCallSessionInitCallback = YES;
+ _processing_sema = dispatch_semaphore_create(1);
+ _networkCount = 0;
+ _asyncRequestCount = 0;
+ _deepLinkControllers = [[NSMutableDictionary alloc] init];
+ _whiteListedSchemeList = [[NSMutableArray alloc] init];
+ _useCookieBasedMatching = YES;
+ self.class.branchKey = key;
+ self.URLBlackList = [BNCURLBlackList new];
+ [BranchOpenRequest setWaitNeededForOpenResponseLock];
+
+ // Register for notifications
+
+ NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
+ [notificationCenter
+ addObserver:self
+ selector:@selector(applicationWillResignActive)
+ name:UIApplicationWillResignActiveNotification
+ object:nil];
+
+ [notificationCenter
+ addObserver:self
+ selector:@selector(applicationDidBecomeActive)
+ name:UIApplicationDidBecomeActiveNotification
+ object:nil];
+
+ return self;
+}
+
+static Class bnc_networkServiceClass = NULL;
+
++ (void) setNetworkServiceClass:(Class)networkServiceClass {
+ @synchronized ([Branch class]) {
+ if (bnc_networkServiceClass) {
+ BNCLogError(@"The Branch network service class is already set. It can be set only once.");
+ return;
+ }
+ if (![networkServiceClass conformsToProtocol:@protocol(BNCNetworkServiceProtocol)]) {
+ BNCLogError(@"Class '%@' doesn't conform to protocol '%@'.",
+ NSStringFromClass(networkServiceClass),
+ NSStringFromProtocol(@protocol(BNCNetworkServiceProtocol))
+ );
+ return;
+ }
+ bnc_networkServiceClass = networkServiceClass;
+ }
+}
+
++ (Class) networkServiceClass {
+ @synchronized ([Branch class]) {
+ if (!bnc_networkServiceClass) bnc_networkServiceClass = [BNCNetworkService class];
+ return bnc_networkServiceClass;
+ }
+}
+
+#pragma mark - BrachActivityItemProvider methods
+
++ (BranchActivityItemProvider *)getBranchActivityItemWithParams:(NSDictionary *)params {
+ return [[BranchActivityItemProvider alloc] initWithParams:params tags:nil feature:nil stage:nil campaign:nil alias:nil delegate:nil];
+}
+
++ (BranchActivityItemProvider *)getBranchActivityItemWithParams:(NSDictionary *)params feature:(NSString *)feature {
+ return [[BranchActivityItemProvider alloc] initWithParams:params tags:nil feature:feature stage:nil campaign:nil alias:nil delegate:nil];
+}
+
++ (BranchActivityItemProvider *)getBranchActivityItemWithParams:(NSDictionary *)params feature:(NSString *)feature stage:(NSString *)stage {
+ return [[BranchActivityItemProvider alloc] initWithParams:params tags:nil feature:feature stage:stage campaign:nil alias:nil delegate:nil];
+}
+
++ (BranchActivityItemProvider *)getBranchActivityItemWithParams:(NSDictionary *)params feature:(NSString *)feature stage:(NSString *)stage tags:(NSArray *)tags {
+ return [[BranchActivityItemProvider alloc] initWithParams:params tags:tags feature:feature stage:stage campaign:nil alias:nil delegate:nil];
+}
+
++ (BranchActivityItemProvider *)getBranchActivityItemWithParams:(NSDictionary *)params feature:(NSString *)feature stage:(NSString *)stage campaign:(NSString *)campaign tags:(NSArray *)tags alias:(NSString *)alias {
+ return [[BranchActivityItemProvider alloc] initWithParams:params tags:tags feature:feature stage:stage campaign:campaign alias:alias delegate:nil];
+}
+
++ (BranchActivityItemProvider *)getBranchActivityItemWithParams:(NSDictionary *)params feature:(NSString *)feature stage:(NSString *)stage tags:(NSArray *)tags alias:(NSString *)alias {
+ return [[BranchActivityItemProvider alloc] initWithParams:params tags:tags feature:feature stage:stage campaign:nil alias:alias delegate:nil];
+}
+
++ (BranchActivityItemProvider *)getBranchActivityItemWithParams:(NSDictionary *)params feature:(NSString *)feature stage:(NSString *)stage tags:(NSArray *)tags alias:(NSString *)alias delegate:(id )delegate {
+ return [[BranchActivityItemProvider alloc] initWithParams:params tags:tags feature:feature stage:stage campaign:nil alias:alias delegate:delegate];
+}
+
+
+#pragma mark - Configuration methods
+
+static BOOL bnc_useTestBranchKey = NO;
+static NSString *bnc_branchKey = nil;
+static BOOL bnc_enableFingerprintIDInCrashlyticsReports = YES;
+
++ (void) resetBranchKey {
+ bnc_branchKey = nil;
+}
+
++ (void) setUseTestBranchKey:(BOOL)useTestKey {
+ @synchronized (self) {
+ if (bnc_branchKey && !!useTestKey != !!bnc_useTestBranchKey) {
+ BNCLogError(@"Can't switch the Branch key once it's in use.");
+ return;
+ }
+ bnc_useTestBranchKey = useTestKey;
+ }
+}
+
++ (BOOL) useTestBranchKey {
+ @synchronized (self) {
+ return bnc_useTestBranchKey;
+ }
+}
+
++ (void)setBranchKey:(NSString *)branchKey {
+ NSError *error;
+ [self setBranchKey:branchKey error:&error];
+
+ if (error) {
+ BNCLogError(@"Branch init error: %@", error.localizedDescription);
+ }
+}
+
++ (void)setBranchKey:(NSString*)branchKey error:(NSError **)error {
+ @synchronized (self) {
+ if (bnc_branchKey) {
+ if (branchKey &&
+ [branchKey isKindOfClass:[NSString class]] &&
+ [branchKey isEqualToString:bnc_branchKey]) {
+ return;
+ }
+
+ NSString *errorMessage = [NSString stringWithFormat:@"Branch key can only be set once."];
+ *error = [NSError branchErrorWithCode:BNCInitError localizedMessage:errorMessage];
+ return;
+ }
+
+ if (![branchKey isKindOfClass:[NSString class]]) {
+ NSString *typeName = (branchKey) ? NSStringFromClass(branchKey.class) : @"";
+
+ NSString *errorMessage = [NSString stringWithFormat:@"Invalid Branch key of type '%@'.", typeName];
+ *error = [NSError branchErrorWithCode:BNCInitError localizedMessage:errorMessage];
+ return;
+ }
+
+ if ([branchKey hasPrefix:@"key_test"]) {
+ bnc_useTestBranchKey = YES;
+ BNCLogWarning(
+ @"You are using your test app's Branch Key. "
+ "Remember to change it to live Branch Key for production deployment."
+ );
+
+ } else if ([branchKey hasPrefix:@"key_live"]) {
+ bnc_useTestBranchKey = NO;
+
+ } else {
+ NSString *errorMessage = [NSString stringWithFormat:@"Invalid Branch key format. Did you add your Branch key to your Info.plist? Passed key is '%@'.", branchKey];
+ *error = [NSError branchErrorWithCode:BNCInitError localizedMessage:errorMessage];
+ return;
+ }
+
+ bnc_branchKey = branchKey;
+ }
+}
+
++ (NSString*) branchKey {
+ @synchronized (self) {
+ if (bnc_branchKey) return bnc_branchKey;
+
+ // The name of this key was specified in the Fabric account-creation API integration
+ NSString * const BNC_BRANCH_FABRIC_APP_KEY_KEY = @"branch_key";
+
+ NSDictionary *branchDictionary =
+ [[[NSBundle mainBundle] infoDictionary] objectForKey:BNC_BRANCH_FABRIC_APP_KEY_KEY];
+
+ if (!branchDictionary) {
+ branchDictionary = [BNCFabricAnswers branchConfigurationDictionary];
+ }
+
+ NSString *branchKey = nil;
+ if ([branchDictionary isKindOfClass:[NSString class]]) {
+ branchKey = (NSString*) branchDictionary;
+ } else
+ if ([branchDictionary isKindOfClass:[NSDictionary class]]) {
+ branchKey =
+ (self.useTestBranchKey) ? branchDictionary[@"test"] : branchDictionary[@"live"];
+ }
+
+ self.branchKey = branchKey;
+ if (!bnc_branchKey) {
+ BNCLogError(@"Your Branch key is not set in your Info.plist file. See "
+ "https://dev.branch.io/getting-started/sdk-integration-guide/guide/ios/#configure-xcode-project"
+ " for configuration instructions.");
+ }
+ return bnc_branchKey;
+ }
+}
+
++ (BOOL) branchKeyIsSet {
+ @synchronized (self) {
+ return (bnc_branchKey.length) ? YES : NO;
+ }
+}
+
++ (void)setEnableFingerprintIDInCrashlyticsReports:(BOOL)enabled
+{
+ @synchronized(self) {
+ bnc_enableFingerprintIDInCrashlyticsReports = enabled;
+ }
+}
+
++ (BOOL) enableFingerprintIDInCrashlyticsReports
+{
+ @synchronized (self) {
+ return bnc_enableFingerprintIDInCrashlyticsReports;
+ }
+}
+
+- (void)setDebug {
+ self.preferenceHelper.isDebug = YES;
+ if (BNCLogDisplayLevel() > BNCLogLevelDebug)
+ BNCLogSetDisplayLevel(BNCLogLevelDebug);
+}
+
+- (void)validateSDKIntegration {
+ [self validateSDKIntegrationCore];
+}
+
+- (void)resetUserSession {
+ self.initializationStatus = BNCInitStatusUninitialized;
+}
+
+- (BOOL)isUserIdentified {
+ return self.preferenceHelper.userIdentity != nil;
+}
+
+- (void)setNetworkTimeout:(NSTimeInterval)timeout {
+ self.preferenceHelper.timeout = timeout;
+}
+
+- (void)setMaxRetries:(NSInteger)maxRetries {
+ self.preferenceHelper.retryCount = maxRetries;
+}
+
+- (void)setRetryInterval:(NSTimeInterval)retryInterval {
+ self.preferenceHelper.retryInterval = retryInterval;
+}
+
+- (void)disableCookieBasedMatching {
+ self.useCookieBasedMatching = NO;
+}
+
+- (void)accountForFacebookSDKPreventingAppLaunch {
+ self.accountForFacebookSDK = YES;
+}
+
+- (void)suppressWarningLogs {
+ BNCLogSetDisplayLevel(BNCLogLevelError);
+}
+
+- (void)setRequestMetadataKey:(NSString *)key value:(NSObject *)value {
+ [self.preferenceHelper setRequestMetadataKey:key value:value];
+}
+
+- (void)enableDelayedInit {
+ self.preferenceHelper.shouldWaitForInit = YES;
+
+ self.useCookieBasedMatching = NO; // Developers delaying init should implement their own SFSafariViewController
+}
+
+- (void)disableDelayedInit {
+ self.preferenceHelper.shouldWaitForInit = NO;
+}
+
+- (NSURL *)getUrlForOnboardingWithRedirectUrl:(NSString *)redirectUrl {
+ return [BNCStrongMatchHelper getUrlForCookieBasedMatchingWithBranchKey:self.class.branchKey redirectUrl:redirectUrl];
+}
+
+- (void)resumeInit {
+ self.preferenceHelper.shouldWaitForInit = NO;
+ if (self.initializationStatus == BNCInitStatusInitialized) {
+ BNCLogError(@"User session has already been initialized, so resumeInit is aborting.");
+ }
+ else if (![self.requestQueue containsInstallOrOpen]) {
+ BNCLogError(@"No install or open request, so resumeInit is aborting.");
+ }
+ else {
+ [self processNextQueueItem];
+ }
+}
+
+- (void)setInstallRequestDelay:(NSInteger)installRequestDelay {
+ self.preferenceHelper.installRequestDelay = installRequestDelay;
+}
+
++ (BOOL) trackingDisabled {
+ @synchronized(self) {
+ return [BNCPreferenceHelper preferenceHelper].trackingDisabled;
+ }
+}
+
++ (void) setTrackingDisabled:(BOOL)disabled {
+ @synchronized(self) {
+ BOOL currentSetting = self.trackingDisabled;
+ if (!!currentSetting == !!disabled)
+ return;
+ if (disabled) {
+ // Set the flag (which also clears the settings):
+ [BNCPreferenceHelper preferenceHelper].trackingDisabled = YES;
+ Branch *branch = Branch.getInstance;
+ [branch clearNetworkQueue];
+ branch.initializationStatus = BNCInitStatusUninitialized;
+ [branch.linkCache clear];
+ // Release the lock in case it's locked:
+ [BranchOpenRequest releaseOpenResponseLock];
+ } else {
+ // Set the flag:
+ [BNCPreferenceHelper preferenceHelper].trackingDisabled = NO;
+ // Initialize a Branch session:
+ [Branch.getInstance initUserSessionAndCallCallback:NO];
+ }
+ }
+}
+
+#pragma mark - InitSession Permutation methods
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options {
+ [self initSessionWithLaunchOptions:options
+ isReferrable:YES
+ explicitlyRequestedReferrable:NO
+ automaticallyDisplayController:NO
+ registerDeepLinkHandler:nil];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options andRegisterDeepLinkHandler:(callbackWithParams)callback {
+ [self initSessionWithLaunchOptions:options isReferrable:YES explicitlyRequestedReferrable:NO automaticallyDisplayController:NO registerDeepLinkHandler:callback];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options andRegisterDeepLinkHandlerUsingBranchUniversalObject:(callbackWithBranchUniversalObject)callback {
+ [self initSessionWithLaunchOptions:options isReferrable:YES explicitlyRequestedReferrable:NO automaticallyDisplayController:NO registerDeepLinkHandlerUsingBranchUniversalObject:callback];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL)isReferrable {
+ [self initSessionWithLaunchOptions:options isReferrable:isReferrable explicitlyRequestedReferrable:YES automaticallyDisplayController:NO registerDeepLinkHandler:nil];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options automaticallyDisplayDeepLinkController:(BOOL)automaticallyDisplayController {
+ [self initSessionWithLaunchOptions:options isReferrable:YES explicitlyRequestedReferrable:NO automaticallyDisplayController:automaticallyDisplayController registerDeepLinkHandler:nil];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL)isReferrable andRegisterDeepLinkHandler:(callbackWithParams)callback {
+ [self initSessionWithLaunchOptions:options isReferrable:isReferrable explicitlyRequestedReferrable:YES automaticallyDisplayController:NO registerDeepLinkHandler:callback];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options automaticallyDisplayDeepLinkController:(BOOL)automaticallyDisplayController deepLinkHandler:(callbackWithParams)callback {
+ [self initSessionWithLaunchOptions:options isReferrable:YES explicitlyRequestedReferrable:NO automaticallyDisplayController:automaticallyDisplayController registerDeepLinkHandler:callback];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL)isReferrable automaticallyDisplayDeepLinkController:(BOOL)automaticallyDisplayController {
+ [self initSessionWithLaunchOptions:options isReferrable:isReferrable explicitlyRequestedReferrable:YES automaticallyDisplayController:automaticallyDisplayController registerDeepLinkHandler:nil];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options automaticallyDisplayDeepLinkController:(BOOL)automaticallyDisplayController isReferrable:(BOOL)isReferrable deepLinkHandler:(callbackWithParams)callback {
+ [self initSessionWithLaunchOptions:options isReferrable:isReferrable explicitlyRequestedReferrable:YES automaticallyDisplayController:automaticallyDisplayController registerDeepLinkHandler:callback];
+}
+
+
+#pragma mark - Actual Init Session
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL)isReferrable explicitlyRequestedReferrable:(BOOL)explicitlyRequestedReferrable automaticallyDisplayController:(BOOL)automaticallyDisplayController registerDeepLinkHandlerUsingBranchUniversalObject:(callbackWithBranchUniversalObject)callback {
+ self.sessionInitWithBranchUniversalObjectCallback = callback;
+ [self initSessionWithLaunchOptions:options isReferrable:isReferrable explicitlyRequestedReferrable:explicitlyRequestedReferrable automaticallyDisplayController:automaticallyDisplayController];
+}
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options isReferrable:(BOOL)isReferrable explicitlyRequestedReferrable:(BOOL)explicitlyRequestedReferrable automaticallyDisplayController:(BOOL)automaticallyDisplayController registerDeepLinkHandler:(callbackWithParams)callback {
+ self.sessionInitWithParamsCallback = callback;
+ [self initSessionWithLaunchOptions:options isReferrable:isReferrable explicitlyRequestedReferrable:explicitlyRequestedReferrable automaticallyDisplayController:automaticallyDisplayController];
+}
+
+
+- (void)initSessionWithLaunchOptions:(NSDictionary *)options
+ isReferrable:(BOOL)isReferrable
+ explicitlyRequestedReferrable:(BOOL)explicitlyRequestedReferrable
+ automaticallyDisplayController:(BOOL)automaticallyDisplayController {
+ // Log this early. Not consistently showing up when logged from [Branch initialize].
+ [self.class addBranchSDKVersionToCrashlyticsReport];
+
+ self.shouldAutomaticallyDeepLink = automaticallyDisplayController;
+
+ // If the SDK is already initialized, this means that initSession is being called later in the app lifecycle
+ // and that the developer is expecting to receive deep link parameters via the callback block immediately
+ if (self.initializationStatus == BNCInitStatusInitialized) {
+ [self initUserSessionAndCallCallback:YES];
+ }
+
+ // The rest of this function assumes that initSession is being called BEFORE continueUserActivity and openUrl
+ // in the application life cycle, and that the SDK is not yet initialized.
+
+ // Handle push notification on app launch
+ if ([options objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) {
+ id branchUrlFromPush =
+ [options objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey][BRANCH_PUSH_NOTIFICATION_PAYLOAD_KEY];
+ if ([branchUrlFromPush isKindOfClass:[NSString class]]) {
+ self.preferenceHelper.universalLinkUrl = branchUrlFromPush;
+ self.preferenceHelper.referringURL = branchUrlFromPush;
+ }
+ }
+
+ if ([BNCSystemObserver getOSVersion].integerValue >= 8) {
+
+ // Handle case where there's no URI scheme or Universal Link
+ if (![options.allKeys containsObject:UIApplicationLaunchOptionsURLKey] &&
+ ![options.allKeys containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) {
+
+ self.asyncRequestCount = 0;
+
+ // These methods will increment self.asyncRequestCount if they make an async call:
+
+ // If Facebook SDK is present, call deferred app link check here which will later on call initUserSession
+ [self checkFacebookAppLinks];
+ // If developer opted in, call deferred apple search attribution API here which will later on call initUserSession
+ [self checkAppleSearchAdsAttribution];
+
+ if (self.asyncRequestCount == 0) {
+ // If we're not looking for App Links or Apple Search Ads, initialize
+ [self initUserSessionAndCallCallback:YES];
+ }
+ }
+ // Handle case where there is Universal Link present
+ else if ([options.allKeys containsObject:UIApplicationLaunchOptionsUserActivityDictionaryKey]) {
+ // Optional flag for the developer if they're letting Facebook return the boolean on didFinishLaunchingWithOptions
+ // Note that this is no longer a recommended path, and that we tell developers to just return YES
+ if (self.accountForFacebookSDK) {
+ // does not work in Swift, because Objective-C to Swift interop is bad
+ id activity =
+ [[options objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey]
+ objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
+ if (activity && [activity isKindOfClass:[NSUserActivity class]]) {
+ [self continueUserActivity:activity];
+ return;
+ }
+ }
+ // Wait for continueUserActivity Branch AppDelegate call to come through
+ self.preferenceHelper.shouldWaitForInit = YES;
+ }
+ }
+ else if (![options.allKeys containsObject:UIApplicationLaunchOptionsURLKey]) {
+ [self initUserSessionAndCallCallback:YES];
+ }
+}
+
+
+
+//these params will be added
+- (void) setDeepLinkDebugMode:(NSDictionary *)debugParams {
+ self.deepLinkDebugParams = debugParams;
+}
+
+-(void)setWhiteListedSchemes:(NSArray *)schemes {
+ self.whiteListedSchemeList = [schemes mutableCopy];
+}
+
+-(void)addWhiteListedScheme:(NSString *)scheme {
+ [self.whiteListedSchemeList addObject:scheme];
+}
+
+- (void) setBlackListURLRegex:(NSArray*)blackListURLs {
+ @synchronized (self) {
+ _userURLBlackList = [[BNCURLBlackList alloc] init];
+ _userURLBlackList.blackList = blackListURLs;
+ }
+}
+
+- (NSArray*) blackListURLRegex {
+ @synchronized (self) {
+ return _userURLBlackList.blackList;
+ }
+}
+
+- (BOOL)handleDeepLink:(NSURL *)url {
+ return [self handleDeepLink:url fromSelf:YES];
+}
+
+-(BOOL)handleDeepLinkWithNewSession:(NSURL *)url{
+ return [self handleDeepLink:url fromSelf:YES];
+}
+
+- (BOOL)handleDeepLink:(NSURL *)url fromSelf:(BOOL)isFromSelf {
+ NSString *blackListPattern = nil;
+ blackListPattern = [self.URLBlackList blackListPatternMatchingURL:url];
+ if (!blackListPattern) {
+ blackListPattern = [_userURLBlackList blackListPatternMatchingURL:url];
+ }
+ if (blackListPattern) {
+ self.preferenceHelper.blacklistURLOpen = YES;
+ self.preferenceHelper.externalIntentURI = blackListPattern;
+ self.preferenceHelper.referringURL = blackListPattern;
+ if (isFromSelf) [self resetUserSession];
+ [self initUserSessionAndCallCallback:(self.initializationStatus != BNCInitStatusInitialized)];
+ return NO;
+ }
+
+ NSString *scheme = [url scheme];
+ if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) {
+ return [self handleUniversalDeepLink_private:url.absoluteString fromSelf:isFromSelf];
+ } else {
+ return [self handleSchemeDeepLink_private:url fromSelf:isFromSelf];
+ }
+}
+
+- (BOOL)handleSchemeDeepLink_private:(NSURL*)url fromSelf:(BOOL)isFromSelf {
+ BOOL handled = NO;
+ self.preferenceHelper.referringURL = nil;
+ if (url && ![url isEqual:[NSNull null]]) {
+
+ NSString *urlScheme = [url scheme];
+
+ // save the incoming url in the preferenceHelper in the externalIntentURI field
+ if ([self.whiteListedSchemeList count]) {
+ for (NSString *scheme in self.whiteListedSchemeList) {
+ if (urlScheme && [scheme isEqualToString:urlScheme]) {
+ self.preferenceHelper.externalIntentURI = [url absoluteString];
+ self.preferenceHelper.referringURL = [url absoluteString];
+ break;
+ }
+ }
+ } else {
+ self.preferenceHelper.externalIntentURI = [url absoluteString];
+ self.preferenceHelper.referringURL = [url absoluteString];
+ }
+
+ NSString *query = [url fragment];
+ if (!query) {
+ query = [url query];
+ }
+
+ NSDictionary *params = [BNCEncodingUtils decodeQueryStringToDictionary:query];
+ if (params[@"link_click_id"]) {
+ handled = YES;
+ if (isFromSelf) {
+ [self resetUserSession];
+ }
+ self.preferenceHelper.linkClickIdentifier = params[@"link_click_id"];
+ }
+ }
+ [self initUserSessionAndCallCallback:(self.initializationStatus != BNCInitStatusInitialized)];
+ return handled;
+}
+
+
+- (BOOL)application:(UIApplication *)application
+ openURL:(NSURL *)url
+ sourceApplication:(NSString *)sourceApplication
+ annotation:(id)annotation {
+
+ BOOL fromSelf = NO;
+ NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
+ if (bundleID && sourceApplication) {
+ fromSelf = [bundleID isEqualToString:sourceApplication];
+ }
+ if (!fromSelf) [self resetUserSession];
+ return [self handleDeepLink:url fromSelf:fromSelf];
+}
+
+- (BOOL)application:(UIApplication *)application
+ openURL:(NSURL *)url
+ options:(NSDictionary*UIApplicationOpenURLOptionsKey*/NSString*,id> *)options {
+
+ NSString *source = nil;
+ NSString *annotation = nil;
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wpartial-availability"
+ if (UIApplicationOpenURLOptionsSourceApplicationKey &&
+ UIApplicationOpenURLOptionsAnnotationKey) {
+ source = options[UIApplicationOpenURLOptionsSourceApplicationKey];
+ annotation = options[UIApplicationOpenURLOptionsAnnotationKey];
+ }
+ #pragma clang diagnostic pop
+ return [self application:application openURL:url sourceApplication:source annotation:annotation];
+}
+
+- (BOOL)handleUniversalDeepLink_private:(NSString*)urlString fromSelf:(BOOL)isFromSelf {
+ if (isFromSelf) {
+ [self resetUserSession];
+ }
+
+ if (urlString.length) {
+ self.preferenceHelper.universalLinkUrl = urlString;
+ self.preferenceHelper.referringURL = urlString;
+ }
+ self.preferenceHelper.shouldWaitForInit = NO;
+ [self initUserSessionAndCallCallback:YES];
+
+ id branchUniversalLinkDomains = [self.preferenceHelper getBranchUniversalLinkDomains];
+ if ([branchUniversalLinkDomains isKindOfClass:[NSString class]] &&
+ [urlString bnc_containsString:branchUniversalLinkDomains]) {
+ return YES;
+ }
+ else if ([branchUniversalLinkDomains isKindOfClass:[NSArray class]]) {
+ for (id oneDomain in branchUniversalLinkDomains) {
+ if ([oneDomain isKindOfClass:[NSString class]] && [urlString bnc_containsString:oneDomain]) {
+ return YES;
+ }
+ }
+ }
+
+ NSString *userActivityURL = urlString;
+ NSArray *branchDomains = [NSArray arrayWithObjects:@"bnc.lt", @"app.link", @"test-app.link", nil];
+ for (NSString* domain in branchDomains) {
+ if ([userActivityURL bnc_containsString:domain])
+ return YES;
+ }
+
+ return NO;
+}
+
+- (BOOL)continueUserActivity:(NSUserActivity *)userActivity {
+ BNCLogDebugSDK(@"continueUserActivity:");
+
+ // Check to see if a browser activity needs to be handled
+ if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
+ // If we're already in-progress cancel the last open and do this one.
+ [self removeInstallOrOpen];
+ return [self handleDeepLink:userActivity.webpageURL fromSelf:YES];
+ }
+
+ // Check to see if a spotlight activity needs to be handled
+ NSString *spotlightIdentifier =
+ [self.contentDiscoveryManager spotlightIdentifierFromActivity:userActivity];
+ NSURL *webURL = userActivity.webpageURL;
+
+
+ if ([self isBranchLink:userActivity.userInfo[CSSearchableItemActivityIdentifier]]) {
+ return [self handleDeepLinkWithNewSession:[NSURL URLWithString:userActivity.userInfo[CSSearchableItemActivityIdentifier]]];
+ }
+ else if (webURL != nil && [self isBranchLink:[webURL absoluteString]]) {
+ return [self handleDeepLinkWithNewSession:webURL];
+ }
+ else if (spotlightIdentifier) {
+ self.preferenceHelper.spotlightIdentifier = spotlightIdentifier;
+ }
+ else {
+ NSString *nonBranchSpotlightIdentifier =
+ [self.contentDiscoveryManager standardSpotlightIdentifierFromActivity:userActivity];
+ if (nonBranchSpotlightIdentifier) {
+ self.preferenceHelper.spotlightIdentifier = nonBranchSpotlightIdentifier;
+ }
+ }
+ self.preferenceHelper.shouldWaitForInit = NO;
+ [self initUserSessionAndCallCallback:YES];
+
+ return spotlightIdentifier != nil;
+}
+
+- (BOOL)isBranchLink: (NSString*)urlString {
+ id branchUniversalLinkDomains = [self.preferenceHelper getBranchUniversalLinkDomains];
+ if ([branchUniversalLinkDomains isKindOfClass:[NSString class]] &&
+ [urlString containsString:branchUniversalLinkDomains]) {
+ return YES;
+ }
+ else if ([branchUniversalLinkDomains isKindOfClass:[NSArray class]]) {
+ for (id oneDomain in branchUniversalLinkDomains) {
+ if ([oneDomain isKindOfClass:[NSString class]] && [urlString containsString:oneDomain]) {
+ return YES;
+ }
+ }
+ }
+
+ NSString *userActivityURL = urlString;
+ NSArray *branchDomains = [NSArray arrayWithObjects:@"bnc.lt", @"app.link", @"test-app.link", nil];
+ for (NSString* domain in branchDomains) {
+ if ([userActivityURL containsString:domain])
+ return YES;
+ }
+ return NO;
+}
+
+#pragma mark - Push Notification support
+
+- (void)handlePushNotification:(NSDictionary *)userInfo {
+ // look for a branch shortlink in the payload (shortlink because iOS7 only supports 256 bytes)
+ NSString *urlStr = [userInfo objectForKey:BRANCH_PUSH_NOTIFICATION_PAYLOAD_KEY];
+ if (urlStr.length) {
+ // reusing this field, so as not to create yet another url slot on prefshelper
+ self.preferenceHelper.universalLinkUrl = urlStr;
+ self.preferenceHelper.referringURL = urlStr;
+ }
+
+ // If app is active, then close out the session and start a new one.
+ // Else the URL will be handled by `applicationDidBecomeActive`.
+
+ Class UIApplicationClass = NSClassFromString(@"UIApplication");
+ if (urlStr && [[UIApplicationClass sharedApplication] applicationState] == UIApplicationStateActive) {
+ NSURL *url = [NSURL URLWithString:urlStr];
+ if (url) [self handleDeepLink:url fromSelf:YES];
+ }
+}
+
+#pragma mark - Apple Search Ad Check
+
+- (void)delayInitToCheckForSearchAds {
+ self.delayForAppleAds = YES;
+}
+
+- (void)setAppleSearchAdsDebugMode {
+ self.searchAdsDebugMode = YES;
+}
+
+- (BOOL)checkAppleSearchAdsAttribution {
+ if (!self.delayForAppleAds)
+ return NO;
+
+ NSNumber *hasAppleSearchAdAttribution = self.preferenceHelper.appleSearchAdDetails[@"iad-attribution"];
+ if ([hasAppleSearchAdAttribution boolValue])
+ return NO;
+
+ NSDate *installDatePlus30 =
+ [[BNCApplication currentApplication].currentInstallDate
+ dateByAddingTimeInterval:(30.0*24.0*60.0*60.0)];
+ if ([installDatePlus30 compare:[NSDate date]] < 0) {
+ return NO;
+ }
+
+ Class ADClientClass = NSClassFromString(@"ADClient");
+ SEL sharedClient = NSSelectorFromString(@"sharedClient");
+ SEL requestAttribution = NSSelectorFromString(@"requestAttributionDetailsWithBlock:");
+
+ BOOL ADClientIsAvailable =
+ ADClientClass &&
+ [ADClientClass instancesRespondToSelector:requestAttribution] &&
+ [ADClientClass methodForSelector:sharedClient];
+
+ if (!ADClientIsAvailable) {
+ BNCLogWarning(@"`delayForAppleAds` is true but ADClient is not available. Is the iAD.framework included and iOS 10?");
+ return NO;
+ }
+
+ id sharedClientInstance = ((id (*)(id, SEL))[ADClientClass methodForSelector:sharedClient])(ADClientClass, sharedClient);
+
+ self.preferenceHelper.shouldWaitForInit = YES;
+ self.preferenceHelper.checkedAppleSearchAdAttribution = YES;
+ self.asyncRequestCount++;
+
+ NSDate *startDate = [NSDate date];
+ _Atomic __block BOOL hasBeenCalled = NO;
+ void (^__nullable completionBlock)(NSDictionary *attrDetails, NSError *error) =
+ ^ void(NSDictionary *__nullable attrDetails, NSError *__nullable error) {
+ @synchronized(self) {
+ NSTimeInterval elapsedSeconds = - [startDate timeIntervalSinceNow];
+ BNCLogDebugSDK(@"Elapsed Apple Search Ad callback time: %1.3fs.", elapsedSeconds);
+
+ if (attrDetails.count > 0 && !error) {
+ [self.preferenceHelper addInstrumentationDictionaryKey:@"apple_search_ad"
+ value:[[NSNumber numberWithInteger:elapsedSeconds*1000] stringValue]];
+ }
+ if (self.searchAdsDebugMode) {
+ // If searchAdsDebugMode is on then force the result to a set value for testing.
+ NSTimeInterval const kOneHour = (60.0*60.0*1.0); // Round down to one day for testing.
+ NSTimeInterval t = trunc([[NSDate date] timeIntervalSince1970] / kOneHour) * kOneHour;
+ NSDate *date = [NSDate dateWithTimeIntervalSince1970:t];
+ attrDetails = @{
+ @"Version3.1": @{
+ @"iad-adgroup-id": @1234567890,
+ @"iad-adgroup-name": @"AdGroupName",
+ @"iad-attribution": (id)kCFBooleanTrue,
+ @"iad-campaign-id": @1234567890,
+ @"iad-campaign-name": @"CampaignName",
+ @"iad-click-date": date,
+ @"iad-conversion-date": date,
+ @"iad-creative-id": @1234567890,
+ @"iad-creative-name": @"CreativeName",
+ @"iad-keyword": @"Keyword",
+ @"iad-lineitem-id": @1234567890,
+ @"iad-lineitem-name": @"LineName",
+ @"iad-org-name": @"OrgName"
+ }
+ };
+ }
+ if (!error) {
+ if (attrDetails == nil)
+ attrDetails = @{};
+ if (self.preferenceHelper.appleSearchAdDetails == nil)
+ self.preferenceHelper.appleSearchAdDetails = @{};
+ if (![self.preferenceHelper.appleSearchAdDetails isEqualToDictionary:attrDetails]) {
+ self.preferenceHelper.appleSearchAdDetails = attrDetails;
+ self.preferenceHelper.appleSearchAdNeedsSend = YES;
+ }
+ }
+ // Only continue with initialization once:
+ BOOL localHasBeenCalled = atomic_exchange(&hasBeenCalled, YES);
+ if (localHasBeenCalled) return;
+
+ if (error) {
+ BNCLogError(@"Error while getting Apple Search Ad attribution: %@.", error);
+ }
+
+ self.asyncRequestCount--;
+ // If there's another async attribution check in flight, don't continue with init:
+ if (self.asyncRequestCount > 0) return;
+
+ self.preferenceHelper.shouldWaitForInit = NO;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self initUserSessionAndCallCallback:(self.initializationStatus != BNCInitStatusInitialized)];
+ });
+ }
+ };
+
+ // Set a expiration timer in case we don't get a call back (I'm looking at you, iPad):
+ // This may be a temporary quick fix until we figure out a better wat to do search ads.
+ BNCAfterSecondsPerformBlock(10.0, ^ {
+ completionBlock(nil, [NSError errorWithDomain:NSNetServicesErrorDomain code:NSNetServicesTimeoutError userInfo:nil]);
+ });
+
+ ((void (*)(id, SEL, void (^ __nullable)(NSDictionary *__nullable attrDetails, NSError * __nullable error)))
+ [sharedClientInstance methodForSelector:requestAttribution])
+ (sharedClientInstance, requestAttribution, completionBlock);
+
+ return YES;
+}
+
+#pragma mark - Facebook App Link Check
+
+- (void)registerFacebookDeepLinkingClass:(id)FBSDKAppLinkUtility {
+ self.FBSDKAppLinkUtility = FBSDKAppLinkUtility;
+}
+
+- (BOOL)checkFacebookAppLinks {
+ if (self.FBSDKAppLinkUtility) {
+
+ SEL fetchDeferredAppLink = NSSelectorFromString(@"fetchDeferredAppLink:");
+
+ if ([self.FBSDKAppLinkUtility methodForSelector:fetchDeferredAppLink]) {
+ void (^__nullable completionBlock)(NSURL *appLink, NSError *error) = ^void(NSURL *__nullable appLink, NSError *__nullable error) {
+ self.asyncRequestCount--;
+
+ // if there's another async attribution check in flight, don't continue with init
+ if (self.asyncRequestCount > 0) { return; }
+
+ self.preferenceHelper.shouldWaitForInit = NO;
+
+ [self handleDeepLink:appLink];
+ };
+
+ self.asyncRequestCount++;
+ self.preferenceHelper.checkedFacebookAppLinks = YES;
+ self.preferenceHelper.shouldWaitForInit = YES;
+
+ ((void (*)(id, SEL, void (^ __nullable)(NSURL *__nullable appLink, NSError * __nullable error)))[self.FBSDKAppLinkUtility methodForSelector:fetchDeferredAppLink])(self.FBSDKAppLinkUtility, fetchDeferredAppLink, completionBlock);
+
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+#pragma mark - Deep Link Controller methods
+
+- (void)registerDeepLinkController:(UIViewController *)controller forKey:(NSString *)key {
+ self.deepLinkControllers[key] = controller;
+}
+
+- (void)registerDeepLinkController:(UIViewController *)controller forKey:(NSString *)key withPresentation:(BNCViewControllerPresentationOption)option{
+
+ BNCDeepLinkViewControllerInstance* deepLinkModal = [[BNCDeepLinkViewControllerInstance alloc] init];
+
+ deepLinkModal.viewController = controller;
+ deepLinkModal.option = option;
+
+ self.deepLinkControllers[key] = deepLinkModal;
+}
+
+
+#pragma mark - Identity methods
+
+- (void)setIdentity:(NSString *)userId {
+ [self setIdentity:userId withCallback:NULL];
+}
+
+- (void)setIdentity:(NSString *)userId withCallback:(callbackWithParams)callback {
+ if (!userId || [self.preferenceHelper.userIdentity isEqualToString:userId]) {
+ if (callback) {
+ callback([self getFirstReferringParams], nil);
+ }
+ return;
+ }
+ [self initSessionIfNeededAndNotInProgress];
+ BranchSetIdentityRequest *req = [[BranchSetIdentityRequest alloc] initWithUserId:userId callback:callback];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+- (void)logout {
+ [self logoutWithCallback:nil];
+}
+
+
+- (void)logoutWithCallback:(callbackWithStatus)callback {
+ if (self.initializationStatus == BNCInitStatusUninitialized) {
+ NSError *error =
+ (Branch.trackingDisabled)
+ ? [NSError branchErrorWithCode:BNCTrackingDisabledError]
+ : [NSError branchErrorWithCode:BNCInitError];
+ BNCLogError(@"Branch is not initialized, cannot logout.");
+ if (callback) {callback(NO, error);}
+ return;
+ }
+
+ BranchLogoutRequest *req =
+ [[BranchLogoutRequest alloc] initWithCallback:^(BOOL success, NSError *error) {
+ if (success) {
+ // Clear cached links
+ self.linkCache = [[BNCLinkCache alloc] init];
+
+ if (callback) {
+ callback(YES, nil);
+ }
+ BNCLogDebug(@"Logout success.");
+ } else /*failure*/ {
+ if (callback) {
+ callback(NO, error);
+ }
+ BNCLogDebug(@"Logout failure.");
+ }
+ }];
+
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+
+
+#pragma mark - User Action methods
+
+
+- (void)userCompletedAction:(NSString *)action {
+ [self userCompletedAction:action withState:nil withDelegate:nil];
+}
+
+- (void)userCompletedAction:(NSString *)action withState:(NSDictionary *)state {
+ [self userCompletedAction:action withState:state withDelegate:nil];
+}
+
+- (void)userCompletedAction:(NSString *)action withState:(NSDictionary *)state withDelegate:(id)branchViewCallback {
+ if (!action) {
+ return;
+ }
+
+ [self initSessionIfNeededAndNotInProgress];
+
+ BranchUserCompletedActionRequest *req = [[BranchUserCompletedActionRequest alloc] initWithAction:action state:state withBranchViewCallback:branchViewCallback];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+
+- (void) sendServerRequest:(BNCServerRequest*)request {
+ [self initSessionIfNeededAndNotInProgress];
+ [self.requestQueue enqueue:request];
+ [self processNextQueueItem];
+}
+
+- (void) sendServerRequestWithoutSession:(BNCServerRequest*)request {
+ [self.requestQueue enqueue:request];
+ [self processNextQueueItem];
+}
+
+- (void) sendCommerceEvent:(BNCCommerceEvent *)commerceEvent
+ metadata:(NSDictionary*)metadata
+ withCompletion:(void (^)(NSDictionary *, NSError *))completion {
+
+ [self initSessionIfNeededAndNotInProgress];
+ BranchCommerceEventRequest *request =
+ [[BranchCommerceEventRequest alloc]
+ initWithCommerceEvent:commerceEvent
+ metadata:metadata
+ completion:completion];
+ [self.requestQueue enqueue:request];
+ [self processNextQueueItem];
+}
+
+#pragma mark - Credit methods
+
+- (void)loadRewardsWithCallback:(callbackWithStatus)callback {
+ [self initSessionIfNeededAndNotInProgress];
+
+ BranchLoadRewardsRequest *req = [[BranchLoadRewardsRequest alloc] initWithCallback:callback];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+- (NSInteger)getCredits {
+ return [self.preferenceHelper getCreditCount];
+}
+
+- (void)redeemRewards:(NSInteger)count {
+ [self redeemRewards:count forBucket:@"default" callback:NULL];
+}
+
+- (void)redeemRewards:(NSInteger)count callback:(callbackWithStatus)callback {
+ [self redeemRewards:count forBucket:@"default" callback:callback];
+}
+
+- (NSInteger)getCreditsForBucket:(NSString *)bucket {
+ return [self.preferenceHelper getCreditCountForBucket:bucket];
+}
+
+- (void)redeemRewards:(NSInteger)count forBucket:(NSString *)bucket {
+ [self redeemRewards:count forBucket:bucket callback:NULL];
+}
+
+- (void)redeemRewards:(NSInteger)count forBucket:(NSString *)bucket callback:(callbackWithStatus)callback {
+ if (count == 0) {
+ if (callback) {
+ callback(false, [NSError branchErrorWithCode:BNCRedeemZeroCreditsError]);
+ }
+ else {
+ BNCLogWarning(@"Cannot redeem zero credits.");
+ }
+ return;
+ }
+
+ NSInteger totalAvailableCredits = [self.preferenceHelper getCreditCountForBucket:bucket];
+ if (count > totalAvailableCredits) {
+ if (callback) {
+ callback(false, [NSError branchErrorWithCode:BNCRedeemCreditsError]);
+ }
+ else {
+ BNCLogWarning(@"You're trying to redeem more credits than are available. Have you loaded rewards?");
+ }
+ return;
+ }
+
+ [self initSessionIfNeededAndNotInProgress];
+
+ BranchRedeemRewardsRequest *req = [[BranchRedeemRewardsRequest alloc] initWithAmount:count bucket:bucket callback:callback];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+- (void)getCreditHistoryWithCallback:(callbackWithList)callback {
+ [self getCreditHistoryForBucket:nil after:nil number:100 order:BranchMostRecentFirst andCallback:callback];
+}
+
+- (void)getCreditHistoryForBucket:(NSString *)bucket andCallback:(callbackWithList)callback {
+ [self getCreditHistoryForBucket:bucket after:nil number:100 order:BranchMostRecentFirst andCallback:callback];
+}
+
+- (void)getCreditHistoryAfter:(NSString *)creditTransactionId number:(NSInteger)length order:(BranchCreditHistoryOrder)order andCallback:(callbackWithList)callback {
+ [self getCreditHistoryForBucket:nil after:creditTransactionId number:length order:order andCallback:callback];
+}
+
+- (void)getCreditHistoryForBucket:(NSString *)bucket after:(NSString *)creditTransactionId number:(NSInteger)length order:(BranchCreditHistoryOrder)order andCallback:(callbackWithList)callback {
+ [self initSessionIfNeededAndNotInProgress];
+
+ BranchCreditHistoryRequest *req = [[BranchCreditHistoryRequest alloc] initWithBucket:bucket creditTransactionId:creditTransactionId length:length order:order callback:callback];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+- (BranchUniversalObject *)getFirstReferringBranchUniversalObject {
+ NSDictionary *params = [self getFirstReferringParams];
+ if ([[params objectForKey:BRANCH_INIT_KEY_CLICKED_BRANCH_LINK] isEqual:@1]) {
+ return [BranchUniversalObject objectWithDictionary:params];
+ }
+ return nil;
+}
+
+- (BranchLinkProperties *)getFirstReferringBranchLinkProperties {
+ NSDictionary *params = [self getFirstReferringParams];
+ if ([[params objectForKey:BRANCH_INIT_KEY_CLICKED_BRANCH_LINK] isEqual:@1]) {
+ return [BranchLinkProperties getBranchLinkPropertiesFromDictionary:params];
+ }
+ return nil;
+}
+
+- (NSDictionary *)getFirstReferringParams {
+ NSDictionary *origInstallParams = [BNCEncodingUtils decodeJsonStringToDictionary:self.preferenceHelper.installParams];
+
+ if (self.deepLinkDebugParams) {
+ NSMutableDictionary* debugInstallParams =
+ [[BNCEncodingUtils decodeJsonStringToDictionary:self.preferenceHelper.sessionParams]
+ mutableCopy];
+ [debugInstallParams addEntriesFromDictionary:self.deepLinkDebugParams];
+ return debugInstallParams;
+ }
+ return origInstallParams;
+}
+
+- (NSDictionary *)getLatestReferringParams {
+ NSDictionary *origSessionParams = [BNCEncodingUtils decodeJsonStringToDictionary:self.preferenceHelper.sessionParams];
+
+ if (self.deepLinkDebugParams) {
+ NSMutableDictionary* debugSessionParams = [origSessionParams mutableCopy];
+ [debugSessionParams addEntriesFromDictionary:self.deepLinkDebugParams];
+ return debugSessionParams;
+ }
+ return origSessionParams;
+}
+
+- (NSDictionary*) getLatestReferringParamsSynchronous {
+ [BranchOpenRequest waitForOpenResponseLock];
+ NSDictionary *result = [self getLatestReferringParams];
+ [BranchOpenRequest releaseOpenResponseLock];
+ return result;
+}
+
+- (BranchUniversalObject *)getLatestReferringBranchUniversalObject {
+ NSDictionary *params = [self getLatestReferringParams];
+ if ([[params objectForKey:BRANCH_INIT_KEY_CLICKED_BRANCH_LINK] isEqual:@1]) {
+ return [BranchUniversalObject objectWithDictionary:params];
+ }
+ return nil;
+}
+
+- (BranchLinkProperties *)getLatestReferringBranchLinkProperties {
+ NSDictionary *params = [self getLatestReferringParams];
+ if ([[params objectForKey:BRANCH_INIT_KEY_CLICKED_BRANCH_LINK] boolValue]) {
+ return [BranchLinkProperties getBranchLinkPropertiesFromDictionary:params];
+ }
+ return nil;
+}
+
+#pragma mark - ShortUrl methods
+
+- (NSString *)getShortURL {
+ return [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:nil andFeature:nil andStage:nil andCampaign:nil andParams:nil ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params {
+ return [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:nil andFeature:nil andStage:nil andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage {
+ return [self generateShortUrl:tags andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andAlias:(NSString *)alias {
+ return [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andAlias:(NSString *)alias ignoreUAString:(NSString *)ignoreUAString {
+ return [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:ignoreUAString forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andCampaign:(NSString *)campaign andAlias:(NSString *)alias ignoreUAString:(NSString *)ignoreUAString forceLinkCreation:(BOOL)forceLinkCreation {
+ return [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:campaign andParams:params ignoreUAString:ignoreUAString forceLinkCreation:forceLinkCreation];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andType:(BranchLinkType)type {
+ return [self generateShortUrl:tags andAlias:nil andType:type andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andMatchDuration:(NSUInteger)duration {
+ return [self generateShortUrl:tags andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage {
+ return [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andAlias:(NSString *)alias {
+ return [self generateShortUrl:nil andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andType:(BranchLinkType)type {
+ return [self generateShortUrl:nil andAlias:nil andType:type andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andMatchDuration:(NSUInteger)duration {
+ return [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature {
+ return [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:nil andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortUrlWithParams:(NSDictionary *)params andTags:(NSArray *)tags andAlias:(NSString *)alias andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andCampaign:(NSString *)campaign andMatchDuration:(NSUInteger)duration {
+ return [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:campaign andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (NSString *)getShortUrlWithParams:(NSDictionary *)params andTags:(NSArray *)tags andAlias:(NSString *)alias andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andMatchDuration:(NSUInteger)duration {
+ return [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params ignoreUAString:nil forceLinkCreation:YES];
+}
+
+- (void)getShortURLWithCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:nil andFeature:nil andStage:nil andCampaign:nil andParams:nil andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:nil andFeature:nil andStage:nil andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:tags andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andAlias:(NSString *)alias andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andType:(BranchLinkType)type andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:tags andAlias:nil andType:type andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andTags:(NSArray *)tags andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andMatchDuration:(NSUInteger)duration andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:tags andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andAlias:(NSString *)alias andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:nil andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andType:(BranchLinkType)type andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:nil andAlias:nil andType:type andMatchDuration:0 andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andMatchDuration:(NSUInteger)duration andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andFeature:(NSString *)feature andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:nil andAlias:nil andType:BranchLinkTypeUnlimitedUse andMatchDuration:0 andChannel:channel andFeature:feature andStage:nil andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getShortUrlWithParams:(NSDictionary *)params andTags:(NSArray *)tags andAlias:(NSString *)alias andMatchDuration:(NSUInteger)duration andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andCampaign:campaign andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:campaign andParams:params andCallback:callback];
+}
+
+- (void)getShortUrlWithParams:(NSDictionary *)params andTags:(NSArray *)tags andAlias:(NSString *)alias andMatchDuration:(NSUInteger)duration andChannel:(NSString *)channel andFeature:(NSString *)feature andStage:(NSString *)stage andCallback:(callbackWithUrl)callback {
+ [self generateShortUrl:tags andAlias:alias andType:BranchLinkTypeUnlimitedUse andMatchDuration:duration andChannel:channel andFeature:feature andStage:stage andCampaign:nil andParams:params andCallback:callback];
+}
+
+- (void)getSpotlightUrlWithParams:(NSDictionary *)params callback:(callbackWithParams)callback {
+ [self initSessionIfNeededAndNotInProgress];
+
+ BranchSpotlightUrlRequest *req = [[BranchSpotlightUrlRequest alloc] initWithParams:params callback:callback];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+#pragma mark - LongUrl methods
+- (NSString *)getLongURLWithParams:(NSDictionary *)params andChannel:(NSString *)channel andTags:(NSArray *)tags andFeature:(NSString *)feature andStage:(NSString *)stage andAlias:(NSString *)alias {
+ return [self generateLongURLWithParams:params andChannel:channel andTags:tags andFeature:feature andStage:stage andAlias:alias];
+}
+
+- (NSString *)getLongURLWithParams:(NSDictionary *)params {
+ return [self generateLongURLWithParams:params andChannel:nil andTags:nil andFeature:nil andStage:nil andAlias:nil];
+}
+
+- (NSString *)getLongURLWithParams:(NSDictionary *)params andFeature:(NSString *)feature {
+ return [self generateLongURLWithParams:params andChannel:nil andTags:nil andFeature:feature andStage:nil andAlias:nil];
+}
+
+- (NSString *)getLongURLWithParams:(NSDictionary *)params andFeature:(NSString *)feature andStage:(NSString *)stage {
+ return [self generateLongURLWithParams:params andChannel:nil andTags:nil andFeature:feature andStage:stage andAlias:nil];
+}
+
+- (NSString *)getLongURLWithParams:(NSDictionary *)params andFeature:(NSString *)feature andStage:(NSString *)stage andTags:(NSArray *)tags {
+ return [self generateLongURLWithParams:params andChannel:nil andTags:tags andFeature:feature andStage:stage andAlias:nil];
+}
+
+- (NSString *)getLongURLWithParams:(NSDictionary *)params andFeature:(NSString *)feature andStage:(NSString *)stage andAlias:(NSString *)alias {
+ return [self generateLongURLWithParams:params andChannel:nil andTags:nil andFeature:feature andStage:stage andAlias:alias];
+}
+
+#pragma mark - Discoverable content methods
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description callback:callback];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description publiclyIndexable:(BOOL)publiclyIndexable callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable callback:callback];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable type:type callback:callback];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl callback:callback];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords callback:callback];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl linkParams:(NSDictionary *)linkParams publiclyIndexable:(BOOL)publiclyIndexable {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable thumbnailUrl:thumbnailUrl userInfo:linkParams];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl linkParams:(NSDictionary *)linkParams publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable thumbnailUrl:thumbnailUrl keywords:keywords userInfo:linkParams];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl linkParams:(NSDictionary *)linkParams type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords userInfo:linkParams];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl linkParams:(NSDictionary *)linkParams type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords userInfo:linkParams callback:callback];
+}
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl linkParams:(NSDictionary *)linkParams type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords expirationDate:(NSDate *)expirationDate callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords userInfo:linkParams expirationDate:expirationDate callback:callback];
+}
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl canonicalId:canonicalId linkParams:(NSDictionary *)linkParams type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords expirationDate:(NSDate *)expirationDate callback:(callbackWithUrl)callback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description canonicalId:canonicalId publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords userInfo:linkParams expirationDate:expirationDate callback:callback];
+}
+
+// Use this with iOS 9+ only
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl linkParams:(NSDictionary *)linkParams type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords expirationDate:(NSDate *)expirationDate spotlightCallback:(callbackWithUrlAndSpotlightIdentifier)spotlightCallback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description canonicalId:nil publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords userInfo:linkParams expirationDate:expirationDate callback:nil spotlightCallback:spotlightCallback];
+}
+
+- (void)createDiscoverableContentWithTitle:(NSString *)title description:(NSString *)description thumbnailUrl:(NSURL *)thumbnailUrl canonicalId:(NSString *)canonicalId linkParams:(NSDictionary *)linkParams type:(NSString *)type publiclyIndexable:(BOOL)publiclyIndexable keywords:(NSSet *)keywords expirationDate:(NSDate *)expirationDate spotlightCallback:(callbackWithUrlAndSpotlightIdentifier)spotlightCallback {
+ [self.contentDiscoveryManager indexContentWithTitle:title description:description canonicalId:canonicalId publiclyIndexable:publiclyIndexable type:type thumbnailUrl:thumbnailUrl keywords:keywords userInfo:linkParams expirationDate:expirationDate callback:nil spotlightCallback:spotlightCallback];
+}
+
+- (void)indexOnSpotlightWithBranchUniversalObject:(BranchUniversalObject*)universalObject
+ linkProperties:(BranchLinkProperties*)linkProperties
+ completion:(void (^) (BranchUniversalObject *universalObject, NSString * url,NSError *error))completion {
+ BNCSpotlightService *spotlightService = [[BNCSpotlightService alloc] init];
+
+ if (!universalObject) {
+ NSError* error = [NSError branchErrorWithCode:BNCInitError localizedMessage:@"Branch Universal Object is nil"];
+ if (completion) completion(universalObject,nil,error);
+ return;
+ } else {
+ [spotlightService indexWithBranchUniversalObject:universalObject
+ linkProperties:linkProperties
+ callback:^(BranchUniversalObject * _Nullable universalObject,
+ NSString * _Nullable url,
+ NSError * _Nullable error) {
+ if (completion) completion(universalObject,url,error);
+ }];
+ }
+}
+
+/* Indexing of multiple BUOs
+ * Content privately indexed irrestive of the value of contentIndexMode
+ */
+
+
+- (void)indexOnSpotlightUsingSearchableItems:(NSArray* )universalObjects
+ completion:(void (^) (NSArray* universalObjects,
+ NSError* error))completion {
+
+ BNCSpotlightService *spotlight = [[BNCSpotlightService alloc] init];
+ [spotlight indexPrivatelyWithBranchUniversalObjects:universalObjects
+ completion:^(NSArray * _Nullable universalObjects,
+ NSError * _Nullable error) {
+ if (completion) completion(universalObjects,error);
+ }];
+}
+
+- (void)removeSearchableItemWithBranchUniversalObject:(BranchUniversalObject *)universalObject
+ callback:(void (^_Nullable)(NSError * _Nullable error))completion {
+ BNCSpotlightService *spotlight = [[BNCSpotlightService alloc] init];
+
+ NSString *dynamicUrl = [universalObject getLongUrlWithChannel:nil
+ andTags:nil
+ andFeature:BNCSpotlightFeature
+ andStage:nil
+ andAlias:nil];
+ [spotlight removeSearchableItemsWithIdentifier:dynamicUrl
+ callback:^(NSError * _Nullable error) {
+ if (completion) completion(error);
+ }];
+}
+
+
+/* Only removes the indexing of BUOs indexed through CSSearchable item
+ */
+- (void)removeSearchableItemsWithBranchUniversalObjects:(NSArray *)universalObjects
+ callback:(void (^)(NSError * error))completion {
+ BNCSpotlightService *spotlight = [[BNCSpotlightService alloc] init];
+ NSMutableArray *identifiers = [[NSMutableArray alloc] init];
+ for (BranchUniversalObject* universalObject in universalObjects) {
+ NSString *dynamicUrl = [universalObject getLongUrlWithChannel:nil
+ andTags:nil
+ andFeature:BNCSpotlightFeature
+ andStage:nil andAlias:nil];
+ if (dynamicUrl) [identifiers addObject:dynamicUrl];
+ }
+
+ [spotlight removeSearchableItemsWithIdentifiers:identifiers
+ callback:^(NSError * error) {
+ if (completion)
+ completion(error);
+ }];
+}
+
+/* Removes all content from spotlight indexed through CSSearchable item and has set the Domain identifier = "com.branch.io"
+ */
+
+- (void)removeAllPrivateContentFromSpotLightWithCallback:(void (^)(NSError * error))completion {
+ BNCSpotlightService *spotlight = [[BNCSpotlightService alloc] init];
+ [spotlight removeAllBranchSearchableItemsWithCallback:^(NSError * _Nullable error) {
+ completion(error);
+ }];
+}
+
+#pragma mark - Private methods
+
++ (Branch *)getInstanceInternal:(NSString *)key returnNilIfNoCurrentInstance:(BOOL)returnNilIfNoCurrentInstance {
+
+ static Branch *branch = nil;
+ @synchronized (self) {
+ if (!branch && returnNilIfNoCurrentInstance) {
+ return nil;
+ }
+
+ static dispatch_once_t onceToken = 0;
+ dispatch_once(&onceToken, ^{
+ BNCPreferenceHelper *preferenceHelper = [BNCPreferenceHelper preferenceHelper];
+
+ // If there was stored key and it isn't the same as the currently used (or doesn't exist), we need to clean up
+ // Note: Link Click Identifier is not cleared because of the potential for that to mess up a deep link
+ if (preferenceHelper.lastRunBranchKey && ![key isEqualToString:preferenceHelper.lastRunBranchKey]) {
+ BNCLogWarning(@"The Branch Key has changed, clearing relevant items.");
+
+ preferenceHelper.appVersion = nil;
+ preferenceHelper.deviceFingerprintID = nil;
+ preferenceHelper.sessionID = nil;
+ preferenceHelper.identityID = nil;
+ preferenceHelper.userUrl = nil;
+ preferenceHelper.installParams = nil;
+ preferenceHelper.sessionParams = nil;
+
+ [[BNCServerRequestQueue getInstance] clearQueue];
+ }
+
+ if (self.enableFingerprintIDInCrashlyticsReports) {
+ BNCCrashlyticsWrapper *crashlytics = [BNCCrashlyticsWrapper wrapper];
+ // may be nil
+ [crashlytics setObjectValue:preferenceHelper.deviceFingerprintID forKey:BRANCH_CRASHLYTICS_FINGERPRINT_ID_KEY];
+ }
+
+ preferenceHelper.lastRunBranchKey = key;
+ branch =
+ [[Branch alloc] initWithInterface:[[BNCServerInterface alloc] init]
+ queue:[BNCServerRequestQueue getInstance]
+ cache:[[BNCLinkCache alloc] init]
+ preferenceHelper:preferenceHelper
+ key:key];
+ });
+ return branch;
+ }
+}
+
+
+#pragma mark - URL Generation methods
+
+- (void)generateShortUrl:(NSArray *)tags
+ andAlias:(NSString *)alias
+ andType:(BranchLinkType)type
+ andMatchDuration:(NSUInteger)duration
+ andChannel:(NSString *)channel
+ andFeature:(NSString *)feature
+ andStage:(NSString *)stage
+ andCampaign:campaign andParams:(NSDictionary *)params
+ andCallback:(callbackWithUrl)callback {
+ [self initSessionIfNeededAndNotInProgress];
+
+ BNCLinkData *linkData = [self prepareLinkDataFor:tags
+ andAlias:alias
+ andType:type
+ andMatchDuration:duration
+ andChannel:channel
+ andFeature:feature
+ andStage:stage
+ andCampaign:campaign
+ andParams:params
+ ignoreUAString:nil];
+
+ if ([self.linkCache objectForKey:linkData]) {
+ if (callback) {
+ callback([self.linkCache objectForKey:linkData], nil);
+ }
+ return;
+ }
+
+ BranchShortUrlRequest *req = [[BranchShortUrlRequest alloc] initWithTags:tags
+ alias:alias
+ type:type
+ matchDuration:duration
+ channel:channel
+ feature:feature
+ stage:stage
+ campaign:campaign
+ params:params
+ linkData:linkData
+ linkCache:self.linkCache
+ callback:callback];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+}
+
+- (NSString *)generateShortUrl:(NSArray *)tags
+ andAlias:(NSString *)alias
+ andType:(BranchLinkType)type
+ andMatchDuration:(NSUInteger)duration
+ andChannel:(NSString *)channel
+ andFeature:(NSString *)feature
+ andStage:(NSString *)stage
+ andCampaign:(NSString *)campaign
+ andParams:(NSDictionary *)params
+ ignoreUAString:(NSString *)ignoreUAString
+ forceLinkCreation:(BOOL)forceLinkCreation {
+
+ NSString *shortURL = nil;
+
+ BNCLinkData *linkData =
+ [self prepareLinkDataFor:tags
+ andAlias:alias
+ andType:type
+ andMatchDuration:duration
+ andChannel:channel
+ andFeature:feature
+ andStage:stage
+ andCampaign:campaign
+ andParams:params
+ ignoreUAString:ignoreUAString];
+
+ // If an ignore UA string is present, we always get a new url.
+ // Otherwise, if we've already seen this request, use the cached version.
+ if (!ignoreUAString && [self.linkCache objectForKey:linkData]) {
+ shortURL = [self.linkCache objectForKey:linkData];
+ } else {
+ BranchShortUrlSyncRequest *req =
+ [[BranchShortUrlSyncRequest alloc]
+ initWithTags:tags
+ alias:alias
+ type:type
+ matchDuration:duration
+ channel:channel
+ feature:feature
+ stage:stage
+ campaign:campaign
+ params:params
+ linkData:linkData
+ linkCache:self.linkCache];
+
+ if ((self.initializationStatus == BNCInitStatusInitialized) && !self.preferenceHelper.trackingDisabled) {
+ BNCLogDebug(@"Creating a custom URL synchronously.");
+ BNCServerResponse *serverResponse = [req makeRequest:self.serverInterface key:self.class.branchKey];
+ shortURL = [req processResponse:serverResponse];
+
+ // cache the link
+ if (shortURL) {
+ [self.linkCache setObject:shortURL forKey:linkData];
+ }
+ } else {
+ if (forceLinkCreation) {
+ if (self.class.branchKey) {
+ return [BranchShortUrlSyncRequest createLinkFromBranchKey:self.class.branchKey
+ tags:tags alias:alias type:type matchDuration:duration
+ channel:channel feature:feature stage:stage params:params];
+ }
+ }
+ BNCLogError(@"Making a Branch request before init has succeeded!");
+ }
+ }
+
+ return shortURL;
+}
+
+- (NSString *)generateLongURLWithParams:(NSDictionary *)params
+ andChannel:(NSString *)channel
+ andTags:(NSArray *)tags
+ andFeature:(NSString *)feature
+ andStage:(NSString *)stage
+ andAlias:(NSString *)alias {
+
+ NSString *baseLongUrl = [NSString stringWithFormat:@"%@/a/%@", BNC_LINK_URL, self.class.branchKey];
+
+ return [self longUrlWithBaseUrl:baseLongUrl params:params tags:tags feature:feature
+ channel:nil stage:stage alias:alias duration:0 type:BranchLinkTypeUnlimitedUse];
+}
+
+- (NSString *)longUrlWithBaseUrl:(NSString *)baseUrl
+ params:(NSDictionary *)params
+ tags:(NSArray *)tags
+ feature:(NSString *)feature
+ channel:(NSString *)channel
+ stage:(NSString *)stage
+ alias:(NSString *)alias
+ duration:(NSUInteger)duration
+ type:(BranchLinkType)type {
+
+ NSMutableString *longUrl = [self.preferenceHelper sanitizedMutableBaseURL:baseUrl];
+ for (NSString *tag in tags) {
+ [longUrl appendFormat:@"tags=%@&", [BNCEncodingUtils stringByPercentEncodingStringForQuery:tag]];
+ }
+
+ if ([alias length]) {
+ [longUrl appendFormat:@"alias=%@&", [BNCEncodingUtils stringByPercentEncodingStringForQuery:alias]];
+ }
+
+ if ([channel length]) {
+ [longUrl appendFormat:@"channel=%@&", [BNCEncodingUtils stringByPercentEncodingStringForQuery:channel]];
+ }
+
+ if ([feature length]) {
+ [longUrl appendFormat:@"feature=%@&", [BNCEncodingUtils stringByPercentEncodingStringForQuery:feature]];
+ }
+
+ if ([stage length]) {
+ [longUrl appendFormat:@"stage=%@&", [BNCEncodingUtils stringByPercentEncodingStringForQuery:stage]];
+ }
+ if (type) {
+ [longUrl appendFormat:@"type=%ld&", (long)type];
+ }
+ if (duration) {
+ [longUrl appendFormat:@"matchDuration=%ld&", (long)duration];
+ }
+
+ NSData *jsonData = [BNCEncodingUtils encodeDictionaryToJsonData:params];
+ NSString *base64EncodedParams = [BNCEncodingUtils base64EncodeData:jsonData];
+ [longUrl appendFormat:@"source=ios&data=%@", base64EncodedParams];
+
+ return longUrl;
+}
+
+- (BNCLinkData *)prepareLinkDataFor:(NSArray *)tags
+ andAlias:(NSString *)alias
+ andType:(BranchLinkType)type
+ andMatchDuration:(NSUInteger)duration
+ andChannel:(NSString *)channel
+ andFeature:(NSString *)feature
+ andStage:(NSString *)stage
+ andCampaign:(NSString *)campaign
+ andParams:(NSDictionary *)params
+ ignoreUAString:(NSString *)ignoreUAString {
+
+ BNCLinkData *post = [[BNCLinkData alloc] init];
+
+ [post setupType:type];
+ [post setupTags:tags];
+ [post setupChannel:channel];
+ [post setupFeature:feature];
+ [post setupStage:stage];
+ [post setupCampaign:campaign];
+ [post setupAlias:alias];
+ [post setupMatchDuration:duration];
+ [post setupIgnoreUAString:ignoreUAString];
+ [post setupParams:params];
+
+ return post;
+}
+
+#pragma mark - BranchUniversalObject methods
+
+- (void)registerViewWithParams:(NSDictionary *)params andCallback:(callbackWithParams)callback {
+ [self initSessionIfNeededAndNotInProgress];
+ BranchUniversalObject *buo = [[BranchUniversalObject alloc] init];
+ buo.contentMetadata.customMetadata = (id) params;
+ [[BranchEvent standardEvent:BranchStandardEventViewItem withContentItem:buo] logEvent];
+ if (callback) callback(@{}, nil);
+}
+
+#pragma mark - Application State Change methods
+
+- (void)applicationDidBecomeActive {
+ if (!Branch.trackingDisabled) {
+ if ((self.initializationStatus != BNCInitStatusInitialized) &&
+ !self.preferenceHelper.shouldWaitForInit &&
+ ![self.requestQueue containsInstallOrOpen]) {
+ [self initUserSessionAndCallCallback:YES];
+ }
+ }
+}
+
+- (void)applicationWillResignActive {
+ if (!Branch.trackingDisabled) {
+ [self callClose];
+ [self.requestQueue persistImmediately];
+ [BranchOpenRequest setWaitNeededForOpenResponseLock];
+ BNCLogDebugSDK(@"Application resigned active.");
+ [self.class closeLog];
+ [self.class openLog];
+ }
+}
+
+- (void)callClose {
+ if (self.initializationStatus != BNCInitStatusUninitialized) {
+ self.initializationStatus = BNCInitStatusUninitialized;
+
+ BranchContentDiscoverer *contentDiscoverer = [BranchContentDiscoverer getInstance];
+ if (contentDiscoverer) [contentDiscoverer stopDiscoveryTask];
+
+ if (self.preferenceHelper.sessionID && ![self.requestQueue containsClose]) {
+ BranchCloseRequest *req = [[BranchCloseRequest alloc] init];
+ [self.requestQueue enqueue:req];
+ [self processNextQueueItem];
+ }
+ }
+}
+
+#pragma mark - Queue management
+
+- (NSInteger) networkCount {
+ @synchronized (self) {
+ return _networkCount;
+ }
+}
+
+- (void) setNetworkCount:(NSInteger)networkCount {
+ @synchronized (self) {
+ _networkCount = networkCount;
+ }
+}
+
+- (void)insertRequestAtFront:(BNCServerRequest *)req {
+ if (self.networkCount == 0) {
+ [self.requestQueue insert:req at:0];
+ }
+ else {
+ [self.requestQueue insert:req at:1];
+ }
+}
+
+static inline void BNCPerformBlockOnMainThreadSync(dispatch_block_t block) {
+ if (block) {
+ if ([NSThread isMainThread]) {
+ block();
+ } else {
+ dispatch_sync(dispatch_get_main_queue(), block);
+ }
+ }
+}
+
+//static inline void BNCPerformBlockOnMainThreadAsync(dispatch_block_t block) {
+// dispatch_async(dispatch_get_main_queue(), block);
+//}
+
+- (void) processRequest:(BNCServerRequest*)req
+ response:(BNCServerResponse*)response
+ error:(NSError*)error {
+
+ // If the request was successful, or was a bad user request, continue processing.
+ if (!error ||
+ error.code == BNCTrackingDisabledError ||
+ error.code == BNCBadRequestError ||
+ error.code == BNCDuplicateResourceError) {
+
+ BNCPerformBlockOnMainThreadSync(^{ [req processResponse:response error:error]; });
+
+ [self.requestQueue dequeue];
+ self.networkCount = 0;
+ [self processNextQueueItem];
+ }
+ // On network problems, or Branch down, call the other callbacks and stop processing.
+ else {
+ BNCLogDebugSDK(@"Network error: failing queued requests.");
+
+ // First, gather all the requests to fail
+ NSMutableArray *requestsToFail = [[NSMutableArray alloc] init];
+ for (int i = 0; i < self.requestQueue.queueDepth; i++) {
+ BNCServerRequest *request = [self.requestQueue peekAt:i];
+ if (request) {
+ [requestsToFail addObject:request];
+ }
+ }
+
+ // Next, remove all the requests that should not be replayed. Note, we do this before
+ // calling callbacks, in case any of the callbacks try to kick off another request, which
+ // could potentially start another request (and call these callbacks again)
+
+ NSSet *replayableRequests = [[NSSet alloc] initWithArray:@[
+ BranchEventRequest.class,
+ BranchUserCompletedActionRequest.class,
+ BranchSetIdentityRequest.class,
+ BranchCommerceEventRequest.class,
+ ]];
+
+ for (BNCServerRequest *request in requestsToFail) {
+ if (Branch.trackingDisabled || ![replayableRequests containsObject:request.class]) {
+ [self.requestQueue remove:request];
+ }
+ }
+
+ // Then, set the network count to zero, indicating that requests can be started again
+ self.networkCount = 0;
+
+ // Finally, call all the requests callbacks with the error
+ for (BNCServerRequest *request in requestsToFail) {
+ BNCPerformBlockOnMainThreadSync(^ { [request processResponse:nil error:error]; });
+ }
+ }
+}
+
+- (void)processNextQueueItem {
+ dispatch_semaphore_wait(self.processing_sema, DISPATCH_TIME_FOREVER);
+
+ if (self.networkCount == 0 &&
+ self.requestQueue.queueDepth > 0 &&
+ !self.preferenceHelper.shouldWaitForInit) {
+
+ self.networkCount = 1;
+ dispatch_semaphore_signal(self.processing_sema);
+ BNCServerRequest *req = [self.requestQueue peek];
+
+ if (req) {
+
+ if (Branch.trackingDisabled) {
+ // If tracking is disabled keep failing everything in the queue.
+ }
+ else if (![req isKindOfClass:[BranchInstallRequest class]] && !self.preferenceHelper.identityID) {
+ BNCLogError(@"User session has not been initialized!");
+ BNCPerformBlockOnMainThreadSync(^{
+ [req processResponse:nil error:[NSError branchErrorWithCode:BNCInitError]];
+ });
+ return;
+ }
+ else if (![req isKindOfClass:[BranchOpenRequest class]] &&
+ (!self.preferenceHelper.deviceFingerprintID || !self.preferenceHelper.sessionID)) {
+ BNCLogError(@"Missing session items!");
+ BNCPerformBlockOnMainThreadSync(^{
+ [req processResponse:nil error:[NSError branchErrorWithCode:BNCInitError]];
+ });
+ return;
+ }
+
+ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
+ dispatch_async(queue, ^ {
+ [req makeRequest:self.serverInterface key:self.class.branchKey callback:
+ ^(BNCServerResponse* response, NSError* error) {
+ [self processRequest:req response:response error:error];
+ }];
+ });
+ }
+ }
+ else {
+ dispatch_semaphore_signal(self.processing_sema);
+ }
+}
+
+- (void) clearNetworkQueue {
+ dispatch_semaphore_wait(self.processing_sema, DISPATCH_TIME_FOREVER);
+ self.networkCount = 0;
+ [[BNCServerRequestQueue getInstance] clearQueue];
+ dispatch_semaphore_signal(self.processing_sema);
+}
+
+#pragma mark - Session Initialization
+
+- (void)initSessionIfNeededAndNotInProgress {
+ if (self.initializationStatus == BNCInitStatusUninitialized &&
+ !self.preferenceHelper.shouldWaitForInit &&
+ ![self.requestQueue containsInstallOrOpen]) {
+ [self initUserSessionAndCallCallback:NO];
+ }
+}
+
+- (void)initUserSessionAndCallCallback:(BOOL)callCallback {
+ self.shouldCallSessionInitCallback = callCallback;
+
+ NSString *urlstring = nil;
+ if (self.preferenceHelper.universalLinkUrl.length)
+ urlstring = self.preferenceHelper.universalLinkUrl;
+ else
+ if (self.preferenceHelper.externalIntentURI.length)
+ urlstring = self.preferenceHelper.externalIntentURI;
+
+ if (urlstring.length) {
+ NSArray *queryItems = [BNCEncodingUtils queryItems:[NSURL URLWithString:urlstring]];
+ for (BNCKeyValue*item in queryItems) {
+ if ([item.key isEqualToString:@"BranchLogLevel"]) {
+ BNCLogLevel logLevel = BNCLogLevelFromString(item.value);
+ [[NSUserDefaults standardUserDefaults]
+ setObject:[NSNumber numberWithInteger:logLevel]
+ forKey:BNCLogLevelKey];
+ BNCLogSetDisplayLevel(logLevel);
+ NSLog(@"[io.branch.sdk] BNCLogLevel set to %ld.", (long) logLevel);
+ }
+ }
+ }
+ // If the session is not yet initialized
+ if (self.initializationStatus == BNCInitStatusUninitialized) {
+ [self initializeSession];
+ }
+ // If the session was initialized, but callCallback was specified, do so.
+ else if (callCallback && self.initializationStatus == BNCInitStatusInitialized) {
+ if (self.sessionInitWithParamsCallback) {
+ self.sessionInitWithParamsCallback([self getLatestReferringParams], nil);
+ }
+ else if (self.sessionInitWithBranchUniversalObjectCallback) {
+ self.sessionInitWithBranchUniversalObjectCallback(
+ [self getLatestReferringBranchUniversalObject],
+ [self getLatestReferringBranchLinkProperties],
+ nil
+ );
+ }
+ }
+}
+
+- (void)initializeSession {
+ Class clazz = [BranchInstallRequest class];
+ if (self.preferenceHelper.identityID) {
+ clazz = [BranchOpenRequest class];
+ }
+
+ callbackWithStatus initSessionCallback = ^(BOOL success, NSError *error) {
+ dispatch_async(dispatch_get_main_queue(), ^ {
+ if (error) {
+ [self handleInitFailure:error];
+ } else {
+ [self handleInitSuccess];
+ }
+ });
+ };
+
+ if ([BNCSystemObserver getOSVersion].integerValue >= 9 && self.useCookieBasedMatching) {
+ [[BNCStrongMatchHelper strongMatchHelper] createStrongMatchWithBranchKey:self.class.branchKey];
+ }
+
+ // Notify everyone --
+
+ NSURL *URL =
+ (self.preferenceHelper.referringURL.length)
+ ? [NSURL URLWithString:self.preferenceHelper.referringURL]
+ : nil;
+
+ if ([self.delegate respondsToSelector:@selector(branch:willStartSessionWithURL:)])
+ [self.delegate branch:self willStartSessionWithURL:URL];
+
+ NSMutableDictionary *userInfo = [NSMutableDictionary new];
+ userInfo[BranchURLKey] = URL;
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:BranchWillStartSessionNotification
+ object:self
+ userInfo:userInfo];
+
+ // Fix the queue order and open --
+
+ @synchronized (self) {
+ [self removeInstallOrOpen];
+ [BranchOpenRequest setWaitNeededForOpenResponseLock];
+ BranchOpenRequest *req = [[clazz alloc] initWithCallback:initSessionCallback];
+ [self insertRequestAtFront:req];
+ self.initializationStatus = BNCInitStatusInitializing;
+ [self processNextQueueItem];
+ }
+}
+
+- (BOOL) removeInstallOrOpen {
+ @synchronized (self) {
+ if ([self.requestQueue removeInstallOrOpen]) {
+ self.networkCount = 0;
+ return YES;
+ }
+ return NO;
+ }
+}
+
+- (void)handleInitSuccess {
+
+ self.initializationStatus = BNCInitStatusInitialized;
+ NSDictionary *latestReferringParams = [self getLatestReferringParams];
+
+ if ([latestReferringParams[@"_branch_validate"] isEqualToString:@"060514"]) {
+ [self validateDeeplinkRouting:latestReferringParams];
+ }
+ else if (([latestReferringParams[@"bnc_validate"] isEqualToString:@"true"])) {
+ NSString* referringLink = [self.class returnNonUniversalLink:latestReferringParams[@"~referring_link"] ];
+ NSURLComponents *comp = [NSURLComponents componentsWithURL:[NSURL URLWithString:referringLink]
+ resolvingAgainstBaseURL:NO];
+
+
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ Class applicationClass = NSClassFromString(@"UIApplication");
+ id sharedApplication = [applicationClass performSelector:@selector(sharedApplication)];
+ SEL openURL = @selector(openURL:);
+ if ([sharedApplication respondsToSelector:openURL])
+ [sharedApplication performSelector:openURL withObject:comp.URL];
+ #pragma clang diagnostic pop
+ }
+
+ if (self.shouldCallSessionInitCallback) {
+ if (self.sessionInitWithParamsCallback) {
+ self.sessionInitWithParamsCallback(latestReferringParams, nil);
+ }
+ else if (self.sessionInitWithBranchUniversalObjectCallback) {
+ self.sessionInitWithBranchUniversalObjectCallback(
+ [self getLatestReferringBranchUniversalObject],
+ [self getLatestReferringBranchLinkProperties],
+ nil
+ );
+ }
+ }
+ [self sendOpenNotificationWithLinkParameters:latestReferringParams error:nil];
+
+ if (!self.URLBlackList.hasRefreshedBlackListFromServer)
+ [self.URLBlackList refreshBlackListFromServerWithCompletion:nil];
+
+ if (self.shouldAutomaticallyDeepLink) {
+ // Find any matched keys, then launch any controllers that match
+ // TODO which one to launch if more than one match?
+ NSMutableSet *keysInParams = [NSMutableSet setWithArray:[latestReferringParams allKeys]];
+ NSSet *desiredKeysSet = [NSSet setWithArray:[self.deepLinkControllers allKeys]];
+ [keysInParams intersectSet:desiredKeysSet];
+
+ // If we find a matching key, configure and show the controller
+ if ([keysInParams count]) {
+ NSString *key = [[keysInParams allObjects] firstObject];
+ UIViewController *branchSharingController = self.deepLinkControllers[key];
+ if ([branchSharingController respondsToSelector:@selector(configureControlWithData:)]) {
+ [branchSharingController configureControlWithData:latestReferringParams];
+ }
+ else {
+ BNCLogWarning(@"The automatic deeplink view controller '%@' for key '%@' does not implement 'configureControlWithData:'.",
+ branchSharingController, key);
+ }
+
+ self.deepLinkPresentingController = [UIViewController bnc_currentViewController];
+ if([self.deepLinkControllers[key] isKindOfClass:[BNCDeepLinkViewControllerInstance class]]) {
+ BNCDeepLinkViewControllerInstance* deepLinkInstance = self.deepLinkControllers[key];
+ UIViewController *branchSharingController = deepLinkInstance.viewController;
+
+ if ([branchSharingController respondsToSelector:@selector(configureControlWithData:)]) {
+ [branchSharingController configureControlWithData:latestReferringParams];
+ }
+ else {
+ BNCLogWarning(@"View controller does not implement configureControlWithData:");
+ }
+ branchSharingController.deepLinkingCompletionDelegate = self;
+ switch (deepLinkInstance.option) {
+ case BNCViewControllerOptionPresent:
+ [self presentSharingViewController:branchSharingController];
+ break;
+
+ case BNCViewControllerOptionPush:
+
+ if ([self.deepLinkPresentingController isKindOfClass:[UINavigationController class]]) {
+
+ if ([[(UINavigationController*)self.deepLinkPresentingController viewControllers]
+ containsObject:branchSharingController]) {
+ [self removeViewControllerFromRootNavigationController:branchSharingController];
+ [(UINavigationController*)self.deepLinkPresentingController
+ pushViewController:branchSharingController animated:false];
+ }
+ else {
+ [(UINavigationController*)self.deepLinkPresentingController
+ pushViewController:branchSharingController animated:true];
+ }
+ }
+ else {
+ deepLinkInstance.option = BNCViewControllerOptionPresent;
+ [self presentSharingViewController:branchSharingController];
+ }
+
+ break;
+
+ default:
+ if ([self.deepLinkPresentingController isKindOfClass:[UINavigationController class]]) {
+ if ([self.deepLinkPresentingController respondsToSelector:@selector(showViewController:sender:)]) {
+
+ if ([[(UINavigationController*)self.deepLinkPresentingController viewControllers]
+ containsObject:branchSharingController]) {
+ [self removeViewControllerFromRootNavigationController:branchSharingController];
+ }
+
+ [self.deepLinkPresentingController showViewController:branchSharingController sender:self];
+ }
+ else {
+ deepLinkInstance.option = BNCViewControllerOptionPush;
+ [(UINavigationController*)self.deepLinkPresentingController
+ pushViewController:branchSharingController animated:true];
+ }
+ }
+ else {
+ deepLinkInstance.option = BNCViewControllerOptionPresent;
+ [self presentSharingViewController:branchSharingController];
+ }
+ break;
+ }
+ }
+ else {
+
+ //Support for old API
+ UIViewController *branchSharingController = self.deepLinkControllers[key];
+ if ([branchSharingController respondsToSelector:@selector(configureControlWithData:)]) {
+ [branchSharingController configureControlWithData:latestReferringParams];
+ }
+ else {
+ BNCLogWarning(@"View controller does not implement configureControlWithData:");
+ }
+ branchSharingController.deepLinkingCompletionDelegate = self;
+ if ([self.deepLinkPresentingController presentedViewController]) {
+ [self.deepLinkPresentingController dismissViewControllerAnimated:NO completion:^{
+ [self.deepLinkPresentingController presentViewController:branchSharingController animated:YES completion:NULL];
+ }];
+ }
+ else {
+ [self.deepLinkPresentingController presentViewController:branchSharingController animated:YES completion:NULL];
+ }
+ }
+ }
+ }
+}
+
+- (void) sendOpenNotificationWithLinkParameters:(NSDictionary*)linkParameters
+ error:(NSError*)error {
+
+ NSURL *originalURL =
+ (self.preferenceHelper.referringURL.length)
+ ? [NSURL URLWithString:self.preferenceHelper.referringURL]
+ : nil;
+ BranchLinkProperties *linkProperties = nil;
+ BranchUniversalObject *universalObject = nil;
+
+ NSNumber *isBranchLink = linkParameters[BRANCH_INIT_KEY_CLICKED_BRANCH_LINK];
+ if ([isBranchLink boolValue]) {
+ universalObject = [BranchUniversalObject objectWithDictionary:linkParameters];
+ linkProperties = [BranchLinkProperties getBranchLinkPropertiesFromDictionary:linkParameters];
+ }
+
+ if (error) {
+
+ if ([self.delegate respondsToSelector:@selector(branch:failedToStartSessionWithURL:error:)])
+ [self.delegate branch:self failedToStartSessionWithURL:originalURL error:error];
+
+ } else {
+
+ BranchLink *branchLink = nil;
+ if (universalObject) {
+ branchLink = [BranchLink linkWithUniversalObject:universalObject properties:linkProperties];
+ }
+ if ([self.delegate respondsToSelector:@selector(branch:didStartSessionWithURL:branchLink:)])
+ [self.delegate branch:self didStartSessionWithURL:originalURL branchLink:branchLink];
+
+ }
+
+ NSMutableDictionary *userInfo = [NSMutableDictionary new];
+ userInfo[BranchErrorKey] = error;
+ userInfo[BranchURLKey] = originalURL;
+ userInfo[BranchUniversalObjectKey] = universalObject;
+ userInfo[BranchLinkPropertiesKey] = linkProperties;
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName:BranchDidStartSessionNotification
+ object:self
+ userInfo:userInfo];
+
+ self.preferenceHelper.referringURL = nil;
+}
+
+-(void)removeViewControllerFromRootNavigationController:(UIViewController*)branchSharingController{
+
+ NSMutableArray* viewControllers =
+ [NSMutableArray arrayWithArray: [(UINavigationController*)self.deepLinkPresentingController viewControllers]];
+
+ if ([viewControllers lastObject] == branchSharingController) {
+
+ [(UINavigationController*)self.deepLinkPresentingController popViewControllerAnimated:YES];
+ }else {
+ [viewControllers removeObject:branchSharingController];
+ ((UINavigationController*)self.deepLinkPresentingController).viewControllers = viewControllers;
+ }
+}
+
+-(void)presentSharingViewController: (UIViewController *)branchSharingController{
+ if ([self.deepLinkPresentingController presentedViewController]) {
+ [self.deepLinkPresentingController dismissViewControllerAnimated:NO completion:^{
+ [self.deepLinkPresentingController presentViewController:branchSharingController animated:YES completion:NULL];
+ }];
+ }
+ else {
+ [self.deepLinkPresentingController presentViewController:branchSharingController animated:YES completion:NULL];
+ }
+}
+
+- (void)handleInitFailure:(NSError *)error {
+ self.initializationStatus = BNCInitStatusUninitialized;
+
+ if (self.shouldCallSessionInitCallback) {
+ if (self.sessionInitWithParamsCallback) {
+ self.sessionInitWithParamsCallback([[NSDictionary alloc] init], error);
+ }
+ else if (self.sessionInitWithBranchUniversalObjectCallback) {
+ self.sessionInitWithBranchUniversalObjectCallback(
+ [[BranchUniversalObject alloc] init],
+ [[BranchLinkProperties alloc] init],
+ error
+ );
+ }
+ }
+
+ [self sendOpenNotificationWithLinkParameters:@{} error:error];
+}
+
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+
+#pragma mark - BranchDeepLinkingControllerCompletionDelegate methods
+
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-implementations"
+- (void)deepLinkingControllerCompleted {
+ [self.deepLinkPresentingController dismissViewControllerAnimated:YES completion:NULL];
+}
+#pragma clang diagnostic pop
+
+- (void)deepLinkingControllerCompletedFrom:(UIViewController *)viewController {
+ [self.deepLinkControllers enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
+
+ if([obj isKindOfClass:[BNCDeepLinkViewControllerInstance class]]) {
+ BNCDeepLinkViewControllerInstance* deepLinkInstance = (BNCDeepLinkViewControllerInstance*) obj;
+
+ if (deepLinkInstance.viewController == viewController) {
+
+ switch (deepLinkInstance.option) {
+ case BNCViewControllerOptionPresent:
+ [viewController dismissViewControllerAnimated:YES completion:nil];
+ break;
+
+ default:
+ [self removeViewControllerFromRootNavigationController:viewController];
+ break;
+ }
+ }
+
+ } else {
+ //Support for old API
+ if ((UIViewController*)obj == viewController)
+ [self.deepLinkPresentingController dismissViewControllerAnimated:YES completion:nil];
+ }
+
+ }];
+}
+
+#pragma mark - FABKit methods
+
++ (NSString *)bundleIdentifier {
+ return @"io.branch.sdk.ios";
+}
+
++ (NSString *)kitDisplayVersion {
+ return BNC_SDK_VERSION;
+}
+
+#pragma mark - Crashlytics reporting enhancements
+
++ (void)logLowMemoryToCrashlytics {
+ [NSNotificationCenter.defaultCenter
+ addObserverForName:UIApplicationDidReceiveMemoryWarningNotification
+ object:nil
+ queue:NSOperationQueue.mainQueue
+ usingBlock:^(NSNotification *notification) {
+ BNCCrashlyticsWrapper *crashlytics = [BNCCrashlyticsWrapper wrapper];
+ [crashlytics setBoolValue:YES forKey:BRANCH_CRASHLYTICS_LOW_MEMORY_KEY];
+ }
+ ];
+}
+
++ (void)addBranchSDKVersionToCrashlyticsReport {
+ BNCCrashlyticsWrapper *crashlytics = [BNCCrashlyticsWrapper wrapper];
+ [crashlytics setObjectValue:BNC_SDK_VERSION forKey:BRANCH_CRASHLYTICS_SDK_VERSION_KEY];
+}
+
++ (void) clearAll {
+ [[BNCServerRequestQueue getInstance] clearQueue];
+ [BranchOpenRequest releaseOpenResponseLock];
+ [BNCPreferenceHelper clearAll];
+}
+
+@end
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BranchActivityItemProvider.h b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BranchActivityItemProvider.h
similarity index 100%
rename from ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch.framework/Headers/BranchActivityItemProvider.h
rename to ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BranchActivityItemProvider.h
diff --git a/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BranchActivityItemProvider.m b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BranchActivityItemProvider.m
new file mode 100644
index 00000000000..cf22849a146
--- /dev/null
+++ b/ios/SAPTracker/src/xcode/SAPTracker/Libraries/Branch-SDK/Branch-SDK/BranchActivityItemProvider.m
@@ -0,0 +1,251 @@
+//
+// BranchActivityItemProvider.m
+// Branch-TestBed
+//
+// Created by Scott Hasbrouck on 1/28/15.
+// Copyright (c) 2015 Branch Metrics. All rights reserved.
+//
+
+#import "BranchActivityItemProvider.h"
+#import "Branch.h"
+#import "BranchConstants.h"
+#import "BNCSystemObserver.h"
+#import "BNCDeviceInfo.h"
+
+@interface BranchActivityItemProvider ()
+
+@property (strong, nonatomic) NSDictionary *params;
+@property (strong, nonatomic) NSArray *tags;
+@property (strong, nonatomic) NSString *feature;
+@property (strong, nonatomic) NSString *stage;
+@property (strong, nonatomic) NSString *campaign;
+@property (strong, nonatomic) NSString *alias;
+@property (strong, nonatomic) NSString *userAgentString;
+@property (weak, nonatomic) id delegate;
+
+@end
+
+@implementation BranchActivityItemProvider
+
+- (id)initWithParams:(NSDictionary *)params
+ andTags:(NSArray *)tags
+ andFeature:(NSString *)feature
+ andStage:(NSString *)stage
+ andAlias:(NSString *)alias {
+ return [self initWithParams:params tags:tags feature:feature stage:stage campaign:nil alias:alias delegate:nil];
+}
+
+- (id)initWithParams:(NSDictionary *)params
+ tags:(NSArray *)tags
+ feature:(NSString *)feature
+ stage:(NSString *)stage
+ campaign:(NSString *)campaign
+ alias:(NSString *)alias
+ delegate:(id )delegate {
+
+ NSString *url =
+ [[Branch getInstance]
+ getLongURLWithParams:params
+ andChannel:nil
+ andTags:tags
+ andFeature:feature
+ andStage:stage
+ andAlias:alias];
+
+ if (self.returnURL) {
+ if ((self = [super initWithPlaceholderItem:[NSURL URLWithString:url]])) {
+ _params = params;
+ _tags = tags;
+ _feature = feature;
+ _stage = stage;
+ _campaign = campaign;
+ _alias = alias;
+ _userAgentString = [BNCDeviceInfo userAgentString];
+ _delegate = delegate;
+ }
+ } else {
+ if ((self = [super initWithPlaceholderItem:url])) {
+ _params = params;
+ _tags = tags;
+ _feature = feature;
+ _stage = stage;
+ _campaign = campaign;
+ _alias = alias;
+ _userAgentString = [BNCDeviceInfo userAgentString];
+ _delegate = delegate;
+ }
+ }
+ return self;
+}
+
+- (BOOL) returnURL {
+ BOOL returnURL = YES;
+ if ([UIDevice currentDevice].systemVersion.doubleValue >= 11.0 &&
+ [UIDevice currentDevice].systemVersion.doubleValue < 11.2 &&
+ [self.activityType isEqualToString:UIActivityTypeCopyToPasteboard]) {
+ returnURL = NO;
+ }
+ return returnURL;
+}
+
+- (id)item {
+ NSString *channel = [BranchActivityItemProvider humanReadableChannelWithActivityType:self.activityType];
+
+
+ // Allow for overrides specific to channel
+ NSDictionary *params = [self paramsForChannel:channel];
+ NSArray *tags = [self tagsForChannel:channel];
+ NSString *feature = [self featureForChannel:channel];
+ NSString *stage = [self stageForChannel:channel];
+ NSString *campaign = [self campaignForChannel:channel];
+ NSString *alias = [self aliasForChannel:channel];
+
+ // Allow the channel param to be overridden, perhaps they want "fb" instead of "facebook"
+ if ([self.delegate respondsToSelector:@selector(activityItemOverrideChannelForChannel:)]) {
+ channel = [self.delegate activityItemOverrideChannelForChannel:channel];
+ }
+
+ // Because Facebook et al immediately scrape URLs, we add an additional parameter to the
+ // existing list, telling the backend to ignore the first click
+ NSArray *scrapers = @[
+ @"Facebook",
+ @"Twitter",
+ @"Slack",
+ @"Apple Notes",
+ @"Skype",
+ @"SMS"
+ ];
+ for (NSString *scraper in scrapers) {
+ if ([channel isEqualToString:scraper]) {
+ NSURL *URL = [NSURL URLWithString:[[Branch getInstance]
+ getShortURLWithParams:params
+ andTags:tags
+ andChannel:channel
+ andFeature:feature
+ andStage:stage
+ andCampaign:campaign
+ andAlias:alias
+ ignoreUAString:self.userAgentString
+ forceLinkCreation:YES]];
+ return (self.returnURL) ? URL : URL.absoluteString;
+ }
+ }
+
+ // Wrap the link in HTML content
+ if (self.activityType == UIActivityTypeMail &&
+ [params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_HEADER] &&
+ [params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_FOOTER]) {
+ NSURL *link = [NSURL URLWithString:[[Branch getInstance]
+ getShortURLWithParams:params
+ andTags:tags
+ andChannel:channel
+ andFeature:feature
+ andStage:stage
+ andCampaign:campaign
+ andAlias:alias
+ ignoreUAString:nil
+ forceLinkCreation:YES]];
+ NSString *emailLink;
+ if ([params objectForKey:BRANCH_LINK_DATA_KEY_EMAIL_HTML_LINK_TEXT]) {
+ emailLink = [NSString stringWithFormat:@"