#swift #generics #model-view-controller #protocols #delegation
#swift #общие #модель-представление-контроллер #протоколы #делегирование
Вопрос:
У меня есть приведенный ниже код, который нацелен на abstraction
— без Decodable
необходимости использовать s — для DataModel
s в приложении. Я хотел использовать эти DataModel
s для их центрирования. Вот как я далеко зашел прямо сейчас, и я как бы в тупике.
В этой конфигурации код сообщает мне, что ProfileResponseDelegate
не может соответствовать ModelDelegate
when ProfileResponseDelegate
is a protocol
, что имеет смысл.
protocol ModelDelegate: class {
associatedtype DataType: Decodable
func didReceive(data: DataType)
}
class Model<Type, Delegate: ModelDelegate> where Type == Delegate.DataType {
var data: Type?
weak var delegate: Delegate?
func requestData() { return }
}
protocol ProfileResponseDelegate: ModelDelegate where DataType == ProfileResponse {}
//throws Value of protocol type 'ProfileResponseDelegate' cannot conform to 'ModelDelegate'; only struct/enum/class types can conform to protocols
class ProfileResponseModel: Model<ProfileResponse, ProfileResponseDelegate> {
override func requestData() {
guard let data = data else {
// go to api to get data
return
}
delegate?.didReceive(data: data)
}
}
class Controller: UIViewController, ProfileResponseDelegate {
let model = ProfileResponseModel()
override func viewDidLoad() {
super.viewDidLoad()
model.delegate = self
model.requestData()
}
func didReceive(data: ProfileResponse) {
//tell view code to update regarding data
}
}
Когда я меняю ProfileResponseDelegate
значение на a class
— не будучи delegate
больше a, но в любом случае — код не позволяет Controller
наследовать от обоих UIViewController
, и ProfileResponseDelegate
рассуждение a class
не может наследовать от нескольких class
es. что опять же имеет смысл.
class ProfileResponseDelegate: ModelDelegate {
typealias DataType = ProfileResponse
func didReceive(data: ProfileResponse) {
return
}
}
class Controller: UIViewController, ProfileResponseDelegate {
let model = ProfileResponseModel()
override func viewDidLoad() {
super.viewDidLoad()
model.delegate = self
model.requestData()
}
override func didReceive(data: ProfileResponse) {
//tell view code to update regarding data
}
}
Что касается первой конфигурации, я не смог заставить ее работать. Однако для второго, когда Controller
он просто наследуется от ProfileResponseDelegate
него, работает просто отлично.
Я должен найти способ заставить это работать — предпочтительно первую конфигурацию — и нуждаюсь в вашем совете. Заранее благодарен.
Обновить
Итак, я удалил associatedType
из ModelDelegate
и удалил ProfileResponseModel
. Прямо сейчас код выглядит следующим образом.
protocol ModelDelegate: class {
//associatedtype DataType: Decodable
func didReceive<T: Decodable>(data: T)
}
class Model<Type: Decodable> {
var data: Type?
weak var delegate: ModelDelegate?
func requestData() { return }
}
//protocol ProfileResponseDelegate: ModelDelegate where DataType == ProfileResponse {}
class ProfileResponseModel: Model<ProfileResponse> {
override func requestData() {
guard let data = data else {
// go to api to get data
return
}
delegate?.didReceive(data: data)
}
}
class Controller: UIViewController, ModelDelegate {
let model = ProfileResponseModel()
override func viewDidLoad() {
super.viewDidLoad()
model.delegate = self
model.requestData()
}
func didReceive<T>(data: T) where T : Decodable {
//I want this `data` to come as what it is.
if let response = data as? ProfileResponse {
print(type(of: response))
}
}
}
Это работает так, однако моя конечная цель для этого — не приводить data
ProfileResponse
здесь — и в других местах — к другому Decodable
типу.
Комментарии:
1. Первый никогда не может работать. Это не то, что протоколы.
2. Какие-либо обходные пути? @matt
3. Передайте обработчик завершения
requestData()
вместо того, чтобы пытаться принудить делегата к этому. Здесь вполне возможно создать делегата, но это слишком сложно для этой проблемы, и обработчик завершения значительно упростит все это. Для более общего подхода к более крупной проблеме см. youtube.com/watch?v=DXwJg0QTlZE а для другого (еще более продвинутого) подхода см. davedelong.com/blog/2020/06/27/http-in-swift-part-14. Ваш первый подход на самом деле ближе к цели, чем ваш второй. Во-вторых, вы сказали, что делегат должен иметь дело с любым декодируемым, который ему передается. Это противоположно тому, что вы хотите. Ваш первый подход технически ближе, но его фактическая работа приведет вас на ложный путь.
5. Спасибо за ваш ответ. Я сейчас смотрю ваш лейтмотив и изучил более продвинутую реализацию. И у меня возникла мысль о том, что, возможно, я ошибался, начиная с кода клиента API. Я написал клиент, очень похожий
Moya
на библиотеку с универсальными функциями. И для огромного процесса рефакторинга, в котором я нахожусь, я стараюсь убрать как можно больше кода из контроллеров представления. Я также включил сетевой код в этот контекст. Цель приведенного выше кода — обеспечить интерфейс между моделью и контроллером, и, таким образом, будет ModelController, который управляет данными во всем приложении.