Swift5:将复杂对象保存到文件存储
Swift 5: saving complex objects to file storage
我正在做一项学校作业,为此我必须在 Swift 中开发一个 iOS 应用程序 5. 该应用程序需要使用 Web 服务(一个 web-API ) 和文件存储或用户默认值。
我选择开发一个“QR 码管理器”,用户可以在其中通过设置一些设计参数为 URL 创建 QR 码,然后将其发送到生成器 API . API(根据 OK 请求)returns 指定格式的图像(在我的例子中是 PNG)。
我有一个 class 和 URL 以及 QR 码的所有设计属性,其中还将包含图像本身。请参阅下面的 class.
代码段
public class QRCode {
var bsId : Int?
var url : String?
var name: String?
var frame: Frame?
var logo: QrCodeLogo?
var marker: Marker?
var color : String?
var bgColor : String?
var image : Data?
init(data: [String:String]) {
self.url = data["url"]
self.frame = Frame.allCases.first(where: { [=11=].description == data["frame"] })
self.logo = QrCodeLogo.allCases.first(where: { [=11=].description == data["logo"] })
self.marker = Marker.allCases.first(where: { [=11=].description == data["marker"] })
self.bgColor = data["backGroundColor"]
self.color = data["colorLight"]
}
init(json: String) {
// todo
}
}
extension QRCode {
func toDict() -> [String:Any] {
var dict = [String:Any]();
let otherSelf = Mirror(reflecting: self);
for child in otherSelf.children {
if let key = child.label {
dict[key] = child.value;
}
}
return dict;
}
}
为了便于开发,所有属性都可以为 null,class 将在我成功实现所有内容后进一步重构。
我尝试了在 Internet 上找到的各种方法,其中一种可以在 class 扩展中找到。函数 toDict()
将对象属性及其值转换为类型 [String:Any]
的 Dictionary
对象。但是,我读到当 Any
数据类型被编码然后解码时,Swift 无法确定解码数据应该是哪种复杂数据类型,有效地使数据变得无意义或无法使用。
我发现的另一种方法是通过扩展 class 中的 Codable
-协议。然而,据我所知,Codable
只接受原始数据类型。
请在下面找到我当前编写的文件存储处理代码。还没有完成,但我觉得这是一个好的开始,可能会对这个问题有所帮助。
class StorageManager {
fileprivate let filemanager: FileManager = FileManager.default;
fileprivate func filePath(forKey key: String) -> URL? {
guard let docURL = filemanager.urls(for: .documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first else {
return nil;
}
return docURL.appendingPathComponent(key);
}
func writeToStorage(identifier: String, data: QRCode) -> Void {
guard let path = filePath(forKey: identifier) else {
throw ApplicationErrors.runtimeError("Something went wrong writing the file to storage");
}
let dict = data.toDict();
// TODO:: Implement
}
func readFromStorage(identifier: String) -> Any {
// TODO:: Implement
return 0;
}
func readAllFromStorage() throws -> [URL] {
let docsURL = filemanager.urls(for: .documentDirectory, in: .userDomainMask)[0];
do {
let fileURLs = try filemanager.contentsOfDirectory(at: docsURL, includingPropertiesForKeys: nil);
return fileURLs;
} catch {
throw ApplicationErrors.runtimeError("Something went wrong retrieving the files from \(docsURL.path): \(error.localizedDescription)");
}
}
}
我是 Swift 的新手,我 运行 卡在了文件存储上。有什么方法可以将此 class 的实例存储在文件存储中,以便在我检索数据时可以重新实例化此 class?
提前致谢!如有任何问题,请随时提出。
编辑
根据 matt 的评论,请在下面找到 Marker
、Frame
和 QrCodeLogo
枚举的代码片段。
Frame
枚举:
public enum Frame: String, CaseIterable {
case noFrame
case bottomFrame
case bottomTooltip
case topHeader
static var count: Int { return 4 }
var description: String {
switch self {
case .noFrame:
return "no-frame"
case .bottomFrame:
return "bottom-frame"
case .bottomTooltip:
return "bottom-tooltip"
case .topHeader:
return "top-header"
}
}
}
QrCodeLogo
枚举:
public enum QrCodeLogo: String, CaseIterable {
case noLogo
case scanMe
case scanMeSquare
static var count: Int { return 3 }
var description: String {
switch self {
case .noLogo:
return "no-logo"
case .scanMe:
return "scan-me"
case .scanMeSquare:
return "scan-me-square"
}
}
}
Marker
枚举:
public enum Marker: String, CaseIterable {
case version1
case version2
case version3
case version4
case version5
case version6
case version7
case version8
case version9
case version10
case version11
case version12
case version13
case version15
case version16
static var count: Int { return 15 }
var description: String {
switch self {
case .version1:
return "version1"
case .version2:
return "version2"
case .version3:
return "version3"
case .version4:
return "version4"
case .version5:
return "version5"
case .version6:
return "version6"
case .version7:
return "version7"
case .version8:
return "version8"
case .version9:
return "version9"
case .version10:
return "version10"
case .version11:
return "version11"
case .version12:
return "version12"
case .version13:
return "version13"
case .version15:
return "version15"
case .version16:
return "version16"
}
}
}
以上所有枚举都包含我使用的 API 的有效设计选项。它们用作输入限制,以防止发生“无效参数”错误。
希望这能解决问题。
再次感谢!
一个类型可以符合 Codable,前提是它的所有属性都符合 Codable。除了枚举之外,您的所有属性 do 都符合 Codable,如果您声明它们符合,它们将符合 Codable。因此,您的类型的这个简单草图可以编译:
public enum Frame: String, Codable {
case noFrame
case bottomFrame
case bottomTooltip
case topHeader
}
public enum QrCodeLogo: String, Codable {
case noLogo
case scanMe
case scanMeSquare
}
public enum Marker: String, Codable {
case version1
case version2
case version3
case version4
case version5
case version6
case version7
case version8
case version9
case version10
case version11
case version12
case version13
case version15
case version16
}
public class QRCode : Codable {
var bsId : Int?
var url : String?
var name: String?
var frame: Frame?
var logo: QrCodeLogo?
var marker: Marker?
var color : String?
var bgColor : String?
var image : Data?
}
关于您的代码,还有很多其他方面可以改进。
您不需要 CaseIterable 或 description
任何东西。现在您的类型是 Codable,您可以使用 it 直接自动从 JSON 检索值。如果您的枚举案例的名称与相应的 JSON 键不匹配,只需制作一个 CodingKey 嵌套枚举作为桥梁。
换句话说,成为 Codable 使您的类型 both 可直接从 JSON and 序列化到磁盘。
我正在做一项学校作业,为此我必须在 Swift 中开发一个 iOS 应用程序 5. 该应用程序需要使用 Web 服务(一个 web-API ) 和文件存储或用户默认值。
我选择开发一个“QR 码管理器”,用户可以在其中通过设置一些设计参数为 URL 创建 QR 码,然后将其发送到生成器 API . API(根据 OK 请求)returns 指定格式的图像(在我的例子中是 PNG)。
我有一个 class 和 URL 以及 QR 码的所有设计属性,其中还将包含图像本身。请参阅下面的 class.
代码段public class QRCode {
var bsId : Int?
var url : String?
var name: String?
var frame: Frame?
var logo: QrCodeLogo?
var marker: Marker?
var color : String?
var bgColor : String?
var image : Data?
init(data: [String:String]) {
self.url = data["url"]
self.frame = Frame.allCases.first(where: { [=11=].description == data["frame"] })
self.logo = QrCodeLogo.allCases.first(where: { [=11=].description == data["logo"] })
self.marker = Marker.allCases.first(where: { [=11=].description == data["marker"] })
self.bgColor = data["backGroundColor"]
self.color = data["colorLight"]
}
init(json: String) {
// todo
}
}
extension QRCode {
func toDict() -> [String:Any] {
var dict = [String:Any]();
let otherSelf = Mirror(reflecting: self);
for child in otherSelf.children {
if let key = child.label {
dict[key] = child.value;
}
}
return dict;
}
}
为了便于开发,所有属性都可以为 null,class 将在我成功实现所有内容后进一步重构。
我尝试了在 Internet 上找到的各种方法,其中一种可以在 class 扩展中找到。函数 toDict()
将对象属性及其值转换为类型 [String:Any]
的 Dictionary
对象。但是,我读到当 Any
数据类型被编码然后解码时,Swift 无法确定解码数据应该是哪种复杂数据类型,有效地使数据变得无意义或无法使用。
我发现的另一种方法是通过扩展 class 中的 Codable
-协议。然而,据我所知,Codable
只接受原始数据类型。
请在下面找到我当前编写的文件存储处理代码。还没有完成,但我觉得这是一个好的开始,可能会对这个问题有所帮助。
class StorageManager {
fileprivate let filemanager: FileManager = FileManager.default;
fileprivate func filePath(forKey key: String) -> URL? {
guard let docURL = filemanager.urls(for: .documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first else {
return nil;
}
return docURL.appendingPathComponent(key);
}
func writeToStorage(identifier: String, data: QRCode) -> Void {
guard let path = filePath(forKey: identifier) else {
throw ApplicationErrors.runtimeError("Something went wrong writing the file to storage");
}
let dict = data.toDict();
// TODO:: Implement
}
func readFromStorage(identifier: String) -> Any {
// TODO:: Implement
return 0;
}
func readAllFromStorage() throws -> [URL] {
let docsURL = filemanager.urls(for: .documentDirectory, in: .userDomainMask)[0];
do {
let fileURLs = try filemanager.contentsOfDirectory(at: docsURL, includingPropertiesForKeys: nil);
return fileURLs;
} catch {
throw ApplicationErrors.runtimeError("Something went wrong retrieving the files from \(docsURL.path): \(error.localizedDescription)");
}
}
}
我是 Swift 的新手,我 运行 卡在了文件存储上。有什么方法可以将此 class 的实例存储在文件存储中,以便在我检索数据时可以重新实例化此 class?
提前致谢!如有任何问题,请随时提出。
编辑
根据 matt 的评论,请在下面找到 Marker
、Frame
和 QrCodeLogo
枚举的代码片段。
Frame
枚举:
public enum Frame: String, CaseIterable {
case noFrame
case bottomFrame
case bottomTooltip
case topHeader
static var count: Int { return 4 }
var description: String {
switch self {
case .noFrame:
return "no-frame"
case .bottomFrame:
return "bottom-frame"
case .bottomTooltip:
return "bottom-tooltip"
case .topHeader:
return "top-header"
}
}
}
QrCodeLogo
枚举:
public enum QrCodeLogo: String, CaseIterable {
case noLogo
case scanMe
case scanMeSquare
static var count: Int { return 3 }
var description: String {
switch self {
case .noLogo:
return "no-logo"
case .scanMe:
return "scan-me"
case .scanMeSquare:
return "scan-me-square"
}
}
}
Marker
枚举:
public enum Marker: String, CaseIterable {
case version1
case version2
case version3
case version4
case version5
case version6
case version7
case version8
case version9
case version10
case version11
case version12
case version13
case version15
case version16
static var count: Int { return 15 }
var description: String {
switch self {
case .version1:
return "version1"
case .version2:
return "version2"
case .version3:
return "version3"
case .version4:
return "version4"
case .version5:
return "version5"
case .version6:
return "version6"
case .version7:
return "version7"
case .version8:
return "version8"
case .version9:
return "version9"
case .version10:
return "version10"
case .version11:
return "version11"
case .version12:
return "version12"
case .version13:
return "version13"
case .version15:
return "version15"
case .version16:
return "version16"
}
}
}
以上所有枚举都包含我使用的 API 的有效设计选项。它们用作输入限制,以防止发生“无效参数”错误。
希望这能解决问题。
再次感谢!
一个类型可以符合 Codable,前提是它的所有属性都符合 Codable。除了枚举之外,您的所有属性 do 都符合 Codable,如果您声明它们符合,它们将符合 Codable。因此,您的类型的这个简单草图可以编译:
public enum Frame: String, Codable {
case noFrame
case bottomFrame
case bottomTooltip
case topHeader
}
public enum QrCodeLogo: String, Codable {
case noLogo
case scanMe
case scanMeSquare
}
public enum Marker: String, Codable {
case version1
case version2
case version3
case version4
case version5
case version6
case version7
case version8
case version9
case version10
case version11
case version12
case version13
case version15
case version16
}
public class QRCode : Codable {
var bsId : Int?
var url : String?
var name: String?
var frame: Frame?
var logo: QrCodeLogo?
var marker: Marker?
var color : String?
var bgColor : String?
var image : Data?
}
关于您的代码,还有很多其他方面可以改进。
您不需要 CaseIterable 或 description
任何东西。现在您的类型是 Codable,您可以使用 it 直接自动从 JSON 检索值。如果您的枚举案例的名称与相应的 JSON 键不匹配,只需制作一个 CodingKey 嵌套枚举作为桥梁。
换句话说,成为 Codable 使您的类型 both 可直接从 JSON and 序列化到磁盘。