Unable to use protocol as associatedtype in another protocol in Swift
The problem, which David has already alluded to, is that once you constrain a protocol's associatedtype
to a specific (non @objc
) protocol, you have to use a concrete type to satisfy that requirement.
This is because protocols don't conform to themselves – therefore meaning that you cannot use Address
to satisfy the protocol's associated type requirement of a type that conforms to Validator
, as Address
is not a type that conforms to Validator
.
As I demonstrate in my answer here, consider the counter-example of:
protocol Validator {
init()
}
protocol Address : Validator {}
protocol FormRepresentable {
associatedtype ValueWrapper: Validator
}
extension FormRepresentable {
static func foo() {
// if ValueWrapper were allowed to be an Address or Validator,
// what instance should we be constructing here?
// we cannot create an instance of a protocol.
print(ValueWrapper.init())
}
}
// therefore, we cannot say:
enum AddressFrom : FormRepresentable {
typealias ValueWrapper = Address
}
The simplest solution would be to ditch the Validator
protocol constraint on your ValueWrapper
associated type, allowing you to use an abstract type in the method argument.
protocol FormRepresentable {
associatedtype ValueWrapper
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: Address) -> String {
// ...
}
}
If you need the associated type constraint, and each AddressFrom
instance only expects a single concrete implementation of Address
as an input – you could use generics in order for your AddressFrom
to be initialised with a given concrete type of address to be used in your method.
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: T) -> String {
// ...
}
}
// replace ShippingAddress with whatever concrete type you want AddressFrom to use
let addressFrom = AddressFrom.Address1
However, if you require both the associated type constraint and each AddressFrom
instance must be able to handle an input of any type of Address
– you'll have use a type erasure in order to wrap an arbitrary Address
in a concrete type.
protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}
struct AnyAddress : Address {
private var _base: Address
var addressLine1: String {
get {return _base.addressLine1}
set {_base.addressLine1 = newValue}
}
var country: String {
get {return _base.country}
set {_base.country = newValue}
}
var city: String {
get {return _base.city}
set {_base.city = newValue}
}
init(_ base: Address) {
_base = base
}
}
enum AddressFrom : Int, FormRepresentable {
// ...
func valueForDetail(valueWrapper: AnyAddress) -> String {
// ...
}
}
let addressFrom = AddressFrom.Address1
let address = ShippingAddress(addressLine1: "", city: "", country: "")
addressFrom.valueForDetail(AnyAddress(address))
Reuse associated type from protocol in another protocol
The requestType
associated type isn't necessary in the RequestProcessor
protocol, as it is implicit based on the requester
associated type.
You should just be able to define the RequestProcessor
protocol like this:
protocol RequestProcesser: AnyObject {
associatedtype requester: Requester
var request: requester { get set }
}
And use it like this:
class MyRequester: Requester {
typealias requestType = Int
var response: ((Int) -> ())?
}
class MyRequestProcessor: RequestProcesser {
typealias requester = MyRequester
var request = MyRequester()
}
Now, the response
closure parameter inside the process()
method will take on the associated type of the MyRequester
protocol (in this case Int
), so no casting is necessary.
Protocol inheritance with associated type
Once a protocol has an associated type, that protocol can't be used as a type by itself for instance declarations-- only for generic constraints and declaring conformance.
So in this case, Swift is saying "yeah, but what is the concrete type for StartRouterProtocol
's associated type?"
In this case, it's asking you to either:
- Use a concrete type directly, i.e.
let router: MyStartViewClass
with this conformance declaration, elsewhere:class MyStartViewClass: StartRouterProtocol { ... }
) - OR, push the need for a concrete type up one layer, as a generic constraint i.e.
class MyRouterController {
let router: T
}
This is probably not what you were hoping for, but unfortunately associated types add complexity to how you use protocols, especially if you're familiar with generics & interfaces from other languages. (i.e. Java/C# interfaces)
You can work around some aspects of associated types by using a concept called "type erasure" -- but that can cause other problems and complexity.
Here's some further reading that may help: https://medium.com/monstar-lab-bangladesh-engineering/swift-from-protocol-to-associatedtype-then-type-erasure-a4093f6a2d08
Protocol restriction on another protocol associated type
As far as I can see MyStruct? (aka Optional) and Codable? (aka
Optional) are equivalent?
No, they are not. MyStruct
conforms to Codable
but is not equivalent of it.
As in: every MyStruct is Codable but not every Codable is MyStruct.
You can try changing MyType == Codable?
to MyType: Codable
:
protocol MyProtocolCodable where Self: MyProtocol, MyType: Codable {}
as MyStruct
is not equal to Codable?
.
returning swift protocol associated type in multiple methods
The solution you came up with by playing around is exactly what you need
As mentioned elsewhere, the main issue with your first protocol is that you're enforcing createSomeView()
createAnotherView()
both return the same type. While ViewA()
and ViewB()
are both candidates for V
, since they conform to View
they are still different types, and therefore cannot BOTH be V
in a given object.
By defining both V1
and V2
, you allow for each function to return a different type, or the same type, it's all acceptable. By making both V1
and V2
require View
conformance, you allow for the some View
syntax
Use protocol with constrained associated type as property in Swift
To expand on my questions in the comments, looking at this code it looks like it would be exactly as flexible without adding AddressBookCellModelType
or AddressBookViewModelType
, and this would also get rid of the headaches, while still being generic over DataSourceCompatible
.
// This protocol is fine and very useful for making reusable view controllers. Love it.
protocol DataSourceCompatible {
associatedtype CellModel
func cellModelForItem(at indexPath: IndexPath) -> CellModel
}
// No need for a protocol here. The struct is its own interface.
// This ensures value semantics, which were being lost behind the protocol
// (since a protocol does not promise value semantics)
struct AddressBookCellModel {
var name: String
var photo: UIImage?
var isInvited: Bool
}
// AddressBookViewModel conforms to DataSourceCompatible
// Its conformance sets CellModel to AddressBookCellModel without needing an extra protocol
class AddressBookViewModel: DataSourceCompatible {
let sectionedContacts: [[AddressBookCellModel]] = []
func cellModelForItem(at indexPath: IndexPath) -> AddressBookCellModel {
return sectionedContacts[indexPath.section][indexPath.row]
}
}
class AddressBookViewController: UIViewController {
private var viewModel: AddressBookViewModel!
func configure(viewModel: AddressBookViewModel) {
self.viewModel = viewModel
}
}
Doing it this way allows for a generic VC without introducing more pieces that required:
class DataSourceViewController: UIView {
private var viewModel: DataSource.CellModel!
func configure(viewModel: DataSource.CellModel) {
self.viewModel = viewModel
}
}
let vc = DataSourceViewController()
What does Protocol ... can only be used as a generic constraint because it has Self or associated type requirements mean?
Protocol Observing
inherits from protocol Hashable
, which in turn inherits from protocol Equatable
. Protocol Equatable
has the following requirement:
func ==(lhs: Self, rhs: Self) -> Bool
And a protocol that contains Self
somewhere inside it cannot be used anywhere except in a type constraint.
Here is a similar question.
Protocol associatedType and <>
Defined associated type makes classes which conform protocol strong typed. This provide compile-time error handling.
In other hand, generic type makes classes which conform protocol more flexible.
For example:
protocol AssociatedRepository {
associatedtype T
func add(data : T) -> Bool
}
protocol GenericRepository {
func add(data : T) -> Bool
}
class A: GenericRepository {
func add(data : T) -> Bool {
return true
}
}
class B: AssociatedRepository {
typealias T = UIViewController
func add(data : T) -> Bool {
return true
}
}
class A
could put any class into add(data:)
function, so you need to makes your sure that function handle all cases.
A().add(data: UIView())
A().add(data: UIViewController())
both would be valid
But for class B
you will get compile-time error when you will try to put anything except UIViewController
B().add(data: UIView()) // compile-time error here
B().add(data: UIViewController())
Related Topics
Interface Builder: What Are the Uiview's Layout iOS 6/7 Deltas For
Unwind Segue Not Working in iOS 8
Uipageviewcontroller Gesture Recognizers
How to Programmatically Connect to a Wifi Network Given the Ssid and Password
How to Implement Method Swizzling Swift 3.0
How to Deserialize a JSON String into an Nsdictionary? (For iOS 5+)
How to Make Drawrect Work Right Now
Array from Dictionary Keys in Swift
Detect Permission of Camera in iOS
Correct Singleton Pattern Objective C (Ios)
How to Set Bold and Italic on Uilabel of Iphone/Ipad
How to Import Own Classes from Your Own Project into a Playground
Retrieving Carrier Name from iPhone Programmatically
Xcode 9 Gm - Wkwebview Nscoding Support Was Broken in Previous Versions