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();
Related
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
}
I have had to use type erasure in Swift a few times however it always involved a generic protocol. In this case, it involves both a generic enum and and generic protocol and I'm stumped.
Here is my generic enum and generic protocol with the necessary extension:
enum UIState<T> {
case Loading
case Success([T])
case Failure(ErrorType)
}
protocol ModelsDelegate: class {
associatedtype Model
var state: UIState<[Model]> { get set }
}
extension ModelsDelegate {
func getNewState(state: UIState<[Model]>) -> UIState<[Model]> {
return state
}
func setNewState(models: UIState<[Model]>) {
state = models
}
}
And here is my type erased generic class:
class AnyModelsDelegate<T>: ModelsDelegate {
var state: UIState<[T]> {
get { return _getNewState(UIState<[T]>) } // Error #1
set { _setNewState(newValue) }
}
private let _getNewState: ((UIState<[T]>) -> UIState<[T]>)
private let _setNewState: (UIState<[T]> -> Void)
required init<U: ModelsDelegate where U.Model == T>(_ models: U) {
_getNewState = models.getNewState
_setNewState = models.setNewState
}
}
I'm getting the following errors (they are marked in the code sample):
Error #1:
Cannot convert value of type '(UIState<[T]>).Type' (aka 'UIState<Array<T>>.Type') to expected argument type 'UIState<[_]>' (aka 'UIState<Array<_>>')
I have been working on this for awhile and there have been quite a few variations on this code that "almost worked". The error always has to do with the getter.
The problem that causes this error, as #dan has pointed out, is that on this line you're trying to pass a type as an argument, instead of an instance of that type:
get { return _getNewState(UIState<[T]>) }
However, I would question your use of an argument to this function in the first place, surely a getting function should have no argument at all? In this case you'll simply want your _getNewState function to have the signature () -> UIState<[T]>, and call it like so:
get { return _getNewState() }
Also, if your getNewState and setNewState(_:) functions in your protocol extension only exist in order to forward the getting and setting of your state property to the type-erasure – you can simplify your code by getting rid of them entirely and use closure expressions in the type-erasure's init instead:
_getNewState = { models.state }
_setNewState = { models.state = $0 }
(These work by capturing a reference to the models argument, for more info see Closures: Capturing Values)
Finally, I suspect that you mean to refer to UIState<T> rather than UIState<[T]> throughout your code, as T in this case refers to an element in the array that your .Success case has as an associated value (unless you want a 2D array here).
All in all, with the above proposed changes, you'll want your code to look something like this:
enum UIState<T> {
case Loading
case Success([T])
case Failure(ErrorType)
}
protocol ModelsDelegate: class {
associatedtype Model
var state: UIState<Model> { get set }
}
class AnyModelsDelegate<T>: ModelsDelegate {
var state: UIState<T> {
get { return _getNewState() }
set { _setNewState(newValue) }
}
private let _getNewState: () -> UIState<T>
private let _setNewState: (UIState<T>) -> Void
required init<U: ModelsDelegate where U.Model == T>(_ models: U) {
_getNewState = { models.state }
_setNewState = { models.state = $0 }
}
}
I am trying to build some mocking infrastructure, I want to be able to return a stubbed value and count the times the value was accessed. I have something simple like this:
class BasicMock<T> {
var callsCount = 0
private let backing: T
var result: T {
callsCount++
return backing
}
init(result: T) {
self.backing = result
}
}
class MockTimeDefinitionSerialiser: BasicMock<[String: [AnyObject]]>, TimeDefinitionSerialiserProtocol {
func serialiseTravelTime(travelTime: JSSTravelTime) -> [String: AnyObject] {
return result
}
}
However trying to build it:
let mockTimeDefinitionSerialiser = MockTimeDefinitionSerialiser(result: ["": ""])
Emits the error 'MockTimeDefinitionSerialiser' cannot be constructed because it has no accessible initialisers
My interpretation of the Swift docs is that I should automatically inherit the initialiser as I have set all stored properties.
What am I doing wrong?
Please remove any unnecessary code when asking a question. I was able to reduce your problem to this:
class Base<T> {
init(t: T) {}
}
class Sub: Base<Int> {}
Sub(t: 0) // error: 'Sub' cannot be constructed because it has no accessible initialisers
It seems like even though you specified the T in the subclass, the compiler cannot infer what the initialiser uses for T. I couldn't find a way to get the initialiser to be inherited, you'd have to use a workaround:
class Sub: Base<Int> {
override init(t: Int) {
super.init(t: t)
}
}
As an exercise in learning I'm rewriting my validation library in Swift.
I have a ValidationRule protocol that defines what individual rules should look like:
protocol ValidationRule {
typealias InputType
func validateInput(input: InputType) -> Bool
//...
}
The associated type InputType defines the type of input to be validated (e.g String). It can be explicit or generic.
Here are two rules:
struct ValidationRuleLength: ValidationRule {
typealias InputType = String
//...
}
struct ValidationRuleCondition<T>: ValidationRule {
typealias InputType = T
// ...
}
Elsewhere, I have a function that validates an input with a collection of ValidationRules:
static func validate<R: ValidationRule>(input i: R.InputType, rules rs: [R]) -> ValidationResult {
let errors = rs.filter { !$0.validateInput(i) }.map { $0.failureMessage }
return errors.isEmpty ? .Valid : .Invalid(errors)
}
I thought this was going to work but the compiler disagrees.
In the following example, even though the input is a String, rule1's InputType is a String, and rule2s InputType is a String...
func testThatItCanEvaluateMultipleRules() {
let rule1 = ValidationRuleCondition<String>(failureMessage: "message1") { $0.characters.count > 0 }
let rule2 = ValidationRuleLength(min: 1, failureMessage: "message2")
let invalid = Validator.validate(input: "", rules: [rule1, rule2])
XCTAssertEqual(invalid, .Invalid(["message1", "message2"]))
}
... I'm getting extremely helpful error message:
_ is not convertible to ValidationRuleLength
which is cryptic but suggests that the types should be exactly equal?
So my question is... how do I append different types that all conform to a protocol with an associated type into a collection?
Unsure how to achieve what I'm attempting, or if it's even possible?
EDIT
Here's it is without context:
protocol Foo {
typealias FooType
func doSomething(thing: FooType)
}
class Bar<T>: Foo {
typealias FooType = T
func doSomething(thing: T) {
print(thing)
}
}
class Baz: Foo {
typealias FooType = String
func doSomething(thing: String) {
print(thing)
}
}
func doSomethingWithFoos<F: Foo>(thing: [F]) {
print(thing)
}
let bar = Bar<String>()
let baz = Baz()
let foos: [Foo] = [bar, baz]
doSomethingWithFoos(foos)
Here we get:
Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.
I understand that. What I need to say is something like:
doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) {
}
Protocols with type aliases cannot be used this way. Swift doesn't have a way to talk directly about meta-types like ValidationRule or Array. You can only deal with instantiations like ValidationRule where... or Array<String>. With typealiases, there's no way to get there directly. So we have to get there indirectly with type erasure.
Swift has several type-erasers. AnySequence, AnyGenerator, AnyForwardIndex, etc. These are generic versions of protocols. We can build our own AnyValidationRule:
struct AnyValidationRule<InputType>: ValidationRule {
private let validator: (InputType) -> Bool
init<Base: ValidationRule where Base.InputType == InputType>(_ base: Base) {
validator = base.validate
}
func validate(input: InputType) -> Bool { return validator(input) }
}
The deep magic here is validator. It's possible that there's some other way to do type erasure without a closure, but that's the best way I know. (I also hate the fact that Swift cannot handle validate being a closure property. In Swift, property getters aren't proper methods. So you need the extra indirection layer of validator.)
With that in place, you can make the kinds of arrays you wanted:
let len = ValidationRuleLength()
len.validate("stuff")
let cond = ValidationRuleCondition<String>()
cond.validate("otherstuff")
let rules = [AnyValidationRule(len), AnyValidationRule(cond)]
let passed = rules.reduce(true) { $0 && $1.validate("combined") }
Note that type erasure doesn't throw away type safety. It just "erases" a layer of implementation detail. AnyValidationRule<String> is still different from AnyValidationRule<Int>, so this will fail:
let len = ValidationRuleLength()
let condInt = ValidationRuleCondition<Int>()
let badRules = [AnyValidationRule(len), AnyValidationRule(condInt)]
// error: type of expression is ambiguous without more context
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.