Skip to content
This repository has been archived by the owner on Jan 6, 2023. It is now read-only.

[Review Only] Google Analytics changes #1146

Draft
wants to merge 11 commits into
base: v.next
Choose a base branch
from
37 changes: 32 additions & 5 deletions arcgis-ios-sdk-samples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
0005580F2819C75700224BC6 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0005580E2819C75600224BC6 /* GoogleService-Info.plist */; };
00069C77241C3B3B00FEE0EC /* ConvexHullViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00D4E8DB241AA18E002689DB /* ConvexHullViewController.swift */; };
000FFFB426CB043200E3CE7E /* SetUpLocationDrivenGeotriggers.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 000FFFB326CB043200E3CE7E /* SetUpLocationDrivenGeotriggers.storyboard */; };
000FFFB626CB044700E3CE7E /* SetUpLocationDrivenGeotriggersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000FFFB526CB044700E3CE7E /* SetUpLocationDrivenGeotriggersViewController.swift */; };
Expand Down Expand Up @@ -103,6 +104,7 @@
00ADC3D12464DD3600A3B88D /* IdentifyRasterCellViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00ADC3D02464DD3600A3B88D /* IdentifyRasterCellViewController.swift */; };
00ADC3D22464DDA200A3B88D /* IdentifyRasterCellViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00ADC3D02464DD3600A3B88D /* IdentifyRasterCellViewController.swift */; };
00BA068525311B0D0039EF4D /* hydrography in Resources */ = {isa = PBXBuildFile; fileRef = 00BA068325311B0D0039EF4D /* hydrography */; settings = {ASSET_TAGS = (Hydrography, ); }; };
00BE7B38275AEFD60012CE95 /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 00BE7B37275AEFD60012CE95 /* FirebaseAnalyticsWithoutAdIdSupport */; };
00BE7B3E2764274C0012CE95 /* BrowseBuildingFloors.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00BE7B3D2764274C0012CE95 /* BrowseBuildingFloors.storyboard */; };
00BE7B402764275D0012CE95 /* BrowseBuildingFloorsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00BE7B3F2764275D0012CE95 /* BrowseBuildingFloorsViewController.swift */; };
00BE7B4127642A6C0012CE95 /* BrowseBuildingFloorsViewController.swift in CopyFiles */ = {isa = PBXBuildFile; fileRef = 00BE7B3F2764275D0012CE95 /* BrowseBuildingFloorsViewController.swift */; };
Expand Down Expand Up @@ -1281,6 +1283,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
0005580E2819C75600224BC6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
000FFFB326CB043200E3CE7E /* SetUpLocationDrivenGeotriggers.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SetUpLocationDrivenGeotriggers.storyboard; sourceTree = "<group>"; };
000FFFB526CB044700E3CE7E /* SetUpLocationDrivenGeotriggersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetUpLocationDrivenGeotriggersViewController.swift; sourceTree = "<group>"; };
00132100279B704200E89DD4 /* ShowDeviceLocationUsingIndoorPositioning.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ShowDeviceLocationUsingIndoorPositioning.storyboard; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1928,6 +1931,7 @@
buildActionMask = 2147483647;
files = (
006A786A262F9F15001D2A5E /* ArcGISToolkit in Frameworks */,
00BE7B38275AEFD60012CE95 /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -2591,6 +2595,7 @@
C7E86C8E21751CFE001C65C3 /* Extensions */,
3E23AA071AFC2DE0002E2214 /* Content Display Logic */,
3E23A9E41AFC28F6002E2214 /* Info.plist */,
0005580E2819C75600224BC6 /* GoogleService-Info.plist */,
);
path = "arcgis-ios-sdk-samples";
sourceTree = "<group>";
Expand Down Expand Up @@ -4850,6 +4855,7 @@
name = "ArcGIS Runtime SDK Samples";
packageProductDependencies = (
006A7869262F9F15001D2A5E /* ArcGISToolkit */,
00BE7B37275AEFD60012CE95 /* FirebaseAnalyticsWithoutAdIdSupport */,
);
productName = "arcgis-ios-sdk-samples";
productReference = 3E23A9E01AFC28F6002E2214 /* ArcGIS Runtime SDK Samples.app */;
Expand Down Expand Up @@ -4949,6 +4955,7 @@
mainGroup = 3E23A9D71AFC28F6002E2214;
packageReferences = (
006A7868262F9F15001D2A5E /* XCRemoteSwiftPackageReference "arcgis-runtime-toolkit-ios" */,
00BE7B36275AEFD60012CE95 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
);
productRefGroup = 3E23A9E11AFC28F6002E2214 /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -4993,6 +5000,7 @@
F1F62C4422E65E0900EFD599 /* militaryoverlay.geodatabase in Resources */,
3EBA254A1D300FB500AB0703 /* SimpleRenderer.storyboard in Resources */,
0083AA192628ED18003A28C8 /* ProgressViewController.xib in Resources */,
0005580F2819C75700224BC6 /* GoogleService-Info.plist in Resources */,
D97B7E5F1FD9BFE700E1239D /* Subdivisions.cpg in Resources */,
F1EF67DD27E2AE480049FA9A /* QueryRelatedFeaturesNonSpatialTable.storyboard in Resources */,
3ED028B81B8E3A9800ACA70D /* SublayerVisibility.storyboard in Resources */,
Expand Down Expand Up @@ -5841,8 +5849,11 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 100.15.0;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "com.esri.arcgis-ios-sdk-samples";
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = com.esri.iOSSamples;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand All @@ -5868,9 +5879,12 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 100.15.0;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "com.esri.arcgis-ios-sdk-samples";
MARKETING_VERSION = 100.145.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
);
PRODUCT_BUNDLE_IDENTIFIER = com.esri.iOSSamples;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -5978,6 +5992,14 @@
minimumVersion = 100.15.0;
};
};
00BE7B36275AEFD60012CE95 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 8.10.0;
};
};
Comment on lines +5995 to +6002
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never used Firebase before. What impact does adding this dependency have on app size (how large is the Firebase framework)? Also, will we need to change the App Privacy section of our App Store page?

