Getting Error while Using JSONDecodable in swift4? - ios

I'm getting the following error while using JSONDecodable in swift4. "Type UserRegister does not confirm to protocol 'Decodable' "
My Model Structure looks like this.
struct UserRegister: Decodable {
let id : NSInteger
let name : String?
let email : String?
let cities : Array<Any>?
let tax : [tax]
let username : [username]
}
struct tax : Decodable {
let deviceId : NSInteger?
let financialYear : String?
let country : String?
let name : String?
let state : String?
let taxCode : String?
let value : NSInteger?
}
struct username : Decodable {
let email : String?
let phone : String?
}

The problem is the Array<Any>. You need an array of some Decodable type, such as String or Int.

Related

Merge or Append two Data models same structure to post values in Swift

Having the same structure of data models I want to merge data so I can post values to server on button click.
Unit Model : I pass this model to AcceptanceViewcontroller in var UnitData for entering final
remarks.
Acceptance Model: Only for Final remarks (comments) and
few values.
How to merge both data into one swift.
var AppData : Acceptance?
var UnitData: Unit?
let decoder = JSONDecoder()
let response = try decoder.decode(Acceptance.self, from: result! as! Data)
self.AppData = response
self.tableView.reloadData()
Data Models:
struct Unit : Codable {
var sectionList : [SectionList]?
}
struct Acceptance : Codable {
var sectionList : [SectionList]?
}
struct SectionList : Codable {
let title : String?
var items : [Item]?
}
struct Item : Codable {
let actionType : Int?
let actionUrl : String?
let bgColor : String?
let booleanValue : Bool?
var textField : String?
var textValue : String?
let unitId : Int?
let latitude : Double?
let longitude : Double?
let actionParamData: String?
let actionTitle: String?
let pickList: [SectionList]?
let multiSelect: Bool?
let selectedValue: [String]?
let version: Int?
let masterId: Int?
let itemValue: String?
}
Please, remember to name your variables starting with lower cases. AppData and UnitData should be renamed to appData and unitData respectively.
Anyway, the solution provided by #AlexandrKolesnik in the comments works. You should do:
unitData?.sectionList?.append(contentsOf: appData?.sectionList ?? [])
If you don't want unitData to be modified by adding appData to it, just make another array and append unitData and then appData.
var allSectionList: [SectionList] = []
allSectionList.append(contentsOf: unit?.sectionList ?? [])
allSectionList.append(contentsOf: acceptance?.sectionList ?? [])
To add the behavior described in the comments of this answer, I'd add a boolean variable to SectionList that indicates if it has been modified. And then whatever you want to merge both lists, only append those SectionList that have said variable as true. For example:
struct SectionList : Codable {
let title : String?
var items : [Item]?
var modified = false
}
If your user modifies that SectionList, then set the modified variable as true. In the moment of merging the data:
var allSectionList: [SectionList] = []
allSectionList.append(contentsOf: unit?.sectionList ?? [])
acceptance?.sectionList?.forEach { section in
if section.modified {
allSectionList.append(section)
}
}
You might need to set the modified variable to false once you've added it to the merged list, but that depends on you and if you need it.

Is it possible to declare generics inside Decodable model

Here is my model class
struct ErrorData: Decodable {
let code : Int
let message : String
let data : [ErrorDataFields]
}
i want to have ErrorDataFields to be array and object like
struct ErrorData: Decodable {
let code : Int
let message : String
let data : [ErrorDataFields]
}
AND
struct ErrorData: Decodable {
let code : Int
let message : String
let data : ErrorDataFields
}
We can use a generic type with Decodable. Only thing is the generic type should also conform to Decodable.
struct ErrorData <T : Decodable> : Decodable {
let code : Int
let message : String
let data : T
}

Parse local json file to object

