diff --git a/LeStorage.xcodeproj/project.pbxproj b/LeStorage.xcodeproj/project.pbxproj index 77ee112..c4e47b2 100644 --- a/LeStorage.xcodeproj/project.pbxproj +++ b/LeStorage.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ C425D4442B6D24E1002A7B48 /* LeStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4432B6D24E1002A7B48 /* LeStorageTests.swift */; }; C425D4452B6D24E1002A7B48 /* LeStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = C425D4372B6D24E1002A7B48 /* LeStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; C425D4582B6D2519002A7B48 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = C425D4572B6D2519002A7B48 /* Store.swift */; }; + C49EF0242BD6BDC50077B5AA /* FileManager+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EF0232BD6BDC50077B5AA /* FileManager+Extensions.swift */; }; C4A47D4F2B6D280200ADC637 /* StoredCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D4E2B6D280200ADC637 /* StoredCollection.swift */; }; C4A47D512B6D2C4E00ADC637 /* Codable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D502B6D2C4E00ADC637 /* Codable+Extensions.swift */; }; C4A47D532B6D2C5F00ADC637 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A47D522B6D2C5F00ADC637 /* Logger.swift */; }; @@ -46,6 +47,7 @@ C425D43E2B6D24E1002A7B48 /* LeStorageTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LeStorageTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C425D4432B6D24E1002A7B48 /* LeStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeStorageTests.swift; sourceTree = ""; }; C425D4572B6D2519002A7B48 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; }; + C49EF0232BD6BDC50077B5AA /* FileManager+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+Extensions.swift"; sourceTree = ""; }; C4A47D4E2B6D280200ADC637 /* StoredCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredCollection.swift; sourceTree = ""; }; C4A47D502B6D2C4E00ADC637 /* Codable+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Codable+Extensions.swift"; sourceTree = ""; }; C4A47D522B6D2C5F00ADC637 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; @@ -136,6 +138,7 @@ C4A47D832B7B97F000ADC637 /* KeychainStore.swift */, C4A47D522B6D2C5F00ADC637 /* Logger.swift */, C4A47D6A2B71244100ADC637 /* Collection+Extension.swift */, + C49EF0232BD6BDC50077B5AA /* FileManager+Extensions.swift */, ); path = Utils; sourceTree = ""; @@ -281,6 +284,7 @@ C4A47D812B7665AD00ADC637 /* Migration.swift in Sources */, C4A47D9B2B7CFFDA00ADC637 /* ApiCall.swift in Sources */, C4A47D942B7CF7C500ADC637 /* MicroStorage.swift in Sources */, + C49EF0242BD6BDC50077B5AA /* FileManager+Extensions.swift in Sources */, C425D4582B6D2519002A7B48 /* Store.swift in Sources */, C4A47D6B2B71244100ADC637 /* Collection+Extension.swift in Sources */, ); diff --git a/LeStorage/Store.swift b/LeStorage/Store.swift index f9becb8..14deaab 100644 --- a/LeStorage/Store.swift +++ b/LeStorage/Store.swift @@ -68,7 +68,11 @@ public class Store { fileprivate var settingsStorage: MicroStorage = MicroStorage() - public init() { } + static let storageDirectory = "storage" + + public init() { + FileManager.default.createDirectoryInDocuments(directoryName: Store.storageDirectory) + } /// Registers a collection /// [synchronize] denotes a collection which modification will be sent to the django server diff --git a/LeStorage/StoredCollection.swift b/LeStorage/StoredCollection.swift index bc1e8c1..87622c0 100644 --- a/LeStorage/StoredCollection.swift +++ b/LeStorage/StoredCollection.swift @@ -89,6 +89,24 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti self._load() } + // MARK: - Paths + + fileprivate func _storageDirectoryPath() throws -> URL { + return try FileUtils.pathForDirectoryInDocuments(directory: Store.storageDirectory) + } + + fileprivate func _writeToStorageDirectory(content: String, fileName: String) throws { + var fileURL = try self._storageDirectoryPath() + fileURL.append(component: fileName) + try content.write(to: fileURL, atomically: false, encoding: .utf8) + } + + fileprivate func _pathForFile(_ fileName: String) throws -> URL { + var storageDirectory = try self._storageDirectoryPath() + storageDirectory.append(component: fileName) + return storageDirectory + } + // MARK: - Loading /// Migrates if necessary and asynchronously decodes the json file @@ -107,7 +125,7 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti } fileprivate func _loadFromFile() throws { - let url: URL = try FileUtils.pathForFileInDocumentDirectory(T.fileName()) + let url: URL = try self._pathForFile(T.fileName()) if FileManager.default.fileExists(atPath: url.path()) { if self.asynchronousIO { @@ -124,7 +142,8 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti /// Decodes the json file into the items array fileprivate func _decodeJSONFile() throws { - let jsonString: String = try FileUtils.readDocumentFile(fileName: T.fileName()) + let fileURL = try self._pathForFile(T.fileName()) + let jsonString: String = try FileUtils.readFile(fileURL: fileURL) if let decoded: [T] = try jsonString.decodeArray() { DispatchQueue.main.async { Logger.log("loaded \(T.fileName()) with \(decoded.count) items") @@ -279,7 +298,8 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti Logger.log("Start write...") do { let jsonString: String = try self.items.jsonString() - let _ = try FileUtils.writeToDocumentDirectory(content: jsonString, fileName: T.fileName()) + try self._writeToStorageDirectory(content: jsonString, fileName: T.fileName()) +// let _ = try FileUtils.writeToDocumentDirectory(content: jsonString, fileName: T.fileName()) } catch { Logger.error(error) // TODO how to notify the main project } @@ -476,8 +496,4 @@ public class StoredCollection: RandomAccessCollection, SomeCollecti } } -// public func append(_ newElement: T) { -// self.addOrUpdate(instance: newElement) -// } - } diff --git a/LeStorage/Utils/FileManager+Extensions.swift b/LeStorage/Utils/FileManager+Extensions.swift new file mode 100644 index 0000000..7b81750 --- /dev/null +++ b/LeStorage/Utils/FileManager+Extensions.swift @@ -0,0 +1,25 @@ +// +// FileManager+Extensions.swift +// LeStorage +// +// Created by Laurent Morvillier on 22/04/2024. +// + +import Foundation + +extension FileManager { + + func createDirectoryInDocuments(directoryName: String) { + let documentsDirectory = self.urls(for: .documentDirectory, in: .userDomainMask).first! + let directoryURL = documentsDirectory.appendingPathComponent(directoryName) + + if !self.fileExists(atPath: directoryURL.path) { + do { + try self.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) + } catch { + Logger.error(error) + } + } + } + +} diff --git a/LeStorage/Utils/FileUtils.swift b/LeStorage/Utils/FileUtils.swift index a2f3969..5edf902 100644 --- a/LeStorage/Utils/FileUtils.swift +++ b/LeStorage/Utils/FileUtils.swift @@ -17,10 +17,17 @@ class FileUtils { let documentsURL: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] return try FileManager.default.contentsOfDirectory(atPath: documentsURL.path) } + + static func pathForDirectoryInDocuments(directory: String) throws -> URL { + if let dir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { + return dir.appending(component: directory) + } + throw FileError.documentDirectoryNotFound + } static func readDocumentFile(fileName: String) throws -> String { let fileURL: URL = try self.pathForFileInDocumentDirectory(fileName) - return try String(contentsOf: fileURL, encoding: .utf8) + return try self.readFile(fileURL: fileURL) } static func readFile(fileURL: URL) throws -> String { @@ -38,26 +45,12 @@ class FileUtils { let fileURL = try self.pathForFileInDocumentDirectory(fileName) try content.write(to: fileURL, atomically: false, encoding: .utf8) return fileURL - -// if let dir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { -// let fileURL: URL = dir.appendingPathComponent(fileName) -// try content.write(to: fileURL, atomically: false, encoding: .utf8) -// return fileURL -// } -// throw FileError.documentDirectoryNotFound } @discardableResult static func writeToDocumentDirectory(data: Data, fileName: String) throws -> URL { let fileURL = try self.pathForFileInDocumentDirectory(fileName) try data.write(to: fileURL) return fileURL - -// if let dir: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first { -// let fileURL: URL = dir.appendingPathComponent(fileName) -// try data.write(to: fileURL) -// return fileURL -// } -// throw FileError.documentDirectoryNotFound } static func removeFileFromDocumentDirectory(fileName: String) throws {