protocol Deck {
var cards: [String] {get set} // {get mutable set}
}
struct MyDeck: Deck {
var cards: [String] = (1...7).map {_ in return String(rand())}
}
Just interested do I need to specify {get mutable set} in protocol?
Can't find any docs about why not using mutable keyword in setter declaration if my setter mutates my struct
First of all note that the keyword of discussion is mutating, not mutable.
Default state of set is mutating
To swiftly answer your question: mutating is the default state for setters, and hence you needn't explicitly use the mutating keyword to specify this.
Details
For getter and setters, the following default behaviour holds
get is nonmutating as per default
set is mutating as per default
Hence, a protocol specifying ... { get set } for, say (as in your example), a computed property, expects the default nonmutating get and mutating set of a struct conforming to such a protocol
protocol Deck {
var cards: [String] {get set}
}
// implicitly, OK
struct MyDeckA: Deck {
var mutateMe: Int = 0
var cards: [String] {
get { return ["foo"] }
set { mutateMe += 1 }
}
}
// explicitly, OK
struct MyDeckB: Deck {
var mutateMe: Int = 0
var cards: [String] {
nonmutating get { return ["foo"] }
mutating set { mutateMe += 1 }
}
}
/* error, MyDeckC does not conform to Deck
(mutating getter, wheres a nonmutating one is blueprinted!) */
struct MyDeckC: Deck {
var mutateMe: Int = 0
var cards: [String] {
mutating get { return ["foo"] }
mutating set { mutateMe += 1 }
}
}
In case we want a getter or setter that deviates from the default cases above, we need to specify this (in the protocol as well as explicitly in say a struct conforming to such a protocol).
protocol Deck {
var cards: [String] {mutating get nonmutating set}
}
/* when conforming to this non-default get/set setup blueprinted in
protocol Deck, we need to explicitly specify our non-default
(w.r.t. mutating) getter and setter */
struct MyDeckD: Deck {
var mutateMe: Int = 0
var cards: [String] {
mutating get { mutateMe += 1; return ["foo"] }
nonmutating set { print("I can't mutate self ...") }
}
}
Finally, interestingly, if we (for some protocol property) blueprint a setter as the default (... {get set}), i.e., defaulted as mutating set, we may still conform to such a protocol with an explicitly nonmutating setter
protocol Deck {
var cards: [String] {get set}
}
struct MyDeckE: Deck {
var mutateMe: Int = 0
var cards: [String] {
get { return ["foo"] }
nonmutating set { print("I can't mutate self ...") }
/* setter cannot mutate self */
}
}
I can assume that this is allowed as we let the structure that conforms to the protocol contain a setter that is more restrictive than the blueprinted one, with regard to mutating self. Naturally the same holds if we blueprint a mutating getter: we may still conform to such a protocol with a nonmutating one.
protocol Deck {
var cards: [String] {mutating get set}
}
struct MyDeckF: Deck {
var mutateMe: Int = 0
var cards: [String] {
nonmutating get { print("I can't mutate self ..."); return ["foo"] }
/* getter cannot mutate self */
set { mutateMe += 1 }
}
}
Related
I have defined the following protocols:
protocol ListableArrayElement: Identifiable, Equatable {
associatedtype T = Hashable
var id:T { get }
}
protocol Listable: ObservableObject, RandomAccessCollection where Element == ArrayElement {
associatedtype ArrayElement: ListableArrayElement
var _elements: [ArrayElement] { get set }
var count: Int { get }
var isEmpty: Bool { get }
var endIndex: Int { get }
var startIndex: Int { get }
subscript(position: Int) -> ArrayElement { get set }
func index(after i: Int) -> Int
func append(_ element: ArrayElement)
func insert(_ newElement: ArrayElement, at i: Int)
func remove(at index: Int)
func removeAll()
func index(of element: ArrayElement) -> Int?
}
protocol FavouritesArray: Listable, Codable where Element: Codable {
// MARK: - PROPERTIES
var _elements: [Element] { get set }
var key: String { get set }
init()
init(key: String)
}
There is an associated extension to FavouritesArray that provides conforming types with the ability to add/remove elements, and persist/load the array to/from UserDefaults via the key. All well and good. (Also note the listable protocol helps me avoid writing some boiler plate code for ViewModels that have an array of 'something' at their heart.)
Now I also want to write a generic SwiftUI view that can build a menu, using the FavouriteArray functions. I am struggling to understand how to express the type signature:
I'm passing instances of types conforming to FavouritesArray via EnvironmentObject, and thus want to write something like:
struct FavouritesArrayView<Favourites: FavouritesArray, Favourite>: View
where Favourite == FavouritesArray.Element {
#EnvironmentObject var favourites: Favourites
#ObservedObject var favourite: Favourite
// Other properties here
var body: some View {
// Layout code here
}
}
This gives the compiler error: Associated type 'Element' can only be used with a concrete type or generic parameter base
Any tips on how to achieve this?
First of all you need to declare that Favourite conforms to ObservableObject and then the where condition should use your associated type and not the protocol it conforms to, where Favourites.Element == Favourite
struct FavouritesArrayView<Favourites: FavouritesArray, Favourite: ObservableObject>: View where Favourites.Element == Favourite
I need to declare an array like this:
var cups: [Cup<Drink>] = []
The Cup is a struct and the Drink is a protocol, but I got the following error:
Value of protocol type 'Drink' cannot conform to 'Drink'; only struct/enum/class types can conform to protocols
I know the protocol type 'Drink' can be erased by a AnyDrink struct, the flowing code is an example.
But in fact, the Type-Erasure will become extremely complicated when associatetype, Self and static-method (in the case of non-final class adopts Drink, such as the protocol Equatable with static method ==) are used in Drink.
Is there a better way to declare the cups array?
Or: Is there any easy way to make Type-Erasure? It should be a builtin feature.
protocol Drink {
...
}
struct AnyDrink: Drink {
let drink: Drink
...
}
struct Water: Drink {
...
}
struct Coffee: Drink {
...
}
struct Tea: Drink {
...
}
struct Cup<T: Drink> {
private(set) var drink: T?
mutating func bottomUp() {
drink = nil
}
}
struct Waiter {
var cups: [Cup<AnyDrink>] = []
mutating func makeACupOfSth(_ cup: Cup<AnyDrink>) {
cups.append(cup)
}
mutating func pleaseGiveMeACupOfSthToDrink() -> Cup<AnyDrink> {
return cups.removeFirst()
}
static func excuse(_ customer: Customer) -> Waiter {
return Waiter()
}
}
struct Customer {
var me: Self { self }
func drink() {
var waiter = Waiter.excuse(me)
var cup = waiter.pleaseGiveMeACupOfSthToDrink()
cup.bottomUp()
}
}
For question Is there a better way to declare the cups array?:
You can use another protocol called DrinkGeneric like this and implement it by Cup Struct:
protocol DrinkGeneric {
func sample()
func typOfDrink() -> Drink.Type
}
struct Cup<T: Drink>: DrinkGeneric {
public var drink: T?
mutating func bottomUp() {
drink = nil
}
public func typeOfDrink() -> Drink.Type {
return type(of: drink!)
}
func sample() {
print("sample")
}
}
Then create an array with type DrinkGeneric like this:
var cups: [DrinkGeneric] = [Cup(drink: Water()), Cup(drink: Tea())]
For check type:
if cups[0].typeOfDrink() is Water.Type {
// Any work
}
If I have a series of protocols like so:
protocol Customer {
var name:String { get set }
var age: Int { get set }
var startDate:Date { get set }
var meals:Array<String> { get set }
var market:Int { get set }
}
protocol Vegan:Customer {
}
protocol Vegetarian:Customer {
}
protocol Paleo:Customer {
}
and extension like so:
extension Customer where Self:Vegan, Self:Vegetarian {
func getMeals() -> Array<String> {
return ["VeganMeal1", "VeganMeal2", "VeganMeal3", "VeganMeal4"]
}
}
extension Customer where Self:Vegetarian {
func getMeals() -> Array<String> {
return ["VegetarianMeal1", "VegetarianMeal2", "VegetarianMeal3", "VegetarianMeal4"]
}
}
extension Customer where Self:Paleo {
func getMeals() -> Array<String> {
return ["PaleoMeal1", "PaleoMeal2", "PaleoMeal3", "PaleoMeal4"]
}
}
and this struct
struct aCustomer:Customer, Vegan {
var name:String
var age: Int
var startDate:Date
var meals:Array<String>
var market:Int
}
when I create a new object based on that struct
var newCustomer = aCustomer(name:"William", age:40, startDate:Date(), meals:[], market:1)
how come I can't access the getMeals function in the extensions? I get an error stating getMeals is an ambiguous reference.
extension Customer where Self:Vegan, Self:Vegetarian {
extends Customer in the case where the adopter or Customer also both adopts Vegan and Vegetarian. Your aCustomer (a struct type starting with a small letter?? the horror, the horror) does not do that, so the extension doesn't apply to it.
If the goal is to inject the same code either when the adopter adopts Vegan or when the adopter adopts Vegetarian, use two extensions, or, if you don't like the repetition, have them both adopt some "higher" protocol that is extended with the desired code to be injected.
I try to use the set method for calling a function after the value is changed.
I did not see why the set method is not called.
The code could be directly executed in playground
//: Playground - noun: a place where people can play
import UIKit
protocol RandomItem {
var range : (Int,Int) {get set}
var result : Int {get set}
init()
mutating func createRandom()
}
extension RandomItem {
var range : (Int,Int) {
get {
return range
}
set {
range = newValue
self.createRandom()
}
}
}
struct Item: RandomItem {
var range = (0,1)
var result: Int = 0
init() {
self.createRandom()
}
mutating func createRandom() {
let low = UInt32(range.0)
let high = UInt32(range.1)
result = Int(arc4random_uniform(high - low + 1) + low)
}
}
Your struct Item declares its own range property, which overrides the default you created in the protocol extension. The range property in Item has no getters or setters defined to do what your extension version does.
Another issue:
Your protocol extension defines the range property as a computed property (no storage) whose getter and setter both call itself. This will loop infinitely.
Maybe you are looking for something more like:
protocol RandomItem {
var storedRange: (Int, Int) { get }
var range : (Int,Int) {get set}
var result : Int {get set}
init()
mutating func createRandom()
}
extension RandomItem {
var range : (Int,Int) {
get {
return storedRange
}
set {
storedRange = newValue
self.createRandom()
}
}
}
struct Item: RandomItem {
var result: Int = 0
var storedRange = (0, 1)
init() {
self.createRandom()
}
mutating func createRandom() {
let low = UInt32(range.0)
let high = UInt32(range.1)
result = Int(arc4random_uniform(high - low + 1) + low)
}
}
This requires a conforming type to define a stored property storedRange, which the default implementation of the computed property range will interact with.
So I'm implementing the following:
A simple LanguageType protocol, which conforms to Hashable
A Translateable protocol, which should allow you to get (and set) a [String] from a dictionary, using a LanguageType as key
// MARK: - LanguageType
protocol LanguageType: Hashable {
var description: String { get }
}
extension LanguageType {
var description: String { return "\(Self.self)" }
var hashValue: Int { return "\(Self.self)".hashValue }
}
func ==<T: LanguageType, U: LanguageType>(left: T, right: U) -> Bool {
return left.description == right.description
}
// MARK: - Translateable
protocol Translateable {
var translations: [LanguageType: [String]] { get set }
}
As usual, Swift has a problem with the way the LanguageType protocol is used:
From what I've read, this has to do with Swift not supporting Existentials, which results in protocols not actually being first class types.
In the context of generics this problem can usually be solved with a type-erased wrapper.
In my case there are no generics or associated types though.
What I want to achieve is to have translations.Key to be any LanguageType, not just one generic type conforming to LanguageType.
So for example this wouldn't work:
protocol Translateable {
typealias Language: LanguageType
var translations: [Language: [String]] { get set }
}
For some reason I just can't think of a way to achieve this. I find it sounds like I need some kind of type-erased wrapper, as I want
translations.Key to be any LanguageType
I think I need to erase the exact type, which is supposed to conform to LanguageType in Translateable.
What can I do to fix this issue?
Update 1:
As just determined in this question, LanguageType actually has associated type requirements (do to it's conformance to Equatable). Therefore I will try to create a type-erased wrapper around LanguageType.
Update 2:
So I've realized, that creating a type-erased wrapper for LanguageType won't actually resolve the problem. I've created AnyLanguage:
struct AnyLanguage<T>: LanguageType {
private let _description: String
var description: String { return _description }
init<U: LanguageType>(_ language: U) { _description = language.description }
}
func ==<T, U>(left: AnyLanguage<T>, right: AnyLanguage<U>) -> Bool {
return left.description == right.description
}
If I now used it in place of LanguageType it wouldn't do much, as Translateable would still require an associated type:
protocol Translateable {
typealias T
var translations: [AnyLanguage<T>: [String]] { get set }
}
Solution:
I removed the generic from AnyLanguage:
struct AnyLanguage: LanguageType {
private(set) var description: String
init<T: LanguageType>(_ language: T) { description = language.description }
}
func ==(left: AnyLanguage, right: AnyLanguage) -> Bool {
return left.description == right.description
}
protocol Translateable {
var translations: [AnyLanguage: [String]] { get set }
}
Not sure why I introduced T in Update 2, as it doesn't do anything. But this seems to work now.
You can't have protocols as a key for a Dictionary, see Swift Dictionary with Protocol Type as Key. Swift need to tie the dictionary key to a concrete type.
Seems that you're trying to achieve static polymorphism and dynamic polymorphism in the same construct (the Translateable protocol), which I'm not sure it can be achieved.
A workaround would be to declare Translateable as a generic struct:
struct Translateable<T: LanguageType> {
var translations: [T: [String]]
}
Possibly you can use an enum that conforms to LanguageType can mimic the behaviour you're looking for. In that case, you needn't explicitly include conformance to hashable in LanguageType, as enums are Hashable.
protocol LanguageType {
var description: String { get }
// ...
}
extension LanguageType {
var description: String { return "\(Self.self)" }
}
enum AnyLanguage : Int, LanguageType {
case English = 1, German, Swedish
// implement non-default description
var description : String {
return "Language: " + String(self)
}
}
protocol Translatable {
var myDict : [AnyLanguage:[String]] { get set }//= [:]
}
class MyFooWordList : Translatable {
private var myBackendDict : [AnyLanguage:[String]] = [:]
var myDict : [AnyLanguage:[String]] {
get {
return myBackendDict
}
set {
for (k, v) in newValue {
myBackendDict[k] = v
}
}
}
}
Example:
/* Example */
var myFooWordList = MyFooWordList()
myFooWordList.myDict = [.English: ["Hello", "World"]]
myFooWordList.myDict = [.German: ["Hallo", "Welt"]]
print("Words for '" + AnyLanguage.English.description + "': \(myFooWordList.myDict[.English] ?? ["<Empty>"])")
/* Words for 'Language: English': ["Hello", "World"] */
print("Words for '" + AnyLanguage.German.description + "': \(myFooWordList.myDict[.German] ?? ["<Empty>"])")
/* Words for 'Language: German': ["Hallo", "Welt"] */
print("Words for '" + AnyLanguage.Swedish.description + "': \(myFooWordList.myDict[.Swedish] ?? ["<Empty>"])")
/* Words for 'Language: Swedish': ["<Empty>"] */
Another workaround is to use an enum-like class where you can "dynamically add members" to this fictive enum
class LanguageType {
class AnyLanguage: Hashable {
let id: Int
let description: String
private init(id: Int, description: String) {
self.id = id
self.description = description
}
var hashValue: Int { return id }
}
class var ENGLISH: AnyLanguage {
class English: AnyLanguage {
}
return English(id: 1, description: "English")
}
class var GERMAN: AnyLanguage {
class German: AnyLanguage {
}
return German(id: 2, description: "German")
}
class func CUSTOM(id: Int, _ description: String) -> AnyLanguage {
return AnyLanguage(id: id, description: description)
}
}
func == (lhs: LanguageType.AnyLanguage, rhs: LanguageType.AnyLanguage) -> Bool {
return lhs.id == rhs.id
}
protocol Translatable {
var myDict : [LanguageType.AnyLanguage:[String]] { get set }//= [:]
}
class MyFooWordList : Translatable {
private var myBackendDict : [LanguageType.AnyLanguage:[String]] = [:]
var myDict : [LanguageType.AnyLanguage:[String]] {
get {
return myBackendDict
}
set {
for (k, v) in newValue {
myBackendDict[k] = v
}
}
}
}
Example usage
/* Example */
var myFooWordList = MyFooWordList()
myFooWordList.myDict = [LanguageType.ENGLISH: ["Hello", "World"]]
myFooWordList.myDict = [LanguageType.GERMAN: ["Hallo", "Welt"]]
myFooWordList.myDict = [LanguageType.CUSTOM(3, "Swedish"): ["Hej", "Varlden"]]
myFooWordList.myDict = [LanguageType.CUSTOM(4, "Finnish"): ["Hei", "Maailma"]]
print("Words for '" + LanguageType.ENGLISH.description + "': \(myFooWordList.myDict[LanguageType.ENGLISH] ?? ["<Empty>"])")
/* Words for 'English': ["Hello", "World"] */
print("Words for '" + LanguageType.GERMAN.description + "': \(myFooWordList.myDict[LanguageType.GERMAN] ?? ["<Empty>"])")
/* Words for 'Language: German': ["Hallo", "Welt"] */
print("Words for '" + LanguageType.CUSTOM(3, "Swedish").description + "': \(myFooWordList.myDict[LanguageType.CUSTOM(3, "Swedish")] ?? ["<Empty>"])")
/* Words for 'Swedish': ["Hej", "Varlden"] */
The solution seems to be a type-erased wrapper. Type-erasure fixes the problem of not being able to use protocols with associated types (PATs) as first-class citizens, by creating a wrapper type, which only exposes the properties defined by the protocol, which it wraps.
In this case, LanguageType is a PAT, due to its adoption of Equatable (which it conforms to, due to its adoption of Hashable):
protocol LanguageType: Hashable { /*...*/ }
Therefore it can not be used as a first-class type in the Translatable protocol:
protocol Translatable {
var translations: [LanguageType: [String]] { get set } // error
}
Defining an associated type for Translatable would not fix the problem, as this would constrain the LanguageType to be one specific type:
protocol Translatable {
typealias Language: LanguageType
var translations: [Language: [String]] { get set } // works
}
struct MyTranslatable<T: LanguageType>: Translatable {
var translations: [T: [String]] // `T` can only be one specific type
//...
}
As mentioned the solution is a type-erased wrapper AnyLanguage (Apple uses the same naming convention for their type-erased wrappers. For example AnySequence):
// `AnyLanguage` exposes all of the properties defined by `LanguageType`
// in this case, there's only the `description` property
struct AnyLanguage: LanguageType {
private(set) var description: String
// `AnyLanguage` can be initialized with any type conforming to `LanguageType`
init<T: LanguageType>(_ language: T) { description = language.description }
}
// needed for `AnyLanguage` to conform to `LanguageType`, as the protocol inherits for `Hashable`, which inherits from `Equatable`
func ==(left: AnyLanguage, right: AnyLanguage) -> Bool {
return left.description == right.description
}
// the use of `AnyLanguage` allows any `LanguageType` to be used as the dictionary's `Key`, as long as it is wrapped as `AnyLanguage`
protocol Translateable {
var translations: [AnyLanguage: [String]] { get set }
}
This implementation now allows the following:
struct SomethingTranslatable: Translatable {
var translations: [AnyLanguage: [String]] = [:]
}
func ==(left: SomethingTranslatable, right: SomethingTranslatable) -> Bool { /*return some `Bool`*/ }
struct English: LanguageType { }
struct German: LanguageType { }
var something = SomethingTranslatable()
something.translations[AnyLanguage(English())] = ["Hello", "World"]
let germanWords = something.translations[AnyLanguage(German())]
Different types, conforming to LanguageType, can now be used as the Key. The only syntactical difference, is the necessary initialization of an AnyLanguage:
AnyLanguage(English())