kitchen800
kitchen800

Reputation: 227

Populating an UITableview from a struct

I have two viewControllers one called programlist that displays the list of tiles and populates a a suitable view.

the second viewController inputs the data. Issues implementing the callback due to an error in the prepareForsegue function. Getting the error "Instance member 'callback' cannot be used on type 'addWorkout'"

viewController 1 aka Programlist:

import UIKit

struct Item: Codable {
var title: String
var others: [String]
}

class ProgramList: UIViewController, UITableViewDataSource, UITableViewDelegate{

var Programs = [Item]()

@IBOutlet weak var programTableView: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()
    load()
}

//saving current state of programs array
func save() {
    guard let data = try? JSONEncoder().encode(Programs) else { return }
    UserDefaults.standard.set(data, forKey: "notes")
}

//loading saved program array
func load() {
    guard let loadedData = UserDefaults.standard.data(forKey: "notes")  else { return }
    do {
        Programs = try JSONDecoder().decode([Item].self, from: loadedData)
        programTableView.reloadData()
    } catch { print(error) }
}






func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return Programs.count
}


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
              cell.programTitle.text = Programs[indexPath.row].title
    return cell
}

//Removing Item by swipping left & saving this newly established array
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {

    if editingStyle == UITableViewCell.EditingStyle.delete {
        Programs.remove(at: indexPath.row)
        programTableView.reloadData()
        save()
    }


    func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if segue.identifier == "toAddPage"{
            workoutController.callback = { [weak self] string in
                let entered = Item(title: string, others: ["hi"])
                self?.programs.append(entered)
                let indexPath = IndexPath(row: self?.programs.count - 1, section: 0)
                self?.tableView.insertRows(at: [indexPath], with: .automatic)
                self?.save()
         }
       }
    }    
  }
}

  }

viewController 2 aka addWorkout:

 import UIKit

 class addWorkout: UIViewController {

@IBOutlet weak var workoutTitle: UITextField!

var callback : ((String) -> Void)?

override func viewDidLoad() {
    super.viewDidLoad()
}

@IBAction func saveWorkoutTitle(_ sender: Any) {

    if !workoutTitle.text!.isEmpty {
        callback?(workoutTitle.text!)
    }   
}
}

Upvotes: 3

Views: 945

Answers (1)

vadian
vadian

Reputation: 285069

The main mistake is you are trying to save an array of Item – which is not supported anyway – to UserDefaults and read an array of String. That's a clear type mismatch.

To be able to save an array of a custom struct to UserDefaults adopt Codable to save the struct as JSON.

struct Item : Codable {
    var title: String
    var others: [String]
}

Further it's a very bad practice to declare a data source array outside of any class.

This is the ProgramList class with adjusted load and save methods and the data source array inside the class. The method viewDidAppear is not needed.

class ProgramList: UIViewController, UITableViewDataSource, UITableViewDelegate {
    @IBOutlet weak var programTableView: UITableView!

    var programs = [Item]()

    override func viewDidLoad() {
        super.viewDidLoad()
        load()
    }

    //saving current state of programs array
    func save() {
        guard let data = try? JSONEncoder().encode(programs) else { return }
        UserDefaults.standard.set(data, forKey: "notes")
    }

    //loading saved program array
    func load() {
        guard let loadedData = UserDefaults.standard.data(forKey: "notes")  else { return }
        do {
            programs = try JSONDecoder().decode([Item].self, from: loadedData)
            programTableView.reloadData()
        } catch { print(error) }
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return programs.count
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
        cell.programTitle.text = programs[indexPath.row].title
        return cell
    }

    //Removing Item by swipping left & saving this newly established array
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            programs.remove(at: indexPath.row)
            programTableView.deleteRows(at: [indexPath], with: .automatic)
            save()
        }
    }
}

To share data between controllers use a closure as callback and pass the string

class AddWorkout: UIViewController {

    @IBOutlet weak var workoutTitle: UITextField!

    var callback : ((String) -> Void)?

    @IBAction func saveWorkoutTitle(_ sender: Any) {
        if !workoutTitle.text!.isEmpty {
            callback?(workoutTitle.text!)
        }
    }
}

Back in ProgramList controller assign a closure to the callback property in prepareForSegue (or right before presenting the controller)

 func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "toAddPage" {
        let workoutController = segue.destination as! AddWorkout
        workoutController.callback = { string in
            let entered = Item(title: string, others: ["hi"])
            self.programs.append(entered)
            let indexPath = IndexPath(row: self.programs.count - 1, section: 0)
            self.tableView.insertRows(at: [indexPath], with: .automatic)
            self.save()
        }
    }
}

Upvotes: 3

Related Questions