Swift 3 : Ambiguous use of mutablecopy - ios

When i migrated the code from swift 2.3 to 3.0, its throwing an error as below:
let dictionary = (self.testArray!.object(at: i) as AnyObject).mutableCopy()
How to resolve this issue.

Do not use mutableCopy in Swift. The var keyword makes objects mutable
var dictionary = self.testArray![i] as! [String:Any]
And don't use Foundation collection types (NSArray / NSDictionary) in Swift either.
Use native types.

Related

Cast error from (Any hashable Any) in Swift to NSMutableDictionary in Objective C

I have an Objective C method which expects the parameters passed to it be of the type NSMutableDictionary. I have another Swift file and a Swift method from which I need to pass a structure which is of type, (AnyHashable: Any)() , to my Objective C method which needs the same in NSMutableDictionary format. I have tried casting and other approaches, but I couldnt find the answer.
//Objective C method belongs to UserLogin class
-(void)loginWithUserData:(NSMutableDictionary *)userLoginData{
// Do something
//Get data from Swift file here
}
//Swift function from which I need to call the Objective C method and pass the data
var data = [AnyHashable: Any]()
var ObjClass = UserLogin()
ObjClass.loginWithUserData(userLoginData: data)
How should this be casted as? Im using Swift 3.
Swift dictionaries are immutable, so you can't simply cast a Swift dictionary to an NSMutableDictionary.
Ideally, loginWithUserData would expect an NSDictionary, not an NSMutableDictionary; it isn't good design for the function to mutate the dictionary. If the parameter type is NSDictionary then you could still pass an NSMutableDictionary.
If you can't change the Objective-C function then you will need to create an NSMutableDictionary in Swift:
var data = NSMutableDictionary()

Adding Swift closure object to NSMutableArray and then removing it doesn't work - LEAK

I have some Swift code that interoperates with Objective C, and I noticed I'm leaking, I narrowed it down to NSMutableArray not removing my closure, here's a pure Swift snippet that reproduces the problem:
let myClosure : ((String?) -> ())! = { (fileName: String?) in
}
let arr = NSMutableArray()
arr.add(myClosure)
arr.remove(myClosure)
Did anyone encounter this -- why does this happen? and how can I get it to work?
The closure doesn't have the reference so the array is unable to compare for removing your closure object that's why this will not remove from the array.
Your code
let arr1 = NSMutableArray()
arr1.add(myClosure)
print(arr1) //("(Function)")
arr1.remove(myClosure)
print(arr1) //("(Function)")
Solution
var arr = Array<Any>()
arr.append(myClosure)
print(arr) //[(Function)]
arr.remove(at: 0)
print(arr) //[]
This will remove by index so you have to use the index for removing the element instead of closure instance also I recommend you to use pure swift classes in Swift.
To re-emphasize, our codebase uses Swift that interops with ObjC, therefore, in my case its not possible to go pure Swift trivially.
I changed our API to use an NSDictionary that maps from NSUInteger handle to the closure and that integer is then used for removal of the closure from the dictionary.
The purpose of that API is to register listener callbacks and have a facility to unregister them. So that NSUInteger handle satisfies the removal bit.

Ambiguous use of 'mutableCopy()' Swift3

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") ?? ""

Could not cast value of type 'Swift.UInt64' (0x10dddde88) to 'Swift.AnyObject' (0x12129d018)

