cannot convert expression type void to type integer in Swift using XCTAssertEqual - swift

I am very new to the Swift language and XCode. Have a error message from this code:
Class Deck
class Deck {
var decks : Integer = 0
init () {
decks = 1
}
init (amountOfDecks : Integer){
decks = amountOfDecks
}
func getAmountOfCards() -> Integer {
return 0
}
}
Test Class
import XCTest
import helloWorldv2
class helloWorldv2Tests: XCTestCase {
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
}
func testDeckConstructor() {
var deck = Deck(amountOfDecks: 1)
var amount : Integer = deck.getAmountOfCards()
let expected : Integer = 52
// ERROR: Cannot convert the expression type 'void' to type 'integer'
XCTAssertEqual(expected, amount)
}
}
I set the two variables to type Integer so don't understand why I cant compare the two values...

the Type you should be using is Int (Integer is a protocol, not a Type, and there is not an implementation in Swift for == that accepts arguments conforming to the Integer protocol)
specifying the Type in the way that you are doing it is unnecessary, thanks to Swift's "type inference" - when you declare and assign a value to a variable, it will automatically give that variable the same Type as the value that is being assigned to it (and literal integers in Swift are typed as Int by default)... so let expected = 52 will automatically give your expected constant the Type Int without you having to declare it as such

Integer is a protocol, you should use Int instead as that is an actual struct.

Related

Swift - Function where parameter must conform to protocol and check is kindOf

I have an OrderBook with expected and completed orders; both of which are array of Ints.
I want to have an 'add' order function which is generic and works for both the expected and completed class;
Both classes follow a protocol.
I wish to make a function call where the object passed in must conform to a protocol; not only that I want to check that the order is kindOf class.
protocol EntryProtocol {
var value: Int { get set }
}
class OrderBook {
var existingOrders: [ExistingOrder] = [ExistingOrder]()
var completedOrders: [CompletedOrder] = [CompletedOrder]()
func add<C1: Any>(order: C1) where C1: EntryProtocol {
print (order is ExistingOrder.Type) // returns false
print (order is CompletedOrder.Type) // returns false
// i want to do a switch here
}
}
class ExistingOrder: EntryProtocol {
var value : Int
init(value: Int) {
self.value = value
}
// .. other methods
}
class CompletedOrder: EntryProtocol {
var value : Int
init(value: Int) {
self.value = value
}
// .. other methods
}
var orderBook: OrderBook = OrderBook()
let howMany = 4
for _ in 1...howMany {
let value = 3 // this is a random number (1-6)
let order = ExistingOrder.init(value: value)
orderBook.add(order: order)
}
print (orderBook.existingOrders)
print (orderBook.completedOrders)
In my swift playground, the add() function always returns false when I check to see whether it is a ExistingOrder or CompletedOrder
How do I make a function where the parameter must conform to a protocol; then check that the object passed in via parameter is of a certain type?
Many thanks
the answer is to use order is ExistingOrder, crediting #rmaddy then check the return if let order = order as? ExistingOrder, crediting #dan

Swift Generic Protocol Function Parameters

