我想了解如何在UserDefaults中存储自定义结构的数组。
这是我的代码:
struct DomainSchema: Codable {
var domain: String
var schema: String
}
var domainSchemas: [DomainSchema] {
get {
if UserDefaults.standard.object(forKey: "domainSchemas") != nil {
let data = UserDefaults.standard.value(forKey: "domainSchemas") as! Data
let domainSchema = try? PropertyListDecoder().decode(DomainSchema.self, from: data)
return domainSchema!
}
return nil
}
set {
UserDefaults.standard.set(try? PropertyListEncoder().encode(newValue), forKey: "domainSchemas")
}
}
struct SettingsView: View {
var body: some View {
VStack {
ForEach(domainSchemas, id: \.domain) { domainSchema in
HStack {
Text(domainSchema.domain)
Text(domainSchema.schema)
}
}
// clear history button
}
.onAppear {
if (domainSchemas.isEmpty) {
domainSchemas.append(DomainSchema(domain: "reddit.com", schema: "apollo://"))
}
}
}
}
这给了我这些错误:
无法将类型为“ DomainSchema”的返回表达式转换为类型为“ [DomainSchema]”
'nil'与返回类型'[DomainSchema]'
不兼容
我不确定如何获取对象数组而不是单个对象,或者如何解决nil
不兼容错误...
答案 0 :(得分:0)
如果您真的想使用UserDefaults持久化数据,最简单的方法是使用一个类并使它符合NSCoding。关于您的全局var domainSchemas,我建议使用单例或扩展UserDefaults并在那里为它创建一个计算属性:
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewmodel"
type="com.example.android.databinding.basicsample.data.SimpleViewModelSolution"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- A simple binding between a TextView and a string observable in the ViewModel -->
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="128dp"
android:text="@{viewmodel.name}"
...
}
class DomainSchema: NSObject, NSCoding {
var domain: String
var schema: String
init(domain: String, schema: String) {
self.domain = domain
self.schema = schema
}
required init(coder decoder: NSCoder) {
self.domain = decoder.decodeObject(forKey: "domain") as? String ?? ""
self.schema = decoder.decodeObject(forKey: "schema") as? String ?? ""
}
func encode(with coder: NSCoder) {
coder.encode(domain, forKey: "domain")
coder.encode(schema, forKey: "schema")
}
}
用法:
extension UserDefaults {
var domainSchemas: [DomainSchema] {
get {
guard let data = UserDefaults.standard.data(forKey: "domainSchemas") else { return [] }
return (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)) as? [DomainSchema] ?? []
}
set {
UserDefaults.standard.set(try? NSKeyedArchiver.archivedData(withRootObject: newValue, requiringSecureCoding: false), forKey: "domainSchemas")
}
}
}
如果您更喜欢Codable方法,还可以使用UserDefaults来保留数据:
UserDefaults.standard.domainSchemas = [.init(domain: "a", schema: "b"), .init(domain: "c", schema: "d")]
UserDefaults.standard.domainSchemas // [{NSObject, domain "a", schema "b"}, {NSObject, domain "c", schema "d"}]
struct DomainSchema: Codable {
var domain: String
var schema: String
init(domain: String, schema: String) {
self.domain = domain
self.schema = schema
}
}
用法:
extension UserDefaults {
var domainSchemas: [DomainSchema] {
get {
guard let data = UserDefaults.standard.data(forKey: "domainSchemas") else { return [] }
return (try? PropertyListDecoder().decode([DomainSchema].self, from: data)) ?? []
}
set {
UserDefaults.standard.set(try? PropertyListEncoder().encode(newValue), forKey: "domainSchemas")
}
}
}
我认为最好的选择是不使用UserDefaults,创建单例“共享实例”,在其中声明domainSchemas属性并将json数据保存在应用程序支持目录的子目录中:
UserDefaults.standard.domainSchemas = [.init(domain: "a", schema: "b"), .init(domain: "c", schema: "d")]
UserDefaults.standard.domainSchemas // [{domain "a", schema "b"}, {domain "c", schema "d"}]
extension URL {
static var domainSchemas: URL {
let applicationSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
let bundleID = Bundle.main.bundleIdentifier ?? "company name"
let subDirectory = applicationSupport.appendingPathComponent(bundleID, isDirectory: true)
try? FileManager.default.createDirectory(at: subDirectory, withIntermediateDirectories: true, attributes: nil)
return subDirectory.appendingPathComponent("domainSchemas.json")
}
}
用法:
class Shared {
static let instance = Shared()
private init() { }
var domainSchemas: [DomainSchema] {
get {
guard let data = try? Data(contentsOf: .domainSchemas) else { return [] }
return (try? JSONDecoder().decode([DomainSchema].self, from: data)) ?? []
}
set {
try? JSONEncoder().encode(newValue).write(to: .domainSchemas)
}
}
}