Parse local json file to object - ios

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 ?? "")
}

Related

How to get a UIImage from a String with JSON data

I am trying to load an icon image form the API OpenWeatherMap, and display it in an ImageView. I am trying to load it into the imageView 'iconImage'. I have successfully loaded the JSON data for the location and humidity, as they are Strings, but the Icon data is also a String and I cannot get it to display as a UIImage.
Code below:
My JSON Structs below:
struct Coordinate : Decodable {
let lat, lon : Double?
}
struct Weather : Decodable {
var id : Int?
var main, myDescription, icon : String?
enum CodingKeys : String, CodingKey {
case id = "id"
case main = "main"
case icon = "icon"
case myDescription = "description"
}
}
struct Sys : Decodable {
let type, id : Int?
let sunrise, sunset : Date?
let message : Double?
let country : String?
}
struct Main : Decodable {
let temp, tempMin, tempMax : Double?
let pressure, humidity : Int?
}
struct Wind : Decodable {
let speed : Double?
let deg : Int?
}
struct MyWeather : Decodable {
let coord : Coordinate?
let cod, visibility, id : Int?
let name : String?
let base : String?
let weather : [Weather]?
let sys : Sys?
let main : Main?
let wind : Wind?
let dt : Date?
}`enter code here`
View controller below:
guard let APIUrl = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=" + text + "&appid=e7b2054dc37b1f464d912c00dd309595&units=Metric") else { return }
//API KEY
URLSession.shared.dataTask(with: APIUrl) { data, response, error in
guard let data = data else { return }
let decoder = JSONDecoder()
//Decoder
do {
if (self.iconImage != nil)
{
if let gicon = weatherData.weather?.first?.icon {
DispatchQueue.main.async {
self.iconImage.image! = gicon
}
}
}
if (self.LocationLabel != nil)
{
if let gmain = weatherData.name {
print(gmain)
DispatchQueue.main.async {
self.LocationLabel.text! = "Current Weather in: " + String (gmain)
}
}
}
if (self.HumidityLabel != nil)
{
if let ghumidity = weatherData.main?.humidity
{
print(ghumidity, "THIS IS HUMIDITY")
DispatchQueue.main.async {
self.HumidityLabel.text! = String (ghumidity)
}
}
}
Use Kingfisher
It creates an extension in ImageView. You will use self.imageview.kf.setImage(with: "url")
Icon is the id of the image , you need to append it to this url and load it
http://openweathermap.org/img/w/10d.png // here ------ id = 10d
suppose you'll use SDWebImage , then do this
let urlStr = "http://openweathermap.org/img/w/\(gicon).png"
self.iconImage.sd_setImage(with: URL(string:urlStr), placeholderImage: UIImage(named: "placeholder.png"))
See here in Docs

Search for a string in an array of objects

I'm doing a product search engine in my application.
I have this code:
func searchStringInProductArray(searchString: String) {
let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let path = documentsDir.appendingPathComponent((AppGlobalManager.sharedManager.loggedUser?.selectedLanguage)! + "/json/products.json")
do {
let data = try Data(contentsOf: path)
let decoder = JSONDecoder()
var productsTmpObjectArray = [Products]()
productsTmpObjectArray = try decoder.decode([Products].self, from: data)
productsTmpObjectArray = productsTmpObjectArray.filter({($0.code?.contains(searchString))!})
productsTmpObjectArray = productsTmpObjectArray.filter({($0.name?.contains(searchString))!})
productsTmpObjectArray = productsTmpObjectArray.filter({($0.langVersions.name?.contains(searchString))!})
productsTmpObjectArray = productsTmpObjectArray.filter({($0.langVersions?.description?.contains(searchString))!})
productsObjectArray = productsTmpObjectArray
collectionView1.reloadData()
} catch {
NotificationCenter.default.post(name: Notification.Name("NotificationAlertMessage"), object: nil, userInfo: ["object": errorMessage(title: "Blad".localized(), description: "problemWithParseData".localized(), buttonTitle: "Ok".localized())])
}
}
and object:
struct ChangeTime : Codable {
let year : Int?
let month : Int?
let dayOfMonth : Int?
let hourOfDay : Int?
let minute : Int?
let second : Int?
}
struct Lang : Codable {
let id : Int?
let code : String?
let name : String?
}
struct LangVersions : Codable {
let id : Int?
let parentId : Int?
let name : String?
let lang : Lang?
let description : String?
}
struct Products : Codable {
let id : Int?
let code : String?
let name : String?
let inPieces : Bool?
let prepDeepFryer : String?
let langVersions : [LangVersions]?
let changeTime : ChangeTime?
}
The function is called after entering text in TextField:
#IBAction func searchProductTextFieldChanged(_ sender: Any) {
searchStringInProductArray(searchString: searchTextField.text!)
}
I have the following errors:
productsTmpObjectArray = productsTmpObjectArray.filter({($0.langVersions.name?.contains(searchString))!}) - Value of type '[LangVersions]?' has no member 'name'
productsTmpObjectArray = productsTmpObjectArray.filter({($0.langVersions?.description?.contains(searchString))!}) - Cannot use optional chaining on non-optional value of type 'String'
I would like my search engine to search for all fields at once:
$0.code?.contains(searchString)
$0.name?.contains(searchString)
$0.langVersions.name?.contains(searchString)
$0.langVersions?.description?contains(searchString)
You haven't noticed that 'Products' object contains array of 'LangVersions' object. edited version of your method:
func searchStringInProductArray(searchString: String) {
let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let path = URL(string: "")!
do {
let data = try Data(contentsOf: path)
let decoder = JSONDecoder()
var productsTmpObjectArray = [Products]()
productsTmpObjectArray = try decoder.decode([Products].self, from: data)
productsObjectArray = productsTmpObjectArray.filter({return ($0.code?.lowercased().contains(searchString.lowercased()))! || ($0.name?.lowercased().contains(searchString.lowercased()))! || (($0.langVersions?.filter({($0.name?.lowercased().contains(searchString.lowercased()))!}))?.count)! > 0 || (($0.langVersions?.filter({($0.description?.lowercased().contains(searchString.lowercased()))!}))?.count)! > 0 })
collectionView1.reloadData()
} catch {
NotificationCenter.default.post(name: Notification.Name("NotificationAlertMessage"), object: nil, userInfo: ["object": errorMessage(title: "Blad".localized(), description: "problemWithParseData".localized(), buttonTitle: "Ok".localized())])
}
}
You can try this to search for all values
let searchString = "searStr".lowercased()
productsTmpObjectArray = productsTmpObjectArray.filter { ($0.name?.lowercased().contains(searchString))! || ($0.code?.lowercased().contains(searchString))! || $0.langVersions?.filter {($0.name?.lowercased().contains(searchString))!}.count != 0 || $0.langVersions?.filter {($0.description?.lowercased().contains(searchString))!}.count != 0 }

Getting Error while Using JSONDecodable in swift4?

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.

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.

How to handle array in model class?

How can make model class for this json data
{
total: 41,
totalPages: 4,
valueData: [
{
id: "23",
lastLogin: "0-Jul-2011 11:27:36 AM",
name: "varii"
},
{
id: "24",
lastLogin: "0-Jul-2015 11:27:36 AM",
name: "sarii"
},
{
id: "25",
lastLogin: "0-Jul-2018 11:27:36 AM",
name: "narii"
} ]
}
class OnResponse {
var total: Int?
var totalPages: Int?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
}
}
It's not working how can make it ready for work
and how to pass values to controller to model and how to get value from model
Follow the below class structure
class Response {
var total: Int?
var totalPages: Int?
var valueData: [LoginData]?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
var items:[LoginData] = ()
for (data in response["valueData"]) {
let login = LoginData(name: data["name"], lastLogin: data["lastLogin"])
items.append(login)
}
self.valueData = items
}
}
class LoginData {
var name: String?
var lastLogin: String?
init(name: String, lastLogin: String) {
self.name = name
self.lastLogin = lastLogin
}
}
you should use "reverseMatches" to retrieve the array, not the "data". May be you can use a third library to convert your json data to a model, such as Unbox, https://github.com/JohnSundell/Unbox.
Model for Your response :
struct ModelName {
var total: Int?
var totalPage: Int?
var reverseMatches: [LoginDetails]?
}
struct LoginDetails {
var id: String?
var lastLogin: String?
var name: String?
}
Parse the api response and assign the values on the appropriate fields. I have made, all the variables are optional.
Assign values like below.
var obj = Model()
obj.total = response["total"] as? Int
obj should be var, because you are going to mutate the struct values. because struct is value based, not reference based
class DataModel{
var total : Int?
var totalPages : Int?
var valueData : [UserModel]?
init(JSON: [String:Any?]){
self = parser.doParse(JSON: JSON)
}
}
class UserModel{
var id : String?
var lastLogin : String?
var name : String?
}
class parser : NSObject{
class func doParse(JSON: [String:Any?])->DataModel{
let dataModel = DataModel()
dataModel.total = JSON["total"] as? Int
dataModel.totalPages = JSON["totalPages"] as? Int
var userInfo : [UserModel] = []
let valueData : [String:String?]? = JSON["valueData"] as? [String:String?]
if let valueData = valueData{
for dataDict : [String:String?] in valueData{
let itemModel = UserModel()
itemModel.id = dataDict["id"] as? String
itemModel.lastLogin = dataDict["lastLogin"] as? String
itemModel.name = dataDict["name"] as? String
userInfo.append(itemModel)
}
dataModel.valueData = userInfo
}
return dataModel
}
}
class LoginData: NSObject {
let total: Int = 0
let totalPages: Int = 0
let valueData: Array<ValueData> = Array<ValueData>()
override init(total: Int!, totalPages: Int, valueData: Array<ValueData>!) {
self.total = total
self.totalPages = totalPages
self.valueData = valueData
}
}
struct ValueData {
let id: int?
let lastLogin: String?
let name: String?
init(id: int!, lastLogin: string, name: string!)
self.id = id ?? 0
self.lastLogin = lastLogin ?? ""
self.name = name ?? ""
}
}
you should use struct instead of class for creating model object...
advantages of struct over class refer
Why Choose Struct Over Class?
class/24232845
use two struct for holding your data one is for your single total count
and other is for last login detail
struct lastLogin {
let totalCount: (total: Int, totalPages: Int)
let valueArray: [lastLoginDetail]
}
struct lastLoginDetail {
let valueData: (id: String, lastLogin: String,name: String)
}
extension lastLoginDetail {
init(json: [String : String]) throws {
let id = json["id"]
let lastLogin = json["lastLogin"]
let name = json["name"]
let value = (id,lastLogin,name)
self.valueData = value as! (id: String, lastLogin: String, name: String)
}
}
extension lastLogin {
init(json: [String : Any]) throws {
let total = (json["total"] as! NSNumber).intValue
let totalPages = (json["totalPages"] as! NSNumber).intValue
let totalCounts = (total,totalPages)
var userInfo : [lastLoginDetail] = []
// Extract and validate valueData
let valueData = json["valueData"] as? NSArray
if let valueData = valueData{
for dataDict in valueData{
let dic : [String : String] = dataDict as! [String : String]
let lastLoginDeatails = try! lastLoginDetail(json: dic)
userInfo.append(lastLoginDeatails)
}
}
self.valueArray = userInfo
self.totalCount = totalCounts
}
}
func HowToUseModelClass(){
let jsonDic = NSDictionary()
// jsonDic // your json value
let dataValue = try! lastLogin(json: (jsonDic as! [String : Any])) // parsing the data
print(dataValue.totalCount.total)
print(dataValue.totalCount.totalPages)
print(dataValue.valueArray[0].valueData.id)
}

Resources