In Swift 2.3, I must cast UInt64 to AnyObject with as!. But It's as in Swift 3. Once I cast this in Swift 2.3, it crashed with the following message.
Could not cast value of type 'Swift.UInt64' (0x10dddde88) to 'Swift.AnyObject' (0x12129d018).
Please help me. Thanks!
Swift 3 greatly differs from Swift 2.3, when it comes to casting. For example, if you run the following in an Xcode 8 playground (using the latest Swift toolchain, Xcode 8.2 in my case):
import Foundation
import Swift
struct Foo
{
init()
{
}
}
NSStringFromClass(type(of: Foo() as AnyObject))
You will see, that NSStringFromClass(type(of: Foo() as AnyObject)) will register as a "_SwiftValue" in the inspection column on the right.
This is because Swift 3 allows you to wrap any construct as an AnyObject using an internal _SwiftValue class, which can then later be unwrapped in some other part of your code.
Swift 2.3 does not have this feature. That is where you are erring.
But what you can do is cast UInt64 to an NSNumber. This works with both Swift 2.3 and 3.0, if I recall correctly. I realize that you might be dealing with a generic type, and you might not know during compile time whether the type you're dealing with is a UInt64 or something else; but this method should work regardless. You could use the as? operator as well, to handle casting errors and exceptions.
In Swift 2, only Int and UInt are convertible to AnyObject (implicitly or explicitly).
When your explicitly convert Int or UInt to AnyObject, you use as casting, not as!:
//Swift 2.3
let ui: UInt = 1234
let uiObj = ui as AnyObject
let i: Int = 5678
let iObj = i as AnyObject
In fact, they are automatically converted to NSNumber:
//Swift 2.3
print(uiObj.dynamicType) //->__NSCFNumber (__NSCFNumber is one of the concrete subclasses of NSNumber)
print(iObj.dynamicType) //->__NSCFNumber
In Swift 3, some implicit conversions are removed, but since 3.0.1, all integer type can be converted to NSNumber with explicit casting:
//Swift 3.0.2
let ui64: UInt64 = 12345678
let ui64Obj = ui64 as AnyObject
let i8: Int8 = 123
let i8Obj = i8 as AnyObject
let ui: UInt = 1234
let uiObj = ui as AnyObject
let i: Int = 5678
let iObj = i as AnyObject
print(type(of: ui64Obj)) //->_SwiftTypePreservingNSNumber (_SwiftTypePreservingNSNumber is another subclass of NSNumber)
print(type(of: i8Obj)) //->_SwiftTypePreservingNSNumber
print(type(of: uiObj)) //->_SwiftTypePreservingNSNumber
print(type(of: iObj)) //->_SwiftTypePreservingNSNumber
So, if you want to make nearly the same result in Swift 2 as uint64Value as AnyObject in Swift 3, you need to construct NSNumber explicitly.
//Swift 2.3
let ui64: UInt64 = 12345678
let ui64Obj = NSNumber(unsignedLongLong: ui64)
print(ui64Obj.dynamicType) //->__NSCFNumber
Replace uint64Value as AnyObject to NSNumber(unsignedLongLong: uint64Value).
(In fact, _SwiftTypePreservingNSNumber is not exactly the same as __NSCFNumber, but that may not be your problem.)
By the way, when I set Legacy Swift flag to YES in Xcode 8.2, it showed a warning saying:
Swift 2.3 is deprecated and will be removed in a future release of
Xcode. Migrate to Swift 3 now.
Apple is strongly encouraging us to move to Swift 3 immediately.
You'd better hurry migrating your code to Swift 3.

NSFastEnumerationIterator.Element (aka Any) has no subscript members

after updating xcode to 8 version, I got this error
searchArray = allArray.filter({$0["test"] as? String == findCode
let resultText: NSString = ($0["test"] as? String)
return (resultText.range(of: searchText, options: NSString.CompareOptions.caseInsensitive).location) != NSNotFound
}) as NSArray!
Any thoughts?
The ultimate source of your problem is this line:
var allArray : NSArray!
NSArray is an Objective-C class. You are trying to write Objective-C code in Swift. Don't. This is Swift. Use Swift types! Write Swift code!
This is presumably an array of something. Swift needs to know what that is. For example, if this is an array of dictionaries, than its type would be [[AnyHashable:Any]]. If you use Swift types, your code will just work, because Swift will know what the elements of your arrays are.

Resources