xcode beta bridgeToObjectiveC alternative? - objective-c

I am currently making a calculator app and I need to use the bridgeToObjectiveC code but that is no longer available in the current beta version of xcode 6. This is the code I need to write:
#IBAction func btnAdditionCalculate(sender: AnyObject) {
let firstNumber = txtAdditionFirst.text.bridgeToObjectiveC().floatValue
let secondNumber = txtAdditionSecond.text.bridgeToObjectiveC().floatValue
let answer = firstNumber + secondNumber
var answerFormat: NSString = NSString(format: "%0.0f", answer)
labelAdditionAnswer.text = " \(answerFormat)"
}
I know I need to use NSString and I tried to do this but it doesn't work:
#IBAction func btnAdditionCalculate(sender: AnyObject) {
let firstNumber = (txtAdditionFirst.text as NSString).floatValue
let secondNumber = (txtAdditionSecond.text as NSString).floatValue
let answer = firstNumber + secondNumber
var answerFormat: NSString = NSString(format: "%0.0f", answer)
labelAdditionAnswer.text = " \(answerFormat)"
}
Thanks for all your help
edit 8/15
When I run the code on the iOS simulator, the app loads and I am able to put numbers into the number 1 and number 2 slot, however when I press calculate xcode highlights the let firstNumber line and says Thread 1:EXC_BAD_INSTRUCTION and this is the error message I get:
2014-08-15 16:14:53.019 Innovative Calculator p1[578:8032] Can't find keyplane that supports type 4 for keyboard iPhone-Portrait-NumberPad; using 3876877096_Portrait_iPhone-Simple-Pad_Default
2014-08-15 16:14:55.425 Innovative Calculator p1[578:8032] Can't find keyplane that supports type 8 for keyboard iPhone-Portrait-DecimalPad; using 1425143906_Portrait_iPhone-Simple-Pad_Default
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)

If your code is halting on let firstNumber = ... with a nil optional conversion, it means you are incorrectly assuming that either txtAdditionFirst or textAdditionFirst.text is not nil. UITextField makes assurances that .text is never nil so the best bet is that you failed to wire up txtAdditionFirst in Interface Builder.
You can verify this by checking the variable definition to see it it is filled in or set a breakpoint above the line and see if it is nil.
If you have a valid reason for the variable to be nil, you have a couple options for doing this:
// The long way (mutable)
var firstNumber:Float = 0
if let value = textField?.text {
firstNumber = (value as NSString).floatValue
}
// The short way (not mutable)
let firstNumber = (textField?.text? as NSString?)?.floatValue ?? 0.0
The first way sets the number to zero initially then uses optional chaining to test if textField and textField.text are not nil. The second way uses optional chaining the whole way to get an optional float value, then unwraps it if not nil or falls back to 0.

Related

Swift parameters are not visible inside of Objective C Function?

