Accessing the super from global scope - ios

There is a code in Objective C I'm trying to translate using mostly swift and that includes changing the isEqual: function to == operand. The problem is that the code asks me to check the super of the instances being compared but that's impossible from global scope where the operator functions are located.
Is there some other way to migrate this code to swift?
- (BOOL)isEqual:(id)object
{
AAPLCollectionViewGridLayoutAttributes *other = object;
if (![super isEqual:other])
return NO;
return YES;
}

You can split the solution into 2 steps:
1) Define an extension of AAPLCollectionViewGridLayoutAttributes where you override the isEqual method. (Here you can access the super).
extension AAPLCollectionViewGridLayoutAttributes {
public override func isEqual(object: AnyObject!) -> Bool {
// your custom logic will go here
var other = object as AAPLCollectionViewGridLayoutAttributes
return super.isEqual(other)
}
}
2) Overload the == operator simply calling the method defined above.
public func ==(lhs: AAPLCollectionViewGridLayoutAttributes, rhs: AAPLCollectionViewGridLayoutAttributes) -> Bool {
return lhs.isEqual(rhs)
}
Please note that I used the "public" modifier twice, they should be compatible with the access modifier of your class AAPLCollectionViewGridLayoutAttributes.

Related

How to make protocol inheritance with strong constraints

I would like to make some abstract protocol with generic types
protocol UseCase {
associatedtype P
associatedtype R
func execute(params: P) async throws -> R
}
Then I would like to make some concrete protocol with some types
protocol TestUseCase: UseCase where P == Int, R == Int { }
And then use it to declare some implementation to use it on another side
class TestUseCaseImpl: TestUseCase {
func execute(params: Int) async throws -> Int {
// some impl
}
}
When I try to use it inside of my view model I got such error:
class ViewModel {
private let testUseCase: TestUseCase // error
init(testUseCase: TestUseCase) { // errror
self.testUseCase = testUseCase
}
}
Protocol 'TestUseCase' can only be used as a generic constraint because it has Self or associated type requirements
To fix such problem I can declare generic ViewModel with some use case type like this:
class ViewModel<UseCase: TestUseCase> {
private let testUseCase: UseCase
init(testUseCase: UseCase) {
self.testUseCase = testUseCase
}
}
Problem around ViewModel - it's possible to have many use cases inside of ViewModel.
How to implement such idea without generic ViewModel?
You need to use the any keyword. This is only supported in Swift >= 5.7.
Note: it is very unlikely that you want a type called ViewModel. Nest type Model inside of View if that's what it is.
class ViewModel {
private let testUseCase: any TestUseCase
init(testUseCase: some TestUseCase) {
self.testUseCase = testUseCase
}
}

Is there an error prone way to exclude certain properties from struct during equality comparison without writting our own == function?

We like the auto equality behavior of struct, by just simply conform Equatable protocol.
Sometimes, we would like to exclude some properties out from struct equality comparison.
Currently, this is the method we are using.
struct House: Equatable {
var field_0: String
var field_1: String
...
var field_50: String
static func == (lhs: House, rhs: House) -> Bool {
if lhs.field_0 != rhs.field_0 { return false }
if lhs.field_1 != rhs.field_1 { return false }
...
if lhs.field_48 != rhs.field_48 { return false }
// Ommit field_49
if lhs.field_50 != rhs.field_50 { return false }
return true
}
}
But, such methodology is pretty error prone, especially when we add in new field in the struct.
Is there a way to exclude certain properties fields from equality comparison, without writing our own == function?
Is there a way to exclude certain properties fields from equality comparison, without writing our own == function?
No. At present, the only alternative to automatic synthesis of Equatable conformance is good old fashioned writing code.
There is a solution using Swift Property Wrapper feature actually. It's described here.
Basically what you need is to define next property wrapper
#propertyWrapper
struct EquatableNoop<Value>: Equatable {
var wrappedValue: Value
static func == (lhs: EquatableNoop<Value>, rhs: EquatableNoop<Value>) -> Bool {
true
}
}
And use it like next
struct Test: Equatable {
var a: Int
#EquatableNoop
var b: () -> Void
}

Swift 3: Array.contains(customObject)

I am trying to implement the if block seen below:
if loggedInUser.following.contains(userToView) {
}
where loggedInUser.following is an array of a custom User class and userToView is a single instance of the User class.
class User {
....
}
However, loggedInUser.following.contains(userToView) is throwing the error seen below:
Cannot convert value of type 'User' to expected argument type '(User) throws -> Bool'
I am under the impression I will need to implement some sort of a function that determines if two users are equal, but i have no idea how to implement such a function. Any help would be much appreciated; thank you in advance!
Yo can use the code below
if (loggedInUser.following as NSArray).contains(userToView) {
}
Thank you all for the suggestions.
Everything worked as expected after creating the following function and implementing the "Equatable" Protocol:
class User: Equatable {
//...
var id = Int() //Unique Identifier
//...
}
func ==(lhs: User, rhs: User) -> Bool {
return lhs.id == rhs.id
}
and after implementing the above code i was able to call the following without any problems:
if loggedInUser.following.contains(userToView) {
...
}

