
Mapr collects data solely for analytics purposes. The data we collect is used to improve the app, understand usage patterns, and measure performance. Collected analytics data is deleted from our systems — we do not use it for advertising or other profiling purposes.
The following is the exact analytics data collected by Mapr (source code):
//
// AnalyticsService.swift
// Mapr
//
// Created by vegar berentsen on 27/01/2024.
//
import SwiftUI
import CloudKit
import CoreLocation
class AnalyticsService: NSObject, CLLocationManagerDelegate {
static let shared = AnalyticsService()
private var hasTrackedInitialTab = false
// Reference to the specific CloudKit container for analytics
let container = CKContainer(identifier: "iCloud.MaprAnalytics")
var publicDatabase: CKDatabase {
return container.publicCloudDatabase
}
private var sessionStart: Date?
func startSession() {
sessionStart = Date()
}
private let locationManager = CLLocationManager()
private var currentLocation: CLLocation?
override init() {
super.init()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization() // or requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.last
}
// --- START OF MODIFICATION ---
// The function now accepts an optional completion handler to signal when the save is complete.
func endSession(completion: (() -> Void)? = nil) {
guard let start = sessionStart else {
completion?()
return
}
let sessionEnd = Date()
let duration = sessionEnd.timeIntervalSince(start)
let record = CKRecord(recordType: "AppOpenEvent")
record["timestamp"] = start
record["sessionDuration"] = duration
record["appVersion"] = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
#if os(iOS)
// iOS-specific data
record["platform"] = "iOS"
record["deviceId"] = UIDevice.current.identifierForVendor?.uuidString ?? "Unknown"
record["osVersion"] = UIDevice.current.systemVersion
record["deviceModel"] = UIDevice.current.model
#elseif os(macOS)
// macOS-specific data
record["platform"] = "macOS"
// Get/create a persistent anonymous device ID from UserDefaults
let userDefaults = UserDefaults.standard
let deviceIdKey = "analyticsDeviceId"
let deviceId: String
if let existingId = userDefaults.string(forKey: deviceIdKey) {
deviceId = existingId
} else {
let newId = UUID().uuidString
userDefaults.set(newId, forKey: deviceIdKey)
deviceId = newId
}
record["deviceId"] = deviceId
record["osVersion"] = ProcessInfo.processInfo.operatingSystemVersionString
// Get Mac model identifier (e.g., "MacBookPro18,1")
var size = 0
sysctlbyname("hw.model", nil, &size, nil, 0)
var machine = [CChar](repeating: 0, count: size)
sysctlbyname("hw.model", &machine, &size, nil, 0)
let modelIdentifier = String(cString: machine)
record["deviceModel"] = modelIdentifier
#endif
record["locale"] = Locale.current.identifier
if let location = currentLocation {
record["location"] = CLLocation(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
}
if let userIdentifier = SignInWithAppleManager.shared.userIdentifier {
record["userId"] = userIdentifier
}
// Save the record to the public CloudKit database
publicDatabase.save(record) { record, error in
if let error = error {
print("CloudKit save error: \(error)")
} else {
print("Successfully saved AppOpenEvent, to the public db")
}
// Signal completion regardless of success or error so the app can quit.
completion?()
}
}
// --- END OF MODIFICATION ---
func trackInitialTabSelection(tabNumber: Int) {
if !hasTrackedInitialTab {
trackTabSelection(tabNumber: tabNumber)
hasTrackedInitialTab = true
}
}
func trackTabSelection(tabNumber: Int) {
let record = CKRecord(recordType: "TabSelectionEvent")
record["timestamp"] = Date()
record["tabNumber"] = tabNumber
record["userId"] = SignInWithAppleManager.shared.userIdentifier ?? "Unknown"
publicDatabase.save(record) { record, error in
if let error = error {
print("CloudKit save error: \(error)")
} else {
print("Successfully saved TabSelectionEvent")
}
}
}
func trackDownload() {
let record = CKRecord(recordType: "DownloadEvent")
record["timestamp"] = Date()
record["userId"] = SignInWithAppleManager.shared.userIdentifier ?? "Unknown"
publicDatabase.save(record) { record, error in
if let error = error {
print("CloudKit save error: \(error)")
} else {
print("Successfully saved DownloadEvent")
}
}
}
func trackRetentionRates() {
let record = CKRecord(recordType: "RetentionEvent")
record["timestamp"] = Date()
record["userId"] = SignInWithAppleManager.shared.userIdentifier ?? "Unknown"
// Logic to calculate retention rates and update record fields accordingly
publicDatabase.save(record) { record, error in
if let error = error {
print("CloudKit save error: \(error)")
} else {
print("Successfully saved RetentionEvent")
}
}
}
func trackActiveUser() {
let record = CKRecord(recordType: "ActiveUserEvent")
record["timestamp"] = Date()
record["userId"] = SignInWithAppleManager.shared.userIdentifier ?? "Unknown"
publicDatabase.save(record) { record, error in
if let error = error {
print("CloudKit save error: \(error)
")
} else {
print("Successfully saved ActiveUserEvent")
}
}
}
func trackUserBehavior(eventType: String, details: [String: Any]) {
let record = CKRecord(recordType: "UserBehaviorEvent")
record["timestamp"] = Date()
record["eventType"] = eventType
record["userId"] = SignInWithAppleManager.shared.userIdentifier ?? "Unknown"
for (key, value) in details {
record[key] = value as? CKRecordValue
}
publicDatabase.save(record) { record, error in
if let error = error {
print("CloudKit save error: \(error)")
} else {
print("Successfully saved UserBehaviorEvent")
}
}
}
// --- MODIFIED FUNCTION ---
/// Tracks when a user prints a document.
/// This should be called from the ViewModel responsible for printing.
func trackPrintEvent(documentName: String, pageCount: Int, phase: String) { // Added phase
let record = CKRecord(recordType: "PrintEvent")
record["timestamp"] = Date()
record["documentName"] = documentName
record["pageCount"] = pageCount
record["projectPhase"] = phase // Added phase field
record["userId"] = SignInWithAppleManager.shared.userIdentifier ?? "Unknown"
publicDatabase.save(record) { record, error in
if let error = error {
print("CloudKit save error (PrintEvent): \(error)")
} else {
print("Successfully saved PrintEvent with phase: \(phase)")
}
}
}
// --- END OF MODIFICATION ---
}
Deletion: collected analytics data is deleted from our systems. If you would like more information or want to request deletion related to your account, please contact us directly from the app support channels.