Close app programmatically?

Discussion in 'iOS Programming' started by Danneman101, Mar 27, 2011.

  1. Danneman101 macrumors 6502

    Joined:
    Aug 14, 2008
    #1
    I've been using the following method with sdk 3.2 to open the safari browser from within my app:

    Code:
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString: @"http://www.mypage.com"]];
    
    Upgrading to 4.3.1 however, I encounter a curious problem.

    It opens safari without any problems.

    But when I return to my application, the webview that contained the link that via shouldStartLoadWithRequest triggered the above code is now unresponsive.

    Any ideas what causes this, and how to solve it?
     
  2. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #2
    After some further research it seems to have to do with the app not being closed completely after opening another application. It just keeps running in the background, and when it reappears the webpage is unresponsive due to the fact that the function that should remove the progressview is not called.

    I can think of a couple of solutions:

    1. Force the app to quit after having called the sharedApplication : openURL-function. But how do I force my app to quit programmatically?

    2. Remove the progressview. This makes the app look bad, though.

    3. Remove the progressview when the app receives focus again (without having been closed). But what function is used to detect this event?
     
  3. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #3
    Close app programmatically?

    How do you close an app programmatically? I can't seem to find such a simple function...
     
  4. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #4
    You can't. There is no supported API. There is a private one I used to use if, for any reason, my app could not read an internal plist but Apple's scanning tool found that usage and rejected my app on the last updated...
     
  5. firewood macrumors 604

    Joined:
    Jul 29, 2003
    Location:
    Silicon Valley
    #5
    Yes, there is a way using supported public APIs.

    Set the "Application does not run in background" plist key so that your app can't run in the background. Then launch Safari by sending it a URL using UIApplication sharedApplication openURL.

    The OS will then close your app for you before bringing Safari to the foreground.
     
  6. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #6
    Beautiful solution - not even a line of code required :D Thanks a lot :)
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    Well, UIApplication sharedApplication openURL would seem to be a line of code.

    You haven't said why you want this. One common solution is to put up an alert with no buttons on it. The alert tells the user why the app can't proceed and the user then hits the home button. But it is correct that this solution won't be so good if the app just goes to the background and can come back later in the same state. Depends on why it can't proceed.
     
  8. firewood macrumors 604

    Joined:
    Jul 29, 2003
    Location:
    Silicon Valley
    #8
    The web page you tell Safari to go to could contain an image of (or HTML for) your informational alert dialog. Then your already out of your app.
     
  9. seepel macrumors 6502

    seepel

    Joined:
    Dec 22, 2009
    #9
    You would really need to give details of how you're setting up this progressview...

    This sounds like the right solution... I wouldn't mess around with shutting your app down, it's not how users will expect an iOS 4 application to behave. Even better if you're opening the link in Safari, you know that you shouldn't be loading the webpage yourself. So before you kick off to Safari do the cleanup.

    But again any advice you get here is just going te be guess work because we don't really know what you're doing. And I don't mean any offense here, I'm guilty of myself. You get so wrapped up in what you're doing you think... Well this is obviously what I'm doing, everyone will understand it.
     
  10. Danneman101 thread starter macrumors 6502

    Joined:
    Aug 14, 2008
    #10
    Yes, I know - sorry about being less than clear on this. The problem is that the code handling this is so convoluted and spread trough so many code-pages that I find it really hard to distill the function to anything that is postable on the forum.

    I'll give it a shot, though, because as you say the expected behavior is that the app should be kept open in the background:

    The progressview is a subview that is loaded on top of the page each time the webview is loaded with a new local html-file:


    .h-file
    Code:
    @interface DetailViewController : UIViewController <UIPopoverControllerDelegate, UISplitViewControllerDelegate, UIWebViewDelegate, AVAudioPlayerDelegate, PopupTextSizeDelegate> {
    	IBOutlet UIView *progressAlert;
    }
    @property (nonatomic, retain) UIView *progressAlert;
    

    .m-file
    Code:
    @synthesize progressAlert;
    
    
    -(void)webViewDidStartLoad:(UIWebView *)webView 
    {
    	// ----------------------
    	// SHOW:	PROGRESS VIEW
    	// ----------------------
    	progressAlert = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    	progressAlert.frame = CGRectMake(0.0f, 0.0f, 1100.0f, 1100.0f);	
    	[self.webView addSubview:progressAlert];
    	progressAlert.backgroundColor = [UIColor whiteColor];
    	[progressAlert release];
    
    	// Also an activityview loaded here, but not important for this example I gather...
    }
    
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
    	// ----------------------
    	// HIDE:	PROGRESS VIEW
    	// ----------------------
    	@try {
    		[self performSelector:@selector(fn_HideProgressView) withObject:nil afterDelay:0.01];
    	}
    	@catch (NSException * e) {}
    	@finally {}
    }
    
    
    -(void) fn_HideProgressView
    {
    	// ---------------------
    	// Stop:	ProgressView
    	// ---------------------
    	if (self.progressAlert)
    	{
    		[self.webView sendSubviewToBack:progressAlert];	
    	}
    }
    
    The strange thing with the code is that when the app returns from having been in the background, all the functions for closing the progressview are called (even the "[self.webView sendSubviewToBack:progressAlert];" code).

    Even when I try reloading a new local html-page into the webview the progressview is persistent.

    This seems to indicate that perhaps I loose the handle for the current progressview as the app is pushed to the background, and when returned I can't access it. I.e. the "sendSubviewToBack"-function is not reaching the actual progressview.

    This was not a problem in previous versions of the sdk, though...

    The obvious solution would be to remove the progressview before opening safari, but that should already be done by the time a user clicks on a link, since fn_HideProgressView is called by webViewDidStartLoad. So I am uncertain why it appears again when the app regains focus.
     
  11. Sykte macrumors regular

    Joined:
    Aug 26, 2010
    #11

    Hang the main thread so the big bad watch dog comes and eats your app. Isn't that kinda supported or unsupported or silly.

    ^^ note this is not good advice, clearly a joke.
     
  12. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #12
    That might well work. As would intentionally writing to a memory address you don't own :p
     
  13. seepel macrumors 6502

    seepel

    Joined:
    Dec 22, 2009
    #13
    This may be your problem. You're not implicitly retaining your progressAlert. There are two ways to set a pointer, using the designated setter

    Code:
    self.progressAlert = ...;
    [self setProgressAlert:...];
    
    and assigning the pointer directly

    Code:
    progressAlert = ...;
    
    By using retain with your property and synthesizing the setter, the memory for the object is automatically retained when assigning it. Here the memory is retained by adding the progressAlert to the subview but maybe something funky is happening elsewhere in your code when going to the background and coming back that your pointer to it gets lost in the shuffle. What if you tried using your setter rather than assigning the pointer directly? An easy way to test this would be to add a break point or NSLog to your code when you check if the progressAlert is nil and see what the value actually is.

    This is the main reason I always use different names for my iVars and my properties... (eg progressAlert_ for the iVar and progressAlert for the property). That way I can easily follow what is happening without having to look for self all the time, and if I do make a mistake, the compiler will whine at me.
     

Share This Page