EXC_BAD_ACCESS when using generics

I'm trying to implement a cache for my entities with using generics in Swift. Here is my code:
class BaseCache<T>: NSObject {
var allEntities = [T]()
// MARK: - Append
func appendEntities(newEntities: [T]) {
for entity in newEntities {
// Check if allEntities array already contains an entity
var contains = false
for item in allEntities {
// EXC_BAD_ACCESS in isEqual method (see below)
if isEqual(entity, rightEntity: item) {
contains = true
break
}
}
if !contains {
allEntities.append(entity)
}
}
}
func isEqual(leftEntity: T, rightEntity: T) -> Bool {
return false
}
}
Here is a concrete implementation of BaseCache:
class CourierCache<T: AftershipCourier>: BaseCache<T> {
override func isEqual(leftEntity: T, rightEntity: T) -> Bool {
println("\(leftEntity)") // EXC_BAD_ACCESS here
println("\(rightEntity)")
return rightEntity.slug == leftEntity.slug
}
}
Any ideas how to fix that? Thanks!
PS: Note that this question is not relevant to my question
Looks to me like you’ve found a Swift bug. Here’s as simple as I could get it:
class C { }
class Base<T> {
func callCrash(t: T) {
crash(t)
}
func crash(t: T) { }
}
class Sub<T: C>: Base<T> {
override func crash(t: T) {
println(t) // EXC_BAD_ACCESS here
}
}
let sub = Sub<C>()
sub.callCrash(C())
However, you would probably be better served by putting the ability to detect equality into a protocol, and then requiring the objects, rather than the cache, to check for equality.
#rakeshbs’s answer shows how to do this with Equatable, but I would add a couple of caveats that means you may not want to use this approach:
You are checking a property, slug, to test for equality. Equality in Swift implies substitutability – i.e. if two elements are equal via ==, they should be completely equivalent and you should be able to substitute one for the other without anyone noticing. If your ships have properties that can vary even while their slug property is the same, this will not be the case. This can lead to some nasty bugs if you use library functions like contains or sort that rely on this substitutability property. If you are using classes, then you might find the identity operator (===) is a good thing to use to implement the equality operator.
Using equatable and == operators and generics means your comparison function will be statically bound, because operators are not member functions. That means if you hold in your cache different objects in the hierarchy, you won't get dynamic dispatch on your == operator. That is, if you have an AftershipCourier cache and you put FastAftershipCourier classes in it, you could find that the == for AftershipCourier be run between them, instead of a custom == that compares FastAftershipCourier. So if you need dynamic behaviour, make sure to have == call a method on the passed-in argument, that can be overridden by subclasses, rather than just comparing properties directly.
To resolve both these issues, use a protocol of your own devising with a comparison function, have the courier classes implement it, and then call it within your cache code.
P.S. your for loop checking the entity against allEntities can be written as let alreadyContained = contains(allEntities) { entity.comparingFuncYouDecideOn($0) }
You can modify your code like this to achieve what you need. Make use of the Equatable protocol to compare the AfterCourier instances. And use type alias to fix the type inside CourierCache.
class BaseCache<T:Equatable> {
var allEntities :Array<T> = []
func appendEntities(newEntities: [T])
{
for entity in newEntities {
if !contains(allEntities,entity)
{
allEntities.append(entity)
}
}
}
func print()
{
println("Entities : \(allEntities)")
}
}
class CourierCache<S>: BaseCache<AftershipCourier>
{
func testCourier()
{
for courier in allEntities
{
println("Courier Slug: \(courier.slug)")
}
}
}
class AftershipCourier : Equatable,Printable
{
var description: String {
return "Courier Slug: \(slug)"
}
var slug : Int = 0
}
func == (lhs: AftershipCourier, rhs: AftershipCourier) -> Bool
{
return lhs.slug == rhs.slug
}
typealias AfterCourierCache = CourierCache<AftershipCourier>
You can use it like this.
var cache = CourierCache<AftershipCourier>()
var t1 = AftershipCourier()
t1.slug = 1
var t2 = AftershipCourier()
t2.slug = 2
cache.appendEntities([t1])
cache.appendEntities([t2])
cache.print()
cache.testCourier();

Lazy/inline implement a protocol in Swift

