Swift call AnyClass class function - ios

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.

Related

Swift Inheritance(Extending classes)

i have two classes ClassA,ClassB,ClassC and ClassC is extended from ClassA
open class classA: NSObject{
var firstName = String()
var lastName = String()
}
open class classB: NSObject{
public func getObject(dictionary : Dictionary<String,Any>) -> Dictionary<String,Any>{
var Dict : [String: Any] = [:]
var ListArray = Array<Any>()
let tempArray = dictionary["data"] as! Array<Any>
for item in 0..<tempArray.count{
let dict = tempArray[item] as! NSMutableDictionary
let myclass = classA()
if let val = dict["firstName"] as? String{
myclass.firstName = val
}else if let val = dict["lastName"] as? String{
myclass.lastName = val
}
ListArray.append(myclass)
}
Dict["data"] = ListArray
return Dict
}
}
if i extend the ClassC like this
public ClassC : ClassA{
var age = String()
var address = String()
}
Is there any way to use this ClassC variables in class B function getObjects ?
i can't move ClassC variables to ClassA.
Can any one help me to achieve this.
Thanks in Advance.
You can use the type as value and pass it as a parameter:
class ClassB {
func someFunc(A: ClassA.Type) {
let classA = A.init(f: "firstName", l: "lastName")
print(classA)
}
}
You'll have to pass the type like this:
let b = ClassB()
b.someFunc(A: ClassA.self)
Swift's polymorphism applies here which means even if you pass ClassC, it'll be permitted:
let b = ClassB()
b.someFunc(A: ClassC.self)
You'll have to have a required initializer for the subclasses:
open class ClassA: NSObject{
var firstName: String?
var lastName: String?
required public init(f: String = "", l: String = "") {
self.firstName = f
self.lastName = l
}
}
class ClassC : ClassA {
var age = String()
var address = String()
}

How to call the correct constructor when using generic T.Type class on Swift4?

