Issue with `Protocol can only be used as a generic constraint because it has Self or associated type...
up vote
3
down vote
favorite
I'm trying to generate a ViewModel that conforms to a Protocol Protocoling
, the protocol is generic, and has an associated type.
There are a few ViewModel's that conform to the protocol, so I am trying to create a factory for the viewModel.
I have encotuntered the following error by Swift:
Protocol can only be used as a generic constraint because it has Self or associated type requirements
Example code:
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> Protocoling {
switch type {
case .name: return NameViewModel(name: "Gil")
case .address: return AddressViewModel(address: ["Israel", "Tel Aviv"])
}
}
}
The error is in func viewModel(forType type: MyTypes) -> Protocoling
.
Is there a way to solve this issue?
ios swift
add a comment |
up vote
3
down vote
favorite
I'm trying to generate a ViewModel that conforms to a Protocol Protocoling
, the protocol is generic, and has an associated type.
There are a few ViewModel's that conform to the protocol, so I am trying to create a factory for the viewModel.
I have encotuntered the following error by Swift:
Protocol can only be used as a generic constraint because it has Self or associated type requirements
Example code:
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> Protocoling {
switch type {
case .name: return NameViewModel(name: "Gil")
case .address: return AddressViewModel(address: ["Israel", "Tel Aviv"])
}
}
}
The error is in func viewModel(forType type: MyTypes) -> Protocoling
.
Is there a way to solve this issue?
ios swift
From the current example I think a factory model is not the best one. Maybe you could consider another way of doing it? For example using an enum with associated values (see docs.swift.org/swift-book/LanguageGuide/Enumerations.html).
– Qbyte
Nov 11 at 10:22
@Qbyte I thought about it, but the enum represents a state in my flow, And according to the state I initialise a ViewModel, the thing is, I don't want to bound the enum to a viewModel.
– Gil Polak
Nov 11 at 11:07
add a comment |
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I'm trying to generate a ViewModel that conforms to a Protocol Protocoling
, the protocol is generic, and has an associated type.
There are a few ViewModel's that conform to the protocol, so I am trying to create a factory for the viewModel.
I have encotuntered the following error by Swift:
Protocol can only be used as a generic constraint because it has Self or associated type requirements
Example code:
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> Protocoling {
switch type {
case .name: return NameViewModel(name: "Gil")
case .address: return AddressViewModel(address: ["Israel", "Tel Aviv"])
}
}
}
The error is in func viewModel(forType type: MyTypes) -> Protocoling
.
Is there a way to solve this issue?
ios swift
I'm trying to generate a ViewModel that conforms to a Protocol Protocoling
, the protocol is generic, and has an associated type.
There are a few ViewModel's that conform to the protocol, so I am trying to create a factory for the viewModel.
I have encotuntered the following error by Swift:
Protocol can only be used as a generic constraint because it has Self or associated type requirements
Example code:
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> Protocoling {
switch type {
case .name: return NameViewModel(name: "Gil")
case .address: return AddressViewModel(address: ["Israel", "Tel Aviv"])
}
}
}
The error is in func viewModel(forType type: MyTypes) -> Protocoling
.
Is there a way to solve this issue?
ios swift
ios swift
asked Nov 11 at 9:00
Gil Polak
1218
1218
From the current example I think a factory model is not the best one. Maybe you could consider another way of doing it? For example using an enum with associated values (see docs.swift.org/swift-book/LanguageGuide/Enumerations.html).
– Qbyte
Nov 11 at 10:22
@Qbyte I thought about it, but the enum represents a state in my flow, And according to the state I initialise a ViewModel, the thing is, I don't want to bound the enum to a viewModel.
– Gil Polak
Nov 11 at 11:07
add a comment |
From the current example I think a factory model is not the best one. Maybe you could consider another way of doing it? For example using an enum with associated values (see docs.swift.org/swift-book/LanguageGuide/Enumerations.html).
– Qbyte
Nov 11 at 10:22
@Qbyte I thought about it, but the enum represents a state in my flow, And according to the state I initialise a ViewModel, the thing is, I don't want to bound the enum to a viewModel.
– Gil Polak
Nov 11 at 11:07
From the current example I think a factory model is not the best one. Maybe you could consider another way of doing it? For example using an enum with associated values (see docs.swift.org/swift-book/LanguageGuide/Enumerations.html).
– Qbyte
Nov 11 at 10:22
From the current example I think a factory model is not the best one. Maybe you could consider another way of doing it? For example using an enum with associated values (see docs.swift.org/swift-book/LanguageGuide/Enumerations.html).
– Qbyte
Nov 11 at 10:22
@Qbyte I thought about it, but the enum represents a state in my flow, And according to the state I initialise a ViewModel, the thing is, I don't want to bound the enum to a viewModel.
– Gil Polak
Nov 11 at 11:07
@Qbyte I thought about it, but the enum represents a state in my flow, And according to the state I initialise a ViewModel, the thing is, I don't want to bound the enum to a viewModel.
– Gil Polak
Nov 11 at 11:07
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
You can use a protocol with an associated type (PAT) as a return type like that without more constraint because the compiler needs to know which type to use.
In your case you must use a technic called the type erasure to be able to work with any Protocoling:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
This will allow you to "erase" the associated type of your protocol and return an Any
version of your view model.
In order to understand why the PAT needs to work like that I like the following example: the Equatable
protocol (which is a PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
This function uses the Self
type which is an associated type. You want to use it in the next generic function:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
Here the compiler will trigger this error: Protocol can only be used as a generic constraint because it has Self or associated type requirements
. Why? Lets take this example:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
There is no reason to compare tomatoes and salads. The associated type Self
is not the same. To avoid this error in this case you need to constraint the Self
type as following:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
Now you know the T
are equatables and with the same associated types.
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
You can use a protocol with an associated type (PAT) as a return type like that without more constraint because the compiler needs to know which type to use.
In your case you must use a technic called the type erasure to be able to work with any Protocoling:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
This will allow you to "erase" the associated type of your protocol and return an Any
version of your view model.
In order to understand why the PAT needs to work like that I like the following example: the Equatable
protocol (which is a PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
This function uses the Self
type which is an associated type. You want to use it in the next generic function:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
Here the compiler will trigger this error: Protocol can only be used as a generic constraint because it has Self or associated type requirements
. Why? Lets take this example:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
There is no reason to compare tomatoes and salads. The associated type Self
is not the same. To avoid this error in this case you need to constraint the Self
type as following:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
Now you know the T
are equatables and with the same associated types.
add a comment |
up vote
0
down vote
You can use a protocol with an associated type (PAT) as a return type like that without more constraint because the compiler needs to know which type to use.
In your case you must use a technic called the type erasure to be able to work with any Protocoling:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
This will allow you to "erase" the associated type of your protocol and return an Any
version of your view model.
In order to understand why the PAT needs to work like that I like the following example: the Equatable
protocol (which is a PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
This function uses the Self
type which is an associated type. You want to use it in the next generic function:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
Here the compiler will trigger this error: Protocol can only be used as a generic constraint because it has Self or associated type requirements
. Why? Lets take this example:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
There is no reason to compare tomatoes and salads. The associated type Self
is not the same. To avoid this error in this case you need to constraint the Self
type as following:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
Now you know the T
are equatables and with the same associated types.
add a comment |
up vote
0
down vote
up vote
0
down vote
You can use a protocol with an associated type (PAT) as a return type like that without more constraint because the compiler needs to know which type to use.
In your case you must use a technic called the type erasure to be able to work with any Protocoling:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
This will allow you to "erase" the associated type of your protocol and return an Any
version of your view model.
In order to understand why the PAT needs to work like that I like the following example: the Equatable
protocol (which is a PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
This function uses the Self
type which is an associated type. You want to use it in the next generic function:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
Here the compiler will trigger this error: Protocol can only be used as a generic constraint because it has Self or associated type requirements
. Why? Lets take this example:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
There is no reason to compare tomatoes and salads. The associated type Self
is not the same. To avoid this error in this case you need to constraint the Self
type as following:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
Now you know the T
are equatables and with the same associated types.
You can use a protocol with an associated type (PAT) as a return type like that without more constraint because the compiler needs to know which type to use.
In your case you must use a technic called the type erasure to be able to work with any Protocoling:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
This will allow you to "erase" the associated type of your protocol and return an Any
version of your view model.
In order to understand why the PAT needs to work like that I like the following example: the Equatable
protocol (which is a PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
This function uses the Self
type which is an associated type. You want to use it in the next generic function:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
Here the compiler will trigger this error: Protocol can only be used as a generic constraint because it has Self or associated type requirements
. Why? Lets take this example:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
There is no reason to compare tomatoes and salads. The associated type Self
is not the same. To avoid this error in this case you need to constraint the Self
type as following:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
Now you know the T
are equatables and with the same associated types.
answered Nov 11 at 9:58
Yannick Loriot
5,94622545
5,94622545
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53247217%2fissue-with-protocol-can-only-be-used-as-a-generic-constraint-because-it-has-sel%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
From the current example I think a factory model is not the best one. Maybe you could consider another way of doing it? For example using an enum with associated values (see docs.swift.org/swift-book/LanguageGuide/Enumerations.html).
– Qbyte
Nov 11 at 10:22
@Qbyte I thought about it, but the enum represents a state in my flow, And according to the state I initialise a ViewModel, the thing is, I don't want to bound the enum to a viewModel.
– Gil Polak
Nov 11 at 11:07