In Swift 2, what changed in NSDictionary? - ios

In Swift 2, what exactly changed in the NSDictionary(objects, forKeys) (Objective-C: dictionaryWithObjects:forKeys:) creation?
Apple's prerelease documentation was particularly useless, given that there only is Obj-C code. Even though there's a slight but obvious change in the provided snippet, I don't speak obj-c and haven't made sense out of the change so far.
Here's some code for you guys:
videoDataOutput.videoSettings = NSDictionary(objects: NSNumber(kCVPixelFormatType_32BGRA), forKeys: (kCVPixelBufferPixelFormatTypeKey))
//Working old Swift code; missing argument for parameter 'count' in call on Swift 2
+ (instancetype)dictionaryWithObjects:(NSArray *)objects
forKeys:(NSArray *)keys
//Old Obj-C code
+ (instancetype nonnull)dictionaryWithObjects:(NSArray<ObjectType> * nonnull)objects
forKeys:(NSArray<id<NSCopying>> * nonnull)keys
//New Obj-C code
I've read elsewhere that nonnull is supposed to facilitate compatibility with Swift, but what about <ObjectType>? And what's id?
#Update
Fixed swift code from NSDictionary(objectsAndKeys: ) to NSDictionary(objects: , forKeys:)

You are not using +dictionaryWithObjects:forKeys:, you are using +dictionaryWithObjectsAndKeys:. These are two different methods. +dictionaryWithObjectsAndKeys: is not supported by swift.
Using +dictionaryWithObjects:forKeys: would look like this.
videoDataOutput.videoSettings = NSDictionary(objects: [NSNumber(integer: kCVPixelFormatType_32BGRA)], forKeys: [kCVPixelBufferPixelFormatTypeKey])
NOTE: I also needed to change NSNumber(kCVPixelFormatType_32BGRA) to NSNumber(integer: kCVPixelFormatType_32BGRA)
Because you only have one entry in the dictionary, you can use +dictionaryWithObject:forKey:.
videoDataOutput.videoSettings = NSDictionary(object: kCVPixelFormatType_32BGRA as NSNumber, forKey: kCVPixelBufferPixelFormatTypeKey as NSString)
NOTE 1: I used kCVPixelFormatType_32BGRA as NSNumber instead of NSNumber(integer: kCVPixelFormatType_32BGRA)
NOTE 2: I had to use kCVPixelBufferPixelFormatTypeKey as NSString instead of just kCVPixelBufferPixelFormatTypeKey.
I think the simplest way to do this is to use swift's Dictionary literal.
videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as NSString: kCVPixelFormatType_32BGRA]

I got this to work in XCode 7 Swift 2 using simply
output.videoSettings = [ kCVPixelBufferPixelFormatTypeKey: Int(kCVPixelFormatType_32BGRA) ]
Check out this project for some current code https://github.com/gibachan/OpenCVSample/

There is no real change for the obj-c programmer. It is mostly for the compiler and as you said for cross-compatibility to swift:
Instead of (NSArray *) you now have (NSArray<ObjectType> * nonnull) which means a) the array may not nonnull (not nil) and b) its contents / values must be of type ObjectType.
Instead of (NSArray *) you have (NSArray<id<NSCopying>> * nonnull) which means a) as above and b) its contents must adapt the NSCopying protocol.
Regarding the expected count: the objectsAndKeys is no longer present (docs), you have to use some other function. or just a literal...
Final note regarding id and ObjectType: id refers to any object, you can sage every view or arra or anything as id - it has itself no type information. ObjectType I think refers to generics, it means that you write ObjectType everywhere you want to use a specific type, you dont know the type yet, but it will be the same at every place where ObjectType is written.

Related

Not able to find equivalent of respondsToSelector:#selector(charValue) in Swift 3