I have a database with some tables, each table represents a object of my project. I want write a generic function to read, by SQL, a table and create a object with the records readed. So, the parameters of my function are: Table Name and Object Type. The code below is my func to do this. In the end of func, I tries call what I would like to do, but with a especific object, that's don't the I want.
func readAll<T>(objeto: String, typeClass: T.Type) -> [T] {
var ret : [T] = []
// STATEMENT DATA
let queryString = "SELECT * FROM \(objeto);"
var queryStatement: OpaquePointer? = nil
// STATEMENT DATA TYPE
let queryString2 = "PRAGMA table_info(\(objeto));"
var queryStatement2: OpaquePointer? = nil
// 1
if sqlite3_prepare_v2(db,queryString,-1,&queryStatement,nil) != SQLITE_OK {
print("Error to compile readAll \(objeto) 1")
return ret
}
if sqlite3_prepare_v2(db,queryString2,-1,&queryStatement2,nil) != SQLITE_OK {
print("Error to compile readAll \(objeto) 2")
return ret
}
var listNameColumns : [String] = []
while( sqlite3_step(queryStatement2) == SQLITE_ROW ) {
listNameColumns.append( String(cString: sqlite3_column_text(queryStatement2, 2)!) )
}
// 2
while ( sqlite3_step(queryStatement) == SQLITE_ROW ) {
var dict: [String:Any] = [:]
for i in 0...listNameColumns.count-1 {
let nameColumn = String(cString: sqlite3_column_name(queryStatement,Int32(i))!)
switch (sqlite3_column_type(queryStatement, Int32(i))) {
case SQLITE_TEXT:
dict[nameColumn] = String(cString: sqlite3_column_text(queryStatement, Int32(i))!)
break
case SQLITE_INTEGER:
dict[nameColumn] = sqlite3_column_int(queryStatement, Int32(i))
break
case SQLITE_FLOAT:
dict[nameColumn] = sqlite3_column_double(queryStatement, Int32(i))
break
default:
print("Tipo desconhecido.")
break
}
}
ret.append(ResPartner(dict: dict)) <------ HERE IS MY QUESTION!
}
// 3
sqlite3_finalize(queryStatement2)
sqlite3_finalize(queryStatement)
return ret
}
Here are two objects, They are a bit different, but the builder works the same and the fields as well.
class ResPartner {
static let fieldsResPartner : [String] = ["id","company_type_enum_for_customer","name","contact_address","customer_account_number","customer_group_id","segment_id","subsegment_id","economic_group_id","street","category_id","type_stablishment_id","final_user","final_taxpayer","cnpj_cpf","inscr_est","ccm","cnae","phone","phone_extension","mobile","fax","email","email_extra","website","lang"]
var attributes : [String:Any] = [:]
init(dict : [String:Any]) {
for k in dict.keys {
if(ResPartner.fieldsResPartner.contains(k)) {
attributes[k] = dict[k]
}
}
}
func toString() {
for k in attributes.keys{
print("\(k) - \(attributes[k]!)")
}
}
}
class Product {
static let fieldsProducts : [String] = ["id","name","default_code","display_name","categ_id","company_ax_id","destination_type","fiscal_class_code","multiple","taxes_id","uom_id","uom_po_id","__last_update","active","create_date","create_uid","currency_id","invoice_police","item_ids","list_price","price","pricelist_id","type"]
public var default_code: String!
public var display_name: String!
public var id: Int!
public var name: String!
public var destination_type: String!
public var company_ax_id: Int!
public var categ_id: Int!
public var fiscal_class_code: String!
public var taxes_id: Int!
public var uom_id: Int!
public var uom_po_id: Int!
public var multiple: Int!
public var last_update: String!
public var active: Bool!
public var create_date: String!
public var create_uid: Int!
public var currency_id: Int!
public var invoice_police: String!
public var item_ids: [Int]!
public var list_price: String!
public var price: Float!
public var pricelist_id: Int!
public var type: String!
init() {
}
init( dict : [String:Any] ) {
self.default_code = dict["default_code"] as! String
self.display_name = dict["display_name"] as! String
self.id = dict["id"] as! Int
self.name = dict["name"] as! String
self.destination_type = dict["destination_type"] as! String
self.company_ax_id = dict["company_ax_id"] as! Int
self.categ_id = dict["categ_id"] as! Int
self.fiscal_class_code = dict["fiscal_class_code"] as! String
self.taxes_id = dict["taxes_id"] as! Int
self.uom_id = dict["uom_id"] as! Int
self.uom_po_id = dict["uom_po_id"] as! Int
self.multiple = dict["multiple"] as! Int
self.last_update = dict["last_update"] as! String
self.active = dict["active"] as! Bool
self.create_date = dict["create_date"] as! String
self.create_uid = dict["create_uid"] as! Int
self.currency_id = dict["currency_id"] as! Int
self.invoice_police = dict["invoice_police"] as! String
self.item_ids = dict["item_ids"] as! [Int]
self.list_price = dict["list_price"] as! String
self.price = dict["price"] as! Float
self.pricelist_id = dict["pricelist_id"] as! Int
self.type = dict["type"] as! String
}
}
So, my question is, How I call the constructor of T.Type class passed by parameter? I did read about protocols, extensions, other posts, but not solves my problem.
You can constrain your generic with protocol:
Define a protocol for initializing with a dictionary:
protocol DictionaryInitializable {
init(dict: [String: Any])
}
Make your two types conform to that type (you'll have to add required to your init methods, as prompted by Xcode), e.g.:
class Product: DictionaryInitializable {
...
required init(dict: [String: Any]) {
...
}
}
and
class ResPartner: DictionaryInitializable {
static let fieldsResPartner = ...
var attributes: [String: Any] = [:]
required init(dict: [String: Any]) {
for k in dict.keys where ResPartner.fieldsResPartner.contains(k) {
attributes[k] = dict[k]
}
}
func toString() { ... }
}
Change your method declaration to make it clear that T must conform to your new protocol:
func readAll<T: DictionaryInitializable>(objeto: String, typeClass: T.Type) -> [T] {
var ret: [T] = []
...
// 2
while ( sqlite3_step(queryStatement) == SQLITE_ROW ) {
var dict: [String: Any] = [:]
...
ret.append(T(dict: dict)) // You can now use `T` here
}
return ret
}
And you’d call it like:
let list = db_respartner.readAll(objeto: "res_partner", typeClass: ResPartner.self)
Create a Protocol with init Method
protocol Mappable {
init(dictionary:[String:AnyObject]) // Changed based on your requirement.
}
Conform your protocol in ResPartner.
class ResPartner: Mappable {
required init(dictionary : [String : AnyObject]) {
// initialize here
}
}
Conform your protocol in Product.
class Product: Mappable {
required init(dictionary : [String : AnyObject]) {
// initialize here
}
}
Create a custom objects
func readAll<T:Mappable>(objeto: String, typeClass: T.Type) -> [T] {
var ret : [T] = []
// intialize your variable
let obj = typeClass.init(dictionary: ["":"" as AnyObject])
return ret
}

How to store an array that is within an another array to a global variable that has been passed from JSON in Swift?

I have a JSON which receives an array from an API call
Within that array are 3 other arrays:
userDetails, userStats, communities
An example of this API call is:
["communities": <__NSArrayI 0x6000002540a0>(
{
id = 5;
name = South;
},
{
id = 13;
name = HurraHarry;
},
{
id = 15;
name = EnclliffeT;
}
)
, "userStats": {
totalDraws = 3;
totalLosses = 10;
totalWins = 1;
}, "userDetails": {
id = 31;
"user_email" = "steve#gmail.com";
"user_name" = "Steve Base";
}]
I would like to store the array userStats in a variable that I can pass to another VC.
I have a global variable var userStatsArray = [AnyObject]() in my class
and the following code deals with the JSON:
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json!)
if let arr = json?["communities"] as? [[String:String]] {
self.communitiesArray = arr.flatMap { $0["name"]!}
self.communityIdsArray = arr.flatMap { $0["id"]!}
}
if let dict = json?["userDetails"] as? [String:String] {
self.tempPlayerId = [dict["id"]!]
let characterArray = self.tempPlayerId.flatMap { String.CharacterView($0) }
let newPlayerId = String(characterArray)
self.playerId = newPlayerId
}
if let tempArray = json?["userStats"] as? [String:AnyObject]{
print ("here ", tempArray)
}
The print command successfully prints the userStats array with all its headers (totalWins, totalDraws, totalLosses...) -
How do I store this array into my global variable var userStatsArray = [AnyObject]() so I can pass it to another VC?
Better you create one custom class like this, and declare the array with that custom class type. then you cast your userStats object to your custom class type.
class userStats: NSObject {
var totalDraws: NSNumber?
var totalLosses: NSNumber?
var totalWins: NSNumber?
init(totalDraws: NSNumber?, totalLosses: NSNumber?, totalWins: NSNumber?) {
self.totalDraws = totalDraws
self.totalWins = totalWins
self.totalLosses = totalLosses
}
}
var userStatsArray = [userStats]()
// CHANGE YOUR CODE LIKE THIS
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json!)
if let arr = json?["communities"] as? [[String:String]] {
self.communitiesArray = arr.flatMap { $0["name"]!}
self.communityIdsArray = arr.flatMap { $0["id"]!}
}
if let dict = json?["userDetails"] as? [String:String] {
self.tempPlayerId = [dict["id"]!]
let characterArray = self.tempPlayerId.flatMap { String.CharacterView($0) }
let newPlayerId = String(characterArray)
self.playerId = newPlayerId
}
if let tempArray = json?["userStats"]as? userStats {
userSytatsArray.append(tempArray)
}
Take a look at ObjectMapper! With that powerful framework you can create the mappable models of your data returned by the API and let it perform the whole work for you :)
Declare your model classes like this:
class UserInfo: Mappable {
var communities : [Community]?
var stats: UserStats?
var details: UserDetails?
required init?(map: Map) {
}
func mapping(map: Map) {
communities <- map["communities"]
stats <- map["userStats"]
details <- map["userDetails"]
}
}
class Community: Mappable {
var id: Int!
var name: String!
required init?(map: Map) {
}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
}
}
class UserStats: Mappable {
var totalDraws : Int!
var totalLosses : Int!
var totalWins : Int!
required init?(map: Map) {
}
func mapping(map: Map) {
totalDraws <- map["totalDraws"]
totalLosses <- map["totalLosses"]
totalWins <- map["totalWins"]
}
}
class UserDetails: Mappable {
var id : Int!
var email : String!
var username : String!
required init?(map: Map) {
}
func mapping(map: Map) {
id <- map["id"]
email <- map["user_email"]
username <- map["user_name"]
}
}
And later just:
let user = UserInfo(JSONString: JSONString)

