Calling a Method on Watchkit - objective-c

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.

Related

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

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;

KVO addObserver logic crashes in XCTest test method

I am having some difficulty with key-value-observing logic in conjunction with XCTest (the original code is being retrofitted with test coverage). The logic works fine in the normal (non-test) context, but blows out with an exception every time in the context of a test.
The gist of the logic is this -- I have two classes, call them Service and Helper. Scaffold implementations are:
interface Service : NSObject {
BOOL svcCallComplete;
}
#end
#implementation Service
- (id) init {
if ((self=[super init])==nil) {
return nil;
}
return self;
}
#end
interface Helper : NSObject {
}
#end
#implementation Helper
- (id) init {
if ((self=[super init])==nil) {
return nil;
}
return self;
}
#end
Helper is an observer of an attribute in Service. In the context of my normal runtime logic I do this with a call to a Service instance method addSvcObserver:
Service.m:
- (void) addSvcObserver:(id)observer {
[self addObserver:observer
forKeyPath:#"svcCallComplete"
options:NSKeyValueObservingOptionNew
context:nil];
}
Helper complies with the KVO observing pattern thus:
Helper.m:
- (void) observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
}
Pretty straight-forward, and I won't go into the logic for monitoring the attribute change as the problem occurs way before that -- if I have a code excerpt like:
Service* service = [[Service alloc] init];
Helper* helper = [[Helper alloc] init];
[service addSvcObserver:helper];
there are no problems in the non-test case (i.e., this and associated KVO logic works as expected). However the addSvcObserver call when performed in the context of an XCTest test method produces an immediate access denied exception.
I've included an exception "break on all" breakpoint -- the problem seems to be occurring in objc_registerClassPair during the addObserver:forKeyPath:options:context: call. The test target has ARC explicitly disabled as the project for which it is providing test coverage (the target is iOS7) is non-ARC for legacy reasons; this does not seem to cause any problems with other tests.
Thoughts?
interface Service : NSObject {
}
#property (nonatomic) BOOL svcCallComplete;
You should declare svcCallComplete as a property.
because The observed class must be key-value observing compliant for the property that you wish to observe
The reason you get the objc_registerClassPair i think maybe because KVO dynamic register a subclass of your Service , but could not find a setter method of svcCallComplete.which the dynamic subclass needs to override that setter method and send notification.
For more detail read this.
The cause of this turned out to be that my implementation of the KVO logic was incomplete. According to the guide here, you must override the NSObject implementation of automaticallyNotifiesObserversForKey: when using manual change notification -- I somehow missed that in my initial read of the text. I added the following to my Service class:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:#"svcCallComplete"]) {
automatic = NO;
} else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
and all is now correct in the test case. Anyone care to hazard a guess as to why this was not blowing out in the normal (non-test) case?

App Crashes whenever empty method is called

