why cant i return opaque types in my swift code? i am already returning a type conforming to that protocol [duplicate] - swift

This question already has answers here:
What is the "some" keyword in Swift(UI)?
(14 answers)
Closed 1 year ago.
image
As showing in the image, i'm already using types such as MilkChocolate which conforms to the "Chocolate" protocal, and im trying to declare a function which returns a "some chocolate", which means return a type of chocolate depends on the input, but it gives me an error says
Function declares an opaque return type, but the return statements in its body do not have matching underlying types
this confused me by a lot and i think MiklChocolate is an underlying type for the Chocolate protocol
whats wrong?🤔
and my code should match this tutorial: https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html
PS: if you cant view the image, here's the code
protocol Chocolate {
associatedtype Content
var mass:Double {get}
}
struct Honey {}
struct Milk {}
struct Wine {}
class HoneyChocolate: Chocolate {
typealias Content = Honey
var mass = 1.0
}
class MilkChocolate: Chocolate {
typealias Content = Milk
var mass = 1.2
var mysteriousEffect = true
}
class WineChocolate: Chocolate {
typealias Content = Wine
var mass:Double = 999
}
func giveMeChocolate(of type:String) -> some Chocolate {
switch type {
case "honey":
return HoneyChocolate()
case "milk":
return MilkChocolate()
default:
return WineChocolate()
}
}

The problem occurs because Swift can't return different types of generic types. HoneyChocolate and WineChocolate are different types and that causes an issue.
A different way of achieving your goal is to return a class type of Chocolate.
First let's declare our enum flavors:
enum Flavor {
case Honey, Milk, Wine
}
then we will declare the superclass Chocolate:
class Chocolate {
var flavor: Flavor
var mass:Double
init(_ flavor: Flavor ,_ mass: Double) {
self.flavor = flavor
self.mass = mass
}
}
Now we can inherit this class:
class HoneyChocolate: Chocolate {
init() {
super.init(Flavor.Honey, 1.0)
}
}
class MilkChocolate: Chocolate {
init() {
super.init( Flavor.Milk, 1.2)
var mysteriousEffect = true
}
}
class WineChocolate: Chocolate {
init() {
super.init( Flavor.Wine, 999)
var mysteriousEffect = true
}
}
finally we can return our chocolates:
func giveMeChocolate(of type:Flavor) -> Chocolate {
switch type {
case Flavor.Honey:
return HoneyChocolate()
case Flavor.Milk:
return MilkChocolate()
default:
return WineChocolate()
}
}
Why some doesn't work
some allows you to return a generic type like and ONLY one type.
for example:
let's make a CandyChocolate class:
class CandyChocolate: Chocolate {
typealias Content = Candy
var mass = 1.0
var sweetness
init(sweetness: int) {
self.sweetness = sweetness
}
}
Now to return a some Chocolate we are allowed to return exactly one type of chocolate:
func giveMeChocolate(of type:String) -> some Chocolate {
switch type {
case "honey":
return CandyChocolate(sweetness: 5)
case "milk":
return CandyChocolate(sweetness: 3)
default:
return CandyChocolate(sweetness: 8)
}
}

Related

Some Problems about Opaque Return Type and Protocol with associatedtype In Swift

How to deal with this problem?
Definitions:
protocol NameProtocol {
var rawValue: String { get }
}
struct CatName: NameProtocol {
enum CatNamesEnum: String {
case Tiger, Max, Sam
}
var literal: CatNamesEnum
var rawValue: String {
literal.rawValue
}
}
struct DogName: NameProtocol {
enum DogNamesEnum: String {
case Jack, Toby, Sadie
}
var literal: DogNamesEnum
var rawValue: String {
literal.rawValue
}
}
Definitions:
protocol AnimalProtocol {
associatedtype Name: NameProtocol
var name: Name { get }
func cry() -> String
}
class Cat: AnimalProtocol {
var name: CatName
func cry() -> String {
return "meow, I am \(name.rawValue)"
}
init(name: CatName) {
self.name = name
}
// some other cat actions...
}
class Dog: AnimalProtocol {
var name: DogName
func cry() -> String {
return "bark, I am \(name.rawValue)"
}
init(name: DogName) {
self.name = name
}
// some other dog actions...
}
The code above are some definition code structure, should not be modified.
And the functions below takes some problem:
Protocol with asccociatedtype cannot be the dictionary value type.
Function with Opaque Return Type cannot return some different types extends the same protocol.
// <1>
// Error: Protocol 'AnimalProtocol' can only be used as a generic constraint because it has Self or associated type requirements
let animals: [String: AnimalProtocol] = [
"cat": Cat(name: CatName(literal: .Sam)),
"dog": Dog(name: DogName(literal: .Jack))
// ...
// maybe a lot of animals
]
for (animal, entity) in animals {
print("\(animal): \(entity.cry())")
}
// <2>
// Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types
func animalCry(animal: String) -> some AnimalProtocol {
switch animal {
case "cat":
return Cat(name: CatName(literal: .Sam))
default:
return Dog(name: DogName(literal: .Toby))
}
}
Any solutions?
Or best practice of different types(with embed generic type) in a list.
You say that the definitions should not be modified but this is exactly what I have done here:
I removed the associated type from the AnimalProtocol protocol since it wasn't used elsewhere in any conforming types
protocol AnimalProtocol {
var name: NameProtocol { get }
func cry() -> String
}
Then I changed both Cat and Dog to conform to the protocol by changing the name declaration in both to
var name: NameProtocol
this resolves the issue with the dictionary and the function was fixed by changing the return type from some AnimalProtocol to AnimalProtocol

