Extra argument 'where' in call - swift

I'm working on removing an item from an array in swift, and I'm not sure why I'm getting this error.
My code is:
var itemToRemove = list[indexPath.item]
selectedCasesArray.removeAll(where: { $0 == itemToRemove })
There code is in a CollectionView's didSelect function.
itemToRemove is of type CaseFormat and selectedCaseArray is of type [CaseFormat].
Why doesn't this work? Apple's documentation allows it in Swift 4.2+, and I'm on Swift 5
I was asked to show how CaseFormat is declared:
class CaseFormat {
var id : Int
var imageName : String
var isSelected : Bool
var solve : String
var testTicks : Int
init(id : Int, imageName : String, isSelected : Bool, solve : String, testTicks : Int) {
self.id = id
self.imageName = imageName
self.isSelected = isSelected
self.solve = solve
self.testTicks = testTicks
}
}

Since CaseFormat is not Equatable, you cannot use == with it.
It's a class so maybe you want to compare references directly using ===?
selectedCasesArray.removeAll(where: { $0 === itemToRemove })
If you want to really use ==, you have to implement Equatable, e.g.:
extension CaseFormat: Equatable {
public static func == (lhs: CaseFormat, rhs: CaseFormat) -> Bool {
return lhs.id == rhs.id
}
}
Of course, the exact behavior should depend on your use case.

Related

Comparing specific values in Dictionary key

