I know this question has been asked before but I have no idea how to solve this current problem. I have defined a protocol MultipleChoiceQuestionable with an associatedtype property:
protocol Questionable {
var text: String {get set}
var givenAnswer: String? {get set}
}
protocol MultipleChoiceQuestionable: Questionable {
associatedtype Value
var answers: Value { get }
}
struct OpenQuestion: Questionable {
var text: String
var givenAnswer: String?
}
struct MultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [String]
var text: String
var givenAnswer: String?
var answers: Value
}
struct NestedMultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [MultipleChoiceQuestion]
var text: String
var answers: Value
var givenAnswer: String?
}
Types which conform to this protocol are saved in an array as Questionable like so:
// This array contains OpenQuestion, MultipleChoiceQuestion and NestedMultipleChoiceQuestion
private var questions: [Questionable] = QuestionBuilder.createQuestions()
Somewhere in my code I want to do something like:
let question = questions[index]
if let question = question as? MultipleChoiceQuestionable {
// Do something with the answers
question.answers = .....
}
This is not possible because Xcode warns me: Protocol MultipleChoiceQuestionable can only be used as a generic constraint. I've been searching around on how to solve this issue since generics are quite new for me. Apparently Swift doesn't know the type of the associatedtype during compile time which is the reason this error is thrown. I've read about using type erasure but I don't know if that solves my problem. Maybe I should use generic properties instead or perhaps my protocols are defined wrong?
If the action you want to apply to your sub-protocol objects does not rely on the associated type (i.e. neither has the a generic parameter nor returns the generic type) you can introduce an auxiliary protocol which just exposes the properties/methods you need, let your type conform to that protocol, and declare the question in terms of that protocol.
For example, if you just want to know some info about the question:
protocol MultipleChoiceInfo {
var numberOfAnswers: Int { get }
}
extension MultipleChoiceQuestion: MultipleChoiceInfo {
var numberOfAnswers: Int { return answers.count }
}
// do the same for the other multiple-choice types
Then you can access the questions through the new protocol like this:
let question = questions[index]
if let info = question as? MultipleChoiceInfo {
print(info.numberOfAnswers)
}
As I said, if you cannot provide an abstract (non-generic) interface then this won't work.
EDIT
If you need to process the generic data inside your questions you can extract the logic depending on the concrete generic type into another "processing" type which provides an interface to your questions. Each question type then dispatches its data to the processor interface:
protocol MultipleChoiceProcessor {
func process(stringAnswers: [String])
func process(nestedAnswers: [MultipleChoiceQuestion])
}
protocol MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor)
}
extension MultipleChoiceQuestion: MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor) {
processor.process(stringAnswers: answers)
}
}
Just create a type conforming to MultipleChoiceProcessor and do the type-check dance again:
if let proxy = question as? MultipleChoiceProxy {
proxy.apply(processor:myProcessor)
}
As an aside, if you don't have more protocols and structs in your real application, you might also just ditch the protocol stuff altogether... for this kind of problem it seems a bit over-engineered.
Related
I'm currently experimenting with Generics in Swift and came to some problem with casting some types around such as SomeType<Protocol> to SomeType<ProtocolImpl>. So basically I have some type that takes a generic parameter which is handled as a Protocol and which at a later point is casted to a more concrete type. My question is if that isn't possible to do?
/// 'dict' is of type [String: SomeType<Protocol>]
if let element = dict["str"], // 'element' here is of type SomeType<Protocol>
let castedElement = element as? SomeType<ProtocolImpl> { // This is always false
return castedElement // Here I want to return castedElement with type SomeType<ProtocolImpl>
}
Is there any way to make this cast work? I'm already working on another solution for my problem, but I'm still interested if there's a way to make this work somehow.
Edit: Because #jtbandes wanted a example he can paste somewhere, here:
class SomeType<T> {
let value: T
init(value: T) {
self.value = value
}
}
protocol Protocol {}
class ProtocolImpl: Protocol {}
var dict: [String: SomeType<Protocol>] = ["str": SomeType(value: ProtocolImpl())]
if let element = dict["str"],
let castedElement = element as? SomeType<ProtocolImpl> {
print(castedElement.value) // I want to get here
}
Long story short, generics in Swift are not covariant, which means that SomeType< ProtocolImpl> is not convertible SomeType<Protocol>, even if ProtocolImpl conforms to Protocol. Thus the direct answer to your question is: this is not currently possible in Swift.
However you might ask yourself is why do you need the downcast in the first place. As you're storing the instances in a container, polymorphic behaviour might be better suited. You could declare the functionality you need to access as part of the protocol, and access is though the protocol interface. This way you don't need to know which is the concrete implementation under the hood, which is one of the main reason of using a protocol.
It's hard for me to tell what you're trying to achieve from the question. Still, maybe the below will help you.
class SomeType<T>: Protocol { // Maybe SomeType IS your ProtoImpl?
let value: T
init(value: T) {
self.value = value
}
}
protocol Protocol {}
//class ProtocolImpl: Protocol {}
//var dict: [String: SomeType<Protocol>] = ["str": SomeType(value: ProtocolImpl())]
var dict: [String: Protocol] = ["str1": SomeType<String>(value: "Some Type"),
"str2": SomeType<Int>(value: 1)
]
if let castedElement = dict["str1"] as? SomeType<String> {
print(castedElement.value) // --> "Some Type"
}
if let castedElement = dict["str2"] as? SomeType<Int> {
print(castedElement.value) // --> "1"
}
I would like to know if I can find the name of a type conforming to a given protocol, from that protocol. I was thinking of protocol extension to avoid repetition in every type conforming to that protocol. I tried this:
protocol T {
var type: String { get }
}
extension T {
var type: String {
return String(describing: T.self)
}
}
struct S: T {}
let s = S()
print(s.type)
But this is showing T instead of S.
Is there any way I can do this?
Naturally it's printing T, that's what you asked for with String(describing: T.self). T is always the protocol itself.
Inside the protocol extension Self (capital 'S') is how you refer to the conforming type.
So the extension should be:
extension T {
var typeName: String {
return String(describing: Self.self)
}
}
Aside, the built-in type(of:) function already gives you the dynamic type of any object, so it's not clear that you really need to duplicate this functionality on your own.
This question already has answers here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
(3 answers)
Closed 5 years ago.
Can anyone explain why this doesn't compile?
// In MyViewKit.framework
protocol UserRenderable {
var name : String { get }
}
protocol PostRenderable {
var title: String { get }
var author: UserRenderable { get }
}
// In MyDataKit.framework
struct User {
let id: String
let name : String
}
struct Post {
let id: String
let title: String
let author: User
}
// In MyApp
extension User : UserRenderable {}
extension Post: PostRenderable {}
But this (below) does? Apparently, read-only property requirements in a protocol can't be satisfied by a property which conforms to them (according to the answers to this question.)
What is typealias doing here?
// In MyViewKit.framework
protocol UserRenderable {
var name : String { get }
}
protocol PostRenderable {
var title: String { get }
var author: UserRenderable { get }
}
// In MyDataKit.framework
struct User {
let id: String
let name : String
}
struct Post {
let id: String
let title: String
let author: User
}
// In MyApp
extension User : UserRenderable {}
extension Post: PostRenderable {
// what is type alias doing here? Note: there are no associated types.
typealias User = UserRenderable
}
UPDATE Just clarifying why IMO this isn't a duplicate to Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?. I understand the limitation in the Swift language: I'm trying to also understanding whether or not this is a valid workaround.
A typealias has nothing to do with associated types in this particular instance. Typealias is just a way to let the compiler know what is the associated type in a protocol if it cannot be inferred, but a typealias can be used in different contexts as well.
Your second example compiles, because you change User from a concrete type to a typealias for UserRenderable in your Post extension. If you check the type of author in Xcode, you'll see that its type is actually UserRenderable after the extension rather than User. Due to the typealias, inside Post, User won't refer to the User struct, but rather it will be UserRenderable due to the local overwrite you did with the typealias.
Is there a point to declaring a static function on a protocol? The client using the protocol has to call the function on a type conforming to the protocol anyway right? That breaks the idea of not having to know the type conforming to the protocol IMO. Is there a way to call the static function on the protocol in a way where I don't have to know the actual type conforming to my protocol?
Nice question. Here is my humble point of view:
Is there a point to declaring a static function on a protocol?
Pretty much the same as having instance methods declared in a protocol.
The client using the protocol has to call the function on a type conforming to the protocol anyway right?
Yes, exactly like instance functions.
That breaks the idea of not having to know the type conforming to the protocol IMO.
Nope. Look at the following code:
protocol Feline {
var name: String { get }
static func createRandomFeline() -> Feline
init()
}
extension Feline {
static func createRandomFeline() -> Feline {
return arc4random_uniform(2) > 0 ? Tiger() : Leopard()
}
}
class Tiger: Feline {
let name = "Tiger"
required init() {}
}
class Leopard: Feline {
let name = "Leopard"
required init() {}
}
let feline: Feline = arc4random_uniform(2) > 0 ? Tiger() : Leopard()
let anotherFeline = feline.dynamicType.createRandomFeline()
I don't know the real type inside the variable feline. I just know that it does conform to Feline. However I am invoking a static protocol method.
Is there a better way to do this?
I see, you would like to call a static method/function declared in a protocol without creating a value that conforms to the protocol.
Something like this:
Feline.createRandomFeline() // DANGER: compiler is not happy now
Honestly I don't know the reason why this is not possible.
yes this is possible:
Swift 3
protocol Thing {
static func genericFunction()
}
//... in another file
var things:[Thing] = []
for thing in things {
type(of: thing).genericFunction()
}
Thank you #appzYourLife for the help! Your answer inspired my answer.
#appzYourLife answered my question. I had an underlying issue I was trying to resolve and the following code resolves my issue, so I'll post this here, maybe it helps someone with my same underlying question:
protocol MyProtocol {
static func aStaticFunc()
}
class SomeClassThatUsesMyProtocolButDoesntConformToIt {
var myProtocolType: MyProtocol.Type
init(protocolType: MyProtocol.Type) {
myProtocolType = protocolType
}
func aFunction() {
myProtocolType.aStaticFunc()
}
}
I created another solution for this case. IMHO this is quite clean and simple.
First, create a protocol for accessing instance type.
protocol TypeAccessible {
func type() -> Self.Type
}
extension TypeAccessible {
func type() -> Self.Type {
return Swift.type(of: self)
}
}
then create your concrete class as here. The point is your protocol should conform to TypeAccessible protocol.
protocol FooProtocol: TypeAccessible {
static func bar()
}
class Foo: FooProtocol {
static func bar() { }
}
On call site use it as
let instance: FooProtocol = Foo()
instance.type().bar()
For further use cases, just make sure your protocols conform to TypeAccessible and that's all.
A little late to the party on this one.
Here's my solution for "adding" static properties/functions/types to a protocol using typealias.
For example:
enum PropertyScope {
case all
case none
}
struct PropertyNotifications {
static var propertyDidChange =
Notification.Name("propertyDidChangeNotification")
}
protocol Property {
typealias Scope = PropertyScope
typealias Notifications = PropertyNotifications
var scope: Scope { get set }
}
Then you can do this anywhere in your code:
func postNotification() {
let scope: Property.Scope = .all
NotificationCenter.post(name: Property.Notifications.propertyDidChange,
object: scope)
}
Using protocols like Java interfaces is rarely a good idea. They are meta types, meant for defining contracts, which is an entirely different kind of thing.
That being said, just for the point of understanding, I find the most simple and effective way for creating the equivalent of a static factory method of a protocol to write a free function.
It should contain the protocol's name, hoping that that will prevent name clashes, and improve discoverability.
In other languages, createP would be a static member of P, named create and be called as P.create(...), which would drastically improve discoverability and guarantee to prevent name clashes.
In swift, though, this is not an option for protocols, so if protocols are for some reason really actually used as a replacement for interfaces, at least including the protocol's name in the function's name is an ugly workaround that's still slightly better than nothing.
P.S. in case the goal is actually to achieve something like an inheritance hierarchy with structs, union style enums are the tool that's meant to serve that purpose :)
protocol P
{
var x: Int { get }
}
func createP() -> P
{
if (todayIsMonday())
{
return A()
}
else
{
return B()
}
}
class A: P
{
var x = 5
}
class B: P
{
var x = 7
}
This isn't an answer so much as it is an extension to the question. Say I have:
#objc public protocol InteractivelyNameable: Nameable {
static func alertViewForNaming(completion:#escaping((_ success: Bool, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?
}
And I have a generic view controller that manages various types (generic type is .fetchableObjectType... basically NSFetchResult). I need to check if a specific object type conforms to the protocol, and if so, invoke it.
something like:
// valid swift code
if self.dataSource.fetchableObjectType is InteractivelyNameable {
// not valid swift code
if let alert = (self.dataSource.fetchableObjectType as InteractivelyNameable).alertViewForNaming(....)
}
I had a situation where I need to create same DomainModel object from 2 different response. so this (static method in protocol helped me) approach helped me.
protocol BaseResponseKeyList: CodingKey {
static func getNameKey()->Self
}
enum FirstResponseKeyList: String, BaseResponseKeyList {
case name
func getNameKey()->FirstResponseKeyList {
return .name
}
}
enum SecondResponseKeyList: String, BaseResponseKeyList {
case userName
func getNameKey()->SecondResponseKeyList {
return .userName
}
}
struct MyDomainModel<T:BaseResponseKeyList> : Decodable {
var name:String?
required init(from d:Decoder) {
do {
let container = try d.container(keyedBy:T.self)
name = try container.decode(String.self, forKey:T.getNameKey())
}catch(_) {
print("error")
}
}
}
let myDomainModel = try JSONDecoder().decode(MyDomainModel <FirstResponseKeyList>.self, from: data)
let myDomainModel2 = try JSONDecoder().decode(MyDomainModel <SecondResponseKeyList>.self, from: data2)
I want to configure an object with multiple presentables; I've created protocols for them; so that I can combine them into a concrete presentable.
protocol Presentable {
}
protocol TextPresentable: Presentable {
var text:String { get }
}
protocol ImagePresentable: Presentable {
var image:String { get }
var images:[String] { get }
}
The concrete struct:
struct ConcretePresentable: TextPresentable, ImagePresentable {
var text:String { return "Text" }
var image:String { return "Image" }
var images:[String] { return ["A", "B"] }
}
The next thing would be to have a presenter, and I would check if the passed in presenter is actually valid:
typealias TextAndImagePresentable = protocol<TextPresentable, ImagePresentable>
struct ConcretePresenter {
func configureWithPresentable(presentable: Presentable) {
guard let textAndImagePresentable = presentable as? TextAndImagePresentable else {
return
}
print(textAndImagePresentable.text)
print(textAndImagePresentable.image)
print(textAndImagePresentable.images)
}
}
To configure:
let concretePresentable = ConcretePresentable()
let concretePresenter = ConcretePresenter()
concretePresenter.configureWithPresentable(concretePresentable)
It goes fine if I run this in a playground, with all of the code in the same place. However, once I put this in a project and split it up into multiple files (ConcretePresenter.swift, ConcretePresentable.swift holding the concrete structs and Presentable.swift which holds the protocols), I get a EXC_BAD_ACCESS.
Why does that happen?
As a disclaimer, I don't necessarily find this answer very satisfying, but it does work.
So, once I noticed that the text & image properties were being returned in each others places (the value for text is being returned by the image property and vice versa), I figured the problem had something to do with what Swift is doing with managing pointers here.
So, out of curiosity, I wanted to add a truly scalar value to the protocols. I added a value property as an Int to the TextPresentable protocol:
protocol Presentable {}
protocol TextPresentable: Presentable {
var text:String { get }
var value: Int { get }
}
protocol ImagePresentable: Presentable {
var image:String { get }
var images:[String] { get }
}
And then I set up the concrete implementation to return some known value. Here, we're returning 0.
struct ConcretePresentable: TextPresentable, ImagePresentable {
var text:String { return "SomeText" }
var value: Int { return 0 }
var image:String { return "SomeImage" }
var images:[String] { return ["A", "B"] }
}
After running this code, we still get the same crash, but I notice that value, which really shouldn't have a problem printing 0 is instead printing some very large number: 4331676336. This isn't right at all.
I also changed images from an array to a dictionary to see if the error persists--it does. It seems the crash is related to collections and not specific to arrays.
From here, I tried some other things.
I tried making ConcretePresentable a class rather than a struct.
class ConcretePresentable: TextPresentable, ImagePresentable
That resulted in the same behavior.
I tried making ConcretePresentable conform to the typealias rather than the protocols independently:
struct ConcretePresentable: TextAndImagePresentable
That resulted in the same behavior.
I tried doing both of the aforementioned at once:
class ConcretePresentable: TextAndImagePresentable
Yet still the same behavior.
I did come up with one way to make it work though. Make a protocol that conforms to the two protocols in your typealias and make ConcretePresentable conform to that:
protocol TextAndImagePresentable: TextPresentable, ImagePresentable {}
struct ConcretePresentable: TextAndImagePresentable {
// ...
}
The problem here is that if you don't explicitly make ConcretePresentable conform to the protocol, it will fail the guard let even if it does conform to TextPresentable and ImagePresentable.
I asked about this on Swift Users and filled a bug.
Confirmed to be a bug in the compiler:
https://bugs.swift.org/browse/SR-4477
Fixed by Joe Groff now:
Joe Groff added a comment - 2 hours ago
Merged. Should be fixed in future snapshots.
struct uses value semantics and so properties are copied. Swift should have reported this as an error since you are trying to inherit from two protocols which derive from the same base protocol. In classes this will work but in struct it wont because of value semantics for struct. In case you decide to add a variable to Presentable protocol Swift would be confused which ones to bring into the struct. From TextPresentable or ImagePresentable
You should use #protocol Presentable : class and then convert ConcretePresentable to class to fix this.
protocol Presentable : class {
}
class ConcretePresenter {...
The problem is in the cast of the Presentable to TextAndImagePresentable. The guard let succeed, but creates an invalid value (I don't know exactly why).
One way to check it, is look to the console on the execution of the commands:
print(textAndImagePresentable.text)
print(textAndImagePresentable.image)
print(textAndImagePresentable.images)
That will print:
Image
Text
Program ended with exit code: 9
One way to avoid it is to change your method signature to avoid the casting:
func configureWithPresentable(presentable: TextAndImagePresentable) {
print(presentable.text)
print(presentable.image)
print(presentable.images)
}
And, in my opinion, since nothing will happen if the presentable do not conform to both protocols, makes more sense to delimiter it on the method signature.