Returning the associatedtype of an opaque return type

I have a simple protocol with an associated type, and a protocol extension that returns an array of this type.
protocol Foo {
associatedtype Unit
}
extension Foo {
var allTheFoos: [Unit] {
return []
}
}
I then have a struct which returns some Foo in a computed property, and another computed property that returns the allTheFoos array.
struct FakeFoo: Foo {
typealias Unit = Int
}
struct FooFactory {
var myFoo: some Foo {
return FakeFoo()
}
/* WHICH RETURN TYPE WILL
PLEASE THE SWIFT GODS?!
*/
var allTheFoos: [Foo.Unit] {
return myFoo.allTheFoos
}
}
The return type of allTheFoos matches Xcode's autocomplete type suggestion for the myFoo.allTheFoos call, but understandably, this yields a:
// var allTheFoos: [Foo.Unit] {}
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
My question is: What return type will make Xcode happy?
Below are my attempts, and their corresponding errors
// var allTheFoos: [some Foo.Unit] {}
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
// func allTheFoos() -> some [Foo.Unit]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
// func allTheFoos<U: Foo.Unit>() -> [U]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
ERROR: Cannot convert return expression of type '[(some Foo).Unit]' to return type '[U]'
// func allTheFoos<U>() -> [U] where U: (some Foo).Unit
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
FYI: The reason I'm doing this in a computed property in the first place is to keep things clean in some SwiftUI code.
Thanks for any help you can give!
=========== UPDATE ===========
I missed some important stuff in my sample code, so to give some context: the code is used in a unit conversion app, so something that can turn Celsius into Kelvin, Kg into lbs, and anything else into anything else.
protocol Unit: Equatable {
var suffix: String { get }
}
struct Value<UnitType: Unit> {
let amount: Double
let unit: UnitType
var description: String {
let formatted = String(format: "%.2f", amount)
return "\(formatted)\(unit.suffix)"
}
}
Value is constrained to a unit type, so that it's not possible to convert Celsius into Litres.
Therefore, we have a Conversion protocol that stores all similar units together:
protocol Conversion {
associatedtype UnitType: Unit
var allUnits: [UnitType] { get }
func convert(value: Value<UnitType>, to unit: UnitType) -> Value<UnitType>
}
extension Conversion {
func allConversions(for value: Value<UnitType>) -> [Value<UnitType>] {
let units = self.allUnits.filter { $0 != value.unit }
return units.map { convert(value: value, to: $0) }
}
}
So an example of a conversion for Temperature would be:
struct Temperature: Conversion {
enum Units: String, Unit, CaseIterable {
case celsius, farenheit, kelvin
var suffix: String {
switch self {
case .celsius: return "ËšC"
case .farenheit: return "ËšF"
case .kelvin: return "K"
}
}
}
var allUnits: [Units] { return Units.allCases }
func convert(value: Value<Units>, to unit: Units) -> Value<Units> {
/* ... */
}
}
Finally, the actual app code where the problem occurs is here:
struct MyApp {
var current: some Conversion {
return Temperature()
}
// ERROR: Associated type 'UnitType' can only be used with a concrete type or generic parameter base
var allConversions: [Value<Conversion.UnitType>] {
// This value is grabbed from the UI
let amount = 100.0
let unit = current.allUnits.first!
let value = Value(amount: amount, unit: unit)
return current.allConversions(for: value)
}
}
Looking at how you've implemented AnyValue, I think what you want here is just:
var allConversions: [String] {
let units = self.allUnits.filter { $0 != value.unit }
return units.map { convert(value: value, to: $0) }.description
}
Or something like that. All the algorithms that match what you're describing are just "conversion -> string." If that's the case, all you really want is CustomStringConvertible.
Managed to solve this issue using some Type Erasure:
struct AnyValue {
let description: String
init<U: Unit>(_ value: Value<U>) {
self.description = value.description
}
}
allowing for:
var allConversions: [AnyValue] {
// This value is grabbed from the UI
let amount = 100.0
let unit = current.allUnits.first!
let value = Value(amount: amount, unit: unit)
return current.allConversions(for: value).map(AnyValue.init)
}
However, this feels like a clunky solution (and one that opaque return types was introduced to avoid). Is there a better way?

