Skip to content

Replace deprecated archiving methods #603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 16, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 117 additions & 98 deletions Sources/MixpanelPersistence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,15 @@ class MixpanelPersistence {

let instanceName: String
let mpdb: MPDB
private static let archivedClasses = [NSArray.self, NSDictionary.self, NSSet.self, NSString.self, NSDate.self, NSURL.self, NSNumber.self, NSNull.self]

init(instanceName: String) {
self.instanceName = instanceName
mpdb = MPDB.init(token: instanceName)
}

deinit {
mpdb.close()
mpdb.close()
}

func closeDB() {
Expand Down Expand Up @@ -90,13 +91,13 @@ class MixpanelPersistence {
}
return entities
}

private func entityWithDistinctId(_ entity: InternalProperties, distinctId: String) -> InternalProperties {
var result = entity;
result["$distinct_id"] = distinctId
return result
}

func removeEntitiesInBatch(type: PersistenceType, ids: [Int32]) {
mpdb.deleteRows(type, ids: ids)
}
Expand Down Expand Up @@ -127,15 +128,19 @@ class MixpanelPersistence {
let prefix = "\(MixpanelUserDefaultsKeys.prefix)-\(instanceName)-"
return defaults.object(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.optOutStatus)") as? Bool
}

static func saveTimedEvents(timedEvents: InternalProperties, instanceName: String) {
guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else {
return
}
let prefix = "\(MixpanelUserDefaultsKeys.prefix)-\(instanceName)-"
let timedEventsData = NSKeyedArchiver.archivedData(withRootObject: timedEvents)
defaults.set(timedEventsData, forKey: "\(prefix)\(MixpanelUserDefaultsKeys.timedEvents)")
defaults.synchronize()
do {
let timedEventsData = try NSKeyedArchiver.archivedData(withRootObject: timedEvents, requiringSecureCoding: false)
defaults.set(timedEventsData, forKey: "\(prefix)\(MixpanelUserDefaultsKeys.timedEvents)")
defaults.synchronize()
} catch {
Logger.warn(message: "Failed to archive timed events")
}
}

static func loadTimedEvents(instanceName: String) -> InternalProperties {
Expand All @@ -146,17 +151,26 @@ class MixpanelPersistence {
guard let timedEventsData = defaults.data(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.timedEvents)") else {
return InternalProperties()
}
return NSKeyedUnarchiver.unarchiveObject(with: timedEventsData) as? InternalProperties ?? InternalProperties()
do {
return try NSKeyedUnarchiver.unarchivedObject(ofClasses: archivedClasses, from: timedEventsData) as? InternalProperties ?? InternalProperties()
} catch {
Logger.warn(message: "Failed to unarchive timed events")
return InternalProperties()
}
}

static func saveSuperProperties(superProperties: InternalProperties, instanceName: String) {
guard let defaults = UserDefaults(suiteName: MixpanelUserDefaultsKeys.suiteName) else {
return
}
let prefix = "\(MixpanelUserDefaultsKeys.prefix)-\(instanceName)-"
let superPropertiesData = NSKeyedArchiver.archivedData(withRootObject: superProperties)
defaults.set(superPropertiesData, forKey: "\(prefix)\(MixpanelUserDefaultsKeys.superProperties)")
defaults.synchronize()
do {
let superPropertiesData = try NSKeyedArchiver.archivedData(withRootObject: superProperties, requiringSecureCoding: false)
defaults.set(superPropertiesData, forKey: "\(prefix)\(MixpanelUserDefaultsKeys.superProperties)")
defaults.synchronize()
} catch {
Logger.warn(message: "Failed to archive super properties")
}
}