I have done lot of google to find the equivalent of respondsToSelector, still i could find any best solution. kindly suggest me for Any object in Swift3 or 4.
Objetive-C
[(id)object respondsToSelector:#selector(charValue)]
In Swift we have .method for AnyObject data type but for Any data type
you should first type cast then use
(object as? Type)?.charValue()
if your object is not of a type then it nil and never call the charValue()
Reason: You can not write respondsToSelector for the swift-based function. There are 2 reasons.
1) In Objective-c, we do have charValue property in NSNumber class along with initWithChar. Whereas in swift we do not have any such properties in NSNumber class.
#property (readonly) char charValue;
- (NSNumber *)initWithChar:(char)value NS_DESIGNATED_INITIALIZER;
2) respondsToSelector only accepts Objective-C functions in argument.
Try with writing responds(to: #selector on NSNumber and you will found that it only accepts objective-c function and we don't have any such method in Swift NSNumber class.
let numb: NSNumber?
numb?.responds(to: #selector(#objc method))
Rather, You can use the swift string conversion of NSNumber, as:
let number: NSNumber?
let numberString = number?.stringValue ?? ""
// Added default variable "" in case of string conversion becomes nil

How do i encode a PersistentID for a song using NSCoder in Swift 3

This is my first time using NSCoder and there used to be a method called encodeInteger but it seems to have vanished in Swift 3 and the docs don't seem to help.
It maybe that the confusion lies in the difference between Int and UInt64. Are they the same?
Should i be using a NSKeyedArchiver and if so how does that work to comply with NSCoding?
Here's before with the error:
And after without an error:
Why don't you use NSNumber and encode it as an object? It'd look like this:
let bigNumber: UInt64 = /* 123 */
let number = NSNumber(value: bigNumber)
// Encoding it just like a String
coder.encode(number, forKey: "BigNumberKey")
// Decoding and using the property uint64Value from NSNumber to get the UInt64 back
if let object = coder.decodeObject(forKey: "BigNumberKey") as? NSNumber {
let decodedBigNumber = object.uint64Value
}
If that's a requirement for some reason, NSCoder supports the encoding of Int64 (and you could cast it, described here).
The change from encodeInteger to just encode is part of SE-0005 (which affected a lot of different classes; UIColor.blueColor() is now UIColor.blue(), for instance).

How to box int (enum) to object in swift?

I need to cast an int to an object, in Objective-C I could do the following
[row.cellConfig setObject:#(UITextFieldViewModeAlways) forKey:#"textField.rightViewMode"];
What would be the Swift equivalent?
The Swift equivalent of UITextFieldViewModeAlways is
UITextFieldViewMode.Always, which is an enumeration value:
enum UITextFieldViewMode : Int {
case Never
case WhileEditing
case UnlessEditing
case Always
}
You get its underlying integer value with .rawValue.
Integers are automatically "bridged" to NSNumber when passed
to functions taking Objective-C parameters (and Swift strings
bridged to NSString).
So this should work:
row.cellConfig.setObject(UITextFieldViewMode.Always.rawValue,
forKey: "textField.rightViewMode")
For more information, see Using Swift with Cocoa and Objective-C.
Use
row.cellConfig.setObject(NSNumber(UITextFieldViewModeAlways), forKey:"textField.rightViewMode")
You need to create a NSNumber object for that int.
NSNumber *intObj = [NSNumber numberWithInt:num];

Swift Int is not convertible to DictionaryIndex?

I'm trying to convert this piece of ojb-c code:
MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
to swift, and as such I have this:
var finishReason: MPMovieFinishReason = notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey.integerValue];
However this gives me the error in the title.
What am I doing wrong here?
You have a few problems, first of all in your direct translation:
var finishReason: MPMovieFinishReason = notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey.integerValue];
The ] is in the wrong place, so you're trying to call integerValue on MPMoviePlayer... instead of on the dictionary lookup.
var finishReason: MPMovieFinishReason = notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey].integerValue;
This is still failing because userInfo is an NSDictionary and not a Dictionary, so it gets mapped to [AnyObject:AnyObject] It seems like the automatic morphing is failing you, so it's falling back to a CFString which isn't mappable to AnyObject.
The process becomes a little clearer if we break it up more:
// Recover the original dictionary, and cast is by input and output, to it
// all makes sense. This will allow String as a parameter and return an NSNumber
// as a result.
let userInfo = notification.userInfo as [String:NSNumber]
// From the userInfo, let's extract the raw NSNumber using lookup
let reason = userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]
// Convert the NSNumber into an MPMovieFinishReason enum. Note that it's
// 0215 and I'm on ambien so I don't care about the force unwrapping that
// should be optional unwrapping
let finishReason = MPMovieFinishReason.fromRaw(reason!.integerValue)
Many problems can be broken down into much simpler problems if you just take it a single step at a time. Modern compilers aren't even too bothered by it as the can recognize when variables are no longer in use and do proper reclamation. Also note using let instead of var for those temporary values since they shouldn't be reused anyway.
Try the type-casting function Int()
ie.,
var finishReason = Int(notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]) as MPMovieFinishReason;

Objective-C - Using Enum identifiers as string [duplicate]

This question already has answers here:
Convert Objective-C enum constants to string names
(2 answers)
Closed 8 years ago.
The enumeration below is used in several places in a BMI tool :
typedef NS_ENUM (NSInteger, BMIStatus) {
Malnutrition = 1,
Anorexia = 2,
Thinness = 3,
Normal = 4,
Overweight = 5,
Obesity = 6,
Morbid = 7
};
Is there a trick to use "Malnutrition" as a string ? Considering I have an image named "Malnutrition.png" that I want to load with a classical ImageNamed, and without using an intermediary array storing [1] => #"Malnutrition" for example.
My idea would be to use a kind of [UIImage imageNamed:[NSString stringWithFormat:#"%e", Malnutrition]] where %e leads to the enum identifier instead of the associated value.
Thanks.
Unfortunately this is just not possible using Objective-C. However, it is in Swift if you can use Swift instead.
This is historically handled in Apple's code with NSString constants. For example:
UIKIT_EXTERN NSString *const NSFontAttributeName NS_AVAILABLE_IOS(6_0);
If you need to map between the int value and the NSString value, you will need to write a mapping function.
Also, do make sure to prefix your enums and string constants!
If you want a string for debugging purposes, add a method - (NSString*) stringFromBMIStatus, with a switch statement returning various strings, and a default printing the numeric value for unexpected input.
If you want a string that gives you the name of an NSImage for each enum value, add a method - (NSString*) imageNameFromBMIStatus, probably returning nil for unexpected input.

Resources