I have an array of Printable objects, but I need them Equatable and AnyObject compliant.
private(set) var items: [Printable] = []
class func withItems<T: AnyObject where T: Equatable, T: Printable>(items: [T], selectedItem: T? = nil) {
... instance init ...
instance.items = items
}
And it result on EXC_BAD_INSTRUCTION:
fatal error: array cannot be bridged from Objective-C
This is one try to this problems:
Generic function and attribute with Equatable and Printable as parameters in Swift
why?
A Swift Array must contain all one kind of object (e.g. all String or all Int). An Objective-C NSArray can contain many different kinds of objects (e.g. some NSStrings and some NSNumbers). Hence if you get that kind of array from Objective-C you can't magically assign it into a Swift array reference.
What I do in that situation is munge the array to make it acceptable to Swift. I don't know what the details are of what you're getting back from Objective-C; your actual strategy will depend on those details and what you want to do with the array. One approach is to assign / cast into a Swift array of AnyObject. Or you might decide to leave it as an NSArray and work with it entirely through NSArray methods.
Here's an example from my own code. arr is an NSArray that's a mixed bag of NSString and NSNull objects. I know none of the NSString objects are the empty string, so I substitute the empty string for all the NSNull objects, thus giving me an array of just strings, which Swift can deal with:
let arr2 = (arr as Array).map { $0 as? String ?? "" }
Now arr2 is a pure Swift [String] array.
Related
var myArray : NSMutableArray = ["First", "Second","Third"]
var myArray = ["First", "Second","Third"]
When should we use the above given type of Array declaration in Swift.
You don't need to use NSMutableArray when writing Swift code. Swift arrays are automatically bridged to the Objective-C NSArray and NSMutableArray types:
let fib = [1, 1, 2, 3, 5, 8, 13] // immutable array bridged to `NSArray`
var friends = ["Pascal", "Jodie", "Craig"] // mutable array bridged to `NSMutableArray`
The bridging is done when you use Cocoa APIs that are written in Objective-C. And vice-versa, if you get an NSMutableArray as a result of calling such an API, you can assign it to a Swift Array as long as it's a var.
As an example of how bridging works, let's suppose UIKit's UITabViewController is still natively an Objective-C API (I don't know if it's the case or not). Then in Objective-C, you can get its #property viewControllers which is an NSArray:
NSArray<__kindof UIViewController *> *rootViewControllers;
rootViewControllers = [myTabViewController viewControllers];
But in Swift you can just assign this property to a regular array (I'm explicitly giving it a type but that's not actually needed with type inference):
let rootViewControllers: [UIViewController]?
rootViewControllers = myTabViewController.viewControllers
Conversely, you can just pass a Swift Array of UIViewControllers to the setViewControllers method (which in Objective-C is expecting an NSArray):
func buildInitialViewControllers() -> [UIViewController] { ... }
let rootViewControllers = self.buildInitialViewControllers()
myTabViewController.setViewControllers(rootViewControllers, animated=True)
Generally in swift we use constant let c = And variable var v: [String]
But When you get in NSSortDescriptor & perform sorting in that case you have to use NSArray & NSMutableArray
which implements function
open func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] // returns a new array by sorting the objects of the receiver
open func sort(using sortDescriptors: [NSSortDescriptor]) // sorts the array itself
I tried to update Swift 3 and I got the following error :
Ambiguous use of 'mutableCopy()'
Before update to swift 3. It runs well.
Swift 2.3
NSUserDefaults.standardUserDefaults().objectForKey("listsavednews")?.mutableCopy() as! NSMutableArray
Swift 3.0
(UserDefaults.standard.object(forKey: "listsavednews")? as AnyObject).mutableCopy() as! NSMutableArray
I found that mutableCopy in Swift3 return Any that doesnt have method mutableCopy() so that it needs to cast to AnyObject.
Any helps thanks.
I dont know why I can't comment.
Thanks all, I'll be using :
UserDefaults.standard.mutableArrayValue(forKey: "listsavednews")
mutableCopy is an Objective-C method from NSObject. There's little reason to use it in Swift 3.
Since you are dealing with UserDefaults and mutableCopy, you must be dealing with either an array or dictionary. Or it could be a string.
The proper way to do this in Swift 3 is to use the proper UserDefaults method to get an array or dictionary. And assign the result to a var. That combination will give you a mutable array or mutable dictionary.
var someArray = UserDefaults.standard.array(forKey: "somekey")
or:
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey")
In the two above cases, you end up with an optional since there might not be any data for the given key. And you also get a non-specific array or dictionary which isn't ideal. It would be better to cast the result to the appropriate type.
Let's say you have an array of strings and you want an empty array if there is nothing currently in user defaults. You can then do:
var someArray = UserDefaults.standard.array(forKey: "somekey" as? [String]) ?? []
Adjust as necessary if the array contains something other than String.
If you actually have a dictionary, the code would be similar.
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey") as? [String:String] ?? [:]
If your original object is just a string, then you could do:
var someString = UserDefaults.standard.string(forKey: "somekey") ?? ""
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.
I have a Swift Array and I want to add all the objects inside to NSMutableArray
let stringName: String = "Something"
let stringNameSeperated = Array(logoName)
let mutableStringName: NSMutableArray = NSMutableArray(array: stringNameSeperated)
How can I do that?
I'm not really sure what you wanted to archive, but if you just wanted a Swift.Character sequence array, here's a simpler way without even the Array() process: (Notice that the result was casted into String. If you want Character, you will have to modify the code a bit.)
let stringName: String = "Something"
let mutableStringName: NSMutableArray = NSMutableArray(array: map(stringName) { String($0) } )
And from my understanding, you no longer have to case between Swift Array and Objective-C Array, you can use them interchangeably. Also, you can pass a Swift Array to a parameter that expected to be NSArray without problem in Swift 1.x as well.
I created a NSMutableArray in swift using let and
when I add addObject in the mutableArray then it will add it even though I
used the let to assign a constant. Can anyone explain how let works in swift? If it doesn't allow you to add value in later then how is the following
code working?
let arr : NSMutableArray = [1,2,3,4,5]
arr.addObject(6)
println(arr)
Classes are reference types, and NSMutableArray is a class.
Foundation's NSMutableArray is different from Swift's Array: the latter is a value type.
If you create a constant NSMutableArray:
let ns: NSMutableArray = ["a", "b"]
then this works:
ns.addObject("c")
but this doesn't:
ns = ["d", "e"] // nope!
because you can change the content of the reference but you can't change what is assigned to the constant.
On the other hand, with Swift's Array:
let sw: [String] = ["a", "b"]
the constant can't be changed because it's a value, not a reference.
sw.append("c") // nope!
Doc: Structures and Enumerations Are Value Types and Classes Are Reference Types
disclaimer: this answer only applies to NS type data structures, please see #Eric D's answer for the full picture
let when used with a class just means the variable cant be changed, eg, to another array. If you dont want the array to be editable, use a normal NSArray and not a mutable one
let arr : NSMutableArray = [1,2,3,4,5]
arr = [1,2,3,4,5] //error trying to assign to a let variable that has already been assigned
arr.addObject(6) //fine because we are not changing what is assigned to arr, but we are allowed to change the object that is assigned to arr itself
I think your understanding of what a constant variable is, is a bit too strict.