Currently, I'm trying to use Swift's codable protocol to add and read custom objects to and from Firebase's database. I've been following the instructions in these docs. However, when I try to write to the database, I'm getting an error "FIRInvalidArgumentException: Nested arrays are not supported". But aren't they supported? I saw another post on here from a couple of years ago that addressed a similar issue but I don't understand what I did wrong here.
These are the Swift struct representations that implement Codable:
struct List: Codable {
var title: String
var date: String
var description: String
var data: [SectionHeaders: Array<Item>] = [
.produce: [],
.seafood: [],
.meat: [],
.deli: [],
.bakery: [],
.pantry: [],
.dairy: [],
.frozen: [],
.other: []
]
enum CodingKeys: String, CodingKey {
case name
case date
case description
case data = "items"
}
struct Item: Codable {
var name: String?
var quantity: String?
}
enum SectionHeaders: Int, Codable {
case produce = 0, seafood, meat, deli, bakery, pantry, dairy, frozen, other, total
enum CodingKeys: String, CodingKey {
case produce = "produce"
case seafood = "seafood"
case meat = "meat"
case deli = "deli"
case bakery = "bakery"
case pantry = "pantry"
case dairy = "dairy"
case frozen = "frozen"
case other = "other"
}
}
And this is the data representation I'm aiming to get:
"title": String,
"date": String,
"description": String,
"items": [
"header1": [{
"name": String,
"quantity": String
},
{
"name": String,
"quantity": String
}
],
"header2": [{
"name": String,
"quantity": String
},
{
"name": String,
"quantity": String
}
]
]
Any help would be great! Thanks in advance!
Related
I have two different arrays which am trying to map them to one object, using the information in the first array ModelOne id. I use two for loops to check if the id in model one appears in the second array if true create an object with model one id, name and array of all names in model two. From my implementation am not able to get the correct results.
// Model One
struct ModelOne: Codable {
let id: Int
let name: String
}
// Model two
struct ModelTwo: Codable {
let id: Int
let modelOneId: Int
let name: String
}
var arrayOne = [ModelOne]()
arrayOne.append(ModelOne(id: 1, name: "One"))
arrayOne.append(ModelOne(id: 2, name: "Two"))
var arrayTwo = [ModelTwo]()
arrayTwo.append(ModelTwo(id: 1, modelOneId: 1, name: "Some name"))
arrayTwo.append(ModelTwo(id: 2, modelOneId: 1, name: "Other name"))
arrayTwo.append(ModelTwo(id: 1, modelOneId: 2, name: "Name one"))
arrayTwo.append(ModelTwo(id: 2, modelOneId: 2, name: "Name two"))
struct MappedModel {
let id: Int
let name: String
let items: [String]
}
var arrayThree = [MappedModel]()
for i in arrayOne {
for x in arrayTwo {
if i.id == x.id {
arrayThree.append(MappedModel(id: i.id, name: i.name, items: [x.name]))
}
}
}
If I'm interpreting the issue correctly, you want the MappedModel to have the id and name from ModelOne, with items containing all of the names from the ModelTwo where modelOneId matches the ModelOne id.
If so, this would do the trick:
var combined = arrayOne.map { item1 in
MappedModel(id: item1.id, name: item1.name, items: arrayTwo.compactMap { $0.id == item1.id ? $0.name : nil})
}
Which yields:
[
MappedModel(id: 1, name: "One", items: ["Some name", "Other name"]),
MappedModel(id: 2, name: "Two", items: ["Name one", "Name two"])
]
I am building an app that lets users search for photos. I am in the process of creating a data model using a codable. Below is the JSON data. The data model will contain id, created_at, username, name, portfolio_url, and images. How do I access the nested user and image information in the user dictionary and URL images.
{
"id": "LBI7cgq3pbM",
"created_at": "2016-05-03T11:00:28-04:00",
"updated_at": "2016-07-10T11:00:01-05:00",
"width": 5245,
"height": 3497,
"color": "#60544D",
"blur_hash": "LoC%a7IoIVxZ_NM|M{s:%hRjWAo0",
"likes": 12,
"liked_by_user": false,
"description": "A man drinking a coffee.",
"user": {
"id": "pXhwzz1JtQU",
"username": "poorkane",
"name": "Gilbert Kane",
"portfolio_url": "https://theylooklikeeggsorsomething.com/",
"bio": "XO",
"location": "Way out there",
"total_likes": 5,
"total_photos": 74,
"total_collections": 52,
"instagram_username": "instantgrammer",
"twitter_username": "crew",
"
},
"urls": {
"raw": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f",
"full": "https://hd.unsplash.com/photo-1416339306562-f3d12fefd36f",
"regular": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=92f3e02f63678acc8416d044e189f515",
"small": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&s=263af33585f9d32af39d165b000845eb",
"thumb": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&s=8aae34cf35df31a592f0bef16e6342ef"
},
When using Codable you need to follow the hierarchy of the model.
Use three structs or classes as:
struct URLs: Codable {
var raw: String
var full: String
var regular: String
var small: String
var thumb: String
}
struct User: Codable {
var id: String
var name: String
var portfolio_url: String
var bio: String
}
struct DataModel: Codable {
var id: String
var created_at: String
var likes: Int
var liked_by_user: Bool
var description: String
var urls: URLs
var user: User
}
When decoding, just user
jsonDecoder.decode(DataModel.self, from: data)
I have JSON that looks like:
[
{
WWW: "2",
XXX: "2",
YYY: "3",
ZZZ: "4"
},
{
WWW: "2",
XXX: "5",
YYY: "6",
ZZZ: "7"
},
{
WWW: "2",
XXX: "2",
YYY: "2",
ZZZ: "3"
}
]
But I'm only interested in working with and Y and Z.
Howe can I remove the entire W and X columns either from the raw JSON or from the JSON in the form of an Array in SWIFT?
Only answers I can find such as here seem outdated. Thanks for suggestions.
Use:
dict["WWW"] = nil
dict["XXX"] = nil
or:
dict.removeValue(forKey: "WWW")
dict.removeValue(forKey: "XXX")
Better Approach would be using JSONEncoder and CodingKeys.
struct Model: Decodable {
var yyy, zzz: String
enum CodingKeys: String, CodingKey {
case yyy = "YYY", zzz = "ZZZ"
}
}
Define your struct to match what you want, and then decode it:
struct Value: Decodable {
enum CodingKeys: String, CodingKey {
case y = "YYY"
case z = "ZZZ"
}
var y: String
var z: String
}
let value = try JSONDecoder().decode([Value].self, from: json)
This will exclude any keys you don't include.
In my case I am trying to get JSON data using codable. Here, I can’t able to get data[Datum] values. How to get it and assign tableview data. I used to generate codable by using quicktype.io
JSON Data
{
"status": true,
"data": [
{
"id": "1",
"name": "one",
"description": "hello",
"date": "2020-08-05 11:37:52",
"startdate": "2019-08-05 11:37:52",
"createdby": "1",
"status": "0"
},
{
"id": "2",
"name": "two",
"description": "hi",
"date": "2020-08-05 11:37:52",
"startdate": "2019-08-05 11:37:52",
"createdby": "1",
"status": "0"
}
]
}
Codable Struct
// MARK: - Welcome
struct Welcome: Codable {
let status: Bool
let data: [Datum]
}
// MARK: - Datum
struct Datum: Codable {
let id, name, datumDescription, date: String
let startdate, createdby, status: String
enum CodingKeys: String, CodingKey {
case id, name
case datumDescription = "description"
case date, startdate, createdby, status
}
}
Code
let id: String = result.data.(Nothing Showing except Description)
let id: String = result.data.(Nothing Showing except Description)
It's not clear what "Nothing Showing except Description" means here, but the syntax you would expect would be:
let id: String = result.data[0].id // The id of the first Datum
data is an Array, so you'll need to subscript or iterate over it to get elements. For a tableview, you'd expect something like:
let id: String = result.data[indexPath.row].id
If this is not what you mean, you need to edit your question to make clear what your actual code is and the specific error message you receive.
This is my api response:
[
[
{
"id": 24,
"request_id": "rqst5c130cae6f7609.41056231",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
"details": "load",
"load_description": "load",
"amount_offered": "1",
"pickup_address": "load",
"dropoff_address": "load",
"timestamp": "2018-12-14 01:51:42"
}
],
[
{
"id": 27,
"request_id": "rqst5c1325881836d2.98441728",
"business_name": "Code Viable",
"business_email": "code#viable.com",
"title": "Load",
"details": "brendan",
"load_description": "test load for brendan",
"amount_offered": "1222",
"pickup_address": "Load",
"dropoff_address": "Load",
"timestamp": "2018-12-14 03:37:44"
}
]
]
As you can see, it is an array wrapped inside of an array, I have an object mapper setup already for the inner array, like this:
struct JobResponseDataObject: Mappable {
init?(map: Map) {
}
var id: Int?
var requestId: String?
var businessName: String?
var businessEmail: String?
var title: String?
var details: String?
var loadDescription: String?
var amountOffered: String?
var pickUpAddress: String?
var dropOffAddress: String?
var timestamp: String?
mutating func mapping(map: Map) {
id <- map["id"]
requestId <- map["request_id"]
businessName <- map["business_name"]
businessEmail <- map["business_email"]
title <- map["title"]
details <- map["details"]
loadDescription <- map["load_description"]
amountOffered <- map["amount_offered"]
pickUpAddress <- map["pickup_address"]
dropOffAddress <- map["dropoff_address"]
timestamp <- map["timestamp"]
}
}
If the parent has a name for it's child array, then I would create another mapper for the top level. But in this case, there is not a name for the outer array, what do I need to do to make the alamofire .responseArray call work?
Alamofire.request(JOB_REQUEST_BASE_URL, method: .post, parameters: parameter, encoding: URLEncoding(), headers: nil).responseArray { (response: DataResponse<[JobResponseDataObject]>) in
}
}
Thanks
Your JSON object have nested Array. So to parse that you can do it two ways. One way to do this is to parse it as nested array.
DataResponse<[[JobResponseDataObject]]>
And your code will looks like this.
Alamofire.request(JOB_REQUEST_BASE_URL, method: .post, parameters: parameter, encoding: URLEncoding(), headers: nil).responseArray { (response: DataResponse<[JobResponseDataObject]>) in
//...
}
Suggestion: So far what I can understand from JSON, it should not be in nested Array. If you only need to add one object in nested array, you can achieve same with simple array.
Second Way
If you only have one object in nested array. You can also parse it as following.
requestId <- map["0.request_id"]
"0." is here to get first object of inner array.
Hope this helps :)