Serializing custom bag object to json string int to Bool in swift - ios

In my project, there is a bag object which was declared as static and when a customer click the "approve order" button, I serialize the bag object to JSON String. There is no problem so far. However, when I print the serialized json string, all of the "IDs" with Int in the bag converted to Bool type in json string result.
The related code blocks for this process as below:
This is my "Serialize" class:
public class Serializable : NSObject
{
func toDictionary() -> NSDictionary
{
var aClass : AnyClass? = self.dynamicType
var propertiesCount : CUnsignedInt = 0
var propertiesInAClass : UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(aClass, &propertiesCount)
var propertiesDictionary : NSMutableDictionary = NSMutableDictionary()
for(var i = 0; i < Int(propertiesCount); i++)
{
var property = propertiesInAClass[i]
var propName = NSString(CString: property_getName(property), encoding: NSUTF8StringEncoding)
var propType = property_getAttributes(property)
var propValue : AnyObject! = self.valueForKey(propName!)
if(propValue is Serializable)
{
propertiesDictionary.setValue((propValue as Serializable).toDictionary(), forKey: propName!)
}
else if(propValue is Array<Serializable>)
{
var subArray = Array<NSDictionary>()
for item in (propValue as Array<Serializable>)
{
subArray.append(item.toDictionary())
}
propertiesDictionary.setValue(subArray, forKey: propName!)
}
else if(propValue is NSData)
{
propertiesDictionary.setValue((propValue as NSData).base64EncodedStringWithOptions(nil), forKey: propName!)
}
else if(propValue is Bool)
{
propertiesDictionary.setValue((propValue as Bool).boolValue, forKey: propName!)
}
else if(propValue is NSDate)
{
var date = propValue as NSDate
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "Z"
var dateString = NSString(format: "/Date(%.0f000%#)/", date.timeIntervalSince1970, dateFormatter.stringFromDate(date))
propertiesDictionary.setValue(dateString, forKey: propName!)
}
else
{
propertiesDictionary.setValue(propValue, forKey: propName!)
}
}
return propertiesDictionary
}
func toJson() -> NSData!
{
var dictionary = self.toDictionary()
var err: NSError?
return NSJSONSerialization.dataWithJSONObject(dictionary, options:NSJSONWritingOptions(0), error: &err)
}
func toJsonString() -> NSString!
{
return NSString(data: self.toJson(), encoding: NSUTF8StringEncoding)
}
override init()
{
}
}
This is my BagItem class:
class BagItem: Serializable, Hashable {
var uniqueID: Int = 0
override var hashValue: Int { return uniqueID.hashValue }
var bagItemId: String
var item: Item
var boughtDate: NSDate!
var boughtTime: String
var branch: Branch
var isMainItem: Bool
override init()
{
self.bagItemId = ""
self.item = Item()
self.boughtDate = NSDate()
self.boughtTime = ""
self.branch = Branch()
self.isMainItem = false
}
}
func ==(lhs: BagItem, rhs: BagItem) -> Bool
{
return lhs.uniqueID == rhs.uniqueID
}
This is my "SerializationBag" class:
class SerializableBag: Serializable
{
var bag: Array<BagItem> = []
override init()
{
}
}
This is my "ConvertBagToJSON" method in Bag class:
static func ConvertBagToJson() -> NSString
{
var serializer: SerializableBag = SerializableBag()
serializer.bag = self.bag
return serializer.toJsonString()
}
And my returned JSON String result as below:
{
"bag": [
{
"branch": {
"city": {
"cityId": false,
"cityName": ""
},
"town": {
"townName": "",
"townId": false
},
"branchName": "Branch",
"branchId": true,
"business": {
"businessPhotoPath": "",
"businessName": "",
"businessId": true
},
"branchAddress": "Some Address",
"branchTelephone": ""
},
"uniqueID": false,
"boughtDate": "/Date(1414581909000+0200)/",
"item": {
"itemName": "Pencil",
"itemId": true,
"itemPrice": true
},
"isMainItem": true,
"bagItemId": "9674D47B-0D2F-46CC-BA16-754875AE277D",
"hashValue": false,
"boughtTime": "00:30"
}
]
}
As you see, in JSON String, IDs are Bool, but they have to be in Int type. How can I solve this problem ?
Thank you for your answers
Best regards

