Can I request a two-dimensional associative array in swift Moya? - swift

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

Related

how to get data based on the previous level data

I have JSON as below.
{
"type": "regular",
"data": [
{
"title": "Title 1",
"price_regular": 1.1,
"price_express": 2.2,
},
{
"title": "Title 2",
"price_regular": 1.1,
"price_express": 2.2,
}
]
}
For this I have model as below.
struct MainModel : Codable {
var type : String?
var data : [DataModel]?
}
struct DataModel : Codable {
var title : String?
var price_regular : Float?
var price_express : Float?
var price : Float {
get {
if (type == "regular") { ----> Please check below query for this.
return price_regular ?? 0.0
}
return price_express ?? 0.0
}
}
}
What I am doing in DataModel is create new variable as price. I want to check what data I have for type of main class and based on that I want to get value for Price. Is there way I can achieve this?
Actually I am playing in model. I know I can make this run-time, but I would like to do this in model so that logic is at 1 place only.
I'd do it with a custom init(from decoder:), allowing to pass type from MainModel to DataModel.
struct MainModel : Codable {
var type : String?
var data : [DataModel]?
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
type = try container.decode(String.self, forKey: .type)
var dataContainer = try container.nestedUnkeyedContainer(forKey: .data)
var dataTemp: [DataModel] = []
while !dataContainer.isAtEnd {
let dataSubcontainer = try dataContainer.nestedContainer(keyedBy: DataModel.DataModelKeys.self)
let title = try dataSubcontainer.decode(String.self, forKey: .title)
let price_regular = try dataSubcontainer.decode(Float.self, forKey: .price_regular)
let price_express = try dataSubcontainer.decode(Float.self, forKey: .price_express)
dataTemp.append(DataModel(title: title, price_regular: price_regular, price_express: price_express, type: type))
}
data = dataTemp
}
}
struct DataModel : Codable {
var title : String?
var price_regular : Float?
var price_express : Float?
var type: String?
enum DataModelKeys: String, CodingKey {
case title
case price_regular
case price_express
}
var price : Float {
get {
if (type == "regular") {
return price_regular ?? 0.0
}
return price_express ?? 0.0
}
}
}
To test it:
let json = """
[{
"type": "regular",
"data": [
{
"title": "Title 1",
"price_regular": 1.1,
"price_express": 1.2,
},
{
"title": "Title 2",
"price_regular": 2.1,
"price_express": 2.2,
}
]
},
{
"type": "irregular",
"data": [
{
"title": "Title 3",
"price_regular": 1.1,
"price_express": 1.2,
},
{
"title": "Title 4",
"price_regular": 2.1,
"price_express": 2.2,
}
]
}]
"""
do {
let model = try JSONDecoder().decode([MainModel].self, from: Data(json.utf8))
print(model)
model.forEach { aMainModel in
print("MainModel type: \(aMainModel.type)")
aMainModel.data?.forEach {
print("Title: \($0.title) - type: \($0.type) - price: \($0.price)")
}
}
} catch {
print("Error: \(error)")
}
Which output:
$>MainModel type: Optional("regular")
$>Title: Optional("Title 1") - type: Optional("regular") - price: 1.1
$>Title: Optional("Title 2") - type: Optional("regular") - price: 2.1
$>MainModel type: Optional("irregular")
$>Title: Optional("Title 3") - type: Optional("irregular") - price: 1.2
$>Title: Optional("Title 4") - type: Optional("irregular") - price: 2.2
With help of #Larme answer, I updated model and made it working.
struct MainModel : Codable {
var type : String?
var data : [DataModel]? {
didSet { // on didSet assigned value of type to type
for i in 0..<(self.data ?? [DataModel]()).count {
self.data![i].type = self.type ?? ""
}
}
}
}
struct DataModel : Codable {
var title : String?
var price_regular : Float?
var price_express : Float?
var type : String? // added new variable
var price : Float? {
get {
if (type == "regular") {
return price_regular ?? 0.0
}
return price_express ?? 0.0
}
}
}
And this helped me of doing what I was looking for...

Getting error "The data couldn’t be read because it is missing." during fetching data using alamofire and swift

