XCTKVOExpectation times out even though element exists - swift

I am using swift, xcode 8.3.3, and XCTest. I am trying to wait for an element to exist on the screen using XCTKVOExpectation. It is always returning the result of timedout (2) even though the element exists.
Here is my code:
func waitForElementToAppear(element: XCUIElement) -> Bool {
let expectation = XCTKVOExpectation(keyPath: "exists", object: element,
expectedValue: true)
let result = XCTWaiter().wait(for: [expectation], timeout: 10)
print(element.exists)
print(result.rawValue)
return result == .completed
}
When I print element.exists, it prints true. However the result.rawValue is 2 (.timedout) Increasing the timeout value did not resolve this either.
I am able to successfully use XCTNSPredicateExpectation:
let myPredicate = NSPredicate(format: "exists == true")
let myExpectation = XCTNSPredicateExpectation(predicate: myPredicate,
object: element)
let result = XCTWaiter().wait(for: [myExpectation], timeout: 10)
return result == .completed
Wondering why XCTKVOExpectation doesn't work though?

Not really an answer, but I've been seeing this in Xcode 9.3.1 as well. The exact same predicate combination works well elsewhere in many places but one in particular almost always times out.
I've worked around it for now by checking whether the object exists before issuing the wait, and then also "result == XCTWaiterResultCompleted || object.exists" (Objective C).
I'd love to know if this is a bug or am I doing something wrong?

I was having the same problem with my own code. I was able to get the KVO change to trigger using didChangeValue(forKey: "keyValuePath").
Since XCTKVOExpectation accepts a String for the key path instead of a KeyPath, paired with needing to call didChangeValue(forKey: "keyValuePath"), it seems XCTKVOExpectation is not meant to work with Swift and is more of a legacy objc object. I just don't think this is going to work in Swift

Related

Confused about Optional vs Default value -1

We are working in a Swift project. A function was there like,
fun getSleepAmmount() -> Int {
// calculate sleep time
// return value when valid
// else
return -1
}
My team member prefers the above function where caller needs to check with -1 which is not I am comfortable with. My suggestion is to redesign with nil return (although callers still need to check nullability) like,
fun getSleepAmmount() -> Int? {
// calculate sleep time
// return value when valid
// else
return nil
}
But my colleagues do not want to redesign. Which version of the functions is cleaner and why?
Obviously, nil is much cleaner. Because of -1 means nothing. This is just a magic word. It is difficult to support, refactor and handle this case.
Returning nil is a better solution instead of using any garbage or default value.
Returning any default value may in future clash with the actual result.
Also, there might be other developers dealing with the same code. So, using nil will have a better explanation than using -1.
Second is the better as youll do
if let v = getSleepAmmount() {}
But with First
let v = getSleepAmmount()
if v > 0 {}
Returning nil means this isn't a valid return while -1 may mean another thing that will be miss-understood by a new developer that checks the code
If there is code in the caller that should only run if there is a valid sleep amount, then optionals is the better and clearer way to go. This is exactly what guard let and if let are designed for:
guard let sleepAmount = getSleepAmount() { else return }
// do something with sleepAmount
An even better way would be to throw an error inside the function:
func getSleepAmmount() throws -> Int {
// calculate sleep time
// return when valid
// else
throw InvalidSleepAmount
}
Then
do {
let sleepAmount = try getSleepAmount()
// do something with it
} catch InvalidSleepAmount {
// error processing
}
(If you want, your function could throw different errors so the caller gets to know why the sleep amount is invalid, SleepTooShort, SleepTooLong, NoSleep etc)

Impossible Index of of Range crash