This is because Int is bridged to NSNumber and NSNumber is always is Bool.
In your case, you don't need these lines:
else if(propValue is Bool)
{
propertiesDictionary.setValue((propValue as Bool).boolValue, forKey: propName!)
}
You can just delete them, because NSJSONSerialization can handle it.
let flg:NSNumber = true
let id:NSNumber = 1
let dict:NSDictionary = [
"bool": flg,
"id": id
]
let jsonDat = NSJSONSerialization.dataWithJSONObject(dict, options: .allZeros, error: nil)!
let jsonStr = NSString(data: dat, encoding:NSUTF8StringEncoding)
// -> {"id":1,"bool":true}
more relevant example:
class Foo:NSObject {
var flg:Bool = true
var id:Int = 1
}
let obj = Foo()
let dict:NSDictionary = [
"flg": obj.valueForKey("flg")!,
"id": obj.valueForKey("id")!
]
let jsonData = NSJSONSerialization.dataWithJSONObject(dict, options: .allZeros, error: nil)!
let jsonStr = NSString(data: jsonData, encoding:NSUTF8StringEncoding)!
// -> {"flg":true,"id":1}

Related

Realm filter for both parent and child classes

I want to implement filter on both the parent and child, as if search 'chicken2' result should return only lines with meal as 'chicken2' + meals with name 'chicken2', below are my model classes with query and result.
import Foundation
import RealmSwift
class Canteen: Object {
#objc dynamic var name: String?
let lines = List<Line>()
func initWithJSON(json: [String: Any]) {
self.name = json["name"] as? String
let lines = json["lines"] as! [[String: Any]]
for lineJSON in lines {
let line = Line()
line.initWithJSON(json: lineJSON)
self.lines.append(line)
}
}
override static func primaryKey() -> String? {
return "name"
}
}
class Line: Object {
#objc dynamic var name: String?
var meals = List<Meal>()
let canteens = LinkingObjects(fromType: Canteen.self, property: "lines")
func initWithJSON(json: [String: Any]) {
self.name = json["name"] as? String
let meals = json["meals"] as! [[String: Any]]
for mealJSON in meals {
let meal = Meal()
meal.initWithJSON(json: mealJSON)
self.meals.append(meal)
}
}
override static func primaryKey() -> String? {
return "name"
}
}
class Meal: Object {
#objc dynamic var name: String?
#objc dynamic var vegan: Bool = false
let lines = LinkingObjects(fromType: Line.self, property: "meals")
func initWithJSON(json: [String: Any]) {
self.name = json["name"] as? String
self.vegan = json["isVegan"] as! Bool
}
override static func primaryKey() -> String? {
return "name"
}
}
Below is my controller class's viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
let file = Bundle.main.path(forResource: "mealss", ofType: ".json")
let data = try! Data(contentsOf: URL(fileURLWithPath: file!))
let json = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
if let dict = json as? [String: Any] {
let canteen = Canteen()
canteen.initWithJSON(json: dict)
try! realm.write {
realm.add(canteen, update: true)
}
}
realm.objects(Line.self).filter("ANY meals.name contains 'chicken2'")
print(lines.description)
}
below is the output of my print statement.
Below is the json file which i have used.
{
"name": "canteen1",
"lines": [
{
"name": "line1",
"meals": [
{
"name": "chicken2",
"isVegan": false
},
{
"name": "egges",
"isVegan": false
}
]
},
{
"name": "line2",
"meals": [
{
"name": "chicken",
"isVegan": true
},
{
"name": "egges",
"isVegan": true
}
]
}
]
}
Below is my expected output.
[Line {
name = line1;
meals = List<Meal> <0x281301b90> (
[0] Meal {
name = chicken2;
vegan = 0;
}
);
}]
You can retrieve a Meal object and show its parent object's name if you change the Meal class like this.
class Meal: Object {
#objc dynamic var name: String?
#objc dynamic var vegan: Bool = false
let lines = LinkingObjects(fromType: Line.self, property: "meals")
var line: Line { return lines.first! } // <- added
func initWithJSON(json: [String: Any]) {
self.name = json["name"] as? String
self.vegan = json["isVegan"] as! Bool
}
override static func primaryKey() -> String? {
return "name"
}
}
Instead of Line objects, retrieve Meal objects and access their parent object's name.
let meals = realm.objects(Meal.self).filter("name contains 'chicken2'")
for meal in meals {
print("name = \(meal.line.name!)")
print(meal)
}
Here is the output:
name = line1
Meal {
name = chicken2;
vegan = 0;
}

