Error Using Associated Types and Generics

Swift protocol with associatedtype error

You have a couple of options.

Firstly you can use a specific type of FooDelegate, like SomeFoo:

class Bar {
//In Bar instance runtime ,it will call delegate to do something...
var delegate: SomeFoo!
}

Or you can make Bar generic and define what type of Item the delegate needs:

class Bar<F> where F: FooDelegate, F.Item == Int {
//In Bar instance runtime ,it will call delegate to do something...
var delegate: F!
}

Swift: Question on Generic Functions and Associated Types

You are getting this error, because indeed self does not match all given constraints. By stating func test<M: Model>(model: M) -> M where M.ModelType == T you only say that this type M (which is only exists in the context of a call to your test function) is to be a Model, which's ModelType is the same as the ModelType of self. That does not mean, however, that Self and M are equivalent.

A little example using your types from above:

Assuming self is an instance of Implementation<Int>. In the context of a call to test(model:), M could be either of Implementation<Int> or Alternative<Int>. Returning self wouldn't be a problem in the first case, as both instances have the same type. However, in the second case you could assign an Implementation<Int> to an Alternative<Int>.

var a = Alternative<Int>()
let x = Implementation<Int>()
// This would assign something of type `Implementation<Int>` to a variable that
// may only contain `Alternative<Int>`.
var a = x.test(model: a)

I am sure there is a method to do what you want to achieve in Swift, but the solution totally depends on what that is.

Why don't associated types for protocols use generic type syntax in Swift?

This has been covered a few times on the devlist. The basic answer is that associated types are more flexible than type parameters. While you have a specific case here of one type parameter, it is quite possible to have several. For instance, Collections have an Element type, but also an Index type and a Generator type. If you specialized them entirely with type parameterization, you'd have to talk about things like Array<String, Int, Generator<String>> or the like. (This would allow me to create arrays that were subscripted by something other than Int, which could be considered a feature, but also adds a lot of complexity.)

It's possible to skip all that (Java does), but then you have fewer ways that you can constrain your types. Java in fact is pretty limited in how it can constrain types. You can't have an arbitrary indexing type on your collections in Java. Scala extends the Java type system with associated types just like Swift. Associated types have been incredibly powerful in Scala. They are also a regular source of confusion and hair-tearing.

Whether this extra power is worth it is a completely different question, and only time will tell. But associated types definitely are more powerful than simple type parameterization.

Why do I get a Swift error with nested associated types

Protocols don't conform to protocols, so although it seems like

typealias Delegate = SomethingDidHappenDelegate

satisfies the Delegate associated type requirement of StructuredNotificationIdentifier, it doesn't, because Delegate must conform to StructuredNotificationUserInfoType. And SomethingDidHappenDelegate, being a protocol, does not conform to any protocols.

I tried to come up with a workaround, and this is what I've got:

protocol StructuredNotificationIdentifier {
associatedtype Delegate
associatedtype UserInfoType
static var notification: Notification.Name { get }
}

protocol SomethingDidHappenDelegate {
func somethingDidHappen(_ value: String)
}

enum SomethingDidHappenNotification: StructuredNotificationIdentifier {
typealias Delegate = SomethingDidHappenDelegate
typealias UserInfoType = String
static var notification: Notification.Name { Notification.Name("SomethingDidHappenNotification") }
}

extension NotificationCenter {
func post<T: StructuredNotificationIdentifier>(identifier: T, userInfo: T.UserInfoType) {
post(name: type(of: identifier).notification, object: nil, userInfo: ["info": userInfo])
}
}

I moved UserInfoType into StructuredNotificationIdentifier as well and deleted the constraint on Delegate, because I don't think it is possible to access T.Delegate.UserInfoType when Delegate is a protocol. The error message I got when I tried to do that was pretty clear:

Associated type 'UserInfoType' can only be used with a concrete type or generic parameter base

Unable to use associatedType as method returnType in swift as compiler throws Ambiguous inference of associated type

Using an opaque return type (i.e. some) is not required for what you want to do. When you go to implement the Office protocol, just return the actual type from the function and computed property you specified and the compiler will infer the associatedtype for you:

protocol DummyOffice {}

struct EmptyOffice: DummyOffice {}

protocol Office {
associatedtype SubBranch: DummyOffice
var subBranch: SubBranch { get }
func getSubBranch() -> SubBranch
}

struct Apple: Office {
let emptyOffice = EmptyOffice()
func getSubBranch() -> EmptyOffice {
return EmptyOffice()
}

var subBranch: EmptyOffice {
return EmptyOffice()
}
}

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<DataSource: DataSourceCompatible>: UIView {
private var viewModel: DataSource.CellModel!

func configure(viewModel: DataSource.CellModel) {
self.viewModel = viewModel
}
}

let vc = DataSourceViewController<AddressBookViewModel>()


Related Topics



Leave a reply



Submit