Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- rxswift
- 리펙터링
- 스위프트
- uitableview
- swift documentation
- RxCocoa
- MVVM
- Refactoring
- 리팩토링
- map
- uiscrollview
- swiftUI
- Xcode
- Protocol
- 리펙토링
- SWIFT
- clean architecture
- tableView
- ribs
- ios
- combine
- UICollectionView
- Observable
- UITextView
- HIG
- 클린 코드
- Human interface guide
- collectionview
- 애니메이션
- Clean Code
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] Decoder(), KeyDecodingStrategy, DateDecodingStrategy 사용 방법 (Date 여러 형태 처리) 본문
iOS 응용 (swift)
[iOS - swift] Decoder(), KeyDecodingStrategy, DateDecodingStrategy 사용 방법 (Date 여러 형태 처리)
jake-kim 2022. 1. 16. 15:14알아야하는 개념 - CodingKeys란?
- 인코딩과 디코딩할때 사용하는 key값
- Codable 타입은 CodingKeys라는 nested type인 CodingKeys를 정의해야 하는데, 이 CodingKeys는 CodingKey타입을 준수
- CodingKey에 case문으로 key값들을 정의하고 Person이라는 구조체를 json으로 변경할때 이 값을 내부적으로 이용
struct Person: Codable {
enum CodingKeys: String, CodingKey {
case name
case age
}
var name: String
var age: Int
}
KeyDocodingStrategy
- json의 key값을 coding keys로 어떻게 decoding할 것인지 정의하는 프로퍼티
- CodingKeys에 정의된 case와 동일하게 매핑시켜주는 역할
- 아래 코드에서 json을 Person으로 매핑 방법? -> Person: Codable에 rawValue로 입력해주는 방법도 있지만, keyDecodingStrategy를 이용한 방법도 존재
struct Person: Codable { var personname: String var personage: Int } let json = """ { "person-name": "jake", "person-age": 10 } """
- 아래 코드에서 json을 Person으로 매핑 방법? -> Person: Codable에 rawValue로 입력해주는 방법도 있지만, keyDecodingStrategy를 이용한 방법도 존재
- keyDecodingStrategy를 이용한 방법
- JSONDecoder.KeyDecodingStrategy를 커스텀
private let myKeyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = { .custom { codingPath in print(codingPath) let key = codingPath.last!.stringValue.split(separator: "-").joined() print("key = \(key)") // personname, personage return MyCodingKey(stringValue: key)! } }()
- JSONDecoder에 위에서 정의한 프로퍼티 keyDeocodingStrategy 사용
private var myDecoder: JSONDecoder { let jsonDecoder = JSONDecoder() jsonDecoder.keyDecodingStrategy = myKeyDecodingStrategy return jsonDecoder }
- 위에서 정의한 decoder사용하여 decoding
private func decoder(_ json: String) throws -> Person { do { let person = try self.myDecoder.decode(Person.self, from: Data(json.utf8)) return person } catch { throw DecoderError.error } } do { let person = try self.decoder(json) print(person) // Person(personname: "jake", personage: 10) } catch { print(error) }
- JSONDecoder.KeyDecodingStrategy를 커스텀
- keyDecodingStrategy 전체 코드
import UIKit enum DecoderError: Error { case error } struct Person: Codable { var personname: String var personage: Int } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let json = """ { "person-name": "jake", "person-age": 10 } """ do { let person = try self.decoder(json) print(person) } catch { print(error) } } private let myKeyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = { .custom { codingPath in print(codingPath) let key = codingPath.last!.stringValue.split(separator: "-").joined() print("key = \(key)") // personname, personage return MyCodingKey(stringValue: key)! } }() private var myDecoder: JSONDecoder { let jsonDecoder = JSONDecoder() jsonDecoder.keyDecodingStrategy = myKeyDecodingStrategy return jsonDecoder } private func decoder(_ json: String) throws -> Person { do { let person = try self.myDecoder.decode(Person.self, from: Data(json.utf8)) return person } catch { throw DecoderError.error } } } struct MyCodingKey: CodingKey { var stringValue: String var intValue: Int? init?(stringValue: String) { self.stringValue = stringValue } init?(intValue: Int) { self.intValue = intValue self.stringValue = String(intValue) } }
DateDecodingStrategy
- json으로 내려오는 date 정보를 formating할때 사용되는 값
- 아래처럼 date 포멧이 여러개로 내려오는 경우, 파싱을 위해 사용
let json = """ { "date1": "2021-12-30 15:18:00", "date2": "2022-01-30T05:18:00", "date3": "2019-12-17" } """
- 일반적으로 json없이 date 문자열을 Date 객체로 인코딩하는 방법은 DateFormatter 객체를 사용
let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" // format: ISO8601DateFormatter let date = dateFormatter.date(from: "2013-07-21T19:32:00Z") print(date) // Optional(2013-07-21 19:32:00 +0000)
- json date를 파싱할때도 DateFormatter 객체가 사용되므로 프로퍼티로 선언
private let myDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" // format: ISO8601DateFormatter return dateFormatter }()
- JsonDecoder에서 위에서 선언한 dateForamtter 사용
private var myDecoder: JSONDecoder { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .custom { decoder in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) guard let date = self.myDateFormatter.date(from: dateString) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "cannot decoding \(dateString)") } return date } return decoder }
- 사용
let json = """ { "date": "2013-07-21T19:32:00Z" } """ let jsonData = json.data(using: .utf8)! let date = try! self.myDecoder.decode(MyDate.self, from: jsonData) print(date) // MyDate(date: 2013-07-21 19:32:00 +0000)
- dateDecodingStrategy 전체 코드
import UIKit struct MyDate: Codable { var date: Date } class ViewController: UIViewController { private let myDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" // format: ISO8601DateFormatter return dateFormatter }() private var myDecoder: JSONDecoder { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .custom { decoder in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) guard let date = self.myDateFormatter.date(from: dateString) else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "cannot decoding \(dateString)") } return date } return decoder } override func viewDidLoad() { super.viewDidLoad() let json = """ { "date": "2013-07-21T19:32:00Z" } """ let jsonData = json.data(using: .utf8)! let date = try! self.myDecoder.decode(MyDate.self, from: jsonData) print(date) // MyDate(date: 2013-07-21 19:32:00 +0000) } }
date 디코딩 응용 - 여러 형태의 date 문자열 처리
- 아래처럼 date 포멧이 여러개가 내려오는 경우 처리 방법
let json = """
{
"date1": "2022-07-21T19:32:00Z",
"date2": "2022-01-03"
}
"""
- JSONDecoder에 extension으로 DateFormatter 배열 추가: for문을 돌면서 인수로 들어온 formatter로 디코딩되면 반환
// https://stackoverflow.com/questions/44682626/swifts-jsondecoder-with-multiple-date-formats-in-a-json-string extension JSONDecoder { var dateDecodingStrategyFormatters: [DateFormatter]? { @available(*, unavailable, message: "This variable is meant to be set only") get { return nil } set { guard let formatters = newValue else { return } self.dateDecodingStrategy = .custom { decoder in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) for formatter in formatters { if let date = formatter.date(from: dateString) { return date } } throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)") } } } }
- DateFormatter에 extension으로 형식 정의
extension DateFormatter { static let myISO8601DateFormatter: DateFormatter = { var dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" return dateFormatter }() static let myDateFormatter: DateFormatter = { var dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" return dateFormatter }() }
- 사용
let json = """ { "date1": "2022-07-21T19:32:00Z", "date2": "2022-01-03" } """ let jsonData = json.data(using: .utf8)! let dates = try! self.myJsonDecoder.decode(MyDate.self, from: jsonData) print(dates) // MyDate(date1: 2022-07-21 19:32:00 +0000, date2: 2022-01-02 15:00:00 +0000)
- 전체 코드
import UIKit struct MyDate: Codable { var date1: Date var date2: Date } class ViewController: UIViewController { private let myJsonDecoder: JSONDecoder = { let jsonDecoder = JSONDecoder() jsonDecoder.dateDecodingStrategyFormatters = [ DateFormatter.myISO8601DateFormatter, DateFormatter.myDateFormatter ] return jsonDecoder }() override func viewDidLoad() { super.viewDidLoad() let json = """ { "date1": "2022-07-21T19:32:00Z", "date2": "2022-01-03" } """ let jsonData = json.data(using: .utf8)! let dates = try! self.myJsonDecoder.decode(MyDate.self, from: jsonData) print(dates) // MyDate(date1: 2022-07-21 19:32:00 +0000, date2: 2022-01-02 15:00:00 +0000) } } extension JSONDecoder { var dateDecodingStrategyFormatters: [DateFormatter]? { @available(*, unavailable, message: "This variable is meant to be set only") get { return nil } set { guard let formatters = newValue else { return } self.dateDecodingStrategy = .custom { decoder in let container = try decoder.singleValueContainer() let dateString = try container.decode(String.self) for formatter in formatters { if let date = formatter.date(from: dateString) { return date } } throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \(dateString)") } } } } extension DateFormatter { static let myISO8601DateFormatter: DateFormatter = { var dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" return dateFormatter }() static let myDateFormatter: DateFormatter = { var dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd" return dateFormatter }() }
* 참고
https://developer.apple.com/documentation/swift/codingkey
https://developer.apple.com/documentation/foundation/jsondecoder/2949119-keydecodingstrategy
'iOS 응용 (swift)' 카테고리의 다른 글
Comments