How to receive data of subitens from a plist?

Asked

Viewed 67 times

2

Good! I’m having trouble receiving sub data from my plist. Note... This is my directory.plist: directory.plist

I want to throw it all in this one Main.storyboard: Main.storyboard

Whereas the Keys Position and Name would be in the first Tableviewcontroller and the Keys Functionary, ImageFace and Phone would stay on Monday Tableviewcontroller.

For that I did the following:

  1. I added to the Appdelegate the following:

    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:Any]] {
            Shared.instance.employees = array.map{Employee(dictionary: $0)}
    }
    return true
    
  2. My Struct got that way:

    struct EmployeeDetails {
        let functionary: String
        let imageFace: String
        let phone: String
    
        init(dictionary: [String: Any]) {
            self.functionary = (dictionary["Functionary"] as? String) ?? ""
            self.imageFace = (dictionary["ImageFace"] as? String) ?? ""
            self.phone = (dictionary["Phone"] as? String) ?? ""
        }
    }
    
    struct Employee {
        let position: String
        let name: String
        let details: [EmployeeDetails] // [String:Any]
    
        init(dictionary: [String: Any]) {
        self.position = (dictionary["Position"] as? String) ?? ""
        self.name = (dictionary["Name"] as? String) ?? ""
    
        let t = (dictionary["Details"] as? [Any]) ?? []
        self.details = t.map({EmployeeDetails(dictionary: $0 as! [String : Any])})
        }
    }
    
    struct Shared {
        static var instance = Shared()
        var employees: [Employee] = []
    }
    
  3. Man first tableviewcontroller, was like this:

    class Page1: UITableViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        let anEmployee = Shared.instance.employees[1]
    
        print("Name:", anEmployee.name)
        print("Position:", anEmployee.position)
    
        anEmployee.details.forEach({
    
            print("Functionary:", $0.functionary)
            print("ImageFace:", $0.imageFace)
            print("Phone:", $0.phone)
        })
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return Shared.instance.employees.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell1
    
            cell.nameLabel.text = Shared.instance.employees[indexPath.row].name
            cell.positionLabel.text = Shared.instance.employees[indexPath.row].position
    
            return cell
        }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destination = segue.destination as? Page2,
            let indexPath = tableView.indexPathForSelectedRow {
            destination.newPage = Shared.instance.employees[indexPath.row].details[indexPath.row]
            tableView .deselectRow(at: indexPath, animated: true)
            }
        }
    }
    
  4. Man according to Tableviewcontroller, was like this:

    var newPage: EmployeeDetails! //recently added
    
    class Page2: UITableViewController {
    
    var newPage: EmployeeDetails!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        if let theEmployee = newPage {
            self.title = theEmployee.name
        }
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let theEmployee = newPage {
            return theEmployee.details.count
        }
    return 0
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell2
    
        if let theEmployee = newPage {
    
            cell.faceImage.image = theEmployee.details[indexPath.row].imageFace as? UIImage // did not work
            cell.functionary.text = theEmployee.details[indexPath.row].functionary
            cell.phoneLabel.text = theEmployee.details[indexPath.row].phone
        }
        return cell
        }
    }
    

After the changes, the project has no more errors, but the 2nd Tableviewcontroller remains empty! The Debug Area says the following:

2017-03-28 17:16:28.456 plist sample[7138:425253] Unknown class Page2 in Interface Builder file.

Any idea?

From now on, thank you.

  • 1

    You forgot to post struct Employee

  • Ready! Thank you for remembering

1 answer

3


Hello,

I believe your first problem ("Está retornando nil") is that the property details in your struct Employee is a dictionary like [String:Any], but in her . plist she is a array of dictionaries. Therefore, the correct one would be [[String:Any]].

Your second problem is related to the first, because if the details is an array, it does not have the subscript that you reference (what generates this Compile error).

So, the correct syntax for you to extract the value from the field of your . plist (as it is, ipsis literis) would be (example):

cell.faceImage.image = Shared.instance.employees[indexPath.row].details[<!INDEX!>]["ImageFace"]

Substitute <!INDEX!> to the correct index.

UPDATE

To send the selected object in its first VC to the second VC, you must implement both methods func prepare(for segue: UIStoryboardSegue, sender: Any?) and func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath).

Examples:

1) When selecting a cell from the table:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let object = Shared.instance.employees[indexPath.row]
    self.performSegue(withIdentifier: "ID_DA_SEGUE_AQUI", sender: object)
}

2) Load the next table with the selected data:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if  let proximoVc = segue.destination as?  {
        proximoVc.newPage = sender as? EmployeeDetails
    }
}

Obs.: The above code can generate a crash if the index in the search inside the array is not correct. Check that the references are correct before proceeding.

  • Dude, it worked great! The debug area is getting the cool items. But the 2nd Tableviewcontroller does not get the subitens from directory.plist. Some idea comrade?

  • 1

    You need to implement the method prepare(for segue: UIStoryboardSegue, sender: Any?).

  • I already asked and updated the question, but my 2nd Tableview is still empty. I do not know what is wrong.

  • 1

    Oi Arnon, I updated my response with this implementation. I focused on showing you the methods that need to be subscribed and their respective implementations. I ignored if the index indexPath.row makes the correct reference to the selected object. Please replace this part for what suits you best. Abs

  • 1

    thanks buddy! I followed your tips! Everything solved!! THANKS

Browser other questions tagged

You are not signed in. Login or sign up in order to post.