How does - (void) translate in swift code - objective-c

I came across this code on apple documentation on how to implement Game Center achievements. But I don't quite understand what "- (void)" really mean and how I can use it in a class.
- (void) challengeViewController:(MyAchievementChallengeViewController*)controller wasDismissedWithChallenge:(BOOL)issued
{
[self dismissViewControllerAnimated:YES completion:NULL];
if (issued)
{
[controller.achievement issueChallengeToPlayers:controller.players message:controller.message];
}
}
Can someone explain to me the use of keyword void in this context?

It means that the function has no return value.

The return type of the function is in front of the function name in Objective-C. So (void) is the return type.
So the Obj-C - (void) myFunc is equivalent in Swift to func myFunc() -> Void or the -> Void can simply be omitted to get func myFunc().
That function would translate to
func challengeViewController( _ controller: MyAchievementChallengeViewController, wasDismissedWithChallenge challenge: Bool) {
}

Related

Invalid redeclaration in code converting from Objective-C to Swift

I'm try to convert code from Objective-C to Swift -
Objc-C part - no errors.
AppDelegate:
- (OWTConferenceClient*)conferenceClient{
return _conferenceClient;
}
-(void)conferenceClient:(OWTConferenceClient *)client didReceiveMessage:(NSString *)message from:(NSString *)senderId{
}
- (void)conferenceClient:(OWTConferenceClient *)client didAddParticipant:(OWTConferenceParticipant *)user{
}
-(void)conferenceClient:(OWTConferenceClient *)client didAddStream:(OWTRemoteStream *)stream{
}
to Swift
AppDelegate:
func conferenceClient() -> OWTConferenceClient { <--- Error here: Invalid redeclaration of 'conferenceClient()'
return conferenceClient
}
func conferenceClient(_ client:OWTConferenceClient, didAdd stream:OWTRemoteStream){
}
func conferenceClient(_ client:OWTConferenceClient, didAdd user:OWTConferenceParticipant) {
}
func conferenceClient(_ client:OWTConferenceClient, didReceiveMessage message:String, from senderId:String) {
}
What is wrong with Swift part?
It might be that you have a (global or instance) variable named conferenceClient which then interferes with that function.
You need to rename the variable (in ObjC, it had an underscore _conferenceClient)

What is the swift equivalent to setting properties on `id`?

I wonder what's the Swift equivalent in calling a method on id in which the availability of the method is determined at runtime. Specifically I'm looking to do this pattern in Swift:
-(IBAction) handleEvent:(id) sender {
BOOL didDisable = NO;
if([sender respondsToSelector:#selector(setEnabled:)]) {
[sender setEnabled:NO];
didDisable = YES;
}
[self doSomethingAsyncWithCompletionHandler:^{
if(didDisable) {
[sender setEnabled:YES];
}
}];
}
The biggest problem is that setEnabled: is imported in Swift as a property (e.g. UIBarItem) and none of the following constructs compile
func handleEvent(sender: AnyObject) {
// Error: AnyObject does not have a member named "enabled"
sender.enabled? = false
// Error: (BooleanLiteralCompatible) -> _ is not identical to Bool
sender.setEnabled?(false)
}
You can in fact do it exactly the same way you were doing it before: by calling respondsToSelector:. Indeed, that is exactly what your proposed expression does:
sender.setEnabled?(false)
That expression is actually a shorthand - it calls respondsToSelector: first, and then calls setEnabled: only if the respondsToSelector: test passes. Unfortunately, as you say, you can't get that code to compile. That, however, is merely a quirk of Swift's known repertory of available methods. The fact is that, although it is a little tricky to get it to compile, it can be done - and once you get it to compile, it behaves just as you would expect.
However, I'm not going to explain how to make it compile, because I don't want to encourage this kind of trickery. This sort of dynamic messaging is discouraged in Swift. In general, dynamic messaging tricks such as key-value coding, introspection, and so forth are not needed in Swift and are not consonant with Swift's strong typing approach. It would be better to do things the Swift way, by casting optionally to something that you have reason to believe this thing might be and that has an enabled property. For example:
#IBAction func doButton(sender: AnyObject) {
switch sender {
case let c as UIControl: c.enabled = false
case let b as UIBarItem: b.enabled = false
default:break
}
}
Or:
#IBAction func doButton(sender: AnyObject) {
(sender as? UIControl)?.enabled = false
(sender as? UIBarItem)?.enabled = false
}
In Swift 2.0 beta 4, your prayers are answered; this code becomes legal:
#IBAction
func handleEvent(sender: AnyObject) {
if sender.respondsToSelector("setHidden:") {
sender.performSelector("setHidden:", withObject: true)
}
}
If you want to avoid using the respondsToSelector: method you could define a protocol instead. Then extend the classes you want to use that is already in conformance with this protocol's definition (enabled) and define the function with a generic variable conforming to your protocol.
protocol Enablable{
var enabled:Bool { get set }
}
extension UIButton : Enablable {}
extension UIBarButtonItem : Enablable {}
//....
func handleEvent<T:Enablable>(var sender: T) {
sender.enabled = false
}
If you need to use it with an IBAction method a little bit of a work around is required since you cannot use generics directly on them.
#IBAction func handleEventPressed(sender:AnyObject){
handleEvent(sender);
}
We also need a matching generic function without Enablable conformance so that we can call handleEvent without knowing wether or not sender is Enablable. Luckily the compiler is smart enough to figure out which of the two generic functions to use.
func handleEvent<T>(var sender: T) {
//Do Nothing case if T does not conform to Enablable
}
As a workaround/alternative, you can use Key-Value Coding:
#IBAction func handler(sender: AnyObject) {
if sender.respondsToSelector("setEnabled:") {
sender.setValue(false, forKey:"enabled")
}
}
This works with both Swift 1.2 (Xcode 6.4) and Swift 2.0 (Xcode 7 beta).