How to make model class for following JSON response in swift iOS

Hi i am beginner for swift ios and my requirement is have to display Json response to table list i got response from web-services and response seems like below
MY requirement is how to map that model classes to Array and how to display them in tableList can some one help me please
JsonResponse:-
[{
"_id" : "5470def9e0c0be27780121d7",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/5470def9e0c0be27780121d7_180.png",
"name" : "Mondo",
"hasVip" : false,
"location" : {
"city" : "Madrid"
}
}, {
"_id" : "540b2ff281b30f3504a1c72f",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540b2ff281b30f3504a1c72f_180.png",
"name" : "Teatro Kapital",
"hasVippler" : false,
"location" : {
"address" : "Atocha, 125",
"city" : "Madrid"
}
}, {
"_id" : "540cd44581b30f3504a1c73b",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540cd44581b30f3504a1c73b_180.png",
"name" : "Charada",
"hasVippler" : false,
"location" : {
"address" : "La Bola, 13",
"city" : "Madrid"
}
}]
mapping:
Club:-
class Club {
var id: String = ""
var name: String = ""
var imageUrl: String = ""
var hasVip: Bool = false
var desc: String = ""
var location: [Location] = []
}
Location:-
class Location {
var country: String = ""
var city: String = ""
var address: String = ""
var zip: String = ""
var underground: [String] = []
}
NSURlSession code:-
class BackGroundPostCall: NSObject {
var delegate:PostProtocol?
func callPostService(url:String,params:NSDictionary){
print("url is===>\(url)")
let request = NSMutableURLRequest(URL: NSURL(string:url)!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
//Note : Add the corresponding "Content-Type" and "Accept" header. In this example I had used the application/json.
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(params, options: [])
let task = session.dataTaskWithRequest(request) { data, response, error in
guard data != nil else {
print("no data found: \(error)")
return
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSArray {
print("Response: \(json)")
} else {
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)// No error thrown, but not NSDictionary
print("Error could not parse JSON: \(jsonStr)")
}
} catch let parseError {
print(parseError)// Log the error thrown by `JSONObjectWithData`
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: '\(jsonStr)'")
}
}
task.resume()
}
}
For mapping you can use Alamofire's extension ObjectMapper.
Ex:
[{
"_id" : "5470def9e0c0be27780121d7",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/5470def9e0c0be27780121d7_180.png",
"name" : "Mondo",
"hasVip" : false,
"location" : {
"city" : "Madrid"
}
}, {
"_id" : "540b2ff281b30f3504a1c72f",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540b2ff281b30f3504a1c72f_180.png",
"name" : "Teatro Kapital",
"hasVippler" : false,
"location" : {
"address" : "Atocha, 125",
"city" : "Madrid"
}
}, {
"_id" : "540cd44581b30f3504a1c73b",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540cd44581b30f3504a1c73b_180.png",
"name" : "Charada",
"hasVippler" : false,
"location" : {
"address" : "La Bola, 13",
"city" : "Madrid"
}
}]
And mapper class:
import ObjectMapper
class Location: Mappable {
var address: String?
var city: String?
required init?(map: Map){
}
func mapping(map: Map) {
address <- map["address"]
city <- map["city"]
}
}
class Club: Mappable {
var id: String?
var imageUrl: Int?
var name: String?
var hasVip: Bool = false
var location: Location?
required init?(map: Map){
}
func mapping(map: Map) {
id <- map["_id"]
imageUrl <- map["imageUrl"]
name <- map["name"]
hasVip <- map["hasVippler"]
location <- map["location"]
}
}
And this way very flexible and transparent to use.
https://github.com/Alamofire/Alamofire
https://github.com/tristanhimmelman/AlamofireObjectMapper
Using example:
Alamofire.request(URL).responseArray { (response: DataResponse<[Club]>) in
let clubs = response.result.value
if let clubs = clubs {
for club in clubs {
print(club.name)
print(club.location.city)
}
}
}
You can make model class using this url : http://www.jsoncafe.com/
open this link and put your json in JSON tab and select any Code Template that is you want. and there is you can also add prefix class name and root class name if you want other wise it is optional. and last click on generate button, your json class is ready.!!
Step1: Create your model like below.
class Club {
var id: String = ""
var name: String = ""
var imageUrl: String = ""
var hasVip: Bool = false
var desc: String = ""
var location = Location()
init?(dictionary:[String:Any],location: Location) {
guard let id = dictionary["_id"],
let name = dictionary["name"],
let imageUrl = dictionary["imageUrl"],
let hasVip = dictionary["hasVippler"]
else {
return nil
}
self.id = id
self.name = name
self.imageUrl = imageUrl
self.hasVip = hasVip
self.location = location
}
}
}
class Location {
var country: String = ""
var city: String = ""
var address: String = ""
var zip: String = ""
init?(dictionary:[String:Any]) {
guard let country = dictionary["country"],
let city = dictionary["city"],
let address = dictionary["address"],
let zip = dictionary["zip"]
else {
return nil
}
self.country = country
self.city = city
self.address = address
self.zip = zip
}
}
}
Step2: Declare your array as below.
var myTargetArray = [Club]()
Step3: Json Parsing.
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSArray {
print("Response: \(json)")
for club in json {
if let clubDictionary = club as? NSDictionary {
if let locationDict = clubDictionary["location"] as? NSDictionary{
if let location = Location(dictionary: locationDict) {
if let clubObj = Club(dictionary: clubDictionary, location:location) {
myTargetArray.append(clubObj)
}
}
}
}
}
} else {
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)// No error thrown, but not NSDictionary
print("Error could not parse JSON: \(jsonStr)")
}
} catch let parseError {
print(parseError)// Log the error thrown by `JSONObjectWithData`
let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("Error could not parse JSON: '\(jsonStr)'")
}
Note: I removed underground array from location model since your json data does not contain it.
Finally you can use your target array as your tableview source. Happy coding...
Model class:
class test : Unboxable {
let id : String
let imageURl : String
let name : String
let hasVip : Bool
let city : String
required init(unboxer: Unboxer) throws {
self.id = unboxer.unbox(key: "id") ?? ""
self.imageURl = unboxer.unbox(key: "imageUrl") ?? ""
self.name = unboxer.unbox(key: "name") ?? ""
self.hasVip = unboxer.unbox(key: "hasVip") ?? false
self.city = (unboxer.unbox(key: "city") ?? nil)!
}
}
parse json:
if let object = json as? [Any] {
// json is an array
var data :[test] = []
for respObject in object {
var dict = respObject as? Dictionary<String,AnyObject>
dict?["city"] = dict?["location"]?["city"] as AnyObject
let result1: [test] = try unbox(dictionaries: [dict!])
data.append(contentsOf: result1)
}
print(data)
For more details follow
https://github.com/JohnSundell/Unbox
**Api call with model class, swiftyjson and alamofire,Pod install alamofire and swiftyjson libraries, create collection view cell class, Create a class and write the following code
**
import UIKit
import Alamofire
import SwiftyJSON
var myResponse : JSON? = nil
var users : [reasonList] = []
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
feedbackApi()
}
func feedbackApi(){
DispatchQueue.main.async {
let url = URL(string: "--------")
let urlRequest = URLRequest(url: url!)
Alamofire.request(urlRequest)
.responseJSON { response in
switch response.result{
case.success(let data):
print("dddd :",data)
self.myResponse = JSON(data)
print(self.myResponse as Any)
let a = self.myResponse![0]["reasonList"]
print(a)
for i in 0..<a.count{
let single = reasonList(reasonListJson: a[i])
self.users.append(single)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
case .failure(let error):
print("dddd",error)
}
}
}
}
}
create a model class
import Foundation
import SwiftyJSON
class user{
var deviceId = String()
var deviceName = String()
var deviceLocationId = Int()
var locationName = String()
var welcomeText = String()
var reason=[reasonList]()
init(userJson:JSON) {
self.deviceId = userJson["deviceId"].stringValue
self.deviceName = userJson["deviceName"].stringValue
self.deviceLocationId = userJson["deviceLocationId"].intValue
self.locationName = userJson["locationName"].stringValue
self.welcomeText = userJson["welcomeText"].stringValue
self.reason = [reasonList(reasonListJson: userJson["reason"])]
}}
class reasonList{
var reason = String()
var id = Int()
init(reasonListJson:JSON) {
self.reason = reasonListJson["reason"].stringValue
self.id = reasonListJson["id"].intValue
]}
**create a collection view in view **
import UIKit
import Alamofire
import SwiftyJSON
class ReasonViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
//DelayCall()
// Do any additional setup after loading the view.
}
func DelayCall() {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { // Change `2.0` to the desired number of seconds.
_ = self.storyboard?.instantiateViewController(withIdentifier: "HomeViewController")as! HomeViewController
self.navigationController?.popViewController(animated: true)
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return users.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ReasonCollectionViewCell", for: indexPath) as! ReasonCollectionViewCell
let useee = users[indexPath.row]
print(useee)
cell.reasonLabel.text = useee.reason
return cell
}}
You can make model class using this url : https://www.json4swift.com/
Open this link and paste your JSON and select from below option you want. Click generate, it will generate class files you can download and used it in your project.

