implementing one method from another in Objective-C, combining plugins - objective-c

I'm trying to combine two plugins written in Objective-C. I have Plugin1 and Plugin2 which execute fine enough independently. I'm trying to add the Plugin2.m code to my Plugin1 Classes folder and execute both at the same time.
When I do this, Plugin1.m seems to execute first, I guess because it has IBAction calls and Plugin2.m doesn't? This is fine, but I'd like to run Plugin2.m within a function in Code1.m. So In the code below, when the IBAction call in Plugin1 is initiated I would like it to do what Plugin2 normally does and then continue with Plugin1 methods.
Plugin1.h:
#import Plugin2.h
#interface Plugin1: NSWindowController {
...
}
+(void) Plugin2;
#end
Plugin1.m:
#import "Plugin1.h"
#import "Plugin2.h"
#implementation Plugin1
-(id) loadPlugin1
{
...
}
-(IBAction) computeStuff:(id)sender
{
[self Plugin2];
//Plugin2* testRun = [Plugin2 alloc] init];
...do other stuff
}
#end
Plugin2.h
#interface Plugin2 : PluginFilter {
...
}
#end
Plugin2.m:
#import Plugin2.h
#implementation Plugin2
-(void) initPlugin
{
...
}
#end
Unfortunately I can't troubleshoot this from within Xcode, I have to install and test the plugin on my program to test. But when I keep an eye on Console and try the above I get "-[Plugin1 Plugin2]: unrecognized selector sent to instance 0x7....

in your plugin1 interface you defined
+(void)plugin2;
but implemented no method
+(void)plugin2 {
}
when you invoke + method you need to tell which class you call the method from because self refers to an object and not the class.
[self.class plugin2];
// OR
[Plugin1 plugin2];
Hints: try to follow naming conventions in objective-c.
This will help you distinguish if a definition is a -method: or Class
Consider reading about delegate design patterns and the use of <Protocols>
Also define special -initPlugin in your Plugin2 implementation and use a return type. Otherwise you initialise nothing.
-(instancetype)initPlugin;

Related

How to initialize a subclass when the init method of the superclass is NS_UNAVAILABLE

I'm attempting to write a test for an objective-c class. The class I'm trying to test is MyClass and it looks like this:
#interface MyClass : NSObject
- (void)dispatchEvent:(IMAAdEvent *)event;
#end
In order to test this dispatchEvent method, I need to pass in an instance of IMAAdEvent. The IMAAdEvent class comes from Google's library GoogleAds-IMA-iOS-SDK.
Unfortunately, I can't call init on this class because the init method is marked as NS_UNAVAILABLE. In XCode I get an error that reflects this:
'init' in unavailable
Ideally, I would like to make my own mock subclass of IMAAdEvent like this. Is there some way I can initialize my subclass without calling the unavailable init method on the superclass?:
#interface MockImaAdEvent : IMAAdEvent
#end
#implementation MockImaAdEvent
- (id)init {
// is there something I can do here so that I return an instances
// of the subclass without calling [super init]?
}
#end
As of Xcode 12.5, Swift is no longer able to use the previous solution. The compiler has started returning errors for init() is unavailable on lines of code where new init functions have been added. The Xcode 12.5 Release Notes indicate the following:
Clang now infers the availability of +new from availability annotations on -init methods. Since +new calls [[Foo alloc] init], +new isn’t available unless +init is available.
Despite this release note, there is still a valid workaround. By writing the mock class in Objective-C and using a bridging-header to bring it into Swift, the mock class can still call super.new to get a new instance of the parent class (and then customize the subclass from there).
Here is an example:
#interface MockInAppMessagingCampaignInfo ()
#property (strong, nonatomic) NSString *campaignNameValue;
#end
#implementation MockInAppMessagingCampaignInfo
+ (id)newWithCampaignName:(NSString *)campaignName {
MockInAppMessagingCampaignInfo *newObject = super.new;
newObject.campaignNameValue = campaignName;
return newObject;
}
- (NSString *)campaignName {
self.campaignNameWasCalled = YES;
return self.campaignNameValue ?: #"";
}
#end
If I use a method that's not called init then this seems to work. It still seems really weird to not call [super init] in this function, but it's working and returning a new instance of the MockImaAdEvent class
#interface MockImaAdEvent : IMAAdEvent {
enum IMAAdEventType type;
}
#property (nonatomic) enum IMAAdEventType type;
#end
#implementation MockImaAdEvent
#synthesize type;
- (id)initWithType:(NSInteger)_type {
type = _type;
return self;
}
#end
// in my test I can initialize like this:
MockImaAdEvent *adEvent = [[MockImaAdEvent alloc] initWithType:kIMAAdEvent_LOADED];
An alternative solution for Xcode 12.5 that doesn't require Objective-C bridging-header is to create a custom static initialiser that uses the objc runtime to invoke new.
I have used that for fakes that subclass objects with unavailable initialisers and works great.
Example:
static func customInit() -> SomeObjectFake {
let instance = SomeObjectFake.perform(NSSelectorFromString("new")).takeRetainedValue() as! SomeObjectFake
...
return instance
}

