рдореЛрдмрд╛рдЗрд▓ рдПрдкреНрд▓рд┐рдХреЗрд╢рди, рдПрдХ рддрд░рд╣ рд╕реЗ рдпрд╛ рдХрд┐рд╕реА рдЕрдиреНрдп рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рддреЗ рд╕рдордп, рд╣рдореЗрдВ рд╕рд░реНрд╡рд░ рдбреЗрдЯрд╛ рдХреЛ рдЖрдВрддрд░рд┐рдХ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдореЙрдбрд▓ рдореЗрдВ рдкрд╛рд░реНрд╕ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдЕрдзрд┐рдХрд╛рдВрд╢ рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рдпрд╣ рдбреЗрдЯрд╛ JSON рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдЖрддрд╛ рд╣реИред рд╕реНрд╡рд┐рдлреНрдЯ 4 рд╕реЗ рд╢реБрд░реВ рд╣реЛрдХрд░, JSON рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдиреЗ рдХрд╛ рдореБрдЦреНрдп рдЙрдкрдХрд░рдг рдПрдХ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ Decodable
рдФрд░ рдПрдХ рдСрдмреНрдЬреЗрдХреНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реИ JSONDecoder
ред
рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдиреЗ рдбреЗрдЯрд╛ рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдмрд╣реБрдд рд╕рд░рд▓ рдХрд░ рджрд┐рдпрд╛ рдФрд░ рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХреЛрдб рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреЛ рдХрдо рдХрд░ рджрд┐рдпрд╛ред рдЬреНрдпрд╛рджрд╛рддрд░ рдорд╛рдорд▓реЛрдВ рдореЗрдВ, рдпрд╣ рдХреЗрд╡рд▓ JSON рдСрдмреНрдЬреЗрдХреНрдЯ рдореЗрдВ рдлрд╝реАрд▓реНрдбреНрд╕ рдХреА рддрд░рд╣ рдирд╛рдорд┐рдд рдЧреБрдгреЛрдВ рдХреЗ рд╕рд╛рде рдореЙрдбрд▓ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╣реИ рдФрд░ JSONDecoder
рдЖрдкрдХреЗ рд▓рд┐рдП рдмрд╛рдХреА рдХрд╛рдо рдХрд░реЗрдЧрд╛ред рдиреНрдпреВрдирддрдо рдХреЛрдб, рдЕрдзрд┐рдХрддрдо рд▓рд╛рднред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдореЗрдВ рдПрдХ рдХрдореА рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдирд╛рдо рд╣реИ, рдмреЗрд╣рдж рдХрдо рдкрд╛рд░реНрд╕рд░ рдХреА рд╡рдлрд╛рджрд╛рд░реАред рдореЗрдВ рд╕рдордЭрд╛ рджреВрдВрдЧрд╛ред рдпрджрд┐ рдЖрдВрддрд░рд┐рдХ рдбреЗрдЯрд╛ рдореЙрдбрд▓ (Decodable рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕) рдФрд░ JSON рдореЗрдВ рдХреНрдпрд╛ рдЖрдпрд╛, рдХреЗ рдмреАрдЪ рдХреЛрдИ рд╡рд┐рд╕рдВрдЧрддрд┐ рд╣реИ,JSONDecoder
рдПрдХ рддреНрд░реБрдЯрд┐ рдлреЗрдВрдХрддрд╛ рд╣реИ рдФрд░ рд╣рдо рдкреВрд░реА рд╡рд╕реНрддреБ рдЦреЛ рджреЗрддреЗ рд╣реИрдВред рд╢рд╛рдпрдж, рдХреБрдЫ рд╕реНрдерд┐рддрд┐рдпреЛрдВ рдореЗрдВ, рд╡реНрдпрд╡рд╣рд╛рд░ рдХрд╛ рдпрд╣ рдореЙрдбрд▓ рдмреЗрд╣рддрд░ рд╣реИ, рдЦрд╛рд╕рдХрд░ рдЬрдм рдпрд╣ рдЖрддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╡рд┐рддреНрддреАрдп рд▓реЗрдирджреЗрди рдХреЗ рд▓рд┐рдПред рд▓реЗрдХрд┐рди рдХрдИ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рдкрд╛рд░реНрд╕рд┐рдВрдЧ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдЕрдзрд┐рдХ рд╡рдлрд╛рджрд╛рд░ рдмрдирд╛рдирд╛ рдЙрдкрдпреЛрдЧреА рд╣реЛрдЧрд╛ред рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ рдЕрдкрдиреЗ рдЕрдиреБрднрд╡ рдХреЛ рд╕рд╛рдЭрд╛ рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ рдФрд░ рдЗрд╕ рд╕рдорд╛рди рдирд┐рд╖реНрдард╛ рдХреЛ рдмрдврд╝рд╛рдиреЗ рдХреЗ рдореБрдЦреНрдп рддрд░реАрдХреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реВрдВрдЧрд╛ред
рдЕрдорд╛рдиреНрдп рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рдХрд░рдирд╛
рдареАрдХ рд╣реИ, рдкрд╣рд▓реА рд╡рд╕реНрддреБ, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рдЕрдорд╛рдиреНрдп рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдЫрд╛рди рд░рд╣реА рд╣реИред рдХрдИ рд╕реНрдерд┐рддрд┐рдпреЛрдВ рдореЗрдВ, рдпрджрд┐ рдХреЛрдИ рдиреЗрд╕реНрдЯреЗрдб рдСрдмреНрдЬреЗрдХреНрдЯ рдорд╛рдиреНрдп рдирд╣реАрдВ рд╣реИ, рддреЛ рд╣рдо рд╕рдВрдкреВрд░реНрдг рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рдЦреЛрдирд╛ рдирд╣реАрдВ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдпрд╣ рдПрдХрд▓ рдСрдмреНрдЬреЗрдХреНрдЯ рдФрд░ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреА рд╕рд░рдгрд┐рдпреЛрдВ рджреЛрдиреЛрдВ рдкрд░ рд▓рд╛рдЧреВ рд╣реЛрддрд╛ рд╣реИред рдореИрдВ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджреВрдВрдЧрд╛ред рдорд╛рди рд▓реАрдЬрд┐рдП рдХрд┐ рд╣рдо рд╕рд╛рдорд╛рди рдмреЗрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЖрд╡реЗрджрди рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдПрдХ рд╕реНрдХреНрд░реАрди рдкрд░ рд╣рдореЗрдВ рд▓рдЧрднрдЧ рдЗрд╕ рд░реВрдк рдореЗрдВ рд╕рд╛рдорд╛рдиреЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА рдорд┐рд▓рддреА рд╣реИред
{
"products": [
{...},
{...},
....
]
}
, . . , , - . , JSONDecoder
тАЬ тАЭ .
, :
{
"id": 1,
"title": "Awesome product",
"price": 12.2,
"image": {
"id": 1,
"url": "http://image.png",
"thumbnail_url": "http://thumbnail.png"
}
}
, , image
. , , image , image = nil
. , , JSONDecoder
.
, JSONDecoder
2 : decode
decodeIfPresent
. optional
, nil
, , null
. .
, , . , init(decoder)
try?
nil
. , boilerplate . .
struct FailableDecodable<Value: Decodable>: Decodable {
var wrappedValue: Value?
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = try? container.decode(Value.self)
}
}
struct FailableDecodableArray<Value: Decodable>: Decodable {
var wrappedValue: [Value]
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var elements: [Value] = []
while !container.isAtEnd {
if let element = try? container.decode(Value.self) {
elements.append(element)
}
}
wrappedValue = elements
}
}
.
struct ProductList: Decodable {
var products:FailableDecodableArray<Product>
}
struct Product: Decodable {
let id: Int
let title: String
let price: Double
let image: FailableDecodable<Image>?
}
struct Image: Decodable {
let id: Int
let url: String
let thumbnailUrl: String
}
. , .
let products = productsList.products.wrappedValue
let image = products.first?.image.wrappedValue
FailableDecodableArray
. , RandomAccessCollection
MutableCollection
, wrappedValue
. FailableDecodable
. , , computed property
, , . , .
@propertyWrapper
Swift 5.1 тАФ @propertyWrapper
.
@propertyWrapper
struct FailableDecodable<Value: Decodable>: Decodable {
...
}
@propertyWrapper
struct FailableDecodableArray<Value: Decodable>: Decodable {
...
}
struct ProductList: Decodable {
@FailableDecodableArray
var products:[Product]
}
struct Product: Decodable {
let id: Int
let title: String
let price: Double
@FailableDecodable
let image:Image?
}
, wrappedValue
. , , , , :)
, , optional
.
@FailableDecodable
let image:Image?
let image: FailableDecodable<Image>
, optional
Image?
, wrappedValue
optional
, .
Swift
@FailableDecodable?
let image:Image?
, , JSON nil
. @propertyWrapper
, 100% JSON.
@dynamicMemberLookup
dynamicMemberLookup
.
@dynamicMemberLookup
struct FailableDecodable<Value: Decodable>: Decodable {
var wrappedValue: Value?
subscript<Prop>(dynamicMember kp: KeyPath<Value, Prop>) -> Prop {
wrappedValue[keyPath: kp]
}
subscript<Prop>(dynamicMember kp: WritableKeyPath<Value, Prop>) -> Prop {
get {
wrappedValue[keyPath: kp]
}
set {
wrappedValue[keyPath: kp] = newValue
}
}
}
2 subscript
, readonly , read/write . .
struct ProductList: Decodable {
var products:FailableDecodableArray<Product>
}
struct Product: Decodable {
let id: Int
let title: String
let price: Double
let image: FailableDecodable<Image>?
}
@propertyWrapper
wrappedValue
, .
let imageURL = products.first?.image.url
, optional
. , , wrappedValue
, , .
products.first?.image.load()
products.first?.image.wrappedValue.load()
( ), .
тАФ . - , ( JSON) Swift , тАЬ1тАЭ 1 тАФ . JSONDecoder
, , . . , Product
.
{
"id": 1,
"title": "Awesome product",
"price": "12.2",
"image": {
"id": 1,
"url": "http://image.png",
"thumbnail_url": "http://thumbnail.png"
}
}
, , , тАФ , , , , , . , .
struct Convertible<Value: Decodable & LosslessStringConvertible>: Decodable {
var wrappedValue: Value
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
guard let stringValue = try? container.decode(String.self),
let value = Value(stringValue) else {
wrappedValue = try container.decode(Value.self)
return
}
wrappedValue = value
}
}
struct Product: Decodable {
let id: Int
let title: String
let price: Convertible<Double>
let image: FailableDecodable<Image>?
}
, . FailableDecdable
@propertyWrapper
@dynamicMemberLookup
.
( ) . , , API - , , - . , , , , -, , , -, , . , , .
struct StringConvertible: Decodable {
var wrappedValue: String
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
guard let number = try? container.decode(Double.self) else {
wrappedValue = try container.decode(String.self)
return
}
wrappedValue = "\(number)"
}
}
рдЙрдкрд░реЛрдХреНрдд рд╕рднреА рдХреЛ рд╕рд╛рд░рд╛рдВрд╢рд┐рдд рдХрд░рддреЗ рд╣реБрдП, рдореИрдВ рдпрд╣ рдиреЛрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ рдХрд┐ Decodable
рд╡рд░реНрдЧ рдХреЗ рд╕рд╛рде рд╕рдВрдпреЛрдЬрди рдореЗрдВ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЗ рдЙрджреНрднрд╡ рдиреЗ JSONDecoder
рд╣рдорд╛рд░реЗ рдЬреАрд╡рди рдХреЛ рд╕рд░реНрд╡рд░ рдбреЗрдЯрд╛ рдХреЗ рдкрд╛рд░реНрд╕рд┐рдВрдЧ рдХреЗ рд╕рд╛рде рдЬреБрдбрд╝рд╛ рд╣реБрдЖ рд╣реИ, рдЬреЛ рд╣рдорд╛рд░реЗ рдЬреАрд╡рди рдХреЛ рдХрд╛рдлреА рд╕рд░рд▓ рдмрдирд╛ рджрд┐рдпрд╛ рд╣реИред рд╣рд╛рд▓рд╛рдВрдХрд┐, рдпрд╣ рдзреНрдпрд╛рди рджреЗрдиреЗ рдпреЛрдЧреНрдп рд╣реИ рдХрд┐ JSONDecoder
рдЗрд╕ рд╕рдордп рдЗрд╕рдХреА рдмрд╣реБрдд рдХрдо рдирд┐рд╖реНрдард╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдмреЗрд╣рддрд░ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП (рдпрджрд┐ рдЖрд╡рд╢реНрдпрдХ рд╣реЛ) рдЖрдкрдХреЛ рдереЛрдбрд╝рд╛ рдХрд╛рдо рдХрд░рдиреЗ рдФрд░ рдХреБрдЫ рд░реИрдкрд░ рд▓рд┐рдЦрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдЗрди рд╕рднреА рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рдХреЛ рд╡рд╕реНрддреБ рдореЗрдВ рд╣реА рдорд╣рд╕реВрд╕ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ JSONDecoder
, рдХреНрдпреЛрдВрдХрд┐ рдЕрдкреЗрдХреНрд╖рд╛рдХреГрдд рд╣рд╛рд▓ рд╣реА рдореЗрдВ рдЙрдиреНрд╣реЗрдВ рдпрд╣ рднреА рдирд╣реАрдВ рдкрддрд╛ рдерд╛ рдХрд┐ рдХреИрд╕реЗ snakecase рд╕реЗ рдХреИрдорд▓рдХреЗрд╕ рдФрд░ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЛ рддрд╛рд░реАрдЦ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдП, рдФрд░ рдЕрдм рдпрд╣ рд╕рдм "рдмреЙрдХреНрд╕ рд╕реЗ рдмрд╛рд╣рд░" рдЙрдкрд▓рдмреНрдз рд╣реИред