Swift Access Control with Target Selectors

Swift access control with target selectors

you need @objc to expose a private method to objc runtime

@objc private func buttonDidTap(button:UIButton!) {
println(button.char)
}

From Xcode6 beta4 release notes

Declarations marked private are not exposed to the Objective-C runtime
if not otherwise annotated. IB outlets, IB actions, and Core Data
managed properties remain exposed to Objective-C whatever their access
level. If you need a private method or property to be callable from
Objective-C (such as for an older API that uses a selector-based
callback), add the @objc attribute to the declaration explicitly.! !

How to perform selector with a nested function?

instead, you can try below code to achieve your goal

class MyClass {
let button = UIButton()

@objc public func parentFunc(_ sender : UIButton?)
{

func childFunc() {
print("User tapped")
}

if sender != nil && sender.tag == 100{
childFunc()
return
}

button.addTarget(self, action: #selector(parentFunc(_:)), for: .touchUpInside)
button.tag = 100
}
}

In above code, Sender is optional so you can pass nil when you don't want to call child function like parentFunc(nil)

NSTimer does not invoke a private func as selector

According to Using Swift with Cocoa and Objective-C:

“ Declarations marked with the private modifier do not appear in the generated header. Private declarations are not exposed to Objective-C unless they are explicitly marked with @IBAction, @IBOutlet, or @objc as well.”

Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C (Swift 2 Prerelease).” iBooks. https://itun.es/us/utTW7.l

So, you can mark your function with @objc to get the desired behavior. I just tested this in one of my apps where I had used public visibility because I assumed that Objective-C couldn't see private declarations at all, and it works when marked as private and decorated with @objc.


I just saw this related question: Swift access control with target selectors — basically the same thing but I think yours is phrased in a more general way so not strictly a duplicate.

@selector() in Swift?

Swift itself doesn't use selectors — several design patterns that in Objective-C make use of selectors work differently in Swift. (For example, use optional chaining on protocol types or is/as tests instead of respondsToSelector:, and use closures wherever you can instead of performSelector: for better type/memory safety.)

But there are still a number of important ObjC-based APIs that use selectors, including timers and the target/action pattern. Swift provides the Selector type for working with these. (Swift automatically uses this in place of ObjC's SEL type.)

In Swift 2.2 (Xcode 7.3) and later (including Swift 3 / Xcode 8 and Swift 4 / Xcode 9):

You can construct a Selector from a Swift function type using the #selector expression.

let timer = Timer(timeInterval: 1, target: object,
selector: #selector(MyClass.test),
userInfo: nil, repeats: false)
button.addTarget(object, action: #selector(MyClass.buttonTapped),
for: .touchUpInside)
view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
with: button, with: otherButton)

The great thing about this approach? A function reference is checked by the Swift compiler, so you can use the #selector expression only with class/method pairs that actually exist and are eligible for use as selectors (see "Selector availability" below). You're also free to make your function reference only as specific as you need, as per the Swift 2.2+ rules for function-type naming.

(This is actually an improvement over ObjC's @selector() directive, because the compiler's -Wundeclared-selector check verifies only that the named selector exists. The Swift function reference you pass to #selector checks existence, membership in a class, and type signature.)

There are a couple of extra caveats for the function references you pass to the #selector expression:

  • Multiple functions with the same base name can be differentiated by their parameter labels using the aforementioned syntax for function references (e.g. insertSubview(_:at:) vs insertSubview(_:aboveSubview:)). But if a function has no parameters, the only way to disambiguate it is to use an as cast with the function's type signature (e.g. foo as () -> () vs foo(_:)).
  • There's a special syntax for property getter/setter pairs in Swift 3.0+. For example, given a var foo: Int, you can use #selector(getter: MyClass.foo) or #selector(setter: MyClass.foo).

General notes:

Cases where #selector doesn't work, and naming: Sometimes you don't have a function reference to make a selector with (for example, with methods dynamically registered in the ObjC runtime). In that case, you can construct a Selector from a string: e.g. Selector("dynamicMethod:") — though you lose the compiler's validity checking. When you do that, you need to follow ObjC naming rules, including colons (:) for each parameter.

Selector availability: The method referenced by the selector must be exposed to the ObjC runtime. In Swift 4, every method exposed to ObjC must have its declaration prefaced with the @objc attribute. (In previous versions you got that attribute for free in some cases, but now you have to explicitly declare it.)

Remember that private symbols aren't exposed to the runtime, too — your method needs to have at least internal visibility.

Key paths: These are related to but not quite the same as selectors. There's a special syntax for these in Swift 3, too: e.g. chris.valueForKeyPath(#keyPath(Person.friends.firstName)). See SE-0062 for details. And even more KeyPath stuff in Swift 4, so make sure you're using the right KeyPath-based API instead of selectors if appropriate.

You can read more about selectors under Interacting with Objective-C APIs in Using Swift with Cocoa and Objective-C.

Note: Before Swift 2.2, Selector conformed to StringLiteralConvertible, so you might find old code where bare strings are passed to APIs that take selectors. You'll want to run "Convert to Current Swift Syntax" in Xcode to get those using #selector.

Expected ')' to complete '#selector' expression

#selector(CreateRoomsViewController.dismissKeyboard(_):)

Needs to be:

#selector(CreateRoomsViewController.dismissKeyboard(tap:)

Besides the extra ) you can’t use (_:) because your parameter has a label of tap.

Even easier is:

#selector(dismissKeyboard)

And you need to add @obj in front of the method.

Including protocol function in tapGesture action selector - iOS Swift

I figured it out, in the target section when initializing the UITapGestureRecognizer you need to use the viewcontroller which is actually the delegate of the delegator. so:

let tapGesture = UITapGestureController(target: vc, action:"filterClicked:");

Thanks all, and hope this will help others stuck on the same issue!

cheers!

Swift dealing with Actions and Selectors Issue -

Based on the updated code the problem appears to be that your

func mapTypeChanged(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
mapView.mapType = .standard
case 1:
mapView.mapType = .hybrid
case 2:
mapView.mapType = .satellite
case _:
break
}
}