Calling a Method on Watchkit

I attended to a watchkit hackathon yesterday and I had some problems regarding calling a method on an NSObject class which uses the Google Maps API and send local notifications. If I call this method from my Watchkit extension, the code doesn't compile, but If I call from the ViewController, for example, everything works perfectly
#import "InterfaceController.h"
#import "Methods.h"
#interface InterfaceController()
#end
#implementation InterfaceController
- (instancetype)initWithContext:(id)context {
self = [super initWithContext:context];
if (self){
// Initialize variables here.
// Configure interface objects here.
NSLog(#"%# initWithContext", self);
}
return self;
}
- (IBAction)butRoute
{
Methods *mt = [[Methods alloc]init];
[mt notif:#"ARRIVING!"];
//***** If I call this method, my code won't compile!!! *****
}
- (void)willActivate {
// This method is called when watch view controller is about to be visible to user
NSLog(#"%# will activate", self);
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
NSLog(#"%# did deactivate", self);
}
#end
The error I get is:
Check the target for your Methods class and make sure it is in your watch kit extensions target.
Alternately, look at building a framework for your shared classes. https://developer.apple.com/videos/wwdc/2014/?id=416
I don't know what xcode version you are using but take into account that the initWithContext method is no longer valid. You should be using:
- (void)awakeWithContext:(id)context
And you shouldn't be overwriting it, just use it.
just remove #import line and replace it with WatchKit framework.

Abstract class in Objective-C

I need your help. Following problem in Objective-C:
// Robot.h
#protocol RobotProtocol <NSObject>
-(void)doWork;
#end
#interface Robot : NSObject
// Rob1 sublass of class Robot
// rob1.h
#interface Rob1 : Robot <RobotProtocol>
// rob1.m
#implementation
-(void)doWork
{
// print 'robot 1'
}
// Rob2 sublass of class Robot
// rob2.h
#interface Rob2 : Robot <RobotProtocol>
// rob2.m
#implementation
-(void)doWork
{
// print 'robot 2'
}
// Task.h
#interface Task : NSObject
{
Robot *rob;
}
// Task.m
#implementation
- (id)init
{
if ([super init]) {
rob = [[Rob1 alloc] init]; // or Rob2 !!
}
return self;
}
-(void)doSomething
{
[rob doWork]; // how to make sure, that this is implemented, depending on Rob1 or Rob2
}
How should Robot and its subclasses be implemented, that Robot *rob can be one of the subclasses of Robot rob1, rob2, ... and the method doWork:(BOOL)val; can be called? My first idea was to implement Robot as an abstract class, but unfortunately there are no abstract classes in Objective-C...
At the moment I am using a protocol, but I am not confident. Because it is not sure, that doWork is implemented, the compiler complains about
'Robot' may not respond to 'doWork'
Thank you for your ideas.
Protocols should work.
#protocol RobotProtocol <NSObject>
#required
- (void)doWork:(BOOL)flag;
#end
#interface Robot1 : NSObject <RobotProtocol>
#end
#implementation Robot1
- (void)doWork:(BOOL)flag
{
}
#end
Unit Test for Robot1 called though id<RobotProtocol>
- (void)testRobot
{
id<RobotProtocol> robot = [[Robot1 alloc] init];
[robot doWork:YES];
}
Update
After looking at your code, #interface Robot : NSObject should be #interface Robot : NSObject <RobotProtocol>. The thing is you don't need #interface Robot : NSObject <RobotProtocol> at all. You can just use id<RobotProtocol>. This is the name of your abstract class.
As many stated, there are no abstract classes in Objective-C. Personally I go for comments + runtime check. Code like this:
#interface Abstract : NSObject {
}
// Abstract method
- (Foo*)doSomething;
#end
#implementation Abstract
// Abstract method
- (Foo*)doSomething {
[_self doesntRecognizeSelector:_cmd];
return nil;
}
#end
If you really want to use something "abstract" and have compile time checks, I think you'll have to use protocols. To do so you need to slightly change your code. Particularly you should declare your variable as conforming to protocol, either id<RobotProtocol> or Robot<RobotProtocol>* depending on what better suits your needs:
// Task.h
#interface Task : Robot <RobotProtocol>
{
id<RobotProtocol> rob;
}
I'm guessing you're trying to do something like this but without code it's hard to say...
//Robot.h
{
-(void)doWork:(BOOL)myBool
}
//Robot.m
{
-(void)doWork:(BOOL)myBool
{
if (myBool)
{
//do something
}
else
{
//do something else
}
}
}
//Task.h
{
#include"Robot.h"
}
//Task.m
{
Robot *rob = [[Robot rob] alloc] init];
[rob doWork:YES];
}
Objective-C does not have something called abstract classes like you have in java. Just create a superclass called Robot and subclass it.. you could potentially even block instancing the class by overriding init and immediately destroying itself ( though I am not too sure about the 'corectness' of such an approach.. )
In any case, it shouldn't be an issue.
Also a handy trick to figure out whether a certain object responds to a method is by using
if( [rob respondsToSelector:#selector(doWork:)] ) { [rob doWork:value]; }
this is often used in order to make methods in a protocol optional, so that you do not get errors at runtime when you call a method that does not exist for a given object.
Does whatever method is trying to call doWork know about the RobotProtocol protocol? You may need to import whatever header file contains it.
For what it's worth, the accepted way to do what you want is to just declare whatever the superclass is (Robot), declare and implement methods with empty bodies (possibly throwing an NSException if they aren't overwritten by a subclass) and create your subclass (SubRobot, or what have you). Protocols are more so different types of classes can implement a few known methods, not for use as de-facto abstract superclasses.

Threading in Objective C

I am new to Objective-C and i was trying out a sample program in Threads from the book "Learn Objective-C for java developers".
I am getting 6 errors on the function definition.
Its with errors.
Is there any link that gives good threading example for beginners like me.
Thread2.m
#import <Foundation/Foundation.h>
#import "Process.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Process* process = [Process new];
NSProgressIndicator* indicator = [NSProgressIndicator new];
Heartbeat* heartbeat = [Heartbeat startHeartbeatProcess:process withIndicator:indicator];
[heartbeat stop];
[pool drain];
return 0;
}
Process.h
#import <Foundation/Foundation.h>
#interface Process : NSObject {
}
#property double progress;
#end
#interface NSProgressIndicator : NSObject {
}
#end
#interface Heartbeat : NSObject {
#public
NSThread* thread;
NSProgressIndicator* indicator;
Process* monitor;
}
+(Heartbeat*)startHeartbeatProcess:(id)process withIndicator:(NSProgressIndicator*)progress;
-(void)stop;
-(void)heartbeatThread:(id)ignored;
-(void)updateIndicator;
#end
Process.m
#import "Process.h"
#implementation Process
+(Heartbeat*)startHeartbeatProcess:(id)process withIndicator:(NSProgressIndicator*)progress {
Heartbeat* heartbeat = [Heartbeat new];
heartbeat->monitor = process;
heartbeat->indicator = progress;
heartbeat->thread = [[NSThread alloc]initWithTarget:heartbeat selector:(heartbeatThread:) object:nil]; //'heartbeatThread' undeclared
[heartbeat->thread start];
return heartbeat;
}
-(void)stop {
[thread cancel]; //thread undeclared
}
-(void)heartbeatThread:(id)ignored {
while (![thread isCancelled]) {
//thread undeclared
[self performSelectorOnMainThread:#selector(updateIndicator) withObject:nil waitUntilDone:YES];
[NSThread sleepForTimeInterval:0.5];
}
}
-(void)updateIndicator {
[indicator setDoubleValue:monitor.progress];
}
#end
Could not find the setDoubleValue method in the class NSProgressIndicator.
Could not find the setDoubleValue method in the class NSProgressIndicator
For this one, that's because NSProgressIndicator is part of AppKit (the Cocoa GUI library), and you're only linking against Foundation (which is the non-GUI stuff). It seems in your code you've attempted to define an interface for NSProgressIndicator yourself, but you haven't declared any methods on it — that's why it's complaining about not being able to find the setDoubleValue method.
What should you do about it? Well, if you're wanting to use Cocoa's GUI stuff, you need to structure your program in the way Cocoa's GUI system expects. In Xcode, if you create a new Cocoa application it should give you a sample project to build on. In particular, your main() function should contain return NSApplicationMain(argc, (const char **) argv);, which handles starting a run loop to receive events.
If you just want to learn about threading, it may be better to abandon trying to get GUI stuff in the same program, and adapt your code to just print stuff to the console instead.
I find it hard to believe that this is an example from a book, since it seems fairly fundamentally broken!
Other errors I found when I tried running it:
Expected ')' before ':' token
This is on the line heartbeat->thread = [[NSThread alloc]initWithTarget:heartbeat selector:(heartbeatThread:) object:nil];.
The problem there is the syntax for declaring a selector: instead of just saying selector:(heartbeatThread:), you need to say selector:#selector(heartbeatThread:).
'thread' undeclared (first use in this function'
In your header file, you claimed that the class Heartbeat has a method called stop. (That is, you defined -(void)stop; in the #interface section for the Heartbeat class).
However, you implemented that method in the #implementation section for the Process class.
You'd make it easier for yourself if you had one pair of .h and .m files per class, rather than trying to cram multiple class definitions into a single pair of files. That way you could make sure you were putting the implementation of the stop method in the correct class's .m file.
property 'progress' requires method '-progress' to be defined - use #synthesize, #dynamic or provide a method implementation
In the implementation for process you defined an #property called progress. If you define a property, you either have to write getters and setters for it yourself, or write #synthesize progress within your implementation. Doing the latter is equivalent to Objective-C generating your getters and setters automatically at runtime.
thread is not a member of the Process class; it belongs to the Heartbeat class. You have to define a member in the Process class to keep a reference on the Heartbeat instance so you can call methods on its thread member.

Cocoa: Calling a method from AppDelegate.m

In order to better understand the startup, event queue, and methods within my application I'm trying to write a program that does two things: Play a beep at the startup and every time the user hits a button. So far it only plays when the user hits the button. I know there may be multiple ways to get the startup beep to play, but in order to work with initialization code I want to do it by calling my beep method from within the applicationDidFinishLaunching method of the AppDelegate.m file.
Here is my code:
Log.h
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m
#import "Log.h"
#implementation Log
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[self beepAndLog];
}
#end
And the applicationDidFinishLaunching method looks like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[Log beepAndLog];
}
In the applicationDidFinishLaunching method, however, XCode warns me that
'Log' may not respond to '+beepAndLog'
and indeed, there is no beep and the log reads as follows:
MethodResponse[11401:a0f] +[Log
beepAndLog]: unrecognized selector
sent to class 0x100002100
("MethodResponse" is the name of my project, btw)
I'm unsure why Log wouldn't respond to beepAndLog, seeing as that's one of its methods. Am I calling it incorrectly? I have a feeling this will be painfully obvious to you more experienced people. I'm a newbie. Any help would be appreciated! Thanks!
There are two possibilities. Either you defined beepAndLog as an instance method, when you wanted a class method, or you want to call it on an instance when you called it on the class.
To change it to a class method, change the header to read:
+(void)beepAndLog;
and the implementation:
+(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
For the other solution, make sure you have an instance of class Log around (probably a singleton), and do something like:
[[Log logInstance] beepAndLog];
from your notification method. The Log class would need to look something like this:
Log.h:
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
+(Log *)logInstance;
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m:
#import "Log.h"
Log *theLog = nil;
#implementation Log
+(Log *)logInstance
{
if (!theLog) {
theLog = [[Log alloc] init];
// other setup (like hooking up that IBAction)
}
return theLog;
}
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[[Log logInstance] beepAndLog];
}