Skip to content

Commit

Permalink
improved code formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
spnkr committed Aug 16, 2023
1 parent d0baddd commit 8a3a7e8
Showing 1 changed file with 120 additions and 120 deletions.
240 changes: 120 additions & 120 deletions Sources/ActiveCoreData/DataStore/CoreDataPlusStore+BackupToFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,60 @@ import CoreData
///
/// Call `deleteDirectory` when you no longer need the file.
struct TemporaryFile {
let directoryURL: URL
let fileURL: URL
/// Deletes the temporary directory and all files in it.
let deleteDirectory: () throws -> Void

/// Creates a temporary directory with a unique name and initializes the
/// receiver with a `fileURL` representing a file named `filename` in that
/// directory.
///
/// - Note: This doesn't create the file!
init(creatingTempDirectoryForFilename filename: String) throws {
let (directory, deleteDirectory) = try FileManager.default
.urlForUniqueTemporaryDirectory()
self.directoryURL = directory
self.fileURL = directory.appendingPathComponent(filename)
self.deleteDirectory = deleteDirectory
}
let directoryURL: URL
let fileURL: URL
/// Deletes the temporary directory and all files in it.
let deleteDirectory: () throws -> Void
/// Creates a temporary directory with a unique name and initializes the
/// receiver with a `fileURL` representing a file named `filename` in that
/// directory.
///
/// - Note: This doesn't create the file!
init(creatingTempDirectoryForFilename filename: String) throws {
let (directory, deleteDirectory) = try FileManager.default
.urlForUniqueTemporaryDirectory()
self.directoryURL = directory
self.fileURL = directory.appendingPathComponent(filename)
self.deleteDirectory = deleteDirectory
}
}

extension FileManager {
/// Creates a temporary directory with a unique name and returns its URL.
///
/// - Returns: A tuple of the directory's URL and a delete function.
/// Call the function to delete the directory after you're done with it.
///
/// - Note: You should not rely on the existence of the temporary directory
/// after the app is exited.
func urlForUniqueTemporaryDirectory(preferredName: String? = nil) throws
-> (url: URL, deleteDirectory: () throws -> Void)
{
let basename = preferredName ?? UUID().uuidString

var counter = 0
var createdSubdirectory: URL? = nil
repeat {
do {
let subdirName = counter == 0 ? basename : "\(basename)-\(counter)"
let subdirectory = temporaryDirectory
.appendingPathComponent(subdirName, isDirectory: true)
try createDirectory(at: subdirectory, withIntermediateDirectories: false)
createdSubdirectory = subdirectory
} catch CocoaError.fileWriteFileExists {
// Catch file exists error and try again with another name.
// Other errors propagate to the caller.
counter += 1
}
} while createdSubdirectory == nil

let directory = createdSubdirectory!
let deleteDirectory: () throws -> Void = {
try self.removeItem(at: directory)
/// Creates a temporary directory with a unique name and returns its URL.
///
/// - Returns: A tuple of the directory's URL and a delete function.
/// Call the function to delete the directory after you're done with it.
///
/// - Note: You should not rely on the existence of the temporary directory
/// after the app is exited.
func urlForUniqueTemporaryDirectory(preferredName: String? = nil) throws
-> (url: URL, deleteDirectory: () throws -> Void)
{
let basename = preferredName ?? UUID().uuidString

var counter = 0
var createdSubdirectory: URL? = nil
repeat {
do {
let subdirName = counter == 0 ? basename : "\(basename)-\(counter)"
let subdirectory = temporaryDirectory
.appendingPathComponent(subdirName, isDirectory: true)
try createDirectory(at: subdirectory, withIntermediateDirectories: false)
createdSubdirectory = subdirectory
} catch CocoaError.fileWriteFileExists {
// Catch file exists error and try again with another name.
// Other errors propagate to the caller.
counter += 1
}
} while createdSubdirectory == nil

let directory = createdSubdirectory!
let deleteDirectory: () throws -> Void = {
try self.removeItem(at: directory)
}
return (directory, deleteDirectory)
}
return (directory, deleteDirectory)
}
}


Expand All @@ -73,55 +73,55 @@ extension FileManager {
/// - Returns: The URL of the backup file, wrapped in a TemporaryFile instance
/// for easy deletion.
extension NSPersistentStoreCoordinator {
func backupPersistentStore(atIndex index: Int) throws -> TemporaryFile {
// Inspiration: https://stackoverflow.com/a/22672386
// Documentation for NSPersistentStoreCoordinate.migratePersistentStore:
// "After invocation of this method, the specified [source] store is
// removed from the coordinator and thus no longer a useful reference."
// => Strategy:
// 1. Create a new "intermediate" NSPersistentStoreCoordinator and add
// the original store file.
// 2. Use this new PSC to migrate to a new file URL.
// 3. Drop all reference to the intermediate PSC.
precondition(persistentStores.indices.contains(index), "Index \(index) doesn't exist in persistentStores array")
let sourceStore = persistentStores[index]
let backupCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)

let intermediateStoreOptions = (sourceStore.options ?? [:])
.merging([NSReadOnlyPersistentStoreOption: true],
uniquingKeysWith: { $1 })
let intermediateStore = try backupCoordinator.addPersistentStore(
ofType: sourceStore.type,
configurationName: sourceStore.configurationName,
at: sourceStore.url,
options: intermediateStoreOptions
)

let backupStoreOptions: [AnyHashable: Any] = [
NSReadOnlyPersistentStoreOption: true,
// Disable write-ahead logging. Benefit: the entire store will be
// contained in a single file. No need to handle -wal/-shm files.
// https://developer.apple.com/library/content/qa/qa1809/_index.html
NSSQLitePragmasOption: ["journal_mode": "DELETE"],
// Minimize file size
NSSQLiteManualVacuumOption: true,
]

// Filename format: basename-date.sqlite
// E.g. "MyStore-20180221T200731.sqlite" (time is in UTC)
func makeFilename() -> String {
let basename = sourceStore.url?.deletingPathExtension().lastPathComponent ?? "store-backup"
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withYear, .withMonth, .withDay, .withTime]
let dateString = dateFormatter.string(from: Date())
return "\(basename)-\(dateString).sqlite"
func backupPersistentStore(atIndex index: Int) throws -> TemporaryFile {
// Inspiration: https://stackoverflow.com/a/22672386
// Documentation for NSPersistentStoreCoordinate.migratePersistentStore:
// "After invocation of this method, the specified [source] store is
// removed from the coordinator and thus no longer a useful reference."
// => Strategy:
// 1. Create a new "intermediate" NSPersistentStoreCoordinator and add
// the original store file.
// 2. Use this new PSC to migrate to a new file URL.
// 3. Drop all reference to the intermediate PSC.
precondition(persistentStores.indices.contains(index), "Index \(index) doesn't exist in persistentStores array")
let sourceStore = persistentStores[index]
let backupCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)

let intermediateStoreOptions = (sourceStore.options ?? [:])
.merging([NSReadOnlyPersistentStoreOption: true],
uniquingKeysWith: { $1 })
let intermediateStore = try backupCoordinator.addPersistentStore(
ofType: sourceStore.type,
configurationName: sourceStore.configurationName,
at: sourceStore.url,
options: intermediateStoreOptions
)

let backupStoreOptions: [AnyHashable: Any] = [
NSReadOnlyPersistentStoreOption: true,
// Disable write-ahead logging. Benefit: the entire store will be
// contained in a single file. No need to handle -wal/-shm files.
// https://developer.apple.com/library/content/qa/qa1809/_index.html
NSSQLitePragmasOption: ["journal_mode": "DELETE"],
// Minimize file size
NSSQLiteManualVacuumOption: true,
]

// Filename format: basename-date.sqlite
// E.g. "MyStore-20180221T200731.sqlite" (time is in UTC)
func makeFilename() -> String {
let basename = sourceStore.url?.deletingPathExtension().lastPathComponent ?? "store-backup"
let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withYear, .withMonth, .withDay, .withTime]
let dateString = dateFormatter.string(from: Date())
return "\(basename)-\(dateString).sqlite"
}

let backupFilename = makeFilename()
let backupFile = try TemporaryFile(creatingTempDirectoryForFilename: backupFilename)
try backupCoordinator.migratePersistentStore(intermediateStore, to: backupFile.fileURL, options: backupStoreOptions, withType: NSSQLiteStoreType)
return backupFile
}

let backupFilename = makeFilename()
let backupFile = try TemporaryFile(creatingTempDirectoryForFilename: backupFilename)
try backupCoordinator.migratePersistentStore(intermediateStore, to: backupFile.fileURL, options: backupStoreOptions, withType: NSSQLiteStoreType)
return backupFile
}
}

