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>[]?

Bouke
  • 11,768
  • 7
  • 68
  • 102
  • possible duplicate of [Convert NSArray to Swift array](http://stackoverflow.com/questions/24422840/convert-nsarray-to-swift-array) – ColinE Jun 26 '14 at 05:19
  • 1
    Type-cast is not possible since NSSet doesn't inherit NSArray, but you can convert to NSArray with `types.allObjects()` – huocp Jun 26 '14 at 05:20
  • 5
    @ColinE 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. – Bouke Jun 26 '14 at 05:52
  • yes, that is duplicate, because the `NSSet` can be converted `NSArray` and the solution is the same from that point. – holex Jun 26 '14 at 11:03
  • @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. – Bouke Jun 26 '14 at 19:46
  • 1
    @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: https://developer.apple.com/library/prerelease/mac/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html, and you are welcome! – holex Jun 27 '14 at 08:15

8 Answers8

100
var set = NSSet() //NSSet
var arr = set.allObjects //Swift Array
var nsarr = set.allObjects as NSArray  //NSArray
Connor Pearson
  • 63,902
  • 28
  • 145
  • 142
  • 6
    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. – Bouke Feb 18 '15 at 06:59
  • This really helped me with my collectionview - I used it to allow my CoreData NSSet to drive my collectionView via indexpath.row – UKDataGeek Sep 06 '15 at 09:47
  • 2
    To expand on the comment @bouke posted, you can convert a Swift `Set` to `Array` like `let array = Array(mySet)` – Aaron Brager Jan 30 '16 at 19:00
69

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)
FranMowinckel
  • 4,233
  • 1
  • 30
  • 26
  • 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. – Mycroft Canner Jun 05 '21 at 23:51
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
}
grg
  • 5,023
  • 3
  • 34
  • 50
Daniel
  • 2,028
  • 20
  • 18
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()
David.Chu.ca
  • 37,408
  • 63
  • 148
  • 190
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.

Bouke
  • 11,768
  • 7
  • 68
  • 102
  • It's alright man, in Swift 1.2 a native `Set` will be introduced so a workaround is fine at the time being. – superarts.org Mar 19 '15 at 23:31
  • 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**. – SwiftArchitect Oct 25 '15 at 17:11
  • @SwiftArchitect 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. – FranMowinckel Jan 29 '16 at 18:52
  • Thanks for the details. +1. You may want to be specific about Xcode version in your answer. – SwiftArchitect Jan 29 '16 at 19:36
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.

mcfroob
  • 1,044
  • 1
  • 11
  • 13
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;
Amr Angry
  • 3,711
  • 1
  • 45
  • 37
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

snowskeleton
  • 644
  • 7
  • 7