Call Swift function with multiple arguments form Objective-C syntax - ios

From Objective-C, to call a a Swift function with one argument you have to use bracket syntax and do something I've always found confusing: Add the word "With" before the argument and capitalize the argument label as in:
//swift extension
#objc public func getImageCalled(name: String?) -> UIImage? {
}
//Called from Objective-C
ImageClass *imgObject = [[ImageClass alloc] init];
UIImage * img = [imgObject getImageCalledWithName:myname];
How do you do this with two arguments as in:
#objc public func getImageCalled(name: String?,width:Int?) -> UIImage? {
}
[imgObject getImageCalledWithName:myname WithWidth:30]; is not working for me.

You can try
[imgObject getImageCalledWithName:myname width:30];

Related

Swift Xcode getting <<error type>> from generic type parameter inside closure

Swift's way to create a red button:
let button: UIButton = {
let button = UIButton()
button.backgroundColor = .red
return button
}()
With my extension (kotlin's approach):
let button: UIButton = UIButton().apply(){ this in
this.backgroundColor = UIColor.red
}
There are two problems with my extension, first one is that Xcode doesn't recognize the closure parameter "this", it shows << error type >>, so I don't have autocomplete (although it's working), and the second one is that I would like to be able to infer the generic type since I'm already instantiating it.
The extension:
extension NSObject {
func apply<T>(_ closure: (T) -> ()) -> T {
let this = self as! T
closure(this)
return this
}
}
I hope someone can find a solution for this. Thanks in advance.
The problem here is that Swift cannot infer the type of T. You did not and cannot tell Swift to use the current type as the type of the closure. Self is only available in the result type of methods in a class.
You can do something like this:
func apply<T>(_ obj: T, closure: (T) -> Void) -> T {
closure(obj)
return obj
}
apply(UIButton()) { this in this.backgroundColor = .red }
but I don't think that's what you want. You want the word apply to be in the middle, right?
Unfortunately, I cannot think of how you would achieve that. As an alternative, you can try replacing the word "apply" with an operator. For example, I think --> is quite suitable for this.
infix operator -->
func --> <T>(obj: T, closure: (T) -> Void) -> T {
closure(obj)
return obj
}
let button = UIButton() --> { this in this.backgroundColor = .red }
I'm not sure why it's not working for you. If the extension is inside a class put it outside of the class so it's on its own. If that is how it is already try clearing the errors with the command (command + k), this will reload them. If the error remains it's very confusing because it is working fine for me.

How to handle Protocol Delegate when converting Objective-C to Swift

I'm trying to convert a speech recognition code to Swift, Protocol defined in ViewController.h as:
#interface ViewController : UIViewController<SpeechRecognitionProtocol>
{
NSMutableString* textOnScreen;
DataRecognitionClient* dataClient;
MicrophoneRecognitionClient* micClient;
SpeechRecognitionMode recoMode;
bool isMicrophoneReco;
bool isIntent;
int waitSeconds;
}
I got stuck converting below function at ViewController.h:
micClient = [SpeechRecognitionServiceFactory createMicrophoneClient:(recoMode)
withLanguage:(language)
withKey:(primaryOrSecondaryKey)
withProtocol:(self)];
This function is defined in the SpeechSDK.framework as:
#interface SpeechRecognitionServiceFactory : NSObject
/*
#param delegate The protocol used to perform the callbacks/events upon during speech recognition.
*/
+(MicrophoneRecognitionClient*)createMicrophoneClient:(SpeechRecognitionMode)speechRecognitionMode
withLanguage:(NSString*)language
withKey:(NSString*)primaryOrSecondaryKey
withProtocol:(id<SpeechRecognitionProtocol>)delegate;
#end
this protocol looks like this in my converted ViewController.Swift:
import UIKit
protocol SpeechRecognitionProtocol {
func onIntentReceived(result: IntentResult)
func onPartialResponseReceived(response: String)
func onFinalResponseReceived(response: RecognitionResult)
func onError(errorMessage: String, withErrorCode errorCode: Int)
func onMicrophoneStatus(recording: DarwinBoolean)
func initializeRecoClient()
}
class ViewController: UIViewController, SpeechRecognitionProtocol {
var myDelegate: SpeechRecognitionProtocol?
finally I am calling this function inside ViewController.swift. I am getting following error after withProtocol: cannot convert value of type 'SpeechRecognitionProtocol.Protocol' to expected argument type 'SpeechRecognitionProtocol!' :
func initializeRecoClient() {
let language: String = "en-us"
let path: String = NSBundle.mainBundle().pathForResource("settings", ofType: "plist")!
let settings = NSDictionary(contentsOfFile: path)
let primaryOrSecondaryKey = settings?.objectForKey("primaryKey") as! String
micClient = SpeechRecognitionServiceFactory.createMicrophoneClient(recoMode!,
withLanguage: language,
withKey: primaryOrSecondaryKey,
withProtocol: SpeechRecognitionProtocol)
}
You shouldn't declare the SpeechRecognitionProtocol yourself (not sure you added this just for demonstration purposes or whether your actually have that in your code). SpeechRecognitionProtocol is already declared in SpeechRecognitionService.h and available to Swift - this is the one you need to use.
The object implementing that protocol is ViewController. Assuming your initializeRecoClient is a method of that class, the call would need to look like:
micClient = SpeechRecognitionServiceFactory
.createMicrophoneClient(recoMode!,
withLanguage: language,
withKey: primaryOrSecondaryKey,
withProtocol: self)
The SpeechSDK API didn't choose a particularly good name for that factory method.
The withProtocol argument doesn't take the protocol object itself (as the name suggests), but the object implementing the protocol (obviously).
P.S.: Not sure what SpeechAPI version you use, I had to implement those Swift methods to make ViewController conform to SpeechRecognitionProtocol:
func onPartialResponseReceived(response: String!) {}
func onFinalResponseReceived (response: RecognitionResult) {}
func onError (errorMessage: String!,
withErrorCode errorCode: Int32) {}
func onMicrophoneStatus (recording: Bool) {}
func onIntentReceived (result: IntentResult) {}

Access Private UIKit Function Without Using Bridging Header

Consider the private C function _UICreateScreenUIImage, which returns a UIImage snapshot of the current device screen:
OBJC_EXTERN UIImage *_UICreateScreenUIImage(void) NS_RETURNS_RETAINED;
I can put this in a bridging header and access it in Swift like so:
MyApp-Bridging-Header.h
#import UIKit;
UIImage *_UICreateScreenUIImage(void) NS_RETURNS_RETAINED;
MyClass.swift
let image = _UICreateScreenUIImage()
print(image) // <UIImage: 0x7fc4ba6081c0>, {375, 667}
Is there a way I can access _UICreateScreenUIImage in pure Swift without using a bridging header?
An initial thought was to create an extension on UIImage, but the extension is expecting me to declare the body of the function in the extension:
extension UIImage {
public func _UICreateScreenUIImage(_: Void) -> UIImage // "Expected '{' in body of function declaration"
}
This implementation is flawed anyways, as _UICreateScreenUIImage isn't a method on UIImage.
Is exposing and accessing this method possible in pure Swift?
People seem to be confusing my question with "How do I take a screenshot?" That's not what I'm asking. I'm asking how do I access methods like UIImage *_UICreateScreenUIImage(void); in Swift. It could be any private method, such as +(UIImage *)_deviceSpecificImageNamed:(NSString *)name inBundle:(NSBundle *)bundle; or +(UIImage *)_pu_PhotosUIImageNamed:(NSString *)name;
.
It's a lot easier than you would expect:
#asmname("_UICreateScreenUIImage")
func _UICreateScreenUIImage() -> UIImage
// That's it – go ahead and call it:
_UICreateScreenUIImage()
As it happens, #asmname has actually just been changed in the 2.3 builds to #_silgen_name, so be ready to adjust accordingly:
#_silgen_name("_UICreateScreenUIImage")
func _UICreateScreenUIImage() -> UIImage
To my knowledge, #_silgen_name does not provide resolution of Objective-C methods. For this, there is the evenmore powerful Objective-C runtime API:
let invokeImageNamed: (String, NSTimeInterval) -> UIImage? = {
// The Objective-C selector for the method.
let selector: Selector = "animatedImageNamed:duration:"
guard case let method = class_getClassMethod(UIImage.self, selector)
where method != nil else { fatalError("Failed to look up \(selector)") }
// Recreation of the method's implementation function.
typealias Prototype = #convention(c) (AnyClass, Selector, NSString, NSTimeInterval) -> UIImage?
let opaqueIMP = method_getImplementation(method)
let function = unsafeBitCast(opaqueIMP, Prototype.self)
// Capture the implemenation data in a closure that can be invoked at any time.
return { name, interval in function(UIImage.self, selector, name, interval) }
}()
extension UIImage {
// Convenience method for calling the closure from the class.
class func imageNamed(name: String, interval: NSTimeInterval) -> UIImage? {
return invokeImageNamed(name, interval)
}
}
UIImage.imageNamed("test", interval: 0)
As far as handling NS_RETURNS_RETAINED, this won't be generated for you. Instead, you can use a return type of Unmanaged, and wrap that in a function to your convenience:
#_silgen_name("_UICreateScreenUIImage")
func _UICreateScreenUIImage() -> Unmanaged<UIImage>
func UICreateScreenUIImage() -> UIImage {
return _UICreateScreenUIImage().takeRetainedValue()
}

performSelector with UIControlState

In a piece of code where I dynamically want to fire a method on an UIButton, I want to execute the methods where a UIControlState is supplied.
For example, the code I have now is:
private func setValue(value: AnyObject, forKey: String, forState: UIControlState) {
let methodName = "set" + forKey.capitalizedString + ":forState:"
let sel = Selector(methodName)
if self.respondsToSelector(sel) {
self.performSelector(sel, withObject: value, withObject: forState.rawValue)
}
}
In this example, the forKey contains Title and the forState contains UIControlState.Normal. The value is just a simple string, but is defined as an AnyObject. It accepts an AnyObject, because there are also methods that need an UIImage, etc.
self in this example is an UIButton.
The UIButton does respond to the Selector I created (which is defined as setTitle:forState:), and it fires the performSelector method without any warnings, but still the title of this UIButton isn't updated.
The problem is probably either in the conversion of the value object form AnyObject to String or in the conversion of the forState object, but I am not sure how I could fix this in a generic way.
Is this the correct way to do this from Swift 2, or does someone spot a simple mistake in my theory?
I found the solution after a bit of fooling around. I noticed the methodForSelector method and found out, that using an IMP is the solution for my problem.
private func setValue(value: AnyObject, forKey key: String, forState state: UIControlState) {
let methodName = "set" + key.capitalizedString + ":forState:"
let sel = Selector(methodName)
if self.respondsToSelector(sel) {
typealias setValueForControlStateIMP = #convention(c) (UIView, Selector, AnyObject, UIControlState) -> Void
let methodSignature = self.methodForSelector(sel)
let callback = unsafeBitCast(methodSignature, setValueForControlStateIMP.self)
callback(self, sel, value, state)
}
}

Overloaded functions in swift error [duplicate]

This question already has answers here:
Compiler error: Method with Objective-C selector conflicts with previous declaration with the same Objective-C selector
(6 answers)
Closed 7 years ago.
I am new to swift and have an error in the following code. I have one function with two different parameters. Xcode(version 6) is giving an error on the second definition with a parameter as a function which takes one value.Here is the code:
func performOperation(operation: (Double, Double) -> Double) {
if(operandStack.count >= 2){
displayValue = operation(operandStack.removeLast(), operandStack.removeLast())
enter()
}
}
func performOperation(operation: Double -> Double){
if(operandStack.count >= 1){
displayValue = operation(operandStack.removeLast())
enter()
}
}
Update:
Another solution is add private before method definition (Source):
private func performOperation(operation: (Double, Double) -> Double) {
if(operandStack.count >= 2){
displayValue = operation(operandStack.removeLast(), operandStack.removeLast())
enter()
}
}
private func performOperation(operation: Double -> Double){
if(operandStack.count >= 1){
displayValue = operation(operandStack.removeLast())
enter()
}
}
Original Answer:
Looks like your methods are defined in class that is inherited from some Objective-C class, for example:
class TestClass : NSObject {
func test(a : String) {}
func test(a : UInt) {}
}
Compiler will produce such error:
Method 'test' with Objective-C selector 'test:' conflicts with
previous declaration with the same Objective-C selector.
To fix that you need avoid inheritance from Objective-C class:
class TestClass {
func test(a : String) {}
func test(a : UInt) {}
}
This variant will work correct.
The problem is that Objective-C doesn't support methods overloading, but Swift does. That's why you need create pure Swift class.

Resources