How to assign dictionary to Struct Class - swift

How to assign dictionary [String: [Any]] to [FirstModel]?
A function return [String:[Any]] I want to assign this to [FirstModel] struct class?
// Reponse Data.
{
"Student" : [
{
"code" : "111",
"id" : "2022-1001",
"name" : "Kiran",
"type_id" : "009-110-123"
}
],
"Subject" : [
{
"code" : "111",
"id" : "2022-2002",
"name" : "Computer Science",
"type_id" : "009-110-123"
}
]
}
Here is Solution which I was written:
func getData(data: RootModel.ResponseFirstModel? = nil) -> [ResultModel]{
let dict: [String : [RootModel.ArrayModel]] = data?.dictionary ?? [:]
var resultArray = [ResultModel]()
var arrayModel = ArrayModel()
for each in dict {
arrayModel = ArrayModel.init(code: each.value.first?.code, id: each.value.first?.id, name: each.value.first?.name, typeId: each.value.first?.typeId)
resultArray.append(.init(name: each.key, values: [arrayModel]))
}
return resultArray
}
//Declare ResultModel Class below.
struct ResultModel {
var name: String?
var values: [ArrayModel]?
}
struct ArrayModel {
var code: String?
var id: String?
var name: String?
var typeId: String?
}

You can't assign a [String: [Any]] dictionary to a [FirstModel] array. They are unrelated types.

Related

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?

Class is not key value coding-compliant for the key, when setting values for an object that has a member which conforms to a custom class

I am trying to use setValuesForKeys on an object that has a member which conforms to a custom class, which is why I believe I get the below error, how could I get around this ? I prefer to do this in a native way if simple or can I use any library/framework to resolve this.
this class is not key value coding-compliant for the key
availabilityTimesDict
func convertJSONDataToSwiftObject(){
let registrationDataDict =
[
"firstName": "Joe Bloggs",
"alternateNames": ["Joe", "Bloggs"],
"availabilityTimesDict" : [
"Friday" : ["times":[["startTime":"9:00 AM","endTime":"1:30 PM"],["startTime":"2:30 PM","endTime":"6:00 PM"]],"weekday":"Friday","available":true],
"Monday" : ["times":[["startTime":"11:00 AM","endTime":"1:30 PM"]], "weekday":"Monday","available":true]]
] as [String : AnyObject]
var profileDetailsObject = ProfileDetails()
profileDetailsObject.setValuesForKeys(registrationDataDict as [String : AnyObject]) // SOURCE OF ERROR
}
class ProfileDetails: NSObject{
var firstName : String!
var alternateNames : [String]!
var availabilityTimesDict : [String : AvailabilityTimes.DailyTimes]!
}
class AvailabilityTimes: NSObject{
struct Times{
var startTime : String?
var endTime : String?
func convertToDictionary() -> Dictionary<String, Any> {
return [
"startTime" : self.startTime as Any,
"endTime" : self.endTime as Any
]
}
}
struct DailyTimes{
let weekday : String
var available : Bool
var times = [Times]()
mutating func update(times: [Times]){
self.times = times
}
func convertToDictionary() -> Dictionary<String, Any> {
return [
"weekday" : self.weekday,
"available" : self.available,
"times" : self.times.map{$0.convertToDictionary()}
]
}
}
}
It is very complicated with the KVO ,
I can clearly see the mistake is with "availabilityTimesDict" :
You Can check it with comment availabilityTimesDict from kvo dict
It is better and more redable if you are using initalizer rather then KVO , it will also helpful in future if any change comes you can do it easily with few modifications
Here is example code
class ProfileDetails: NSObject{
var firstName : String!
var alternateNames : [String]!
var availabilityTimesDict : [String : AvailabilityTimes.DailyTimes]!
private override init() {
super.init()
}
required init(with FirstName : String,alterName:[String],availabilityTimesDict:[String : AvailabilityTimes.DailyTimes]) {
super.init()
self.firstName = FirstName;
self.alternateNames = alterName;
self.availabilityTimesDict = availabilityTimesDict;
}
}
class AvailabilityTimes: NSObject{
struct Times{
var startTime : String?
var endTime : String?
func convertToDictionary() -> Dictionary<String, Any> {
return [
"startTime" : self.startTime as Any,
"endTime" : self.endTime as Any
]
}
}
struct DailyTimes{
let weekday : String
var available : Bool
var times = [Times]()
mutating func update(times: [Times]){
self.times = times
}
func convertToDictionary() -> Dictionary<String, Any> {
return [
"weekday" : self.weekday,
"available" : self.available,
"times" : self.times.map{$0.convertToDictionary()}
]
}
}
}
var availTimes = AvailabilityTimes.Times.init(startTime: "9:00 AM", endTime: "1:30 PM")
var avaiblityTimes = AvailabilityTimes.DailyTimes.init(weekday: "Friday", available: true, times:[availTimes] )
var times = ["Firday":avaiblityTimes]
var profileDetailsObject = ProfileDetails.init(with: "Joe Bloggs", alterName: ["Joe", "Bloggs"], availabilityTimesDict: times)
profileDetailsObject.firstName = "test"

Filter array of objects with multiple criteria and types in Swift

