将自定义对象转换为要保存在 NSUserDefaults 中的数据
Convert a custom object to Data to be saved in NSUserDefauts
我们有一个像这样的自定义对象设置:
struct BallPark: Codable,Equatable {
static func == (lhs: LeanVenue, rhs: LeanVenue) -> Bool {
return lhs.id == rhs.id
}
let id: String
let name: String?
let location: VenueCoordinates?
let mapEnabled: Bool?
let facilityStatus: String?
}
struct VenueCoordinates: Codable {
let latitude: String?
let longitude: String?
var lat: Double? {
guard let latitud = latitude else { return nil}
return Double(latitud)
}
var lon: Double? {
guard let longitude = longitude else { return nil}
return Double(longitude)
}
}
我们正在尝试将其转换为数据类型,以便可以将其保存为用户默认值,如下所示:
guard let bestBallParkToSave = theBestBallPark // this is of type BallPark, after fetching data
其他{return}
let encodedBestBallPark = NSKeyedArchiver.archivedData(withRootObject: bestBallParkToSave)
UserDefaults.standard.set(encodedBestBallPark, forKey: "favoriteBallPark")
问题是它在尝试转换时导致错误:
'NSInvalidArgumentException', reason: '-[__SwiftValue
encodeWithCoder:]: unrecognized selector sent to instance
0x6000027dca80'
我试过使用这个堆栈溢出答案:How to convert custom object to Data Swift 但除了确保它符合 Codable 之外并没有真正明确的答案,我相信我的结构和其中的所有内容都是如此。如果您有任何建议,请告诉我。
那里的问题。是您将 Codable 与 NSCoding 混淆了。要使用 NSKeyedArchiver 的 archivedData 方法,您需要有一个符合 NSCoding 的 class。在你的情况下,你有一个符合 Codable 的结构,所以你需要使用 JSONEncoder encode 方法。注意:你的 equatable 方法声明是错误的:
struct BallPark: Codable, Equatable {
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id
}
let id: String
let name: String?
let location: VenueCoordinates?
let mapEnabled: Bool?
let facilityStatus: String?
}
struct VenueCoordinates: Codable {
let latitude: String?
let longitude: String?
var lat: Double? {
guard let latitud = latitude else { return nil}
return Double(latitud)
}
var lon: Double? {
guard let longitude = longitude else { return nil}
return Double(longitude)
}
}
let bestBallParkToSave = BallPark.init(id: "1", name: "Steve", location: .init(latitude: "20.0", longitude: "40.0"), mapEnabled: true, facilityStatus: "a status")
do {
let encodedBestBallPark = try JSONEncoder().encode(bestBallParkToSave)
UserDefaults.standard.set(encodedBestBallPark, forKey: "favoriteBallPark")
if let ballParkData = UserDefaults.standard.data(forKey: "favoriteBallPark") {
let loadedBallPark = try JSONDecoder().decode(BallPark.self, from: ballParkData)
print(loadedBallPark) // BallPark(id: "1", name: Optional("Steve"), location: Optional(VenueCoordinates(latitude: Optional("20.0"), longitude: Optional("40.0"))), mapEnabled: Optional(true), facilityStatus: Optional("a status"))
}
} catch {
print(error)
}
您还可以让您的生活更轻松地扩展 UserDefaults 并创建自定义编码和解码方法:
extension UserDefaults {
func decodeObject<T: Decodable>(forKey defaultName: String, using decoder: JSONDecoder = .init()) throws -> T {
try decoder.decode(T.self, from: data(forKey: defaultName) ?? .init())
}
func encode<T: Encodable>(_ value: T, forKey defaultName: String, using encoder: JSONEncoder = .init()) throws {
try set(encoder.encode(value), forKey: defaultName)
}
}
用法:
let bestBallParkToSave = BallPark(id: "1", name: "Steve", location: .init(latitude: "20.0", longitude: "40.0"), mapEnabled: true, facilityStatus: "a status")
do {
try UserDefaults.standard.encode(bestBallParkToSave, forKey: "favoriteBallPark")
let loadedBallPark: BallPark = try UserDefaults.standard.decodeObject(forKey: "favoriteBallPark")
print(loadedBallPark) // BallPark(id: "1", name: Optional("Steve"), location: Optional(VenueCoordinates(latitude: Optional("20.0"), longitude: Optional("40.0"))), mapEnabled: Optional(true), facilityStatus: Optional("a status"))
} catch {
print(error)
}
我们有一个像这样的自定义对象设置:
struct BallPark: Codable,Equatable {
static func == (lhs: LeanVenue, rhs: LeanVenue) -> Bool {
return lhs.id == rhs.id
}
let id: String
let name: String?
let location: VenueCoordinates?
let mapEnabled: Bool?
let facilityStatus: String?
}
struct VenueCoordinates: Codable {
let latitude: String?
let longitude: String?
var lat: Double? {
guard let latitud = latitude else { return nil}
return Double(latitud)
}
var lon: Double? {
guard let longitude = longitude else { return nil}
return Double(longitude)
}
}
我们正在尝试将其转换为数据类型,以便可以将其保存为用户默认值,如下所示:
guard let bestBallParkToSave = theBestBallPark // this is of type BallPark, after fetching data
其他{return}
let encodedBestBallPark = NSKeyedArchiver.archivedData(withRootObject: bestBallParkToSave)
UserDefaults.standard.set(encodedBestBallPark, forKey: "favoriteBallPark")
问题是它在尝试转换时导致错误:
'NSInvalidArgumentException', reason: '-[__SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x6000027dca80'
我试过使用这个堆栈溢出答案:How to convert custom object to Data Swift 但除了确保它符合 Codable 之外并没有真正明确的答案,我相信我的结构和其中的所有内容都是如此。如果您有任何建议,请告诉我。
那里的问题。是您将 Codable 与 NSCoding 混淆了。要使用 NSKeyedArchiver 的 archivedData 方法,您需要有一个符合 NSCoding 的 class。在你的情况下,你有一个符合 Codable 的结构,所以你需要使用 JSONEncoder encode 方法。注意:你的 equatable 方法声明是错误的:
struct BallPark: Codable, Equatable {
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id
}
let id: String
let name: String?
let location: VenueCoordinates?
let mapEnabled: Bool?
let facilityStatus: String?
}
struct VenueCoordinates: Codable {
let latitude: String?
let longitude: String?
var lat: Double? {
guard let latitud = latitude else { return nil}
return Double(latitud)
}
var lon: Double? {
guard let longitude = longitude else { return nil}
return Double(longitude)
}
}
let bestBallParkToSave = BallPark.init(id: "1", name: "Steve", location: .init(latitude: "20.0", longitude: "40.0"), mapEnabled: true, facilityStatus: "a status")
do {
let encodedBestBallPark = try JSONEncoder().encode(bestBallParkToSave)
UserDefaults.standard.set(encodedBestBallPark, forKey: "favoriteBallPark")
if let ballParkData = UserDefaults.standard.data(forKey: "favoriteBallPark") {
let loadedBallPark = try JSONDecoder().decode(BallPark.self, from: ballParkData)
print(loadedBallPark) // BallPark(id: "1", name: Optional("Steve"), location: Optional(VenueCoordinates(latitude: Optional("20.0"), longitude: Optional("40.0"))), mapEnabled: Optional(true), facilityStatus: Optional("a status"))
}
} catch {
print(error)
}
您还可以让您的生活更轻松地扩展 UserDefaults 并创建自定义编码和解码方法:
extension UserDefaults {
func decodeObject<T: Decodable>(forKey defaultName: String, using decoder: JSONDecoder = .init()) throws -> T {
try decoder.decode(T.self, from: data(forKey: defaultName) ?? .init())
}
func encode<T: Encodable>(_ value: T, forKey defaultName: String, using encoder: JSONEncoder = .init()) throws {
try set(encoder.encode(value), forKey: defaultName)
}
}
用法:
let bestBallParkToSave = BallPark(id: "1", name: "Steve", location: .init(latitude: "20.0", longitude: "40.0"), mapEnabled: true, facilityStatus: "a status")
do {
try UserDefaults.standard.encode(bestBallParkToSave, forKey: "favoriteBallPark")
let loadedBallPark: BallPark = try UserDefaults.standard.decodeObject(forKey: "favoriteBallPark")
print(loadedBallPark) // BallPark(id: "1", name: Optional("Steve"), location: Optional(VenueCoordinates(latitude: Optional("20.0"), longitude: Optional("40.0"))), mapEnabled: Optional(true), facilityStatus: Optional("a status"))
} catch {
print(error)
}