Default protocol implementation takes precedence over the subclass's implementation [duplicate]

I'm experimenting with Swift protocol extensions and I found this quite confusing behaviour. Could you help me how to get the result I want?
See the comments on the last 4 lines of the code. (You can copy paste it to Xcode7 playground if you want). Thank you!!
protocol Color { }
extension Color { var color : String { return "Default color" } }
protocol RedColor: Color { }
extension RedColor { var color : String { return "Red color" } }
protocol PrintColor {
func getColor() -> String
}
extension PrintColor where Self: Color {
func getColor() -> String {
return color
}
}
class A: Color, PrintColor { }
class B: A, RedColor { }
let colorA = A().color // is "Default color" - OK
let colorB = B().color // is "Red color" - OK
let a = A().getColor() // is "Default color" - OK
let b = B().getColor() // is "Default color" BUT I want it to be "Red color"
The short answer is that protocol extensions don't do class polymorphism. This makes a certain sense, because a protocol can be adopted by a struct or enum, and because we wouldn't want the mere adoption of a protocol to introduce dynamic dispatch where it isn't necessary.
Thus, in getColor(), the color instance variable (which may be more accurately written as self.color) doesn't mean what you think it does, because you are thinking class-polymorphically and the protocol is not. So this works:
let colorB = B().color // is "Red color" - OK
...because you are asking a class to resolve color, but this doesn't do what you expect:
let b = B().getColor() // is "Default color" BUT I want it to be "Red color"
...because the getColor method is defined entirely in a protocol extension. You can fix the problem by redefining getColor in B:
class B: A, RedColor {
func getColor() -> String {
return self.color
}
}
Now the class's getColor is called, and it has a polymorphic idea of what self is.
There are two very different issues at play here: The dynamic behavior of protocols and the resolution of protocol "default" implementations.
On the dynamic front, we can illustrate the problem with a simple example:
protocol Color { }
extension Color {
var color: String { return "Default color" }
}
class BlueBerry: Color {
var color: String { return "Blue color" }
}
let berry = BlueBerry()
print("\(berry.color)") // prints "Blue color", as expected
let colorfulThing: Color = BlueBerry()
print("\(colorfulThing.color)") // prints "Default color"!
As you point out in your answer, you can get the dynamic behavior if you define color as part of the original Color protocol (i.e. thereby instructing the compiler to reasonably expect the conforming classes to implement this method and only use the protocol's implementation if none is found):
protocol Color {
var color: String { get }
}
...
let colorfulThing: Color = BlueBerry()
print("\(colorfulThing.color)") // now prints "Blue color", as expected
Now, in your answer, you question why this falls apart a bit when B is a subclass of A.
I think it helps to remember that the method implementations in protocol extensions are "default" implementations, i.e. implementations to be used if the conforming class doesn't implement it, itself. The source of the confusion in your case comes from the fact that B conforms to RedColor which has a default implementation for color, but B is also a subclass of A which conforms to Color, which has a different default implementation of color.
So, we might quibble about Swift's handling of this situation (personally I'd rather see a warning about this inherently ambiguous situation), but the root of the problem, in my mind, is that there are two different hierarchies (the OOP object hierarchy of subclasses and the POP protocol hierarchy of protocol inheritance) and this results in two competing "default" implementations.
I know this is an old question, so you've probably long since moved on to other things, which is fine. But if you're still struggling regarding the right way to refactor this code, share a little about what this class hierarchy and what this protocol inheritance actually represent and we might be able to offer more concrete counsel. This is one of those cases where abstract examples just further confuse the issue. Let's see what the types/protocols really are. (If you've got working code, http://codereview.stackexchange.com might be the better venue.)
I managed to get it working by defining color on Color and switching the implementation list for B. Not much good if B must be an A though.
protocol Color {
var color : String { get }
}
protocol RedColor: Color {
}
extension Color {
var color : String {
get {return "Default color"}
}
}
extension RedColor {
var color : String {
get {return "Red color"}
}
}
protocol PrintColor {
func getColor() -> String
}
extension PrintColor where Self: Color {
func getColor() -> String {
return color
}
}
class A : Color, PrintColor {
}
class B : RedColor, PrintColor {
}
let a = A().getColor() // "Default color"
let b = B().getColor() // "Red color"
I came across this problem whilst trying to implement an "optional" method through a protocol. It can be made to work, in structs, in classes that do not inherit, and also in classes that inherit from a base that implements a non-protocol-default method which can be overridden. The only case that doesn't work is a class that inherits from a base that declares conformity but doesn't provide its own "non-default" implementation - in that case the protocol extension's default is "baked-in" to the base class, and cannot be overridden or re-defined.
Simple example:
typealias MyFunction = () -> ()
protocol OptionalMethod {
func optionalMethod() -> MyFunction?
func executeOptionalMethod()
}
extension OptionalMethod {
func optionalMethod() -> MyFunction? { return nil }
func executeOptionalMethod() {
if let myFunc = self.optionalMethod() {
myFunc()
} else {
print("Type \(self) has not implemented `optionalMethod`")
}
}
}
class A: OptionalMethod {
}
class B: A {
func optionalMethod() -> MyFunction? {
return { print("Hello optional method") }
}
}
struct C: OptionalMethod {
func optionalMethod() -> MyFunction? {
return { print("Hello optionalMethod") }
}
}
class D: OptionalMethod {
func optionalMethod() -> MyFunction? {
return { print("Hello optionalMethod") }
}
}
class E: D {
override func optionalMethod() -> MyFunction? {
return { print("Hello DIFFERENT optionalMethod") }
}
}
/* Attempt to get B to declare its own conformance gives:
// error: redundant conformance of 'B2' to protocol 'OptionalMethod'
class B2: A, OptionalMethod {
func optionalMethod() -> MyFunction? {
return { print("Hello optional method") }
}
}
*/
class A2: OptionalMethod {
func optionalMethod() -> MyFunction? {
return nil
}
}
class B2: A2 {
override func optionalMethod() -> MyFunction? {
return { print("Hello optionalMethod") }
}
}
let a = A() // Class A doesn't implement & therefore defaults to protocol extension implementation
a.executeOptionalMethod() // Type __lldb_expr_201.A has not implemented `optionalMethod`
let b = B() // Class B implements its own, but "inherits" implementation from superclass A
b.executeOptionalMethod() // Type __lldb_expr_205.B has not implemented `optionalMethod`
let c = C() // Struct C implements its own, and works
c.executeOptionalMethod() // Hello optionalMethod
let d = D() // Class D implements its own, inherits from nothing, and works
d.executeOptionalMethod() // Hello optionalMethod
let e = E() // Class E inherits from D, but overrides, and works
e.executeOptionalMethod() // Hello DIFFERENT optionalMethod
let a2 = A2() // Class A2 implements the method, but returns nil, (equivalent to A)
a2.executeOptionalMethod() // Type __lldb_expr_334.A2 has not implemented `optionalMethod`
let b2 = B2() // Class B2 overrides A2's "nil" implementation, and so works
b2.executeOptionalMethod() // Hello optionalMethod
Note: The proposed solution "Defining color as part of the original Color protocol" is not solving the problem when you have inheritance involved e.g.
RedBerry inherits from BlueBerry which conforms to protocol Color.
protocol Color {
var color: String { get }
}
extension Color {
var color: String { return "Default color" }
}
class BlueBerry: Color {
// var color: String { return "Blue color" }
}
class RedBerry: BlueBerry {
var color: String { return "Red color" }
}
let berry = RedBerry()
print(berry.color) // Red color
let colorfulThing: Color = RedBerry()
print(colorfulThing.color) // Actual: Default color, Expected: Red color

