-
[iOS] - Cocoa Touch μμ JSON λ€λ£¨κΈ°iOS/π€ App 2021. 5. 7. 23:27
1μ°¨ μμ : 2021.05.25
JSON = JavaScript Object Notation
π€ μ¬μ μ§μ - JSON μ΄λ?
λ¨μνκ² λ°μ΄ν°λ₯Ό νννλ λ°©λ² μ€ νλμ΄λ€. (ν΅μ λ°©λ²λ μλκ³ , νλ‘κ·Έλλ° λ¬Έλ²λ μλλ€. ν¬λ§·μ΄λ€ !!!!)
μλ²μ ν΄λΌμ΄μΈνΈ κ°μ λ°μ΄ν° κ΅νμμ μΌλ°μ μΌλ‘ λ§μ΄ μ¬μ©λλ€.
ν΄λΌμ΄μΈνΈκ° API Request λ₯Ό 보λ΄λ©΄ μλ²κ° μλ΅μΌλ‘ JSON λ°μ΄ν°λ₯Ό 보λ΄μ€λ€.
JSONμ ν¬λ§·μ μλ°μ€ν¬λ¦½νΈ κ°μ²΄ νκΈ°λ²μ λ°λ₯Έλ€
key
-value
μμ μ΄λ£¨μ΄ νννλ©°, keyλ λ¬Έμμ΄μ΄λ€. λ¬Έμμ΄μ" "
μλ°μ΄νλ₯Ό μ¬μ©νμ¬ νκΈ°νλ€.- { } μ λ΄λΆμ
key-value
λ‘ κ΅¬μ±λλ€. μ¦ Swift λμ λ리μ μ μ¬ν ꡬ쑰μ΄λ€. - JSONνμμμλ null, number, string, array, object, booleanμ μ¬μ©ν μ μλ€.
λ€μ νλ² λ§νμ§λ§ JSONμ λ°μ΄ν° νν λ°©μμΌ λΏμ΄λ©°, ν μ€νΈνμμ λ°μ΄ν° μ΄λ€.
κ·Έλ κΈ° λλ¬Έμ κ°λ μ±μ΄ μ’μΌλ©°, λ¨μν ν μ€νΈμ΄κΈ° λλ¬Έμ νΉμ μΈμ΄μ μ’ μλμ§ μλλ€.
λλΆλΆμ νλ‘κ·Έλλ° μΈμ΄μμ JSON ν¬λ§· λ°μ΄ν°λ₯Ό νΈλ€λ§ ν μ μλ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ 곡νλ€
π€μ¬μ μ§μ - JSON μ΄μ μ μ¬μ©νλ XMLμ?
νΉμ§ λ°μ΄ν° κ° μμͺ½μΌλ‘ νκ·Έκ° μλ€. νκ·Έλ‘ ννλλ λ°μ΄ν°μ΄λ€.
μ΄κ²μ HTMLμ κΈ°λ°μΌλ‘ κ³ μλμκΈ° λλ¬ΈμΈλ°, νκ·Έλ₯Ό μ€μΈλ€ ν΄λ μ΅μν νννλ €λ©΄ μμͺ½μ λͺ κΈμμ©μ΄ μμ΄μΌ νκΈ° λλ¬Έμ λ΄κ° λ΄£μ λ μμ±λ μ΄λ ΅κ³ κ°λ μ±λ μ’μ§ μλ€.
μ°Έκ³ μλ£ : https://velog.io/@surim014/JSON
κ°μ
λ€νΈμν¬ μ°κ²°μ ν΅ν λ°μ΄ν° μ μ‘μ΄λ, λμ€ν¬μ λ°μ΄ν°λ₯Ό μ μ₯νκ±°λ, API μλ²λ μλΉμ€λ‘ λ°μ΄ν°λ₯Ό μ μΆνλ κ²κ³Ό κ΄λ ¨λ νλ‘κ·Έλλ° μμ λ€μ΄ λ§μ΅λλ€.
μ΄λ° μμ λ€μ μ€κ°μ μν μ ν μ μλ νμμΌλ‘μ λ°μ΄ν°μ μΈμ½λ© κ³Ό λμ½λ©μ νμλ‘ ν©λλ€.
μλλ©΄ μ€μννΈλ‘ ννλ λ°μ΄ν°λ₯Ό λ€λ₯Έ νκ²½μμλ ν΄μνκΈ° μ΄λ ΅κΈ° λλ¬Έμ λλ€.
Swift standard library λ λ°μ΄ν° μΈμ½λ© κ³Ό λμ½λ©μ νμ€νλ λ°©λ²μ μ΄λ―Έ μ μν΄λμμ΅λλ€.
μ°λ¦¬λ νμν 컀μ€ν νμ μ μ΄ λ°©λ²μ μ μ©ν κ²μΈλ°, μ΄κ²μ Encodable κ³Ό Decodable νλ‘ν μ½μ ꡬνμ ν΅ν΄μ κ°λ₯ν©λλ€.
μΌλ¨ μ νλ‘ν μ½μ ꡬν νλ©΄ Encoder μ Decoder κ° λ°μ΄ν°λ₯Ό κ°μ§κ³ JSON μ΄λ property list μ κ°μ external representation* μΌλ‘ μΈμ½λ© & λμ½λ©μ ν΄μ€λλ€.
π‘μ€μννΈ μΈλΆμμ μ¬μ©λλ λ°μ΄ν° νμμ external representation μ΄λΌκ³ νννμμ΅λλ€.
Encodable κ³Ό Decodable μ λμμ μ±ννλ€λ©΄ μΈμ½λ©κ³Ό λμ½λ© μμ λͺ¨λλ₯Ό ν μ μμ΅λλ€.
κ·Έλ¦¬κ³ κ·Έκ²μ protocol composition(&) μ ν΅νμ¬ Codable λ‘ μ μλμ΄ μμ΅λλ€.
typealias Codable = Decodable & Encodable // μ€μ λ‘ μ΄λ κ² κ΅¬νλμ΄ μμ π
λ³Έλ‘ μΌλ‘ λμμμ...
JSON Encoding κ³Ό Decoding μ νλ 주체λ
JSONEncoder
μJSONDecoder
μ€λΈμ νΈμ λλ€.Encoding κ³Ό Decoding μ΄ κ°λ₯νλ €λ©΄ λμ struct λ
Encodable
,Decodable
νλ‘ν μ½μ μ±ννκ³ μμ΄μΌ ν©λλ€.λ§μ½ Encodable κ³Ό Decodable μ λͺ¨λ μ±ννλ€λ©΄, Codable νλ‘ν μ½μ μ±ννλ€κ³ λͺ μνλ©΄ λ©λλ€.
μ΄λ€ 컀μ€ν νμ μ codable νκ² λ§λλ κ°μ₯ μ¬μ΄ λ°©λ²μ, κ·Έ νμ μ΄ κ°μ§ μμ±λ€μ λͺ¨λ Codable νμ μΌλ‘ μ μνλ κ²μ λλ€.
Codable ν νμ μ standart library νμ μΈ String, Int, Double νΉμ Foundation νμ μΈ Date, Data, URL λ±μ΄ μμ΅λλ€.
μ΄λ° νμ λ€μ μμ±μΌλ‘λ§ κ΅¬μ±λ νμ μ Codable νλ‘ν μ½μ λ°λ₯Έλ€κ³ λͺ μλ§νλ©΄ λ©λλ€.
"automatically conforms to Codable just declaring that conformance."
struct Landmark: Codable { var name: String var foundingYear: Int // μμ±λ€μ΄ μ΄λ―Έ Codable νλ―λ‘, νλ‘ν μ½ κ΅¬νμ΄ νμ μμ΅λλ€. var location: Coordinate // λ§μ½ 컀μ€ν νμ (Coordinate)μ΄ μΆκ°λλ€κ³ νλλΌλ, κ·Έ νμ μ Codableνκ² μ μνλ€λ©΄ λ¬Έμ μμ΅λλ€. var metadata: [String: String] // Array, Dictionay, Optional κ°μ Built-in typeμ κ·Έ λ΄λΆ νμ μ΄ Codable μΌ κ²½μ° Codableμ΄ λ©λλ€. }
let encoder = JSONEncoder() encoder.outFormatting = .prettyPrinted encoder.keyEncodingStrategy = .converToSnakeCase do { // 1 let jsonData = try encoer.encode(someStruct) // 2 if let str = String(data: jsonData, encoding: .utf8) { print( str ) } } catch { print(error) }
1 : encode() λ Throwing Method μ΄λ―λ‘ try λ‘ μ²λ¦¬ν΄μ£Όμμ΅λλ€.
κ·Έλ¦¬κ³ try λ do catch λ‘ μ²λ¦¬ν©λλ€.
λ°ν κ°μ λ°μ΄λ리 λ°μ΄ν° ννμ JSON μ λλ€.
2 :
λ°μ΄λ리 λ°μ΄ν°λ μΈκ°μ΄ μ½μ μ μκΈ° λλ¬Έμ μΈμ½λ©μ΄ νμν©λλ€.
λμΌλ‘ νμΈνλ €λ©΄ String(data:encoding:) μμ±μλ‘ λ¬Έμμ΄ μΈμ€ν΄μ€λ‘ μμ±ν΄ μΆλ ₯ν΄λ³Ό μ μμ΅λλ€.
λν
outFormatting
μμ±μ.prettyPrinted
λ‘ λ³κ²½ν΄ key-value κ° κ°νμ μΆκ°νμ¬ κ°λ μ±μ λμΌ μ μλ€.
JSON Encoding
Encoding μ Struct μΈμ€ν΄μ€μμ JSON λ°μ΄ν°λ‘ λ³ννλ κ³Όμ μ λλ€.
μμ± μ΄λ¦κ³Ό key μ΄λ¦ 맀μΉ
JSON μμ ν€ λ€μ΄λ° 컨벀μ μ
snake_case
μ΄λ€.νμ§λ§ Swift μμλ
lowerCamelCase
λ₯Ό μ¬μ©νκΈ° λλ¬Έμ μμ μμ΄ Encoding μ μ§ννλ€λ©΄ key name μ΄ lowerCamelCase λ‘ λ€μ΄κ°κΈ° λλ¬Έμ νΈνμ±μ΄ λ¨μ΄μ§ κ²μ΄λ€.μ΄λλ
keyEncodingStrategy
μμ±μ λ³κ²½νμ¬ λμν μ μλ€.encoder.keyEncodignStrategy = .convertToSnakeCase
λ μ§ νμ 맀μΉ
Swift λ Date νμ μ μμ±μ JSON μΌλ‘ μΈμ½λ© ν λ, Double κ°μΌλ‘ μΈμ½λ©νλ€.
μ΄ κ°μ 2001λ 1μ 1μΌμ μμμ μΌλ‘ λ¨μ΄μ§ λ§νΌμ μκ°μ νννλλ°, μ΄ λ¨μμ΄λ€.
κ°λ μ±μ΄ λ§€μ° λ¨μ΄μ§λ―λ‘ λ§μ΄ μ¬μ©νλ iso8601 νμμ λ°λ₯Έ λ¬Έμμ΄λ‘ μΈμ½λ©νκ² λ°κΏμ€ μ μλ€.
μΈμ½λ μ€λΈμ νΈμ
dateEncodingStrategy
μμ±μ λ³κ²½νλ©΄ λλ€.encoder.dateEncodingStrategy = .iso8601
κ·Έλ°λ° μ΄ νμμ κ·Έλλ‘ λ°λ₯΄μ§ μλ API λ€μ΄ μλ€. (μ: Kakao REST API)
μ΄ κ²½μ°μ ν΄λΉ Key νΉμ μμ±μ Encoding νΉμ Decoding μ μ€ν¨νκ² λλ€.
μ΄λλ DateFormatter μΈμ€ν΄μ€λ₯Ό μ§μ λ§λ€μ΄μ encoder μ μ λ¬ν΄μΌνλ€.
// 1. Custom DateFormatter λ₯Ό μμ±νλ€. var dateFormatter: DateFormatter = { var formatter = DateFormatter() formatter.dateFormat = "MMMyyyy" // dateFormat νμλ¬Έμμ΄ }() // 2. encoder μ€λΈμ νΈμ .formmated caseμ μ°κ΄κ°μΌλ‘ μ λ¬νλ€. encoder.dateEncodingStrategy = .formmatted(dateFormatter)
π― κ·Έλ°λ° μνλ DateFormat νμλ¬Έμμ΄μ μ§μ μμ±νλ κ²μ΄ μ΄λ ΅κ² λκ»΄μ§μ μλ€.
μ΄ λλ https://nsdateformatter.com μμ μνλ νμλ¬Έμμ΄μ κ°νΈνκ² μμ±ν μ μλ€.
JSON Decoding
Decodingμ μλ²λ‘λΆν° μ λ¬λ°μ JSON λ°μ΄ν°λ₯Ό Struct μΈμ€ν΄μ€λ‘ λ³ννλ κ³Όμ μ΄λ€.
guard let jsonData = jsonStr.data(using: .utf8) else { fatalError() } // let decoder = JSONDecoder() let result = try? decoder.decode(Person.self, from: jsonData)
Struct μμ±μ νμ
μΌλ° νμ μ΄λΌλ©΄ decoding μ€ν¨ μ λ°νμ μ€λ₯κ° λ°μνλ€
μ΅μ λ νμ μ΄λΌλ©΄ decoding μ€ν¨ μ nil λ‘ μ΄κΈ°νλλ€.
JSON key μ struct μμ±μ΄ λ€λ₯Έ κ²½μ°
λ§μ½ ν€ λ€μκ³Ό μμ± μ΄λ¦μ Naming convention λ§ λ€λ₯΄λ€λ©΄ encoding λμ κ°μ λ°©μμΌλ‘ ν΄κ²°ν΄μ£Όλ©΄ λλ€.
decoder.keyDecodingStrategy = .convertFromSnakeCase
νμ§λ§ μ΄λ¦μ΄ μμ λ€λ₯Έκ²½μ°λ custom key mappping μ ν΄μ£Όμ΄μΌ νλ€.
CodingKey μ λν λ΄μ©μ λ°λ‘ κΈλ‘ μ 리νμμ΅λλ€
struct iPhone: Codable { var SerialNumber: String var Storage: Int var generation: Int var ramSize: Int enum CodingKeys: String, CodingKey { case SerialNumber case Storage case generation = "model_number" case ramSize } }
μλ₯Ό λ€μ΄ JSON λ°μ΄ν°μμ
model_number
ν€μ μ μ₯λ κ°μgeneration
μμ±μ λ°μμ€κ³ μΆμ λ, custom Key mapping μ νμ§ μμΌλ©΄ decoding error κ° λ°μν κ²μ΄λ€. Int νμ μΈgeneration
μ 맀νλ κ°μ΄ μκΈ° λλ¬Έμ΄λ€.μμ μ½λμ κ°μ΄ νμ λ΄μ enum μ μ μΈνκ³ , raw Value λ‘ String λ‘ μ μΈνκ³ , CodingKey νλ‘ν μ½μ μ±ννλ€μ 맀ννκ³ μΆμ case λ§ rawValue μ€μ μ ν΄μ£Όλ©΄ ν΄κ²°λλ€. rawValue μ λ€μ΄κ°λ κ°μ 맀ννκ³ μΆμ JSON μ key name μ΄λ€.
π― Custom Encoding & Custom Decoding
λ³΄ν΅ μΈμ½λ©κ³Ό λμ½λ©μ μ μ½μ λ£κ³ μΆμλ 컀μ€ν μΌλ‘ ꡬννλ€.
Encodable & Decodable νλ‘ν μ½μ λ©μλλ₯Ό μ§μ ꡬννλ©΄ λλ€.
" π₯³ μ리λ₯Ό μ λλ‘ μ΄ν΄νμΌλ, 컀μ€ν μ΄ νμν λ λ΄μ©μ λ μΆκ°νμ! "
π‘ κΈ°λ³Έ μ리: protocol method λ₯Ό μ§μ ꡬννκ³ , container μ unkeyed-data λ₯Ό μ μ νκ² encode/decode νμ!!!!
Encodable μ κ²½μ° encode(to encoder:) Decodable μ κ²½μ° init(from decoder:) λ©μλλ₯Ό μ§μ ꡬννλ©΄ λλ€.
func encode(to encoder: Encoder) throws { <#code#> } init(from decoder: Decoder) throws { <#code#> }
μ°μ 컀μ€ν μΈμ½λ©μ νλ €λ©΄ CodingKey νλ‘ν μ½μ μ±νν CodingKeys μ΄κ±°νμ μμ±ν΄μΌ νλ€.
struct Person: Codable { var firstName: String var lastName: String var age: Int var address: String? enum CodingKeys: String, CodingKey { case firstName case lastName case age case address }
μ? container(keyedBy:) μ νλΌλ―Έν°λ‘ CodingKey μ λ©ν νμ μ λ°λλ€.
func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(firstName, forKey: .firstName) try container.encode(lastName, forKey: .lastName) guard (30...60).contains(age) else { // μ μ½μ μΆκ°νκ³ μ‘°κ±΄μ λ§μ§ μλλ€λ©΄ μλ¬λ₯Ό λμ§λ€. throw DecodingError.invalidRange } try container.encodeIfPresent(address, forKey: .address) }
컨ν μ΄λμ μ μ : " an container appropriate for holding multiple unkeyed values."
그리κ³
encode(_, forKey:)
λ©μλλ‘ container λ΄λΆμ λ°μ΄ν°(unkeyed values)λ€μ μΈμ½λ©νμ¬ ν€μ 맀νν΄μ€λ€.encode λ
throws
ν€μλμμ λ³Όμ μλ―μ΄ Throwing Method λ‘, λ΄λΆ λΈλμμ μλ¬λ₯Ό λμ§ μ μλ€.μ μ½λμμλ
age
μμ±μ κ°μ΄ 30~60 μ¬μ΄κ° μλλΌλ©΄ 미리 μ μλ μλ¬λ₯Ό λμ§κ³ μλ€.λ!
π€π’[μ°μ§±μ iOS λΈλ‘κ·Έ]π΅π»
iOSλ₯Ό 곡λΆνλ©΄μ λ°°μ΄ λ΄μ©μ κΈ°λ‘νκ³ μμ΅λλ€.μ°Έκ³ μλ£: https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding
'iOS > π€ App' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[iOS] GCD (Grand Central Dispatch) λ? (feat. main & global dispatch queue) (0) 2021.06.07 [iOS] KVO λ? (Key Value Observing) (0) 2021.05.22 'νμκΈ λνΉ μ±'μ μ μνλ©΄μ λ°°μ΄ μ νκ³ (0) 2021.03.05 [UIKit] λ·° νκΉ μ¬μ©ν΄λ³΄κΈ° (View Tagging) (0) 2021.03.05 [iOS] MVC λ? (feat. Cocoa MVC) (0) 2021.03.03