How to populate my object Array with JSON data using Alamofire - json

I am new to Swift and I'm trying to creat a mobile app for my woocommerce store. I created a product class with name, price and image src. I also created another productBank to store my product objects but I don't know how to populate it with JSON using Alamofire.
class Simpleproduct {
var productName: String
var productPrice: Int
var ProductImage: String
init(price: Int, name: String, image: String) {
productName = name
productPrice = price
ProductImage = image
}
}
class Productbank {
var productlist = [Simpleproduct]()
init() {
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
productlist.append(Simpleproduct(price: 5, name: "productname", image: "imagesrc"))
// [...]

Posting my answer again because it was deleted...
I recommend using AlamoFireObjectMapper, is a cool and easy to use library:
https://github.com/tristanhimmelman/AlamofireObjectMapper
your code will be something like this (you will receive the json automatically mapped to your object model):
The object Model
class EncuestaModel:Object, Mappable{
#objc dynamic var Id = 0
#objc dynamic var Name:String?
#objc dynamic var CreationDate:Date?
#objc dynamic var Status = 0
#objc dynamic var Default = false
required convenience init?(map: Map) {
self.init()
}
}
and for the Alamofire request:
func getEncuestas(){
let url = "yourApiUrl"
Alamofire.request(url).responseObject {(response:DataResponse<LoginResultModel>) in
if let result = response.result.value {
//do your code here
}
}
}

Related

Returning specific field in vapor4

I don't want to get the location field, but return the other fields. How would I make it happen?
func getperinfo(_ req: Request) throws -> EventLoopFuture<[Info]>{
let user = try req.auth.require(User.self)
return Info.query(on: req.db).filter(\.$user.$id == user.id!).all()
}
Model:
import Foundation
import Fluent
import Vapor
import FluentPostgresDriver
import FluentPostGIS
final class Info:Model,Content{
static let schema = "info"
#ID(key: .id)
var id:UUID?
#Field(key: "姓名")
var name: String
#Field(key: "IG帳號")
var account: String
#Field(key: "頭像")
var picture: String
#Field(key: "年紀")
var age: String
#Field(key: "生日")
var birth: String
#Field(key: "居住城市")
var city: String
#Field(key: "興趣")
var hobby : String
#Field(key:"位置")
var location: GeometricPoint2D
#Parent(key: "user_id")
var user: User
init(){}
init(id:UUID?=nil, name:String, account:String, picture:String ,age:String, birth:String,location: GeometricPoint2D , city:String, hobby:String, userId:UUID){
self.id=id
self.name=name
self.account=account
self.picture=picture
self.age=age
self.birth=birth
self.location=location
self.city=city
self.hobby=hobby
self.$user.id=userId
}
}
You just could create a new struct with less fields and then map results into it
struct MyCustomResult: Content {
let id: UUID
let name: String
}
func getperinfo(_ req: Request) throws -> EventLoopFuture<[MyCustomResult]>{
let user = try req.auth.require(User.self)
return Info.query(on: req.db).filter(\.$user.$id == user.id!).all().map {
$0.map { MyCustomResult(id: $0.id!, name: $0.name) }
}
}

Parse JSON with swiftyJSON and more

Hi I've tried to parse my JSON but I couldn't get my data from it,
(I used SwiftyJSON)
how can I parse this ugly JSON?
//Mark: parser functions:
private func parseProvincesResult(provincesJSON: JSON, completion: #escaping(_ :ProvincesV2) -> Void) {
print(provincesJSON)
let errorCode: Int = provincesJSON["ErrorCode"].intValue
let errorDescriptions: String = provincesJSON["ErrorString"].stringValue
let newMacKey: String = provincesJSON["NewMacKey"].stringValue
let newPinKey: String = provincesJSON["NewPinKey"].stringValue
let version: Int = provincesJSON["Ver"].intValue
var provinceList: [ProvinceListResult] = []
for i in provincesJSON["ProvinceListResult"].arrayValue {
let id: Int = i["Id"].intValue
let name: String = i["Name"].stringValue
let proList = ProvinceListResult(id: id, name: name)
provinceList.append(proList)
}
let model = ProvincesV2(errorCode: errorCode, errorDescriptions: errorDescriptions, newMacKey: newMacKey, newPinKey: newPinKey, version: version, provinceList: provinceList)
completion(model)
}
and my JSON is:
{"ErrorCode":"8",
"ErrorString":"عملیات با موفقیت انجام شد.",
"NewMacKey":"vph+eLFgxa6LVq90QfsNUA==",
"NewPinKey":"evJiM9W6S9RWEClR6csxEQ==",
"Ver":201,
"ProvinceListResult":[{"Id":1,"Name":"آذربايجان شرقي"},
{"Id":2,"Name":"آذربايجان غربي"},
{"Id":3,"Name":"اردبيل"},
{"Id":4,"Name":"اصفهان"},
{"Id":5,"Name":"البرز"},
{"Id":6,"Name":"ايلام"},
{"Id":7,"Name":"بوشهر"},
{"Id":8,"Name":"تهران"},
{"Id":9,"Name":"چهارمحال و بختياري"},
{"Id":10,"Name":"خراسان جنوبي"},{"Id":11,"Name":"خراسان رضوي"},{"Id":12,"Name":"خراسان شمالي"},{"Id":13,"Name":"خوزستان"},{"Id":14,"Name":"زنجان"},{"Id":15,"Name":"سمنان"},{"Id":16,"Name":"سيستان و بلوچستان"},{"Id":17,"Name":"فارس"},{"Id":18,"Name":"قزوين"},{"Id":19,"Name":"قم"},{"Id":20,"Name":"کردستان"},{"Id":21,"Name":"کرمان"},{"Id":22,"Name":"کرمانشاه"},{"Id":23,"Name":"کهکيلويه و بويراحمد"},{"Id":24,"Name":"گلستان"},{"Id":25,"Name":"گيلان"},{"Id":26,"Name":"لرستان"},{"Id":27,"Name":"مازندران"},{"Id":28,"Name":"مرکزي"},{"Id":29,"Name":"هرمزگان"},{"Id":30,"Name":"همدان"},{"Id":31,"Name":"يزد"},{"Id":44,"Name":"کیش"}]}
how can I parse It?
tnx
Using Codable, you could do this:
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let errorCode, errorString, newMACKey, newPinKey: String
let ver: Int
let provinceListResult: [ProvinceListResult]
enum CodingKeys: String, CodingKey {
case errorCode = "ErrorCode"
case errorString = "ErrorString"
case newMACKey = "NewMacKey"
case newPinKey = "NewPinKey"
case ver = "Ver"
case provinceListResult = "ProvinceListResult"
}
}
// MARK: - ProvinceListResult
struct ProvinceListResult: Codable {
let id: Int
let name: String
enum CodingKeys: String, CodingKey {
case id = "Id"
case name = "Name"
}
}
(generated by https://app.quicktype.io)
And getting a value out might look like:
let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
print(welcome?.provinceListResult[1])
print(welcome?.provinceListResult[1])
If your original data is in String form, it can be converted to Data by doing this:
myStringJSON.data(using: .utf8)!
create two models for parse JSON
first for the province:
public struct ProvinceModel {
var id: Int?
var name: String?
init(json: JSON) {
self.id = json["Id"].int
self.name = json["Name"].string
}
}
with a constructor, you can parse JSON and set data to variables.
and second model:
public struct MyJSONModel {
var errorString: String?
var newMacKey: String?
var newPinKey: String?
var Ver: Int?
var Provinces: [ProvinceModel]?
init(json: JSON) {
// simple parse
self.errorString = json["ErrorCode"].string
self.newMacKey = json["NewMacKey"].string
self.newPinKey = json["NewPinKey"].string
self.Ver = json["Ver"].int
// array
self.Provinces = [] // empty array
if let jsonArray = json["ProvinceListResult"].array {
for json in jsonArray {
let ProvinceObject = ProvinceModel.init(json: json)
self.Provinces?.append(ProvinceObject)
}
}
}
}
for parse province items in the array, you must create a loop and create an object then set in variables
I prefer to use a constructor for parse JSON instead of a custom function.

Return Children and Siblings values in response

It's probably a trivial question, but I couldn't solve it and didn't find the answer.
I have 3 tables related to each other as Parent-Child and Siblings. And I want to return some values form all of the tables and the same JSON response.
final class ClimbingGym: Model, Content {
static let schema = "climbing_gyms"
#ID(key: .id)
var id: UUID?
#Field(key: "name")
var name: String
#Field(key: "description")
var description: String
#Siblings(through: ClimbingDisciplineClimbingGym.self, from: \.$gym, to: \.$discipline)
var disciplines: [ClimbingDiscipline]
#Children(for: \.$gym)
var socialNetworks: [SocialNetwork]
init() { }
init(
id: UUID? = nil,
name: String,
description: String,
) {
self.id = id
self.name = name
self.description = description
}
}
enum ClimbingDisciplineType: String, Codable, CaseIterable, Equatable {
static let schema = "climbing_discipline_type"
case lead
case boulder
case speed
}
final class ClimbingDiscipline: Model, Content {
static let schema = "climbing_disciplines"
#ID(key: .id)
var id: UUID?
#Enum(key: "type")
var type: ClimbingDisciplineType
init() { }
init(
id: UUID? = nil,
type: ClimbingDisciplineType
) {
self.id = id
self.type = type
}
}
final class SocialNetwork {
static let schema = "social_networks"
#ID(key: .id)
var id: UUID?
#Field(key: "link")
var link: String
#Parent(key: "gym_id")
var gym: ClimbingGym
init() { }
init(
id: UUID? = nil,
link: String,
gym: ClimbingGym
) throws {
self.id = id
self.link = link
self.$gym.id = try gym.requireID()
}
}
And I want to return that model:
struct ClimbingGymResponse: Codable, Content {
let id: UUID
let name: String
let description: String
let disciplines: [ClimbingDisciplineType]
let socialNetworks: [String]
}
so the query that I'm using now looks like that
func getAll(req: Request) throws -> EventLoopFuture<[ClimbingGymResponse]> {
ClimbingGym
.query(on: req.db)
.join(children: \ClimbingGym.$socialNetworks)
.join(siblings: \ClimbingGym.$disciplines)
.all()
}
and it obviously doesn't work, because it returns [ClimbingGym] instead of [ClimbingGymResponse].
So how can I transform one to another?
I have problems with filling disciplines: [ClimbingDisciplineType] and socialNetworks: [String] field for each gym
Thank you!
You can map the array of results into the type you want. So
ClimbingGym
.query(on: req.db)
.with(\.$socialNetworks)
.with(\.$disciplines)
.all().flatMapThrowing { gyms in
try gyms.map { try ClimbingGymResponse(id: $0.requireID(), ...) }
}

Swift 5 Parsing strange json format

I'm trying to parse JSON but keep getting incorrect format error. The JSON I get back from FoodData Central (the USDA's Nutrition API) is as follows:
{
dataType = "Survey (FNDDS)";
description = "Chicken thigh, NS as to cooking method, skin not eaten";
fdcId = 782172;
foodNutrients = (
{
amount = "24.09";
id = 9141826;
nutrient = {
id = 1003;
name = Protein;
number = 203;
rank = 600;
unitName = g;
};
type = FoodNutrient;
},
{
amount = "10.74";
id = "9141827";
nutrient = {
id = 1004;
name = "Total lipid (fat)";
number = 204;
rank = 800;
unitName = g;
};
type = FoodNutrient;
}
);
}
My Structs:
struct Root: Decodable {
let description: String
let foodNutrients: FoodNutrients
}
struct FoodNutrients: Decodable {
// What should go here???
}
From the JSON, it looks like foodNutrients is an array of unnamed objects, each of which has the values amount: String, id: String, and nutrient: Nutrient (which has id, name etc...) However, forgetting the Nutrient object, I can't even parse the amounts.
struct FoodNutrients: Decodable {
let amounts: [String]
}
I don't think its an array of string, but I have no idea what the () in foodNutrients would indicate.
How would I go about parsing this JSON. I'm using Swift 5 and JSONDecoder. To get the JSON I use JSONSerializer, then print out the JSON above.
This is not a JSON. This is a property list in the openStep format.
This is how it can be modelled (use String instead of Int):
struct Root: Decodable {
let description: String
let foodNutrients: [FoodNutrient]
}
struct FoodNutrient: Decodable {
let id: String
let amount: String
let nutrient: Nutrient
}
struct Nutrient: Decodable {
let name: String
let number: String
let rank: String
let unitName: String
}
And then decode it like this:
try PropertyListDecoder().decode(Root.self, from: yourStr)
The () in foodNutrients indicates that it holds an array of objects - in that case FoodNutrient objects. Therefore your root object should look like this:
struct Root: Decodable {
let description: String
let foodNutrients: [FoodNutrient]
}
Now the foodNutrient is except for the nutrient object straightforward:
struct FoodNutrient: Decodable {
let id: Int // <-- in your example it is an integer and in the second object a string, choose the fitting one from the API
let amount: String
let nutrient: Nutrient
}
And the nutrient object should look like this:
struct Nutrient: Decodable {
let name: String
let number: Int
let rank: Int
let unitName: String
}
Using Decodable is a good and easy way to serialize JSON. Hope that helps. Happy coding :)

