Reputation: 53
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)
Upvotes: 3
Views: 9021
Reputation: 780
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
}
Upvotes: 8
Reputation: 17040
?!
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.
Upvotes: 6