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
}
I'm working in swift 4.0, i have model object in MutableArray,
NSPredicate not working.
//Here is code
//Model class:
class Modelclass: NSObject
{
var firstName:String!
var lastName:String!
}
// Viewcontroller doing predicate:
let ary = NSMutableArray()
let userModel = Modelclass()
userModel.firstName = "Arrya"
userModel.lastName = "stark"
ary.add(userModel)
let commitPredicate = NSPredicate(format: "firstName == %#", "Arrya")
let resultAry = ary.filtered(using: commitPredicate)
print(resultAry)
I solved the problem,
Solution 1:
//I just added below code in model class
extension Modelclass {
#objc override func value(forKey key: String) -> Any? {
switch key {
case "firstName":
return firstName
case "lastName":
return lastName
default:
return nil
}
}
}
Solution 2:
Add #objc before variable:
class Modelclass: NSObject
{
#objc var firstName:String!
#objc var lastName:String!
}
var is introduced for Mutable Content/Variables
In swift you can use filter alternative to NSPredicate
Try Like this:
//Model class:
class Modelclass
{
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
// Viewcontroller doing predicate:
var ary:[Modelclass] = []
let userModel = Modelclass(firstName: "Arrya", lastName: "stark")
ary.append(userModel)
let resultAry = ary.filter{ $0.firstName == "Arrya" }
print(resultAry)
Unrecognized Selector to instance name.
I want to create partion array from section. I am trying to do this in swift 2, but I am not able to get it to work.
var currentCollation : UILocalizedIndexedCollation!
var sections: [Section] {
let selector: Selector = "name"
let users: [User] = array.map { name in
let a = name["fullName"] as? String
let b = name["email"] as! String
let c = name["mobile"] as! String
let d = name["img"] as! String
let user = User(name: a! )
user.email = b
user.mobile = c
user.img = d
user.section = UILocalizedIndexedCollation.current().section(for: user, collationStringSelector:"name")
return user
}
var sections = [Section]()
for _ in 0..<currentCollation.sectionIndexTitles.count {
sections.append(Section())
}
for user in users {
sections[user.section!].addUser(user: user)
}
print(sections)
for section in sections {
print(section.users)
var user = section.users as? User
print(user?.name)
section.users = self.currentCollation.sortedArray(from: section.users, collationStringSelector: "name") as! [User]
}
return sections
}
#objc class User: NSObject {
let name: String
var section: Int?
var img: String?
var email: String?
var mobile : String?
init(name: String) {
self.name = name
}
}
class Section {
var users: [User] = []
func addUser(user: User) {
self.users.append(user)
}
}
Don't use strings for selectors in Swift. Use the #selector construct:
let selector: Selector = #selector(name)
However, that will only work if the current class is an NSObject and has an Objective-C function "name" that has no parameters:
class Foo: NSObject {
#objc func name() {
print("In \(#function)")
}
}
(Actually, you can create a selector to an object that doesn't inherit from NSObject, but you can't use functions like perform() on such a selector, so it isn't very useful)
You don't appear to have a function named "name" in your class, of type #objc or not.
I have this class:
class AClass {
var name = "Name"
var value = 0
static var A = AClass(name: "A", value: 1)
static var B = AClass(name: "B", value: 2)
}
I have a function that returns the name of A and B, how do I get A and B as AClass instances WITH string A and B, like this?
func someMethod(name: String) -> AClass
// func("A") will return A: AClass
UPDATE:
I have many type properties in my actual project, so I'm looking for a convenient way to get 'em all without having to create a value of each type property.
class AClass {
var name = "Name"
var value = 0
static var A = AClass(name: "A", value: 1)
static var B = AClass(name: "B", value: 2)
init(name:String, value:NSInteger){
self.name = name
self.value = value
}
}
Get it like this:
let aObj = AClass.A
let bObj = AClass.B
print(aObj.name)
print(bObj.name)
Try this:
class AClass
{
var name = "Name"
var value = 0
init(name : String, value : Int)
{
self.name = name
self.value = value
}
static var A = AClass(name: "A", value: 1)
static var B = AClass(name: "B", value: 2)
}
To get A and B as AClass instances use:
let objA = AClass.A
print(objA.name)
let objB = AClass.B
print(objB.name)
I guess this is what you are looking for
class AClass {
var name = "Name"
var value = 0
static var dataDict = [String: AClass]()
init(name: String) {
self.name = name
}
static func someMethod(name: String) -> AClass {
if let obj = AClass.dataDict[name] {
return obj
} else {
AClass.dataDict[name] = AClass(name: name)
return AClass.dataDict[name]!
}
}
}
Get object of AClass like:
let obj = AClass.someMethod(stringReturnValue) // AClass.someMethod("A")
// here if already AClass object exist with this key in AClass.dataDict then it will return that object else created one and store it in dataDict.
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.