I am fetching data from server in swift using Alamofire and codable. But i am getting error
The data couldn’t be read because it is missing.
is there anything wrong in my code? why i am getting error like this?
My code:
import Foundation
// MARK: - Welcome
struct UserDetails: Codable {
let success: Int
let message: String
let result: UserResult
enum CodingKeys: String, CodingKey {
case success = "Success"
case message = "Message"
case result = "Result"
}
}
// MARK: - Result
struct UserResult: Codable {
let userID: Int
let firstName, location, state, about: String
let totalScore: Int
let sun, rising, moon: String
let ego, communication, birthStar, sexual: Int
let intellectual, temperament, emotional, healthGenetic: Int
let profilePic: String
let numberOfPhotos: Int
let imagelist: [Imagelist]
let verified: Int
let dateOfBirth, timeOfBirth: String
let age: Int
enum CodingKeys: String, CodingKey {
case userID = "User_Id"
case firstName = "First_Name"
case location = "Location"
case state = "State"
case about = "About"
case totalScore = "TotalScore"
case sun
case rising = "Rising"
case moon = "Moon"
case ego = "Ego"
case communication = "Communication"
case birthStar = "Birth_star"
case sexual = "Sexual"
case intellectual = "Intellectual"
case temperament = "Temperament"
case emotional = "Emotional"
case healthGenetic = "Health_Genetic"
case profilePic = "Profile_Pic"
case numberOfPhotos = "NumberOfPhotos"
case imagelist, verified
case dateOfBirth = "DateOfBirth"
case timeOfBirth = "TimeOfBirth"
case age = "Age"
}
}
// MARK: - Imagelist
struct Imagelist: Codable {
let userID: Int
let imagePath: String
enum CodingKeys: String, CodingKey {
case userID = "User_Id"
case imagePath = "Image_path"
}
}
import Foundation
import Alamofire
class ApiRequest {
func getUserDetailData(_ userId: Int, _ accessToken: String, completion: #escaping UserProfileDetailsData) {
guard let url = URL(string: "\(USER_PROFILE_URL)?UserID=\(userId)&RequestingUser_id=1") else{return}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = HTTPMethod.get.rawValue
urlRequest.addValue(accessToken, forHTTPHeaderField: "Authorization")
Alamofire.request(urlRequest).responseJSON { (response) in
if let error = response.result.error {
debugPrint(error.localizedDescription)
completion(nil)
return
}
guard let data = response.data else { return completion(nil)}
let jsonDecoder = JSONDecoder()
do {
let person = try jsonDecoder.decode(UserDetails.self, from: data)
completion(person)
} catch {
debugPrint(error.localizedDescription)
completion(nil)
}
}
}
}
In my constant class i have following two codes
let USER_PROFILE_URL = URL_BASE + "GetUserProfile/"
typealias UserProfileDetailsData = (UserDetails?) -> Void
I am calling the api in my view controller like:
func getUserDetailsData(_ userId: Int, accessToken: String) {
apiRequest.getUserDetailData(userId, accessToken) { (userDetails) in
self.userDataResult = userDetails?.result
}
}
I am getting data from server like
{
"Success": 1,
"Message": "Record Get Successfully.",
"Result": {
"User_Id": 230,
"First_Name": "Fgfdg",
"Location": "Fond du Lac County",
"State": " Wisconsin",
"About": "SDFDdsgsGgsddsSSGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG",
"TotalScore": 27,
"sun": "Scorpio",
"Rising": "Scorpio",
"Moon": "Libra",
"Ego": 100,
"Communication": 100,
"Birth_star": 99,
"Sexual": 50,
"Intellectual": 100,
"Temperament": 99,
"Emotional": 0,
"Health_Genetic": 100,
"Profile_Pic": "Images/ProfilePic/yugva0y.jpg",
"NumberOfPhotos": 8,
"imagelist": [
{
"User_Id": 230,
"Image_path": "Images/UploadImage/uUk8lUP.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/Sf5jBIU.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/uw8hdNt.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/vRdKGmM.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/vQchUIO.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/HYHQHiL.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/owM3Al9.jpg"
},
{
"User_Id": 230,
"Image_path": "Images/UploadImage/T4ufyc6.jpg"
}
],
"verified": 0,
"DateOfBirth": "1978-11-28",
"TimeOfBirth": "07:52:00.0000000",
"Age": 41
}
}
The error i am getting is:
Swift.DecodingError.valueNotFound(Swift.String, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "Result", intValue: nil), CodingKeys(stringValue: "sun", intValue: nil)], debugDescription: "Expected String value but found null instead.", underlyingError: nil))

Converting inherited class to JSON in Swift 4.2