static func loadSuperProperties(instanceName: String) -> InternalProperties {
Expand All @@ -167,7 +181,12 @@ class MixpanelPersistence {
guard let superPropertiesData = defaults.data(forKey: "\(prefix)\(MixpanelUserDefaultsKeys.superProperties)") else {
return InternalProperties()
}
return NSKeyedUnarchiver.unarchiveObject(with: superPropertiesData) as? InternalProperties ?? InternalProperties()
do {
return try NSKeyedUnarchiver.unarchivedObject(ofClasses: archivedClasses, from: superPropertiesData) as? InternalProperties ?? InternalProperties()
} catch {
Logger.warn(message: "Failed to unarchive super properties")
return InternalProperties()
}
}

static func saveIdentity(_ mixpanelIdentity: MixpanelIdentity, instanceName: String) {
Expand Down Expand Up @@ -245,12 +264,12 @@ class MixpanelPersistence {
MixpanelPersistence.saveSuperProperties(superProperties: superProperties, instanceName: instanceName)
MixpanelPersistence.saveTimedEvents(timedEvents: timedEvents, instanceName: instanceName)
MixpanelPersistence.saveIdentity(MixpanelIdentity.init(
distinctID: distinctId,
peopleDistinctID: peopleDistinctId,
anonymousId: anonymousId,
userId: userId,
alias: alias,
hadPersistedDistinctId: hadPersistedDistinctId), instanceName: instanceName)
distinctID: distinctId,
peopleDistinctID: peopleDistinctId,
anonymousId: anonymousId,
userId: userId,
alias: alias,
hadPersistedDistinctId: hadPersistedDistinctId), instanceName: instanceName)
if let optOutFlag = optOutStatus {
MixpanelPersistence.saveOptOutStatusFlag(value: optOutFlag, instanceName: instanceName)
}
Expand All @@ -260,46 +279,46 @@ class MixpanelPersistence {
private func filePathWithType(_ type: String) -> String? {
let filename = "mixpanel-\(instanceName)-\(type)"
let manager = FileManager.default

#if os(iOS)
let url = manager.urls(for: .libraryDirectory, in: .userDomainMask).last
#else
let url = manager.urls(for: .cachesDirectory, in: .userDomainMask).last
#endif // os(iOS)
#if os(iOS)
let url = manager.urls(for: .libraryDirectory, in: .userDomainMask).last
#else
let url = manager.urls(for: .cachesDirectory, in: .userDomainMask).last
#endif // os(iOS)
guard let urlUnwrapped = url?.appendingPathComponent(filename).path else {
return nil
}

return urlUnwrapped
}

private func unarchiveFromLegacy() -> (eventsQueue: Queue,
peopleQueue: Queue,
groupsQueue: Queue,
superProperties: InternalProperties,
timedEvents: InternalProperties,
distinctId: String,
anonymousId: String?,
userId: String?,
alias: String?,
hadPersistedDistinctId: Bool?,
peopleDistinctId: String?,
peopleUnidentifiedQueue: Queue,
optOutStatus: Bool?) {
peopleQueue: Queue,
groupsQueue: Queue,
superProperties: InternalProperties,
timedEvents: InternalProperties,
distinctId: String,
anonymousId: String?,
userId: String?,
alias: String?,
hadPersistedDistinctId: Bool?,
peopleDistinctId: String?,
peopleUnidentifiedQueue: Queue,
optOutStatus: Bool?) {
let eventsQueue = unarchiveEvents()
let peopleQueue = unarchivePeople()
let groupsQueue = unarchiveGroups()
let optOutStatus = unarchiveOptOutStatus()

let (superProperties,
timedEvents,
distinctId,
anonymousId,
userId,
alias,
hadPersistedDistinctId,
peopleDistinctId,
peopleUnidentifiedQueue) = unarchiveProperties()
timedEvents,
distinctId,
anonymousId,
userId,
alias,
hadPersistedDistinctId,
peopleDistinctId,
peopleUnidentifiedQueue) = unarchiveProperties()

if let eventsFile = filePathWithType(PersistenceType.events.rawValue) {
removeArchivedFile(atPath: eventsFile)
Expand Down Expand Up @@ -331,11 +350,11 @@ class MixpanelPersistence {
peopleUnidentifiedQueue,
optOutStatus)
}

private func unarchiveWithFilePath(_ filePath: String) -> Any? {
if #available(iOS 11.0, macOS 10.13, watchOS 4.0, tvOS 11.0, *) {
guard let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)),
let unarchivedData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) else {
let unarchivedData = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: MixpanelPersistence.archivedClasses, from: data) else {
Logger.info(message: "Unable to read file at path: \(filePath)")
removeArchivedFile(atPath: filePath)
return nil
Expand All @@ -350,96 +369,96 @@ class MixpanelPersistence {
return unarchivedData
}
}

