Call Methods in Extension Delegate - ios

Is it possible to call methods in the extension delegate from a other IntefaceController in general?
Something like:
InterfaceController *interfaceController =[[InterfaceController alloc] init];
interfaceController callMethod
My Interface Controller
#import "InterfaceController.h"
#import "OrdinaryEventRow.h"
#import <UIKit/UIKit.h>
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController()
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
//Configure interface objects here.
-(void)doSomething {
[self presentControllerWithName:#"goalView" context:nil];
}
#end
ExtensionDelegate:
#import "ExtensionDelegate.h"
#import "InterfaceController.h"
#import <WatchConnectivity/WatchConnectivity.h>
#import "setGoal.h"
#implementation ExtensionDelegate
//Handle Local Notification Actions
-(void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UNNotification *)localNotification{
if([identifier isEqualToString:#"action"]){
//Setup WCSession
if ([WCSession isSupported]) {
[[WCSession defaultSession] setDelegate:self];
[[WCSession defaultSession] activateSession];
//Get the value from slider
NSString *someString = [[NSUserDefaults standardUserDefaults]
stringForKey:#"Update"];
NSString *Update = #"Update";
NSDictionary *applicationData = [[NSDictionary alloc] initWithObjects:#[Update] forKeys:#[#"Update"]];
//Send Message to the iPhone (handle over the goal value)
[[WCSession defaultSession] sendMessage:applicationData
replyHandler:^(NSDictionary *reply) {
//handle reply from iPhone app here
}
errorHandler:^(NSError *error) {
//catch any errors here
}
];
}
}
//If Goal Setting was clicked
if([identifier isEqualToString:#"action3"]){
//here I want to call doSomething from InterfaceController
}
}
So I just want to call a method defined in the InterfaceController from ExtensionDelegate.

There is not a way to init a WKInterfaceController from the ExtensionDelegate and call a method on it. If the controller you are trying to call a method is the root controller, you can get the rootController from the WKExtension in the ExtensionDelegate cast it and call a method on it.
// Objective-C
if ([WKExtension shared].rootController isKindOfClass:[InitialInterfaceController class]) {
(InitialInterfaceController *)[WKExtension shared].rootController customMethod];
}
// Swift
if let root = WKExtension.shared().rootInterfaceController as? InitialInterfaceController {
root.customMethod()
}
* My objective-c is a little rusty so if there is a syntax error there please update or let me know in the comments and I can edit.
From your code example, you are trying to do an action based on a local notification so the best thing to do is handle the notification in the Interface Controller itself. Depending on what watchOS you are using
watchOS 3
handleAction(withIdentifier:for:) reference
watchOS 2
handleAction(withIdentifier:for:) reference
Important note here for these methods to be called your extension delegate does not implement the handleAction(withIdentifier:for:) method, WatchKit calls this method on your app’s root interface controller to respond to button taps in your notification interface.

Related

CXCallObserverDelegate not being invoked

Note: I am new to Objective C
I have implemented the CXCallObserverDelegate callChanged delegate in my custom React Native module but it is not invoked when i receive a VOIP call
In didFinishLaunchingWithOptions of AppDelegate
CallManager *manager = [CallManager new];
[manager someMethod:#"Hoola"]; // sending a test string to print
In CallManager.m
#import "CallManager.h"
#import "RNCallKeep.h"
#implementation CallManager
RCT_EXPORT_MODULE();
-(void)someMethod: (NSString *)someString
{
NSLog(#"Hello world callObserver %#", someString); // Hoola string gets printed here means this method is working
CXCallObserver *callObserver = [[CXCallObserver alloc] init];
self.callObserver = callObserver;
[callObserver setDelegate:(id)self queue:nil];
}
-(void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
NSLog(#"Hello world callObserver"); // this doesnt get printed when call is received or status changes
}
#end
In my CallManager.h file
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import <CallKit/CallKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface CallManager : NSObject<RCTBridgeModule, CXCallObserverDelegate>
#property (atomic, strong) CXCallObserver *callObserver;
- (void)someMethod: (NSString *)someString;
#end
NS_ASSUME_NONNULL_END
App is compiling fine and launching and performing all things but when I receive a VOIP call and CallKIT UI is invoked, i don't get call status changed events on accept or reject. Note that NSLogs are printed correctly, i put them there for debug and they are working.

Why can't I call object methods from AppDelegate? No known class method for selector

I have a DataHandler class which acts like a singleton, and it has sharedHandler object method. I use it throughout the whole project, but for some reason, I can't use it in AppDelegate.
DataHandler.h
#import <Foundation/Foundation.h>
#interface DataHandler : NSObject
+ (id)sharedHandler;
- (void)logout;
#end
DataHandler.m
#import "DataHandler.h"
/**
* Singleton static method
* #return singleton instance
*/
+ (id)sharedHandler {
static DataHandler *sharedHandler = nil;
#synchronized (self) {
if (sharedHandler == nil)
sharedHandler = [[self alloc] init];
}
return sharedHandler;
}
-(void) logout {
// ...
}
#end
AppDelegate.m
#import "AppDelegate.h"
#import "DataHandler.h"
#implementation AppDelegate {
- (void)applicationWillResignActive:(UIApplication *)application {
[[DataHandler sharedHandler] logout];
}
#end
I keep getting:
Error: no known class method for selector 'sharedHandler`
Error: no known instance method for selector 'logout'
What is the cause of this behavior?
You have two files named DataHandler.h and the import in AppDelegate.m is picking up the wrong file. Note that it may be picking up a file that's not actually in your project, as long as it's in the folder on disk.

Refactoring Local Authentication Framework for Cleaner Code

I successfully implemented Local Authentication Framework in my code by adding the method to my ViewController, but I want to refactor my code to embrace MVC pattern more effectively. I want to move the code into an NSObject and call it separately, but I'm running into two problems.
For reference, this is the code I'm implementing: https://developer.apple.com/library/ios/documentation/LocalAuthentication/Reference/LocalAuthentication_Framework/
First problem is that the code is not firing when I test it with a UIButton touch, and the second problem is that I want to call performSegueWithIdentifier when successful but I need a UIViewController to call it. So this is what I tried at first:
Authenticate.h
#interface Authenticate : NSObject
- (void)startAuthenticating;
Authenticate.m
#implementation Authenticate
- (void)startAuthenticating{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
...
if (success) {
[self performSegueWithIdentifier: #"Success" sender:nil];
...
} ...
}
^ [self performSegue...] would throw an error here, which I understand.
#import "Authenticate.h"
ViewController.h
#interface ViewController : UIViewController
#property(nonatomic, strong) Authenticate *touchID;
ViewController.m
#implementation ViewController
- (void)viewDidLoad{
...
[self.touchID startAuthenticating];
}
I also tried to
#import "ViewController.h" into the Authenticate.h and add
#property(nonatomic, weak) ViewController *viewController;
and instead of the [self performSegue...] it became
[self.viewController performSegue...] even though that seems like a retain cycle.
Currently, I have refactored my code by implementing Local Authentication framework into another UIViewController and subclassed my main ViewController under that AuthenticationViewController, but that feels like a cheat to me since that is not what I was aiming to do. Could someone please show me how to accomplish this in a better way?
I do not know why your code is not firing. But for the second problem do something like following :
In Authenticate class write the method as,
- (void)startAuthenticatingWithCompletion:(void (^)(BOOL success, NSError *error))completion{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (success) {
completion(YES, nil);
//[self performSegueWithIdentifier: #"Success" sender:nil];
} else {
completion(NO, authError);
}
}
And in ViewController class call the method like,
[self.touchID startAuthenticatingWithCompletion:^(BOOL success, NSError *error) {
if (success) {
[self performSegue...];
}
}];

Calling NSObject class method from the app delegate

I am trying to call a method from a NSObject class from my AppDelegate. Usually this works if calling from a UIViewController but not having luck within the AppDelegate. My code:
AppDelegate.m
#import "ACManager.h"
#implementation AppDelegate {
ACManager *acManager;
}
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[acManager login];
}
ACManager.h
#interface ACManager : NSObject
-(void)login;
#end
ACManager.m
+(ACManager*)sharedInstance {
static ACManager *sharedInstance;
#synchronized(self) {
if (!sharedInstance) {
sharedInstance = [[self alloc]init];
}
}
return sharedInstance;
}
-(void)login
{
NSLog(#"login run");
}
#end
Any ideas thank you. Is there a different way around this when calling from the app delegate?
For the singleton you need to use:
[[ACManager sharedInstance] login];
or assign value to your variable:
acManager=[ACManager sharedInstance];
and then, call:
[acManager login];
You are saying:
[acManager login];
Thus, you are sending an instance message to acManager. But acManager is nil! You have forgotten to supply an actual ACManager instance and place it in that slot (assign it to the variable).
Thus, nothing happens.

Objective C Callback on particular event

I am new to objective C and trying to develop my own callback function, the callback function gets called on a particular event like receiving data from network like NSURLprotocol does and once received it will NSLog a message that "Event has Occured" or display as text on UIViewController or any UI related action.
So, I am totally confused as to where the eventOccuredMethod should be called to let the receiveController be called and execute the implementation inside it.
I have used protocols like NSURLProtocol before, but I don't know how to implement them to get such callbacks being called.
Any video links, answers, articles links are welcomed.
//Sample.h file
#import <Foundation/Foundation.h>
#class Sample;
#protocol SampleProtocol <NSObject>
-(void)receivedCallback;
#end
#interface Sample : NSObject
#property (nonatomic,weak) id<SampleProtocol> delegate;
-(void)eventOccured;
#end
//Sample.m file
#import "Sample.h"
#implementation Sample
-(void)eventOccured{
if([_delegate conformsToProtocol:#protocol(SampleProtocol)])
[_delegate receivedCallback];
}
#end
//ViewController.h file
#interface ViewController : UIViewController<SampleProtocol>
#end
//ViewController.m file
#import "ViewController.h"
#interface ViewController (){
Sample *s;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
s = [[Sample alloc] init];
s.delegate = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)receivedCallback:(Sample *)sample{
NSLog(#"Event Has Occured");
}
#end
I am not sure of the following call which I am making ...
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
Sample *s = [[Sample alloc] init];
[s eventOccured];
}
You are implementing the delegate pattern the right way. But if the Sample object doesn't generate its own events but instead is relaying events posted to it from somewhere else, as is the case in your example, you have to ensure that the object which has the ViewController as a delegate and the object that receives the message are in fact the same. One way to do it is to make Sample a singleton :
#import "Sample.h"
#implementation Sample
+ (instancetype)sharedInstance
{
static id sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});
return sharedInstance;
}
-(void)eventOccured{
if([_delegate conformsToProtocol:#protocol(SampleProtocol)])
[_delegate receivedCallback];
}
#end
And then in your view controller you would do
s = [Sample sharedInstance];
and in your appDelegate :
[[Sample sharedInstance] eventOccured];
Another way to ensure that you are using the same object, as vikingosegundo pointed out, would be to set the view controller's Sample object from the appDelegate.
For this use case, you could also consider using Notifications.
?, i'm very confused. I don't think you understand what you have written. You should never try copy code like this from online without first reading a tutorial to understand what it is you are doing. This can be very dangerous.
Sample.h / .m is a class, this class defines a protocol that says "In order for me to alert you to the fact an event has occurred, you need to implement method X".
This is the "protocol", by conforming to the protocol, another class (lets say a ViewController) is saying that it implements the method that Sample is looking for.
So Sample will run code, and when it wants to pass some info back to the other class (ViewController in this case) it calls one of the methods defined in the protocol.
e.g. (not fully working code)
Sample.m
- (void)getDataFromURL:(NSStirng *)url
{
[self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject)
{
if([_delegate conformsToProtocol:#protocol(SampleProtocol)])
{
[_delegate receivedCallback];
}
}];
}
So when Sample runs the method getDataFromURL it will request its data, when the data returns, Sample will call the method receivedCallback on its delegate. Which in this case is an instance of a viewController.
EDIT
please also note what [_delegate conformsToProtocol:#protocol(SampleProtocol)] does. This asks does the delegate instance conform to the protocol. But this protocol hasn't said that recievedCallback is required. So you have no way of knowing the method is there.
either use:
#protocol SampleProtocol <NSObject>
#required
-(void)receivedCallback;
#end
in the protocol definition or
if(self.delegate && [self.delegate respondsToSelector:#selector(receivedCallback)])
to check is it implemented
you are calling eventOccured on a second, independent Sample instance that has now delegate set.
The easiest fix: make the view controller send it to it's sample instance.
better: give the view controller a property that holds sample and sat that from the application delegate.
You should call EventOccurred within your data retrieving method. Once the data retrieving is complete call EventOccured.
#protocol SampleProtocol <NSObject>
-(void)receivedCallback;
#end
This protocol must be implemented in your data retrieving class. And make sure -(void)receivedCallback; has a parameter to send data to your ViewController

Resources