XCode 6, Beta 5
I have a unit test like this:
func testMyObjectsEqual()
{
//....
XCTAssertEqual(myObject, myOtherObject, "\(myObject) and \(myOtherObject) should be equal")
}
XCTAssertEqualObjects is no longer available in Swift since the language makes no distinction between scalars and objects.
So we have to use XCTAssertEqual which leads to the following error:
"Type MyObject does not conform to protocol Equatable"
The only workaround I have found is to inherit (MyObject) from NSObject so that I can do the following:
XCTAssert(myObject == myOtherObject, "\(myObject) and \(myOtherObject) should be equal")
So my question is: Is there a way (as of beta 5) to use XCTAssertEqual for custom types without having to rely on NSObject or littering all custom types with "==" overloads ?
If you want to compare references in Swift, you can use === operator. This is what happens when you subclass from NSObject (when you are comparing objects in Objective-C using XCTAssertEqual, you are comparing references).
But is this really what you want?
class MyObject {
var name: String
init(name: String) {
self.name = name
}
}
func testMyObjectsEqual() {
let myObject = MyObject(name: "obj1")
let myOtherObject = MyObject(name: "obj1")
let otherReferenceToMyFirstObject = myObject
XCTAssert(myObject === myOtherObject) // fails
XCTAssert(myObject === otherReferenceToMyFirstObject) // passes
}
I guess that when you are comparing custom objects, you probably should make them conform to the Equatable protocol and specify, when your custom objects are equal (in the following case the objects are equal when they have the same name):
class MyObject: Equatable {
var name: String
init(name: String) {
self.name = name
}
}
func ==(lhs: MyObject, rhs: MyObject) -> Bool {
return lhs.name == rhs.name
}
func testMyObjectsEqual()
{
let myObject = MyObject(name: "obj1")
let myOtherObject = MyObject(name: "obj1")
XCTAssertEqual(myObject, myOtherObject) // passes
}
Since Xcode 12.5 there is XCTAssertIdentical
Related
I am trying to use my custom class ChallengeUIElement as rawValue of my enum ChallengeUIElementType.
Therefore, I tried everything that Xcode wanted me to do, but I still get error messages thrown right at my face. It starts to hurt...
Anyways, I followed along this tutorial and get lots of error messages that I do not understand.
After googling for a while, I found some stack overflow entries about classes as rawValue types of enums.
However, I still was unable to get my enum with class type working.
My errors:
Here is my code:
enum ChallengeUIElementType: ChallengeUIElement, CaseIterable {
typealias RawValue = ChallengeUIElement
case Default = "DefaultElementCell"
}
class ChallengeUIElement: Equatable, ExpressibleByStringLiteral {
static func == (lhs: ChallengeUIElement, rhs: ChallengeUIElement) -> Bool {
return lhs.identifier == rhs.identifier && lhs.height == rhs.height && lhs.type == rhs.type
}
var height: CGFloat
var identifier: String
var type: UICollectionViewCell.Type
public init(stringLiteral value: String) {
let components = value.components(separatedBy: ",")
if components.count == 1 {
if components[0] == "DefaultElementCell" {
self.identifier = components[0]
self.type = DefaultElementCell.self
self.height = 380
}
}
}
public init(unicodeScalarLiteral value: String) {
self.init(stringLiteral: value)
}
public init(extendedGraphemeClusterLiteral value: String) {
self.init(stringLiteral: value)
}
}
Also, I do not understand why I could use RawRepresentable as protocol with this approach, but not with this one?
Any help would be highly appreciated.
The errors you get are mostly about the way you incorrectly declared your initialisers. They don't really have much to do with creating an enum with a class raw value.
Because your initialisers are required by the protocol, they need to be marked as required. You don't need to do this if your class is final. So either mark the class as final or mark all the initialisers as required.
The two convenience initialisers need to be marked as convenience:
public convenience init(unicodeScalarLiteral value: String) {
self.init(stringLiteral: value)
}
public convenience init(extendedGraphemeClusterLiteral value: String) {
self.init(stringLiteral: value)
}
These are "convenience" initialisers because they call another initialiser declared in the class.
Additionally, the public init(stringLiteral value: String) initialiser does not initialise all the properties. Think about what happens if the if statements are not run. You need to give your properties (height, identifier and type) some default values.
I'm trying to generate a ViewModel that conforms to a Protocol Protocoling, the protocol is generic, and has an associated type.
There are a few ViewModel's that conform to the protocol, so I am trying to create a factory for the viewModel.
I have encotuntered the following error by Swift:
Protocol can only be used as a generic constraint because it has Self or associated type requirements
Example code:
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> Protocoling {
switch type {
case .name: return NameViewModel(name: "Gil")
case .address: return AddressViewModel(address: ["Israel", "Tel Aviv"])
}
}
}
The error is in func viewModel(forType type: MyTypes) -> Protocoling.
Is there a way to solve this issue?
You can use a protocol with an associated type (PAT) as a return type like that without more constraint because the compiler needs to know which type to use.
In your case you must use a technic called the type erasure to be able to work with any Protocoling:
class AnyProtocoling: Protocoling {
let data: Any
init<U: Protocoling>(_ viewModel: U) {
self.data = viewModel.data as Any
}
}
class DataFactory {
func viewModel(forType type: MyTypes) -> AnyProtocoling {
switch type {
case .myName:
return AnyProtocoling(NameViewModel(name: "Gil"))
case .myAddress:
return AnyProtocoling(AddressViewModel(address: ["Israel", "Tel Aviv"]))
}
}
}
This will allow you to "erase" the associated type of your protocol and return an Any version of your view model.
In order to understand why the PAT needs to work like that I like the following example: the Equatable protocol (which is a PAT):
static func ==(lhs: Self, rhs: Self) -> Bool
This function uses the Self type which is an associated type. You want to use it in the next generic function:
func areEquals(left: Equatable, right: Equatable) -> Bool {
return left == right
}
Here the compiler will trigger this error: Protocol can only be used as a generic constraint because it has Self or associated type requirements. Why? Lets take this example:
struct tomato: Equatable {}
struct salad: Equatable {}
areEquals(left: tomato(), right: salad())
There is no reason to compare tomatoes and salads. The associated type Self is not the same. To avoid this error in this case you need to constraint the Self type as following:
func areEquals<T: Equatable>(left: T, right: T) -> Bool
Now you know the T are equatables and with the same associated types.
This is very simple to fix, in your concrete factory implementation you just need to specify a generic for your factory that has to conform to protocol protocoling, see code below :
Swift 4
protocol Protocoling {
associatedtype modulingType
var data: modulingType { get }
}
enum MyTypes {
case myName
case myAddress
}
class NameViewModel: Protocoling {
let data: String
init(name: String) {
data = name
}
}
class AddressViewModel: Protocoling {
let data: [String]
init(address: [String]) {
data = address
}
}
class DataFactory<T> where T: Protocoling {
func viewModel(forType type: MyTypes) -> T? {
switch type {
case .myName: return NameViewModel(name: "Gil") as? T
case .myAddress: return AddressViewModel(address: ["Israel", "Tel Aviv"]) as? T
default: return nil /* SUPPORT EXTENSION WITHOUT BREAKING */
}
}
}
It's a first step into the wonderful world of abstraction with protocols. You really create some amazing things with it. Though, I have to say, that personally it's not as intuitive as something like inheritance, it's a great little mind bending puzzle for creating decoupled and abstract systems, that are actually far more powerful.
Swift is a great introductory language, and I believe that it's protocol and extension mechanisms make it one of the more complex and interesting languages.
This design pattern is a great way setting up things like dependency injection.
I have been trying to extract non-nil values from the String array. Like below. But, my senior wants it to be able to extract non-nil values from other types too.
I read, generics could help me for handling different types. How can I use generics so that I get to use following like extension to work with other types too?
getNonNil must return the extracted non-nil values of the specific type (i.e. if array is [String?] it must return [String], returns [Int] if [Int?])
Because I have to do further calculations.
What I have tried is below:
import Foundation
// Extended the collection-type so that collectiontype is constrained to having element with optional strings
extension CollectionType where Self.Generator.Element == Optional<String>{
func getNonNil() -> [String] {
// filter out all nil elements and forcefully unwrap them using map
return self.filter({$0 != nil}).map({$0!})
}
}
// Usage
let x: [String?] = ["Er", "Err", nil, "errr"]
x.getNonNil().forEach { (str) in
print(str)
}
For getNonNil you could simply use
x.flatMap { $0 }
// returns ["Er", "Err", "errr"] which is [String]
For the original question, typically you could introduce a protocol to the Optional type (e.g. via the muukii/OptionalProtocol package):
protocol OptionalProtocol {
associatedtype Wrapped
var value: Wrapped? { get }
}
extension Optional: OptionalProtocol {
public var value: Wrapped? { return self }
}
extension CollectionType where Self.Generator.Element: OptionalProtocol {
func getNonNil() -> [Self.Generator.Element.Wrapped] {
...
}
}
There's no easy way of achieving this through an extension, as you cannot introduce new generic types into extensions (although this is part of the Swift Generics Manifesto – so may well be possibly in a future version of Swift).
As #kennytm says, the simplest solution is just to use flatMap, which filters out nil:
x.flatMap{$0}.forEach { (str) in
print(str)
}
If however, you still want this as an extension, you could use a protocol workaround in order to allow you to constrain the extension to any optional element type (Swift 3):
protocol _OptionalProtocol {
associatedtype Wrapped
func _asOptional() -> Wrapped?
}
extension Optional : _OptionalProtocol {
func _asOptional() -> Wrapped? {return self}
}
extension Collection where Self.Iterator.Element : _OptionalProtocol {
func getNonNil() -> [Iterator.Element.Wrapped] {
return flatMap{$0._asOptional()}
}
}
...
let x : [String?] = ["Er", "Err", nil, "errr"]
x.getNonNil().forEach { (str) in
print(str)
}
(In Swift 3, CollectionType has been renamed to Collection, and Generator is now Iterator)
Although flatMap is almost certainly preferred in this situation, I'm only really adding this for the sake of completion.
The easiest approach is using flatMap as kennytm suggested, but if you absolutely want to know how to create such a method using generics, one approach would be to create a global method that takes in the collection as a parameter:
public func getNonNil<T, C: CollectionType where C.Generator.Element == Optional<T>>(collection: C) -> [T] {
return collection.filter({$0 != nil}).map({$0!})
}
let x: [String?] = ["Er", "Err", nil, "errr"]
print(getNonNil(x)) // returns ["Er", "Err", "errr"]
When I am writing code for finding an item from the array with the use of indexOf it shows me the above stated error.
Here is my code:-
func addItemViewController(controller: AddItemViewController, didFinishEditingItem item: ChecklistItem)
{
if let index = items.indexOf(item)
{
let indexPath = NSIndexPath(forRow: index, inSection: 0)
if let cell = tableView.cellForRowAtIndexPath(indexPath)
{
configureTextForCell(cell, withChecklistItem: item)
}
}
In order to use indexOf the ChecklistItem must adopt Equatable protocol. Only by adopting this protocol the list can compare an item with other items to find the desired index
indexOf can only be applied to Collections of Equatable types, your ChecklistItem doesn't conform to Equatable protocol (have an == operator).
To be able to use indexOf add this to the file containing ChecklistItem class in the global scope :
func ==(lhs: ChecklistItem, rhs: ChecklistItem) -> Bool {
return lhs === rhs
}
Swift3:
public static func ==(lhs: Place, rhs: Place) -> Bool {
return lhs === rhs
}
Please note it will make comparison by comparing instances addresses in memory. You may want to check equality by comparing members of the class instead.
In Swift 4 and Swift 3, update your Data Model to conform to "Equatable" protocol, and implement the lhs=rhs method , only then you can use ".index(of:...)", because you are comparing your custom object
Eg:
class Photo : Equatable{
var imageURL: URL?
init(imageURL: URL){
self.imageURL = imageURL
}
static func == (lhs: Photo, rhs: Photo) -> Bool{
return lhs.imageURL == rhs.imageURL
}
}
Usage:
let index = self.photos.index(of: aPhoto)
I realize this question already has an accepted answer, but I found another case that will cause this error so it might help someone else. I'm using Swift 3.
If you create a collection and allow the type to be inferred you may also see this error.
Example:
// UITextfield conforms to the 'Equatable' protocol, but if you create an
// array of UITextfields and leave the explicit type off you will
// also see this error when trying to find the index as below
let fields = [
tf_username,
tf_email,
tf_firstname,
tf_lastname,
tf_password,
tf_password2
]
// you will see the error here
let index = fields.index(of: textField)
// to fix this issue update your array declaration with an explicit type
let fields:[UITextField] = [
// fields here
]
The possible reason is you didn't tell the ChecklistItem type that it is equatable, maybe you forgot to mention ChecklistItem class is inherited from NSObject.
import Foundation
class ChecklistItem: NSObject {
var text = ""
var checked = false
func toggleChecked() {
checked = !checked
}
}
NSObject adopts or conforms to the equatable protocol
I'm banging my head against the wall with the following code in Swift. I've defined a simple protocol:
protocol Nameable {
var name : String { get set }
}
and implemented that with:
class NameableImpl : Nameable {
var name : String = ""
}
and then I have the following method in another file (don't ask me why):
func nameNameable( nameable: Nameable, name: String ) {
nameable.name = name
}
The problem is that the compiler gives the following error for the property assignment in this method:
cannot assign to 'name' in 'nameable'
I can't see what I'm doing wrong... The following code compiles fine:
var nameable : Nameable = NameableImpl()
nameable.name = "John"
I'm sure it's something simple I've overlooked - what am I doing wrong?
#matt's anwer is correct.
Another solution is to declare Nameable as a class only protocol.
protocol Nameable: class {
// ^^^^^^^
var name : String { get set }
}
I think, this solution is more suitable for this case. Because nameNameable is useless unless nameable is a instance of class.
It's because, Nameable being a protocol, Swift doesn't know what kind (flavor) of object your function's incoming Nameable is. It might be a class instance, sure - but it might be a struct instance. And you can't assign to a property of a constant struct, as the following example demonstrates:
struct NameableStruct : Nameable {
var name : String = ""
}
let ns = NameableStruct(name:"one")
ns.name = "two" // can't assign
Well, by default, an incoming function parameter is a constant - it is exactly as if you had said let in your function declaration before you said nameable.
The solution is to make this parameter not be a constant:
func nameNameable(var nameable: Nameable, name: String ) {
^^^
NOTE Later versions of Swift have abolished the var function parameter notation, so you'd accomplish the same thing by assigning the constant to a variable:
protocol Nameable {
var name : String { get set }
}
func nameNameable(nameable: Nameable, name: String) {
var nameable = nameable // can't compile without this line
nameable.name = name
}
Here, i written some code, that might give some idea on Associated generic type Usage:
protocol NumaricType
{
typealias elementType
func plus(lhs : elementType, _ rhs : elementType) -> elementType
func minus(lhs : elementType, _ rhs : elementType) -> elementType
}
struct Arthamatic :NumaricType {
func addMethod(element1 :Int, element2 :Int) -> Int {
return plus(element1, element2)
}
func minusMethod(ele1 :Int, ele2 :Int) -> Int {
return minus(ele1, ele2)
}
typealias elementType = Int
func plus(lhs: elementType, _ rhs: elementType) -> elementType {
return lhs + rhs
}
func minus(lhs: elementType, _ rhs: elementType) -> elementType {
return lhs - rhs
}
}
**Output:**
let obj = Arthamatic().addMethod(34, element2: 45) // 79