AlamofireObjectMapper in swift 3

I want to use AlamofireObjectMapper for the first time to parse a json response in swift.
The response is:
"success": true,
"terms": "https:\/\/currencylayer.com\/terms",
"privacy": "https:\/\/currencylayer.com\/privacy",
"timestamp": 1480007048,
"source": "USD",
"quotes": {
"USDAED": 3.672598,
"USDAFN": 66.599998,
"USDALL": 127.999937,
"USDAMD": 478.679993,
"USDANG": 1.780277,
"USDAOA": 165.072998,
"USDARS": 15.497261,
"USDAUD": 1.348899,
"USDAWG": 1.79,
"USDAZN": 1.714104,
"USDBAM": 1.855297,
"USDBBD": 2,
"USDBDT": 79.179735,
"USDBGN": 1.854199,
"USDBHD": 0.377036,
"USDBIF": 1668.300049,
"USDBMD": 1,
"USDBND": 1.429902,
"USDBOB": 6.870014,
"USDBRL": 3.396898,
"USDBSD": 1,
}
I mapped it like this:
class ModelCurrency: Mappable {
var success : Bool?
var terms : String?
var privacy : String?
var timestamp : CGFloat?
var source : String?
var quotes : [Quotes]?
init() {}
required init?(map: Map) {
}
func mapping(map: Map) {
success<-map["success"]
terms<-map["terms"]
privacy<-map["privacy"]
timestamp<-map["timestamp"]
source<-map["source"]
quotes<-map["quotes"]
print("It json\(terms)")
}
}
class Quotes : Mappable {
var name : String?
var val : CGFloat?
required init?(map: Map) {
}
func mapping(map: Map) {
name<-map["name"]
val<-map["val"]
}
}
And in my controller:
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
super.viewDidLoad()
let URL = "http://www.apilayer.net/api/live?access_key=ad847a0a855c0647590df2b818923025"
Alamofire.request(URL).responseArray(queue: "quotes") { (response: DataResponse<[Quotes]>) in
let arrayCurency = response.result.value!
for quotes in arrayCurency {
print(quotes.name!)
print(quotes.val!)
}
}
}
It gives me error mapping and this error:
cannot convert value 'String' to expected argument type 'DispatchQueue?'
There are several issues.
You want to reference DataResponse<ModelCurrency> rather than DataResponse<[Quotes]>.
You want to use responseObject, not responseArray.
You don't want to use that queue parameter with a String value. The queue parameter is used to specify which dispatch queue you want the completion handlers to run. But that's not what you're doing here, so you should just remove it.
The value associated with the quotes key is not an array of objects. It is yet another dictionary. So you should map it to a dictionary, and then use map method to convert that to an array of Quote objects.
So, pulling that all together:
Alamofire.request(urlString).responseObject { (response: DataResponse<ModelCurrency>) in
...
}
And
class ModelCurrency: Mappable {
var success : Bool?
var terms : String?
var privacy : String?
var timestamp : CGFloat?
var source : String?
var quotes : [Quote]?
required init?(map: Map) { }
func mapping(map: Map) {
success <- map["success"]
terms <- map["terms"]
privacy <- map["privacy"]
timestamp <- map["timestamp"]
source <- map["source"]
var dictionary: [String: CGFloat]?
dictionary <- map["quotes"]
quotes = dictionary?.map { return Quote(name: $0.key, val: $0.value) }
}
}
class Quote {
var name : String?
var val : CGFloat?
init(name: String?, val: CGFloat?) {
self.name = name
self.val = val
}
}
(I've renamed Quotes to Quote as it would appear to be a quote for a single currency.)
The issue is with this part of code:
func mapping(map: Map) {
name<-map["name"]
val<-map["val"]
}
in Quotes.
What you should do is map dictionary:
var quotes: [String : CGFloat] = [:]
and when mapping use:
quotes <- map["quotes"]
Take a look at:
https://github.com/Hearst-DD/ObjectMapper
at the basics there is mapping dictionary.
To be more specific, in quote object you don't have name and val JSON objects, it is just what you want. If you map it to dictionary you will be able to access theses values.
Haven't seen your image - but if you don't change above the app will crash.
For the above issue you need to provide queue in which you want to run your request, if you don't want to mess with that and you actually want keyPath, then you need to call function with keyPath: instead of queue:. Link:
https://github.com/tristanhimmelman/AlamofireObjectMapper
look for Array Responses