Dropbox not calling delegate methods on iPad only - objective-c

I'm using dropbox to upload files to a user's Dropbox.
On iPhone it works flawlessly, but on iPad the delegate methods for the DBRestClientDelegate are not being called.
I am still using v1.
The code I'm using is
- (DBRestClient *)restClient {
if (!_restClient) {
_restClient =
[[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
_restClient.delegate = self;
}
return _restClient;
}
[[self restClient] uploadFile:[NSString stringWithFormat:#"%#.jpeg",fileName]
toPath:#"/"
withParentRev:nil
fromPath:imagePath];
After calling this on the iPhone the delegate method
- (void)restClient:(DBRestClient*)client uploadedFile:(NSString*)destPath from:(NSString*)srcPath
is successfully called, allowing me to handle that. On the iPad, however, that, along with the other delegate methods, are not called, does not upload the file and does not throw any errors.
Any thoughts are appreciated.
Thanks,
Luke

There are a few things that might cause your delegate methods to not be called:
Your rest client is nil or is being released (e.g., by ARC) prematurely.
You're making the call in a background thread that doesn't have a run loop.
Your delegate method that should be called back has a typo in it. Unfortunately the SDK doesn't warn you if it can't find a delegate method to call; it just completes without telling anyone.

Related

"[GCController controllers]" does not contain any controllers that were connected prior to application launch

"[GCController controllers]" does not contain any controllers that were connected prior to application launch
TLDR;
I am trying to implement gamepad input on macOS using the Game Controller Framework. When invoked in my code, [GameController controllers] always returns an empty list until new controllers are connected. It never reflects gamepads connected to macOS prior to application launch, except if you disconnect them and reconnect them while the app is running. Does anyone know what I need to do to make controllers populate with pre-launch connections?
Full question
Now that Apple has added support for Xbox and Playstation controllers to the GameController framework, I'm trying to use it for gamepad input on a C++ game engine I'm developing. I'm using the framework instead of IOKit in order to "future-proof" my games to support additional controller types in the future, as well as to simplify my own input handling code.
Like many other game engines, I've foregone using NSApplicationMain() and nib files in favor of implementing my own event loop and setting up my game window programmatically. While my "Windows style" event loop appears to be working correctly, I've discovered that [GCController controllers] does not. The array it returns is always empty at launch, and will only ever reflect controllers that are connected while the game is running. Disconnecting a pre-connected controller does not trigger my GCControllerDidDisconnectNotification callback.
Here is a simplified version of my event loop:
int main(int argc, const char * argv[])
{
#autoreleasepool
{
// Create application
[NSApplication sharedApplication];
// Set up custom app delegate
CustomAppDelegate * delegate = [[CustomAppDelegate alloc] init];
[NSApp setDelegate:delegate];
// Activate and launch app
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
[NSApp activateIgnoringOtherApps:YES]; // Strictly speaking, not necessary
[NSApp finishLaunching]; // NSMenu is set up at this point in applicationWillFinishLaunching:.
// Initialize game engine (window is created here)
GenericEngineCode_Init(); // <-- Where I want to call [GCController controllers]
NSEvent *e;
do
{
do
{
// Pump messages
e = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: nil
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (e)
{
[NSApp sendEvent: e];
[NSApp updateWindows];
}
} while (e);
} while (GenericEngineCode_Run()); // Steps the engine, returns false when quitting.
GenericEngineCode_Cleanup();
}
return 0;
}
I've confirmed that even when using [NSApp run] instead of [NSApp finishLaunching], the behavior is the same. As best as I can tell, the problem is that there's something NSApplicationMain() does that I'm not doing, but that function is a black box -- I can't identify what I need to do to get controllers to populate correctly. Does anyone know what I'm missing?
The closest thing I could find to an explanation of this problem is this answer, which suggests that my app isn't getting didBecomeActive notifications, or that at the least, the private _GCControllerManager isn't getting a CBApplicationDidBecomeActive message. I'm not a professional macOS developer, though: I don't know if this actually applies to my situation, or how I'd go about correcting the problem if it does.
After a huge amount of time searching, I found the answer on my own. It turns out that my code wasn't the problem -- the problem was that my Info.plist file was having its CFBundleIdentifier value stripped out due to a problem with my build system. It appears that the Game Controller Framework needs the bundle identifier to correctly populate [GCController controllers] at launch. While a missing CFBundleIdentifier would have been a problem anyway, as a Windows person it didn't occur to me that the identifier might be used for things besides the App Store, so I let it slide until now.
If someone else has this problem, make sure that CFBundleIdentifier isn't missing or empty in Info.plist in your assembled app bundle. In my case with Premake, I had to manually set PRODUCT_BUNDLE_IDENTIFIER with xcodebuildsettings so that $(PRODUCT_BUNDLE_IDENTIFIER) would get properly replaced in Info.plist.

How to ensure UIManagedDocument is ready before NSFetchedResultsController method is called?

I am totally stuck on this one. My basic problem is that my:
- (NSFetchedResultsController *)fetchedResultsController
Method is crashing when it tries to read my Core Core entity, as the managedObjectContext / UIManagedDocument is nil. At the moment I think it is because my UIManagedDocument is not open / ready. So for the last 3 hours I have been trying to make it so my delegate method is not fired until the document is open.
This is the code that I am using to get the document:
if (!self.document) {
[[CATManagedDocumentHandler sharedDocumentHandler] performWithDocument:^(UIManagedDocument *document) {
self.document = document;
}];
}
This works fine at any other place in my app, but it seems as the opening process is just not quick enough for the delegate methods in my tableView.
Links I have looked at so far:
http://omegadelta.net/2011/05/10/how-to-wait-for-ios-methods-with-completion-blocks-to-finish/
Executing Code Block In Place Of #selector
About calling of dispatch_queue_t and dispatch_sync
Grand Central Dispatch (GCD) vs. performSelector - need a better explanation
GCD to perform task in main thread
iOS - how to be notified when a thread (using GCD) ends it's job
I have tried: Blocking main thread until I get NSNotification (set up in CATManagedDocumentHandler) & Blocking main thread until I get a block call back.
Neither of these work. My app just freezes. Am I think about this wrongly? How can I get the delegate method to wait until my document is open / ready? Or is there a different approach I should be taking with this?
Thanks
Carl.
ok, when your app first starts, I would suggest checking whether 1. your database exists (if it doesn't, you alloc init it) and 2. if the document doesn't exist (on disk), is closed or is open.
here's how you could do this:
looks like you are using a singleton for your database, so when your first view controller comes up, check whether the Managed Document has been alloc inited, so in ViewWillAppear:
if (!dataBase)
{
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Your DB Name"];
self.dataBase=[[UIManagedDocument alloc]initWithFileUrl:url];
}
so now if the database (UIManagedDocument) has been alloc inited, it still does not mean that the actual database file has been created on disk. You have to check for it as follows (and you could do this in a setter for your database or in ViewDidLoad, but don't do it in another thread because it won't work)
so you are checking for 3 cases here: does not exist
if (![[NSFileManager defaultManager]fileExistsAtPath:[yourManagedDocumentFromSingleton.fileUrl path])
{
[[NSFileManager defaultManager]fileExistsAtPath:[yourManagedDocumentFromSingleton saveToUrl:[[NSFileManager defaultManager]fileExistsAtPath:[yourManagedDocumentFromSingleton.fileUrl forSaveOperation:UIDocumentSaveForCreating completionHandler:
^(BOOL success){ now you can use your uidocument and its managed object context}];
}
if file does exist but is closed:
else if (yourManagedDocumentFromSingleton.documentState==UIDocumentStateClosed)
{
[yourManagedDocumentFromSingleton openWithCompletionHandler:
^(BOOL success) {use your document here}];
}
finally if the document is already open, just use it:
else if (yourManagedDocumentFromSingleton.documentState=UIDocumentStateNormal)
{
//do whatever with the document
}
one important thing to mention is that UIDocument is not thread safe. So it must be used and checked in the same thread that it was created (presumably main thread here). Otherwise it will not work.
I don't know the exact structure of your view controller or singleton but if you follow these steps it will work.
PS. Also make sure that once your doc is up and running and you're adding items to it or removing, save it after each operation so your NSFetchedResultsController gets updated. CoreData does have autosave but I found that I had to manually save for things to work properly. You can save with (from previous method):
forSaveOperation:UIDocumentSaveForOverwriting

Async call in Objective-C

I'm trying to get data from a website- xml. Everything works fine.
But the UIButton remains pressed until the xml data is returned and thus if theres a problem with the internet service, it can't be corrected and the app is virtually unusable.
here are the calls:
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if(!appDelegate.XMLdataArray.count > 0){
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray.
}
XMLViewController *controller = [[XMLViewController alloc] initWithNibName:#"MedGearsApps" bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
}
It works fine, but how can I make the view buttons functional with getting stuck. In other words, I just want the UIButton and other UIButtons to be functional whiles the thing works in the background.
I heard about performSelectorInMainThread but I can't put it to practice correctly.
You don’t understand the threading model much and you’re probably going to shoot yourself in the foot if you start adding asynchronous code without really understanding what’s going on.
The code you wrote runs in the main application thread. But when you think about it, you don’t have to write no main function — you just implement the application delegate and the event callbacks (such as touch handlers) and somehow they run automatically when the time comes. This is not a magic, this is simply a Cocoa object called a Run Loop.
Run Loop is an object that receives all events, processes timers (as in NSTimer) and runs your code. Which means that when you, for example, do something when the user taps a button, the call tree looks a bit like this:
main thread running
main run loop
// fire timers
// receive events — aha, here we have an event, let’s call the handler
view::touchesBegan…
// use tapped some button, let’s fire the callback
someButton::touchUpInside
yourCode
Now yourCode does what you want to do and the Run Loop continues running. But when your code takes too long to finish, such as in your case, the Run Loop has to wait and therefore the events will not get processed until your code finishes. This is what you see in your application.
To solve the situation you have to run the long operation in another thread. This is not very hard, but you’ll have to think of a few potential problems nevertheless. Running in another thread can be as easy as calling performSelectorInBackground:
[appDelegate performSelectorInBackground:#selector(GetApps) withObject:nil];
And now you have to think of a way to tell the application the data has been loaded, such as using a notification or calling a selector on the main thread. By the way: storing the data in the application delegate (or even using the application delegate for loading the data) is not very elegant solution, but that’s another story.
If you do choose the performSelectorInBackground solution, take a look at a related question about memory management in secondary threads. You’ll need your own autorelease pool so that you won’t leak autoreleased objects.
Updating the answer after some time – nowadays it’s usually best to run the code in background using Grand Central Dispatch:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// No explicit autorelease pool needed here.
// The code runs in background, not strangling
// the main run loop.
[self doSomeLongOperation];
dispatch_sync(dispatch_get_main_queue(), ^{
// This will be called on the main thread, so that
// you can update the UI, for example.
[self longOperationDone];
});
});
Use NSURLConnection's connectionWithRequest:delegate: method. This will cause the specified request to be sent asynchronously. The delegate should respond to connection:didReceiveResponse: and will be sent that message once the response is completely received.
You can make use of a background operation that gets pushed into the operation queue:
BGOperation *op = [[BGOperation alloc] init];
[[self operationQueue] addOperation:op];
[op release];
I've created specific "commands" that get executed in the background:
#implementation BGOperation
# pragma mark Memory Management
- (BGOperation *)init
{
if ((self = [super init]) != nil)
/* nothing */;
return self;
}
- (void)dealloc
{
self.jobId = nil;
[super dealloc];
}
# pragma mark -
# pragma mark Background Operation
- (void)main
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[appDelegate GetApps];
[pool release];
return;
}
#end
After completion it might be a good idea to send a notification to the main thread because the internal database has been changed.
It looks as if you might be using NSURLConnection inside your getApps method. If so, you should convert it to an asynchronous call.

Exception/Error handling in Objective-C (iPhone app)

I actually have two questions regarding exception/error handling in the iPhone app that I am making:
The app uses Internet, but when there's no connection, the app just dies (during launch). How can I handle this to print some infomsg to the user, instead of just getting thrown back to the springboard?
Can someone show me an example of how to handle for instance a "page not found" or "no contact with server" error, so I can give some sort of info to the user in the same way as above?
For crashes, the first step is to use error messages and the debugger to figure out what call is causing the problem. If the problem is caused by an uncaught exception, read this Apple article on exception handling. The specific answer really depends on your code and exactly what is causing the crash, so I won't speculate about a particular solution.
As far as detecting server error response codes (such as 404), that's more specific to WebKit. I assume you're using UIWebView on iPhone, and you've probably noticed that none of the primary methods return errors. This is because it uses a delegate model to report progress or errors asynchronously. (It makes sense because you don't want your UI code to be at the mercy of a slow-loading (or non-existent) webpage. To be notified of such errors, there are a few steps.
Adopt the UIWebViewDelegate protocol, usually in the same class that will start the webpage load for convenience.
Set that object as the delegate of the UIWebView instance. (It has a delegate property, so you can use something like either uiView.delegate = self or [uiView setDelegate:self] based on what you prefer.)
Implement the webView:didFailLoadWithError: method in that class. (You can be notified when the load finishing by implementing webViewDidFinishLoad: as well.) This is where you include the logic of what should happen when an error occurs.
I didn't see any detailed documentation on the content of any particular errors handed back via this delegate method, but it's a standard NSError object, and I recommend checking out the contents by calling its methods, such as -localizedDescription and -userInfo.
Here is some sample code with #import statements excluded for brevity.
MyClass.h
#interface MyClass : NSObject <UIWebViewDelegate> {
IBOutlet UIWebView* myWebView;
}
-(void)webView:(UIWebView*)webView didFailLoadWithError:(NSError *)error;
#end
MyClass.m
#implementation MyClass
- (id) init {
if ((self = [super init]) == nil)
return nil;
// initialize myWebView
myWebView.delegate = self;
return self;
}
- (void) webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error {
...
}
#end
Testing for a connection is pretty easy...
NSString * test = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.stackoverflow.com"]];
if (test == nil) {
//display an alertview saying you don't have an internet connection
}
Using a URL to test for a connection is not a good idea, it is not robust enough to determine if the internet connection is down, the website is down or some other network issue etc and above all it adds an overhead to the call as far as network traffic.
Look at the Reachability demo on the Apple site, it uses the correct way to determine connectivity, including whether you are on wifi etc.

How can I troubleshoot my custom URL scheme?

I set up a simple event handler as mentioned here, but it appears that the selector isn't called. I put the code in my AppDelegate class and wired up the delegate in IB. Tried putting in some NSLog()s and breakpoints in the selector I expect to be called, but none of it is hit. The URL scheme works inasmuch as it launches my app, but it doesn't do anything after that. Can anyone advise how to troubleshoot this? Thanks!
Well, I can't help but notice that you're -init method is mis-declared. If should have return type id and have a return self; at the end.
- (id)init
{
self = [super init];
if (self) {
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
return self;
}
With those fixes, I was able to paste those two routines into a test AppController class and have it print out the URLs (with a custom scheme) that I typed into Safari. I'd put a breakpoint on that -init method and step through it to make absolutely sure that -setEventHandler: method is getting called.
The big question is: Where are you calling NSAppleEventManager's -setEventHandler:...? You need to call this before your app finishes launching if you want to catch a URL that started your app. If your app delegate is created in your MainMenu.nib, then either its -init or -awakeFromNib methods will work, but, for example, -applicationDidFinishLaunching: won't.
Also, make sure that the selector you provide to -setEventHandler: is exactly the same as your method name, paying particular attention to capitalization and the proper number of colons.
Obviously, if you posted your app delegate's relevant code, it would be quite helpful.
Thanks for the suggestions. I double-checked those things. I'm sure it's some newbie mistake, but I'd appreciate anyone looking at the code. (The URL bits are stored in info.plist.) Right now I'm just trying to confirm that it's working before I try to do anything with the URL.
- (void)init{
self = [super init];
if(self){
[[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:#selector(getUrl:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
}
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
NSString *url = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
NSLog(url);
// now you can create an NSURL and grab the necessary parts
}
Newbie mistake #2: Didn't set the class of my app delegate in IB. Fixing this and the init method as above got me going. Grrr...