private func removeArchivedFile(atPath filePath: String) {
do {
try FileManager.default.removeItem(atPath: filePath)
} catch let err {
Logger.info(message: "Unable to remove file at path: \(filePath), error: \(err)")
}
}

private func unarchiveEvents() -> Queue {
let data = unarchiveWithType(PersistenceType.events.rawValue)
return data as? Queue ?? []
}

private func unarchivePeople() -> Queue {
let data = unarchiveWithType(PersistenceType.people.rawValue)
return data as? Queue ?? []
}

private func unarchiveGroups() -> Queue {
let data = unarchiveWithType(PersistenceType.groups.rawValue)
return data as? Queue ?? []
}

private func unarchiveOptOutStatus() -> Bool? {
return unarchiveWithType("optOutStatus") as? Bool
}

private func unarchiveProperties() -> (InternalProperties,
InternalProperties,
String,
String?,
String?,
String?,
Bool?,
String?,
Queue) {
let properties = unarchiveWithType("properties") as? InternalProperties
let superProperties =
properties?["superProperties"] as? InternalProperties ?? InternalProperties()
let timedEvents =
properties?["timedEvents"] as? InternalProperties ?? InternalProperties()
let distinctId =
properties?["distinctId"] as? String ?? ""
let anonymousId =
properties?["anonymousId"] as? String ?? nil
let userId =
properties?["userId"] as? String ?? nil
let alias =
properties?["alias"] as? String ?? nil
let hadPersistedDistinctId =
properties?["hadPersistedDistinctId"] as? Bool ?? nil
let peopleDistinctId =
properties?["peopleDistinctId"] as? String ?? nil
let peopleUnidentifiedQueue =
properties?["peopleUnidentifiedQueue"] as? Queue ?? Queue()
InternalProperties,
String,
String?,
String?,
String?,
Bool?,
String?,
Queue) {
let properties = unarchiveWithType("properties") as? InternalProperties
let superProperties =
properties?["superProperties"] as? InternalProperties ?? InternalProperties()
let timedEvents =
properties?["timedEvents"] as? InternalProperties ?? InternalProperties()
let distinctId =
properties?["distinctId"] as? String ?? ""
let anonymousId =
properties?["anonymousId"] as? String ?? nil
let userId =
properties?["userId"] as? String ?? nil
let alias =
properties?["alias"] as? String ?? nil
let hadPersistedDistinctId =
properties?["hadPersistedDistinctId"] as? Bool ?? nil
let peopleDistinctId =
properties?["peopleDistinctId"] as? String ?? nil
let peopleUnidentifiedQueue =
properties?["peopleUnidentifiedQueue"] as? Queue ?? Queue()

return (superProperties,
timedEvents,
distinctId,
anonymousId,
userId,
alias,
hadPersistedDistinctId,
peopleDistinctId,
peopleUnidentifiedQueue)
return (superProperties,
timedEvents,
distinctId,
anonymousId,
userId,
alias,
hadPersistedDistinctId,
peopleDistinctId,
peopleUnidentifiedQueue)
}

private func unarchiveWithType(_ type: String) -> Any? {
let filePath = filePathWithType(type)
guard let path = filePath else {
Logger.info(message: "bad file path, cant fetch file")
return nil
}

guard let unarchivedData = unarchiveWithFilePath(path) else {
Logger.info(message: "can't unarchive file")
return nil
}

return unarchivedData
}

private func needMigration() -> Bool {
return fileExists(type: LegacyArchiveType.events.rawValue) ||
fileExists(type: LegacyArchiveType.people.rawValue) ||
fileExists(type: LegacyArchiveType.people.rawValue) ||
fileExists(type: LegacyArchiveType.groups.rawValue) ||
fileExists(type: LegacyArchiveType.properties.rawValue) ||
fileExists(type: LegacyArchiveType.optOutStatus.rawValue)
fileExists(type: LegacyArchiveType.people.rawValue) ||
fileExists(type: LegacyArchiveType.people.rawValue) ||
fileExists(type: LegacyArchiveType.groups.rawValue) ||
fileExists(type: LegacyArchiveType.properties.rawValue) ||
fileExists(type: LegacyArchiveType.optOutStatus.rawValue)
}

private func fileExists(type: String) -> Bool {
Expand Down