this repo has no description

re-add old logic and update readme

Changed files
+118 -49
Sources
StorageKit
+38 -32
README.md
··· 1 # StorageKit 2 3 - ### WARNING 4 - This package is subject to change a lot. If you want to use this it probably makes sense to copy and paste the code (it is only 1 file with 82 lines of code). 5 6 - Sometimes Core Data or SQLite are overkill for your app. In those situations, StorageKit is a great solution to save `Codable` objects to disk. 7 8 ## Install StorageKit 9 10 - StorageKit currently only supports installation via Swift Package Manager. 11 - 12 - ### Add Package Dependency 13 - In Xcode, select `File` > `Add Packages...`. 14 15 - ### Specify the Repository 16 - Copy and paste the following into the search/input box. 17 - `https://github.com/SparrowTek/StorageKit.git` 18 - 19 - ### Specify Options 20 - In the options for the StorageKit package, we recommend setting the Dependency Rule to Up to Next Major Version, and enter the 21 - current StorageKit version. Then, click `Add Package`. 22 - 23 - ### Select the Package Products 24 - Select `StorageKit`, then click Add Package. 25 26 ## Getting started 27 - First import StorageKit 28 29 ```swift 30 import StorageKit 31 - ``` 32 - 33 - For any `Codable` object save and retrieve from disk. 34 35 - ```swift 36 struct User: Codable { 37 let name: String 38 let id: Int 39 } 40 ``` 41 42 - ### Store 43 ```swift 44 do { 45 let user = User(name: "Thomas", id: 123) 46 try Storage.store(user, to: .documents, as: "user.json") 47 } catch { 48 - // TODO: handle error 49 } 50 ``` 51 52 - ### retrieve 53 ```swift 54 let user = Storage.retrieve("user.json", from: .documents, as: User.self) 55 ``` 56 57 - ### delete 58 ```swift 59 - do { 60 - try Storage.remove("user.json", from: .documents) 61 - } catch { 62 - // TODO: handle error 63 - } 64 ``` 65 66 - ### does a file exist? 67 ```swift 68 let exists = Storage.fileExists("user.json", in: .documents) 69 ```
··· 1 # StorageKit 2 3 + # StorageKit 4 5 + Sometimes Core Data or SQLite are overkill for your app. StorageKit saves `Codable` objects or raw `Data` to disk in the app sandbox. 6 7 ## Install StorageKit 8 9 + StorageKit currently only supports installation via Swift Package Manager. 10 11 + - In Xcode, select `File` > `Add Packages...`. 12 + - Enter `https://github.com/SparrowTek/StorageKit.git`. 13 + - Set the dependency rule (e.g. Up to Next Major Version) and add the `StorageKit` product. 14 15 ## Getting started 16 17 ```swift 18 import StorageKit 19 20 struct User: Codable { 21 let name: String 22 let id: Int 23 } 24 ``` 25 26 + `Storage.Directory` supports `.documents` and `.caches`. All `store` functions overwrite any existing file with the same name. You can customize file protection via the optional `writingOptions` parameter (defaults to `.completeFileProtection`). 27 + 28 + ### Store a Codable value 29 ```swift 30 do { 31 let user = User(name: "Thomas", id: 123) 32 try Storage.store(user, to: .documents, as: "user.json") 33 } catch { 34 + // handle StorageError.createURL, StorageError.removeObject, or file write errors 35 } 36 ``` 37 38 + Pass a custom `JSONEncoder` or `writingOptions` if needed: 39 + ```swift 40 + let encoder = JSONEncoder() 41 + encoder.dateEncodingStrategy = .iso8601 42 + try Storage.store(user, 43 + to: .documents, 44 + as: "user.json", 45 + encoder: encoder, 46 + writingOptions: .noFileProtection) 47 + ``` 48 + 49 + ### Store raw Data 50 + ```swift 51 + let data = Data([0x01, 0x02]) 52 + try Storage.store(data, to: .caches, as: "bytes.bin", writingOptions: .completeFileProtectionUnlessOpen) 53 + ``` 54 + 55 + ### Retrieve a Codable value 56 + `retrieve` returns an optional decoded value. Supply a `JSONDecoder` if you need custom settings. 57 ```swift 58 let user = Storage.retrieve("user.json", from: .documents, as: User.self) 59 ``` 60 61 + ### Delete a file 62 ```swift 63 + try Storage.remove("user.json", from: .documents) 64 ``` 65 66 + ### Check existence 67 ```swift 68 let exists = Storage.fileExists("user.json", in: .documents) 69 ``` 70 + 71 + ### Clear a directory 72 + Removes every file in the chosen directory. Throws `StorageError.clearDirectory(failedURLs:)` if any removals fail. 73 + ```swift 74 + try Storage.clear(.caches) 75 + ```
+80 -17
Sources/StorageKit/StorageKit.swift
··· 3 public enum StorageError: Error { 4 case createURL 5 case removeObject 6 - case clearDirectory 7 - case createURLError 8 } 9 10 public struct Storage { ··· 18 } 19 20 /// Returns URL constructed from specified directory 21 - private static func getURL(for directory: Directory, fileManager: FileManager) throws -> URL { 22 let searchPathDirectory: FileManager.SearchPathDirectory = switch directory { 23 case .documents: .documentDirectory 24 case .caches: .cachesDirectory 25 } 26 27 - guard let url = fileManager.urls(for: searchPathDirectory, in: .userDomainMask).first else { throw StorageError.createURLError } 28 return url 29 } 30 ··· 34 /// - directory: `Directory` to look in 35 /// - fileManager: An optional FileManager` argument. Defaults to `FileManager.default` 36 /// - Returns: an optional `URL` 37 - public static func url(for fileName: String, in directory: Directory, fileManager: FileManager = .default) -> URL? { 38 try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) 39 } 40 41 /// Store an encodable struct to the specified directory on disk 42 /// 43 /// - Parameters: 44 - /// - data: the data to store 45 /// - directory: where to store the struct 46 /// - fileName: what to name the file where the struct data will be stored 47 /// - fileManager: An optional FileManager` argument. Defaults to `FileManager.default` 48 - public static func store(_ data: Data, to directory: Directory, as fileName: String, fileManager: FileManager = .default) throws { 49 - guard !fileExists(fileName, in: directory) else { return } 50 - guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { throw StorageError.createURLError } 51 - try data.write(to: url, options: .completeFileProtection) 52 } 53 54 /// Remove specified file from specified directory 55 - public static func remove(_ fileName: String, from directory: Directory, fileManager: FileManager = .default) throws { 56 - guard !fileExists(fileName, in: directory) else { return } 57 - guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { throw StorageError.createURLError } 58 59 do { 60 try fileManager.removeItem(at: url) ··· 64 } 65 66 /// - Returns: `Bool` indicating whether file exists at specified directory with specified file name 67 - public static func fileExists(_ fileName: String, in directory: Directory, fileManager: FileManager = .default) -> Bool { 68 guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { return false } 69 return fileManager.fileExists(atPath: url.path) 70 } 71 72 /// Remove all files at specified directory 73 - public static func clear(_ directory: Directory, fileManager: FileManager = .default) throws { 74 - guard let url = try? getURL(for: directory, fileManager: fileManager) else { return } 75 let contents = try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: []) 76 77 for fileUrl in contents { 78 - try fileManager.removeItem(at: fileUrl) 79 } 80 } 81 }
··· 3 public enum StorageError: Error { 4 case createURL 5 case removeObject 6 + case clearDirectory(failedURLs: [URL]) 7 } 8 9 public struct Storage { ··· 17 } 18 19 /// Returns URL constructed from specified directory 20 + private static func getURL(for directory: Directory, 21 + fileManager: FileManager) throws -> URL { 22 let searchPathDirectory: FileManager.SearchPathDirectory = switch directory { 23 case .documents: .documentDirectory 24 case .caches: .cachesDirectory 25 } 26 27 + guard let url = fileManager.urls(for: searchPathDirectory, in: .userDomainMask).first else { throw StorageError.createURL } 28 return url 29 } 30 ··· 34 /// - directory: `Directory` to look in 35 /// - fileManager: An optional FileManager` argument. Defaults to `FileManager.default` 36 /// - Returns: an optional `URL` 37 + public static func url(for fileName: String, 38 + in directory: Directory, 39 + fileManager: FileManager = .default) -> URL? { 40 try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) 41 } 42 43 + /// Store Data to the specified directory on disk 44 + /// 45 + /// - Paramete\rs: 46 + /// - data: the data to store 47 + /// - directory: where to store the struct 48 + /// - fileName: what to name the file where the struct data will be stored 49 + /// - fileManager: An optional FileManager` argument. Defaults to `FileManager.default` 50 + public static func store(_ data: Data, 51 + to directory: Directory, 52 + as fileName: String, 53 + fileManager: FileManager = .default, 54 + writingOptions: Data.WritingOptions = .completeFileProtection) throws { 55 + guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { throw StorageError.createURL } 56 + 57 + if fileExists(fileName, in: directory) { 58 + try remove(fileName, from: directory) 59 + } 60 + 61 + try data.write(to: url, options: writingOptions) 62 + } 63 + 64 /// Store an encodable struct to the specified directory on disk 65 /// 66 /// - Parameters: 67 + /// - object: the encodable struct to store 68 /// - directory: where to store the struct 69 /// - fileName: what to name the file where the struct data will be stored 70 /// - fileManager: An optional FileManager` argument. Defaults to `FileManager.default` 71 + public static func store<T: Encodable>(_ object: T, 72 + to directory: Directory, 73 + as fileName: String, 74 + encoder: JSONEncoder = JSONEncoder(), 75 + fileManager: FileManager = .default, 76 + writingOptions: Data.WritingOptions = .completeFileProtection) throws { 77 + guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { throw StorageError.createURL } 78 + let data = try encoder.encode(object) 79 + try store(data, to: directory, as: fileName, fileManager: fileManager, writingOptions: writingOptions) 80 + } 81 + 82 + /// Retrieve and convert a struct from a file on disk 83 + /// 84 + /// - Parameters: 85 + /// - fileName: name of the file where struct data is stored 86 + /// - directory: directory where struct data is stored 87 + /// - type: struct type (i.e. Message.self) 88 + /// - Returns: decoded struct model(s) of data 89 + public static func retrieve<T: Decodable>(_ fileName: String, 90 + from directory: Directory, 91 + as type: T.Type, 92 + decoder: JSONDecoder = JSONDecoder(), 93 + fileManager: FileManager = .default) -> T? { 94 + guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { return nil } 95 + guard fileManager.fileExists(atPath: url.path) else { return nil } 96 + guard let data = fileManager.contents(atPath: url.path) else { return nil } 97 + 98 + return try? decoder.decode(type, from: data) 99 } 100 101 /// Remove specified file from specified directory 102 + public static func remove(_ fileName: String, 103 + from directory: Directory, 104 + fileManager: FileManager = .default) throws { 105 + guard fileExists(fileName, in: directory) else { return } 106 + guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { throw StorageError.createURL } 107 108 do { 109 try fileManager.removeItem(at: url) ··· 113 } 114 115 /// - Returns: `Bool` indicating whether file exists at specified directory with specified file name 116 + public static func fileExists(_ fileName: String, 117 + in directory: Directory, 118 + fileManager: FileManager = .default) -> Bool { 119 guard let url = try? getURL(for: directory, fileManager: fileManager).appendingPathComponent(fileName, isDirectory: false) else { return false } 120 return fileManager.fileExists(atPath: url.path) 121 } 122 123 /// Remove all files at specified directory 124 + public static func clear(_ directory: Directory, 125 + fileManager: FileManager = .default) throws { 126 + let url = try getURL(for: directory, fileManager: fileManager) 127 let contents = try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: []) 128 + var failedURLs: [URL] = [] 129 130 for fileUrl in contents { 131 + do { 132 + try fileManager.removeItem(at: fileUrl) 133 + } catch { 134 + failedURLs.append(fileUrl) 135 + continue 136 + } 137 + } 138 + 139 + 140 + if !failedURLs.isEmpty { 141 + throw StorageError.clearDirectory(failedURLs: failedURLs) 142 } 143 } 144 }