I am using both swift and objective-c in my app.
I have a CustomClass and I want to create a swift array for the class and add content to it from my objective-c class called oldClass that has an array of these objects in a NSArray called arrayOfCustomClass.
var newArray = [CustomClass]()
newArray += oldClass.arrayOfCustomClass
This causes an error:
'[(CustomClass)]' is not identical to 'CGFloat'
Any help?
thanks
Reza
The problem is that Swift knows nothing of what's in an NSArray. You must cast the NSArray explicitly to a [CustomClass] (and you'd better not be lying or you'll crash at runtime).
What seems to work is:
newArray += oldClass.arrayOfCustomClass as AnyObject as [CustomClass]
To do this safely you just need to try an optional cast. If you think the NSArray only has elements of type CustomClass, you can do this:
var newArray = [CustomClass]()
if let customArray = oldClass.arrayOfCustomClass as? [CustomClass] {
newArray += customArray
}
If you want to extract the CustomClass elements (a little different from what you asked, I know), this is the way:
var newArray = [CustomClass]()
for element: AnyObject in oldClass.arrayOfCustomClass {
if let custom = element as? CustomClass {
newArray.append(custom)
}
}
Related
Just trying to be extra careful here...
If I have an app that saved a value in UserDefaults like this in Objective-C:
NSString *newString = textField.text;
[[NSUserDefaults standardUserDefaults] setObject: newString forKey:#"textKey"];
Would this be the proper way of checking whether this value exists when I release an update to the app that is now coded in Swift:
if (UserDefaults.standard.object(forKey: "textKey") as? String) != nil {
print("There is a string")
} else {
print("No string exists")
}
I try to use .string(forKey:) and .bool(forKey:) since they've been introduced, but is it safest since this was saved as an object to pull it out as an object and then test it with "as? String"?
Trickier version of the same question:
An NSMutableArray of NSDictionary objects was saved as an object in UserDefaults
if let oldData = UserDefaults.standard.object(forKey: "theData") as? [NSMutableDictionary] {
}
Will NSDictionary and NSMutableDictionary be interchangeable here?
Thanks!
[NS]UserDefaults is backed by a plist and a dictionary. The Objective-C string was saved to the plist as a <string>Some Text</string>.
That same plist loaded in Swift gives a string for that key.
So you should have no issue using UserDefaults.string to read the value stored with Objective-C. Of course you still need to verify the value actually exists.
if let str = UserDefaults.standard.string(forKey: "textKey") {
print("Found \(str)")
} else {
print("No string for key")
}
Even if the original value isn't a string, using UserDefault.string is safe. It will simply return nil for non-string values.
With regard to NSDictionary and NSMutableDictionary, note that UserDefaults only retrieves immutable NSDictionary. You can't read an NSMutableDictionary (or array) from UserDefaults.
Note you can safely use a Swift dictionary to read the original NSDictionary. No need to use Objective-C classes.
Yes your solution is safe, if the value you saved in the Objective C code is indeed a String type than, when you retrieve that value from NSUserDefaults, it will be a String and will correctly be coerced to a String by the ? operator of your Swift code.
The recommended way (in Swift and Objective-C) is to register each key value pair to provide a default value. This default value is considered until the value is changed the first time.
let userDefaults = UserDefaults.standard
let defaultValues : [String : Any] = ["textKey" : "", "dictionaryKey" : [:]]
userDefaults.register(defaults: defaultValues)
Now you can safely write
if UserDefaults.standard.string(forKey: "textKey")!.isEmpty {
print("No string exists")
} else {
print("There is a string")
}
Of course the developer is responsible for overwriting a value with the same type.
NSMutableDictionary is not related to Swift Dictionary. To get a mutable object just use the var keyword.
var dictionary = UserDefaults.standard.object(forKey: "dictionaryKey") as! [String:Any]
I'm trying to build an Objective-C block in Swift 2 in order to add it to an NSArray like so :
typealias CompletionBlock = () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
nsArray.addObject(aBlock) // Error
I know it will work just fine with a Swift array, but I need an NSArray here for compatibility with existing Objective-C code. And if I use a swift array the compiler will refuse to cast it to an NSArray because it won't be a [AnyObject] (it will be a [Any]).
The problem here is that a swift closure is not an object contrary to Objective-C blocks which are objects behind the scene (they are instances of NSBlock which is a subclass of NSObject)
So my question is : How do a create an Objective-C block in swift ? I've tried using #convention (block) in the typealias but it doesn't work.
EDIT : As of Swift 3, this is completely unnecessary (and doesn't even work). Adding closures to Objective-C arrays works out of the box in Swift 3. The answer below is valid for Swift 2 only.
I know this is a duplicate but I will still post a refactored answer from swift-closure-as-anyobject and cast-closures-blocks in case anyone lands on this one first.
The solution is to use the unsafeBitCast function to convert the Swift closure to an Objective-C compatible object before adding it to an NSArray and back before using it in Swift.
// The `#convention(block)` is important here in order to get
// Objective-C like memory management
typealias CompletionBlock = #convention(block) () -> Void
let aBlock:CompletionBlock = {
print("Hello world!")
}
let nsArray = NSMutableArray()
let blockObject = unsafeBitCast(aBlock, AnyObject.self)
nsArray.addObject(blockObject)
let closureObject = nsArray[0]
let closure = unsafeBitCast(closureObject, CompletionBlock.self)
closure()
I am facing a issue where I need to find the type of property defined in a class.
For Ex:
class Demo {
var employee: [String] = [String]()
var departments: [Int] = [Int]()
}
I am able to find that employee or departments is of type Array. However, I am not able to find out which kind of array the variable is. Is it a array of Strings or Numbers?
Thanks for the help.
In Objective-C arrays are untyped at runtime. So they can't tell you their object type because they don't know it. This doesn't change with lightweight generics; they allow the compiler to perform some checks by declaring intended usage but leave no runtime footprint.
Get object type from empty Swift Array tackles the Swift side of things; I couldn't directly speak to that from memory. If it's correct then it looks like it could do with some votes.
you can make type checking for objects like
for(id object in departments)
{
if([object isKindOfClass:[NSString class]])
{
//NSString here
}
else if([object isKindOfClass:[NSNumber class]])
{
//NSNumber here
}
}
or if you want an object of responding to selector
for(id object in departments)
{
if([object respondsToSelector:#selector(methodName:)])
{
//object responds to selector here
}
}
Improving meth's answer in swift just like objective-c you can check for type of an item in the array
for item in library {
if item is String {
//Do something with Strings
} else if item is Int {
//Do something with Numbers
}
}
You can find all the documentation for swift Type Casting here.
I have a model object written in Objective-C, which has a property of the type NSMutableArray.
It can either be nil or has a valid object reference.
I am using bridging header and I have a few files written in Swift.
In the Swift file I want to iterate through the objects in the array, only if it has something in it.
How can I achieve this?
I have tried things such as:
if let a = MyObj.myArray {
}
if(MyObj.myArray != nil) {
}
if(MyObj.myArray != NSNull()) {
}
I finally got the hang of optionals in Swift code alone, but I am not able to understand the behavior when I am passing around objects written in Objective-C in Swift code.
My Actual Code Looks like this :
Code :
if let values = attribute.values {
for val in values {
print(val);
}
return true;
}
Exception:
-[NSNull countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance
The error message tells us the problem:
-[NSNull countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance
Your attribute.values isn't nil or an NSMutableArray. It's [NSNull null].
NSNull is a real object that's used instead of nil in collections, because the common Foundation collections (NSArray and NSDictionary) cannot store nil as a value.
We most often come across NSNull when decoding a data structure from JSON. When the JSON decoder finds null (a JSON literal) in an array or object, it stores [NSNull null] in the corresponding NSArray or NSDictionary.
Something like this should work:
if let values = attribute.values where values != NSNull() {
for val in values {
print(val);
}
return true;
}
You should use the new Nullability annotation syntax for your Objective-C properties. These annotations help communicate to Swift whether you intend for an object to be nil or not. For example:
#property (nullable) NSMutableArray* myArray;
With these annotations, your Objective-C objects should work just like a native Swift object. You can do an if let or any other nil check.
if let arr = myObject.myArray {
// Do something with arr
}
else {
// Object is nil do something else
}
if myObject.myArray == nil {
// Array is nil, handle it.
}
You can read more about Nullability annotations for Objective-C at the Apple Swift blog.
https://developer.apple.com/swift/blog/?id=25
In Objective-C, I might use +[NSValue valueWithNonretainedObject:] to keep a unique ID of an object, where I don't wish to retain the object itself. Seems like that's deprecated for Swift.
How to do in Swift?
It's still there. It's just been adjusted to be one of NSValue's initializers:
let anObject = "Hello!"
let value = NSValue(nonretainedObject: anObject)
This works:
let foo: NSString = "hello"
let fooval: NSValue = NSValue(nonretainedObject: foo)
fooval.description // evaluates to "<5040d191 b87f0000>", address of `foo`