Refactoring Local Authentication Framework for Cleaner Code - ios

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...];
}
}];

Related

Call Methods in Extension Delegate

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.

Objective-C how to release yourself in ARC code

I'm new to Objective-C (from java background), so apologies if this question is too trivial.
Suppose i have two classes, where one holds a reference to another, as such:
#interface PostOffice
#property (nonatomic, strong) MailGuy *mailGuy;
#end
#implementation PostOffice
-(void)getMailmanToSendMail {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.mailGuy = [[MailGuy alloc] init];
[self.mailGuy sendMail];
}
}
#end
and for MailGuy:
#interface MailGuy () <MFMailComposeViewControllerDelegate>
#end
#implementation MailGuy
-(void)sendMail {
NSLog(#"send mail");
[self.viewController presentViewController:mailViewController animated:YES completion:nil];
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
// upon dismissal, how do i get the PostOffice instance to release this MailGuy instance?
}
#end
How do i get the PostOffice to release the MailGuy?? i only know when it should be free based on the callback. but i don't want to store a reference to the PostOffice? or do i ? and does it matter that i'm instantiating the MailGuy from a background thread?
any help would be appreciated. thanks!
The usual way to do so is to use protocol and delegates.
So in your MailGuy.h you should add a protocol
#protocol MailGuyDelegate
- (void) didPostHisLetter;
#end
And still in the .h file but this time INSIDE the #interface you would add a delegate reference
#property (weak, nonatomic) id <MailGuyDelegate> delegate;
This adds a delegate property to your MailGuy and it says that the delegate must implement the given protocol (which has the method).
Then in your mail guy implementation code here's what you would do
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
if (self.delegate) {
[self.delegate didPostHisLetter];
}
}
This tells his delegate "Hey I finished my job". So all you have to do now is implement the delegate in your PostOffice class
In your .m file of PostOffice, add a private property
#interface PostOffice() <MailGuyDelegate>
#end
Then when you invoke your mail guy, you associate it's delegate to self. Notice that I remove the async dispatch as it is not used and may cause problems as mentioned in comments
-(void)getMailmanToSendMail {
self.mailGuy = [[MailGuy alloc] init];
self.mailGuy.delegate = self;
[self.mailGuy sendMail];
}
And all is left to do is implement the protocol's method (still in postoffice implementation)
- (void) didPostHisLetter {
self.mailGuy = nil;
}

Simple iOS delegate/callback