This seems like it should work to me. All I am trying to do is make the Rule protocol able to performRule on whatever struct adopts that Rule protocol and then return a boolean. However, with the way my code is currently I cannot access any properties on the performRule(:value) value parameter. I feel like I am missing an important concept or something is buggy. You should be able to copy the code below into a playground to see the issue for yourself.
import Foundation
protocol NumberCalculation {
var number : NSNumber { get set }
}
protocol Rule {
var invalidMessage : String { get set }
func performRule<T>(value: T) -> Bool
}
struct GreaterThanRule : Rule, NumberCalculation {
var invalidMessage: String
var number : NSNumber
init(valueMustBeGreaterThan value: NSNumber, withInvalidMessage message : String = "") {
number = value
invalidMessage = message
}
func performRule<NSNumber>(value: NSNumber) -> Bool {
number.decimalValue // works
value.decimalValue // doesn't work
return true
}
}
Saying <NSNumber> defines a new generic placeholder type in your performRule(value:) method, which, as you've named it NSNumber, will shadow Foundation's NSNumber class – meaning that the value: parameter is of type your generic placeholder, not Foundation's NSNumber.
If you want it so that types conforming to Rule can choose their own type for the parameter of the performRule(value:) method – then you want an associated type, not a generic placeholder.
protocol NumberCalculation {
var number : NSNumber { get set }
}
protocol Rule {
// define associated type that conforming types must satisfy
// by providing a type to replace it with
associatedtype Value
var invalidMessage : String { get set }
func performRule(value: Value) -> Bool
}
struct GreaterThanRule : Rule, NumberCalculation {
var invalidMessage: String
var number : NSNumber
init(valueMustBeGreaterThan value: NSNumber, withInvalidMessage message : String = "") {
number = value
invalidMessage = message
}
// satisfy associated type implicitly by annotating the type of the parameter
// as NSNumber – the compiler will infer that Value == NSNumber.
func performRule(value: NSNumber) -> Bool {
number.decimalValue // works
value.decimalValue // also works!
return true
}
}

Cannot create class object or array of class objects