Is there some workaround to cast to a generic base class without knowing what the defined element type is?

I am trying to achieve a design where I can have a base class that has a generic property that I can change values on by conforming to a protocol.
protocol EnumProtocol {
static var startValue: Self { get }
func nextValue() -> Self
}
enum FooState: EnumProtocol {
case foo1, foo2
static var startValue: FooState { return .foo1 }
func nextValue() -> FooState {
switch self {
case .foo1:
return .foo2
case .foo2:
return .foo1
}
}
}
enum BarState: EnumProtocol {
case bar
static var startValue: BarState { return .bar }
func nextValue() -> BarState {
return .bar
}
}
class BaseClass<T: EnumProtocol> {
var state = T.startValue
}
class FooClass: BaseClass<FooState> {
}
class BarClass: BaseClass<BarState> {
}
Is it possible to end up with a solution similar to this where the element type is unknown and the value relies on the nextValue() method.
let foo = FooClass()
let bar = BarClass()
if let test = bar as? BaseClass {
test.state = test.state.nextValue()
}
This works but BarState will be unknown in my case and a lot of classes will be subclasses of BaseClass and have different state types.
let bar = BarClass()
if let test = bar as? BaseClass<BarState> {
test.state = test.state.nextValue()
}
This is a simplified example. In my case I will get a SKNode subclass that has a state property that is an enum with a nextvalue method that have defined rules to decide what the next value will be. I am trying to have a generic implementation of this that only relies on what is returned from the nextValue method. Is there a better pattern to achieve this?
This will not work for this exact scenario because EnumProtocol can not be used as concrete type since it has a Self type requirement, however, in order to achieve this type of behavior in other cases you can create a protocol that the base class conforms to and try to cast objects to that type when you are trying to determine if an object is some subclass of that type.
Consider the following example
class Bitcoin { }
class Ethereum { }
class Wallet<T> {
var usdValue: Double = 0
}
class BitcoinWallet: Wallet<Bitcoin> {
}
class EthereumWallet: Wallet<Ethereum> {
}
let bitcoinWallet = BitcoinWallet() as Any
if let wallet = bitcoinWallet as? Wallet {
print(wallet.usdValue)
}
This will not work, due to the same error that you are referring to:
error: generic parameter 'T' could not be inferred in cast to 'Wallet<_>'
However, if you add the following protocol
protocol WalletType {
var usdValue: Double { get set }
}
and make Wallet conform to that
class Wallet<T>: WalletType
then you can cast values to that protocol and use it as expected:
if let wallet = bitcoinWallet as? WalletType {
print(wallet.usdValue)
}

