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

6
  • possible duplicate of Convert NSArray to Swift array Commented Jun 26, 2014 at 5:19
  • 1
    Type-cast is not possible since NSSet doesn't inherit NSArray, but you can convert to NSArray with types.allObjects() Commented Jun 26, 2014 at 5: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. Commented Jun 26, 2014 at 5:52
  • yes, that is duplicate, because the NSSet can be converted NSArray and the solution is the same from that point. Commented Jun 26, 2014 at 11:03
  • 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: developer.apple.com/library/prerelease/mac/documentation/Swift/…, and you are welcome! Commented Jun 27, 2014 at 8:15

8 Answers 8

101
var set = NSSet() //NSSet
var arr = set.allObjects //Swift Array
var nsarr = set.allObjects as NSArray  //NSArray
Sign up to request clarification or add additional context in comments.

3 Comments

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.
This really helped me with my collectionview - I used it to allow my CoreData NSSet to drive my collectionView via indexpath.row
To expand on the comment @bouke posted, you can convert a Swift Set to Array like let array = Array(mySet)
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)

1 Comment

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.
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
}

Comments

14

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()

Comments

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.

4 Comments

It's alright man, in Swift 1.2 a native Set will be introduced so a workaround is fine at the time being.
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 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.
Thanks for the details. +1. You may want to be specific about Xcode version in your answer.
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.

Comments

2

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;

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.