When to cast in swift - ios

So this is probably a super simple question, but I have looked and cannot find the answer.
Let's say I have made a class and I fill an NSMutableArray with instances of this class and that is it. I want to use a for in loop to loop through. What is the easiest way to cast so I don't have to everytime I want to call the current instance in the loop?
Here it is shown:
for m in objects {
m.randomVar = "hello"
}
where m is my custom object that has a randomVar as a String and objects is an array of custom objects.
Of course the code above will not execute because xcode assumes m is an [AnyObject?]. Where and how would be the best was to get m in its own class. Let's assume we are changing more than one variable so casting everytime would not be optimal.
Sorry if I am unclear. Thank you

Avoid the problem.
Don't use NSArray/NSMutableArray — just use let/var variables of pure Swift arrays, such as [MyClass].
Or, if your array is coming from Objective-C, in Xcode 7+ you can use the NSArray<MyClass *> * syntax to expose it to Swift as [MyClass] rather than [AnyObject].
Cast the array.
Use objects as! [MyClass] (unsafe) if you are absolutely sure it contains instances of MyClass.
Cast the variable.
for m in objects {
if let m = m as? MyClass { ...
// or
let myobj = m as! MyClass // unsafe
or in Swift 2 (Xcode 7)
for case let m as MyClass in objects {
// this is executed only for objects which are instances of MyClass,
// ignoring other objects in the array.
}

Related

Assign Array type reference in Swift [duplicate]

When using Objective-C I would pass a NSMutableArray from one view controller VC_A to another VC_B by simply assigning a property in VC_B as
VC_B.list = self.list
where self is VC_A
It allows the changes done in VC_B on the list to be seen in the list in VC_A when the view controller was say popped off the navigation stack.
However in Swift as arrays are passed by value, assigning as above does not work so I am stuck how to solve this. What would be the correct way to handle this now?
You can still do this in Swift by making the property an NSMutableArray, just as before. Foundation types still exist if you ask for them. But this is bad design in both ObjC and Swift. It creates spooky action at a distance (when things magically change values that were not part of the call) and likely breaks MVC (if the data is persistent, it should live in the model, not in the view controllers).
There are two common patterns in Cocoa: store the data in the model, or pass it via delegation.
If this array represents some kind of persistent state (such as a list of items in the system), then that belongs in the model layer, and both view controllers should read and manipulate it there rather than by communicating with each other. (See Model-View-Controller.)
If this array is a transient piece of data (such as selections from a list), then the calling VC should set itself as the delegate to the receiving VC, and when the receiving VC finishes, it should pass the data back to its delegate. (See Delegates and Data Sources.)
If you use the standard Swift Array which is a value type you have to use a wrapper or a untyped NSArray.
// a generic wrapper class
class Reference<T> {
var value: T
init(_ val: T) { value = val }
}
// Usage
class ViewController1 {
static var list = Reference<[Int]>([])
}
class ViewController2 {
static var list = Reference([3, 5, 7, 9, 11])
func passToOtherVC() {
ViewController1.list = self.list
}
}
If you want to mutate the array you should always change the value property of the Reference object.
In Swift, objects are automatically passed by reference. NSArray is an Objective C class (pass by reference), where as Array is a struct (pass by value).
So if you are working with NSMutableArray the array is already being passed by reference.
Just as a potential proof of concept that complements my comment on the question - it is possible to use the Objective-C NSMutableArray to accomplish this task:
class A {
var x: NSMutableArray = NSMutableArray(capacity: 12)
}
class B {
var y: NSMutableArray!
}
let a = A()
let b = B()
b.y = a.x
b.y[0] = 123
assert(a.x[0] === b.y[0])
Still, this is approach is not following the Swift style of handling data structures IMO.

Extracting NSManagedObject attribute values from NSOrderedSet

I have an initializer that takes an array of Strings as a parameter. Rather than re-write the class and risk breaking things, I'd prefer to feed the initializer what it wants.
I'm trying to extract NSStrings from NSManagedObjects stored in an NSOrderedSet.
Here's what I've tried:
let soundsToPlay = sequenceToPlay.sounds as NSOrderedSet
for element in soundsToPlay {
// this prints out the object in console
print("\(element)")
// this, doesn't give me accessors for the sound object
// print("\(element.fileName)")
}
I'm missing something basic, but I'm not sure what. How would I enumerate the objects in an NSOrderedSet and extract the value of the attributes for the entities contained within the set?
I would suggest reading the documentation on KVC (Key Value Coding) as you can write this as one line of code:
let filenameArray = soundsToPlay.valueForKey("fileName").array()
The call to valueForKey will return an NSOrderedSet of the string and then you can convert that to an array with a call to array()
I figured it out. I was missing a step:
let soundsToPlay = sequenceToPlay.sounds as NSOrderedSet
for element in soundsToPlay {
// I have to tell the compiler what type of thing my thing is
let whatIWantToAccess = element as! MyObjectINeedAccessorsFor
print("\(whatIWantToAccess.fileName)")
}
Thats probably because compiler thinks that "element" is instance of NSManagedObject and it does not have fileName , try explicit type-casting , something like
for element: YourClass in soundsToPlay

Mutable Swift array becomes NSArray when I initialize with Obj-C

I'm creating my base models in Swift(2.0) and then controlling the views in Objective-C. I'm still new to Swift, so hopefully I'm just overlooking something simple, but here is the problem:
I’m making a mutable array in Swift, but when I initialize the array in my Objective-c portion of the program, it becomes an NSArray, more specifically it becomes: Swift._SwiftDeferredNSArray
Why is it becoming immutable when I initialize? Here’s my Swift code:
import Foundation
#objc public class Model : NSObject {
var books:[Book]
override init(){
self.books = [Book]()
}
}
And here’s my Obj-c Code;
Model *bookCollection = [[Model alloc]init];
I’m unable to add objects to my bookCollection.books array (because it has become an NSArray) and when I set a breakpoint and po it, I can see that it is a Swift._SwiftDeferredNSArray. bookCollection.books is supposed to be an NSMutableArray.
Any thoughts?
In swift, the difference between mutable and immutable array is;
var books:[Book] // is a mutable array
let books:[Book] = [book1, book2]; // is immutable array due to let
but I don't think, same rule is followed when bridging to ObjC.
Just for a fix, you may have mutableArray specifically.
import Foundation
#objc public class Model : NSObject {
var books:NSMutableArray = NSMutableArray();
override init(){
super.init();
// other code
}
}
You will need to parse the values to Book Class when retrieving from the array.
bookCollection.books is supposed to be an NSMutableArray.
No, it is not. Var does not mean that the bridged Objective-C object is to be mutable: it means that the property can be assigned to.
The Swift array type is a structure, not a class. This has important bridging implications. The reference itself cannot be shared without passing it as an inout value, and even then the references cannot be stored. If it bridged as a NSMutableArray, it would be possible to have undetectable mutating references, and Swift does not allow that.
You should be able to assign a new NSArray to your property from Objective-C code, though. For instance, this should work:
bookCollection.books = [bookCollection.books arrayByAddingObject:myNewBook];
Your other option, obviously, is to declare books as a NSMutableArray from the Swift side.

