How to Update addTarget method to swift 4 [duplicate] - ios

This question already has answers here:
How can I deal with #objc inference deprecation with #selector() in Swift 4?
(5 answers)
Closed 5 years ago.
when update project code to swift 4 get some error for add.target method
how i can fix this error?
//swift3
var chatLogController: ChatLogController? {
didSet {
sendButton.addTarget(chatLogController, action: #selector(ChatLogController.handleSend), for: .touchUpInside)
uploadImageView.addGestureRecognizer(UITapGestureRecognizer(target: chatLogController, action: #selector(ChatLogController.handleUploadTap)))
}
}

The hint message is telling you what to do, add #obj before declaring your function.
#objc func handleSend(_ sender: UIGestureRecognizer){
...
}
The reason is because:
In Objective-C, a selector is a type that refers to the name of an
Objective-C method. In Swift, Objective-C selectors are represented by
the Selector structure, and can be constructed using the #selector
expression. To create a selector for a method that can be called from
Objective-C, pass the name of the method, such as
#selector(MyViewController.tappedButton(_:)). To construct a selector for a property’s Objective-C getter or setter method, pass the
property name prefixed by the getter: or setter: label, such as
#selector(getter: MyViewController.myButton).
Read more here, at Apples documentation.

Related

When adding target to button, why must target action be an objc func? [duplicate]

This question already has an answer here:
Why is the #objc tag needed to use a selector?
(1 answer)
Closed 4 years ago.
Why does a button action need to be a function declared with objc? Im curious as to what the difference is between an objc func and a func and why a button can't simply reference it's action to a func.
Edit: I am using Swift. Thank you very much for your time.
var thisButton: UIButton = {
let button = UIButton(type: .system)
button.addTarget(self, action: #selector(thisFunction), for: .touchUpInside)
return button
}()
#objc func thisFunction() {
//stuff
}
Swift generates code that is only available to other Swift code, but
if you need to interact with the Objective-C runtime – all of UIKit,
for example – you need to tell Swift what to do.
That’s where the #objc attribute comes in: when you apply it to a
class or method it instructs Swift to make those things available to
Objective-C as well as Swift code. So, any time you want to call a
method from a UIBarButtonItem or a Timer, you’ll need to mark that
method using #objc so it’s exposed – both of those, and many others,
are Objective-C code.
Refer :
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html

Selector vs Action Swift 4

New to Swift. I have two snippets below:
NotificationCenter.default.addObserver(self,
selector:#selector(ViewController.notificationReceived),
name: Notification.Name(rawValue: name), object: nil)
#objc func notificationReceived(notification:Notification){
let x = notification.userInfo!
print("\(x["name"]!)")
}
and finally
let x:UITapGestureRecognizer = UITapGestureRecognizer(target: self,
action: #selector(tapped))
self.addGestureRecognizer(x)
func tapped(){
print("tapped")
self.delegate!.theViewTapped()
}
Why is it that for the notificationCenter? I am supposed to provide the #objc tag for the selector parameter but not for the UITapGestureRecognizer action parameter?
What exactly is the difference between Selector and Action in Swift?
Check this proposal for Swift 4:
SE-0160 Limiting #objc inference
According to the description in the proposal, your second code snippet also needs #objc.
In fact, Swift 4 compiler bundled with Xcode 9 beta2 generates this error for the line using #selector(tapped):
error: argument of '#selector' refers to instance method 'tapped()'
that is not exposed to Objective-C
note: add '#objc' to expose this instance method to Objective-C
Maybe your second is a little bit too old to use with Swift 4. You better think all methods invoked through selector need #objc attribute.

Swift 4 CADisplayLink without #objc [duplicate]

I'm trying to convert my project's source code from Swift 3 to Swift 4. One warning Xcode is giving me is about my selectors.
For instance, I add a target to a button using a regular selector like this:
button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)
This is the warning it shows:
Argument of '#selector' refers to instance method 'myAction()' in 'ViewController' that depends on '#objc' attribute inference deprecated in Swift 4
Add '#objc' to expose this instance method to Objective-C
Now, hitting Fix on the error message does this to my function:
// before
func myAction() { /* ... */ }
// after
#objc func myAction() { /* ... */ }
I don't really want to rename all of my functions to include the #objc mark and I'm assuming that's not necessary.
How do I rewrite the selector to deal with the deprecation?
Related question:
The use of Swift 3 #objc inference in Swift 4 mode is deprecated?
The fix-it is correct – there's nothing about the selector you can change in order to make the method it refers to exposed to Objective-C.
The whole reason for this warning in the first place is the result of SE-0160. Prior to Swift 4, internal or higher Objective-C compatible members of NSObject inheriting classes were inferred to be #objc and therefore exposed to Objective-C, therefore allowing them to be called using selectors (as the Obj-C runtime is required in order to lookup the method implementation for a given selector).
However in Swift 4, this is no longer the case. Only very specific declarations are now inferred to be #objc, for example, overrides of #objc methods, implementations of #objc protocol requirements and declarations with attributes that imply #objc, such as #IBOutlet.
The motivation behind this, as detailed in the above linked proposal, is firstly to prevent method overloads in NSObject inheriting classes from colliding with each other due to having identical selectors. Secondly, it helps reduce the binary size by not having to generate thunks for members that don't need to be exposed to Obj-C, and thirdly improves the speed of dynamic linking.
If you want to expose a member to Obj-C, you need to mark it as #objc, for example:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(foo), for: .touchUpInside)
}
#objc func foo() {
// ...
}
}
(the migrator should do this automatically for you with selectors when running with the "minimise inference" option selected)
To expose a group of members to Obj-C, you can use an #objc extension:
#objc extension ViewController {
// both exposed to Obj-C
func foo() {}
func bar() {}
}
This will expose all the members defined in it to Obj-C, and give an error on any members that cannot be exposed to Obj-C (unless explicitly marked as #nonobjc).
If you have a class where you need all Obj-C compatible members to be exposed to Obj-C, you can mark the class as #objcMembers:
#objcMembers
class ViewController: UIViewController {
// ...
}
Now, all members that can be inferred to be #objc will be. However, I wouldn't advise doing this unless you really need all members exposed to Obj-C, given the above mentioned downsides of having members unnecessarily exposed.
As Apple Official Documentation. you need to use #objc to call your Selector Method.
In Objective-C, a selector is a type that refers to the name of an
Objective-C method. In Swift, Objective-C selectors are represented by
the Selector structure, and can be constructed using the #selector
expression. To create a selector for a method that can be called from
Objective-C, pass the name of the method, such as
#selector(MyViewController.tappedButton(sender:)). To construct a selector for a property’s Objective-C getter or setter method, pass
the property name prefixed by the getter: or setter: label, such as
#selector(getter: MyViewController.myButton).
As of, I think Swift 4.2, all you need to do is assign #IBAction to your method and avoid the #objc annotation.
let tap = UITapGestureRecognizer(target: self, action: #selector(self.cancel))
#IBAction func cancel()
{
self.dismiss(animated: true, completion: nil)
}
As already mentioned in other answers, there is no way to avoid the #objc annotation for selectors.
But warning mentioned in the OP can be silenced by taking following steps:
Go to Build Settings
Search for keyword #objc
Set the value of Swift 3 #objc interface to Off
below is the screenshot that illustrates the above mentioned steps:
Hope this helps
If you need objective c members in your view controller just add #objcMembers at the top of the view controller. And you can avoid this by adding IBAction in your code.
#IBAction func buttonAction() {
}
Make sure to connect this outlet in storyboard.

Using Swift3 Function Parameter in Selector [duplicate]

This question already has answers here:
Passing closure in swift as parameter to be used by selector in function
(2 answers)
Closed 5 years ago.
I'm attempting to pass in a function to add as a target to a UIButton in Swift3, but I'm getting the following error:
Argument of '#selector' does not refer to an '#objc' method, property, or initializer
Here's my code:
func updateButtonAction(_ action: () -> (), button: UIButton) {
button.addTarget(self, action: #selector(customFunction(action)), for: .touchUpInside)
}
func customFunction(_ function: () -> ()) {
function()
}
I'm wrapping a passed in function in another function so I know the class that owns it.
I think the issue is that first class functions in Swift aren't supported in Objective-C and the selector keyword only uses Objective-C functions.
Does anyone know a way around this?
Selector is just a fancy synonym for method name. It's not a closure or a function. It's the name of a method on an Obj-C object. Technically, a selector is a string.
customFunction(action) is not a selector. The selector is customFunction(_:).
You are trying to pass an implicit parameter to the selector. That's not how selectors work. You are not calling the method, you are just telling the UIButton which method it should call - UIButton is calling the method and the button is also specifying the parameters.
If you need to pass some code to the event handler, save it on your instance.
var function: (() -> Void)?
func customFunction() {
self.function?()
}
In general it's a good idea to have only one event handler and not change it.
Also note that your example has no need for first class functions.

Proper way to use selectors in Swift

I'm creating a view programatically, and adding a function so the action responds to the UIControlEvents.TouchUpInside event:
button.addTarget(self, action: action, forControlEvents:
UIControlEvents.TouchUpInside)
So, by going into the documentation I've added this action as a selector:
#selector(ViewController.onRegularClick)
XCode then complaints about:
Argument of #selector refers to a method that is not exposed to
Objective-C
So I have to set up the handler function with:
#objc func onRegularClick(sender: UIButton)
Can some one please put this noob on the right direction by guiding me to the documentation, or even give a short explanation, on:
why can't I no longer pass simply the function name String to the action?
how is the proper way to implement this following the Swift Way? Using the Selector class?
why do we need to pass the #objc keyword and how it affects the function?
Thank you!
why can't I no longer pass simply the function name String to the action?
Using strings for selectors has been deprecated, and you should now write #selector(methodName)instead of "methodName". If the methodName() method doesn't exist, you'll get a compile error – another whole class of bugs eliminated at compile time. This was not possible with strings.
how is the proper way to implement this following the Swift Way? Using the Selector class?
You did it the right way:
button.addTarget(self, action: #selector(ClassName.methodName(_:)), forControlEvents: UIControlEvents.TouchUpInside)
why do we need to pass the #objc keyword and how it affects the function?
In Swift the normal approach is to bind method's calls and method's bodies at compile time (like C and C++ do). Objective C does it at run time. So in Objective C you can do some things that are not possible in Swift - for example it is possible to exchange method's implementation at run time (it is called method swizzling). Cocoa was designed to work with Objective C approach and this is why you have to inform the compiler that your Swift method should be compiled in Objective-C-like style. If your class inherits NSObject it will be compiled ObjC-like style even without #objc keyword.
Well, it is called evolution
When there are some arguments in the method, you should declare the selector as:
let selector = #selector(YourClass.selector(_:))
You can type only #selector(selector(_:)) if the selector is in the same class of the caller. _: means that accept one parameter. So, if it accept more parameters, you should do something like: (_:, _:) and so on.
I found out that the #objc is needed only when the function is declared as private or the object doesn't inherit from NSObject
1: Currently you can, but it will create a deprecated warning. In Swift
3 this will be an error, so you should fix it soon. This is done
because just using a String can not be checked by the compiler if
the function really exists and if it is a valid Objective C function
which can be resolved dynamically during runtime.
2: Do it in this way:
button.addTarget(self, action: #selector(MyViewControllerClass.buttonPressed(_:)), forControlEvents: UIControlEvents.TouchUpInside)
3: Usually you not have to use the #objc attribute. I assume your class ViewController is (for any reason) not derived from UIViewController. If it derives from UIViewController is inherits also the needed ObjC behavior for calling selectors on functions.
For swift3.0 just do like below code :
yourButton.addTarget(self, action: #selector(yourButtonPressed), for: .touchUpInside)
and yourButtonPressed method
#IBAction func yourButtonPressed(sender:UIButton) {
// Do your code here
}
Everyones's answers are perfect but I have a better approach. Hope you gonna like it.
fileprivate extension Selector {
static let buttonTapped =
#selector(ViewController.buttonTapped(_:))
}
...
button.addTarget(self, action: .buttonTapped, for: .touchUpInside)
here in this file private will help to show buttonTapped only in file.
Programmatically
button.addTarget(self, action: #selector(returnAction), for: .touchUpInside)
// MARK: - Action
#objc private func returnAction(sender: UIButton) {
print(sender.tag)
}

Resources