How to call struct using string value in swift 4 - swift

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

Related

Issue decoding JSON | Swift

I am having an issue dynamically populating the date associated with each section.
The value of grouped is currently derived from grouping the raw JSON data by routeid
You can see that I am currently using a static value for date as a test, while $0.key represent the value of myID dynamically.
How can also add the dynamic value of myDate intead of "test"?
Variables:
var sections = [mySections]()
var structure = [myStructure]()
Currently this is true
sections.map(\.id) == [8,4]
At the end this must be true as well
sections.map(\.date) == [2021-01-20, 2021-01-18]
Decoding:
do {
let decoder = JSONDecoder()
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.myID })
_ = grouped.keys.sorted()
sections = grouped.map { mySections(date: "test", id: $0.key, items: $0.value) }
.sorted { $0.id > $1.id }
}
Struct:
struct mySections {
let date : String
let id : Int
var items : [myStructure]
}
struct myStructure: Decodable {
let name: String
let type: String
let myID: Int
let myDate: String
}
Example JSON:
[
{
"name": "Jeff",
"type": "large",
"myID": 8,
"myDate": "2021-01-20"
},
{
"name": "Jessica",
"type": "small",
"myID": 4,
"myDate": "2021-01-18"
},
{
"name": "Beth",
"type": "medium",
"myID": 4,
"myDate": "2021-01-18"
}
]
So, you want it grouped by both id number and date string? I would create a Hashable structure for those two values.
struct Section {
let date: String
let id: Int
var items: [Item]
}
extension Section {
struct Header: Hashable {
let date: String
let id: Int
}
}
struct Item: Decodable {
let name: String
let type: String
let myID: Int
let myDate: String
}
And
let decoder = JSONDecoder()
let res = try decoder.decode([Item].self, from: data)
let sections = Dictionary(grouping: res) { Section.Header(date: $0.myDate, id: $0.myID) }
.sorted { $0.key.id > $1.key.id }
.map { Section(date: $0.key.date, id: $0.key.id, items: $0.value) }

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

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

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

Firebase iOS - fetching data with boolean operator

I am trying to use firebase for iOS and I am in front of a problem.
I am using the following JSON tree :
{
"expressions" : {
"expr0" : {
"id" : 0,
"text" : "Text0"
},
"expr1" : {
"id" : 1,
"text" : "Text1"
},
"expr2" : {
"id" : 2,
"text" : "Text2"
},
"expr3" : {
"id" : 3,
"text" : "Text3"
}
},
"levels" : {
"level0" : {
"coverImage" : "lvl0",
"expressions" : {
"expr0" : true,
"expr2" : true
},
"id" : 0,
"title" : "Level0"
},
"level1" : {
"coverImage" : "lvl1",
"expressions" : {
"expr0" : true,
"expr1" : true,
"expr3" : true
},
"id" : 1,
"title" : "Level1"
},
"level2" : {
"coverImage" : "lvl2",
"expressions" : {
"expr1" : true,
"expr2" : true,
"expr3" : true
},
"id" : 2,
"title" : "Level2"
},
"level3" : {
"coverImage" : "lvl3",
"expressions" : {
"expr3" : true
},
"id" : 3,
"title" : "Level3"
}
}
}
I have two kind of objects, expressions and levels. Here are the data models of those 2 objects :
struct Level {
let id : Int
let coverImage : String
let title : String
let expressions : [Expression]
}
struct Expression {
let id : Int
let expression : String
}
I also have a DataService singleton to provide me Firebase Database References :
import UIKit
import FirebaseDatabase
class DataService {
private static let _instance = DataService()
static var instance: DataService {
return _instance
}
var mainRef: FIRDatabaseReference {
return FIRDatabase.database().reference()
}
var levelsRef: FIRDatabaseReference {
return mainRef.child("levels")
}
var expressionsRef: FIRDatabaseReference {
return mainRef.child("expressions")
}
}
I want to store on a Levels array the level with their corresponded expressions and display this array with a collectionView. Based on the JSON file, the final result must be :
levelsArr[Level(id: 0, coverImage: "lvl0", title: "Level0", expressions: [Expression(id: 0, expression: "Text0"), Expression(id: 2, expression: "Text2")]), Level(id: 1, coverImage: "lvl1", title: "Level1", expressions: [Expression(id: 0, expression: "Text0"), Expression(id: 1, expression: "Text1"), Expression(id: 3, expression: "Text3")]), Level(id: 2, coverImage: "lvl2", title: "Level2", expressions: [Expression(id: 1, expression: "Text1"), Expression(id: 3, expression: "Text3"), Expression(id: 2, expression: "Text2")]), Level(id: 3, coverImage: "lvl3", title: "Level3", expressions: [Expression(id: 3, expression: "Text3")])]
Here is the following function I have for the moment :
private var levelsArr = [Level]()
func fetchLevels() {
DataService.instance.levelsRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let levels = snapshot.value as? Dictionary<String, AnyObject> {
for(_, valueLvl) in levels {
if let dictLvl = valueLvl as? Dictionary<String, AnyObject> {
if let id = dictLvl["id"] as? Int, let coverImage = dictLvl["coverImage"] as? String, let title = dictLvl["title"] as? String {
var expressionsArr = [Expression]()
if let expr = dictLvl["expressions"] as? Dictionary<String, AnyObject> {
for(keyLvlExp, _) in expr {
DataService.instance.expressionsRef.observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot)
if let expressions = snapshot.value as? Dictionary<String, AnyObject> {
for (keyExpr, valueExpr) in expressions {
if keyLvlExp == keyExpr {
if let dictExpr = valueExpr as? Dictionary<String, AnyObject> {
if let id = dictExpr["id"] as? Int, let text = dictExpr["text"] as? String{
let expression = Expression(id: id, expression: text)
expressionsArr.append(expression)
}
}
}
}
}
let level = Level(id: id, coverImage: coverImage, title: title, expressions: expressionsArr)
self.levelsArr.append(level)
print(level)
})
}
}
}
}
}
}
})
DispatchQueue.main.async{
self.collectionView?.reloadData()
}
}
The function fetchLevels() is called in the viewDidLoad().
The result of the print(snapshot) is :
Snap (expressions) {
expr0 = {
id = 0;
text = Text0;
};
expr1 = {
id = 1;
text = Text1;
};
expr2 = {
id = 2;
text = Text2;
};
expr3 = {
id = 3;
text = Text3;
};
}
Thank you.