For what it is worth, I generally am not in favor of adding third part dependencies unless there is an extremely compelling reason to do so. Each dependency, especially one like this that executes code even before that app is useable by the user, is a failure point. Remember a while back when a bunch of prominent apps stopped working for a while because they used a popular third party analytics framework that had a problem? That is the power that dependencies like this hold. In my opinion, it isn't worth it. But that's up to you and the samples team to decide.

Copy link
Collaborator Author

@yo1995 yo1995 Mar 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • app size
    The impact is quite minimum. The previous version (100.13.0.1) of sample viewer app is ~85 MB (can't remember exact number), and the current version (100.13.0.2) is 87 MB, according to App Store on iPhone 11.
  • App Privacy section
    I've included usage data (select sample, category, and source code view) and search history (search bar) policies, prior to the release.
  • third party dependencies
    I agree. I also tried to avoid using 3rd party dependencies in my personal project.
    We researched a few options: Google Analytics, Amazon Pinpoint, Adobe Analytics, and building an AWS based in house solution. Eventually Trevor decided GA is the easiest way to go for now, as we wanted to use it as an experiment to see what data we can get. In the future we may decide to move to other solutions or remove GA dependency.

/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
Expand All @@ -5986,6 +6008,11 @@
package = 006A7868262F9F15001D2A5E /* XCRemoteSwiftPackageReference "arcgis-runtime-toolkit-ios" */;
productName = ArcGISToolkit;
};
00BE7B37275AEFD60012CE95 /* FirebaseAnalyticsWithoutAdIdSupport */ = {
isa = XCSwiftPackageProductDependency;
package = 00BE7B36275AEFD60012CE95 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseAnalyticsWithoutAdIdSupport;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 3E23A9D81AFC28F6002E2214 /* Project object */;
Expand Down
14 changes: 14 additions & 0 deletions arcgis-ios-sdk-samples/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import UIKit
import ArcGIS
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
Expand Down Expand Up @@ -48,6 +49,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
// Enable/disable touches based on settings.
self.setTouchPref()

// Set up Google Analytics 4.
self.setGoogleAnalyticsPref()

// Set license key and/or API key.
application.license()

Expand Down Expand Up @@ -94,6 +98,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
}
}

// MARK: - Google Analytics settings

func setGoogleAnalyticsPref() {
// Enable/disable GA based on settings.
let disableGoogleAnalytics = UserDefaults.standard.bool(forKey: "disableGoogleAnalytics")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is due to an iOS limitation, see: https://stackoverflow.com/questions/36253998

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't an iOS limitation but rather a misunderstanding of the User Defaults API. It has been a while since I've used it, but if I remember quickly, default values must be registered using UserDefaults.register(defaults:). Also, be aware that the setting can change while the app is active, so the setting should be observed for changes as well. In fact, using the initial option, the observation itself can be what enables and disables analytics exclusively.

if !disableGoogleAnalytics {
FirebaseApp.configure()
}
}

// MARK: - Appearance modification

func modifyAppearance() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import UIKit
import Firebase

class CategoriesCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
@IBOutlet private var collectionViewFlowLayout: UICollectionViewFlowLayout!
Expand Down Expand Up @@ -98,12 +99,17 @@ class CategoriesCollectionViewController: UICollectionViewController, UICollecti
let category = categories[indexPath.item]
let controller = storyboard!.instantiateViewController(withIdentifier: "CategoryTableViewController") as! CategoryTableViewController
controller.title = category.name

// Filter and display the favorited samples.
if category.name == "Favorites" {
controller.allSamples = categories
.flatMap { $0.samples.filter(\.isFavorite) }
controller.isFavoritesCategory = true
} else {
// Google Analytics select category event.
Analytics.logEvent("select_category", parameters: [
AnalyticsParameterContentType: category.name
])
// Otherwise, show all samples.
controller.allSamples = category.samples
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import UIKit
import Firebase

class CategoryTableViewController: UITableViewController {
/// The samples to display in the table. Searching adjusts this value
Expand All @@ -29,7 +30,7 @@ class CategoryTableViewController: UITableViewController {
displayedSamples = allSamples
}
}

var searchEngine: SampleSearchEngine?
/// Tracks whether or not it is the favorites category.
var isFavoritesCategory = false
Expand Down Expand Up @@ -90,6 +91,11 @@ class CategoryTableViewController: UITableViewController {
/// Responds to the selected sample being changed.
private func selectedSampleDidChange() {
if let sample = selectedSample {
// Google Analytics select sample event.
Analytics.logEvent("select_sample", parameters: [
AnalyticsParameterContentType: sample.name
])

let indexPathForSample = indexPath(for: sample)
if tableView.indexPathForSelectedRow != indexPathForSample {
tableView.selectRow(at: indexPathForSample, animated: true, scrollPosition: .top)
Expand Down Expand Up @@ -202,7 +208,18 @@ class CategoryTableViewController: UITableViewController {

// Must use the presenting controller when opening from search results or else splitViewController will be nil.
let presentingController: UIViewController? = searchEngine != nil ? presentingViewController : self


// If the sample is selected from search results, log the search term.
if let searchController = presentingViewController?.navigationItem.searchController,
searchController.isActive,
let searchTerm = searchController.searchBar.text?.trimmingCharacters(in: .whitespacesAndNewlines),
!searchTerm.isEmpty {
// Google Analytics search event.
Analytics.logEvent(AnalyticsEventSearch, parameters: [
AnalyticsParameterSearchTerm: searchTerm
])
}

let navController = UINavigationController(rootViewController: controller)

// Don't use large titles on samples.
Expand Down Expand Up @@ -261,8 +278,8 @@ extension CategoryTableViewController: UISearchResultsUpdating {
expandedRowIndexPaths.removeAll()

if searchController.isActive,
let query = searchController.searchBar.text?.trimmingCharacters(in: .whitespacesAndNewlines),
!query.isEmpty {
let query = searchController.searchBar.text?.trimmingCharacters(in: .whitespacesAndNewlines),
!query.isEmpty {
displayedSamples = searchEngine.sortedSamples(matching: query)
} else {
displayedSamples = allSamples
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import UIKit
import WebKit
import Firebase

class SourceCodeViewController: UIViewController, UIAdaptivePresentationControllerDelegate {
/// The view to which the web view is added.
Expand Down Expand Up @@ -56,6 +57,11 @@ class SourceCodeViewController: UIViewController, UIAdaptivePresentationControll
setupToolbarTitle(filename, arrowPointingDown: true)
let htmlString = htmlStringForContent(content)
webView.loadHTMLString(htmlString, baseURL: URL(fileURLWithPath: Bundle.main.bundlePath))

// Google Analytics select code file event.
Analytics.logEvent("select_sourcefile", parameters: [
AnalyticsParameterContentType: filename
])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import UIKit
import Firebase

final class FavoritesBarButtonItem: UIBarButtonItem {
let sample: Sample
Expand All @@ -35,6 +36,12 @@ final class FavoritesBarButtonItem: UIBarButtonItem {
func toggleIsFavorite() {
// Update the bool.
sample.isFavorite.toggle()
if sample.isFavorite {
// Google Analytics set favorite event.
Analytics.logEvent("set_favorite", parameters: [
AnalyticsParameterContentType: sample.name
])
}
// Update the image.
self.image = makeImage(isFavorite: sample.isFavorite)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Disable Google Analytics</string>
yo1995 marked this conversation as resolved.
Show resolved Hide resolved
<key>Key</key>
<string>disableGoogleAnalytics</string>
<key>DefaultValue</key>
<false/>
</dict>
</array>
</dict>
</plist>
2 changes: 2 additions & 0 deletions arcgis-ios-sdk-samples/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>GOOGLE_ANALYTICS_REGISTRATION_WITH_AD_NETWORK_ENABLED</key>
<false/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
Expand Down