Why the first completionHandler return the data before all methods are called?

Here is my API code
import UIKit
import Alamofire
import SwiftyJSON
class ItunesApi: NSObject
{
var artistArray:Array <Artist> = []
func downloadData(name:String, _ completionHandler: #escaping (_ result: Array<Artist>) -> Void)
{
Alamofire.request("https://itunes.apple.com/search?term=U2&entity=musicArtist").response
{ response in
if let data = response.data
{
let JsonResult = JSON(data)
self.findDiscography(data: JsonResult)
}
completionHandler(self.artistArray)
}
}
func findDiscography (data: JSON)
{
for (subJson):(String, JSON) in data["results"]
{
if let artistName = subJson.1["artistName"].string
{
print(artistName);
self.downloadDiscography(name: artistName)
}
}
}
func downloadDiscography (name: String)
{
Alamofire.request("https://itunes.apple.com/search?term=U2&entity=album").response
{ response in
if let data = response.data
{
let JsonResult = JSON(data)
self.createDataModel(name: name, data: JsonResult)
}
}
}
func createDataModel (name: String, data: JSON)
{
var albums:Array <Album> = []
for (subJson):(String, JSON) in data["results"]
{
var thumbnail:String = ""
var title:String = ""
var year:String = ""
if let thumbImage = subJson.1["artworkUrl60"].string
{
thumbnail = thumbImage;
}
if let titleString = subJson.1["collectionName"].string
{
title = titleString;
}
if let releaseDate = subJson.1["releaseDate"].string
{
year = releaseDate;
}
let album = Album(_thumbnail: thumbnail, _title: title, _year: year)
albums.append(album)
}
let artist = Artist(_name: name, _musicStyle: "Rock", _albums: albums as NSArray);
self.artistArray.append(artist);
}
}
And I call here in MyClassTableView.m
func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
{
if let artist = searchBar.text
{
self.itunesApi.downloadData(name: artist, { (array) in
print(array);
})
}
}
Why the copmletionHandler return before all method are called? I want to return in first completionHandeler the result of all method but it return before. The self.itunesApi.downloadData return [] instead of an array filled

How To increase index of Array when the value is Dynamic (Swift)

I am Getting a Dynamic Array which Consist (Dictionary with Array ) and want to increase the Index of Array and will go on to Next Dictionary on action.
After Parsing The Value in swapLibs
var currentQuizIndex = 0
func Dataparsed() {
ServiceManager.service(ServiceType.POST, path: urslStr, param: nil, completion: { (sucess, response, error, code) -> Void in
if (code == 200) {
let QuizData = (swapLibs?.valueForKey("quiz") as! NSArray)
let quizData = playInfo.PlayQuizInfo(QuizData[self.currentQuizIndex] as? NSDictionary)
self.playQuizArray.addObject(quizData)
self.playQuizTitle?.text = quizData.quizQuestion
self.playQImage.sd_setImageWithURL(NSURL(string: quizData.quizQImage!))
self.QNumber?.text = "\(self.Qno)/\(self.noofQuestion)"
}
})
}
And The Modal is
class playInfo: NSObject {
var quizId : String? = ""
var quizQId : String? = ""
var quizQImage : String? = ""
var quizQuestion : String? = ""
var quizType : String? = ""
var quizIndex : String? = ""
class func PlayQuizInfo(dict: NSDictionary?) -> playInfo {
let Pinfo = playInfo()
Pinfo.WrapPlayQuiz(dict)
return Pinfo
}
func WrapPlayQuiz(dict: NSDictionary?) {
if dict == nil {
return
}
self.quizId = dict!.objectForKey("quizId") as? String
self.quizIndex = dict!.objectForKey("index") as? String
self.quizQImage = dict!.objectForKey("QuesImage") as? String
self.quizQuestion = dict!.objectForKey("question") as? String
self.quizType = dict!.objectForKey("Type") as? String
self.quizQId = dict!.objectForKey("questionId") as? String
}
}
Here is Structure
{
"quiz":[
{
"quizId":"7295",
"QuesImage":"http:\/\/proprofs.com\/api\/ckeditor_images\/man-approaches-woman1(1).jpg",
"question":"How do you know him?",
"questionId":"216210",
"Type":"PQ",
"index":4,
"keys":[
{
"answerId":"8266",
"option":"He's in one or more of my classes, and we're good friends.",
"AnsImage":"Image Not Available"
},
{ },
{ },
{ },
{ }
]
},
{ },
{ },
{ },
{ },
{ },
{ },
{ },
{ },
{ },
{ },
{ }
]
}
Each Dictionary is Containing same Key As above
Any Help Will Be Appreciated.Thanks.
As I don't have ServiceManager at my end so I have created this code hypothetically. It might solve you issue of saving all data in to one array. It also adds keys in to array as an object.
EDIT 1 : correct QuizKey object array formation. Let me know if any kind of error occurs, as I am unable to test it at my end.
Edit 2: I have made a general ViewController its working perfectly.Try running this View Controller file and you will see the results.
class TestVC: UIViewController {
//An Array similar to the response you are getting from the server
var response:[AnyObject] = [
[
"quizId" : "1111",
"QuesImage" : "http://proprofs.com/api/ckeditor_images/man-approaches-woman1(1).jpg",
"question" : "How do you know him?",
"questionId" : "216210",
"Type" : "PQ",
"index" : 4,
"keys":[
[
"answerId":"8266",
"option":"He's in one or more of my classes, and we're good friends.",
"AnsImage":"Image Not Available"
],
[
"answerId":"8266",
"option":"He's in one or more of my classes, and we're good friends.",
"AnsImage":"Image Not Available"
],
[
"answerId":"8266",
"option":"He's in one or more of my classes, and we're good friends.",
"AnsImage":"Image Not Available"
]
]
],
[
"quizId" : "2222",
"QuesImage" : "http://proprofs.com/api/ckeditor_images/man-approaches-woman1(1).jpg",
"question" : "How do you know him?",
"questionId" : "216210",
"Type" : "PQ",
"index" : 4,
"keys":[
[
"answerId":"8266",
"option":"He's in one or more of my classes, and we're good friends.",
"AnsImage":"Image Not Available"
],
[
"answerId":"8266",
"option":"He's in one or more of my classes, and we're good friends.",
"AnsImage":"Image Not Available"
],
[
"answerId":"8266",
"option":"He's in one or more of my classes, and we're good friends.",
"AnsImage":"Image Not Available"
]
]
]
]
var playQuizArray:[playInfo]! = []
override func viewDidLoad() {
super.viewDidLoad()
print(response)
for dict in response {
self.playQuizArray.append(playInfo.PlayQuizInfo(dict as? [String:AnyObject]))
}
print(self.playQuizArray)
let quiz = self.playQuizArray[0]
print("quizId \(quiz.quizId)")
print("keyAnswerId \(quiz.quizKeys![0].keyAnswerId)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
class playInfo: NSObject {
var quizId : String? = ""
var quizQId : String? = ""
var quizQImage : String? = ""
var quizQuestion : String? = ""
var quizType : String? = ""
var quizIndex : String? = ""
//quizKeys will contain quiz array
var quizKeys : [QuizKey]? = []
class func PlayQuizInfo(dict: [String:AnyObject]?) -> playInfo {
let Pinfo = playInfo()
Pinfo.WrapPlayQuiz(dict)
return Pinfo
}
func WrapPlayQuiz(dict: [String:AnyObject]?) {
if dict == nil {
return
}
self.quizId = dict!["quizId"] as? String
self.quizIndex = dict!["index"] as? String
self.quizQImage = dict!["QuesImage"] as? String
self.quizQuestion = dict!["question"] as? String
self.quizType = dict!["Type"] as? String
self.quizQId = dict!["questionId"] as? String
//add key object array to the quizKeys
if let arrKeys = dict!["keys"] as? [AnyObject] {
for arr in arrKeys {
let key:QuizKey = QuizKey.QuizKeyInfo(arr as? [String : AnyObject])
self.quizKeys?.append(key)
}
}
}
}
class QuizKey: NSObject {
var keyAnswerId : String? = ""
var keyOption : String? = ""
var keyAnsImage : String? = ""
class func QuizKeyInfo(dict: [String:AnyObject]?) -> QuizKey {
let QKeys = QuizKey()
QKeys.WrapQuizKeys(dict)
return QKeys
}
func WrapQuizKeys(dict: [String:AnyObject]?) {
if dict == nil {
return
}
self.keyAnswerId = dict!["answerId"] as? String
self.keyOption = dict!["option"] as? String
self.keyAnsImage = dict!["AnsImage"] as? String
}
}

Swift call AnyClass class function

According to the article http://www.weheartswift.com/swift-objc-magic/
I create a NSObject extension to parse JSON string to object
import Foundation
extension NSObject {
class func fromJson(jsonInfo: NSDictionary) -> Self {
var object = self()
(object as NSObject).load(jsonInfo)
return object
}
func load(jsonInfo: NSDictionary) {
for (key, value) in jsonInfo {
let keyName = key as String
if (respondsToSelector(NSSelectorFromString(keyName))) {
setValue(value, forKey: keyName)
}
}
}
func propertyNames() -> [String] {
var names: [String] = []
var count: UInt32 = 0
var properties = class_copyPropertyList(classForCoder, &count)
for var i = 0; i < Int(count); ++i {
let property: objc_property_t = properties[i]
let name: String = String.fromCString(property_getName(property))!
names.append(name)
}
free(properties)
return names
}
func asJson() -> NSDictionary {
var json:Dictionary<String, AnyObject> = [:]
for name in propertyNames() {
if let value: AnyObject = valueForKey(name) {
json[name] = value
}
}
return json
}
}
I create a Class inherited NSObject
import Foundation
class Weather : NSObject {
var date : String = ""
var currentCity : String = ""
var weather : String = ""
var wind : String = ""
var dayPictureUrl : String = ""
var nightPictureUrl : String = ""
var temperature : String = ""
}
then I build a helper to get API and parse result to Object
func requestApi(url :String, returnType: AnyClass, success: (res: AnyObject)->() ){
var queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
var group = dispatch_group_create()
dispatch_group_async(group, queue) { () -> Void in
var url = NSURL.init(string:url)
var data = NSData.init(contentsOfURL:url!)
let jsonObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments,error:nil)
if let topDict = jsonObj as? NSDictionary {
var obj : AnyObject = returnType.fromJson(topDict)
success(res: obj);
}
}
}
var api : String = "http://somesite.com/jsonapi"
requestApi(api, Weather.self, { (res) -> () in
// finish get the res obj
})
I know how to do it use Objective-C
but Swift I get this error at this line
LINE : var obj : AnyObject = returnType.fromJson(topDict)
ERROR : 'AnyClass' does not h``ave a member named from JSON
I don't know a lot about Swift,
I only want to call returnType class function ,
how can I do it ?
Your returnType is declared as being AnyClass. So of course it does not have a member fromJson. Probably you need to declare is as a class type that has this method.

Resources