Prevent NSDocument-based app from reopening documents after a crash - swift

I have a read-only music player app for macOS that uses NSDocument to get all the file-handling logic for free.
The problem that I now have is that every time the app crashes (or is stopped by the debugger) while one or more player windows are open, they automatically get reopened when the app is restarted. I don't want that, since it interferes with debugging and legitimate crashes don't really happen with this app.
Apple's NSDocument documentation contains nothing regarding the reopening of files, so I'm out of luck there. Is there a proper way to do this?

First create a subclass of NSDocumentController and make sure you create an instance of it in - (void)applicationWillFinishLaunching:(NSNotification *)notification so it becomes the sharedDocumentController. (see this question)
Then in you subclass override the restore method:
+ (void)restoreWindowWithIdentifier:(NSString *)identifier state:(NSCoder *)state completionHandler:(void (^)(NSWindow *, NSError *))completionHandler
{
if (_preventDocumentRestoration) { // you need to decide when this var is true
completionHandler(nil, [NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]);
}
else {
[super restoreWindowWithIdentifier:identifier state:state completionHandler:completionHandler];
}
}

Related

Refresh values when application enters foreground

My application is using 'ALAssetsGroup' to keep track of the users albums
and i display these albums in my application
the user can click on the albums and see the images as well.
Now the problem arises when the user downloads images through safari or he alters the albums in any way .
my application still holds reference to the old albums instead of the new one
so i tried to refresh the albums using the
- (void)applicationWillEnterForeground:(UIApplication *)application
but the albums still seem to have the old value,
if i refresh them once more 'within' the application they get the correct values.
is there something else i can use to resolve this issue .. ?
maybe a notification .. ?
//This Registers a Notification for any changes
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didChangeLibrary:)
name:ALAssetsLibraryChangedNotification
object:[AGImagePickerController defaultAssetsLibrary]];
- (void)didChangeLibrary:(NSNotification *)notification
{
//Enter some code here to deal with the album changing
}
Edit: It seems like this does not work on iOS 5 (A radar has been opened for Apple, because this is a bug).
Here is a work around to use in the meantime:
Call
[self.assetsLibrary writeImageToSavedPhotosAlbum:nil metadata:nil
completionBlock:^(NSURL *assetURL, NSError *error) { }];
immediately after creating instance of ALAssetsLibrary Observe
ALAssetsLibraryChangedNotification (not
NSManagedObjectContextObjectsDidChangeNotification)
Use the applicationDidBecomeActive method in the ApplicationDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}

