Binary operator '>' cannot be applied to two 'String?!' operands - swift

So I have a project, that builds fine. but hen I want to Archive it it throws this error on this line of code:
let pred = NSPredicate(format: "%K in %#", "categoryID", selectedCategoryIDs!)
let selectedAlmanacEntries = almanacEntries.filter { pred.evaluate(with: $0) }.sorted(by: { ($0 as AnyObject).title > ($1 as AnyObject).title })
this hasn't been an issue before (Earlier releases).
Have tried restarting Xcode and cleaning the project before.
Any hints appreciated.
(Xcode 8, Swift 3)

Swift 5
you can simply unwrap optional by this way
let selectedAlmanacEntries = selectedAlmanacEntries.sorted {
var isSorted = false
if let first = $0.title, let second = $1.title {
isSorted = first < second
}
return isSorted
}

?! is a really great construction, isn't it? Perfectly sums up the reaction whenever you see it in an error message.
Anyway, in Swift, you're probably better off casting to the appropriate type that these objects should belong to instead of AnyObject. If you must duck type, then you should be aware that every time Swift calls a method on AnyObject, it gains a level of optionality due to the fact that Swift can't verify that the object actually responds to the message. So you will have to deal with the optionals.
The other problem is that since there exist multiple classes in the frameworks that have properties named title, Swift has no way of knowing which one to use. And some of them have different signatures; for example, NSButton has a title property that is typed as String, NSStatusItem has a title property that is typed as String?, and NSWindowTab has a title property that is typed as String!. Which one the compiler picks is a bit of a luck of the draw, which is why random chance can make it behave differently from compile to compile. So you need to help the compiler out by telling it what type to expect.
So, something like this can work:
let selectedAlmanacEntries = almanacEntries.filter { pred.evaluate(with: $0) }.sorted(by: {
guard let first: String = ($0 as AnyObject).title else { return false }
guard let second: String = ($1 as AnyObject).title else { return true }
return first > second
})
Or, if your heart is set on a long one-liner as in the original:
let selectedAlmanacEntries = almanacEntries.filter { pred.evaluate(with: $0) }.sorted(by: { (($0 as AnyObject).title as String?) ?? "" > (($1 as AnyObject).title as String?) ?? "" })
I'd really recommend casting to the actual type instead, though.

Related

Optional Any? equals to nil

I have:
let value: Any? = myObject[keyPath: myParamKeyPath]
where myParamKeyPath refers to a String?.
Then, when myParam is supposed to be nil, I have:
value == nil returns false
(value as? String?) == nil returns true
Is it possible to check if value equals nil without having to cast it to a String? in the first place? Something like comparing it to NSNull maybe?
Also, I can't change the value type to String? directly as it is also used for other type in my code.
EDIT:
(value as? String?) == nil returns true is irrelevant indeed.
But I can still go print my value pointed by the keypath and it will actually be nil. So I still don't get why value == nil returns false when Im expecting it to be true...
EDIT2 with more code:
let setting = appSettings.settings[indexPath.row]
let value = appSettings[keyPath: setting.keyPath]
let fontAwesome: FontAwesome
switch setting.keyPath {
case \PrinterSettings.printableImageIDs:
fontAwesome = blablabla
case \WeatherSettings.lockscreenImageIDs:
fontAwesome = blablabla
default:
if let value = value as? FakeButtonPlacementSubSettings {
fontAwesome = blablabla
} else {
fontAwesome = value != nil ? .checkSquareO : .squareO
}
}
I am expecting to get fontAwesomeIcon = .squareO but I am getting checkSquareO when the value pointed by myObject[keyPath: myParamKeyPath] is String? (it does the same for another value which is a Bool? later on).
I must be missing something somewhere...
EDIT 3 screenshot:
EDIT 4 more clarification of what I'm trying to do here:
First, thank you again for your help if you get there.
Here are 2 photos about my project current design:
I am using with KVO in my project. I was previously using the objective-c string #keyPath for this. It was working great, but almost all my model had to be converted in #objc. So my current goal here is to remove it and switch to the new Swift 4 keypath system.
To resume: I have a user class containing lot of settings (more than on the screenshot) which can be of several type (and some custom types also).
On the other hand, I have created around 10 "setting editor view controllers": one by type of settings. I would like to use the same setting editor VC to edit each one of the same type of settings.
For example, if the setting to edit is a boolean, I would like to use the "boolean editor VC" and get my selected setting edited by the user's new choice. This is why I am using KVO system. I'm not willing to keep it if you guys have a better solution for this. It would then also get rid of my Any? == nil issue.
How about the following?
struct S {
let x: String?
}
let v1: Any? = S(x: "abc")[keyPath: \S.x]
print(v1 is String? && v1 == nil) // false
let v2: Any? = S(x: nil)[keyPath: \S.x]
print(v2 is String? && v2 == nil) // true
The issue is that your code is assuming the value is String?, whereas it is really String??. I believe this stems from the use of AnyKeyPath:
struct Foo {
let bar: String?
}
let foo = Foo(bar: "baz")
let keyPath: AnyKeyPath = \Foo.bar
print(foo[keyPath: keyPath]) // Optional(Optional("baz"))
let keyPath2 = \Foo.bar
print(foo[keyPath: keyPath2]) // Optional("baz")
I wonder if there is some way to adapt your code to use KeyPath<T, String?> pattern, so that, as you say, your myParamKeyPath really does explicitly refer to a String?, .e.g.:
let foo = Foo(bar: "baz")
let keyPath = \Foo.bar
func keyPathExperiment(object: Any, keyPath: AnyKeyPath) {
print(object[keyPath: keyPath])
}
func keyPathExperiment2<T>(object: T, keyPath: KeyPath<T, String?>) {
print(object[keyPath: keyPath])
}
keyPathExperiment(object: foo, keyPath: keyPath) // Sigh: Optional(Optional("baz"))
keyPathExperiment2(object: foo, keyPath: keyPath) // Good: Optional("baz")
There's not enough in your code snippet for us to see how precisely this might be adapted to your situation.
As Rob Napier said, I wonder if there might be Swiftier approaches to the problem. E.g. when I see switch statements like this, I might tend to gravitate towards enum pattern.

