Jim Jim - 2 months ago 9
iOS Question

My convenience init doesn't work in an extension

I'm refactoring a

UIViewController
to remove the non-UI stuff that shouldn't be in there. I've been watching Andy Matuschak's great talk about this and in one example, he's extending
NSPredicate
and moving logic that figures out what the predicate should be from the view controller into it where it will set the
fetchRequest.predicate
with the result.

I'm trying to do the same thing but I'm getting errors in my extension's convenience init:

import Foundation

extension NSPredicate {
convenience init(forLiftLogFilter) { // it doesn't like this line
if let logFilter = UserDefaultsManager.sharedInstance.logFilter?.rawValue {
filterPredicate = NSPredicate(format: "lift.liftName = [c] %@", logFilter)
} else {
filterPredicate = nil
}
}
}


And I'll call it from a `UIViewController' like so:

override func viewWillAppear(animated: Bool) {
let filterPredicate = NSPredicate.forLiftLogFilter // thinks NSPredicate doesn't have a member named 'forLiftLogFilter'
reloadData(filterPredicate)

super.viewWillAppear(animated)
}


In my case, I don't need to pass in any initialization parameters but I'm pretty sure I need an external name which is
forLiftLogFilter
so I can make the call. Xcode (8.0/Swift 2.3) is telling me 'Unnamed parameters must be written with the empty name '_'. I understand that error, but I don't actually have any parameters. It must be thinking
forLiftLogFilter
is a parameter or something. And of course since this isn't working, in my
UIViewController
where I'm making the call tells me that 'Type NSPredicate has no member 'forLiftLogFilter'.

I did a lot of reading about extensions, initializers, etc but no answer about a convenience initializer with a name but no parameters.

Can anyone help me understand what's wrong with my initializer?

For comparison, here's the code Andy uses in his demo:

extension NSPredicate {
convenience init(forTasksWithinNumberOfDays numberOfDays: Int, ofDate: Date, calendar: NSCalendar = NSCalendar.currentCalendar()) {
self.init(format: "dueDate = %@" argumentArray: [calendar.dateByAddingUnit(.day, value: numberOfDays, toDate: date, options: NSCalendarOptions())!]
}
}


and he calls it like from view controller like this:

override func viewDidLoad() {
super.viewDidLoad()

fetchRequest.predicate = NSPredicate(forTasksWithinNumberOfDays: 10, ofDate: NSDate())


I've added this to show what kind of structure I think I need to be following...

Answer

Sounds like you are trying to create a class method that returns an instance of an NSPredicate.

Your extension would need to be something like this:

import Foundation

extension NSPredicate { 
    static func forLiftLogFilter() -> NSPredicate? {
        if let logFilter = UserDefaultsManager.sharedInstance.logFilter?.rawValue {
            return NSPredicate(format: "lift.liftName = [c] %@", logFilter)
        } else {
            return nil
        }  
    }
}

Now you can properly call this class method:

let filterPredicate = NSPredicate.forLiftLogFilter