In application I am working on, I have to take one element of the array by specifying element's index in the array, like this array[index].
It's a simple UICollectionView which is populated with items I am getting from the array.
In order to guard against Index out of range exception, I am doing this:
guard index < array.count else { return }
return array[index]
Even though I have this guard, I got an Index out of range exception on the array[index] line (but not always).
I don't know how this can happen.
I have even added another check:
extension Collection where Indices.Iterator.Element == Index {
subscript (optional index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
So I am doing this (this is the actual code snippet from the application):
...
guard let section = chatSections[optional: indexPath.section] else {
return nil
}
guard indexPath.item < section.itemViewModels.count else {
return nil
}
return section.itemViewModels[optional: indexPath.item]
It doesn't always happen, but sometimes I get the Index out of Range exception there.
I was debugging most of the day, trying to figure out conditions when crash happens so I might be able to figure out why, but it seems to happen randomly.
Does anyone have any idea how is this possible? Did anyone encounter this kind of issue?
Are you, by any chance, doing any of your updating from a background thread/queue? If so, make sure your UI interactions are done on the main thread/queue and that you aren't changing the array contents behind your UI's back.
That is, if you're changing the array contents in one queue and trying to update your UI while this is happening, your guard statement could be passing just before the array is modified elsewhere, then by the time the rest of your UI-interacting code executes, the index my no longer be valid.
Without a more complete picture, it's hard to say what's going on, but all these bounds checks you're adding in order to guard against mysteriously-changing array indexes are a big clue to multithreading shenanigans.
I believe you have multiple access to the your data source array (multiple thread try to access add/remove from the array).
to overcome this you should use something to enforce synchronization while accessing your array, there is multiple approce using semaphore or DispatchGroup.
I would recommend to use semaphore since the array is considered as shared resource, example:
private let semaphore = DispatchSemaphore(value: 1)
private var _array:[Item] = []
var array:[Item] {
get {
semaphore.wait()
let result = self._allMessages
defer {
semaphore.signal()
}
return result
}
set {
semaphore.wait()
self. _array = newValue
semaphore.signal()
}
}
and use the array variable to access the array data source not the private _array.

.prepare vs. .select

I have a working connection to a database in an iOS10 app, using SQLite.swift.
I want to select details for a specific university where I have an IHE_ID passed in from another view controller.
I would like to just select the row for that specific university, but I can't get a query to work. I can however loop through all the data with a prepare and then choose the one I need from that, which of course is more resource intensive than I need since I already have the specific IHE_ID passed in as anIHE Int from the sending view controller.
connection is working so omitting that code...
do {
let db = try Connection(destinationDBPath, readonly: true)
let IHEs = Table("IHE")
let IHE_ID = Expression<Int>("IHE_ID")
let IHE_Name = Expression<String>("IHE_Name")
let IHE_COE_Url = Expression<String>("IHE_COE_URL")
let IHE_Sector = Expression<Int>("IHE_Sector")
let IHE_COE_Name = Expression<String>("IHE_COE_Name")
for ihe in try db.prepare(IHEs){
if (ihe[IHE_ID] == anIHE){
// build this string, otherwise ignore rest of dataset (doing this because query isn't working)
buildIHE = "Name: \(ihe[IHE_Name])\n"
buildIHE.append("URL: \(ihe[IHE_COE_Url])\n")
// buildIHE.append("Sector: \(ihe[IHE_Sector])\n")
if (ihe[IHE_Sector] == 0) {
buildIHE.append("Sector: Public\n")
} else {
buildIHE.append("Sector: Private\n")
}
buildIHE.append("College of Education Name: \(ihe[IHE_COE_Name])\n")
}
}
print ("Got through building IHE portion of view")
What I'd like to do is use this instead of the for loop.
if let query = IHEs.select(IHE_ID,IHE_Name,IHE_COE_Url,IHE_Sector,IHE_COE_Name).filter(IHE_ID == anIHE){
print("Query successful for \(anIHE) with name \(query[IHE_Name])")
// more actions to build the string I need would then occur
} else {
print("Query has failed or returned nil")
}
Finally, I'll use the selected elements if I can get the query to work.
I think I probably just have something wrong with my syntax on the query, but any help is appreciated.
The line with the "if let query" has this error in Xcode:
Initializer for conditional binding must have Optional type, not 'Table'.
This leads me to think it's something with my use of the .select statement and just new to using SQLite.swift and swift in general.
Last thing is that anIHE comes into this function as an Int, and IHE_ID is Expression as shown in this code. I'm thinking this may be part of the problem.
The Initializer for conditional binding must have Optional type error means that the expression on the right of the if let v = expr statement is not an Optional: there is no point using if let, and the Swift compiler says that you should just write let v = expr.
And indeed, IHEs.select(...).filter(...) returns a non-optional value of type Table.
It is not the database row you expect, because the query has been defined, but not executed yet. After all, you weren't using db: where would the rows be loaded from?
The solution is to bring back the database connection, and load a single row. This is done with the pluck method.
if let ihe = try db.pluck(IHEs.select(...).filter(...)) {
print("Name \(ihe[IHE_Name])")
}

Best way to pull non-optional values from optional containers

This is a best practices question, and I have a feeling I'm missing an obvious solution...
I was stumped why this bit of code made it past the if when wantsGroundAlways was false and I1 was undefined:
let i = c.data["I1"]?.integerValue
if !wantsGroundAlways && i == 0 { return }
The problem was that data["I1"] is, by definition, optional, so Swift inferred i as int?, and nil != 0. Subtle, but obvious in retrospect.
But what is the best way to deal with this common case? One solution is:
let i = (c.data["I1"] ?? 0).integerValue
But personally I think that looks terrible and hides the intent. Something along the lines of:
guard let i = c.data["I1"]?.integerValue else { i = 0 }
would make it obvious what you're trying to do, but it doesn't work because the i cannot be accessed in the else clause and { let i = 0 } is not the same i (try it, you'll see what I mean).
Now there is this:
guard let arcradius = c.data["F1"]?.doubleValue else { return }
which seems really close to what I want to do, but I'm not sure this is really what I think it means - will the return really fire if F1 is not in the dict? And what is the difference between that version and this:
guard case let arcradius = c.data["F1"]?.doubleValue else { return }
Which tells me it's always true?
I think I am missing something key here... so what do you all do in these situations?
I think this is the most straightforward and expressive way of solving your first question:
let i = c.data["I1"]?.integerValue ?? 0
It doesn't require brackets, and shows intent.
?? is the nil coalescence operator. It's a binary operator. If the left operand is not nil, it's returned, otherwise the right operand is returned.
c.data["I1"] can be nil (because there might be no value for the "I1" key). In such a case, c.data["I1"]?.integerValue will be nil. The ?? will then return the 0. If all goes well, and the left side isn't nil, it'll be returned, ignoring the 0.
If you want only to check if a key exist or not then a "type cast" to Int or Double is irrelevant.
Why not simply
guard c.data["I1"] == nil && !wantsGroundAlways else { return }
It passes the test if I1 is not in the dictionary and wantsGroundAlways is false.
if let or guard let is not needed either because according the condition the value for key is never used.

Creating a calculator app

So I'm very new to Swift and have been following this tutorial to make this app https://www.youtube.com/watch?v=NJHsdjH2HdY
This was the first problem: currentNumber = currentNumber * 10 + Float(sender.titleLabel!.text!.toInt()!)
In the comments section the guy said to change that line to:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel!.text!)!)
I did this and I get the error: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
As said in the comments, you should always avoid using the 'crash operator' (!) – and learn to safely deal with optional values instead.
Your program is crashing because either the titleLabel, text or Int(...) are nil – and you are then trying to force unwrap them. This could mean that your button doesn't have a titleLabel or that titleLabel's text isn't convertible to an Int.
The solution is to safely deal with the optionals as you encounter them. There are many ways of doing this, but I usually like to use one or multiple guard statements. This allows you to safely unwrap an optional if it has a value, otherwise it will execute the code within the brackets. This is useful for when future code depends on the optional not being nil. For example:
guard let buttonText = sender.titleLabel?.text else {
print("Sender didn't have either a titleLabel or text!")
return
}
guard let textAsInt = Int(buttonText) else {
print("Text wasn't convertible to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Now you get helpful print messages instead of crashes! Therefore you know exactly what went wrong, and what you can do to fix it (if it needs fixing).
You could also consolidate both of these checks into a single guard if you want more concise code, but less precise errors:
guard let buttonText = sender.titleLabel?.text, textAsInt = Int(buttonText) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(textAsInt)
Or you can use flatMap if you like closures:
guard let i = sender.titleLabel?.text.flatMap({Int($0)}) else {
print("Something went wrong when converting the button title to an Int!")
return
}
currentNumber = currentNumber*10 + Float(i)
The flatMap option can look a bit weird at first, but all it's doing is attempting to convert the button's titleLabel's text to an Int. If it fails it will return nil (which the guard will pick up), else it will return the numerical value of the text.
As #vacawama said in the comments, you could also use the nil coalescing operator in order to use 0 in the event that the titleLabel, text or Int(...) are nil:
currentNumber = currentNumber * 10 + Float(Int(sender.titleLabel?.text ?? "") ?? 0)
However bear in mind that this could lead to unexpected behaviour. I suspect that your program is crashing because your logic is getting run for non-numerical buttons, for example the "+" button. If this is the case, you'll be multiplying your number by 10 every time you press a non-numerical button. You'd have to first ensure that your logic only gets called on numerical buttons.
Although without seeing your full code, it's hard to say for sure.
For more info about how to safely deal with optionals, see this extensive Q&A on the subject.