Pass array by reference between viewcontrollers in swift

When using Objective-C I would pass a NSMutableArray from one view controller VC_A to another VC_B by simply assigning a property in VC_B as
VC_B.list = self.list
where self is VC_A
It allows the changes done in VC_B on the list to be seen in the list in VC_A when the view controller was say popped off the navigation stack.
However in Swift as arrays are passed by value, assigning as above does not work so I am stuck how to solve this. What would be the correct way to handle this now?
You can still do this in Swift by making the property an NSMutableArray, just as before. Foundation types still exist if you ask for them. But this is bad design in both ObjC and Swift. It creates spooky action at a distance (when things magically change values that were not part of the call) and likely breaks MVC (if the data is persistent, it should live in the model, not in the view controllers).
There are two common patterns in Cocoa: store the data in the model, or pass it via delegation.
If this array represents some kind of persistent state (such as a list of items in the system), then that belongs in the model layer, and both view controllers should read and manipulate it there rather than by communicating with each other. (See Model-View-Controller.)
If this array is a transient piece of data (such as selections from a list), then the calling VC should set itself as the delegate to the receiving VC, and when the receiving VC finishes, it should pass the data back to its delegate. (See Delegates and Data Sources.)
If you use the standard Swift Array which is a value type you have to use a wrapper or a untyped NSArray.
// a generic wrapper class
class Reference<T> {
var value: T
init(_ val: T) { value = val }
}
// Usage
class ViewController1 {
static var list = Reference<[Int]>([])
}
class ViewController2 {
static var list = Reference([3, 5, 7, 9, 11])
func passToOtherVC() {
ViewController1.list = self.list
}
}
If you want to mutate the array you should always change the value property of the Reference object.
In Swift, objects are automatically passed by reference. NSArray is an Objective C class (pass by reference), where as Array is a struct (pass by value).
So if you are working with NSMutableArray the array is already being passed by reference.
Just as a potential proof of concept that complements my comment on the question - it is possible to use the Objective-C NSMutableArray to accomplish this task:
class A {
var x: NSMutableArray = NSMutableArray(capacity: 12)
}
class B {
var y: NSMutableArray!
}
let a = A()
let b = B()
b.y = a.x
b.y[0] = 123
assert(a.x[0] === b.y[0])
Still, this is approach is not following the Swift style of handling data structures IMO.

NSMutableArray cast to swift Array of custom type

I'd like to figure out how to specify or cast an NSMutableArray to a swift Array of a custom type. I currently have 2 files:
First file requires an NSMutableArray for its functionality (passed by reference, ability to remove particular objects with indices I don't know)
Second file uses a Swift array (better memory / throwaway array), with a custom type, which I declare using
let newArray: [CustomType]!
I need to pass the NSMutableArray in as a parameter to a function in the second file, which requires a [CustomType]. When simply calling it:
let newVC = UIViewController(array: mutableArray)
it keeps telling me 'CustomType' is not identical to 'AnyObject'. I've tried calling the function using mutableArray as [CustomType], which does not work either. How can I make the swift Array function accept my NSMutableArray?
This works for me:
var swiftArray = NSArray(array:mutableArray) as Array<CustomType>
swiftArray is then a Swift array of objects of CustomType. You can pass the array and iterate over it as you would expect.
What I needed was this:
let newVC = UIViewController(array: mutableArray as AnyObject as [CustomType])

Resources