I have struct that I use for Dictionary key:
struct MyKey {
let name: String
let flag: Bool
}
What I want is, to change the "flag" property and still being able to find a value specified by that key. Here is complete playground example:
struct MyKey {
let name: String
let flag: Bool
}
extension MyKey: Hashable {
static func == (lhs: MyKey, rhs: MyKey) -> Bool {
return lhs.name == rhs.name
}
}
var fKey = MyKey(name: "fKey", flag: false)
var sKey = MyKey(name: "sKey", flag: true)
var dictFirst: [MyKey: String] = [fKey: "fValue",
sKey: "sValue"]
var dictSecond: [MyKey: String] = [fKey: "fValue",
sKey: "sValue"]
let changedFKey = MyKey(name: "fKey", flag: true)
print(dictFirst[changedFKey]) // Prints nil, want it to be fValue
In fact, in extension I tried to specify that I only cares about "name", but still It's not work as intended
You also have to implement hash value correctly, the generated hash takes flag into account:
extension MyKey: Hashable {
static func == (lhs: MyKey, rhs: MyKey) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
However, in principle this is bad design. If the flag does not affect the key, it shouldn't be part of the key. It would be better to index by name directly.

Deleting from array via object

I have a protocol "Visual" which is implemented by two classes : Point2D and Square
protocol Visual {
var text: String { get }
}
class Point2D : Visual {
var text: String
var x: Double
var y: Double
init(x : Double , y : Double)
{
self.x = x
self.y = y
self.text = "I'm 2d point"
}
}
class Square : Visual {
var text: String
var x: Int
init(x : Int)
{
self.x = x
self.text = "I'm square"
}
}
I have made a class Storage which will keep in array the elements and has two operations add and delete
class Storage
{
var elements : [Visual]
init()
{
elements = [Visual]()
}
func add(item : Visual)
{
elements.append(item)
}
func delete(item : Visual)
{
}
func printItems()
{
print(elements)
}
}
The add works fine , but how should I implement the delete since I pass object with refrence to "Visual".I want to delete the first seen object from left to right ?
First of all, you need a way to evaluate the equality of them. I would suggest something like this. (I found this answer about equality on protocol very helpful.)
protocol Visual {
var text: String { get }
func isEqual(to other: Visual) -> Bool
}
extension Point2D {
func isEqual(to other: Visual) -> Bool {
return (other as? Self).flatMap({ $0.x == self.x && $0.y == self.y }) ?? false
}
}
extension Square {
func isEqual(to other: Visual) -> Bool {
return (other as? Self).flatMap({ $0.x == self.x }) ?? false
}
}
You can then find the first index of the item you want to delete and remove it using the returned index.
class Storage {
// ...
func delete(item : Visual) {
if let index = elements.firstIndex(where: { $0.isEqual(to: item) }) {
elements.remove(at: index)
}
}
}
You can implement the Equatable protocol when you declare Visual protocol, in this way you will be enable to compare object and to understand which are equals and they have to be removed.

Equatable protocol in swift

I am trying to make a simple game implementation. So each game has a correct Answer. The Answer could be an Int or String. So what I have in code is:
protocol Answer {}
extension Int: Answer {}
extension String: Answer {}
protocol CorrectAnswer {
var correctAnswer: Answer { get }
}
I have a protocol for what a game needs:
protocol GameDescriber {
var name: String { get }
var description: String { get }
var points: Int { get }
}
And the implementation of the Game struct:
struct Game: GameDescriber, Equatable, CorrectAnswer {
var correctAnswer: Answer
var name: String
var description: String
var points: Int
static func ==(_ lhs: Game, _ rhs:Game) -> Bool {
if let _ = lhs.correctAnswer as? String, let _ = rhs.correctAnswer as? Int {
return false
}
if let _ = lhs.correctAnswer as? Int, let _ = rhs.correctAnswer as? String {
return false
}
if let lhsInt = lhs.correctAnswer as? Int, let rhsInt = rhs.correctAnswer as? Int {
if lhsInt != rhsInt {
return false
}
}
if let lhsString = lhs.correctAnswer as? String, let rhsString = rhs.correctAnswer as? String {
if lhsString != rhsString {
return false
}
}
return lhs.description == rhs.description &&
lhs.name == rhs.name &&
lhs.points == rhs.points
}
}
If I want to add another Answer type (let's say an array of Ints) I have to do that:
extension Array: Answer where Element == Int {}
But what bothers me is in the implementation of the Equatable func == I have to cover this and possibly other cases as well. Which can me dramatic :)
Is there a solution for this and can it be done in more elegant and generic way?
First note that your implementation of == can be simplified to
static func ==(_ lhs: Game, _ rhs:Game) -> Bool {
switch (lhs.correctAnswer, rhs.correctAnswer) {
case (let lhsInt as Int, let rhsInt as Int):
if lhsInt != rhsInt {
return false
}
case (let lhsString as String, let rhsString as String):
if lhsString != rhsString {
return false
}
default:
return false
}
return lhs.description == rhs.description &&
lhs.name == rhs.name &&
lhs.points == rhs.points
}
so that adding another answer type just means adding one additional
case.
The problem is that the compiler cannot verify that all possible
answer types are handled in your == function, so this approach
is error-prone.
What I actually would do is to use an enum Answer instead of a
protocol, and make that Equatable:
enum Answer: Equatable {
case int(Int)
case string(String)
}
Note that you don't have to implement ==. As of Swift 4.1, the
compiler synthesizes that automatically, see
SE-0185 Synthesizing Equatable and Hashable conformance.
And now Game simplifies to
struct Game: GameDescriber, Equatable, CorrectAnswer {
var correctAnswer: Answer
var name: String
var description: String
var points: Int
}
where the compiler synthesizes == as well, with a default implementation that compares all stored properties for equality.
Adding another answer type is simply done by adding another case to
the enumeration:
enum Answer: Equatable {
case int(Int)
case string(String)
case intArray([Int])
}
without any additional code.

Access Computed Property of a Protocol in a Protocol Extension

Assume I have a protocol like
protocol TypedString : CustomStringConvertible, Equatable, Hashable { }
func == <S : TypedString, T : TypedString> (lhs : T, rhs : S) -> Bool {
return lhs.description == rhs.description
}
I want to be able to provide a default implementation of Hashable:
extension TypedString {
var hashValue = { return self.description.hashValue }
}
But the problem is that I get an error Use of unresolved identifier self.
How do I create a default implementation of Hashable using the string representation given by the description property defined in the CustomStringConvertible protocol?
The motivation is I want to create shallow wrappers around Strings, so I can semantically type-check my code. For example instead of writing func login (u : String, p : String) -> Bool I would instead write func login (u : UserName, p : Password) -> Bool where the (failable) initialisers for UserName and Password do validation. For example I might write
struct UserName : TypedString {
let description : String
init?(userName : String) {
if userName.isEmpty { return nil; }
self.description = userName
}
}
What you want is totally possible, the error message you're receiving just isn't making it very clear what the problem is: your syntax for a computed property is incorrect. Computed properties need to be explicitly typed and don't use an equals sign:
extension TypedString {
var hashValue: Int { return self.description.hashValue }
}
This works fine!

Change the value that is being set in variable's willSet block

I'm trying to sort the array that is being set before setting it but the argument of willSet is immutable and sort mutates the value. How can I overcome this limit?
var files:[File]! = [File]() {
willSet(newFiles) {
newFiles.sort { (a:File, b:File) -> Bool in
return a.created_at > b.created_at
}
}
}
To put this question out of my own project context, I made this gist:
class Person {
var name:String!
var age:Int!
init(name:String, age:Int) {
self.name = name
self.age = age
}
}
let scott = Person(name: "Scott", age: 28)
let will = Person(name: "Will", age: 27)
let john = Person(name: "John", age: 32)
let noah = Person(name: "Noah", age: 15)
var sample = [scott,will,john,noah]
var people:[Person] = [Person]() {
willSet(newPeople) {
newPeople.sort({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
people = sample
people[0]
I get the error stating that newPeople is not mutable and sort is trying to mutate it.
It's not possible to mutate the value inside willSet. If you implement a willSet observer, it is passed the new property value as a constant parameter.
What about modifying it to use didSet?
var people:[Person] = [Person]()
{
didSet
{
people.sort({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
willSet is called just before the value is stored.
didSet is called immediately after the new value is stored.
You can read more about property observers here
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
You can also write a custom getter and setter like below. But didSet seems more convenient.
var _people = [Person]()
var people: [Person] {
get {
return _people
}
set(newPeople) {
_people = newPeople.sorted({ (a:Person, b:Person) -> Bool in
return a.age > b.age
})
}
}
It is not possible to change value types (including arrays) before they are set inside of willSet. You will need to instead use a computed property and backing storage like so:
var _people = [Person]()
var people: [Person] {
get {
return _people
}
set(newPeople) {
_people = newPeople.sorted { $0.age > $1.age }
}
}
Another solution for people who like abstracting away behavior like this (especially those who are used to features like C#'s custom attributes) is to use a Property Wrapper, available since Swift 5.1 (Xcode 11.0).
First, create a new property wrapper struct that can sort Comparable elements:
#propertyWrapper
public struct Sorting<V : MutableCollection & RandomAccessCollection>
where V.Element : Comparable
{
var value: V
public init(wrappedValue: V) {
value = wrappedValue
value.sort()
}
public var wrappedValue: V {
get { value }
set {
value = newValue
value.sort()
}
}
}
and then assuming you implement Comparable-conformance for Person:
extension Person : Comparable {
static func < (lhs: Person, rhs: Person) -> Bool {
lhs.age < lhs.age
}
static func == (lhs: Person, rhs: Person) -> Bool {
lhs.age == lhs.age
}
}
you can declare your property like this and it will be auto-sorted on init or set:
struct SomeStructOrClass
{
#Sorting var people: [Person]
}
// … (given `someStructOrClass` is an instance of `SomeStructOrClass`)
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people.last
Caveat: Property wrappers are not allowed (as of time of writing, Swift 5.7.1) in top-level code— they need to be applied to a property var in a struct, class, or enum.
To more literally follow your sample code, you could easily also create a ReverseSorting property wrapper:
#propertyWrapper
public struct ReverseSorting<V : MutableCollection & RandomAccessCollection & BidirectionalCollection>
where V.Element : Comparable
{
// Implementation is almost the same, except you'll want to also call `value.reverse()`:
// value = …
// value.sort()
// value.reverse()
}
and then the oldest person will be at the first element:
// …
#Sorting var people: [Person]
// …
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]
And even more directly, if your use-case demands using a comparison closure via sort(by:…) instead of implementing Comparable conformance, you can do that to:
#propertyWrapper
public struct SortingBy<V : MutableCollection & RandomAccessCollection>
{
var value: V
private var _areInIncreasingOrder: (V.Element, V.Element) -> Bool
public init(wrappedValue: V, by areInIncreasingOrder: #escaping (V.Element, V.Element) -> Bool) {
_areInIncreasingOrder = areInIncreasingOrder
value = wrappedValue
value.sort(by: _areInIncreasingOrder)
}
public var wrappedValue: V {
get { value }
set {
value = newValue
value.sort(by: _areInIncreasingOrder)
}
}
}
// …
#SortingBy(by: { a, b in a.age > b.age }) var people: [Person] = []
// …
someStructOrClass.people = sample
let oldestPerson = someStructOrClass.people[0]
Caveat: The way SortingBy's init currently works, you'll need to specify an initial value ([]). You can remove this requirement with an additional init (see Swift docs), but that approach is much less complicated when your property wrapper works on a concrete type (e.g. if you wrote a non-generic PersonArraySortingBy property wrapper), as opposed to a generic-on-protocols property wrapper.