How to return a JSONObject as a String in swift?

I would like to create a new object called RegisterIn, the target of the object is to generate a json object as dictionary and return as a String
Here is my code
public class RegisterIn {
private var a : String = "" //required
private var b: String = "" //required
private var c: String = "" //required
private var d: Int = 0 //required
private let BD_a : String = "a"
private let BD_b : String = "b"
private let BD_c : String = "c"
private let BD_d : String = "d"
init(a: String, b: String, c: String, d: Int) {
self.a = a
self.b = b
self.c = c
self.d = d
}
func getJSONObject() {
let jsonDic : [String: AnyObject] = [
BD_a: a,
BD_b: b,
BD_c: c,
BD_d: d
]
do {
let jsonObject = try NSJSONSerialization.dataWithJSONObject( jsonDic, options: NSJSONWritingOptions.PrettyPrinted)
} catch let error as NSError {
print(error)
}
}
func toString() {
return String(getJSONObject()) <- this line occur error
}
}
At function getJSONObject, I think it return a jsonObject as [String: AnyObject]. In my ViewController, I want to assign it to a Label.text, it always
my ViewController:
#IBOutlet weak var jsonLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let a = RegisterIn.init(a: "123", b: "456", c: "789", d: 00000)
jsonLabel.text = a
}
I think I have to change some code in RegisterIn class, really need some help!
you never returned a string from getJSONObject(), try
func getJSONObject() -> String? {
let jsonDic : [String: AnyObject] = [
BD_a: a,
BD_b: b,
BD_c: c,
BD_d: d
]
do {
let jsonObject = try NSJSONSerialization.dataWithJSONObject( jsonDic, options: NSJSONWritingOptions.PrettyPrinted)
return NSString(data: jsonObject, encoding:NSUTF8StringEncoding)
} catch let error as NSError {
print(error)
return nil
}
}
func toString() {
return getJSONObject() //to be more correct, but this function is sort of redundant, just call getJSONObject directly, but up to you whats the best
}
It may be
"let jsonObject = try NSJSONSerialization.dataWithJSONObject( jsonDic, options: NSJSONWritingOptions.PrettyPrinted)"
has problem.
You can make options with null for the replace the PrettyPrinted.
"options:[])"