I have this code:
struct ProductObject : Codable {
let palletHeight : Double?
let layerPallet : Int?
let prepCombisteamer : String?
let id : Int?
let avikoWorlds : [String]?
let avikoSegments : [String]?
let sunFlower : Bool?
let inPieces : Bool?
let noBox : Int?
let prepFryingPan : String?
let packageContents : Double?
let carbohydrates : Int?
let fat : Double?
let eanBox : Int?
let weightYieldPercent : Int?
let kcal : Int?
let markedAsFavourite1 : Bool?
let avikoPodSegmentyRynku : [String]?
let prepPot : String?
let prepMicrowave : String?
let name : String?
let code : Int?
let prepDeepFryer : String?
let avikoConcepts : [String]?
let boxLayer : Int?
let avikoSegmentyRynku : [String]?
let active : Bool?
let shelfLifeTimeFrame : String?
let markedAsFavourite2 : Bool?
let palletWeight : Double?
let changeTime : ChangeTime?
let kj : Int?
let langVersions : [LangVersions]?
let proteins : Double?
let markedAsFavourite3 : Bool?
let containsGluten : Bool?
let regions : [Int]?
let eanFoil : Int?
let shelfLife : Int?
let contentPerBox : Int?
let prepOven : String?
}
func downloadImagesFromJsonProductFile(fileName: URL){
let filePath = fileName.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
do {
let data = try Data(contentsOf: fileName)
let jsonData = try JSONDecoder().decode(ProductObject.self, from: data)
} catch let error {
self.errorLoginMessage(txt: "MainView - Error 109: Problem with parse file \(error)", title: "Blad".localized())
}
}
}
downloadImagesFromJsonProductFile(fileName: documentsDir.appendingPathComponent((AppGlobalManager.sharedManager.loggedUser?.selectedLanguage)! + "/json/products.json"))
My local json file: https://files.fm/u/73n845ty
When I run function: downloadImagesFromJsonProductFile I have error
(when app was started): Problem with parse file typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(coding-path:[], debugDescription: "Expected the decode Dictionary but found an array instead.", underlyingError: will).
How to fix it?
As I review your JSON here http://json.parser.online.fr/, there is array as root object, therefore you should do
let json = try JSONDecoder().decode([ProductObject].self, from: data)
UPDATE
Please create JSON struct Codable from here json4swift, please resolve type mismatch errors first otherwise your JSON could not be parsed and you will get type Mismatch errors.
You can get product code by following,
let jsonData = try JSONDecoder().decode([ProductObject].self, from: data)
for detail in jsonData {
print(detail.code ?? "")
}

I am getting this error : "Expected to decode Dictionary<String, Any> but found an array instead." [duplicate]

