Convert NSSet to Swift Array
Asked Answered
K

8

71

In CoreData I have defined an unordered to-many relationship. This relationship is defined in Swift like this:

@NSManaged var types : NSMutableSet

However, to use Swift at it's best, I want to use a normal Swift array like Type[]. However, CoreData forces me to use NS(Mutable)Set. How can I type-cast / convert the NSSet to Array<Type>[]?

Kine answered 26/6, 2014 at 5:13 Comment(6)
possible duplicate of Convert NSArray to Swift arrayBoffin
Type-cast is not possible since NSSet doesn't inherit NSArray, but you can convert to NSArray with types.allObjects()Monster
@Boffin it's not a duplicate as the one you're referring to is about NSArray and this is about NSSet. Different types, different question and probably different answers.Kine
yes, that is duplicate, because the NSSet can be converted NSArray and the solution is the same from that point.Florist
@holex, well actually that's not true either. NSSet can be converted to an Array, as shown in the answers below. However NSArray doesn't appear to have an 'easy' cast / convert to Array.Kine
@bouke, the Swift documentation says the following about Cocoa Types: Swift automatically bridges between the Array type and the NSArray class., and (...)in Swift code, you can pass an Array value to a method expecting an NSArray object. You can also cast between a bridged type and its counterpart., I guess that is quite clear statement, source: developer.apple.com/library/prerelease/mac/documentation/Swift/…, and you are welcome!Florist
L
70

As of Xcode 7.2 with Swift 2.1.1

Actually, I found out by trial that:

@NSManaged var types : Set<Type>

Works perfectly fine.

The fact is that CoreData generates somethink like:

@NSManaged var types : NSSet

when creating a NSManagedObject subclass from the editor. So I was wondering if there was a not very verbose method to use the filter method without casting. But then Swift is supposed to bridge automatically between NSSet, NSArray and their counterparts. So I tried declaring it directly as a Set and it works.

Answering the question, converting it to an Array becomes trivial:

Array(types)
Lindo answered 29/1, 2016 at 18:46 Comment(1)
Core Data uses NSSet to optimise things under the hood. It might be less memory efficient to use Set even though the ergonomic is better.Wommera
F
101
var set = NSSet() //NSSet
var arr = set.allObjects //Swift Array
var nsarr = set.allObjects as NSArray  //NSArray
Frankly answered 26/6, 2014 at 10:1 Comment(3)
While this answer is still valid, Swift 1.2 now also contains a native Set structure. The Set doesn't have allObjects, but supports (most) operations that an array would support. So there is less need to convert a NSSet to array.Kine
This really helped me with my collectionview - I used it to allow my CoreData NSSet to drive my collectionView via indexpath.rowTigerish
To expand on the comment @bouke posted, you can convert a Swift Set to Array like let array = Array(mySet)Lianeliang
L
70

As of Xcode 7.2 with Swift 2.1.1

Actually, I found out by trial that:

@NSManaged var types : Set<Type>

Works perfectly fine.

The fact is that CoreData generates somethink like:

@NSManaged var types : NSSet

when creating a NSManagedObject subclass from the editor. So I was wondering if there was a not very verbose method to use the filter method without casting. But then Swift is supposed to bridge automatically between NSSet, NSArray and their counterparts. So I tried declaring it directly as a Set and it works.

Answering the question, converting it to an Array becomes trivial:

Array(types)
Lindo answered 29/1, 2016 at 18:46 Comment(1)
Core Data uses NSSet to optimise things under the hood. It might be less memory efficient to use Set even though the ergonomic is better.Wommera
P
16

This is how I did it:

let array = object.NSSet?.allObjects as! [ArrayType]

Then you can manipulate the array as normal:

for item in array {
    id = item.id
}
Pedestal answered 10/3, 2017 at 11:53 Comment(0)
E
13

Use map to get array like this:

extension NSSet {
  func toArray<T>() -> [T] {
    let array = self.map({ $0 as! T})
    return array
  }
}

...
let myArray: [MyType] = set.toArray()
Evadnee answered 19/3, 2018 at 3:28 Comment(0)
K
8

This answer is outdated as of Xcode 7.2/Swift 2.1. See the accepted answer for an updated answer.


I'm currently using obj.types.allObjects as Type[], but that feels like a hack/workaround.

Kine answered 26/6, 2014 at 19:44 Comment(4)
It's alright man, in Swift 1.2 a native Set will be introduced so a workaround is fine at the time being.Marucci
As of Xcode 7, Swift 2. CoreData stil uses NSSet, so this very set till needs to be turned into either a native Array or native Set.Silkweed
@Silkweed Actually, as of Xcode 7.2 (I haven't tried other versions), it works if you declare it directly as a native Set as I explain in my answer.Lindo
Thanks for the details. +1. You may want to be specific about Xcode version in your answer.Silkweed
I
2

You can also use .sortedArray to sort the elements.

Swift 3:

yourNSSetType?.sortedArray(using: [NSSortDescriptor(key: "keyToSortBy", ascending: true, selector: #selector(NSNumber.compare(_:)))]) as! [yourNSSetType]

For strings, use NSString.compare in the #selector call.

Immoral answered 5/11, 2016 at 7:14 Comment(0)
S
1

Thanks to @David for his solution I had the same situation In my current project, we still use Objective C with swift bridging for the coredata modules so in my case, I had to use the following as compiler can't intervene what is the Generic Type to convert i had to pass it with using actually :(

extension Set {
    
    func toArray<S>(_ of: S.Type) -> [S] {
        let array = self.map({$0 as! S})
        return array
    }
    
}

and using:

Items?.toArray(XYZ.self)

in my Managed object; variable was like this for reference

@property (nullable, nonatomic, retain) NSSet<XYZ *> * items;
Sequester answered 3/10, 2021 at 9:57 Comment(0)
R
0

Here's how to do it with a ForEach view in SwiftUI

ForEach(object.allObjects as! [ObjectType], id: \.self)
{ obj in
    Text("\(obj.name)")
}

XCode 14.2, targeting iOS 16.2

Ruckus answered 21/12, 2022 at 21:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.