This is my property list: directory.plist
I would like to know how call the subitems into a ViewController. This is my actual ViewController:
import UIKit
class Page1: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var positionLabel: UILabel!
#IBOutlet weak var phoneLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
Shared.instance.employees.forEach({
nameLabel.text = (("name:", $0.name) as? String)
print("name:", $0.name)
print("position:", $0.position)
print("phone:", $0.phone)
print("email:", $0.email)
print()
})
}
}
And this is the Struct I am using:
import UIKit
struct Employee {
let position: String
let name: String
let email: String
let phone: String
init(dictionary: [String: String]) {
self.position = dictionary["Position"] ?? ""
self.name = dictionary["Name"] ?? ""
self.email = dictionary["Email"] ?? ""
self.phone = dictionary["Phone"] ?? ""
}
}
struct Shared {
static var instance = Shared()
var employees: [Employee] = []
}
Inside the AppDelegate I put this:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let url = Bundle.main.url(forResource: "directory", withExtension: "plist"), let array = NSArray(contentsOf: url) as? [[String: String]] {
Shared.instance.employees = array.map{Employee(dictionary: $0)}
}
return true
I do I have to do to call the subitems on my directory.plist? I mean the items inside the key Details. Then I wanna show the Func1, Func2 and Func3.
obs.: (this func is an abbreviation of functionary)
Thanks!
After some changes, now I got the Subitems nil: debugger
Your code in the application delegate is fine. You just need to update your Employee struct with another property to store the Details. But this also means that your employee dictionary isn't just a dictionary of string values. So you need to update the code to handle the proper value types:
struct Employee {
let position: String
let name: String
let email: String
let phone: String
let details: [String:String]
init(dictionary: [String: Any]) {
self.position = (dictionary["Position"] as? String) ?? ""
self.name = (dictionary["Name"] as? String) ?? ""
self.email = (dictionary["Email"] as? String) ?? ""
self.phone = (dictionary["Phone"] as? String) ?? ""
self.details = (dictionary["Details"] as? [String:String]) ?? [:]
}
}
Related
I have created separate NSObject class called ProfileModel
like below:
class ProfileModel : NSObject, NSCoding{
var userId : String!
var phone : String!
var firstName : String!
var email : String!
var profileImageUrl : String!
var userAddresses : [ProfileModelUserAddress]!
// Instantiate the instance using the passed dictionary values to set the properties values
init(fromDictionary dictionary: [String:Any]){
userId = dictionary["userId"] as? String
phone = dictionary["phone"] as? String
firstName = dictionary["firstName"] as? String
email = dictionary["email"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
}
/**
* Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
*/
func toDictionary() -> [String:Any]
{
var dictionary = [String:Any]()
if userId != nil{
dictionary["userId"] = userId
}
if phone != nil{
dictionary["phone"] = phone
}
if firstName != nil{
dictionary["firstName"] = firstName
}
if email != nil{
dictionary["email"] = email
}
if profileImageUrl != nil{
dictionary["profileImageUrl"] = profileImageUrl
}
return dictionary
}
/**
* NSCoding required initializer.
* Fills the data from the passed decoder
*/
#objc required init(coder aDecoder: NSCoder)
{
userId = aDecoder.decodeObject(forKey: "userId") as? String
userType = aDecoder.decodeObject(forKey: "userType") as? String
phone = aDecoder.decodeObject(forKey: "phone") as? String
firstName = aDecoder.decodeObject(forKey: "firstName") as? String
email = aDecoder.decodeObject(forKey: "email") as? String
profileImageUrl = aDecoder.decodeObject(forKey: "profileImageUrl") as? String
}
/**
* NSCoding required method.
* Encodes mode properties into the decoder
*/
#objc func encode(with aCoder: NSCoder)
{
if userId != nil{
aCoder.encode(userId, forKey: "userId")
}
if phone != nil{
aCoder.encode(phone, forKey: "phone")
}
if firstName != nil{
aCoder.encode(firstName, forKey: "firstName")
}
if email != nil{
aCoder.encode(email, forKey: "email")
}
if profileImageUrl != nil{
aCoder.encode(profileImageUrl, forKey: "profileImageUrl")
}
}
}
In RegistrationViewController I adding firstName value which i need to show in ProfileViewController How ?
In RegistrationViewController i am adding firstName and phone values which i need in ProfileViewController:
class RegistrationViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var firstNameTextField: FloatingTextField!
var userModel : ProfileModel?
override func viewDidLoad() {
let userID: String=jsonObj?["userId"] as? String ?? ""
self.userModel?.firstName = self.firstNameTextField.text
self.userModel?.phone = phoneTextField.text
}
}
This is ProfileViewController here in name and number i am not getting firstName and phone values why?:
class ProfileViewController: UIViewController {
#IBOutlet var name: UILabel!
#IBOutlet var number: UILabel!
var userModel : ProfileModel?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
name.text = userModel?.firstName
number.text = userModel?.phone
}
}
PLease help me with code.
You cannot set firstName or phone to the userModal which is nil. First you should create an instance, and then you can pass it through your controllers. We should change code step by step:
class ProfileModel {
var userId : String?
var phone : String?
var firstName : String?
var email : String?
var profileImageUrl : String?
var userAddresses : [ProfileModelUserAddress]?
init() {}
}
Second, you need to reach ProfileModel instance from both of your ViewController classes. For this, you can create a singleton class:
class ProfileManager {
static var shared = ProfileManager()
var userModel: ProfileModel?
private init() {}
}
Then you can reach it from both of your ViewControllers:
class RegistrationViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var firstNameTextField: FloatingTextField!
override func viewDidLoad() {
super.viewDidLoad()
let userModel = ProfileModel()
userModel.firstName = self.firstNameTextField.text
ProfileManager.shared.userModel = userModel
}
}
Other VC:
class ProfileViewController: UIViewController {
#IBOutlet var name: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let userModel = ProfileManager.shared.userModel,
let firstName = userModel.firstName {
name.text = firstName
}
}
}
Modify it as you wanted.
I am facing the error of self used before self.init call or assignment to self in the below code for the model class for the tableview cell item, it happened after I tried to get document id of the table cell item.
What should be done? Please recommend.
import Foundation
import Firebase
import FirebaseFirestore
protocol DocumentSerializable {
init?(dictionary:[String:Any])
}
struct Post {
var _postKey: String!
var _username: String!
var _postTitle: String!
var _postcategory: String!
var _postContent: String!
var dictionary:[String:Any] {
return [
"username": _username,
"postTitle":_postTitle,
"postcategory":_postcategory,
"postContent":_postContent,
"postKey":_postKey
]
}
}
extension Post : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let postKey = key,
let username = dictionary["username"] as? String,
let postTitle = dictionary["postTitle"] as? String,
let postcategory = dictionary["postcategory"] as? String,
let postContent = dictionary["postContent"] as? String else { return nil }
self.init(_postKey: postKey, _username: username ,_postTitle: postTitle, _postcategory: postcategory, _postContent: postContent)
}
}
You have some circular dependency going on, to access the dictionary variable you need an instance, for the instance to be created you need to call init but again to call init you should have the dictionary variable initialized and so on. You could make the dictionary variable static.
I have problem with use data from firebase after get them. I written function getData() in model, use delegate to call them on UITableViewController and set data to TableView.
But when I create new array to get data from func getData(), this array is nil.
This is my model:
import Foundation
import Firebase
protocol myDelegate: class {
func didFetchData(datas: [Book])
}
class Book {
var Id: String?
var Author: String?
var ChapterCount: Int?
var CoverPhoto: String?
var Genre: String?
var Image: String?
var Intro: String?
var Like: Int?
var Name: String?
var Status: String?
var UpdateDay: String?
var UploadDay: String?
var View: Int?
var ref: DatabaseReference!
weak var delegate: myDelegate?
init()
{
}
init(Id: String,Author: String,Image: String,Name: String,Status: String,UpdateDay: String,View: Int)
{
self.Id = Id
self.Author = Author
self.Image = Image
self.Name = Name
self.Status = Status
self.UpdateDay = UpdateDay
self.View = View
}
func getListBook() {
ref = Database.database().reference()
ref.child("Book").observe(.value, with: { snapshot in
var newNames: [Book] = []
let value = snapshot.value as? NSDictionary
for nBook in value! {
let val = nBook.value as? NSDictionary
self.Name = val?["Name"] as? String ?? ""
self.Author = val?["Author"] as? String ?? ""
self.View = val?["View"] as? Int ?? 0
self.Status = val?["Status"] as? String ?? ""
self.Id = val?["Id"] as? String ?? ""
self.Image = val?["Image"] as? String ?? ""
self.UpdateDay = val?["UpdateDay"] as? String ?? ""
newNames.append(Book(Id: self.Id!, Author: self.Author!, Image: self.Image!, Name: self.Name!, Status: self.Status!, UpdateDay: self.UpdateDay!, View: self.View!))
}
self.delegate?.didFetchData(datas: newNames)
})
}
}
And there is class UITableViewController:
import Firebase
class ListStoryTableView: UITableViewController, myDelegate {
var ref: DatabaseReference!
var book = Book()
var listBook: [Book] = []
func didFetchData(datas: [Book]) {
listBook = datas
}
override func viewDidLoad() {
super.viewDidLoad()
let nib = UINib.init(nibName: "ListStoryTableViewCell", bundle: nil)
self.tableView.register(nib, forCellReuseIdentifier: "ListStoryTableViewCell")
book.delegate = self
book.getListBook()
print("\(listBook)") //this is return 0
}```
One solution would be to change-remove your protocol implementation and use a completion block in your getListBook func. Delete myDelegate reference from your ListStoryTableView and do the following change:
func getListBook(completion: #escaping (_ books: [Book]) -> Void) {
ref = Database.database().reference()
ref.child("Book").observe(.value, with: { snapshot in
var newNames: [Book] = []
let value = snapshot.value as? NSDictionary
for nBook in value! {
let val = nBook.value as? NSDictionary
self.Name = val?["Name"] as? String ?? ""
self.Author = val?["Author"] as? String ?? ""
self.View = val?["View"] as? Int ?? 0
self.Status = val?["Status"] as? String ?? ""
self.Id = val?["Id"] as? String ?? ""
self.Image = val?["Image"] as? String ?? ""
self.UpdateDay = val?["UpdateDay"] as? String ?? ""
newNames.append(Book(Id: self.Id!, Author: self.Author!, Image: self.Image!, Name: self.Name!, Status: self.Status!, UpdateDay: self.UpdateDay!, View: self.View!))
}
completion(newNames)
})
}
and then in your viewDidLoad or any other function you use the following to fetch your data:
book.getListBook { books in
listBook = books
tableView.reloadData()
}
I hope that helps you.
func didFetchData(datas: [Book]) {
listBook = datas
print("\(listBook)")
}
print listBook in this function and you will have the data..
The script below worked fine using Swift 2 on both simulator on phone. Having updated to swift 3, the script works fine on simulator but throws up an error when building for phone - Ambiguous use of subscript on the following line -
let aObject = linkJSON[index] as! [String : AnyObject]
I have endeavoured to change the line to use Any, however there is no change to the error.
the full code is thus -
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var orgs: [String] = []
var icon: [String] = []
var address1: [String] = []
var address2: [String] = []
var address3: [String] = []
var address4: [String] = []
var postcodes: [String] = []
var phone: [String] = []
var email: [String] = []
var website: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
let url=URL(string:"http://webdata.net/service.php")
do {
let allLinksData = try Data(contentsOf: url!)
let allLinks = try JSONSerialization.jsonObject(with: allLinksData, options:.allowFragments) as! [String : AnyObject]
if let linkJSON = allLinks["organisations"] {
for index in 0...linkJSON.count-1 {
let aObject = linkJSON[index] as! [String : AnyObject]
orgs.append(aObject["org"] as! String)
icon.append(aObject["icon"] as! String)
address1.append(aObject["address1"] as! String)
address2.append(aObject["address2"] as! String)
address3.append(aObject["address3"] as! String)
address4.append(aObject["address4"] as! String)
postcodes.append(aObject["postcode"] as! String)
phone.append(aObject["phone"] as! String)
email.append(aObject["email"] as! String)
website.append(aObject["website"] as! String)
}
}
print(orgs)
self.tableView.reloadData()
}
catch {
}
}
It may be because its Monday, but i'm having a blank here.
Thanks
Bowcaps
I suggest you replace
if let linkJSON = allLinks["organisations"] {
with
if let linkJSON = allLinks["organisations"] as? [[String: AnyHashable]] {
Then you won't need to force unwrap the linkJSON[index] and you can just do
let aObject = linkObject[index]
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
}
}