I try to decode json from api, for that i use this website : https://app.quicktype.io/
this is my json from api :
{
"id":"7d37091c-f214-491a-a50c-d289f0f7e255",
"uuid":"7d37091c-f214-491a-a50c-d289f0f7e255",
"brand":"Nike",
"breadcrumbs":[
],
"browseVerticals":[
"sneakers"
],
"category":"Nike Dunk",
"charityCondition":0,
"childId":null,
"colorway":"White/Black-Total Orange",
"condition":"New",
"countryOfManufacture":"VN",
"dataType":"product",
"description":"",
"hidden":false,
"listingType":"standard",
"minimumBid":25,
"gender":"men",
"doppelgangers":[
],
"media":{
"imageUrl":"https://images.stockx.com/images/Nike-Dunk-Low-Black-White-2021.jpg?fit=fill&bg=FFFFFF&w=700&h=500&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1624941957",
"smallImageUrl":"https://images.stockx.com/images/Nike-Dunk-Low-Black-White-2021.jpg?fit=fill&bg=FFFFFF&w=300&h=214&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1624941957",
"thumbUrl":"https://images.stockx.com/images/Nike-Dunk-Low-Black-White-2021.jpg?fit=fill&bg=FFFFFF&w=140&h=100&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1624941957",
"gallery":[
],
"hidden":false
},
"name":"Black White (2021)",
"productCategory":"sneakers",
"releaseDate":"",
"releaseTime":0,
"belowRetail":false,
"retailPrice":110,
"shoe":"Nike Dunk High",
"shortDescription":"Nike-Dunk-Low-Black-White-2021",
"styleId":"DD1399-105",
"tickerSymbol":"NK-NIDLFDXWB",
"title":"Nike Dunk High Black White (2021)",
"traits":[
{
"name":"Style",
"value":"DD1399-105",
"filterable":true,
"visible":true,
"highlight":true
},
{
"name":"Colorway",
"value":"White/Black-Total Orange",
"filterable":true,
"visible":true,
"highlight":true
},
{
"name":"Retail Price",
"value":110,
"filterable":true,
"visible":true,
"highlight":false
},
{
"name":"Featured",
"value":false,
"filterable":false,
"visible":false,
"highlight":false
}
],
"type":0,
"urlKey":"nike-dunk-high-black-white-total-orange-2021",
"year":0,
"shoeSize":null,
"market":{
"productId":0,
"skuUuid":"",
"productUuid":"7d37091c-f214-491a-a50c-d289f0f7e255",
"lowestAsk":173,
"lowestAskSize":"15",
"parentLowestAsk":0,
"numberOfAsks":196,
"hasAsks":1,
"salesThisPeriod":69,
"salesLastPeriod":0,
"highestBid":220,
"highestBidSize":"9.5",
"numberOfBids":230,
"hasBids":1,
"annualHigh":297,
"annualLow":67,
"deadstockRangeLow":238,
"deadstockRangeHigh":274,
"volatility":0.070391,
"deadstockSold":5891,
"pricePremium":1.627,
"averageDeadstockPrice":175,
"lastSale":256,
"lastSaleSize":"10.5",
"salesLast72Hours":69,
"changeValue":51,
"changePercentage":0.249544,
"absChangePercentage":0.249544,
"totalDollars":1029494,
"lastLowestAskTime":1639141012,
"lastHighestBidTime":1639160362,
"lastSaleDate":"2021-12-11T00:55:16+00:00",
"createdAt":"2021-06-29T04:46:03+00:00",
"updatedAt":1639184839,
"deadstockSoldRank":37,
"pricePremiumRank":1,
"averageDeadstockPriceRank":42,
"featured":0
},
"_tags":[
"sneakers",
"nike",
"nike",
"dunk",
"style_id|dd1399-105",
"retail_price|110",
"colorway|white/black-total orange"
],
"lock_selling":false,
"selling_countries":[
"AD",
"AE",
],
"buying_countries":[
"AD",
"AE",
"ES",
"US",
"XK",
"IC",
"IN"
],
"objectID":"7d37091c-f214-491a-a50c-d289f0f7e255"
}
this is my Preview Provider :
class DeveloperPreview {
static let instance = DeveloperPreview()
init() { }
let sneaker = Sneakers(
id:"7d37091c-f214-491a-a50c-d289f0f7e255",
uuid:"7d37091c-f214-491a-a50c-d289f0f7e255",
brand:"Nike",
breadcrumbs:[
],
browseVerticals:[
"sneakers"
],
category:"Nike Dunk",
charityCondition:0,
childID: JSONNull(),
colorway:"White/Black-Total Orange",
condition:"New",
countryOfManufacture:"VN",
dataType:"product",
welcomeDescription: "welcom",
hidden:false,
listingType:"standard",
minimumBid:25,
gender:"men",
doppelgangers:[
],
media: Media(
imageURL:"https://images.stockx.com/images/Nike-Dunk-Low-Black-White-2021.jpg?fit=fill&bg=FFFFFF&w=700&h=500&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1624941957",
smallImageURL:"https://images.stockx.com/images/Nike-Dunk-Low-Black-White-2021.jpg?fit=fill&bg=FFFFFF&w=300&h=214&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1624941957",
thumbURL:"https://images.stockx.com/images/Nike-Dunk-Low-Black-White-2021.jpg?fit=fill&bg=FFFFFF&w=140&h=100&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1624941957",
gallery:[
],
hidden:false
),
name:"Black White (2021)",
productCategory:"sneakers",
releaseDate:"",
releaseTime:0,
belowRetail:false,
retailPrice:110,
shoe:"Nike Dunk High",
shortDescription:"Nike-Dunk-Low-Black-White-2021",
styleID:"DD1399-105",
tickerSymbol:"NK-NIDLFDXWB",
title:"Nike Dunk High Black White (2021)",
traits: [traits],
type:0,
urlKey:"nike-dunk-high-black-white-total-orange-2021",
year:0,
shoeSize: JSONNull(),
market: Market(
productID:0,
skuUUID:"",
productUUID:"7d37091c-f214-491a-a50c-d289f0f7e255",
lowestAsk:173,
lowestAskSize:"15",
parentLowestAsk:0,
numberOfAsks:196,
hasAsks:1,
salesThisPeriod:69,
salesLastPeriod:0,
highestBid:220,
highestBidSize:"9.5",
numberOfBids:230,
hasBids:1,
annualHigh:297,
annualLow:67,
deadstockRangeLow:238,
deadstockRangeHigh:274,
volatility:0.070391,
deadstockSold:5891,
pricePremium:1.627,
averageDeadstockPrice:175,
lastSale:256,
lastSaleSize:"10.5",
salesLast72Hours:69,
changeValue:51,
changePercentage:0.249544,
absChangePercentage:0.249544,
totalDollars:1029494,
lastLowestAskTime:1639141012,
lastHighestBidTime:1639160362,
lastSaleDate:"2021-12-11T00:55:16+00:00",
createdAt:"2021-06-29T04:46:03+00:00",
updatedAt:1639184839,
deadstockSoldRank:37,
pricePremiumRank:1,
averageDeadstockPriceRank:42,
featured:0
),
tags:[
"sneakers",
"nike",
"nike",
"dunk",
"style_id|dd1399-105",
"retail_price|110",
"colorway|white/black-total orange"
],
lockSelling:false,
sellingCountries:[
"AD",
"AE",
"US",
"IN"
],
buyingCountries:[
"AD",
"AE",
"IN"
],
objectID:"7d37091c-f214-491a-a50c-d289f0f7e255"
)
let traits = Trait(
name:"Style",
value: .bool(true),
filterable:true,
visible:true,
highlight:true
)
and this is my decoder :
struct Trait: Codable {
let name: String
let value: Value
let filterable, visible, highlight: Bool
}
enum Value: Codable {
case bool(Bool)
case integer(Int)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Bool.self) {
self = .bool(x)
return
}
if let x = try? container.decode(Int.self) {
self = .integer(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Value.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Value"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .bool(let x):
try container.encode(x)
case .integer(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
My error come from the preview provider :
"Cannot use instance member 'traits' within property initializer; property initializers run before 'self' is available"
enter image description here
The error is exactly pointed out by compiler.
Converting from API response, this should work as expected on error snippet.
traits: [
Trait(
name:"Style",
value: .string("DD1399-105"),
filterable:true,
visible:true,
highlight:true
),
Trait(
name:"Colorway",
value: .string("White/Black-Total Orange"),
filterable:true,
visible:true,
highlight:true
),
Trait(
name:"Retail Price",
value: .integer(110),
filterable:true,
visible:true,
highlight:false
),
Trait(
name:"Featured",
value: .bool(false),
filterable:false,
visible:false,
highlight:false
)
],
You cannot initialize properties which depend on each other on the top level of a class, you have to create the trait inside let sneaker = Sneakers( ...
Replace
traits: [traits],
with
traits: [Trait(
name:"Style",
value: .bool(true),
filterable:true,
visible:true,
highlight:true)],
The following two forms of data were successfully requested.
{
"ride_fare": 1000,
"km": 7
]
}
{
"ride_fare": 1000,
"km": 7,
"options": [ 0, 1, 2]
}
However, I don't know how to request a two-dimensional associative array like the one below.
How can I request it?
{
"ride_fare": 1000,
"km": 7,
"option_fares": [
{
"price": 200,
"name": "立ち寄り",
"id": 1
}
]
}
The code that I wrote:
var options = [Any]()
for option in optionFares {
let params = [
"id" : option.id ?? 0,
"name" : option.name ?? "",
"price" : option.price ?? 0
] as [String : Any]
options.append(params)
}
let faresData = [
"id" : driverOrder.id ?? 0,
"km" : driverOrder.distance ?? 0,
"option_fares" : options,
"ride_fare" : driverOrder.ride_fare ?? 0
] as [String : Any]
First, create a struct that matches the json format you want to request.
struct Params: Codable {
let rideFare, km: Int
let optionFares: [OptionFare]
enum CodingKeys: String, CodingKey {
case rideFare = "ride_fare"
case km
case optionFares = "option_fares"
}
}
struct OptionFare: Codable {
let price: Int
let name: String
let id: Int
}
And you must create a request parameter in Moya's task.
import Moya
extension APITarget: TargetType {
var task: Task {
case .yourCaseName(let price, let name, let id, let rideFare, let km):
let encoder: JSONEncoder = JSONEncoder()
let optionFareData: [OptionFare] = []
optionFareData.append(OptionFare(price, name, id))
let paramsData = Params(rideFare, km, optionFareData)
let jsonData: Data = try! encoder.encode(paramsData)
return .requestData(jsonData)
}
}
}
I’m stuck with some problem. I have a JSON response from server:
{
"days" : [
{
"id" : 1,
"name" : "Day 1 - first day",
"url": "http://example.com/days/1"
},
{
"id" : 2,
"name" : "Day 2 - second day",
"url": "http://example.com/days/2"
},
...
],
"week" : [
{
"id" : 1,
"dayIds" : [1, 2, 6, 9, 23, 44, 2345],
"name" : "Rest week"
},
{
"id" : 35,
"dayIds" : [34,77,23,67,126,224],
"name" : "Educational week"
},
],
"plan" : {
"weekIds: [1, 6, 23, 74]
}
}
My data models (without mapping):
class Day: Object {
#objc dynamic var id: Int = -1
#objc dynamic var name: String = ""
#objc dynamic var url: String = ""
}
class Week: Object {
var dayIds = List<String>()
#objc dynamic var name: String = ""
#objc dynamic var id: Int = -1
var days: List<Week>? = nil
}
class Plan: Object {
var weekDays = List<String>()
var weeks: List<Week>? = nil
}
Mapping code:
let json: [String: Any] = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
let plans: [Plan] = Mapper<Plan>().mapArray(JSONArray: json["plans"] as! [[String: Any]])
let days: [Day] = Mapper<Day>().mapArray(JSONArray: json["days"] as! [[String: Any]])
let weeks: [Week] = Mapper<Week>().mapArray(JSONArray: json["weeks"] as! [[String: Any]])
So, I need to tell realm that an array weeks belong to plan.weeks and an array days belong to object week.days and related by theirs id’s. How can I do this more simply? Do you have any ideas?
The alternative solution is in-head brute force like this.
for week in weeks {
for dayId in week.dayIds {
for day in days {
if day.id == dayId {
week.days.append(day)
}
}
}
}
for plan in plans {
for week in weeks {
for weekId in plans.weekIds {
if weekId == week.id {
plan.weeks.append(week)
}
}
}
}
I believe that somewhere exist more pure and simple solution :)
Thanks.
Your data structures seem very nested, so you're going to have to do the internal looping. If you want something more swifty, use map and filter here instead of for loops:
let days = weeks.map({
$0.dayIds.map({
$0.filter({
$0.id == dayId
})
})
})
My question is simple, I want to know how to do a deep merge of 2 Swift dictionaries (not NSDictionary).
let dict1 = [
"a": 1,
"b": 2,
"c": [
"d": 3
],
"f": 2
]
let dict2 = [
"b": 4,
"c": [
"e": 5
],
"f": ["g": 6]
]
let dict3 = dict1.merge(dict2)
/* Expected:
dict3 = [
"a": 1,
"b": 4,
"c": [
"d": 3,
"e": 5
],
"f": ["g": 6]
]
*/
When dict1 and dict2 have the same key, I expect the value to be replaced, but if that value is another dictionary, I expect it to be merged recursively.
Here is the solution I'd like:
protocol Mergeable {
mutating func merge(obj: Self)
}
extension Dictionary: Mergeable {
// if they have the same key, the new value is taken
mutating func merge(dictionary: Dictionary) {
for (key, value) in dictionary {
let oldValue = self[key]
if oldValue is Mergeable && value is Mergeable {
var oldValue = oldValue as! Mergeable
let newValue = value as! Mergeable
oldValue.merge(newValue)
self[key] = oldValue
} else {
self[key] = value
}
}
}
}
but it gives me the error Protocol 'Mergeable' can only be used as a generic constraint because it has Self or associated type requirements
EDIT:
My question is different from Swift: how to combine two Dictionary instances? because that one is not a deep merge.
With that solution, it would produce:
dict3 = [
"a": 1,
"b": 4,
"c": [
"e": 5
]
]
In my view the question is incoherent. This is an answer, however:
func deepMerge(d1:[String:AnyObject], _ d2:[String:AnyObject]) -> [String:AnyObject] {
var result = [String:AnyObject]()
for (k1,v1) in d1 {
result[k1] = v1
}
for (k2,v2) in d2 {
if v2 is [String:AnyObject], let v1 = result[k2] where v1 is [String:AnyObject] {
result[k2] = deepMerge(v1 as! [String:AnyObject],v2 as! [String:AnyObject])
} else {
result[k2] = v2
}
}
return result
}
Here is your test case:
let dict1:[String:AnyObject] = [
"a": 1,
"b": 2,
"c": [
"d": 3
]
]
let dict2:[String:AnyObject] = [
"b": 4,
"c": [
"e": 5
]
]
let result = deepMerge(dict1, dict2)
NSLog("%#", result)
/*
{
a = 1;
b = 4;
c = {
d = 3;
e = 5;
};
}
*/
3rd-party edit: Alternate version using variable binding and newer Swift syntax.
func deepMerge(_ d1: [String: Any], _ d2: [String: Any]) -> [String: Any] {
var result = d1
for (k2, v2) in d2 {
if let v1 = result[k2] as? [String: Any], let v2 = v2 as? [String: Any] {
result[k2] = deepMerge(v1, v2)
} else {
result[k2] = v2
}
}
return result
}
How about manually doing it.
func += <KeyType, ValueType> (inout left: Dictionary<KeyType, ValueType>, right: Dictionary<KeyType, ValueType>) {
for (k, v) in right {
left.updateValue(v, forKey: k)
}
}
You can also try ExSwift Library
extension Dictionary {
mutating func deepMerge(_ dict: Dictionary) {
merge(dict) { (current, new) in
if var currentDict = current as? Dictionary, let newDict = new as? Dictionary {
currentDict.deepMerge(newDict)
return currentDict as! Value
}
return new
}
}
}
How to use/test:
var dict1: [String: Any] = [
"a": 1,
"b": 2,
"c": [
"d": 3
],
"f": 2
]
var dict2: [String: Any] = [
"b": 4,
"c": [
"e": 5
],
"f": ["g": 6]
]
dict1.deepMerge(dict2)
print(dict1)