Some background: I'm building an app in react native that uses an Objective C library written about 6 or 7 years ago, maybe older. I'm writing swift code that has been set up to send callbacks to the react native application in JS. I have this function that I'm trying to use:
token = service.getUserToken(server,
port: P2PFunctions.tls_port,
appId: P2PFunctions.appID,
appSecret: P2PFunctions.appSecret,
phone: P2PFunctions.phone,
token: nil, errcode: errCode, errmsg: nil);
callback(["\(token!)"]);
And this is its definition:
- (NSInteger)getUserToken:(NSString*)ip_In port:(NSInteger)port_In appId:
(NSString*)appId_In appSecret:(NSString*)appSecret_In phone:
(NSString*)phoneNum_In token:(NSString**)accessTok_Out errcode:
(NSString**)strErrCode_Out errmsg:(NSString**)errMsg_Out;
These are the types I'm using (EDIT: I changed them from private to public, and they still are not being recognized):
The problem is, I'm getting nil back from the function. I believe I'm getting an HTTP response that is empty, and I notice that inside the debugger when I step to the Objective C function, I see nil for all my parameters inside of the Objective C function. I think... it is that I'm not passing the correct type. Or my Swift parameters are not visible in Objective C's memory space. If it is expecting an (NSString *), should I be passing a String?
How do I pass the correct types from Swift to Objective C? What would I change in my function call? Are my parameter types okay? I cannot edit the original Objective C library. They share a common memory space for all variables in the entire program, right?
Thank you so much!
I just ran this successfully:
// the objc part
#interface Test : NSObject
- (NSInteger)getUserToken:(NSString*)ip_In
port:(NSInteger)port_In
appId:(NSString*)appId_In
appSecret:(NSString*)appSecret_In
phone:(NSString*)phoneNum_In
token:(NSString* _Nonnull * _Nonnull)accessTok_Out
errcode:(NSString* _Nonnull * _Nonnull)strErrCode_Out
errmsg:(NSString* _Nonnull * _Nonnull)errMsg_Out;
#end
#implementation Test
- (NSInteger)getUserToken:(NSString*)ip_In
port:(NSInteger)port_In
appId:(NSString*)appId_In
appSecret:(NSString*)appSecret_In
phone:(NSString*)phoneNum_In
token:(NSString**)accessTok_Out
errcode:(NSString**)strErrCode_Out
errmsg:(NSString**)errMsg_Out {
*accessTok_Out = #"Token";
*strErrCode_Out = #"OK";
*errMsg_Out = #"msg";
return 42;
}
#end
// and the swift part
let t = Test()
var token: NSString = "t"
var errcode: NSString = "c"
var errmsg: NSString = "m"
let result = t.getUserToken("ip", port: 1,
appId: "2", appSecret: "3", phone: "4",
token: &token, errcode: &errcode, errmsg: &errmsg)
print(result)
and it works as expected.
Maybe this gives you a hint as to what's different in your situation.
So, after spending a week or so on this problem, I realize that it was not that the parameters are not actually being passed, but that the debugger is just not displaying the values of those parameters, at least on the main thread. Because I was getting the error code, I thought that something had to be wrong with the way I called the function - but actually, the function call is fine, the variables just didn't appear in the debugger for some reason:
Variables above appear to be nil - but in fact, they do have values.

Determine window position on macOS using swift (or Objective-C)

I am looking for a way to determine the position and dimension of a window that is not part of my application in macOS using Swift (prefered) or Objective-C to program an overlay/hud.
Is it possible to read a list of all processes and start from there or is there something similar to the getWindowHandle() function in the Windows api?
Take a look at CGWindowListCopyWindowInfo:
import CoreGraphics
if let windowList = CGWindowListCopyWindowInfo([.optionAll], kCGNullWindowID) as? [[String: AnyObject]] {
for window in windowList {
let number = window[kCGWindowNumber as String]!
let bounds = CGRect(dictionaryRepresentation: window[kCGWindowBounds as String] as! CFDictionary)!
let name = window[kCGWindowName as String] as? String ?? ""
print("number = \(number), name = \(name), bounds = \(bounds)")
}
} else {
print("Can't get window list")
}
The function returns an array of CFDictionary, which the code above bridged into [String: AnyObject] for easy working in Swift. Here are required and optional keys for the dictionaries. The keys are all defined as CFString you must bridge them into String.

Cannot invoke 'global' with an argument list of type '(Int, Int)'

I am on the process of migrating old swift code to Swift 3.0, and at times via Swift 2.3.
On the way I made some wrong actions, leading to a mix up of different versions here and there.
That said I must now fall back on my feet and make things work.
Here some code I have:
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = DispatchQueue.global(Int(qualityOfServiceClass.rawValue), 0)
backgroundQueue.asynchronously(execute: {
self.getAppData()
self.waitingForAppData = false
self.busyWithAppDataAccess = false
})
And here is an error message I get, relative to the code above:
Cannot invoke 'global' with an argument list of type '(Int, Int)'
Does anyone know what the problem is here?
I cannot find a matching signature for your use of the global method. I'm assuming that the second Int is suppose to represent the relative priority.
Since global(priority:) has been deprecated. Here is how you could update your code for Swift 3:
let backgroundQueue = DispatchQueue.global(qos: .background)
backgroundQueue.async {
self.getAppData()
self.waitingForAppData = false
self.busyWithAppDataAccess = false
}
You could also do this to keep relative priority in the picture. However, it doesn't seem to be relevant anymore when using Swift 3.
let qos = DispatchQoS(qosClass: .background, relativePriority: 0)
let backgroundQueue = DispatchQueue.global(qos: qos.qosClass)
backgroundQueue.async {
self.getAppData()
self.waitingForAppData = false
self.busyWithAppDataAccess = false
}

