Reputation: 4200
I've been trying to do something in Swift that I thought should be relatively straightforward - store a closure as a property in a struct so it can be used to filter an array by using another property of the struct. However I've come up against a block - I can't add the closure in struct initialiser as the variable it wants to reference doesn't exist, I can't make it a lazy variable as its part of a protocol (not sure if this would work anyway), and I can't assign it after the struct has been initialised as it will then capture the context of the object where eI make the assignment from and try to use that for the local property. A simplified scenario is below:
struct EventSpec {
var dateRange: DateInterval
var filterClosure: ((Date) -> Bool)?
init( dateRange: DateInterval) {
self.dateRange = dateRange
}
func provideMatchingEvents(for events: [Event] {
return events.filters{filterClosure($0.date)}
}
struct Event {
name: String
date: Date
}
ideally would want to init like
eventSpec = EventSpec(dateRange: aDateRamge, filterClosure: { date in self.dateRange.contains(date)}
but that doesn't work as there isn't a self.dateRange at this point.
Trying to add the closure afterwards, either directly or by a 'builder' function doesn't work either as it captures the 'self' of where it was being involved from, rather than accessing the EventSpec's dateRange property.
I'm sure this should be a common pattern, and I' missing something obvious, but can only find references (lots of them) to adding closures as variables that don't reference other properties.
(I realise if I hard-coded the closure within the struct I could access the local variables through a standard capture list, but this removes the ability to define a filter at run-time).
Anyone any ideas?
Upvotes: 0
Views: 508
Reputation: 77641
The scope of the code that is inside the closure is in the object that it is written in. Not the object it is passed into.
So in...
EventSpec(dateRange: aDateRamge) { date in
self.dateRange.contains(date)
}
self
is whatever the place is that is creating the EventSpec
object.
What you can do is capture the dateRange
in the closure itself.
So you could redefine you EventSpec like so...
struct EventSpec {
var filterClosure: ((Date) -> Bool)?
func provideMatchingEvents(for events: [Event]) {
return events.filters{ filterClosure($0.date) }
}
}
And then create it like...
let dateRange = //some date range that you have already got
let eventSpec = EventSpec(filterClosure: { dateRange.contains($0) })
// in this line the dateRange is captured by the closure so you don't need to capture it as a separate property
This would do what you are trying to do I think.
Upvotes: 1