Search code examples
iosswiftplist

Find a string inside a plist Dictionary


I'm attempting to find a string inside a plist Dictionary but I'm not sure how. Can I get some help please?

The code contains two plists, one with the list of Clients and the second with the list or Products, we're populating the client's data in a cell from the ClientArray, but I need to also include the ProductName for that client from the ProductArray in the same Cell, the matching key is productID.

enter image description here plist ClientArray

enter image description here plist ProductArray

import UIKit

class TestViewController: UIViewController {
    var ClientArray = [[String:Any]]()
    var ProductArray = [[String:Any]]()

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        //path of plist file Array Client
        let path1 = Bundle.main.path(forResource: "ClientList", ofType: "plist")
        ClientArray = NSArray(contentsOfFile: path1!)! as! [Any] as! [[String : Any]]

        //path of plist file Array Products
        let path2 = Bundle.main.path(forResource: "ProductList", ofType: "plist")
        ProductArray = NSArray(contentsOfFile: path2!)! as! [Any] as! [[String : Any]]
        // Do any additional setup after loading the view, typically from a nib.
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestTableViewCell

        //fill out custom cell values
        cell.testName.text = ClientArray[indexPath.row]["name"] as? String
        cell.testNumber.text = ClientArray[indexPath.row]["number"] as? String


        for product in ProductArray {
            if let productName = product[ClientArray[indexPath.row]["productID"] as! String] {
                cell.testProduct.text = productName["productName"] as? String
            }
        }

        return cell
    }
}

Solution

  • First of all don't use NSArray and NSDictionary in Swift. Use native types. This avoids weird cast dances like NSArray ... as! [Any] as! [[String : Any]].

    Second of all there is a class PropertyListSerialization to convert Property List to collection types and vice versa.

    Finally the proper type of both arrays is [[String:String]]. The avoids more unnecessary type casting.

    Please conform to the naming convention that variable names start with a lowercase letter.

    var clientArray = [[String:String]]()
    var productArray = [[String:String]]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        //URL of plist file Array Client
        let clientURL = Bundle.main.url(forResource: "ClientList", withExtension: "plist")!
        let clientData = try! Data(contentsOf: clientURL)
        clientArray = try! PropertyListSerialization.propertyList(from: clientData, format: nil) as! [[String:String]]
    
        //URL of plist file Array Products
        let productURL = Bundle.main.url(forResource:  "ProductList", withExtension: "plist")!
        let productData = try! Data(contentsOf: productURL)
        productArray = try! PropertyListSerialization.propertyList(from: productData, format: nil) as! [[String:String]]
        // Do any additional setup after loading the view, typically from a nib.
    }
    

    In cellForRow filter the product name with the first function.

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestTableViewCell
    
        //fill out custom cell values
        let client = clientArray[indexPath.row]
        cell.testName.text = client["name"]
        if let product = productArray.first{ $0["productID"]! == client["productID"]! } {
            cell.testNumber.text = product["productName"]
        }
    
        return cell
    }
    

    A more efficient solution is to decode the Property List into structs with PropertyListDecoder

    struct Client : Decodable {
        let name, number, productID : String
    }
    
    struct Product : Decodable {
        let productID, productName, productQty : String
    }
    
    ...
    
    var clients = [Client]()
    var products = [Product]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        //URL of plist file Array Client
        let clientURL = Bundle.main.url(forResource: "ClientList", withExtension: "plist")!
        let clientData = try! Data(contentsOf: clientURL)
        clients = try! PropertyListDecoder().decode([Client].self, from: clientData)
    
        //URL of plist file Array Products
        let productURL = Bundle.main.url(forResource:  "ProductList", withExtension: "plist")
        let productData = try! Data(contentsOf: productURL)
        products = try! PropertyListDecoder().decode([Product].self, from: productData)
    }
    
    ...
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestTableViewCell
    
        //fill out custom cell values
        let client = clients[indexPath.row]
        cell.testName.text = client.name
        if let product = products.first{ $0.productID == client.productID } {
            cell.testNumber.text = product.productName
        }
    
        return cell
    }
    

    Consider to use CoreData with relationships for the data model. It's still more efficient.