How to implement protocol method for the UIApplicationDelegate? - ios

I'm working in a class, call it Module, and I need to implement this method in the AppDelegate:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
...
}
I know which class is the AppDelegate (call it App), but I do not have access to edit that class. How can I implement the delegate method from my Module?
I thought about using a category to extend App but this line in the category docs is a concern:
"you’ll need to import the category header file in any source code file where you wish to use the additional methods, otherwise you’ll run into compiler warnings and errors."
The question is, how can I implement the protocol method in my module in a way so that iOS knows to call my protocol method at the appropriate time?

Ok Joe... If you want to IMPLEMENT the application:openURL:sourceApplication:annotation: from another module, you can do it in runtime.
WE NEED TO ASSUME THAT THE AppDelegate HAVE TEH METHOD IMPLEMENTED
First you need to import the class:
#import <objc/runtime.h>
Then the you need to declare the struct of an object method:
struct objc_method {
SEL method_name;
char *method_types;
IMP method_imp;
};
And finally you can change the implementation whit this:
//Create the selector of the method.
NSString * openURL = #"application:openURL:sourceApplication:annotation:";
SEL selectorOpenURL = NSSelectorFromString(openURL);
//Get the method of the intance.
Method openURLMethod = class_getInstanceMethod([[[UIApplication sharedApplication] delegate] class], selectorOpenURL);
//Get the current implementation.
IMP openURLIMP = openURLMethod->method_imp;
//Create your own implementation.
IMP myOpenURLIMP = imp_implementationWithBlock(^BOOL(id _s, UIApplication * app,NSURL *url,NSString *sourceApplication,id annotation) {
//Call the original implementation.
openURLIMP(_s,selectorOpenURL,app,url,sourceApplication,annotation);
//Here your implementation code.
NSLog(#"Handling the URL");
return YES;
});
BUT BE CAREFUL. If you look in my code, I'm calling the original implementation inside my implementation, so if I execute the code to change the implementation more than one, my implementation will be an inception (Like the film, my implementation inside my implementation, inside my implementation, inside my implementation and so on).
EDIT:
There is way to add your implementation to the class:
If class_getInstanceMethod return null you can alloc the memory for the method and add it to the class later:
//If the method dont exist. We need to create one.
if (!openURLMethod) {
existMethod = NO;
openURLMethod = malloc(sizeof(struct objc_method));
openURLMethod->method_types = "c24#0:4#8#12#16#20";
openURLMethod->method_name = selectorOpenURL;
}
Adding the method to the class:
if (!existMethod) {
class_addMethod([[[UIApplication sharedApplication] delegate] class], openURLMethod->method_name, openURLMethod->method_imp, openURLMethod->method_types);
}
But the problem is, I suppose, that the Operative System are registering the method when the app start, so if when the app start the method don't exist the OS never will call your method.
I will research about how the OS are management that events. With the applicationDidEnterBackground, if you don't have the implementation on the AppDelegate and you add it in runtime, the OS never call your implementation. This is why I assume that the operating system is registering the events when the application starts.

Related

A singleton with delegates?

I have some bluetooth class that needs to be alive during all views .
That means, that i have to call functions in this class from different views, and ALSO get delegates from this class to different views .
So , i have to initialise it once to create connection, than later, i have to use it again from different views(classes) and register to get notifications from it.
I was thinking about a singleton, that HE will create an instance of that bluetooth class, and i can access him from anywhere .
But, i would also like that any view can get delegates from it .
How would i do that ?
i have read What should my Objective-C singleton look like?
But maybe singleton is not what i need ?
How can you create a class to always be alive, and register to get delegates from it from anywhere ?
(how can it be done with app delegate class ? )
I have had the similar query a while back
Problem : Multiple Classes need to receive delegate calls from single instance
Solution: I used a combination of sharedInstance , delegates and NSNotifications to handle the problem
SharedInstance
+ (SomeBluetoothClass *) sharedInstance {
static dispatch_once_t once;
static SomeBluetoothClass *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Delegate
#property (weak, nonatomic) id <SomeBluetoothClassDelegate> delegate;
Since delegate can respond to only one class at a time. Assign class in focus based on your logic. Then whenever you want send info to all send it across using NSNotificationCenter from the sharedInstance. Send the info through using userInfo dictionary of NSNotifications
Notifications
[[NSNotificationCenter defaultCenter] postNotificationName:SomeBluetoothClassNotification
object:self
userInfo:info];
Model the structure of SomeBluetoothClass to be thread safe and handle all notifications along with the delegates and it should work fine.
Have a lots way for your situation. Firstly, you should understand that creating object is not too heavy.
So if you want to use Delegate, you can create a Factory method
Ex:
+ (instancetype)bluetoothManagerWithDelegate:(id<delegate>)delegate {
return [self alloc] initWithDelegate:delegate];
}
So that you also don't care about conflict of concurrence. Because you have separate Bluetooth class.
If you still want to use Singleton, in this situation, it depends on how many object you want to notify.
Only 1 views, just use delegate, and set new delegate when you present new view.
More than one, you can use NSNotificationCenter or Observer, you can google these keywords, it have a lot of tutorial and document on the internet help you use it.
I think you can create NSMutableArray in your singleton with links on your views and call someMethod for all objects when is needed. Don't forget remove views from array when its don't need anymore. That is simple realization of pattern called "Observer".
You need to create a class that should be allocated memory once in a lifetime
I am posting a small code snippet which can help you.
In your .m file
#pragma mark - Shared Instance
static BlootoothClass *_sharedblootoothclass = nil;
+ (BlootoothClass *) sharedClass {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedblootoothclass = [[self alloc] init];
// DO YOUR STUFF TO INTIALISE YOUR CLASS
});
return _sharedblootoothclass;
}
in Your .h file
+ (BlootoothClass *) sharedClass
dispatch_once is the queue with dispatch the instance of your class one in a lifetime and you can access its function all over in the app any time.
Now to get any data from it you can get it from instance from any where like
[BlootoothClass sharedClass].anyObject
And you also can send post notification from here in any of its function
- (void)detectedBlootoothdevice{
[[NSNotificationCenter defaultCenter] postNotificationName:#"newdevicedetected" object:nil];
}
You should not use delegates as you cant call same delegate function at multiple class because last delegate get overwritten.

Where should I initialize my singleton in iOS/OS X app?

I want to initialize my singleton object which stores and manages the application settings over the entire class within my app. Also, the singleton instance should be initialized by loading the data from NSUserDefaults upon launch. However, I'm not fully sure where I should initialize the singleton upon launch.
In Cocoa app, I first wrote the singleton initialization code within applicationWillFinishLaunching:, taking parameters from NSUserDefaults. However, later I found that this doesn't work properly if I also write the singleton initialization code (taking no parameter!) within my initial view controller, set in storyboard, because the viewWillLoad:, viewDidLoad: etc. of the class of the view controller are called before the applicationWillFinishLaunching:.
So now I'm sure I should write the singleton initalization code within viewWillLoad: earlier than applicationWillFinishLaunching, but still not sure whether it is appropriate. Specifically, I know the NSApplicationMain is the first method to be called upon launch, but it seems that the next method is not anything within AppDelegate, at least if you use storyboard.
To summary, what I want to ask are the following:
What method from what class will be called after NSApplicationMain, if you use storyboard.
Where should I write my singleton initialization code within my app? I want to initialize it as soon as possible.
Does it differ between iOS and OS X app?
You should initialize it when it's first accessed. Something like this, maybe:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
As a side note, if you're literally only using this class as an accessor to NSUserDefaults, you might want to consider using static methods instead.
+ (id)mySpecificDataPoint {
return [[NSUserDefaults standardUserDefaults] objectForKey:#"whatever"];
}
+ (void)setMySpecificDataPoint:(id)data {
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"whatever"];
}
Or maybe a more well-designed way might be to add a category to NSUserDefaults for this purpose.
#interface NSUserDefaults (MyData)
#property (nonatomic) NSString *someDataPoint;
#property (nonatomic) NSInteger somePrimitiveDataPoint;
#end
#implementation NSUserDefaults (MyData)
- (NSString *)someDataPoint {
return [self objectForKey:#"someDataPoint"];
}
- (void)setSomeDataPoint:(NSString *)someDataPoint {
[self setObject:someDataPoint forKey:#"someDataPoint"];
}
- (NSInteger)somePrimitiveDataPoint {
return [[self objectForKey:#"somePrimitiveDataPoint"] integerValue];
}
- (void)setSomePrimitiveDataPoint:(NSInteger)somePrimitiveDataPoint {
[self setObject:#(somePrimitiveDataPoint) forKey:#"somePrimitiveDataPoint"];
}
#end
You init the singleton when you have to use it. So as Daji Djan said: lazy wins. Just take attention that, you should not do a long-run process in your applicationWillFinishLaunching, it should return as soon as possible.
If the singleton is not mandatory during applicationWillFinishLaunching, you should call it in viewWillAppear of first view controller if you need to initialize it ASAP.
lazy always wins
if you can get away with it: as late as possible :) AND always do the minimum needed (but do as much as is reasonable to keep your code clean!)

Assign code blocks to a property objective c

I'm attempting to get Background App Refresh going in my iOS application. However, I'm having some trouble understanding code blocks.
I've done some research on it, and would say I have a beginner's understanding so far. The method in question is:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
This method wants a UIBackgroundFetchResult return type. Due to the complexity of my application though, I cannot return that with ease. There's a lot that happens when pulling data from the internet in Background mode.
In the body of that method, I have a custom method that also has a completion block. What I'm trying to do is define another custom method in my code that would be assigned to the completion handler.
In my data manager, I have a property defined as :
#property (copy, nonatomic) void (^fetchCompleted)(UIBackgroundFetchResult);
In the performFetchWtihCompletionHandler method implementation, I call on my data manager:
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
_fetchCompleted = completionHandler;
_state = DMSMT_WaitingForPartnerData;
[self getActiveQueues];
}
Once my downloads are completed, I call on the fetchCompleted method:
[self fetchCompleted];
Herein lies my problem. I need to pass a UIBackgroundFetchResult argument, but I see no way to do that. I tried [self fetchCompleted:UIBackgroundFetchResultNewData]; but it yells at me.
Any ideas?
Thanks in advance.
EDIT:
Here was the fix. So simple!
if(_fetchCompleted != nil){
[self fetchCompleted](UIBackgroundFetchResultNewData);
}
You are treating fetchCompleted as a method but it is a block! Try this out:
-(void)getActiveQueues {
// Do some work here
// When you are finished...
// Set the appropriate result
UIBackgroundFetchResult result;
// Check to make sure fetchCompleted is not NULL
if (self.fetchCompleted) {
// Invoke the block!
self.fetchCompleted(result);
}
}
This method wants a UIBackgroundFetchResult return type
No, it wants a void return type. One of the parameters is of type UIBackgroundFetchResult. Parameters are not return results. UIBackgroundFetchResult is just a type of variable.
Which appears to flow into your error. [self fetchCompleted] is the getter that will return the fetchCompleted variable. It doesn't do anything with it.
To perform a block, use function-like syntax. E.g. [self fetchCompleted]().

How to swizzle AudioSessionAddPropertyListener

There is a library in my project that is adding audio property listeners. I really need to be able to block it from doing so, but I don't have the source code.
I've done a swizzle before for the addObserver method in NSNotificationCenter. Could you help me do the same for AudioSessionAddPropertyListener? If the method trying to be added in the call does not match my whitelist, I want to block it. Otherwise, I'll call the original method.
I don't know what class for which I should overload the load function. I'm looking inside of
Audio.h. I'm adding some pseudo/real/badlyNamed code so you can see what I'm trying to do.
#import <AudioToolbox/AudioToolbox.h>
#interface AuidoClassUmm (SOMETHING)
#end
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(AudioSessionAddPropertyListener:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_AudioSessionAddPropertyListener:selector:name:object:));
method_exchangeImplementations(original, swizzled);
}
-(void) swizzled_AudioSessionAddPropertyListener:selector:name:object:
{
if(//funciton object I don't like)
{
return;
}
else
{
// Calls the original addObserver function
[self swizzled_AudioSessionAddPropertyListener::notificationObserver selector:notificationSelector name:notificationName object:notificationSender];
}
}
AudioSessionAddPropertyListener() is a C function, not an Objective-C method. You can't swizzle it.
(Even if you could swizzle it, that would almost certainly be a bad idea.)

Add additional behavior to method without subclassing

I need to add additional behavior to methods I need to extend, i.e. implement method that looks like
- (void)extendMethod:(SEL)selector forClass:(Class)class withCompletionBlock:(void (^)(void))completionBlock;
So every time Class instance call a method with SEL selector in addition should be invoked my completion block.
I've tried method swizzling, but ran into some problems: I want original method implementation to be called.
What I need is very similar with subclassing, but this should be implemented without subclassing.
UPDATE:
For example I have subclass of UIViewController named MyViewController. MyViewController have - (void)viewDidLoad method. Somewhere in application I call method
[methodExtender extendMethod:#selector(viewDidLoad)
forClass:[MyViewController class]
withCompletionBlock:^{
NSLog(#"view did load called");
}];
So after viewDidLoad method of every instance of MyViewController my completion block invoked.
I'm not sure how you want to use selector, but you can try extend any class(even ones that you don't have implementation file) by using mechanism in Objective-C know as "Categories".
From Xcode click on File->New->File (command+n)
From Cocoa Touch choose Objective-C category
Type name of your category and choose class on which you want to make category (I choosed UIButton)
Then next and Create.
Xcode will create two files for example: UIButton+extendMethod.h
Declare your method in header file and implement it in *.m file.
Using
If you want to use in let's say your View Controller in *.h file import
#import "UIButton+extendMethod.h"
and then you can call your method like this:
UIButton *button = [[UIButton alloc] init];
[button extendMethod:#selector(yourMethod:)];
Swizzling does allow you to call the original implementation, though it is just a bit confusing. Because the implementations are swapped after swizzling, you call the original implementation using the selector of the swizzled method:
- (void)mySwizzledImplementation {
// do stuff
// now call original implementation (using swizzled selector!)
[self mySwizzledImplementation];
// do more stuff
}
See also http://cocoadev.com/wiki/MethodSwizzling
There is no way (anymore)to simulate inheritance without subclassing. There use to be Posing, method swizzling is all that is left (not as elegant as posing though). Here is one way to do method swizzling correctly while being able to call the original implementation.
int swizzle_instance_methods(Class class, SEL selector, IMP replacement, IMP *store) {
#synchronized(class) {
Method method = class_getInstanceMethod(class, selector);
IMP original_imp = NULL;
if (method != NULL) {
const char *type = method_getTypeEncoding(method);
IMP original_imp = class_replaceMethod(class, selector, replacement, type);
if (original_imp == NULL)
original_imp = method_getImplementation(method);
if (original_imp != NULL && store != NULL) {
*store = original_imp;
}
}
return (original_imp != NULL);
}
}
+ (void) load
{
static IMP originalMethodImpl = NULL;
IMP customMethodImpl = imp_implementationWithBlock(^(id self_) {
NSString *descr = ((NSString(*)(id,SEL))originalMethodImpl)(self_, #selector(description);
return [NSString stringWithFormat:#"<--- %# --->",descr];
});
swizzle_instance_methods([self class], #selector(description), customMethodImpl, &originalMethodImpl);
}
I might add that this is nice for debugging and I think that it can be greate for building excellent frameworks. Alas, Apple seems to think differently and using method swizzling can result in your app being excluded from the App store. If you are not aiming for the app store then all the power to you.
It is be possible with ObjC categories. For ex, you can extend hasPrefix method of NSString as follows,
-(BOOL)hasPrefixx:(NSString *)aString
{
NSLog(#"Checking has prefix");
return [self hasPrefix:aString];
}
If you import the category, you should be able to call this method. But his means you got change the method in your call.
By the way, Method swizzling should work. Bit of explanation here.

Resources