How do you compare NSFontSymbolicTraits and NSFontBoldTrait in Swift?

I'm trying to bitwise compare NSFontSymbolicTraits and NSFontBoldTrait in Swift.
In Objective-C it'd be done like this:
BOOL isBold = (fontDescriptorSymbolicTraits & UIFontDescriptorTraitBold);
So I'm thinking it should be this in Swift:
let isBold:Bool = font.fontDescriptor.symbolicTraits & NSFontBoldTrait
...However that results in the following error:
Cannot invoke '&' with an argument list of type '(NSFontSymbolicTraits, Int)'
Anyone know what I'm missing? Any help would be greatly appreciated.
Notes:
I've Googled like a madman and spend hours trying to find a solution and read through the documentation for NSFontDescriptor and the other NSFont-related classes.
I don't know Obj-C very well so I don't know if it's because the NSFont*Trait constants are implemented differently in Swift? Not even sure if that's the case.
In Swift, NSFontSymbolicTraits is a type alias for UInt32. So to check for the existence of a particular trait, you need to compare the result of your bitwise & to zero. Unfortunately, the individual constraints have been imported as type Int, so you also need to convert them to the right type:
let isBold = 0 != (font.fontDescriptor.symbolicTraits & NSFontSymbolicTraits(NSFontBoldTrait))
If you wanted to do this in iOS (instead of OS X), UIFont has a different implementation. UIFontDescriptorSymbolicTraits is a RawOptionSetType, so you compare the result of your & with nil:
let isBold = nil != (font.fontDescriptor().symbolicTraits & UIFontDescriptorSymbolicTraits.TraitBold)
Update based on Nates answer, since his iOS version doesn't work for me. Which most likely is due to the changes in swift since 2014.
iOS, Swift 4(.2):
extension UIFontDescriptor {
var isBold: Bool {
if 0 == (symbolicTraits.rawValue & UIFontDescriptor.SymbolicTraits.traitBold.rawValue) {
return false
} else {
return true
}
}
}
In Swift 5.4 on iOS.
let isBold = font.fontDescriptor.symbolicTraits.contains(.traitBold)

iOS: How to convert UIViewAnimationCurve to UIViewAnimationOptions?

