I just switched to Swift 1.2 and now it does no longer like
let v = optionalVal as SomeThing ?? default
Apple's release notes do not tell anything about it. Sure I can use if nil but the ?? was handy.
You just have to put it inside a parentheses and forced cast now needs an exclamation mark as! or as?:
let v = ( optionalVal as? SomeThing ) ?? default
UPDATE:
Apple has just released today Xcode6.3 Beta 2 and seems that it is not needed anymore:
Swift Compiler
This expression now parses correctly, without need for parentheses:
dict[someKey] as? Int ?? 5
Related
The recent #available addition to Objective-C can be handy but is apparently quite short-sighted..
Any app being around for more than a couple weeks will need to target e.g. multiple macOS versions.
Unfortunately the only #available construct for explicitly targeting older OS releases that appears to work (as of Xcode up to and including 12) is this cumbersome
if (#available(macOS 11.0, *))
{
// all hunky-dory: no-op
}
else
{
// legacy work arounds
...
...
}
Using just the else branch with a negation emits the dreaded #available does not guard availability here compiler warning..
Is there any way to simplify this ghastly code and remove the dummy no-op branch?
You could use the preprocessor, define a macro:
#define ifNotAvailable(A, B) if (#available(A, B)) {} else
and then write your code as:
ifNotAvailable(macOS 11.0, *)
{
// legacy work arounds
...
...
}
Whether this is less "ghastly" is in the eye of the beholder, YMMV!
I have an objective-c method:
-(DeviceVar *)var:(NSString*)valid
In objective-c I simple use it as:
DeviceVar* rtc = [device var:#"rtc"];
But in swift I have a problem using this method:
let rtc = device.var("etc")
as var is a keyword I guess, so my question is how to make it work.
You can always enclose a reserved word in backticks if you need to
use it as a method name (see for example
Use reserved keyword a enum case):
let rtc = device.`var`("etc")
If you have write access to the Objective-C header files then another
option is to define a different method name for Swift
(compare Swift and Objective-C in the Same Project in the
"Using Swift with Cocoa and Objective-C " reference):
-(DeviceVar *)var:(NSString*)valid NS_SWIFT_NAME(deviceVar(_:));
which can then be called from Swift as
let rtc = device.deviceVar("etc")
I've NSData category for base64Encoding written in Obj-C.
#interface NSData (Base64)
+ (NSData*)dataFromBase64String:(NSString*)aString;
- (NSString*)base64EncodedString;
#end
Initially in Swift 2.2, I was using it as:
let dbPassData = NSData(fromBase64String: password)
Now, in Swift 3, there is no any NSData, instead have Data.
How to use these category methods in swift 3?
After converting project to Swift 3, it converted that line as:
let dbPassData = Data(fromBase64String: password)
which yields an error:
Argument labels do not match any available overloads
In Swift 3, Apple also renamed some initializers and Xcode 8 beta makes some mistakes when converting from 2.2 to 3.
In that case, the initializer was renamed to this:
let data = Data(base64Encoded: "hello")
When you are facing this or similar problems, you can start typing ClassName( and look at the proposed initializers. In most cases, you'll find the new one easily.
NSData is still available in Swift 3, just like String has been coexisting with NSString. You need to ignore the compiler's advice in this case:
let dbPassData = NSData(fromBase64String: password) as Data
This assumes you don't want to port your Base64 category to Swift or you want to maintain compatibility with Obj-C code. Swift 3's Data also offer built-in Base 64 conversion:
let dbPassData = Data(base64Encoded: password)
let base64String = data.base64EncodedString(options: [.lineLength64Characters, .endLineWithLineFeed])
I read Mike Ash's Objective-C pitfalls page, and now I'm paranoid about implicitly casting variables of type id to BOOL.
Assume I have a 'dangerous' pointer with the lower bits zeroed out, so that casting it to a BOOL would produce NO even though it points to a valid object. Let's call this 'dangerous' pointer foo.
How do simple if statements work?
Does the if statement cast foo to a BOOL when it evaluates the condition? Or does it cast to a pure boolean?
if( foo )
{
[myArray addObject:foo];
}
Does it work the same way in ternary expressions?
// Does this break when foo is 'dangerous'?
self.titleLabel.hidden = foo ? YES : NO ;
Or will the ternary only break if I do this:
// I'm pretty sure it breaks now
self.titleLabel.hidden = ((BOOL)foo) ? YES : NO ;
I feel like I'm missing something basic about logical operations in C, and how they relate to BOOL. Please help enlighten me.
When does Objective-C implicitly cast to BOOL?
Never, since this phrase is semantically incorrect. A cast is, by definition, explicit. What you're asking about is called an "implicit type conversion" (coercion - thanks, Josh!).
Also, strictly speaking, the conditionals in Objective-C are not special: they're inherited from C. So expressions, when needed to evaluated as booleans, aren't treated as BOOL, but as int.
// Does this break when foo is 'dangerous'?`
self.titleLabel.hidden = foo ? YES : NO;
No, it doesn't. (expr) when used as a "boolean" expression, is equivalent to (expr != 0). No risks here.
// I'm pretty sure it breaks now
self.titleLabel.hidden = ((BOOL)foo) ? YES : NO;
Now this can break, since BOOL is just typedeffed to signed char, which is 8 bit on iOS. Thus, if foo, which is a (32-bit) pointer, gets truncated to 8 bits, and the lower 8 bits of the pointer were all zero, but the pointer itself wasn't nil, this will incorrectly report false. You don't want to do this superfluous, ugly and dangerous cast. If you want to be more explicit, write
var = (obj != nil) ? valueOne : valueTwo;
instead.
There's no casting in your first two samples. Writing if(expr) is equal to writing if(expr == 0).
Your third example might indeed break in some cases, as described in the article you refer to.
In the past, when I've had to compile something on Snow Leopard (or any older Mac OS X) and use some #selector that's available on Lion and higher, I've done something like this:
if([foo respondsToSelector:#selector(awesomeLionSelector)]) {
[foo awesomeLionSelector]
}
And this has worked great. So, now I'm trying to do the same thing for a new method that returns a float, but the following line:
float f = [foo awesomeLionSelectorWhichReturnsFloat];
throws a compile error: "cannot convert 'objc_object*' to 'float' in assignment". This is because the compiler doesn't know about the method and has to assume a return type of 'id'.
What is the proper way to fix this?
EDIT: according to John Caswell comment, the correct runtime function to use is objc_msgSend_fpret(), which returns a double.
I think you should try your way with: objc_msgSend_stret():
objc_msgSend_stret
Sends a message with a data-structure return value to an instance of a class.
void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...)
Something like:
objc_msgSend_stret(&myFloatReturnValue, foo, #selector(awesomeLionSelector));
Not the most elegant fix, but a fix none the less.
#ifdef SnowLeopard
float f = [foo awesomeLionSelectorWhichReturnsFloat];
#endif