function is currently inside of your other method.

It should look like:

    class viewControllerForMap: UIViewController {

var mapView : MKMapView!

override func loadView() {
mapView = MKMapView()
self.view = mapView

let segmentedControl = UISegmentedControl(items: ["Standard", "Hybrid", "Satellite"])
segmentedControl.backgroundColor = UIColor.white.withAlphaComponent(0.5)
segmentedControl.selectedSegmentIndex = 0
segmentedControl.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(segmentedControl)

segmentedControl.addTarget(self, action: #selector(mapTypeChanged(_:)), for: .valueChanged)

let topConstraint = segmentedControl.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 8)
let leadingConstraint = segmentedControl.leadingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.leadingAnchor)
let trailingConstraint = segmentedControl.trailingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.trailingAnchor)

topConstraint.isActive = true
leadingConstraint.isActive = true
trailingConstraint.isActive = true

}

func mapTypeChanged(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
mapView.mapType = .standard
case 1:
mapView.mapType = .hybrid
case 2:
mapView.mapType = .satellite
case _:
break
}
}

}

Hope that helps.

EXC_BAD_ACCESS when an NSMenuItem's target/selector is set to an object

So the problem is that the items inside the array I'm passing to the static getMenu function are being deallocated after the getMenu function is complete (which is very soon followed by the popUpMenuWithEvent:forView).

I solved it by having a strong reference to that array.

How to control seeking forward and backward from control center?]

Event Handler

You need to add a handler for all the event's you want to receive:

commandCenter.changePlaybackPositionCommand.addTarget(handler: { (event) in
// Handle position change
return MPRemoteCommandHandlerStatus.success
})

Apple Documentation

... To respond to a particular event, register a handler with the appropriate MPRemoteCommand object.

https://developer.apple.com/documentation/mediaplayer/mpremotecommand



Related Topics



Leave a reply



Submit