2024. 9. 17. 22:57ㆍiOS/Swift 문법
1. 복잡한 JSON Model
이전에는 quick.io에서 무지성 JSON 복붙으로 모델을 만들었는데, 디코딩 커스텀하는 좋은 예시가 있어서 공유해본다.
위의 GeoJSON은 type, metadata, features, bbox라는 4개의 키를 가진 JSON 모델이다. 각각의 키값은 단일 값일 수도, 배열일 수도, 다른 오브젝트를 자식으로 가질 수 있다.
우리는 earthquake만을 필요하기 때문에 전체를 디코딩하기 보다는 features 키안의 필요한 값만 디코딩해볼 것이다.
Keyed Contaienr and UnKeyed Conatiner
Container는 Keyed 또는 UnKeyd이다. 일반적으로 우리가 써왔던 유일한 값들은 Keyed이다. 반면, UnKeyed는 unnamed element로, array가 있다.
{
"type":"FeatureCollection",
"metadata":{"generated":1636130213000,"url":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson","title":"USGS All Earthquakes, Past Month","status":200,"api":"1.10.3","count":9104},
"features":[
{"type":"Feature","properties":{"mag":0.34,"place":"5km NW of The Geysers, CA","time":1636129710550,"updated":1636129809904,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/nc73649170","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc73649170.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"automatic","tsunami":0,"sig":2,"net":"nc","code":"73649170","ids":",nc73649170,","sources":",nc,","types":",nearby-cities,origin,phase-data,","nst":11,"dmin":0.001816,"rms":0.03,"gap":62,"magType":"md","type":"earthquake","title":"M 0.3 - 5km NW of The Geysers, CA"},"geometry":{"type":"Point","coordinates":[-122.7968369,38.8093338,4.65]},"id":"nc73649170"},
{"type":"Feature","properties":{"mag":2.25999999,"place":"4 km ENE of Pahala, Hawaii","time":1636129061070,"updated":1636129255400,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/hv72783692","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/hv72783692.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"automatic","tsunami":0,"sig":79,"net":"hv","code":"72783692","ids":",hv72783692,","sources":",hv,","types":",origin,phase-data,","nst":42,"dmin":null,"rms":0.119999997,"gap":127,"magType":"md","type":"earthquake","title":"M 2.3 - 4 km ENE of Pahala, Hawaii"},"geometry":{"type":"Point","coordinates":[-155.434173583984,19.2189998626709,34.8699989318848]},"id":"hv72783692"},
{"type":"Feature","properties":{"mag":0.87,"place":"8km NW of The Geysers, CA","time":1636128941980,"updated":1636130052015,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/nc73649165","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc73649165.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"automatic","tsunami":0,"sig":12,"net":"nc","code":"73649165","ids":",nc73649165,","sources":",nc,","types":",nearby-cities,origin,phase-data,scitech-link,","nst":22,"dmin":0.008485,"rms":0.02,"gap":49,"magType":"md","type":"earthquake","title":"M 0.9 - 8km NW of The Geysers, CA"},"geometry":{"type":"Point","coordinates":[-122.8133316,38.831501,2.21]},"id":"nc73649165"},
{"type":"Feature","properties":{"mag":2.48,"place":"37km ENE of Ensenada, B.C., MX","time":1636128601890,"updated":1636128819182,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/ci39849871","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ci39849871.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"automatic","tsunami":0,"sig":95,"net":"ci","code":"39849871","ids":",ci39849871,","sources":",ci,","types":",nearby-cities,origin,phase-data,scitech-link,","nst":14,"dmin":0.2566,"rms":0.17,"gap":110,"magType":"ml","type":"earthquake","title":"M 2.5 - 37km ENE of Ensenada, B.C., MX"},"geometry":{"type":"Point","coordinates":[-116.2463333,31.9651667,19.17]},"id":"ci39849871"},
{"type":"Feature","properties":{"mag":0.51,"place":"15km NE of Little Lake, CA","time":1636127455870,"updated":1636129810317,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/ci39849855","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ci39849855.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"reviewed","tsunami":0,"sig":4,"net":"ci","code":"39849855","ids":",ci39849855,","sources":",ci,","types":",nearby-cities,origin,phase-data,scitech-link,","nst":12,"dmin":0.007215,"rms":0.08,"gap":64,"magType":"ml","type":"earthquake","title":"M 0.5 - 15km NE of Little Lake, CA"},"geometry":{"type":"Point","coordinates":[-117.7733333,36.0221667,1.84]},"id":"ci39849855"},
{"type":"Feature","properties":{"mag":1.8,"place":"29 km WNW of Rachel, Nevada","time":1636126658738,"updated":1636129630877,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/nn00827478","detail":"https://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nn00827478.geojson","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"reviewed","tsunami":0,"sig":50,"net":"nn","code":"00827478","ids":",nn00827478,","sources":",nn,","types":",origin,phase-data,","nst":7,"dmin":0.509,"rms":0.1503,"gap":216.76,"magType":"ml","type":"earthquake","title":"M 1.8 - 29 km WNW of Rachel, Nevada"},"geometry":{"type":"Point","coordinates":[-116.0623,37.7392,0]},"id":"nn00827478"}
],
"bbox":[
-179.9818,-62.918,-7.3,179.9919,86.307,655.67
]
}
Root/features/properties가 우리가 추출하고자 하는 object이다.
GeoJSON 모델 코드를 봐보자
struct GeoJSON: Decodable {
private enum RootCodingKeys: String, CodingKey {
case features
}
private enum FeatureCodingKeys: String, CodingKey {
case properties
}
private(set) var quakes: [Quake] = []
init(from decoder: Decoder) throws {
let rootContainer = try decoder.container(keyedBy: RootCodingKeys.self)
var featuresContainer = try rootContainer.nestedUnkeyedContainer(forKey: .features)
while !featuresContainer.isAtEnd {
let propertiesContainer = try featuresContainer.nestedContainer(keyedBy: FeatureCodingKeys.self)
if let properties = try? propertiesContainer.decode(Quake.self, forKey: .properties) {
quakes.append(properties)
}
}
}
}
1. 루트 키를 이용하여 루트 컨테이너를 가져온다.
2. 루트 컨테이너에서 우리가 원하는 컨테이너에 접근한다. 여기서 features는 유일한 값이아닌 array형태인 UnKeyed이기 때문에 `nestedUnKeydContainer`로 접근한다.
3. while문 안에서 feature container array가 끝날 때 까지 값을 가져온다.
4. 피처 컨테이너안에서 properties는 유일한 키인 object이다. `nestedContainer`에 접근한다.
5. Quake Codable 모델을 이용하여 디코딩에 성공하면 리스트에 추가한다.
2. Depth가 깊은 Model - GeoJSONDetail
다음에서 우리가 원하는 값은 lattitude와 longitude 뿐이다. 하지만 이미지와 같이 depth가 엄청 깊다. 이번에는 depth가 깊은 model에서 원하는 값만 디코딩해보도록 하겠다.
{
"type":"Feature",
"properties":{
"mag":2.25999999,"place":"4 km ENE of Pahala, Hawaii","time":1636129061070,"updated":1636129255400,"tz":null,"url":"https://earthquake.usgs.gov/earthquakes/eventpage/hv72783692","felt":null,"cdi":null,"mmi":null,"alert":null,"status":"automatic","tsunami":0,"sig":79,"net":"hv","code":"72783692","ids":",hv72783692,","sources":",hv,","types":",origin,phase-data,","nst":42,"dmin":null,"rms":0.119999997,"gap":127,"magType":"md","type":"earthquake","title":"M 2.3 - 4 km ENE of Pahala, Hawaii",
"products":{
"origin":[
{"indexid":"834411","indexTime":1636129256903,"id":"urn:usgs-product:hv:origin:hv72783692:1636129255400","type":"origin","code":"hv72783692","source":"hv","updateTime":1636129255400,"status":"UPDATE",
"properties":{"azimuthal-gap":"127","depth":"34.8699989318848","depth-type":"from location","error-ellipse-azimuth":"339","error-ellipse-intermediate":"1728.0000696","error-ellipse-major":"2015.9999376","error-ellipse-minor":"864.0000336","error-ellipse-plunge":"71","error-ellipse-rotation":"0","evaluation-status":"preliminary","event-type":"earthquake","eventParametersPublicID":"quakeml:hv.anss.org/Event/HV/72783692#163612925540","eventsource":"hv","eventsourcecode":"72783692","eventtime":"2021-11-05T16:17:41.070Z","horizontal-error":"0.68",
"latitude":"19.2189998626709","longitude":"-155.434173583984","magnitude":"2.25999999","magnitude-error":"0.839999974","magnitude-num-stations-used":"24","magnitude-source":"HV","magnitude-type":"md","num-phases-used":"52","num-stations-used":"42","origin-source":"HV","pdl-client-version":"Version 2.4.0 2020-05-11","quakeml-magnitude-publicid":"quakeml:hv.anss.org/Netmag/HV/95279212","quakeml-origin-publicid":"quakeml:hv.anss.org/Origin/HV/75783687","quakeml-publicid":"quakeml:hv.anss.org/Event/HV/72783692","review-status":"automatic","standard-error":"0.119999997","title":"4 km ENE of Pahala, Hawaii","version":"0","vertical-error":"0.790000021"},"preferredWeight":157,"contents":{"contents.xml":{"contentType":"application/xml","lastModified":1636129255000,"length":195,"url":"https://earthquake.usgs.gov/realtime/product/origin/hv72783692/hv/1636129255400/contents.xml"},"quakeml.xml":{"contentType":"application/xml","lastModified":1636129255000,"length":2966,"url":"https://earthquake.usgs.gov/realtime/product/origin/hv72783692/hv/1636129255400/quakeml.xml"}}}],"phase-data":[{"indexid":"834412","indexTime":1636129257794,"id":"urn:usgs-product:hv:phase-data:hv72783692:1636129255400","type":"phase-data","code":"hv72783692","source":"hv","updateTime":1636129255400,"status":"UPDATE","properties":{"azimuthal-gap":"127","depth":"34.8699989318848","depth-type":"from location","error-ellipse-azimuth":"339","error-ellipse-intermediate":"1728.0000696","error-ellipse-major":"2015.9999376","error-ellipse-minor":"864.0000336","error-ellipse-plunge":"71","error-ellipse-rotation":"0","evaluation-status":"preliminary","event-type":"earthquake","eventParametersPublicID":"quakeml:hv.anss.org/Event/HV/72783692#163612925540","eventsource":"hv","eventsourcecode":"72783692","eventtime":"2021-11-05T16:17:41.070Z","horizontal-error":"0.68","latitude":"19.2189998626709","longitude":"-155.434173583984","magnitude":"2.25999999","magnitude-error":"0.839999974","magnitude-num-stations-used":"24","magnitude-source":"HV","magnitude-type":"md","num-phases-used":"52","num-stations-used":"42","origin-source":"HV","pdl-client-version":"Version 2.4.0 2020-05-11","quakeml-magnitude-publicid":"quakeml:hv.anss.org/Netmag/HV/95279212","quakeml-origin-publicid":"quakeml:hv.anss.org/Origin/HV/75783687","quakeml-publicid":"quakeml:hv.anss.org/Event/HV/72783692","review-status":"automatic","standard-error":"0.119999997","version":"0","vertical-error":"0.790000021"},"preferredWeight":157,"contents":{"contents.xml":{"contentType":"application/xml","lastModified":1636129255000,"length":195,"url":"https://earthquake.usgs.gov/realtime/product/phase-data/hv72783692/hv/1636129255400/contents.xml"},"quakeml.xml":{"contentType":"application/xml","lastModified":1636129255000,"length":106487,"url":"https://earthquake.usgs.gov/realtime/product/phase-data/hv72783692/hv/1636129255400/quakeml.xml"
}
}
}
]
}
},
"geometry":{"type":"Point","coordinates":[-155.434173583984,19.2189998626709,34.8699989318848]},"id":"hv72783692"}
1. Container에서 Keyd propery인 'properties'에 접근한다.
2. 차례대로 Products -> Origin -> OriginProperties에 접근한다.
struct QuakeLocation: Decodable {
var latitude: Double { properties.products.origin.first!.properties.latitude }
var longitude: Double { properties.products.origin.first!.properties.longitude }
private var properties: RootProperties
struct RootProperties: Decodable {
var products: Products
}
struct Products: Decodable {
var origin: [Origin]
}
struct Origin: Decodable {
var properties: OriginProperties
}
struct OriginProperties {
var latitude: Double
var longitude: Double
}
}
1. Keyed property인 properties를 디코딩한다.
2. properties에서 Products -> Origin -> OriginProeprties model을 디코딩한다.
extension QuakeLocation.OriginProperties: Decodable {
private enum OriginPropertiesCodingKeys: String, CodingKey {
case latitude, longitude
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: OriginPropertiesCodingKeys.self)
let longitude = try container.decode(String.self, forKey: .longitude)
let latitude = try container.decode(String.self, forKey: .latitude)
guard let longitude = Double(longitude),
let latitude = Double(latitude) else { throw QuakeError.missingData }
self.longitude = longitude
self.latitude = latitude
}
}
1. properties 모델에서 latitude와 longitude만 디코딩한다.
2. doble로 사용할 것이지만, json에서는 string으로 오므로, string으로 디코딩 후 type을 바꿔준다.
'iOS > Swift 문법' 카테고리의 다른 글
Using Existential and Generic feat.Dispatch (1) | 2024.09.17 |
---|---|
면접 질문: Closure에서 weak self를 사용하지 않아도 되는 경우? (0) | 2024.09.08 |
Map과 FlatMap, CompactMap (0) | 2022.03.21 |
iOS XCTest 코드 (1) (0) | 2022.03.02 |
Class function과 Static function (0) | 2022.02.28 |