fatal Error Unexpectedly while unwrapping an optional value while loading Comments

Hi guys i have been stuck in this problem for nearly 2 week
The Problem is that i am getting the error fatal Error Unexpectedly while unwrapping an optional value while i am loading comments..
I am trying to load JSON data For the Comments
in PopularShotsCollectionViewController
import UIKit
import FMMosaicLayout
private let reuseIdentifier = "Cell"
class PopularShotsCollectionViewController: UICollectionViewController {
private var shots : [Shot] = [Shot](){
didSet{
self.collectionView?.reloadData()
}
}
var API_URL = Config.SHOT_URL
var shotPages = 1
var shot : Shot!
var comments : [Comment] = [Comment]()
var tableView : UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let layout : FMMosaicLayout = FMMosaicLayout()
self.collectionView?.collectionViewLayout = layout
// Do any additional setup after loading the view.
self.title = "Popular"
self.API_URL = Config.POPULAR_URL
self.loadShots()
self.loadComments()
}
func loadComments(){
// When i debug shot.commentsUrl does not return nil
DribbleObjectHandler.getComments(shot.commentsUrl) { (comments) -> Void in
self.comments = comments
}
}
But i am getting this error:
The code for the getCommentsMethod
class func getComments(commentsUrl : String, completion:(([Comment]) -> Void)) {
var comments = [Comment]()
let url = commentsUrl + "&access_token=" + Config.ACCESS_TOKEN
HttpService.getJSON(url) { (jsonData) -> Void in
for commentData in jsonData {
let comment = Comment(data: commentData as! NSDictionary)
comments.append(comment)
}
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0), { () -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(comments)
})
})
}
}
Code for the HttpService
import Foundation
import UIKit
import Alamofire
class HttpService {
class func getJSON(url: String, callback:((NSArray) -> Void)) {
let nsURL = NSURL(string: url)!
Alamofire.request(.GET, nsURL).response { (request, response, data, error) -> Void in
if error != nil{
print("error")
}
if data != nil {
let jsonData = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSArray
print(jsonData)
callback(jsonData)
}
}
}
}
If you need more Code to solve the problem i am more than happy
And in PopularShotsCollectionViewController i have been loading shots also
with the same method
func loadShots(){
DribbleObjectHandler.getShots(API_URL) { (shots) -> Void in
self.shots = shots
}
}
but i get no error and it works perfectly
and code for the get Shots is same.. Just we access The Shot class in loadShots and we access The Comment Class in loadComments
The Comment Class
import Foundation
import UIKit
class Comment {
var id : Int!
var body : String!
var date : String!
var user : User!
init(data : NSDictionary){
self.id = data["id"] as! Int
let bodyHTML = Utils.getStringFromJSON(data, key: "body")
self.body = Utils.stripHTML(bodyHTML)
let dateInfo = Utils.getStringFromJSON(data, key: "created_at")
self.date = Utils.formatDate(dateInfo)
if let userData = data["user"] as? NSDictionary {
self.user = User(data: userData)
}
}
}
Code for the Shot Class
import Foundation
class Shot: DribbbleBase {
var imageUrl : String!
var htmlUrl : String!
var commentsUrl : String!
var bucketsUrl : String!
var likesUrl : String!
var attachmentUrl : String!
var reboundUrl : String!
var title : String!
var date : String!
var description : String!
var commentCount : Int!
var viewsCount : Int!
var likesCount : Int!
var bucketsCount : Int!
var attachmentsCount : Int!
var reboundCount : Int!
var user : User!
override init(data: NSDictionary) {
super.init(data: data)
self.commentCount = data["comments_count"] as! Int
self.likesCount = data["likes_count"] as! Int
self.viewsCount = data["views_count"] as! Int
self.bucketsCount = data["buckets_count"] as! Int
self.attachmentsCount = data["attachments_count"] as! Int
self.reboundCount = data["rebounds_count"] as! Int
self.commentsUrl = Utils.getStringFromJSON(data, key: "comments_url")
self.bucketsUrl = Utils.getStringFromJSON(data, key: "buckets_url")
self.likesUrl = Utils.getStringFromJSON(data, key: "likes_url")
self.title = Utils.getStringFromJSON(data, key: "title")
self.attachmentUrl = Utils.getStringFromJSON(data, key: "attachments_url")
self.reboundUrl = Utils.getStringFromJSON(data, key: "rebounds_url")
let dateInfo = Utils.getStringFromJSON(data, key: "created_at")
self.date = Utils.formatDate(dateInfo)
let desc = Utils.getStringFromJSON(data, key: "description")
self.description = Utils.stripHTML(desc)
let images = data["images"] as! NSDictionary
self.imageUrl = Utils.getStringFromJSON(images, key: "normal")
//let tags = data["tags"] as! NSArray
if let userData = data["user"] as? NSDictionary {
self.user = User(data: userData)
}
}
}
and Code for DribbbleBase
import Foundation
class DribbbleBase {
var id: Int
init(data: NSDictionary){
self.id = data["id"] as! Int
}
}
Please help me.. And Please Bear in mind i am fairly new to Swift
Thanks in Advance
Aryan
you declared shot property in your PopularShotsCollectionViewController and you didn't initialize it so shot is be nil. Then you tried to access commentsUrl property of shot which is nil in the loadComments method. You should instantiate shot in the init or view did load method of your view controller

Resources