Quit the application in iphone by code [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
how to quit (exit) an app in iPhone4 sdk
I have a close button in the first view of the app. I need to close the application succesfully on button click. I had tried the two cases but not worked.
[[UIApplication sharedApplication] terminate];
[[UIApplication sharedApplication] exit(0)];
iOS apps aren't supposed to quit on their own; the user quits/suspends apps using the devices' button. For more on this read Don't Quit Programmatically in the iOS Human Interface Guidelines.
If your app has finished whatever task it's meant to do and you would normally exit, you can instead just reset the app's state back to whatever it would be when the app starts. You should probably include some cues in the UI so that the user understands what's happening -- remember, the user is supposed to be in charge here. But if your app has reached it's logical conclusion, it may be that the thing that will feel most "right" to the user will be to go back to the initial state. You often see this with games -- once the game is done, the app goes back to the intro screen and waits for the user to start another game.
Note that both snippets you posted are incorrect. UIApplication doesn't have a -terminate method. (NSApplication does have a -terminate: method, but it takes a parameter and obviously isn't available in iOS.) Your second example mixes function call syntax with message sending syntax in a way that doesn't make any sense -- I doubt that will even compile.
Similar Thing i am also doing while i want to Quit my application after Show alert i think this may help u...
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
NSLog(#"%#",alertView.title);
if(alertView.title== #"Collision failed")
{
if(buttonIndex==0)
{
//gameState = kGameStateRunning;
[self loadView];
[self viewDidLoad];
count=0;
}
else
{
exit(0);
}
}
else
{
if(buttonIndex==0)
{
//gameState = kGameStateRunning;
[self loadView];
[self viewDidLoad];
}
else
{
second_stage *obj=[[second_stage alloc]initWithNibName:#"second_stage" bundle:nil];
obj.count2=count;
[self presentModalViewController:obj animated:YES];
}
}
simply i am writing exit(0);
Just call exit(0);
No need to call with the instance of UIApplication. But before call the exit method, do whatever you need to do such as saving the state, values if any, etc.,
I am not very sure but acc to HIG, iPhone apps should not be closed.
but you can Set UIApplicationExitsOnSuspend in your application's plist.When you switch to another app, it will cause the app not to go into the background under iOS4. :)

Core Data database doesn't save

I'm trying to implement the Paparazzi 2 assignment from the Stanford CS193 course and I'm running into a problem. My one call to save the database is when the app exits (I'm borrowing heavily from Mike Postel's version to check my code):
- (void)applicationWillTerminate:(UIApplication *)application {
if (flickrContext != nil) {
if ([flickrContext hasChanges] == YES) {
NSError *error = nil;
BOOL isSaved = [flickrContext save:&error];
NSLog(#"isSaved? %#", (isSaved ? #"YES" :#"NO") );
// Replace this implementation with code to handle the error appropriately.
if(isSaved == NO){
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
}
Unfortunately, this doesn't seem to be doing the job. I'm getting the occasional EXEC_BAD_ACCESS call that might be related to this, but the database never saves. I've inserted the save into other pieces and it works fine there, just not in this routine. I'm not releasing any of the managed objects in my views, just the managed object context (flickrContext, or whatever I'm calling it in a view).
Any ideas?
Are you sure that applicationWillTerminate: is even being called?
With iOS4 and background process support, the usual application lifecycle is now:
running -> background -> background suspended -> exit
You get an applicationDidEnterBackground: call when transitioning into the background state, but no further notification when the background process suspends or exits.
So, you really need to save state in applicationDidEnterBackground: for iOS4, as well as in applicationWillTerminate: for older versions
flickrContext is your managedObjectContext? I'm betting it is nil or otherwise hosed when you hit this method. You say you are releasing it in a view - surely you should be creating just one, having it owned by the application delegate, and release it only in app delegate's dealloc?
(And when you need to use it -
NSManagedObjectContext* moc = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
)
Regarding your EXEC_BAD_ACCESS - what happens with NSZombieEnabled = YES? What does the static analyzer say?
Good call. I actually solved this one the old fashioned (brute force) way. It turns out that applicationWillTerminate wasn't being called, but it wasn't obvious. The routine to create the database that I had borrowed from the web was explicitly releasing an NSArray that I'm pretty sure was autoreleased. It essentially turned the program into a time bomb. Although I still haven't figured out why it lasted as long as it did and just didn't manifest until I tried to exit.
I'm still learning XCode and CocoaTouch. I know about NSZombieEnabled but I haven't figured out how to use it correctly yet. I'm still in the ham-fisted monkey stage. Thanks for the tips, though. They were helpful.

iOS 4: Remote controls for background audio

I'm currently attempting to set up background audio for an app I'm developing for iOS 4. The app doesn't have a dedicated music player viewController, however, unlike other background audio apps such as Pandora, which makes the task a bit more confusing.
I've set the appropriate Info.plist settings correctly and have an AVAudioPlayer object in my app delegate which is accessible from everywhere. When the user plays a song, I replace the AVAudioPlayer with a new one initialized with the song and play it. This all works great, except now I have no idea how to go about supporting remote control events.
Based on Apple's documentation, I have this:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
switch(event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
if([iPhoneAppDelegate backgroundAudioPlayer].playing)
[iPhoneAppDelegate pauseBackgroundAudioPlayer];
else
[iPhoneAppDelegate playBackgroundAudioPlayer];
break;
}
}
The thing is, where do I put this? Apple's documentation seems to suggest this should go in some view controller somewhere, but my app has lots of view controllers and navigation controllers. Wherever I try to put this, for some reason tapping the Toggle Play/Pause button in the multitasking tray remote controls either causes the song to just pause for a moment and then unpause, or somehow causes the song to play twice.
The documentation examples are a bit misleading, but there is no need to subclass anything anywhere. The correct place to put remoteControlReceivedWithEvent: is in the application delegate, as it remains in the responder chain regardless of whether the app is in the foreground or not. Also the begin/end receiving remote control events should be based on whether you actually need the events, not on the visibility of some random view.
I found a couple of solutions to receiving global remote control events on the Apple Developer Forums after a bit of searching.
One way is to subclass UIWindow and override its remoteControlReceivedWithEvent:.
The second, perhaps nicer way is to subclass UIApplication and override sendEvent:. That way, you can intercept all the remote control events and handle them there globally, and not have any other responders handle them later in the responder chain.
- (void)sendEvent:(UIEvent *)event {
if (event.type == UIEventTypeRemoteControl) {
// Handle event
}
else
[super sendEvent:event];
}
The second method didn't work for me, sendEvent was never called. However the first method worked just nicely (subclassing UIWindow).
I struggled with this one for a while and none of the answers above worked. The bug in my code, and I hope that it will help someone reading this, was that I had the AudioSession set to mix with others. You want to be the foreground audio player to get Remote Control events. Check to see if you have INCORRECT code like this:
[[AVAudioSession sharedInstance] setDelegate: self];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 0;
AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (doSetProperty),
&doSetProperty
);
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
And remove the AudioSessionSetProperty, or change doSetProperty to 1.
No need to subclass Window or forward events. Simply handle it from your main view controller. See the Audio Mixer (MixerHost) example for details.
http://developer.apple.com/LIBRARY/IOS/#samplecode/MixerHost/Listings/Classes_MixerHostViewController_m.html
Documentation explains it very well.
https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/Remote-ControlEvents/Remote-ControlEvents.html
One thing that seems to influence this behavior is any category options you set for your AVAudioSession using setCategory:withOptions:error: instead of just setCategory:error:. In particular, from trial and error, it appears that if you set AVAudioSessionCategoryOptionMixWithOthers you will not get remote control events; the now playing controls will still control the iPod app. If you set AVAudioSessionCategoryOptionDuckOthers you will get remote control events, but it seems like there may be some ambiguity regarding which app is controlled. Setting the categoryOptions to 0 or just calling setCategory:error: works best.

Received memory warning. Level=1 when showing a UIImagePickerController

This is driving me crazy!!!
I'm getting a "Received memory warning. Level=1" whenever I attempt to show a UIImagePickerController with a sourceType = UIImagePickerControllerSourceTypeCamera.
Here is the code from my viewDidLoad where I set things up:
- (void)viewDidLoad {
[super viewDidLoad];
// Set card table green felt background
self.view.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed:#"green_felt_bg.jpg"]];
// Init UIImagePickerController
// Instantiate a UIImagePickerController for use throughout app and set delegate
self.playerImagePicker = [[UIImagePickerController alloc] init];
self.playerImagePicker.delegate = self;
self.playerImagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}
And here is how I present it modally ..
- (IBAction) addPlayers: (id)sender{
[self presentModalViewController:self.playerImagePicker animated:YES];
}
The result ... UIImagePicker starts to show and then boom ... I get the memory warning ... EVERY TIME! Interestingly enough, if I switch to sourceType = UIImagePickerControllerSourceTypePhotoLibrary ... everything works fine.
What in the heck am I missing or doing wrong? All I want to do is show the camera, take and save a picture.
FYI - I'm testing on my 3GS device.
Thanks to anyone who can help :)
This is very common. As long as you handle the memory warning without crashing and have enough space to keep going, don't let it drive you crazy.
It is not about how much memory your app has used, because it will probably happen even when you write a very simple app which have only one view with one button, clicking the button and then open camera.
I have tested on iPhone 3GS, iPad 2 and iPod touch 3G. It only happened in iPhone 3GS.
I found it will not happen anymore if you restart you device before you execute you app.
Another real solution is to comment the code, [super didReceiveMemoryWarning], in your viewController.
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
After lots of test on iPhone 3GS with iOS 4.3.2, I found the logic might like that:
-> Open as much as app running on background
-> Presenting a imagePicker of UIImagePickerController, clicking "Back" or "Save" from imagePicker
-> ApplicationDelegate's method, applicationDidReceiveMemoryWarning:(UIApplication *)application, will be invoked
-> Then ViewController's method, didReceiveMemoryWarning:, will be invoked
-> Then viewDidUnload
-> Then viewDidLoad
Then you could find some views have been released and the current view has been pointed to a unexpected one.
By default, [super didReceiveMemoryWarning] will run when ViewController's didReceiveMemoryWarning method is invoked. Commenting it, and viewDidUnload: and viewDidLoad: methods will not be invoked. It means the mem warning has been totally ignored. That's what we expected.
Now after I upgraded to 4.0 it happens to my app too - before in 3.1 there were no warnings.
Actually as you said before, there should be no issue. However, this causes the view that comes after it to load again and viewDidLoad is being called. This messes up my app, since I initialize the view in viewDidLoad - now it gets initialized all over again - even though it shouldn't.
Just as a comment, this might also happen to many other apps that rely on loading the view only once!
It did happen in my app Did I Do That on iOS 4.0 too. It was not consistent, but the most common cause was creating a UIImagePickerController instance and navigating to some large photo stored in one of the albums.
Fixed by persisting state in the didReceiveMemoryWarning method, and loading from state in the viewDidLoad method. One caveat is to remember to clear the state-persisted file in the correct point for your application. For me it was leaving the relevant UIViewController under normal circumstances.
I'm getting the memory warning when opening a UIImagePickerController as well. I'm on 4.01 as well.
But in addition, the UIImagePickerController is running the close shutter animation and stalling there, with the closed shutter on screen.
It seems like the UIImagePickerController's behavior on memory warnings is to close itself.
I could dismiss the UIImagePickerController from the parent ViewController in the didReceiveMemoryWarning method, but that would make for a terrible user experience.
Has anyone seen this problem?
Is there a way to handle the memory warning so that the UIImagePickerController doesn't shut itself down?
I have been struggling with the same problem for some days now. However, resetting my iPhone 4 (clearing out memory) solves the problem so it's not really an app problem.
It appears that a level 1 or 2 memory warning triggers the UIimgPickerController delegate to offload itself. The same happens in my app with the delegate of the delegate (yes it can). After the memory warning however, it will load the delegate (and it's delegate) again causing the viewDidLoad to execute any code that's in there.
I am not sure this happens only while using the UIimgPickerController because testing all that is very time consuming.
I could write some extra code to prevent the code in viewDidLoad en viewWillAppear from execuring while showing the UIimgPickerController but that's not classy, right?
Here's food for thought: it could be
that you are running out of memory
because you are testing your app. With
some memoryleaks it is very well
possible that you are working towards
this problem every time you debug.
The UIImagePickerControllerDelegate is a memory hog because you are capturing high memory assets, be that an image or video. So from the start be sure to specify the medium capture settings, as a start point, reduce this if you don't need the quality:
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.videoQuality=UIImagePickerControllerQualityTypeMedium;
Then after capturing and using these assets. Remove any temp files from the applications temp folder. Could be an extra obsessive step but its a good habit:
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[lastCapturedFile substringFromIndex:7] ]) {
NSError *error;
// Attempt to delete the folder containing globalDel.videoPath
if ([fileManager removeItemAtPath:[lastCapturedFile substringFromIndex:7] error:&error] != YES) {
NSLog(#"Unable to delete recorded file: %#", [error localizedDescription]);
} else {
NSLog(#"deleted file");
}
}
With above it is clearing the file that was created by the delegate. In some instances if you are transcoding or creating you own assets delete the folder with that file. Note above I am removing the 'file://' part of the url string as the file manager doesn't like it:
[lastCapturedFile substringFromIndex:7]
Other things to consider are covered in the various documentation for what you are doing with that asset - transcoding, image size reduction and more. Beware that any transcoding using the AVFoundation will crash if the UIImagePickerViewController is displaying.