trouble debugging during runtime - objective-c

I have written a some code for a drill down table app I have been making, but the app crashes only during runtime. Xcode doesn't give me any errors while building the app. The debugger outputs:
2012-10-18 10:58:26.513 second[474:c07] -[NavController setItems:]: unrecognized selector >sent to instance 0xc217a00
2012-10-18 10:58:26.515 second[474:c07] * Terminating app due to uncaught exception >'NSInvalidArgumentException', reason: '-[NavController setItems:]: unrecognized selector sent >to instance 0xc217a00'
* First throw call stack:
(0x14b8022 0xeb8cd6 0x14b9cbd 0x141eed0 0x141ecb2 0x3fbe 0xe2a1e 0x41401 0x41670 0x41836 >0xbfc9dd8 0x4872a 0x19596 0x1a274 0x29183 0x29c38 0x1d634 0x13a2ef5 0x148c195 0x13f0ff2 >0x13ef8da 0x13eed84 0x13eec9b 0x19c65 0x1b626 0x1d40 0x1cd9)
terminate called throwing an exception
I think I understand that the error lies in NavController.m where:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString* path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
MasterViewController* root = (MasterViewController*)self.topViewController;
NSDictionary* thelist = [NSDictionary dictionaryWithContentsOfFile:path];
root.items = [thelist objectForKey:#"Items"];
root.navigationItem.title = [thelist objectForKey:#"name"];
}
btw, I made the array items like this: (nonatomic, retain) NSArray* items;

The app crashes, because of the following line:
root.items = [thelist objectForKey:#"Items"];
This line is just a shortcut for writing:
[root setItems:[thelist objectForKey:#"Items"]];
And the runtime complains that there is no method setItems: found. The reason why there is compile error is because you tell the compiler that root is of class MasterViewController, however, that is not true. At runtime the obj-c runtime finds out that root is in fact of class NavController and it seems like NavController has no setItems: method.
In other words, this line is wrong:
MasterViewController* root = (MasterViewController*)self.topViewController;
You lie about the class; self.topViewController returns an object that is of class NavController, yet you force the compiler to treat it as MasterViewController which will fail as soon as you call a method that is not found for NavController.

The problem is probably here:
MasterViewController* root = (MasterViewController*)self.topViewController;
self.topViewController is proably not an instance of MasterViewController, but a NavController instance and there you go.

Related

Passing NSArray of custom objects as NSData via WatchConnectivity's sendMessageData

Once a WKInterfaceController's didAppear function is fired, I send an empty NSData to WCSession's default session with the sendMessageData callback function:
// WKInterfaceController
NSData *emptyData = [[NSData alloc] init];
[[WCSession defaultSession] sendMessageData:emptyData replyHandler:^(NSData *replyMessageData) {
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:replyMessageData];
} errorHandler:^(NSError *error) {
NSLog(#"WATCH: Error from replyData %#", error);
}];
The emptyData NSData object is sent because sendMessageData: is a non-null argument. I only use it to be able to fire WCSession's Delegate method, didReceiveMessageData on the iOS app. Then the replyHandler in that very function sends the appropriate data back to the replyHandler to the WKInterfaceController.
// UITableViewController
- (void)session:(WCSession *)session didReceiveMessageData:(NSData *)messageData replyHandler:(void (^)(NSData * _Nonnull))replyHandler
{
[self loadData:nil onSuccess:^(NSArray *tips) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:tips];
replyHandler(data);
}];
}
The problem I'm having is that I get a crash on the following line in the WKInterfaceController
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:replyMessageData];
Here's the error I get:
* Terminating app due to uncaught exception
'NSInvalidUnarchiveOperationException', reason: '*
-[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Tip) for key (NS.objects); the class may be defined in source
code or a library that is not linked'
What I've found so far:
The NSArray I'm trying to pass is made of custom objects (Tip.m). I know that all of the objects within the NSArray must conform to the NSCoding protocol (How to convert NSArray to NSData?), which I have done properly in my opinion. I've encoded and decoded every variable and object within the object with initWithCoder and encodeWithCoder.
My Tip.m object should be added to my WatchKit Extension (NSInvalidUnarchiveOperationException cannot decode object error in Apple Watch extension). Adding the Tip.m file only gives me: "Undefined symbols for architecture i386" from other objects.
Sorry for the long post but I've tried everything to find a solution to this problem, without success. Hope this helps more people that are having issues with WatchConnectivity Framework.
I solved this temporarily by using didReceiveMessage (the NSDictionary version instead of the NSData).
I sent a manually created NSDictionary of a single NSArray that held regular NSStrings of my previous custom objects.
I have the same scenario and reached the same problem. After some searching (without any luck) and experimenting, I've solved it by adding the -all_load flag to the linker flags in the extension target.

Bug when unit testing Google Analytics tracker in XCode 6

I'm writing a unit test to check that the string I pass to the GAITracker class is being returned as the kGAIScreenName property for each screen.
However when I try to pass a sharedInstance to the GAI class to initialize the WNGoogleAnalyticsService instance I am getting the error Thread 1: EXC_BAD_ACCESS (code=1, address=0x20) as if it has not been allocated to the memory. No matter where I try to declare the sharedInstance won't initialize in the test class although it works fine in the AppDelegate.m.
WNGoogleAnlayticsServiceTest.m:
-(void)testIfNoScreenNameExists {
NSString *screenName = #"Screen";
Class builder = [GAIDictionaryBuilder class];
GAI *gai = [GAI sharedInstance];
WNGoogleAnalyticsService *s = [[WNGoogleAnalyticsService alloc] initWithGAInstance:gai
gaKey:#"test"
gaDictionaryBuilderClass:builder
debugging:NO];
id<GAITracker> tracker = [s trackerForScreen:screenName];
XCTAssertEqualObjects([tracker get:kGAIScreenName], screenName);
}
AppDelegate.m:
Add Google Analytics as analytics service
WNGoogleAnalyticsService *googleAnalyticsService = [[WNGoogleAnalyticsService alloc] initWithGAInstance:[GAI sharedInstance]
gaKey:[[NSBundle mainBundle] objectForInfoDictionaryKey:#"WNGoogleKey"]
gaDictionaryBuilderClass:[GAIDictionaryBuilder class]
debugging:analyticsDebugging];
I'm at a loss as to how to even go about fixing this bug so any help would be appreciated.
In line with the comment I left underneath my question, I feel I should formally answer this with my solution.
In the case of this unit test, I was trying to re-use a singleton by reusing the [GAI SharedInstance] method in both my WNGoogleAnlayticsServiceTest and in my AppDelegate.m file which, by definition, it cannot do.
So if you want to test the Google Analytics methods, you must use a tool like OCMock to do so as you can't initialise the sharedInstance twice.

Objective-C errors after getting the IAP product response

This code is from the Phonegap Code: IAP Plugin. The error happens on the line of the code right after the "sent js". All the elements sent to the function are non-nil except for the last one 'nil'. I even logged them out to make sure they were sent. This code is right out of the plugin (https://github.com/usmart/InAppPurchaseManager-EXAMPLE) and has not been modified except for the logging. In the debugger i saw that none of the objects were nil, so i don't understand why the error is happening.
Here is the error:
[__NSArrayI JSONRepresentation]: unrecognized selector sent to
instance 0xdc542d0
2013-02-13 23:26:17.209 GoblinSlots[4519:707] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSArrayI JSONRepresentation]: unrecognized selector sent to
instance 0xdc542d0'
here is the code:
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse: (SKProductsResponse *)response
{
NSLog(#"got iap product response");
for (SKProduct *product in response.products) {
NSLog(#"sending js for %#", product.productIdentifier);
NSLog(#" title %#", product.localizedTitle );
NSLog(#" desc%# - %#", product.localizedDescription, product.localizedPrice );
NSArray *callbackArgs = [NSArray arrayWithObjects:
NILABLE(product.productIdentifier),
NILABLE(product.localizedTitle),
NILABLE(product.localizedDescription),
NILABLE(product.localizedPrice),
nil ];
NSLog(#"sent js");
NSString *js = [NSString stringWithFormat:#"%#.apply(plugins.inAppPurchaseManager, %#)", successCallback, [callbackArgs JSONSerialize]];
NSLog(#"js: %#", js);
[command writeJavascript: js];
}
All the stuff to do JSON serialization seems to be already included with the Cordova plugins.
There's no need to download and install yet another JSON library.(a)
It appears that PhoneGap is in the process of switching from SBJson to JSONKit.(b)
PhoneGap is also in the process of changing all the JSON methods to use a "cdvjk_" prefix. (c)
As far as I can tell, something didn't quite go right during those changes.
What I did was edit the file Plugins/InAppPurchaseManager.m , where I made these changes:
Add the line
#import <Cordova/CDVJSON.h>
Replace the line
return [self respondsToSelector:#selector(cdvjk_JSONString)] ? [self cdvjk_JSONString] : [self cdvjk_JSONRepresentation];
with
return [self JSONString];
. (What's the right way to push this or a better bugfix back to the nice PhoneGap people?)
JSONRepresentation is a category that SBJson adds so you have to include SBJson.h in the class that uses it.

How do I load a nib from a secondary process?

I have a process spawned with NSTask that needs to display a window. I started out just writing all the UI code by hand, but this has become a pain.
So, I created a new class with a xib, MyWindowController. I want to load up an instance of this controller in secondary process and have all the IBOutlets and whatnot work properly.
Here's what I've got so far:
// Get the bundle for the main application (not the subprocess).The executable lives in Contents/Helpers, so look two dirs up from its path for the main app bundle root.
NSArray *executablePathComponents = [[[NSBundle mainBundle] executableURL] pathComponents];
NSIndexSet *indexOfEveryComponentExceptLastTwo = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [executablePathComponents count] - 2)];
NSBundle *myBundle = [NSBundle bundleWithURL:[NSURL fileURLWithPathComponents:[executablePathComponents objectsAtIndexes:indexOfEveryComponentExceptLastTwo]]];
// Load the controller nib.
NSNib *windowControllerNib = [[NSNib alloc] initWithNibNamed:#"MyWindowController" bundle:myBundle];
MyWindowController *windowController = [[MyWindowController alloc] init];
NSArray *topLevelObjects = nil;
[windowControllerNib instantiateNibWithOwner:windowController topLevelObjects:topLevelObjects];
This gives me an instance of the window controller and it displays the window from the nib on screen, so this appears to work. BUT, instantiateNibWithOwner:topLevelObjects is deprecated in favor of instantiateNibWithOwner:topLevelObjects.
Using the non-deprecated method results in an exception: "-[NSNib instantiateWithOwner:topLevelObjects:]: unrecognized selector sent to instance 0x10291ab1"
At the very least I'd like to not use the deprecated method. But maybe there is a better way to approach this whole thing?

Adding CoreData to existing Project

I am trying to add CoreData to an existing project and
there is strange error...
I get the error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason:
'Cannot create an
NSPersistentStoreCoordinator with a
nil model'
in RootView there is a warning in:
- (NSFetchedResultsController *)fetchedResultsController;
in line:
_fetchedResultsController.delegate = self;
and it says:
warning: class 'RootViewController'
does not implement the
'NSFetchedResultsControllerDelegate'
protocol
any ideas?
argh!
sorry for even asking, I just missed that I changed the name of .xcdatamodel
I needed just to change the name in managedObjectModel and persistentStoreCoordinator
hope it'll help someone anyway.
I was getting the same exception.
My issue was that I had added the .xcdatamodeld file in the root directory of the project (with siblings Frameworks and Products) instead of in the folder named after the project (with siblings Supporting Files, AppDelegate.h, etc.)
I tracked it down to this line: NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Foo" withExtension:#"momd"];
In this method: - (NSManagedObjectModel *)managedObjectModel