#ios #json #swift #core-data #enums
#iOS #json #быстрый #основные данные #перечисления #swift #core-data
Вопрос:
Модель
struct UserList {
var privacy: Access
var sharing: Access
}
enum Access: {
case `private`
case friendsOnly
case someFriends(ids: [String])
case `public`
}
Модели CoreData
@objc(UserListModel)
public class UserListModel: NSManagedObject {
@NSManaged public var privacy: AccessModel
@NSManaged public var sharing: AccessModel
@NSManaged public var someFriendsPrivacy: SomeFriendsPrivacyModel?
@NSManaged public var someFriendsSharing: SomeFriendsSharingModel?
}
@objc(SomeFriendsSharingModel)
public class SomeFriendsSharingModel: NSManagedObject {
@NSManaged public var ids: [String]
@NSManaged public var userList: UserListModel
}
@objc public enum AccessModel: Int32 {
case `public` = 0
case friendsOnly = 1
case someFriends = 2
case `private` = 3
}
Настройка CoreData:
Вопросы:
- Как я могу добавить Codable в список пользователей, чтобы получить модель из json?
{"privacy":"friendsOnly",
"sharing":{
"some_friends":
{"ids":["id1","id2"]}
}
}
- Как мне добавить Codable в Core Data models? Чтобы иметь возможность напрямую сохранять модели из json в Core Data?
- Как мне инициализировать модель списка пользователей из Core Data UserListModel?
Ответ №1:
- Как я могу добавить Codable в список пользователей, чтобы получить модель из json?
extension UserList: Codable {}
enum AccessCodableError: Error {
case error(String)
}
extension Access: Codable {
enum CodingKeys: String, CodingKey {
case someFriends
}
enum NestedKeys: String, CodingKey {
case ids
}
var rawValue: String {
switch self {
case .friendsOnly:
return "friendsOnly"
case .private:
return "private"
case .public:
return "public"
case .someFriends:
return "someFriends"
}
}
init(from decoder: Decoder) throws {
if let container = try? decoder.container(keyedBy: CodingKeys.self) {
let nestedContainer = try container.nestedContainer(keyedBy: NestedKeys.self, forKey: .someFriends)
let ids = try nestedContainer.decode([String].self, forKey: .ids)
self = .someFriends(ids: ids)
}
else {
let container = try decoder.singleValueContainer()
let accessValue = try container.decode(String.self)
switch accessValue {
case let value where value == Access.private.rawValue:
self = .private
case let value where value == Access.friendsOnly.rawValue:
self = .friendsOnly
case let value where value == Access.public.rawValue:
self = .public
default:
throw AccessCodableError.error("Can't determine the decodable key")
}
}
}
func encode(to encoder: Encoder) throws {
switch self {
case .private, .friendsOnly, .public:
var container = encoder.singleValueContainer()
try container.encode(self.rawValue)
case .someFriends(let userIds):
var container = encoder.container(keyedBy: CodingKeys.self)
var nestedContainer = container.nestedContainer(keyedBy: NestedKeys.self, forKey: .someFriends)
try nestedContainer.encode(userIds, forKey: .ids)
}
}
}
Использование (например, небезопасное):
// Encode
let userList1 = UserList(privacy: .friendsOnly, sharing: .someFriends(ids: ["id1", "id2"]))
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let json = try! encoder.encode(userList1)
// Decode
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedList = try! decoder.decode(UserList.self, from: json)
- Как мне добавить Codable в Core Data models? Чтобы иметь возможность напрямую сохранять модели из json в Core Data?
extension Access {
var ids: [String]? {
switch self {
case .someFriends(let ids):
return ids
default:
return nil
}
}
}
extension CodingUserInfoKey {
static let context = CodingUserInfoKey(rawValue: "context")!
}
@objc(UserListModel)
public class UserListModel: NSManagedObject, Decodable {
enum CodingKeys: String, CodingKey {
case privacy
case sharing
}
required convenience public init(from decoder: Decoder) throws {
guard let context = decoder.userInfo[.context] as? NSManagedObjectContext
else { throw NSError() }
self.init(context: context)
let container = try decoder.container(keyedBy: CodingKeys.self)
let privacy = try container.decode(Access.self, forKey: .privacy)
let sharing = try container.decode(Access.self, forKey: .sharing)
self.privacy = AccessModel(privacy)
self.sharing = AccessModel(sharing)
if let ids = privacy.ids {
let model = SomeFriendsPrivacyModel(context: context)
model.ids = ids
model.userList = self
someFriendsPrivacy = model
}
if let ids = sharing.ids {
let model = SomeFriendsSharingModel(context: context)
model.ids = ids
model.userList = self
someFriendsSharing = model
}
}
}
Как использовать (например, небезопасно):
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.userInfo[.context] = context
try! decoder.decode(UserListModel.self, from: json) // json from 1 question
try! context.save()
- Как мне инициализировать модель списка пользователей из Core Data UserListModel?
extension Access {
init?(_ accessModel: AccessModel) {
switch accessModel {
case .friendsOnly:
self = .friendsOnly
case .private:
self = .private
case .public:
self = .public
default:
return nil
}
}
}
enum UserListInitError: Error {
case error(String)
}
extension UserList {
init(_ listModel: UserListModel) throws {
if let privacy = Access(listModel.privacy) {
self.privacy = privacy
} else {
guard let ids = listModel.someFriendsPrivacy?.ids else { throw UserListInitError.error("Unable to get data from Core Data model") }
self.privacy = .someFriends(ids: ids)
}
if let sharing = Access(listModel.sharing) {
self.sharing = sharing
} else {
guard let ids = listModel.someFriendsSharing?.ids else { throw UserListInitError.error("Unable to get data from Core Data model") }
self.sharing = .someFriends(ids: ids)
}
}
}
Использование (например, небезопасное):
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let request: NSFetchRequest<UserListModel> = UserListModel.fetchRequest()
let listModel = try! context.fetch(request).first!
let userList = try! UserList(listModel)