passing in a method in Objective C

In C# you can create a delegate method, assign it to a variable or pass it into a method as if it were a variable. For example:
public delegate int Lookup(String s);
//...
public static int Evaluate(String exp, Lookup variableEvaluator)
{
//...
}
I heard that in C you can create a pointer to any method and then pass that pointer to a method.
Can anyone give me a simple example of doing that in Objective-C? Of course, I can create an object with a singe method and pass that object into a method. But I am curious if there is a way of doing that similar to that of C# or C.
Lots of ways.
One: the good. Use blocks (closures, lambda calculus, however you call it):
typedef void (^MyCallback)();
- (void)callTheCallback:(MyCallback)blockToInvoke
{
blockToInvoke();
}
MyCallback cb = ^{
NSLog(#"I was called! :D");
};
[self callTheCallback:cb];
Two: the bad. Grab a pointer to the method function itself and call that. (Warning: if you use this approach, I'll sue you.)
- (void)callTheCallback:(IMP)funcPtrToCall withObject:(id)obj selector:(SEL)sel
{
funcPtrToCall(obj, sel);
}
- (void)someCallbackMethod
{
NSLog(#"I was called! :D");
}
IMP implemt = [[self class] instanceMethodForSelector:#selector(someCallbackMethod)];
[self callTheCallback:implemt withObject:self selector:#selector(someCallbackMethod)];
Three: the ugly. Use a delegate:
- (void)delegateMethodOfSomeObject:(SomeObject *)obj
{
NSLog(#"I was called! :D");
}
SomeObject *obj = [[SomeObject alloc] init];
obj.delegate = self;
[obj makeThisObjectSomehowCallItsDelegateThatIsCurrentlySelf];
Two quick thoughts come to mind.
The short answer is called "blocks", but it's lower level than is probably recommended for what you need.
The "cleaner" solution (read: higher level) is to pass two params: and object (called "target") and a selector (called "action"). This is a very common pattern in Objective-C, so I'll only demonstrate this one. If you are interested in the blocks idea, check out this doc.
Essentially, the object should be passed as an id, and the selector as a SEL, for which we have the handy #selector() construct:
-(void) doThingWithTarget:(id) targetObj action:(SEL) actionSel {
if([targetObj respondsToSelector:actionSel]) {
[targetObj performSelector:actionSel withObject:self];
}
}
// ...
[thatOtherObject doThingWithTarget:self action:#selector(myMethod:)];
// ... where
-(void) myMethod:(id) sender {
// sender is the calling object, or should be by contract.
}
Objective C uses selectors. http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocSelectors.html

How to check if a view controller can perform a segue

This might be a very simple question but didn't yield any results when searching for it so here it is...
I am trying to work out a way to check if a certain view controller can perform a segue with identifier XYZ before calling the performSegueWithIdentifier: method.
Something along the lines of:
if ([self canPerformSegueWithIdentifier:#"SegueID"])
[self performSegueWithIdentifier:#"SegueID"];
Possible?
To check whether the segue existed or not, I simply surrounded the call with a try-and-catch block. Please see the code example below:
#try {
[self performSegueWithIdentifier:[dictionary valueForKey:#"segue"] sender:self];
}
#catch (NSException *exception) {
NSLog(#"Segue not found: %#", exception);
}
Hope this helps.
- (BOOL)canPerformSegueWithIdentifier:(NSString *)identifier
{
NSArray *segueTemplates = [self valueForKey:#"storyboardSegueTemplates"];
NSArray *filteredArray = [segueTemplates filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"identifier = %#", identifier]];
return filteredArray.count>0;
}
This post has been updated for Swift 4.
Here is a more correct Swift way to check if a segue exists:
extension UIViewController {
func canPerformSegue(withIdentifier id: String) -> Bool {
guard let segues = self.value(forKey: "storyboardSegueTemplates") as? [NSObject] else { return false }
return segues.first { $0.value(forKey: "identifier") as? String == id } != nil
}
/// Performs segue with passed identifier, if self can perform it.
func performSegueIfPossible(id: String?, sender: AnyObject? = nil) {
guard let id = id, canPerformSegue(withIdentifier: id) else { return }
self.performSegue(withIdentifier: id, sender: sender)
}
}
// 1
if canPerformSegue("test") {
performSegueIfPossible(id: "test") // or with sender: , sender: ...)
}
// 2
performSegueIfPossible(id: "test") // or with sender: , sender: ...)
As stated in the documentation:
Apps normally do not need to trigger segues directly.
Instead, you configure an object in Interface Builder associated with
the view controller, such as a control embedded in its view hierarchy,
to trigger the segue. However, you can call this method to trigger a
segue programmatically, perhaps in response to some action that cannot
be specified in the storyboard resource file. For example, you might
call it from a custom action handler used to process shake or
accelerometer events.
The view controller that receives this message must have been loaded
from a storyboard. If the view controller does not have an associated
storyboard, perhaps because you allocated and initialized it yourself,
this method throws an exception.
That being said, when you trigger the segue, normally it's because it's assumed that the UIViewController will be able to respond to it with a specific segue's identifier. I also agree with Dan F, you should try to avoid situations where an exception could be thrown. As the reason for you not to be able to do something like this:
if ([self canPerformSegueWithIdentifier:#"SegueID"])
[self performSegueWithIdentifier:#"SegueID"];
I am guessing that:
respondsToSelector: only checks if you are able to handle that message in runtime. In this case you can, because the class UIViewController is able to respond to performSegueWithIdentifier:sender:. To actually check if a method is able to handle a message with certain parameters, I guess it would be impossible, because in order to determine if it's possible it has to actually run it and when doing that the NSInvalidArgumentException will rise.
To actually create what you suggested, it would be helpful to receive a list of segue's id that the UIViewController is associated with. From the UIViewController documentation, I wasn't able to find anything that looks like that
As for now, I am guessing your best bet it's to keep going with the #try #catch #finally.
You can override the -(BOOL)shouldPerformSegueWithIdentifier:sender: method and do your logic there.
- (BOOL) shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if ([identifier isEqualToString:#"someSegue"]) {
if (!canIPerformSegue) {
return NO;
}
}
return YES;
}
Hope this helps.
Reference CanPerformSegue.swift
import UIKit
extension UIViewController{
func canPerformSegue(identifier: String) -> Bool {
guard let identifiers = value(forKey: "storyboardSegueTemplates") as? [NSObject] else {
return false
}
let canPerform = identifiers.contains { (object) -> Bool in
if let id = object.value(forKey: "_identifier") as? String {
return id == identifier
}else{
return false
}
}
return canPerform
}
}
Swift version of Evgeny Mikhaylov's answer, which worked for me:
I reuse a controller for two views. This helps me reuse code.
if(canPerformSegueWithIdentifier("segueFoo")) {
self.performSegueWithIdentifier("segueFoo", sender: nil)
}
else {
self.performSegueWithIdentifier("segueBar", sender: nil)
}
func canPerformSegueWithIdentifier(identifier: NSString) -> Bool {
let templates:NSArray = self.valueForKey("storyboardSegueTemplates") as! NSArray
let predicate:NSPredicate = NSPredicate(format: "identifier=%#", identifier)
let filteredtemplates = templates.filteredArrayUsingPredicate(predicate)
return (filteredtemplates.count>0)
}
It will be useful, before call performSegue, check native storyboard property on base UIViewController (for example screen was from StoryBoard or Manual Instance)
extension UIViewController {
func performSegueWithValidate(withIdentifier identifier: String, sender: Any?) {
if storyboard != nil {
performSegue(withIdentifier: identifier, sender: sender)
}
}
}
enter image description here
There is no way to check that using the standard functions, what you can do is subclass UIStoryboardSegue and store the information in the source view controller (which is passed to the constructor). In interface builder select "Custom" as the segue type as type the class name of your segue, then your constructor will be called for every segue instantiated and you can query the stored data if it exists.
You must also override the perform method to call [source presentModalViewController:destination animated:YES] or [source pushViewController:destination animated:YES] depending on what transition type you want.

In Objective C what is the equivalent of passing a function pointer in C?

#implementation ThisObject
-(void)start {
SomeOtherObject *someOtherObject = [SomeOtherObject alloc];
[someOtherObject doSomethingAndCallThisFunctionWhenUrDone:myCallBackFunction :self];
}
-(void)myCallBackFunction {
// :)
}
Basically, how can I make this work?
There are four ways to make a callback:
Function Pointer You can do a function pointer if you really want, but it's not recommended. It's done the same way you would do it in C. The problem is you can't use a function pointer to an Objective-C method. It looks something like this:
void callback(/* Some args */) {
// Some callback.
}
- (void)doSomethingAndCallThisFunctionWhenDone:(void(*)(/* Some args */))func {
// Do something.
if (func)
func(/* Some args */);
}
- (void)start {
[self doSomethingAndCallThisFunctionWhenDone:&callback];
}
Selectors You can use -performSelector:. It looks like this:
- (void)doSomethingAndCallTarget:(id)target withSelector:(SEL)sel {
// Do something.
[target performSelector:sel];
}
- (void)start {
SomeOtherObject * someOtherObject = [[SomeOtherObject alloc] init];
[self doSomethingAndCallTarget:someOtherObject withSelector:#selector(MyCallback)];
}
Delegates Use a delegate. This is similar to UITableViewDelegate/UITableViewDataSource. See the Apple docs here. You might do it like this:
- (void)doSomethingDelegate:(id<MyCallbackObject>)delegate {
[delegate retain];
// Do something.
[delegate performMyCallback]; // -performMyCallback must be declared in the MyCallbackObject protocol and implemented by SomeOtherObject.
[delegate release];
}
- (void)start {
id<MyCallbackObject> someOtherObject = [[SomeOtherObject alloc] init];
[self doSomethingDelegate:someOtherObject];
[someOtherObject release];
}
Blocks The preferred way for callbacks is to use blocks. They are only available for iOS 4.0+ or Mac OS X 10.6+. It looks something like this:
- (void)doSomethingAndCallThisBlockWhenDone:(void(^)(/* Some args */))block {
[block copy];
// Do something.
if (block)
block(/* Some args */);
[block release];
}
- (void)start {
[self doSomethingAndCallThisBlockWhenDone:^void(/* Some args */){ // Return type and arguments may be omitted if you don't have any.
// Your callback
}];
}
As you can see with the block, it's easier to read and your callback is inline with your code. This is especially nice so you don't have to hunt it down. There are many more benefits of blocks, but I couldn't possibly cover them all here.
One last thing, if you use a block, you will want to use a typedef so you don't have to type obscure block types like void(^)(/* Some args */) all the time. The typedef could look like this:
typdef void(^MyCallback)(/* Some args */);
Then, you can declare your method like this:
- (void)doSomethingAndCallThisBlockWhenDone:(MyCallback)block;
Update:
I have shown more detail of how to implement the different techniques (see above).
Are you talking about this?
-(void)callSomePassedSelector:(SEL)callbackSelector {
[someObjectThatRespondesToThisSelector performSelector:callbackSelector];
}
I assume you want to store it and call it later, but this should give you all the needed information about how to pass and call it. There are other methods to invoke the selector, see more here
im a bit confused about what you talking about but is this it?
[self performSelector:#selector(myCallFunction)];