모델 객체의 인스턴스를 데이터 타입으로 인코딩하거나, 데이터를 모델 객체 타입으로 디코딩할 때 Codable과 NSCoding 프로토콜을 사용할 수 있다.
Codable
typealias Codable = Encodable & Decodable
class, struct, enum에 모두 Codable protocol을 채택할 수 있다.
CodingKey 프로토콜을 채택해서 json 데이터의 key와, 속성의 이름과 다르게 지정할 수 있다.
decoder를 이용해서 init()을 구현할 수 있다. (JSON으로부터 데이터 빼내기)
extension User: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
birth = try container.decode(String.self, forKey: .birth)
phoneNum = try container.decode(String.self, forKey: .phoneNum)
}
}
decode와 반대로 encode 함수를 작성한다.
// encode 함수 직접 구현
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Codingkeys.self)
try contrainer.encode(someDate, forKey: .someDate)
}
Codable은 속성의 타입이 지정되어 있는(Int, String...) 경우에만 사용할 수 있다.
struct 안의 struct를 갖고 있거나, Any 타입을 속성으로 갖고 있는 타입은 Codable을 채택할 수 없다.
배열 형태로부터 Data를 얻는 예제는 다음과 같다.
let json = """
[
{
"name": "Banana",
"points": 200,
"description": "A banana grown in Ecuador."
},
{
"name": "Orange",
"points": 100
}
]
""".data(using: .utf8)!
struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}
let decoder = JSONDecoder()
let products = try decoder.decode([GroceryProduct].self, from: json)
다이나믹하게 다형성으로 동작하는 방식이 필요한 경우에는 NSCoding 프로토콜을 사용하는 것이 권장된다.
(Codable과 동시에 채택도 가능하다)
class NBeverage : NSObject, NSCoding {
private var brand : String = ""
required init?(coder: NSCoder) {
self.brand = coder.decodeObject(forKey: "brand") as! String
}
func encode(with coder: NSCoder) {
coder.encode(brand, forKey: "brand")
}
}
required init
과 encode
메서드 구현이 필요하다.
NSCoding 프로토콜을 채택한 객체는 NSKeyedArchiver로 인코딩, NSKeyedUnarchiver로 디코딩한다.
아카이브 한 결과는 Data 타입이 되고, json 형식은 불가능하다.
(json 형식으로 데이터를 전송하려면 UserDefault or Plist 파일을 이용해서 저장하는 방법이 있다)
static func unarchive(with text: Data) -> Any? {
do {
let object = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(text)
return object
}
catch {
print(error)
}
return nil
}
static func archive<T>(with things: T) -> Data {
do {
let archived = try NSKeyedArchiver.archivedData(withRootObject: things, requiringSecureCoding: false)
return archived
}
catch {
print(error)
}
return Data()
}
Codable과 NSCoding의 차이는 다음 블로그 글을 참고😉