Я думаю, что вы на правильном пути с KeyPath. Я бы, вероятно, немного реорганизовал бы это так, чтобы метод listen
возвращал Listener?
(независимо от того, какой это тип), и вывел бы «если слушатель уже установлен, не делайте этого» вне этой функции.
Это оставило бы вас в таком свете:
func listen<Response>(reference: Int?, // Or whatever type this is
updating keyPath: WritableKeyPath<C, Response>,
completion: (Result<Response, Error>) -> Void) -> Listener? {
guard let reference = reference else { return nil }
return backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
guard var strongSelf = self else {
return
}
switch result {
case .success(let result):
strongSelf[keyPath: keyPath] = result
case .failure:
break
}
}
Или вы можете сохранить существующую структуру примерно так:
func listen<Response>(reference: Int?,
updating keyPath: WritableKeyPath<C, Response>,
forListener listener: WritableKeyPath<C, Listener?>,
completion: (Result<Response, Error>) -> Void) {
guard let reference = reference else { return }
guard self[keyPath: listener] == nil else {
return
}
var mutableSelf = self
mutableSelf[keyPath: listener] = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
guard var strongSelf = self else {
return
}
switch result {
case .success(let result):
strongSelf[keyPath: keyPath] = result
case .failure:
break
}
}
}
(C
во всем этом коде означает «этот класс». Мы еще не можем написать Self
для обозначения «текущего класса».)