I have a simple ViewController. In the .m file I put
#import "Manager.h"
There is a button and when it is clicked the following code is executed:
Manager* manager = [[Manager alloc] init];
NSString* str = [manager doit];
NSLog(#"str = %#", str);
Manager is a subclass of NSObject.
In Manager.m I have this method:
- (NSString*)doit{
return #"did it";
}
Great. All this works as expected.
What I need is, if possible and if a good practice, to send/make Manager to understand, that from the method doit
another method in ViewController should be called. Some kind of callback/delegate. How do I accomplish this?
When I call
[manager doit];
I also want to inform that a method in ViewController should be executed.
I hope you understand what I mean otherwise I can write some more details. Thanks
Yes you could use delegate, Your implementation of the delegate in the Manager object should look like so:
#protocol managerDelegate <NSObject>
-(void)doSomthingAndGetThisString: (NSString *)stringText;
#end
#interface Manager : NSObject
#property (nonatomic,strong) id <managerDelegate>delegate;
#end
As you can see, the Manager object claims his delegate protocol in the header interface, So any class could sign on it, And if they do, they should preform the method "doSomthingAndGetThisString".
Whenever "Manager" object chooses to fire the delegate methods he will call it like so:
- (NSString*)doit{
//Call my delegate:
[self.delegate doSomthingAndGetThisString:#"Passing this stringt to my delegate"];
return #"did it";
}
ViewController:
ViewController needs to keep a property of the manager object:
#property (nonatomic,strong) Manager *myManager;
And Now when you allocate "myObject", ViewController should "sign" to preform the Delegate like so:
_myManager = [Manager new];
_myManager.delegate = self;
And of course have the method:
-(void)doSomthingAndGetThisString: (NSString *)stringText;
That will call whenever "Manager" object firs it.
Hope this helps

For loop that doesn't get entered

So I got this for loop in a function, but it never gets entered,
for (Window *window in _app.windows) {
NSLog(#"test.");
}
I'm a beginner so where do I start to debug this and see where it goes wrong?
EDIT
This is in another class
(its in a function (loadApp) that I call in my ViewController, like this: self.app = [MyClass loadApp]; , the above code is also in my ViewController.
Window *window = [[Window alloc] initWithName:title subtitle:subtitle number:number ident:ident type:type chapternumber:chapternumber icon:icon text:text img:img question:question answerFormat:answerFormat answerLength:answerLength tip1:tip1 tip2:tip2 tip3:tip3 tip1Answer:tip1Answer tip2Answer:tip2Answer tip3Answer:tip3Answer];
[app.windows addObject:window];
}
return app;
Try the following
if(!_app) {
NSLog(#"app is nil");
}
else if(!_app.windows) {
NSLog(#"windows is nil");
}
else {
NSLog(#"there are %d windows", [_app.windows count]);
}
I suspect you'll see there are 0 windows
You have to make sure you are accessing the same variable. That is the gist of all the other comments and answers you are getting. It needs to be setup something like this. Keep in mind, your app may not be setup exactly like this. This is just a general structure to follow:
//myViewController.h
#import "WindowClass.h"
#import "AppClass.h"
#property (strong, nonatomic) AppClass *app;
//myViewController.m
#import "myViewController.h"
#synthesize app;
(id)init....{
//...init code here
//Synthesized objects must be initialized before they are accessed!
self.app = [[AppClass alloc] init];
return self;
}
(void)loadApp {
WindowClass *aWindow = [[WindowClass alloc] init];
[self.app.windowArray addObject:aWindow];
return;
}
(void)loopFunction {
for (WindowClass *window in self.app.windowArray) {
NSLog(#"test.");
}
return;
}
//AppClass.h
#property (strong, nonatomic) NSArray *windowArray;
//AppClass.m
#import "AppClass.h"
#synthesize windowArray;

How to make a single shared instance of iAd banner throughout many view controllers?

I have tried using a singleton class in my app delegate but I haven't been able to get that to work. I've also checked out the iAdSuite examples (particularly the containerBanner example because it seemed to be the most relative) but I can't figure it out. If there's a better way to accomplish this without using a singleton class and you can point me in the right direction I'd really appreciate it. Some of my singleton class code is below. Thank you!
#interface App Delegate
#property (assign) iAdController *iadc;
+ (AppDelegate*) sharedApplication;
- (iAdController*)sharedAd;
#end
#implementation AppDelegate
#synthesize iadc;
+ (AppDelegate*) sharedApplication
{
return [[UIApplication sharedApplication] delegate];
}
-(iAdController*)sharedAd
{
if(iadc==nil){
iadc=[iAdController new];
}
return iadc;
}
#interface ViewController
iAdController*iadc=[[AppDelegate sharedApplication] sharedAd];
//here i get an error saying, "initializer element is not a compile-time constant.
Everything is imported correctly. If there's anything else I should post let me know.
try changing your singleton creation to this:
+ (LocationManagerSingleton*)sharedInstance {
static LocationManagerSingleton *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// PERFORM any custom initialization here
}
return self;
}
Obviously change the name of the class.
Whenever you want to use your singleton in any of your viewcontrollers just call it like this:
locationManager = [LocationManagerSingleton sharedInstance];
Dont forget to add
+ (LocationManagerSingleton*) sharedInstance;
on the header.
EDIT
well it seems i misunderstood your code (forget my answer, you simply want to be able to access your iAdController from everywhere. so just place
Add inside the .m of the ViewController
#interface ViewController()
{
iAdController *iadc;
}
And inside the
-(void)viewDidLoad
{
iadc=[[AppDelegate sharedApplication] sharedAd];
}
but import the app delegate.h on whichever viewcontroller you want to use it in.
#import "AppDelegate.h"
also there shouldnt be a space in the AppDelegate on the #interface

Resources