协议内部的Swift协议

时间:2019-11-11 12:11:17

标签: swift swift-protocols

我遇到了Swift协议的问题。我有这样的东西:

protocol OnboardingPage {
  var viewModel: OnboardingPageViewModel! { get }
}

protocol OnboardingPageViewModel {
  func getValue() -> Bool
}

// ---

class FirstNameViewController: UIViewController, OnboardingPage {
  var viewModel: FirstNameViewModel!
}

class FirstNameViewModel: OnboardingPageViewModel {
  func getValue() -> Bool {
    return true
  }
}

class GenderViewController: UIViewController, OnboardingPage {
  var viewModel: GenderViewModel!
}

class GenderViewModel: OnboardingPageViewModel {
  func getValue() -> Bool {
    return false
  }
}

我有一堆确认使用OnboardingPage协议的视图控制器,并且它们都有一个都符合OnboardingPageViewModel协议的视图模型。

但是可悲的是,上面的代码无法正常工作:它说FirstNameViewController未确认OnboardingPage协议,因为它的viewModel属性的类型为FirstNameViewModel而不是OnboardingPageViewModel-即使FirstNameViewModelOnboardingPageViewModel

我该如何解决?

2 个答案:

答案 0 :(得分:4)

使用关联类型 OnboardingPageViewModel类型ViewModel

已编辑

protocol OnboardingPage {
    associatedtype ViewModel: OnboardingPageViewModel

    var viewModel: ViewModel { get }
}

protocol OnboardingPageViewModel {
    func getValue() -> Bool
}

class FirstNameViewController: OnboardingPage {
    let viewModel: FirstNameViewModel

    init(viewModel: FirstNameViewModel) {
        self.viewModel = viewModel
    }
}

class FirstNameViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return true
    }
}

class GenderViewController: OnboardingPage {
    let viewModel: GenderViewModel

    init(viewModel: GenderViewModel) {
        self.viewModel = viewModel
    }
}

class GenderViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return false
    }
}

答案 1 :(得分:0)

您可以删除关联类型来解决页面数组issue

protocol OnboardingPage {    
    var viewModel: OnboardingPageViewModel { get }
}

protocol OnboardingPageViewModel {
    func getValue() -> Bool
}

class FirstNameViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return true
    }
}

class FirstNameViewController: OnboardingPage {
    let viewModel: OnboardingPageViewModel

    init(viewModel: FirstNameViewModel) {
        self.viewModel = viewModel
    }
}

class GenderViewModel: OnboardingPageViewModel {
    func getValue() -> Bool {
        return false
    }
}

class GenderViewController: OnboardingPage {
    let viewModel: OnboardingPageViewModel

    init(viewModel: GenderViewModel) {
        self.viewModel = viewModel
    }
}

用法:

let nameModel = FirstNameViewModel()
let genderModel = GenderViewModel()

let array: [OnboardingPage] = [FirstNameViewController(viewModel: nameModel), GenderViewController(viewModel: genderModel)]

for page in array {
    print(page.viewModel.getValue())
}

但是,以这种方式,您的模型的具体类型将对符合OnboardingPage的类的调用者不可用。您可以尝试通过在OnboardingPage内实施访客模式或增强其功能来解决该问题。

当然,您始终可以使用Cruz示例编写let pages: [Any] = [FirstNameViewController(), GenderViewController()],但是这样做很丑陋,如果可能,我强烈建议您不要在API中使用Any