I have this error: XCTAssertTrue failed: throwing "[<XCElementSnapshot 0x7fea978b1a10> valueForUndefinedKey:]: this class is not key value coding-compliant for the key staticTexts."
Here is the code:
let predicate = NSPredicate(format: "(self.staticTexts[%#].exists == true) AND (self.staticTexts[%#].exists == true)", message, nameString)
XCTAssert(app.collectionViews.childrenMatchingType(.Cell).elementMatchingPredicate(predicate).exists)
Error is thrown on the second line.
I have looked at other answers on SO with the same error, and it's mostly caused by having a variable of a different class, however I don't see the possibility for this error here. Also, I checked to see that the predicate is formatted correctly.
How can I get rid of this error?
Make sure that your staticTexts property is dynamic or otherwise available to objc (by marking it #objc for instance). Swift will not generate KVC-compliant accessors unless it thinks it needs to.
Alternately, use something other than NSPredicate here. Making a property dynamic when it's not needed has a performance cost, which is why Swift doesn't do it automatically. So marking it dynamic just so a unit test can access it may be a poor tradeoff.
Apparently, the error goes away when I apply the predicate to static texts vs. the cells and then try to access the static texts inside the predicate. So for example,
let predicate = NSPredicate("self.title like %#", message)
app.descendantsMatchingType(.StaticText).elementMatchingPredicate(predicate).exists
would get rid of the error.
Related
I have a Core Data Entity with a Date attribute (e.g. current_date. Technically, in the Class which Core Data generates for that Entity, this attribute is optional (#NSManaged public var current_date: Date?). However, in my app this Date is always provided, which is why in the View I am displaying this fetched Entity via a list, I am force unwrapping it with current_date!. This all works fine so far. However, I have added an onDelete to that list to be able to delete single elements and now I am getting a bug Fatal error: Unexpectedly found nil while unwrapping an Optional value. Seems to be some problem related to the deletion process - as said, that value in the data is actually never empty. Does anyone know what is the problem here?
Your code may not set current_date to nil. But if you
Delete a managed object object
Save changes
Try to use that same managed object reference
...then current_date is nil, because that's something Core Data does. This error message suggests that you are attempting to use the object after deleting it and saving changes. Ideally you should fix that, because you won't have valid data anyway. You should also avoid force-unwrapping anyway, because using it is a way of specifically requesting app crashes any time you use it.
Full Error Message:
error: warning: <EXPR>:12:9: warning: initialization of variable '$__lldb_error_result' was never used; consider replacing with assignment to '_' or removing it
var $__lldb_error_result = __lldb_tmp_error
~~~~^~~~~~~~~~~~~~~~~~~~
_
error: <EXPR>:19:5: error: use of unresolved identifier '$__lldb_injected_self'
$__lldb_injected_self.$__lldb_wrapped_expr_7(
^~~~~~~~~~~~~~~~~~~~~
This error appears in Console when I interrogate the value of a Dictionary<String, String> property within a generic UITableViewController (TVC).
More Detail...
I have a generic TVC (noted above) that is more or less based on the framework outlined in the book "Core Data" by Florian Kugler and Daniel Eggert and takes, amongst other things, a generic value of T.
class TVCDataSource_List<T: Managed, etc...>
This generic TVC includes a dictionary that is designed to hold a list of longer 'alternative' names for the TVC's section headers.
var dictionarySectionData: [String: String] = [:]
I've elected to program the TVC this way because it seems more efficient to hold a reference to a name as a short two character String in the data model attribute (section identifier) than a long name as a String.
I've tried populating this dictionary at many different places in code, most of which work but all with the same outcome, specifically:
I step through the code using the debugger and, as expected, the dictionary is populated via a single fetch request to the persistent store;
Immediately following, a call to print(dictionarySectionData.description) to the console, prints out a properly populated dictionary, as expected;
Interrogating LLDB with p dictionarySectionData (or po) immediately before and after this print to console, produces the Full Error Message detailed at the start of this question;
At the same time, the Assistant Editor Variable Viewer shows the dictionary to be empty, which surprisingly conflicts with the print;
I continue to step through the code to construct the TVC, as the dictionary no longer has its key value pairs, I cannot recall the value for my section header and as expected, the console reports "Fatal error: Unexpectedly found nil while unwrapping an Optional value".
I've done a bit of simple research:
This Blog by Scott Berrevoets titled "Re-binding self: the debugger's break(ing) point".
This Swift Bug Report by Keith Smiley titled "LLDB: warning: initialization of variable '$__lldb_error_result'".
This Swift Bug Report by Zev Eisenberg titled "error: use of undeclared type '$__lldb_context' in NSAttributedString extension".
It seems that I may have either:
stumbled across a bug in the compiler; or
attempted to set the value for the dictionary within the generic TVC such that the compiler interprets an attempt to re-bind to self??
Frankly neither of which I understand and from my shallow knowledge of the compiler and Swift, would take me months, possibly years of learning and experience. Which I'm happy to slowly accumulate over time.
I do have a satisfactory solution... instead of building a dictionary of the longer 'alternative' names for the TVC's section headers at the beginning of the TVC lifecycle, I run a fetch request EACH TIME the code resolves the name for the current TVC section header. This works perfectly and does not block the UI (yet).
However, it really annoys me that I cannot run one fetch at the start of the construction of my generic TVC to prepare a concise dictionary of longer 'alternative' names for the TVC's section headers and instead have to run a fetch for each section that the user decides to scroll through. To perform one fetch and hold a dictionary of 12-15 key value pairs in memory seems far more efficient that running many fetches.
Has any one experienced this problem?
If so, are you able to offer any advice?
UPDATE
The problem seems to be with my use - or perhaps more correctly, my misuse - of the explicitly unwrapped Optional.
Here is the code I use to populate the dictionary...
func createDictionaryOfSectionHeaderText() {
let request = Types.preparedFetchRequest
// noting .preparedFetchRequest is a static var, available through generics
let key = "typeParent.typeParentName"
let name = "Taxonomy"
let predicate = NSPredicate(format: "%K == %#", argumentArray: [key, name])
request.predicate = predicate
var results: [Types] = []
do {
results = try <<My NSManagedObjectContext>>.fetch(request)
}
catch {
let fetchError = error
print(fetchError)
}
for type in results {
let formatSortOrder = String(format: "%02d", type.sortOrder)
dictionarySectionData[formatSortOrder] = type.typeName
}
}
There were two elements of code that caused the error message...
A. As above in the func createDictionaryOfSectionHeaderText()
let stringSortOrder = String(type.sortOrder)
let formatSortOrder = String(format: "%02d", stringSortOrder)
...which was feeding a string into the format "%02d", uncertain of the effect... TBA.
(Now changed from those two lines to the single let formatSortOrder = String(format: "%02d", type.sortOrder) - which of course works.)
B. Within the UITableViewDelegate method func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
stringHeaderText = dictionarySectionData[stringSectionName]!
// "Fatal error: Unexpectedly found nil while unwrapping an Optional value"
...which, following more thought on the matter, is exactly as expected when explicitly unwrapping the Optional, when that Optional is nil!!
So, when I change the setter to stringHeaderText by removing the instruction to explicitly unwrap, and instead offer a default value when nil, my programming problem disappears.
stringHeaderText = dictionarySectionData[stringSectionName] ?? "ERROR"
I may even provide an answer if/when I understand this better.
These issues should be solved in XCode 12.
There were indeed a lot of bugs in XCode 10 & 11, especially when it came to Generics
Could I do the following Predicate in Swift 4 to the following string 'name':
let predicate = NSPredicate(format: "name.dropLast() = %#", searchText)
Basically, could I add the extension 'dropLast()' inside the predicate? If not, what is an alternative?
"Could I do the following"
No. NSPredicate comes with its own completely specified language. Predicate strings must conform to that language; otherwise, they cannot be parsed.
"If not, what is an alternative?"
An alternative to do what? You are not telling us anything about the goal here. For example, instead of calling NSPredicate(format:), you might be able to call NSPredicate(block:) which lets you write the predicate test in Swift code. But you can't do that in all situations. And you have not said what your situation is.
Ok, this is an odd error and it's taken me many hours to track down the exact location (although the cause remains unknown). The error only occurs on 64-bit devices. In other words, it works fine on my iPhone 5, but crashes on my iPhone 6 Plus.
Essentially, I have a private variable in a class:
class Manager {
private lastUpdated:NSDate?
}
Initially, it's not set. This class retrieves messages from a server and then sets the lastUpdated variable to NSDate().
private func checkForNewMessages(complete:Result<[Message]> -> ()) {
self.server.userMessages(u, fromDate: self.lastUpdated) { result in
result.onSuccess { messages in
self.insertMessages(messages)
self.lastUpdated = NSDate()
complete(Result(messages))
}
result.onError { err in complete(Result(err)) }
}
}
I've deleted extraneous code, but nothing inside the code path that's crashing (just stuff around it)
Note: I'm using a Result enum. For more info about this kind of enum, this article is pretty good at explaining it: Error Handling in Swift.
The first time the manager retrieves messages, everything works fine and the lastUpdated value is set correctly. If I then try to retrieve messages again, the app crashes when it tries to set self.lastUpdated = NSDate().
I can get it to work if I delay the assignment by using GCD, dispatch_after. But I don't want to do that.
Does anyone have any idea why this is occurring?
Update
This is only occurring when I assign a NSDate. It's not occurring if I try to set another object type (Int, Bool, String) etc.
The crash occurs if I change the stored variable to NSTimeInterval and attempt to set it from NSDate.timeIntervalSinceReferenceDate()
The crash occurs if I store an array of updates and attempt to simply append a new date to that array.
The crash occurs if I attempt to set lastUpdated before the server is called rather than in the callback.
The crash occurs if I wrap NSDate in another class.
The crash occurs is I set lastUpdated in a background thread
The crash doesn't occur if I println(NSDate()) (removing the assignment)
Ok, it took me numerous hours but I finally figured it out.
This will be a little difficult to explain and I'm not certain I fully understand what is going on. The error actually occurred in the Server class that was making the request. It took the lastUpdated date, converted it to a string and then sent that up to the server. The first run, there was no date, so no issue. In the second run, it took the date and used a date formatter to convert it to a string. However, I thought that stringFromDate returned an Optional. It does not. Normally, the compiler would warn me if I attempted to unwrap something that wasn't an option. However, I use Swift's functional aspects to use Infix operators to unwrap and conditionally pass the unwrapped value to another function, which can then pass it's value on. It allows me to chain unwraps together so I can avoid "nested if-let hell".
Very simple but powerful:
infix operator >>? { associativity left}
func >>? <U,T>(opt: T?, f: (T -> U?)) -> U? {
if let x = opt {
return f(x)
}
return nil
}
Note that the function must return an Optional. For some reason, the Swift compiler missed the fact that stringFromDate did not return an optional and so it didn't warn me. I'm not sure why that is, it's certainly warned me before:
if let update = fromDate >>? self.dateFormatter.stringFromDate {
This fails, but not immediately
Unwrapping a non-optional value doesn't immediately result in a crash, error or anything apparently. I successfully used the date string to send and receive data from the server. I'm not certain why unwrapping a non-optional string would later result in a crash and that only when I attempted to set the instance variable that of the backing NSDate object the string was generated from. I think somehow, unwrapping a non-optional screwed up some pointers, but not necessarily the pointer to the unwrapped object. I think the crash itself has to do with Swift attempting to release or dealloc the original instance variable (lastUpdated) when a new one is set, but found the memory address messed up. I think only a Apple Swift engineer could tell me what actually happened, but perhaps there are clues here to how Swift works internally.
I still muck up with bool's in my core data config data. The NSManagedObject for say a core data "config" class one can quickly type in the following and you don't get an error:
if (self.myCoreDataConfigObject.isOn) { ...
but it doesn't give the correct result as what is required is the conversion from NSNumber to a bool:
if ([self.myCoreDataConfigObject.isOn boolValue]) { ...
Question - Any tips/tricks on how to avoid this? It would be great if XCode would show a warning in this case...
You could rename the field to something like isOnValue and then provide an accessor on your NSManagedObject subclass called isOn that runs the -boolValue conversion for you.
Don't forget, however, that "optional" values may be nil and you may care about this as something distinct than NO.
Under 'Targets' -> 'Build Settings' in the project settings there is a group of options for compiler warnings. I'm not 100% on this but turning on 'Pedantic Warnings' might do it, if not there are dozens of different settings for each different type of compiler. Try toggling some of them on and see if it generates a warning where you want one.
However, if (self.myCoreDataConfigObject.isOn) is a perfectly valid expression, so no errors (warnings maybe, but not errors) will ever get generated because using if(SOME_OBJECT) just checks if it is equivalent to nil or 0. (Unless, of course, you specify for the compiler to treat warnings as errors)
One thing you could do is use the C99 bool type in if(). For instance, the following:
NSNumber *num = [NSNumber numberWithInt:0];
if (num == true) {
NSLog(#"num is true");
}
makes XCode issue a warning. You'd have to adhere to a convention of always using "== true" in conditions though.