I'm new to Swift, I'm trying to have this JSON
{
"title": "newSurvey",
"pages": [
{
"questions": [
{
"title": "Untitled Question",
"type": "QUESTION_SLIDER",
"content": {
"min": "1",
"max": "10",
"step": "3",
"defaultValue": "5"
}
},
{
"title": "asdasddfdf",
"type": "QUESTION_TEXT",
"choices": ["choice1","choice2"]
}
]
}
]}
I'm suffering from converting subclass to JSON
I thought about divide my code to three objects then add them to the final
string from jsonEncoder
so that's what I did
public class Question : Encodable {
var title : String?
var description: String?
init(_ title: String , _ desc: String) {
self.title = title
self.description = desc
}}
struct Questions : Encodable{
var questions : [Question]
}
class Create : Encodable {
var title : String?
var pages : [Questions] = []
init(_ title:String , _ q : Questions) {
self.title = title
self.pages.append(q)
}
func postData () -> String{
let jsonEncoder = JSONEncoder()
do {
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try jsonEncoder.encode([self])
let json = String(data: jsonData ,encoding: .utf8)
print( json!)
return json!
}
catch {
print("Error")
}
return ""
}
class Content :Encodable {
init(_ min : Int , _ max : Int){
self.min = min
self.max = max
}
var min : Int?
var max : Int?
var minLabel : String?
var maxLabel : String?
var defaultValue : Int?
var step : Int?
}
class SliderQuestion :Question {
let TYPE = "SHORT TEXT"
var content = Content(0,2)
init(_ title: String, _ desc: String,_ content : Content ) {
self.content = content
super.init(title, desc)
}
}
I'm sorry for the long code but I want to clarify my idea, is there any way to have the subclass converted to JSON?

Matching separated arrays for Realm Object

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

How to call struct using string value in swift 4

JSON
{
"method" : 105,
"values" : {
"item_image" : "A123",
"name" : "Image1",
"description" : "Sample Description"
},
"columns" : {
"name" : {
"type" : "text",
"source" : "name"
},
"item_image" : {
"type" : "image",
"source" : "item_image"
},
"description" : {
"type" : "text",
"source" : "description"
}
}
}
Model
struct Params:Decodable {
let values: Values
let columns: [String: Columns]
}
struct Columns: Decodable {
let source: String
let type: String
}
struct Values: Decodable {
let name: String
let item_image: String
let description: String
}
Code
for (_, val) in param.columns {
colType = val.type
colSource += ", \(val.source)"
let s = "param.values.\(val.source)" \\this line is invalid
let vals = s
print("value: ",vals)
}
Hi! Im working with the lines of code above, and I'm having a problem to call my struct. Instead of using static values i want it dynamic with the code below but its invalid:
let s = "param.values.\(val.source)"
invalid because when it comes to this line:
let vals = s
the result is param.values.name or param.values.item_image, my expected value is Image1 or A123.
to make it clear here's the expected value from the codes and how can I achieve this:
for (_, val) in param.columns {
colType = val.type! expectedvalue= text
colSource += ", \(val.source)" expectedvalue= ", name, item_image, description"
let s = "param.values.\(val.source)" expectedvalue: "param.values.name" or "param.values.item_image"
let vals = s expectedvalue: Image1 or A123
print("value: ",vals) expectedvalue: value: Image1
}
Thanks!
I don't think you can access a property by string. One solution could be to create a mapper function that actually handles that. Still, this assumes that you have pre-determined keys.
struct Params:Decodable {
let values: Values
let columns: [String: Columns]
func getValues(from columns: Columns) -> String? {
switch columns.source {
case "item_image":
return values.item_image
case "name":
return values.name
case "description":
return values.description
default:()
}
if columns.source == "item_image" {
return values.item_image
}
return nil
}
}
struct Columns: Decodable {
let source: String
let type: String
}
struct Values: Decodable {
let name: String
let item_image: String
let description: String
}
let column = Columns(source: "name", type: "text")
let column2 = Columns(source: "item_image", type: "image")
let column3 = Columns(source: "description", type: "text")
let values = Values(name: "Image1", item_image: "A123", description: "Sample Description")
let params = Params(values: values, columns: ["name": column, "item_image": column2, "description": column3])
let name = params.getValues(from: column) //Image1
let itemImage = params.getValues(from: column2) //A123
let description = params.getValues(from: column3) //Sample Description