I'm trying to use JSONDecoder to convert a JSON to Structs in Swift, so I wrote all the Structs, revised them for hours, and it still gives me this error. I don't know if there is a way to see the line that gives this.
I'll post my struct below and the Json File links right after.
The complete error description is:
typeMismatch(Swift.Dictionary<Swift.String, Any>,
Swift.DecodingError.Context(codingPath: [], debugDescription:
"Expected to decode Dictionary<String, Any> but found an array
instead.", underlyingError: nil))
// Created by Breno Ramos on 28/12/17.
// Copyright © 2017 brenor2. All rights reserved.
//
import Foundation
struct Owner:Decodable {
let login : String?
let id : Double?
let avatar_url : String?
let gravatar_id : String?
let url : String?
let html_url : String?
let followers_url : String?
let following_url : String?
let gists_url : String?
let starred_url : String?
let subscriptions_url : String?
let organizations_url : String?
let repos_url : String?
let events_url : String?
let received_events_url : String?
let type : String?
let site_admin : Bool?
}
struct License:Decodable {
let key : String?
let name : String?
let spdx_id : String?
let url : String?
}
struct Repo:Decodable {
let id : Double?
let name : String?
let full_name : String?
let owner : Owner?
let `private` : Bool?
let html_url : String?
let description : String?
let fork : Bool?
let url : String?
let forks_url : String?
let keys_url : String?
let collaborators_url : String?
let teams_url : String?
let hooks_url : String?
let issue_events_url : String?
let events_url : String?
let assignees_url : String?
let branches_url : String?
let tags_url : String?
let blobs_url : String?
let git_tags_url : String?
let git_refs_url : String?
let trees_url : String?
let statuses_url : String?
let languages_url : String?
let stargazers_url : String?
let contributors_url : String?
let subscribers_url : String?
let subscription_url : String?
let commits_url : String?
let git_commits_url : String?
let comments_url : String?
let issue_comment_url : String?
let contents_url : String?
let compare_url : String?
let merges_url : String?
let archive_url : String?
let downloads_url : String?
let issues_url : String?
let pulls_url : String?
let milestones_url : String?
let notifications_url : String?
let labels_url : String?
let releases_url : String?
let deployments_url : String?
let created_at : String?
let updated_at : String?
let pushed_at : String?
let git_url : String?
let ssh_url : String?
let clone_url : String?
let svn_url : String?
let homepage : String?
let size : Double?
let stargazers_count : Double?
let watchers_count : Double?
let language : String?
let has_issues : Bool?
let has_projects : Bool?
let has_downloads : Bool?
let has_wiki : Bool?
let has_pages : Bool?
let forks_count : Double?
let mirror_url : String?
let archived : Bool?
let open_issues_count : Double?
let license : License?
let forks : Double?
let open_issues : Double?
let topics : Topic?
let permissions : Permissions?
let watchers : Double?
let default_branch : String?
// let score : Double?
// let subscribers_count : Double?
// let network_count : Double?
// let allow_rebase_merge: Bool?
// let allow_squash_merge: Bool?
// let allow_merge_commit: Bool?
}
struct Topic:Decodable {
let topics : [String]?
}
struct Permissions:Decodable {
let admin : Bool
let push : Bool
let pull : Bool
}
struct RepoList:Decodable{
let total_count : Int?
let incomplete_results : Bool?
let items : [Repo]?
}
struct User:Decodable {
let login: String?
let id: Double?
let avatar_url: String?
let gravatar_id: String?
let url: String?
let html_url: String?
let followers_url: String?
let following_url: String?
let gists_url: String?
let starred_url: String?
let subscriptions_url: String?
let organizations_url: String?
let repos_url: String?
let events_url: String?
let received_events_url: String?
let type: String?
let site_admin: Bool?
}
struct Creator:Decodable {
let login: String?
let id: Double?
let avatar_url: String?
let gravatar_id: String?
let url: String?
let html_url: String?
let followers_url: String?
let following_url: String?
let gists_url: String?
let starred_url: String?
let subscriptions_url: String?
let organizations_url: String?
let repos_url: String?
let events_url: String?
let received_events_url: String?
let type: String?
let site_admin: Bool?
}
struct Link:Decodable {
let href :String?
}
struct _Links:Decodable {
let `self` :Link?
let html :Link?
let issue :Link?
let comments :Link?
let review_comments :Link?
let review_comment :Link?
let commits :Link?
let statuses :Link?
}
struct Base:Decodable {
let label :String?
let ref :String?
let sha :String?
let user :User?
let repo :Repo?
}
struct Head:Decodable {
let label :String?
let ref :String?
let sha :String?
let user :User?
let repo :Repo?
}
struct Milestone:Decodable {
let url:String?
let html_url:String?
let labels_url:String?
let id: Double?
let number:Double?
let title:String?
let description:String?
let creator:Creator?
let open_issues:Double?
let closed_issues:Double?
let state:String?
let created_at:String?
let updated_at:String?
let closed_at:String?
let due_on:String?
}
struct Assignee:Decodable {
let login :String?
let id :Double?
let avatar_url :String?
let gravatar_id :String?
let url :String?
let html_url :String?
let followers_url :String?
let following_url :String?
let gists_url :String?
let starred_url :String?
let subscriptions_url :String?
let organizations_url :String?
let repos_url :String?
let events_url :String?
let received_events_url :String?
let type :String?
let site_admin :Bool?
}
struct Reviewers:Decodable {
let login: String?
let id: Double?
let avatar_url: String?
let gravatar_id: String?
let url: String?
let html_url: String?
let followers_url: String?
let following_url: String?
let gists_url: String?
let starred_url: String?
let subscriptions_url: String?
let organizations_url: String?
let repos_url: String?
let events_url: String?
let received_events_url: String?
let type: String?
let site_admin: Bool?
}
struct Pull:Decodable {
let id: Double?
let url:String?
let html_url:String?
let diff_url:String?
let patch_url:String?
let issue_url:String?
let number:Double?
let state:String?
let locked:Bool?
let title:String?
let user:User?
let body:String?
let created_at:String?
let updated_at:String?
let closed_at:String?
let merged_at:String?
let merge_commit_sha: String?
let assignee: Assignee?
let assignees: [Assignee]?
let requested_reviewers: [Reviewers]?
let milestone:Milestone?
let commits_url:String?
let review_comments_url:String?
let review_comment_url:String?
let comments_url:String?
let statuses_url:String?
let head:Head?
let base:Base?
let _links:_Links?
let author_association:String?
}
struct PullList:Decodable {
let pulls:[Pull]?
}
/////////////////////////////////////////////////////////
1.This one is working fine with this structs:
2.This one is the one that gives the typeMismatch error
You're probably doing this right now:
let decoder = JSONDecoder()
let repoList = decoder.decode(RepoList.self, from: data)
which is fine for the response with a top-level Object.
To decode JSON responses that are top-level Arrays, use code like this instead:
let decoder = JSONDecoder()
let repos = decoder.decode([Repo].self, from: data)
If you have a problematic JSON, which can contain number or string for some keys, you can decode object without that property and manually set that property after decoding.
For example, I have a Vehicle class inside HistoryItem. In Vehicle model_year can be empty String or non empty Int. Here I am decoding modelYear manually using NSDictionary and trying to get Int. Swift 4 cannot do it automatically.
do {
// Decoding HistoryItem from JSON
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
let decoder = JSONDecoder()
let historyItem = try decoder.decode(HistoryItem.self, from: jsonData)
if let modelYear = (dict as NSDictionary).value(forKeyPath: "vehicle.model_year") as? Int {
historyItem.vehicle?.modelYear = modelYear
}
// Saving HistoryItem to Realm
let realm = try! Realm()
try! realm.write {
realm.add(historyItem, update: true)
}
} catch {
print(error.localizedDescription)
}
This is my Vehicle class that is contained inside HistoryItem:
class Vehicle: Object, Codable {
#objc dynamic var VIN: String = ""
#objc dynamic var make: String?
#objc dynamic var modelName: String?
#objc dynamic var recallCount: Int = 0
#objc dynamic var modelYear: Int = 0
override static func primaryKey() -> String? {
return "VIN"
}
private enum CodingKeys: String, CodingKey {
case VIN = "vin"
case make
case modelName = "model_name"
case recallCount = "recall_count"
}
}
As you see, there is no model_year key in CodingKeys.

parsing nested Array using codable in swift 4

I am getting many random issues. Mostly like some structure is not decodable not able to understand how to define structure.
Please find the code snipped
var JSON = """
{"variants":{"variant_groups":[{"group_id":"1","name":"Crust","variations":[{"name":"Thin","price":0,"default":1,"id":"1","inStock":1},{"name":"Thick","price":0,"default":0,"id":"2","inStock":1,"isVeg":1},{"name":"Cheese burst","price":100,"default":0,"id":"3","inStock":1,"isVeg":1}]},{"group_id":"2","name":"Size","variations":[{"name":"Small","price":0,"default":1,"id":"10","inStock":1,"isVeg":0},{"name":"Medium","price":100,"default":0,"id":"11","inStock":1,"isVeg":1},{"name":":Large","price":200,"default":0,"id":"12","inStock":1,"isVeg":0}]},{"group_id":"3","name":"Sauce","variations":[{"name":"Manchurian","price":20,"default":0,"id":"20","inStock":1,"isVeg":0},{"name":"Tomato","price":20,"default":0,"id":"21","inStock":1,"isVeg":1},{"name":"Mustard","price":20,"default":0,"id":"22","inStock":1,"isVeg":0}]}],"exclude_list":[[{"group_id":"1","variation_id":"3"},{"group_id":"2","variation_id":"10"}],[{"group_id":"2","variation_id":"10"},{"group_id":"3","variation_id":"22"}]]}}
""".data(using: .utf8)
/*
not sure is this the right way to define Root
*/
struct Root : Codable {
let variants : varientStruct
let exclude_list : exclude_list
}
struct exclude_list : Codable{
let variation_id : String
let group_id : String
}
struct varientStruct: Codable {
let variant_groups = [variant_groups_struct]
}
struct variant_groups_struct : Codable {
let group_id : String
let name :String
let variations: [variationsStruct]
}
struct variationsStruct :Codable {
let name : String
let price : Int
let selected: Int
let id : String
let inStock: Bool
enum CodingKeys : String, CodingKey {
case name
case price
case selected = "default"
case id
case inStock
}
}
}
do {
let data = Data(person.utf8)
let result = try JSONDecoder().decode(Root.self, from: JSON)
print(result)
} catch {
print(error)
}
First of all and once again, please conform to the naming convention:
struct and class names start with a uppercase letter.
Variable and function names start with a lowercase letter.
All variable and struct / class names are camelCased rather than snake_cased.
Second of all, JSON is very easy to read. There are only two collection types (array [] and dictionary {}) and four value types.
Format the JSON string to be able to recognize the structure more conveniently
let jsonString = """
{"variants":{"variant_groups":[{"group_id":"1","name":"Crust","variations":
[{"name":"Thin","price":0,"default":1,"id":"1","inStock":1},
{"name":"Thick","price":0,"default":0,"id":"2","inStock":1,"isVeg":1},
{"name":"Cheese burst","price":100,"default":0,"id":"3","inStock":1,"isVeg":1}]
},{"group_id":"2","name":"Size","variations":
[{"name":"Small","price":0,"default":1,"id":"10","inStock":1,"isVeg":0},
{"name":"Medium","price":100,"default":0,"id":"11","inStock":1,"isVeg":1},
{"name":":Large","price":200,"default":0,"id":"12","inStock":1,"isVeg":0}]
},{"group_id":"3","name":"Sauce","variations":
[{"name":"Manchurian","price":20,"default":0,"id":"20","inStock":1,"isVeg":0},
{"name":"Tomato","price":20,"default":0,"id":"21","inStock":1,"isVeg":1},
{"name":"Mustard","price":20,"default":0,"id":"22","inStock":1,"isVeg":0}]
}],
"exclude_list":[[{"group_id":"1","variation_id":"3"}, {"group_id":"2","variation_id":"10"}],
[{"group_id":"2","variation_id":"10"},{"group_id":"3","variation_id":"22"}]]
}
}
"""
Then build the structs according to the JSON structure step by step
struct Root : Decodable {
let variants : Variant
}
struct Variant : Decodable {
private enum CodingKeys : String, CodingKey {
case groups = "variant_groups"
case excludeList = "exclude_list"
}
let groups : [VariantGroup]
let excludeList : [[ExcludeList]]
}
struct VariantGroup : Decodable {
private enum CodingKeys : String, CodingKey {
case groupID = "group_id"
case name, variations
}
let groupID : String
let name : String
let variations : [Variation]
}
struct Variation : Decodable {
let name : String
let price : Int
let `default` : Int
let id : String
let inStock : Int
}
struct ExcludeList : Decodable {
private enum CodingKeys : String, CodingKey {
case groupID = "group_id"
case variationID = "variation_id"
}
let groupID : String
let variationID : String
}
Then decode the stuff
do {
let data = Data(jsonString.utf8)
let result = try JSONDecoder().decode(Root.self, from: data)
print(result)
} catch { print(error) }

Resources