I want to lazy/inline implement a protocol in Swift.
So in the point of the implementation I will have access to variables outside the protocol scope ,
Same as implementing a interface in Java without declaring a class:
class MyClass:UIView {
var someComponent:SomeInnerComponent = SomeInnerComponent();
var count:Int = 0;
var a = :SomeProtocol { //<----- IS THIS POSSIBLE, IF YES HOW ?
func a0() {MyClass.count--}
func a1() {MyClass.count++}
}
someComponenet.delegate = a;
}
protocol SomeProtocol {
func a0()
func a1()
}
editing----
thanks i look at this solution, and i didn't see how to access a variable of the parent class.
all the examples show an Anonymous class but no one of the examples is accessing the parent variables .
What you're looking for is an inner class (not necessarily an anonymous one), declared in a scope that lets it access the count variable of a MyClass instance, and that adopts a protocol defined at a different scope. Right now Swift has a few of those pieces, but it doesn't look like you can put them all together in any way that's as concise as what you might be looking for.
You might think about declaring an inner class:
class MyView: UIView {
let someComponent = SomeInnerComponent() // type SomeInnerComponent is inferred
var count = 0 // type Int is inferred
class Helper: SomeProtocol {
func a0() { count-- } // ERROR
// ...
}
init() {
someComponent.delegate = Helper()
}
}
But that won't work, because count is implicitly self.count, where self is a Helper instance, not the MyView instance that "owns" the Helper instance. And there isn't a way to reference that MyView instance (or its properties) from within a Helper's methods, because you could just as well construct a MyView.Helper() without having an existing MyView instance. Inner classes (or nested types in general) in Swift nest only in lexical scope, not in existential ownership. (Or to put it another way, since you referenced Java: all inner classes in Swift are like static inner classes in Java. There's no non-static inner class.) If that's a feature you'd like, though, it's probably worth telling Apple you want it.
You could also try declaring Helper inside MyView.init() -- in Swift you can nest type definitions anywhere, including inside functions or methods of other types. Defined there, it can refer to MyView's properties. However, now the type information for Helper is only visible inside of MyView.init(), so when you assign it to someComponent.delegate (whose type is just SomeProtocol), you can't make use of it... this crashes the compiler, even. (That's another bug to report, but it's hard to say whether the bug is really "compiler crashes on valid usage" or "code is bad, but compiler crashes instead of producing error".)
The closest solution I can come up with looks something like this:
class SomeInnerComponent {
var delegate: SomeProtocol?
}
protocol SomeProtocol {
func a0()
func a1()
}
class MyClass {
var someComponent = SomeInnerComponent()
var count = 0
struct Helper: SomeProtocol {
var dec: () -> ()
var inc: () -> ()
func a0() { dec() }
func a1() { inc() }
}
init() {
someComponent.delegate = Helper(
dec: { self.count -= 1 }, // see note below
inc: { self.count += 1 }
)
}
}
How it works:
Helper is an inner struct (could be a class, but a struct is simpler)
It implements the a0 and a1 methods, satisfying the requirements of SomeProtocol
The implementations of a0 and a1 call through to the closures dec and inc, which are stored properties (aka instance variables) of the Helper struct
You write (or otherwise specify) these closures when you construct a Helper instance (using the default member-wise initializer, Helper(dec: (Void -> Void), inc: (Void -> Void)))
Because you can write the closures when initializing a Helper, those closures can capture variables where you're calling the initializer, including the implicit self that refers to the MyClass instance creating the Helper.
You need both a0/a1 and dec/inc because you need closures (the latter), not methods, for capturing the enclosing state. And even though closures and funcs/methods are in many ways interchangeable, you can't create a method/func implementation by assigning a closure to a method/func name. (It'd be a different story if SomeProtocol required closure properties instead of methods, but I'm assuming SomeProtocol isn't something under your control.)
Anyway, this is kind of a lot of boilerplate and a layer of abstraction that you might not really need, so it's probably worth looking into other ways to architect your code.
Note: my example uses the closure { self.count -= 1 } where you might expect { self.count-- }. The latter doesn't work because that's an expression with a value, so Swift will interpret it as shorthand for the closure's return value. Then it'll complain that you assigned a () -> Int closure to a property that expects a () -> () (aka Void -> Void) closure. Using -= 1 instead works around this issue.
I would go for a different approach, I know this a pretty old topic but just in case someone else struggles with this issue:
class MyClass:UIView {
var someComponent:SomeInnerComponent = SomeInnerComponent();
var count:Int = 0;
init(){
// Assign the delegate or do it somewhere else to your preference:
someComponenet.delegate = ProtocolImplementation(myClass: self);
}
private class ProtocolImplementation: SomeProtocol {
let selfReference: MyClass
init(myClass: MyClass){
selfReference = myClass
}
public func a0(){
selfReference.count--
}
public func a1(){
selfReference.count++
}
}
}
protocol SomeProtocol {
func a0()
func a1()
}
By following this approach it's also possible to include the same protocol multiple times, lets say your Protocol supports a generic and you want to implement it twice. SomeProtocol< SomeObject > and SomeProtocol< OtherObject > could be both used this way if needed.
Kind regards

Resources