swiftnspredicateswift-blockswift-optionals

Swift ignoring parameter types defined in block signature


I have the code below, which the compiler is happy with:

func CheckPaintExists(colorCode : String, applicationCode : String) {
    let checkRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Paint")
    checkRequest.predicate = NSPredicate(block: { (item, bindings) -> Bool in
        return (item as! Paint).ColorCode == colorCode 
            && (item as! Paint).ApplicationCode == applicationCode
    })
    checkRequest.includesSubentities = false;

    //managedContext.count(for: ...)do further stuff
}

But once I define the item type in the block signature, I get an error on the return line:

func CheckPaintExists2(colorCode : String, applicationCode : String) {
    let checkRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Paint")
    checkRequest.predicate = NSPredicate(block: { (item : Paint?, bindings : NSDictionary?) -> Bool in
        return item.ColorCode == colorCode //*Value of type 'Any?' has no member 'ColorCode'
            && item.ApplicationCode == applicationCode
    })
    checkRequest.includesSubentities = false;

    //managedContext.count(for: ...)do further stuff
}

It says Value of type 'Any?' has no member 'ColorCode'. How can I resolve this? Why is it still using the default Any type provided by the block?


Solution

  • If you look at the signature for NSPredicate.init(block:) then you will see that block takes two parameters and returns a boolean: (Any?, [String : Any]?) -> Bool) and the first parameter is indeed an Any.

    In your first example you are force casting with as! which is why it works (it would crash here if the type was not actually Paint boxed in an Any. Your second example is giving you an error because your type annotation is wrong; the compiler expects the first parameter to be Any and not Paint?; it really should be giving you the error on the line above but it seems its first catching it on the line with the return.

    If you want to unwrap safely it should be:

    func CheckPaintExists(colorCode : String, applicationCode : String) {
        let checkRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Paint")
        checkRequest.predicate = NSPredicate(block: { (item, bindings) -> Bool in
            guard let paint = item as? Paint else {
               return false
            }
            return paint.ColorCode == colorCode 
                && paint.ApplicationCode == applicationCode
        })
        checkRequest.includesSubentities = false;
    
        //managedContext.count(for: ...)do further stuff
    }