Created
August 6, 2020 16:53
-
-
Save thecoolwinter/dea0a68ac8957f1213f9a5f6ea5a0f40 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
public class CodableStorage { | |
fileprivate init() { } | |
enum Directory { | |
case documents | |
case caches | |
case shared // Use for sharing files between containers. Eg, with an app extension. | |
} | |
/// Returns URL constructed from specified directory | |
static fileprivate func getURL(for directory: Directory) -> URL { | |
var searchPathDirectory: FileManager.SearchPathDirectory | |
switch directory { | |
case .documents: | |
searchPathDirectory = .documentDirectory | |
case .caches: | |
searchPathDirectory = .cachesDirectory | |
case .shared: | |
return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: <#Bundle ID#>) ?? FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! | |
} | |
if let url = FileManager.default.urls(for: searchPathDirectory, in: .userDomainMask).first { | |
return url | |
} else { | |
fatalError("Could not create URL for specified directory!") | |
} | |
} | |
/// Store an encodable struct to the specified directory on disk | |
/// | |
/// - Parameters: | |
/// - object: the encodable struct to store | |
/// - directory: where to store the struct | |
/// - fileName: what to name the file where the struct data will be stored | |
static func store<T: Encodable>(_ object: T, to directory: Directory, as fileName: String, encryption: URLFileProtection?) { | |
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false) | |
let encoder = JSONEncoder() | |
do { | |
let data = try encoder.encode(object) | |
if FileManager.default.fileExists(atPath: url.path) { | |
try FileManager.default.removeItem(at: url) | |
} | |
FileManager.default.createFile(atPath: url.path, contents: data, attributes: nil) | |
if encryption != nil { | |
try (url as NSURL).setResourceValue(encryption!, forKey: .fileProtectionKey) | |
} | |
} catch { | |
fatalError(error.localizedDescription) | |
} | |
} | |
/// Retrieve and convert a struct from a file on disk | |
/// | |
/// - Parameters: | |
/// - fileName: name of the file where struct data is stored | |
/// - directory: directory where struct data is stored | |
/// - type: struct type (i.e. Message.self) | |
/// - Returns: decoded struct model(s) of data | |
static func retrieve<T: Decodable>(_ fileName: String, from directory: Directory, as type: T.Type) -> T? { | |
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false) | |
if !FileManager.default.fileExists(atPath: url.path) { | |
print("File at path \(url.path) does not exist!") | |
return nil | |
} | |
if let data = FileManager.default.contents(atPath: url.path) { | |
let decoder = JSONDecoder() | |
do { | |
let model = try decoder.decode(type, from: data) | |
return model | |
} catch { | |
print(error.localizedDescription) | |
return nil | |
} | |
} else { | |
print("No data at \(url.path)!") | |
return nil | |
} | |
} | |
/// Remove all files at specified directory | |
static func clear(_ directory: Directory) { | |
let url = getURL(for: directory) | |
do { | |
let contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: []) | |
for fileUrl in contents { | |
try FileManager.default.removeItem(at: fileUrl) | |
} | |
} catch { | |
fatalError(error.localizedDescription) | |
} | |
} | |
/// Remove specified file from specified directory | |
static func remove(_ fileName: String, from directory: Directory) { | |
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false) | |
if FileManager.default.fileExists(atPath: url.path) { | |
do { | |
try FileManager.default.removeItem(at: url) | |
} catch { | |
fatalError(error.localizedDescription) | |
} | |
} | |
} | |
/// Returns BOOL indicating whether file exists at specified directory with specified file name | |
static func fileExists(_ fileName: String, in directory: Directory) -> Bool { | |
let url = getURL(for: directory).appendingPathComponent(fileName, isDirectory: false) | |
return FileManager.default.fileExists(atPath: url.path) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Use this class like so:
CodableStorage.store(CodableObject, to: .directory, as: "Filename", encryption: nil)
The encryption enum simply adds apple's native file protection, it's not actually encryption. Also, there's an option in the Directory enum for a shared file location, which will require you to have an ID for a shared container, such as for an app extension.