I'm working on a project thatand am simply trying to create a object instance of a simple custom class:
import Foundation
class Set {
private var gam1: Int!
private var gam2: Int!
init (gam1: Int, gam2: Int) {
self.gam1 = gam1
self.gam2 = gam2
}
//returns set info as array
func getStats () -> [Int] {
let arr = [gam1!, gam2!]
return arr
}
}
The class simply stores a few variables for use later and I want an array of such objects to store several values. However, when I try to create a n instance of the class in a different class I get errors:
import Foundation
class AnotherClass {
var mySet = Set(gam1: 6, gam2: 5) //error 1
//array of set objects
var setArray = [Set]() // error 2
//returns array of set objects
func getSets () -> [Set] { //error 3
return setArray
}
}
The errors state:
Cannot find an initializer for type 'Set' that accepts an argument list of type '(gam1: Int, gam2: Int)'
Cannot invoke initializer for type '[Set]' with no arguments
and
Reference to generic type 'Set' requires arguments in <...>
Any ideas of what the issue is here? could the 'Set' name of the class be conflicting with a reserved keyword?
Many thanks,
Kw
The issue that you are having is due to the naming conflict between Set in the Swift standard library and the one you defined.
This is never a good idea. Instead, give it a more descriptive name (and one that doesn't conflict with anything else). For instance, call it gamHolder and initialize it gamHolder(gam1: <an int>, gam2: <another int>).
Also, if you have defined variables inside the init function they do not need to be forced unwrapped optionals.
For example:
class myClass {
var myInt: Int
init(anInt: Int) {
myInt = anInt
}
}
You defined 2 parameters in your init method (since gam1 and gam2 are not optional). So, you have 2 solutions:
1 - You add parameters into your init call (like this):
var mySet = Set(gam1: 1, gam2: 2)
2 - You change gam1 and gam2 to optionals and you add a zero parameters init:
class Set {
private var gam1: Int?
private var gam2: Int?
init() {
}
init(gam1: Int, gam2: Int) {
self.gam1 = gam1
self.gam2 = gam2
}
// returns set info as array
func getStats() -> [Int] {
let arr = [gam1!, gam2!]
return arr
}
}
So, you will be able to call it like this: var mySet = Set()
Also: be careful: Set is a class used in the Swift standard library. It's never a good idea to use same class names than Swift Standard Library. A name like TenisSet would be better.
Edit:
Here is a final working example with a renamed class:
class TenisSet {
private var gam1: Int?
private var gam2: Int?
init() {
}
init(gam1: Int, gam2: Int) {
self.gam1 = gam1
self.gam2 = gam2
}
// returns set info as array
func getStats() -> [Int] {
let arr = [gam1!, gam2!]
return arr
}
}
class AnotherClass {
var mySet = TenisSet()
// array of set objects
var setArray = [TenisSet]()
// returns array of set objects
func getSets() -> [TenisSet] {
return setArray
}
}

Swift subscript setter that accepts a different type than the getter's return value

I have a custom collection that can receive values of any type and converts them to strings. For example:
collection["key"] = 10
let value = collection["key"] // value is a String
Is there a way to do this? I tried implementing two subscript methods but Swift doesn't support write-only subscripts.
subscript(key: String) -> String {
get { ... }
}
// Doesn't compile
subscript(key: String) -> AnyObject {
set { ... }
}
You can use two different subscript implementations and disable the getter for one of them:
subscript(key: String) -> String {
get { return "howdy" } // put real implementation here
}
subscript(key: String) -> AnyObject {
get { fatalError("Oooops") }
set { }
}
However, this still leaves open the question of how to distinguish between these two subscript calls in context. It would be better to give them different signatures through their external parameter names:
subscript(key: String) -> String {
get { return "howdy" } // put real implementation here
}
subscript(# any: String) -> AnyObject {
get { fatalError("Oooops") }
set { }
}
And here's how to use it:
let m = MyClass()
m[any:"thing"] = 1
println(m["thing"]) // "1", presumably
Define subscript to return AnyObject (or Any as needed) and at the point you use the getter cast the result to String. You may already need to deal with subscript returning an optional so the coercion is just all part of extracting your desired value.
if let value = collection["key"] as String { ... }
else {...}
You could also define your own type and make it conform to the IntegerLiteralConvertible and the StringLiteralConvertible protocols.
Technically you could also write an extension for String to make it conform to IntegerLiteralConvertible but that might get confusing, since it will be available in your entire project.
I was facing a similar problem here and I solved it using a generic type for my variable and returning the type I want on its getter. You can try doing something like this:
class StorageClass {
private var _value: String?
public var value: Any? {
set {
if let num = newValue as? Int {
self._value = String(format: "%d",num)
}
}
get {
return self._value
}
}
}
By doing this, it is possible to do something like:
var storage = StorageClass()
storage.value = 10 /* setting value as an Integer */
let aString = storage.value as! String /* receiving a String value back */

Automatic Type Conversion with extension: What is happening here?

I'm going through the first chapter of The Swift Programming Language book and I'm at the part where it's describing the extension keyword.
I had a go at the "Experiment":
“Write an extension for the Double type that adds an absoluteValue property.”
I got it working like this:
extension Double {
var absoluteValue: Double {
if(self < 0) {
return self * -1
}
return self
}
}
(-10.5).absoluteValue // 10.5
But it also seems to work for integers:
(-4).absoluteValue // 4.0
What is happening here? Is the compiler changing the type from Int to Double because it sees that there is a absoluteValue extension on Double but not Int?
This appears to be the case because if I add another extension of the same name on Int like so:
extension Int {
var absoluteValue: Int {
return 42
}
}
That overrides the extension on Double. And (-4).absoluteValue returns 42
Is there a way to add an extension that only works on Doubles but not Ints?
Edit: Looks like it's doing a conversion at compile-time and since I didn't define a type for my literal it converted it. The following produces an error
var i:Int = -4;
i.absoluteValue
"Playground execution failed: error: :12:1: error: 'Int' does not have a member named 'absoluteValue'
i.absoluteValue
^ ~~~~~~~~~~~~~"
Edit 2: It appears to only apply to literals; the following also produces an error:
var i = -4;
i.absoluteValue
Yes, the extension you wrote is actually only for Doubles, not for Ints. Take a look at this example:
extension Double {
var absoluteValue: Double {
if (self < 0) {
return self * -1
}
return self
}
}
var double: Int = 10
double.absoluteValue // Int does not have a member named absoluteValue
But, in your code the compiler is implicitly converting your Int to a Double.
In case anyone would like an answer that conforms to the example protocol:
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
extension Double: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
var absoluteValue: Double {
return fabs(self)
}
mutating func adjust() {
self = round(self)
}
}
var double: Double = -12.34
double.simpleDescription
double.absoluteValue