Reputation: 17847
I have a list of items that have conditions associated with them. I want to store this list of items and their conditions in a plist rather than hardcode them into a .swift file.
The only problem with that is that there needs to a function associated with each item to check a condition. Here is what it might look like hardcoded:
let myJobStep1 = JobStep(heading: "My Heading", description: "This is the description", warningText: "", condition_check: { () -> Bool in
return (self.trayColor == .Blue) || (self.trayColor == .Red)
})
let myJobStep2 = JobStep(heading: "My Heading", description: "Another description", warningText: "", condition_check: { () -> Bool in
return (self.trayColor == .Green)
})
The question is how to encapsulate the function that is checking conditions in a string that can be in a plist file.
Thanks!
Upvotes: 3
Views: 2521
Reputation: 41226
The closest you're going to come is NSPredicate
and/or NSExpression
which give you a limited ability to dynamically evaluate expressions given as strings.
enum Colors : Int {
case Red = 1
case Green = 2
case Blue = 3
}
class Line : NSObject {
var lineColor : Int
init(lineColor:Colors) {
self.lineColor = lineColor.rawValue
}
}
let red = Line(lineColor: .Red)
let green = Line(lineColor: .Green)
let basic = NSPredicate(format: "self.lineColor == $Red")
let test = basic.predicateWithSubstitutionVariables(["Red":Colors.Red.rawValue])
test.evaluateWithObject(red) // true
test.evaluateWithObject(green) // false
Since NSExpression
is based on Objective-C and Key Values, there are some restrictions:
NSObject
.AnyObject
, hence we store the int and not the Color. Note that you could probably handle this a little differently by using a derived property.[String:AnyObject]
so you'll have to use the enum raw value instead of the enum value itself.Upvotes: 3
Reputation: 126127
This sounds like a job for predicates!
The functional programming constructs in Swift — map
, filter
, shorthand closures, etc. — are great tools for when you want to express relationships between data in imperative code. But they aren't the only way to look at such problems.
In particular, it's often useful to be able to express relationships between data as data:
If you're loading a bunch of data model objects from a simple file representation, you can encode their relationships as such, too.
If you're encoding data relationships as data, you can load / update / download them at runtime, instead of changing code and shipping a new app binary.
If you're dealing with a backend database / web service / RDBMS / ORM (CloudKit, Core Data, etc), you want a way to express things like filters, queries, sort orders, and relationships that you can pass to the backend and let the big expensive data-crunching operations happen there.
This is what NSPredicate
(and, relatedly, NSSortDescriptor
) are for. Unlike a filter / comparison closure, the essential logic associated with a predicate can be data, not code. So you can have a data source that looks like this:
[
"heading": "My Heading",
"description": "This is the description",
"condition": "trayColor == \"Blue\" || trayColor == \"Red\""
]
Then you can create predicates from your data source:
let predicate = NSPredicate(format: item.condition)
And use them to test conditions on individual objects, filter collections, etc:
predicate.evaluateWithObject(item)
(items as NSArray).filteredArrayUsingPredicate(predicate)
Edit: @DavidBerry beat me to the punch. This is more of a "why" / high-level answer, but his has some good details on bits like setting up a mapping between enum cases, data-file contents, and predicate formats.
Upvotes: 1