Я разработчик Android, который начал изучать iOS. Я пытаюсь передать данные между приложением в стиле основной детали. У меня есть controller1, в котором есть список элементов ToDo, и controller2, который позволяет создать новый элемент ToDo и добавить его в список на controller1.

Я создал протокол:

protocol ListDataHolder {
    
    associatedtype T
    
    func addItem(item: T)
    
    func reloadData()
}

Назначено self в prepare из controller1:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let controller2 = segue.destination as? Controller2{
            controller2.toDoDataHolder = self
        }
    } 

Объявлен делегат в controller2

// how do I tell to use ToDo class for generic type here
var toDoDataHolder: ListDataHolder? = nil

И используйте это так:

@IBAction func onAddClicked(_ sender: Any) {
        let toDo = ToDo()
        ...
        toDoDataHolder?.addItem(item: toDo)
        toDoDataHolder?.reloadData()
        navigationController?.popViewController(animated: true)
    }

Когда я пошел по этому пути, у меня было несколько ошибок:

Для объявления делегата:

Protocol 'ListDataHolder' can only be used as a generic constraint because it has Self or associated type requirements

При использовании addItem():

Cannot convert value of type 'ToDo' to expected argument type 'ListDataHolder.T'
Insert ' as! ListDataHolder.T'
Member 'addItem' cannot be used on value of protocol type 'ListDataHolder'; use a generic constraint instead

Когда я удаляю общий из протокола и оставляю только addItem(item: ToDo), все работает нормально. Но я хочу иметь возможность использовать ListDataHolder с любым типом данных.

Для меня это просто эксперимент, я не ищу правильный способ передачи данных между контроллерами.

РЕДАКТИРОВАТЬ: вы можете найти полный код в этом репозитории GitHub: github.com/Sermilion/ios_learning

0
Sermilion 8 Окт 2021 в 14:08

2 ответа

Лучший ответ

Что вам нужно сделать, так это сделать второй контроллер представления универсальным, используя протокол и ограничить тип объектов, используемых (или удерживаемых) классом, соответствующим ListDataHolder

Это можно сделать в объявлении контроллера представления.

class SecondViewController<Holder: ListDataHolder>: UIViewController where Holder.T == ToDo

Тогда ваш метод onAddClicked будет работать как есть.

0
Joakim Danielson 8 Окт 2021 в 14:20

Вместо использования связанного типа в протоколе ListDataHolder используйте ToDo, если вы всегда передаете экземпляр ToDo методу addItem.

Если вы хотите, чтобы этот метод протокола addItem работал с любым genericType, используйте следующий код.

protocol ListDataHolder {
    func addItem<T:Decodable>(item: T)
    func reloadData()
}

struct ToDo: Decodable {

}

class A: ListDataHolder {
   
    func addItem<T:Decodable>(item: T) {
          print("Add Item called")
    }
    
    func reloadData(){

    }
}

Здесь вам нужно реализовать шаблон проектирования Delegation. Пожалуйста, проверьте подробно шаблон проектирования делегирования.

0
Dávid Pásztor 8 Окт 2021 в 14:07