Check if a Swift object is a generic that conforms to a specific protocol

I am trying to come up with a model for fields in a form. There is a parent "Field" class, with a generic for the type, and then subclasses for the different types of fields. Here is the main Field class with its relevant protocol:
protocol FieldProtocol {
var stringRepresentation: String { get }
}
class Field<T>: FieldProtocol {
var value: T
var stringRepresentation: String {
return "\(value)"
}
required init(value: T) {
self.value = value
}
}
Then I have a subclass that is a text field:
class TextField: Field<String> {
required init(value: String) {
super.init(value: value)
}
}
Also, I want a field that can use any Enum as the field, as long as the enum conforms to a certain protocol:
protocol EnumProtocol {
var rawValue: String { get }
static func getValueFromString(value: String) -> EnumProtocol
}
class EnumField<T: EnumProtocol>: Field<T> {
override var stringRepresentation: String {
return value.rawValue
}
required init(value: T) {
super.init(value: value)
}
}
So far so good. I go about defining such an enum that conforms to that protocol, and set up my fields:
enum AppleType: String, EnumProtocol {
case RedDelicious = "red"
case Honeycrisp = "honeycrisp"
static func getValueFromString(value: String) -> EnumProtocol {
if value == "red" {
return AppleType.RedDelicious
} else {
return AppleType.Honeycrisp
}
}
}
let nameField = TextField(value: "Will Clarke")
var appleField = EnumField<AppleType>(value: .RedDelicious)
This is all working as expected. I have a TextField object, and an EnumField object.
In my main form though, I have an array of all the fields on that form. I want to loop through them and do something different depending on their actual type.
var fieldArray: [FieldProtocol] = [appleField, nameField]
for field in fieldArray {
if let validField = field as? TextField {
print(validField.value)
} else if let validField = field as? WHAT GOES HERE??? {
print(validField.value.rawValue)
}
}
My question, obviously, is what to put in "WHAT GOES HERE???" Semantically, what I want is "an EnumField that uses any object conforming to EnumField", which I assumed would be "EnumField". But that doesn't work.
I tried just "EnumField" but Xcode complains that "'EnumField ?' does not conform to protocol 'EnumField'".
There's gotta be a way to do this... right?
You can take all the code and put it into a Playground - it should all work other than the line in question.