Kenny Wyland
Kenny Wyland

Reputation: 21870

Elegantly iterate over a CoreData relationship in swift?

I've got a ServiceGroup entity with a one-to-many relationship to ServiceItem called items. I want to iterate over the items but swift seems completely ignorant of the variable type because items is just defined as an NSOrderedSet?.

The only way I've found to be able to iterate over my items is that I have to cast them using the new case syntax added to for loops recently, but this seems ridiculous since the code -should- know what the type is already.

Anyone have suggestions so that I don't have to constantly case/coerce my relationship entities?

if let items = self.items {
    for case let item as ServiceItem in items {
        mins += item.calculateTotalMinutes()
    }
}

Upvotes: 1

Views: 1005

Answers (3)

Fred Klein
Fred Klein

Reputation: 658

You can convert the NSSet used to model the CoreData one-to-many relationship to an array :

let serviceItems: [ServiceItem] = (myServiceGroup.items?.allObjects as? [ServiceItem]) ?? []

From there on, you can use usual forEach(), map() or reduce() operations.

Upvotes: 0

vadian
vadian

Reputation: 285082

NSOrderedSet has a property array to get the backing array. As the property returns [Any] you have to cast the type. And for convenience you could use forEach

if let items = self.items {
    (items.array as! [ServiceItem]).forEach {
        mins += $0.calculateTotalMinutes()
    }
}

or reduce

if let items = self.items {
    mins = (items.array as! [ServiceItem]).reduce(mins, { $0 + $1.calculateTotalMinutes() })
}

Upvotes: 0

Pratik Patel
Pratik Patel

Reputation: 449

Something slightly more elegant (in my opinion) would be to use higher order functions like so:

let minutes = items
        .map({$0 as? ServiceItem})
        .flatMap({$0})
        .reduce(0) { (result, serviceItem) -> Double in
            return result + serviceItem.calculateTotalMinutes()
    }

Upvotes: 2

Related Questions