Swift 2.x to swift 3, XCode complaining error : Type of expression is ambiguous without more context

Here is my swift 2.X code that does'nt work any more on swift 3 :
var dictThemesNamesStyles=[String:[Int:Int]]()
self.styles=dictThemesNamesStyles
let keysArray:Array=Array(self.styles.keys)
let sortedKeysArray = keysArray.sorted(by:
{
(str1: NSObject, str2: NSObject) -> Bool in
return Int((str1 as! String))<Int((str2 as! String))
})
self.stylesLevel1Keys=sortedKeysArray
self.styleThemesPickerView.reloadAllComponents()
On line :
"return Int((str1 as! String)) < Int((str2 as! String))"
it complains with the error : "Type of expression is ambiguous without more context"
What do I have to change in this code to make it work ?
Thanks a lot.
Let's go through this line by line:
var dictThemesNamesStyles=[String:[Int:Int]]()
self.styles=dictThemesNamesStyles
Okay, we've got a dictionary of strings to dictionaries of integers.
let keysArray:Array=Array(self.styles.keys)
There are a few problems with this line:
The declaration of :Array without a generic parameter.
The type declaration is unnecessary, since the type system already knows this is an Array, since you're calling Array's initializer.
Creating this whole Array is unnecessary, since we're just passing the result to sorted, which already exists on the collection returned by keys, and which will return an Array. Creating the array is therefore a needless performance hit which we should avoid.
I would, in fact, delete this entire line, and just replace keysArray with self.styles.keys in the next line:
let sortedKeysArray = self.styles.keys.sorted(by:
Next:
{
(str1: NSObject, str2: NSObject) -> Bool in
return Int((str1 as! String))<Int((str2 as! String))
Okay, we've got a few problems here.
str1 and str2 are declared as NSObject, when they are in fact Strings.
Consequently, the as! String casts are unnecessary.
Int(String) returns an Optional, so you need to take into account the case where the result may be nil. I'd just provide a default value; probably 0, although you could also use a guard statement to throw an error if you prefer.
In general, there's a lot of verbosity here. This whole closure can actually be succinctly written as a one-liner:
let sortedKeysArray = self.styles.keys.sorted { (Int($0) ?? 0) < (Int($1) ?? 0) }
Anyway, take care of these issues, and your code will compile.
That all seems pretty complex. You can simplify this quite a bit:
self.styles = [String: [Int: Int]]()
self.stylesLevel1Keys= self.styles.keys.sorted { Int($0)! < Int($1)! }
self.styleThemesPickerView.reloadAllComponents()

swift XCTAssertEquals succeeded with Optional

I have this succeeded test:
func testProfileFieldValue() {
let realm = try! Realm()
let vs = ["name":"n"]
createOrUpdate(realm: realm, value: vs)
let profile = realm.objects(Profile.self).first
XCTAssertEqual("n", profile?.name)
}
private func createOrUpdate(realm:Realm, value: Any = [:]) {
try! realm.write() {
realm.create(Profile.self,value:value,update: true)
}
}
Why did this test succeed? "n" is not an optional. If i try to assert:
XCTAssertEqual("nf", profile?.name)
I get this failure message from Xcode:
XCTAssertEqual failed: ("Optional("nf")") is not equal to
("Optional("n")")
Why "nf" is and Optional?
thx
please try this way apple suggest
if let profilename= profile?.name {
XCTAssertEqual("n", profilename)
}
else {
XCTFail("Value isn't set")
}
UPDATE:
#MartinR comment good point out :
The non-optional on the left side "nf" gets automatically promoted to an optional.because profile?.name is an optiona
More check this
Swift comparing Strings optionals vs non-optional
What's happening with
XCTAssertEqual("nf", profile?.name)
is the compiler sees that the second expression evaluates to an optional String. It then implicitly converts the first expression to an optional, in order to do the comparison. This is why the failure message shows both sides as Optional.

Converting from Int to String Swift 2.2

Dears
I have this case where chatId is a property of type Int
let StringMessage = String(self.listingChat?.messages.last?.chatId)
When I debug I find that StringMessage is returning Optional(15) Which means it is unwrapped. But at the same time XCode does not allow me to put any bangs (!) to unwrap it. So I am stuck with Unwrapped Variable. I know its noob question but it I really cant get it. Your help is appreciated.
Thank you
It depends on what you want the default value to be.
Assuming you want the default value to be an empty string (""), You could create a function or a method to handle it.
func stringFromChatId(chatId: Int?) -> String {
if let chatId = chatId {
return String(chatId)
} else {
return ""
}
}
let stringMessage = stringFromChatId(self.listingChat?.messages.last?.chatId)
Or you could handle it with a closure.
let stringMessage = { $0 != nil ? String($0!) : "" }(self.listingChat?.messages.last?.chatId)
If you don't mind crashing if self.listingChat?.messages.last?.chatId is nil, then you should be able to directly unwrap it.
let StringMessage = String((self.listingChat?.messages.last?.chatId)!)
or with a closure
let stringMessage = { String($0!) }(self.listingChat?.messages.last?.chatId)
Update
Assuming chatId is an Int and not an Optional<Int> (AKA Int?) I missed the most obvious unwrap answer. Sorry, I was tired last night.
let StringMessage = String(self.listingChat!.messages.last!.chatId)
Force unwrap all the optionals along the way.
Optionals have a very nice method called map (unrelated to map for Arrays) which returns nil if the variable is nil, otherwise it calls a function on the (non-nil) value. Combined with a guard-let, you get very concise code. (I've changed the case of stringMessage because variables should begin with a lower-case letter.)
guard let stringMessage = self.listingChat?.messages.last?.chatId.map { String($0) } else {
// Do failure
}
// Success. stringMessage is of type String, not String?
I think:
let StringMessage = String(self.listingChat?.messages.last?.chatId)!

Printing optional variable

I am trying with these lines of code
class Student {
var name: String
var age: Int?
init(name: String) {
self.name = name
}
func description() -> String {
return age != nil ? "\(name) is \(age) years old." : "\(name) hides his age."
}
}
var me = Student(name: "Daniel")
println(me.description())
me.age = 18
println(me.description())
Above code produces as follow
Daniel hides his age.
Daniel is Optional(18) years old.
My question is why there is Optional (18) there, how can I remove the optional and just printing
Daniel is 18 years old.
You have to understand what an Optional really is. Many Swift beginners think var age: Int? means that age is an Int which may or may not have a value. But it means that age is an Optional which may or may not hold an Int.
Inside your description() function you don't print the Int, but instead you print the Optional. If you want to print the Int you have to unwrap the Optional. You can use "optional binding" to unwrap an Optional:
if let a = age {
// a is an Int
}
If you are sure that the Optional holds an object, you can use "forced unwrapping":
let a = age!
Or in your example, since you already have a test for nil in the description function, you can just change it to:
func description() -> String {
return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}
To remove it, there are three methods you could employ.
If you are absolutely sure of the type, you can use an exclamation mark to force unwrap it, like this:
// Here is an optional variable:
var age: Int?
// Here is how you would force unwrap it:
var unwrappedAge = age!
If you do force unwrap an optional and it is equal to nil, you may encounter this crash error:
This is not necessarily safe, so here's a method that might prevent crashing in case you are not certain of the type and value:
Methods 2 and three safeguard against this problem.
The Implicitly Unwrapped Optional
if let unwrappedAge = age {
// continue in here
}
Note that the unwrapped type is now Int, rather than Int?.
The guard statement
guard let unwrappedAge = age else {
// continue in here
}
From here, you can go ahead and use the unwrapped variable. Make sure only to force unwrap (with an !), if you are sure of the type of the variable.
Good luck with your project!
For testing/debugging purposes I often want to output optionals as strings without always having to test for nil values, so I created a custom operator.
I improved things even further after reading this answer in another question.
fileprivate protocol _Optional {
func unwrappedString() -> String
}
extension Optional: _Optional {
fileprivate func unwrappedString() -> String {
switch self {
case .some(let wrapped as _Optional): return wrapped.unwrappedString()
case .some(let wrapped): return String(describing: wrapped)
case .none: return String(describing: self)
}
}
}
postfix operator ~? { }
public postfix func ~? <X> (x: X?) -> String {
return x.unwrappedString
}
Obviously the operator (and its attributes) can be tweaked to your liking, or you could make it a function instead. Anyway, this enables you to write simple code like this:
var d: Double? = 12.34
print(d) // Optional(12.34)
print(d~?) // 12.34
d = nil
print(d~?) // nil
Integrating the other guy's protocol idea made it so this even works with nested optionals, which often occur when using optional chaining. For example:
let i: Int??? = 5
print(i) // Optional(Optional(Optional(5)))
print("i: \(i~?)") // i: 5
Update
Simply use me.age ?? "Unknown age!". It works in 3.0.2.
Old Answer
Without force unwrapping (no mach signal/crash if nil) another nice way of doing this would be:
(result["ip"] ?? "unavailable").description.
result["ip"] ?? "unavailable" should have work too, but it doesn't, not in 2.2 at least
Of course, replace "unavailable" with whatever suits you: "nil", "not found" etc
To unwrap optional use age! instead of age. Currently your are printing optional value that could be nil. Thats why it wrapped with Optional.
In swift Optional is something which can be nil in some cases. If you are 100% sure that a variable will have some value always and will not return nil the add ! with the variable to force unwrap it.
In other case if you are not much sure of value then add an if let block or guard to make sure that value exists otherwise it can result in a crash.
For if let block :
if let abc = any_variable {
// do anything you want with 'abc' variable no need to force unwrap now.
}
For guard statement :
guard is a conditional structure to return control if condition is not met.
I prefer to use guard over if let block in many situations as it allows us to return the function if a particular value does not exist.
Like when there is a function where a variable is integral to exist, we can check for it in guard statement and return of it does not exist.
i-e;
guard let abc = any_variable else { return }
We if variable exists the we can use 'abc' in the function outside guard scope.
age is optional type: Optional<Int> so if you compare it to nil it returns false every time if it has a value or if it hasn't. You need to unwrap the optional to get the value.
In your example you don't know is it contains any value so you can use this instead:
if let myAge = age {
// there is a value and it's currently undraped and is stored in a constant
}
else {
// no value
}
I did this to print the value of string (property) from another view controller.
ViewController.swift
var testString:NSString = "I am iOS Developer"
SecondViewController.swift
var obj:ViewController? = ViewController(nibName: "ViewController", bundle: nil)
print("The Value of String is \(obj!.testString)")
Result :
The Value of String is I am iOS Developer
Check out the guard statement:
for student in class {
guard let age = student.age else {
continue
}
// do something with age
}
When having a default value:
print("\(name) is \(age ?? 0) years old")
or when the name is optional:
print("\(name ?? "unknown") is \(age) years old")
I was getting the Optional("String") in my tableview cells.
The first answer is great. And helped me figure it out. Here is what I did, to help the rookies out there like me.
Since I am creating an array in my custom object, I know that it will always have items in the first position, so I can force unwrap it into another variable. Then use that variable to print, or in my case, set to the tableview cell text.
let description = workout.listOfStrings.first!
cell.textLabel?.text = description
Seems so simple now, but took me a while to figure out.
This is not the exact answer to this question, but one reason for this kind of issue.
In my case,
I was not able to remove Optional from a String with "if let" and "guard let".
So use AnyObject instead of Any to remove optional from a string in swift.
Please refer link for the answer.
https://stackoverflow.com/a/51356716/8334818
If you just want to get rid of strings like Optional(xxx) and instead get xxx or nil when you print some values somewhere (like logs), you can add the following extension to your code:
extension Optional {
var orNil: String {
if self == nil {
return "nil"
}
return "\(self!)"
}
}
Then the following code:
var x: Int?
print("x is \(x.orNil)")
x = 10
print("x is \(x.orNil)")
will give you:
x is nil
x is 10
PS. Property naming (orNil) is obviously not the best, but I can't come up with something more clear.
With the following code you can print it or print some default value. That's what XCode generally recommend I think
var someString: String?
print("Some string is \(someString ?? String("Some default"))")
If you are printing some optional which is not directly printable but has a 'to-printable' type method, such as UUID, you can do something like this:
print("value is: \(myOptionalUUID?.uuidString ?? "nil")")
eg
let uuid1 : UUID? = nil
let uuid2 : UUID? = UUID.init()
print("uuid1: \(uuid1?.uuidString ?? "nil")")
print("uuid2: \(uuid2?.uuidString ?? "nil")")
-->
uuid1: nil
uuid2: 0576137D-C6E6-4804-848E-7B4011B40C11