extension ActiveCoreDataStore {
Expand All @@ -133,29 +133,29 @@ extension ActiveCoreDataStore {
/// - fileName: Name of the file to save to
/// - completion: The URL on the filesystem of the backup is permanent. If you want to remove it, use `FileManager` to delete the file once you're finished using it.
public func backupToFile(fileName: String, completion: (URL?)->Void ){
var result: URL?
let storeCoordinator: NSPersistentStoreCoordinator = persistentContainer.persistentStoreCoordinator
do {
let backupFile = try storeCoordinator.backupPersistentStore(atIndex: 0)
defer {
// Delete temporary directory when done
try! backupFile.deleteDirectory()
}
print("The backup is at \"\(backupFile.fileURL.path)\"")

let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destURL = documentsURL.appendingPathComponent("\(fileName).sqlite")

if FileManager.default.fileExists(atPath: destURL.path) {
try! FileManager.default.removeItem(at: destURL)
var result: URL?
let storeCoordinator: NSPersistentStoreCoordinator = persistentContainer.persistentStoreCoordinator
do {
let backupFile = try storeCoordinator.backupPersistentStore(atIndex: 0)
defer {
// Delete temporary directory when done
try! backupFile.deleteDirectory()
}
print("The backup is at \"\(backupFile.fileURL.path)\"")

let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let destURL = documentsURL.appendingPathComponent("\(fileName).sqlite")

if FileManager.default.fileExists(atPath: destURL.path) {
try! FileManager.default.removeItem(at: destURL)
}
try! FileManager.default.copyItem(at: backupFile.fileURL, to: destURL)

completion(destURL)
} catch {
completion(nil)
print("Error backing up Core Data store: \(error)")
}
try! FileManager.default.copyItem(at: backupFile.fileURL, to: destURL)

completion(destURL)
} catch {
completion(nil)
print("Error backing up Core Data store: \(error)")
}

}
}

0 comments on commit 8a3a7e8

Please sign in to comment.