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
Subclassing VS Extension in Swift
Get Rawvalue from Enum in a Generic Function
Uialertcontroller Change Font Color
How to Check If a String Contains Chinese in Swift
Compiler Error: Invalid Library File - Corelocation
Swift: How to Animate the Rowheight of a Uitableview
React Native Sending Events to JavaScript in Swift
How to Mock Uiapplication in Swift
Creating Gif Image Color Maps in iOS 11
Pop to Root View Using Tab Bar in Swiftui
Swiftui - Using Geometryreader Without Modifying the View Size
How to Prove "Copy-On-Write" on String Type in Swift
How to Add Initializers in Extensions to Existing Uikit Classes Such as Uicolor
What Does the Swift 'Mutating' Keyword Mean
Can't Able to Get Video Tracks from Avurlasset for Hls Videos(.M3U8 Format) for Avplayer