符合协议和依赖注入问题的关联类型

时间:2018-04-20 09:19:45

标签: swift swift-protocols

我正在研究依赖注入,目前正在更新我的项目以利用它。但是,我遇到了相关类型和协议符合的问题。

我创建了一个快速演示项目,并创建了一些协议和扩展,以便符合我的协议ViewModelBased的viewControllers必须实现一个关联类型。理想情况下,我希望此关联类型符合viewModel。这是我到目前为止所拥有的

protocol ViewModel {
    associatedtype Services
    init (withServices services: Services)
}

protocol ViewModelBased: class {
    associatedtype ViewModelType
    var viewModel: ViewModelType { get set }
}

extension ViewModelBased where Self: UIViewController{
    static func instantiateController(with viewModel : ViewModelType) -> Self {

        // I have created UIStoryboard extension to allow for easy opening of view controllers
        // in storyboard
        let viewController : Self = UIStoryboard.mainStoryboard.instantiateViewController()
        viewController.viewModel = viewModel
        return viewController
    }
}

因此,我的应用程序中的所有viewModel都符合ViewModel,强制它们实现服务类型。例如,我的LoginModel看起来像这样

 struct LoginModel : ViewModel{

    // service type
    typealias Services = LoginService

    // init service
    var services : LoginService
    init(withServices services: LoginService) {
        self.services = services
    }

    /// calls login service - attempts login api
    func attemptLogin() {
        services.login()
    }
}

所以这是一个实现这个

的viewController的例子
class SecondController: UIViewController, ViewModelBased  {

    var viewModel: LoginModel!

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    @IBAction func loginTest() {
        viewModel.services.onLoginSuccess = { isVerified in

            print(isVerified)
        }

        viewModel.services.onLoginFailure = { errorCode in

            print(errorCode)
        }

        viewModel.attemptLogin()
    }
}

所以把它放在一起,这允许app启动一个viewController并传入一个像这样的viewModel

   let loginModel = LoginModel(withServices: LoginService())
        let controller = SecondController.instantiateController(with: loginModel)
        self.navigationController?.pushViewController(controller, animated: true)

这一切都很有效,但我遇到的问题是,相关的Type目前可以是任何类型。理想情况下,我希望此associatedType符合ViewModel协议。但是当我尝试这个时

protocol ViewModelBased: class {
    associatedtype ViewModelType : ViewModel
    var viewModel: ViewModelType { get set }
}

我的SecondController现在抛出一个错误,现在强迫我初始化LoginModel

var viewModel : LoginModel = LoginModel(withServices: LoginService())

但是这不再使用依赖注入,因为viewController现在负责创建viewModel实例并且知道viewModel类的行为。

我有办法解决这个问题吗?如果有人能给我一些关于我做错的信息,我将非常感激。

1 个答案:

答案 0 :(得分:1)

您可以将Optional扩展为有条件地符合ViewModel

extension Optional: ViewModel where Wrapped: ViewModel {

    typealias Services = Wrapped.Services

    init(withServices services: Services) {
        self = Wrapped(withServices: services)
    }

}

现在,您的SecondController将其模型声明为var viewModel: LoginModel?,默认情况下会将其初始化为nil,同时仍确保其ViewModelTypeViewModel实际上需要创建它的实例。