how one can fetch a JSON file in the following format:
https://developers.themoviedb.org/3/people/get-popular-people
Alamofire is used to fetch the data from online database.
I don't know exactly how to format the JSON file received so the nested array can have the elements saved in an instance MovieModel(poster: UIImage,
name: String,
rating: Double,
year: String,
posterLink: String,
id: Int)
ERROR: Fatal error: NSArray element failed to match the Swift Array Element type
My code looks like:
class MovieModel {
private var _poster: UIImage!
private var _background: UIImage!
private var _name: String! // original_title
private var _overview: String! // overview
private var _rating: Double! // vote_average
private var _year: String!
private var _posterLink: String!
private var _backgroundLink: String!
private var _genres: [Int]!
private var _id: Int!
init(poster: UIImage,
name: String,
rating: Double,
year: String,
posterLink: String,
id: Int){
_poster = poster
_name = name
_rating = rating
_year = year
_posterLink = posterLink
_id = id
getPosterImage()
}
}
class ActorModel {
private var _poster : UIImage!
private var _birthday : String?
private var _known_for_department : String?
private var _deathday : String?
private var _id : Int!
private var _known_for : [MovieModel]?
private var _name : String!
private var _also_known_as : [String]!
private var _gender : Int!
private var _biography : String?
private var _popularity : Double?
private var _place_of_birth : String?
private var _profile_path : String?
private var _adult : Bool!
private var _imdb_id : String!
private var _homepage : String!
init(poster : UIImage, id : Int, known_for: [MovieModel], name: String, popularity : Double, profile_path : String) {
_poster = poster
_id = id
_known_for = known_for
_name = name
_popularity = popularity
_profile_path = profile_path
getPosterImage()
}
}
func getPopularActors(){
let url = BASE_URL + "/person/popular?" + API_KEY + LANG + "&page=1"
self.actorList = []
Alamofire.request(url).responseJSON {response in
if let result = response.result.value as? Dictionary<String,Any> {
if let list = result["results"] as? [Dictionary<String,Any>] {
for i in list {
self.actorList.append(ActorModel(
poster: UIImage(),
id: i["id"] as! Int,
known_for: i["known_for"] as! [MovieModel],
name: i["name"] as! String,
popularity: i["popularity"] as! Double,
profile_path: "\(self.IMAGE_URL)\(i["profile_path"] as! String)"
))
}
if let del = self.actorDelegate {
del.transferActors(data: self.actorList)
}
}
}
}
}
You should really use Decodable for this. But building on your current solution:
for i in list {
var movieModels = [MovieModel]()
if let knownForArray = i["_known_for"] as? [[String: Any]] {
for knownFor in knownForArray {
movieModels.append(MovieModel(
poster: UIImage(),
name: knownFor["name"] as! String,
rating: knownFor["rating"] as! Double,
... //And so on
))
}
}
self.actorList.append(ActorModel(
poster: UIImage(),
id: i["id"] as! Int,
known_for: movieModels,
name: i["name"] as! String,
popularity: i["popularity"] as! Double,
profile_path: "\(self.IMAGE_URL)\(i["profile_path"] as! String)"
))
}
Related
I have the following class
// LearningItem
class LearningItem : NSObject {
var id: String
var title: String
var subtitle: String?
var image: String
var uploadDate: Int
init(id: String, title: String, image: String, uploadDate: Int) {
self.id = id
self.title = title
self.image = image
self.uploadDate = uploadDate
}
I have another class
// Book.swift
class Book: LearningItem {
var publishDate: String?
var author: String?
var mediaUrl: String?
var video : String?
var tags: [String]?
var lists: [String: AnyObject]?
var readCount: Int
var categories: [String]?
var courses: [String]?
var amazonBuyUrl: String?
var starCount: Int
var read: Bool?
var completed = [String : Int]()
var stars = [String : Int]()
var completedDate : Int?
var desc: String
var epub: String
var authorDesc: String
init(id: String, title: String, desc: String, authorDesc: String, image: String, epub: String, readCount: Int, uploadDate: Int, starCount: Int) {
super.init(id: id, title: title, image: image, uploadDate: uploadDate)
self.id = id
self.desc = desc
self.authorDesc = authorDesc
self.title = title
self.epub = epub
self.image = image
self.readCount = readCount
self.uploadDate = uploadDate
self.starCount = starCount
}
I get the error "Property 'self.readCount' not initialized at super.init call"
where I call "super.init(id: id, title: title, image: image, uploadDate: uploadDate)" in Book.swift
Class initialisation not finished until it's designated initializers
not finished with initializing all properties
and after that you can call super class's designated initializers
Designated initializers are the primary initializers for a class. A designated initializer fully initializes all properties introduced by that class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain.
SO
Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
apple docs https://docs.swift.org/swift-book/LanguageGuide/Initialization.html
so
class Book: LearningItem {
var publishDate: String?
var author: String?
var mediaUrl: String?
var video : String?
var tags: [String]?
var lists: [String: AnyObject]?
var readCount: Int
var categories: [String]?
var courses: [String]?
var amazonBuyUrl: String?
var starCount: Int
var read: Bool?
var completed = [String : Int]()
var stars = [String : Int]()
var completedDate : Int?
var desc: String
var epub: String
var authorDesc: String
init(id: String, title: String, desc: String, authorDesc: String, image: String, epub: String, readCount: Int, uploadDate: Int, starCount: Int) {
self.readCount = readCount
self.starCount = starCount
self.desc = desc
self.epub = epub
self.authorDesc = authorDesc
super.init(id: id, title: title, image: image, uploadDate: uploadDate)
}
}
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
}
This function gets variables from another file users.swift
append data
func getData(salval:String )
{ self.tabArray.removeAll()
ref = Database.database().reference()
let userID = Auth.auth().currentUser!.uid
if salval != "" {
ref?.child("Schedule").child("Codaphic").child(salval).child(userID).observe(.childAdded, with: { (snapshot)
in
if let dictionary = snapshot.value as? [String : AnyObject]
{
let user = users()
user.setValuesForKeys(dictionary)
self.tabArray.append(user)
self.tableView1.reloadData()
}
})
}
}
This is users.swift
import UIKit
class users : NSObject
{
var cname : String?
var _logi: String?
var key : String?
var wname : String?
var address : String?
var endtime : String?
var _leti : String?
var starttime : String?
var date : String?
var groupname : String?
var month : String?
}
this file is inherited with NSObject
but when project debugging its show error
If you're using user.setValuesForKeys(dictionary) it means that the variables in your User class MUST match the same names as the ones in your database. The safest way is to remove user.setValuesForKeys(dictionary) and use:
let user = User()
user.cname = dictionary["cname"] as? String
user.date = dictionary["data"] as? String
etc...
How can make model class for this json data
{
total: 41,
totalPages: 4,
valueData: [
{
id: "23",
lastLogin: "0-Jul-2011 11:27:36 AM",
name: "varii"
},
{
id: "24",
lastLogin: "0-Jul-2015 11:27:36 AM",
name: "sarii"
},
{
id: "25",
lastLogin: "0-Jul-2018 11:27:36 AM",
name: "narii"
} ]
}
class OnResponse {
var total: Int?
var totalPages: Int?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
}
}
It's not working how can make it ready for work
and how to pass values to controller to model and how to get value from model
Follow the below class structure
class Response {
var total: Int?
var totalPages: Int?
var valueData: [LoginData]?
init(response: [String: Any]) {
self.total = response["total"]
self.totalPages = response["totalPages"]
var items:[LoginData] = ()
for (data in response["valueData"]) {
let login = LoginData(name: data["name"], lastLogin: data["lastLogin"])
items.append(login)
}
self.valueData = items
}
}
class LoginData {
var name: String?
var lastLogin: String?
init(name: String, lastLogin: String) {
self.name = name
self.lastLogin = lastLogin
}
}
you should use "reverseMatches" to retrieve the array, not the "data". May be you can use a third library to convert your json data to a model, such as Unbox, https://github.com/JohnSundell/Unbox.
Model for Your response :
struct ModelName {
var total: Int?
var totalPage: Int?
var reverseMatches: [LoginDetails]?
}
struct LoginDetails {
var id: String?
var lastLogin: String?
var name: String?
}
Parse the api response and assign the values on the appropriate fields. I have made, all the variables are optional.
Assign values like below.
var obj = Model()
obj.total = response["total"] as? Int
obj should be var, because you are going to mutate the struct values. because struct is value based, not reference based
class DataModel{
var total : Int?
var totalPages : Int?
var valueData : [UserModel]?
init(JSON: [String:Any?]){
self = parser.doParse(JSON: JSON)
}
}
class UserModel{
var id : String?
var lastLogin : String?
var name : String?
}
class parser : NSObject{
class func doParse(JSON: [String:Any?])->DataModel{
let dataModel = DataModel()
dataModel.total = JSON["total"] as? Int
dataModel.totalPages = JSON["totalPages"] as? Int
var userInfo : [UserModel] = []
let valueData : [String:String?]? = JSON["valueData"] as? [String:String?]
if let valueData = valueData{
for dataDict : [String:String?] in valueData{
let itemModel = UserModel()
itemModel.id = dataDict["id"] as? String
itemModel.lastLogin = dataDict["lastLogin"] as? String
itemModel.name = dataDict["name"] as? String
userInfo.append(itemModel)
}
dataModel.valueData = userInfo
}
return dataModel
}
}
class LoginData: NSObject {
let total: Int = 0
let totalPages: Int = 0
let valueData: Array<ValueData> = Array<ValueData>()
override init(total: Int!, totalPages: Int, valueData: Array<ValueData>!) {
self.total = total
self.totalPages = totalPages
self.valueData = valueData
}
}
struct ValueData {
let id: int?
let lastLogin: String?
let name: String?
init(id: int!, lastLogin: string, name: string!)
self.id = id ?? 0
self.lastLogin = lastLogin ?? ""
self.name = name ?? ""
}
}
you should use struct instead of class for creating model object...
advantages of struct over class refer
Why Choose Struct Over Class?
class/24232845
use two struct for holding your data one is for your single total count
and other is for last login detail
struct lastLogin {
let totalCount: (total: Int, totalPages: Int)
let valueArray: [lastLoginDetail]
}
struct lastLoginDetail {
let valueData: (id: String, lastLogin: String,name: String)
}
extension lastLoginDetail {
init(json: [String : String]) throws {
let id = json["id"]
let lastLogin = json["lastLogin"]
let name = json["name"]
let value = (id,lastLogin,name)
self.valueData = value as! (id: String, lastLogin: String, name: String)
}
}
extension lastLogin {
init(json: [String : Any]) throws {
let total = (json["total"] as! NSNumber).intValue
let totalPages = (json["totalPages"] as! NSNumber).intValue
let totalCounts = (total,totalPages)
var userInfo : [lastLoginDetail] = []
// Extract and validate valueData
let valueData = json["valueData"] as? NSArray
if let valueData = valueData{
for dataDict in valueData{
let dic : [String : String] = dataDict as! [String : String]
let lastLoginDeatails = try! lastLoginDetail(json: dic)
userInfo.append(lastLoginDeatails)
}
}
self.valueArray = userInfo
self.totalCount = totalCounts
}
}
func HowToUseModelClass(){
let jsonDic = NSDictionary()
// jsonDic // your json value
let dataValue = try! lastLogin(json: (jsonDic as! [String : Any])) // parsing the data
print(dataValue.totalCount.total)
print(dataValue.totalCount.totalPages)
print(dataValue.valueArray[0].valueData.id)
}
I am trying to grab a list of bars from a Firebase Database and store it in an array so I can display it in a table view.
I have configured Firebase and managed to get data in the app as String, AnyObject dictionary.
Here is my code :
struct Bar {
var latitude: Double?
var longitude: Double?
var name: String!
var phoneNumber: String?
var happyHour: String?
var url: NSURL?
var barLogo: UIImage?
var followers: Int?
var addedToFavorites: Int?
var zipCode: Double?
var area: String?
}
class ViewController: UIViewController {
var ref: FIRDatabaseReference!
var refHandle: UInt!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
refHandle = ref.observe(FIRDataEventType.value , with: {(snapshot) in
let dataDict = snapshot.value as! [String : AnyObject]
}
)
}
Here is my JSON exported from Firebase:
{
"data" : {
"bars" : {
"bar1" : {
"addedToFavorites" : 0,
"area" : "upper east",
"follwers" : 0,
"happyHour" : "m-f 16-19",
"lattitude" : 4412334,
"longitude" : 223455,
"name" : "bar1",
"phone" : 212222,
"url" : "http://www.bar1.com",
"zipCode" : 12345
},
"bar2" : {
"addedToFavorites" : 0,
"area" : "upper west",
"follwers" : 0,
"happyHour" : "f - s 20-22",
"lattitude" : 4443221,
"longitude" : 221234,
"name" : "bar 2",
"phone" : 215555,
"url" : "http://www.bar2.com",
"zipCode" : 54321
}
}
}
}
What would be the best approach for this?
I would like to scale it and download hundreds of bars, so manually grabbing the data from the dictionary and storing it into a Bar struct variable and then appending it to an array is not a path I want to go on.
I need a solution to grab all the bars and somehow adding them to an array (or any other method to display them into a tableView).
Thanks in advance.
I found a way to solve this issue :
First of all I got rid of the struct and created a class :
My class file :
import Foundation
import UIKit
class Bar {
private var _name: String!
private var _area: String!
private var _latitude: Double!
private var _longitude: Double!
private var _followers: Int!
private var _happyHour: String!
private var _phone: Double!
private var _url: String!
private var _zipCode: Double!
private var _addedToFav: Int!
var name: String! {
return _name
}
var area: String! {
return _area
}
var latitude: Double! {
return _latitude
}
var longitude: Double! {
return _longitude
}
var followers: Int! {
return _followers
}
var happyHour: String! {
return _happyHour
}
var phone: Double! {
return _phone
}
var url: String! {
return _url
}
var zipCode: Double! {
return _zipCode
}
var addedToFav: Int! {
return _addedToFav
}
init(name: String,area: String! , latitude: Double, longitude: Double, followers: Int, happyHour: String, phone: Double, url: String, zipCode: Double, addedToFav: Int) {
self._name = name
self._area = area
self._latitude = latitude
self._longitude = longitude
self._followers = followers
self._happyHour = happyHour
self._phone = phone
self._url = url
self._zipCode = zipCode
self._addedToFav = addedToFav
}
init(barData: Dictionary<String, AnyObject>) {
if let name = barData["name"] as? String {
self._name = name
}
if let area = barData["area"] as? String {
self._area = area
}
if let latitude = barData["lattitude"] as? Double {
self._latitude = latitude
}
if let longitude = barData["longitude"] as? Double {
self._longitude = longitude
}
if let followers = barData["followers"] as? Int {
self._followers = followers
}
if let happyHour = barData["happyHour"] as? String {
self._happyHour = happyHour
}
if let phone = barData["phone"] as? Double {
self._phone = phone
}
if let url = barData["url"] as? String {
self._url = url
}
if let zipCode = barData["zipCode"] as? Double {
self._zipCode = zipCode
}
if let addedToFav = barData["addedToFavorites"] as? Int {
self._addedToFav = addedToFav
}
}
}
I created a DataService class with a singleton
Data service class file:
import Foundation
import Firebase
let URL_BASE = FIRDatabase.database().reference()
class DataService {
static let ds = DataService()
private var _REF_BASE = URL_BASE
private var _REF_BARS = URL_BASE.child("data").child("bars")
var REF_BASE: FIRDatabaseReference {
return REF_BASE
}
var REF_BARS: FIRDatabaseReference {
return _REF_BARS
}
}
And my modified viewController file (i did not use a tableViewController)
class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource {
#IBOutlet weak var myTableView: UITableView!
var baruri = [Bar]()
override func viewDidLoad() {
super.viewDidLoad()
myTableView.dataSource = self
myTableView.delegate = self
DataService.ds.REF_BARS.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshot {
print(snap)
if let barData = snap.value as? Dictionary<String, AnyObject> {
let bar = Bar(barData: barData)
self.baruri.append(bar)
print(self.baruri)
self.myTableView.reloadData()
}
self.myTableView.reloadData()
}
self.myTableView.reloadData()
}
self.myTableView.reloadData()
}
)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView( _ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return baruri.count
}
func tableView( _ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.myTableView.dequeueReusableCell(withIdentifier: "newCell", for: indexPath) as! NewCell
var baruriTabel: Bar!
baruriTabel = baruri[indexPath.row]
cell.barNameLBl.text = baruriTabel.name
cell.followersNrLbl.text = String(baruriTabel.followers)
cell.areaLbl.text = baruriTabel.area
cell.addedToFavoritesLbl.text = String(baruriTabel.addedToFav)
cell.happyHourLbl.text = baruriTabel.happyHour
cell.urlLbl.text = baruriTabel.url
cell.lattitudeLbl.text = String(baruriTabel.latitude)
cell.longitudeLbl.text = String(baruriTabel.longitude)
cell.phoneLbl.text = String(baruriTabel.phone)
cell.zipCode.text = String(baruriTabel.zipCode)
return cell
}
}