diff --git a/Sources/Segment/State.swift b/Sources/Segment/State.swift index 5fba332..3b0e53e 100644 --- a/Sources/Segment/State.swift +++ b/Sources/Segment/State.swift @@ -149,7 +149,6 @@ struct System: State { func reduce(state: System) -> System { var waitingPlugins = state.waitingPlugins - let countBefore = waitingPlugins.count waitingPlugins.removeAll { p in return plugin === p } diff --git a/Sources/Segment/Utilities/Utils.swift b/Sources/Segment/Utilities/Utils.swift index 72c20b9..feba819 100644 --- a/Sources/Segment/Utilities/Utils.swift +++ b/Sources/Segment/Utilities/Utils.swift @@ -75,17 +75,45 @@ extension Optional: Flattenable { } internal func eventStorageDirectory(writeKey: String) -> URL { - #if (os(iOS) || os(watchOS)) && !targetEnvironment(macCatalyst) - let searchPathDirectory = FileManager.SearchPathDirectory.documentDirectory - #else - let searchPathDirectory = FileManager.SearchPathDirectory.cachesDirectory - #endif + let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask) + let appSupportURL = urls[0] + let segmentURL = appSupportURL.appendingPathComponent("segment/\(writeKey)/") + + // Handle one-time migration from old locations + migrateFromOldLocations(writeKey: writeKey, to: segmentURL) - let urls = FileManager.default.urls(for: searchPathDirectory, in: .userDomainMask) - let docURL = urls[0] - let segmentURL = docURL.appendingPathComponent("segment/\(writeKey)/") // try to create it, will fail if already exists, nbd. // tvOS, watchOS regularly clear out data. try? FileManager.default.createDirectory(at: segmentURL, withIntermediateDirectories: true, attributes: nil) return segmentURL } + +private func migrateFromOldLocations(writeKey: String, to newLocation: URL) { + let fm = FileManager.default + + // Get the parent of where our new segment directory should live + let appSupportURL = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] + let newSegmentDir = appSupportURL.appendingPathComponent("segment") + + // If segment dir already exists in app support, we're done + guard !fm.fileExists(atPath: newSegmentDir.path) else { return } + + // Only check the old location that was actually used on this platform + #if (os(iOS) || os(watchOS)) && !targetEnvironment(macCatalyst) + let oldSearchPath = FileManager.SearchPathDirectory.documentDirectory + #else + let oldSearchPath = FileManager.SearchPathDirectory.cachesDirectory + #endif + + guard let oldBaseURL = fm.urls(for: oldSearchPath, in: .userDomainMask).first else { return } + let oldSegmentDir = oldBaseURL.appendingPathComponent("segment") + + guard fm.fileExists(atPath: oldSegmentDir.path) else { return } + + do { + try fm.moveItem(at: oldSegmentDir, to: newSegmentDir) + Analytics.segmentLog(message: "Migrated analytics data from \(oldSegmentDir.path)", kind: .debug) + } catch { + Analytics.segmentLog(message: "Failed to migrate from \(oldSegmentDir.path): \(error)", kind: .error) + } +} diff --git a/Tests/Segment-Tests/Storage_Tests.swift b/Tests/Segment-Tests/Storage_Tests.swift index 116fdc7..d931fc3 100644 --- a/Tests/Segment-Tests/Storage_Tests.swift +++ b/Tests/Segment-Tests/Storage_Tests.swift @@ -137,7 +137,10 @@ class StorageTests: XCTestCase { } func testFilePrepAndFinish() { - let analytics = Analytics(configuration: Configuration(writeKey: "test")) + let config = Configuration(writeKey: "test") + .storageMode(.diskAtURL(URL(fileURLWithPath: NSTemporaryDirectory()))) + let analytics = Analytics(configuration: config) + analytics.storage.hardReset(doYouKnowHowToUseThis: true) analytics.waitUntilStarted() @@ -302,4 +305,45 @@ class StorageTests: XCTestCase { let remaining = analytics.storage.read(.events) XCTAssertNil(remaining) } + + func testMigrationFromOldLocation() { + let writeKey = "test-migration" + let fm = FileManager.default + + // Clean slate + let appSupportURL = fm.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] + let newSegmentDir = appSupportURL.appendingPathComponent("segment") + try? fm.removeItem(at: newSegmentDir) + + // Create fake old data in the platform-specific old location + #if (os(iOS) || os(watchOS)) && !targetEnvironment(macCatalyst) + let oldSearchPath = FileManager.SearchPathDirectory.documentDirectory + #else + let oldSearchPath = FileManager.SearchPathDirectory.cachesDirectory + #endif + + let oldBaseURL = fm.urls(for: oldSearchPath, in: .userDomainMask)[0] + let oldSegmentDir = oldBaseURL.appendingPathComponent("segment/\(writeKey)") + try! fm.createDirectory(at: oldSegmentDir, withIntermediateDirectories: true, attributes: nil) + + // Write some fake event files + let testFile1 = oldSegmentDir.appendingPathComponent("0-segment-events.temp") + let testFile2 = oldSegmentDir.appendingPathComponent("1-segment-events.temp") + try! "fake event data 1".write(to: testFile1, atomically: true, encoding: .utf8) + try! "fake event data 2".write(to: testFile2, atomically: true, encoding: .utf8) + + // Trigger migration + let resultURL = eventStorageDirectory(writeKey: writeKey) + + // Verify migration worked + XCTAssertTrue(fm.fileExists(atPath: resultURL.path)) + XCTAssertTrue(fm.fileExists(atPath: resultURL.appendingPathComponent("0-segment-events.temp").path)) + XCTAssertTrue(fm.fileExists(atPath: resultURL.appendingPathComponent("1-segment-events.temp").path)) + + // Verify old directory is gone + XCTAssertFalse(fm.fileExists(atPath: oldSegmentDir.path)) + + // Clean up + try? fm.removeItem(at: newSegmentDir) + } }