I am trying to do some complex filtering in my app and I am to a point where I don't know what to do next. My data consistes of an array of dictionaries where the values in each of the dictionaries can be String, Int or [String].
let person1: [String : Any] = ["first_name" : "John",
"last_name" : "Smith",
"age" : 21,
"skills" : ["C#", "Java", "Swift"]]
let person2: [String : Any] = ["first_name" : "Kim",
"last_name" : "Smith",
"age" : 28,
"skills" : ["Java", "Swift"]]
let person3: [String : Any] = ["first_name" : "Kate",
"last_name" : "Bell",
"age" : 24,
"skills" : ["C#"]]
var people = [person1, person2, person3]
I let the user choose how to filter this data and create a dictionary of filter criteria. This dictionary can have any number of keys and values.
let filters: [String : [Any]] = ["age" : [28, 24],
"skills" : ["Java", "Swift"]]
In this example I want to show persons who are age 28 or 24 and have a skills of Java or Swift, which would be person2
Here is what I have so far but it only works with Int values:
for (key, values) in filters {
var filteredItems = people.filter {
var match = false
for filterValue in values {
if $0[key] as! Int == filterValue as! Int {
match = true
break
}
else {
match = false
}
}
return match
}
people = filteredItems
}
Here's how I would do this:
struct Person {
let firstName: String
let lastName: String
let age: Int
let skills: [String]
enum Filter {
enum FilterType<T: Hashable> {
case one(of: [T])
case all(of: [T])
// Match against a property that's a single value
func matches(_ value: T) -> Bool {
switch self {
case .one(let filterValues): return filterValues.contains(value)
case .all(let filterValues): return filterValues.count == 1 && filterValues[0] == value
}
}
// Match against a property that's a list of values
func matches(_ values: [T]) -> Bool {
switch self {
case .one(let filterValues): return !Set(filterValues).intersection(values).isEmpty
case .all(let filterValues): return Set(filterValues).isSuperset(of: values)
}
}
}
case age(is: FilterType<Int>)
case skills(is: FilterType<String>)
func matches(_ p: Person) -> Bool {
switch self {
case .age(let filterValues): return filterValues.matches(p.age)
case .skills(let filterValues): return filterValues.matches(p.skills)
}
}
}
}
extension Array where Element == Person.Filter {
func atLeastOneMatch(_ p: Person) -> Bool {
self.contains(where: { $0.matches(p) })
}
func matchesAll(_ p: Person) -> Bool {
self.allSatisfy { $0.matches(p) }
}
}
let people = [
Person(
firstName: "John",
lastName : "Smith",
age: 21,
skills: ["C#", "Java", "Swift"]
),
Person(
firstName: "Kim",
lastName : "Smith",
age: 28,
skills: ["Java", "Swift"]
),
Person(
firstName: "Kate",
lastName: "Bell",
age: 24,
skills: ["C#"]
),
]
let filters: [Person.Filter] = [
.age(is: .one(of: [28, 24])),
.skills(is: .one(of: ["Java", "Swift"])),
]
let peopleWhoMatchAllFilters = people.filter(filters.matchesAll)
print(peopleWhoMatchAllFilters)
let peopleWhoMatchAtLeastOneFilter = people.filter(filters.atLeastOneMatch)
print(peopleWhoMatchAtLeastOneFilter)
I've extended the filtering capability to be able to specify wether all values of a filter should be matched (e.g. a person must know Java AND Swift AND C#) or at least one (e.g. a person must know AT LEAST Java OR Swift OR C#)

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.

how can I construct the alamofire query based on array entries in Swift?

I have a class SingleUser:
class SingleUser: NSObject, NSCoding {
var username: String
var display_name: String
var facebook_username: String
var device_id: String
.
.
.
}
and I have an array of those users:
let blockedUsers: [SingleUser] = NSKeyedUnarchiver.unarchiveObject(with:
(defaults.object(forKey: "myUsers") as! Data)) as! [SingleUser]
I want to pass it with Alamofire to my webservice.
I have already some parameters defined:
var params = [
"long": lon as AnyObject,
"lat": lat as AnyObject,
"startDate": DateCalc.convertValueToDate(defaults.float(forKey: "startDate")) as AnyObject,
"endDate": DateCalc.convertValueToDate(defaults.float(forKey: "endDate")) as AnyObject
] as [String: AnyObject]
and now I want to append my users somehow, e.g. as:
"users" = {
{ "username" = "xxxx",
"display_name" = "xxxx",
"facebook_username" = "xxxx",
"device_id" = "xxxx"
},
{ "username" = "yyyy",
"display_name" = "yyyy",
"facebook_username" = "yyyy",
"device_id" = "yyyy"
},
.
.
.
}
Can you tell me how could I iterate over that array and append it to my params array?
class SingleUser: NSObject, NSCoding {
var username: String
var display_name: String
var facebook_username: String
var device_id: String
var jsonFormat: [String: AnyObject] {
return ["username": username,
"display_name": display_name,
"facebook_username": facebook_username,
"device_id": device_id]
}
}
And then:
var users = [[String: AnyObject]]()
blockedUsers.forEach { user in
users.append(user.jsonFormat)
}
params["users"] = users