so i was programming a game in Xcode 3.2.4 with cocos2d and i made a class named player. as of now it only has the one function that does nothing.
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Player : CCSprite {
}
-(void)leftButtonPressed;
#end
#import "Player.h"
#implementation Player
- (id)init{
if((self=[super init])){
self = [CCSprite spriteWithFile:#"playerPlane.png"];
}
return self;
}
-(void)leftButtonPressed{
}
#end `
whenever i try to call leftButtonPressed from anywhere, the entire app crashes with this in the console
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CCSprite leftButtonPressed]: unrecognized selector sent to instance 0x6e4bb50'
i initialize the Player as an instance in another class by doing
Player * thePlayer = [[Player alloc]init];
please help! thanks!!
This causes the problem:
- (id)init{
if((self=[super init])){
self = [CCSprite spriteWithFile:#"playerPlane.png"];
}
return self;
}
What are you doing? You initialise the newly created (the caller that calls alloc/init created it) Player object. The init method may even have returned another instance than the one that is currently executing (some strange unique cocoa thing). And when that was successful then you crate a new CCSprite object and assign it to self. Self is then returned to the caller.
(If you would not ARC - I assume you do - then this for sure would create a memory leak);
But the caller expects to deal with a Player object and later sends the leftButtonPressed message to it which the CCSprite cannot respond to.
That is, what the error message tells you.
You may use instead:
- (id)initSpriteWithFile(NSString*)fileName{
if((self=[super initSpriteWithFile:fileName])){
// Do any additional setup here. If you don't have any setup then do not overwrit the init method at all.
}
return self;
}
or something like:
- (id)init{
if((self=[super init])){
[self setFileName:#"playerPlane.png"]; //This method may not exist at all but you may find an appropriate one in the docs. I am not used to CCSprit. I am just trying to explain the concept by example.
}
return self;
}
You are calling self = [CCSprite spriteWithFile:#"playerPlane.png"]; but this creates a CCSprite instance rather than a Player instance.
I am guessing you do not have the source of CCSprite or there isn't a non-factory based initializer?
In this case, you would be better creating a class extension rather than subclassing.

Dropbox Delegate methods not being called

I have a problem with the Dropbox API. I'm working on big app, which was started by another developer. Now I'm going to clean the code. All the Delegate Methods (loadedMetadata & Co.) were directly in the view. Now I want to extract them into their own class. So I created a DropboxService class with all the methods in it. So I have the view and call the method loadMetadata from the DropboxService. The method is called and woking fine. But the Delegate method loadedMetadata is never called.
What did I do wrong / what do I have to change to get this working correctly?
The Dropbox Service has the DBRestClientDelegate as a "Superclass" (don't know how it i exactly called)
#interface DropboxService : CloudProviderService <DBRestClientDelegate> {
}
Edit:
The Service is instantiated in the AppDelegate and is a variable there:
- (DropboxService *)getDropboxService {
if (self.dropboxService == nil) {
self.dropboxService = [[DropboxService alloc] init];
}
return self.dropboxService;
}
Greetings from Germany
Alexander
You need to also set the delegate after init
DBRestClient.delegate = self;
from this interface
#interface DBRestClient : NSObject { ... id<DBRestClientDelegate> delegate;
That header doesnt say that DropboxService is a sub-class of DBRestClientDelegate
it says that DropboxService conforms to the protocol of DBRestClientDelegate
The Dropbox sample project explains all this, but you want to look for where you set up DBRestClient and make sure DropboxService is made the delegate of that instance.
This is what it looks like in my code which conforms to DBRestClientDelegate
- (DBRestClient*)restClient {
if (restClient == nil) {
restClient = [((DBRestClient *)[DBRestClient alloc]) initWithSession:[DBSession sharedSession]];
restClient.delegate = self;
}
return restClient;
}

Objective-c class extensions not effectives in static library

while creating a library that will be used on several projects, I encountered an error that I was not able to resolve by myself.
The library is composed of several "modules" that each declares its set of classes. The modules declares a header file that references the classes. Each module header is included in the library header, and all of them are copied to the library target.
The "GMData" module defines the ORM layer of the library, it declares a "GMInitializerBase" class, its purpose is to initialize the module. It must be called once in the UIApplicationDelegate.
The "GMModel" module contains the base model for the application (Categories, Articles, ...), It must register itself to "GMData" in order to function properly.
Structure:
<Library Root>
Library.h
<GMData>
GMData.h
GMInitializerBase.{h,m}
<GMModel>
GMModel.h
GMInitializerBase+GMModel.{h,m}
Contents of Library.h
#import "GMData.h"
#import "GMModel.h"
Contents of GMData.h
#import "... ORM related headers ..."
#import "GMInitializerBase.h"
Contents of GMInitializerBase.{h,m}
#import "... ORM Classes ..."
#interface GMInitializerBase : NSObject {
}
+ (void) bootstrap;
+ (GMInitializerBase*) initializer; // autoreleased instance creator
- (void) setup;
- (void) setupStore:(GMManagerFactory*)factory; // Setup database connection
- (void) setupHelpers:(GMHelperFactory*)factory; // Register helpers (abstract)
- (void) setupManagers:(GMManagerFactory*)factory; // Register managers (abstract)
#end
#implementation GMInitializerBase
+ (void) bootstrap {
GMInitializerBase* initializer = [self initializer];
[initializer setup];
}
- (void) setup {
/* Breakpoint 01 */
GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];
[self setupStore:managerFactory];
[self setupHelpers:helperFactory];
[self setupManagers:managerFactory];
}
#end
Contents of GMModel.h
#import "... Base Models files ..."
#import "GMInitializerBase+GMModel.h"
Contents of GMInitializerBase+GMModel.{h,m}
#interface GMInitializerBase (GMModel_Additions)
- (void) setup;
- (void) setupGMModelHelpers:(GMHelperFactory*)factory;
- (void) setupGMModelManagers:(GMManagerFactory*)factory;
#end
#implementation GMInitializerBase (GMModel_Additions)
- (void) setup {
/* Breakpoint 02 */
GMHelperFactory* helperFactory = [GMHelperFactory sharedInstance];
GMManagerFactory* managerFactory = [GMManagerFactory sharedInstance];
// parent implementation
[self setupStore:managerFactory];
// current implementation
[self setupGMModelHelpers:helperFactory];
[self setupGMModelManagers:managerFactory];
// parent implementation
[self setupHelpers:helperFactory];
[self setupManagers:managerFactory];
}
- (void) setupGMModelHelpers:(GMHelperFactory*)factory { /* ... */ }
- (void) setupGMModelManagers:(GMManagerFactory*)factory { /* ... */ }
#end
Contents of ProjectAppDelegate.m (located in another project, it includes the library.a and search the "Headers" directory)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[GMInitializerBase initializer] setup];
}
Stops at the first breakpoint (Breakpoint 01)
It crashed when in the library:
I declare an addition without overloading a method;
I declare an addition to a Cocoa class ([NSString toto]) without overloading;
In works when in the test project:
I declare an addition to a Cocoa class ([NSString toto]) without overloading;
I didn't try to overload a library class but I assume it will work too.
My problem is the following: I can't get the addition workingm and I need it.
Thanks for reading, thanks for answering.
Make sure you have the -all_load and -ObjC flags set in the "Other Linker Flags" in the project settings. Categories in a library won't work without them.
In Objective-C, you shouldn't override the method in a category of a class. Say you have
#implementation MyClass
-(void)foo
{
NSLog(#"%#",#"original!");
}
#end
and later you have
#implementation MyClass (MyCategoryA)
-(void)foo
{
NSLog(#"%#",#"categoryA!");
}
#end
#implementation MyClass (MyCategoryB)
-(void)foo
{
NSLog(#"%#",#"categoryB!");
}
#end
Then the result of
MyClass* myInstance=...;
[myInstance foo];
is not reliable, see the discussion in the official documentation. The documentation says it works if you have only one category, but the documentation says at the same time you shouldn't use that feature. So, don't do this.
The sole exception is +load. If a category defines this method, the runtime calls it for each category you define. So, if you want to perform some initialization task per category, +load is the way. Read the official documentation and this blog post by Mike Ash.