The UIKeyboardAnimationCurveUserInfoKey has a UIViewAnimationCurve value. How do I convert it to the corresponding UIViewAnimationOptions value for use with the options argument of +[UIView animateWithDuration:delay:options:animations:completion:]?
// UIView.h
typedef enum {
UIViewAnimationCurveEaseInOut, // slow at beginning and end
UIViewAnimationCurveEaseIn, // slow at beginning
UIViewAnimationCurveEaseOut, // slow at end
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
// ...
enum {
// ...
UIViewAnimationOptionCurveEaseInOut = 0 << 16, // default
UIViewAnimationOptionCurveEaseIn = 1 << 16,
UIViewAnimationOptionCurveEaseOut = 2 << 16,
UIViewAnimationOptionCurveLinear = 3 << 16,
// ...
};
typedef NSUInteger UIViewAnimationOptions;
Obviously, I could create a simple category method with a switch statement, like so:
// UIView+AnimationOptionsWithCurve.h
#interface UIView (AnimationOptionsWithCurve)
#end
// UIView+AnimationOptionsWithCurve.m
#implementation UIView (AnimationOptionsWithCurve)
+ (UIViewAnimationOptions)animationOptionsWithCurve:(UIViewAnimationCurve)curve {
switch (curve) {
case UIViewAnimationCurveEaseInOut:
return UIViewAnimationOptionCurveEaseInOut;
case UIViewAnimationCurveEaseIn:
return UIViewAnimationOptionCurveEaseIn;
case UIViewAnimationCurveEaseOut:
return UIViewAnimationOptionCurveEaseOut;
case UIViewAnimationCurveLinear:
return UIViewAnimationOptionCurveLinear;
}
}
#end
But, is there an even easier/better way?
The category method you suggest is the “right” way to do it—you don’t necessarily have a guarantee of those constants keeping their value. From looking at how they’re defined, though, it seems you could just do
animationOption = animationCurve << 16;
...possibly with a cast to NSUInteger and then to UIViewAnimationOptions, if the compiler feels like complaining about that.
Arguably you can take your first solution and make it an inline function to save yourself the stack push. It's such a tight conditional (constant-bound, etc) that it should compile into a pretty tiny piece of assembly.
Edit:
Per #matt, here you go (Objective-C):
static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve)
{
switch (curve) {
case UIViewAnimationCurveEaseInOut:
return UIViewAnimationOptionCurveEaseInOut;
case UIViewAnimationCurveEaseIn:
return UIViewAnimationOptionCurveEaseIn;
case UIViewAnimationCurveEaseOut:
return UIViewAnimationOptionCurveEaseOut;
case UIViewAnimationCurveLinear:
return UIViewAnimationOptionCurveLinear;
}
}
Swift 3:
extension UIViewAnimationOptions {
init(curve: UIViewAnimationCurve) {
switch curve {
case .easeIn:
self = .curveEaseIn
case .easeOut:
self = .curveEaseOut
case .easeInOut:
self = .curveEaseInOut
case .linear:
self = .curveLinear
}
}
}
In Swift you can do
extension UIViewAnimationCurve {
func toOptions() -> UIViewAnimationOptions {
return UIViewAnimationOptions(rawValue: UInt(rawValue << 16))
}
}
An issue with the switch based solution is that it assumes no combination of options will be ever passed in. Practice shows though, that there may be situations where the assumption doesn't hold. One instance I found is (at least on iOS 7) when you obtain the keyboard animations to animate your content along with the appearance/disappearance of the keyboard.
If you listen to the keyboardWillShow: or keyboardWillHide: notifications, and then get the curve the keyboard announces it will use, e.g:
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
you're likely to obtain the value 7. If you pass that into the switch function/method, you won't get a correct translation of that value, resulting in incorrect animation behaviour.
Noah Witherspoon's answer will return the correct value. Combining the two solutions, you might write something like:
static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve)
{
UIViewAnimationOptions opt = (UIViewAnimationOptions)curve;
return opt << 16;
}
The caveat here, as noted by Noah also, is that if Apple ever changes the enumerations where the two types no longer correspond, then this function will break. The reason to use it anyway, is that the switch based option doesn't work in all situations you may encounter today, while this does.
iOS 10+
Swift 5
A Swift alternative to converting UIView.AnimationCurve to UIView.AnimationOptions, which may not even be possible, is to use UIViewPropertyAnimator (iOS 10+), which accepts UIView.AnimationCurve and is a more modern animator than UIView.animate.
Most likely you'll be working with UIResponder.keyboardAnimationCurveUserInfoKey, which returns an NSNumber. The documentation for this key is (Apple's own notation, not mine):
public class let keyboardAnimationCurveUserInfoKey: String // NSNumber of NSUInteger (UIViewAnimationCurve)
Using this approach, we can eliminate any guesswork:
if let kbTiming = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber, // doc says to unwrap as NSNumber
let timing = UIView.AnimationCurve.RawValue(exactly: kbTiming), // takes an NSNumber
let curve = UIView.AnimationCurve(rawValue: timing) { // takes a raw value
let animator = UIViewPropertyAnimator(duration: duration, curve: curve) {
// add animations
}
animator.startAnimation()
}