I'm developing an app with a very specific focus on shuffling songs within a selected playlist. When the app launches, it lists all the user's playlists, and then when one is selected it loads it into my music player view and get's going. I'm using an MPMusicPlayerController iPodMusicPlayer to handle doing this because I'm pretty new at iOS development and it handles backgrounding and everything nice and easily for me. I want music to continue playing in the background and I like how whatever I change within my app is also reflected in the iPod app. However, I don't want the user to be able to change things in the iPod app and then return to mine. Since my app is all about shuffling, the user could easily go into iPod and disable shuffle, then come back to my app and the experience is ruined. I also don't want them to change the playlist from within iPod.
Can anyone help me think of a way to accomplish this? Play/pause/skip/prev/volume is no problem from within iPod (I want that to be possible), but I don't want changing the song queue to be possible. Is there a way I could add a handler in my app that catches if the user changed something while my app was in the background (and if so, stop music and reset my app to the initial screen)? I'd really appreciate any suggestions. I'm hoping something like this is possible before I venture into using AVPlayerQueue or the like (as a side note, can you play DRM'd files with AVPlayerQueue?).
What I would do is set a bool when backgrounded:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setBool:YES forKey:#"BACKGROUNDED"];
Then, in your handler for the MPMusicPlayerControllerNowPlayingItemDidChangeNotification that you should be implementing so that you have that control, do the following:
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if ([prefs boolForKey:#"BACKGROUNDED"]) {
[prefs setBool:NO forKey:#"BACKGROUNDED"];
exit(0);
}
It might be too abrupt, depending on when it checks this code (I don't know when it does in your project) but that should accomplish your goal. exit(0) is the way to terminate your app without throwing an exception. Is that helpful?
Related
I need to get triggered eventos from pusher, and when the app go the background I don't get them (only the first one).
I have this:
#property(strong, nonatomic) PTPusherPresenceChannel *taxi_channel;
PTPusherPresenceChannel *taxi = [PusherController sharedApp].taxi_channel;
- (void)viewDidLoad
#weakify(self);
[taxi bindToEventNamed:#"client-driver-cancel-service" handleWithBlock:^(PTPusherEvent *event)
{
DDLogDebug(#"%#: %#", event.name, event.data);
#strongify(self);
[self cancelServiceAcepted];
}];
The problem is that I get a single event when get into the background, but after the first I don't get them anymore.
I have implemented the code at https://github.com/pusher/pusher-test-iOS/blob/master/Diagnostics/Code/ClientDisconnectionHandler.h
If the app go the foreground it work fine.
I'm the author of libPusher. I answered your question on Github but I thought I'd post it here as it might be helpful for others.
Unfortunately its not really possible to use Pusher in the background and its not really what it is designed for. Pusher works great for receiving events in realtime while your app is running but to get background notifications, you really need to be looking at using Apple push notifications OR period fetch, depending on whether you'd prefer push or pull.
My suggestion would be:
Use Pusher while your app is in the foreground to receive real-time updates
Use push notifications to send significant events to your app while it is in the background (these should be less frequent) AND/OR
Possibly use background fetch to pull the latest changes/events from your server
Restart listening to events from Pusher when your app resumes in the foreground
I currently have background music that starts up when the app opens and runs an infinite loop until the user goes into the settings and turns the music off using this
-(IBAction)stopMusicButton{
[(AppDelegate*)[UIApplication sharedApplication].delegate stopMusic];
}
They can also resume the music in the settings page. What I'm a confused about is how to stop the other sounds in my app from playing if the user doesn't want the noise. For example, when the user navigates the menu of my app there are various sounds that play when certain things are pressed. How can I allow the user to be able to toggle these sounds on and off? There are also a few different views with their respective .h/.m files, all of which play sounds. If you could point me in the right direction I'd really appreciate it! Thanks in advance!
I use a singleton class to store settings information - you could probably use your app delegate as well. All you need is a boolean property called isSoundMuted or something like that. Set that whenever the sound is muted or unmuted, then check the variable's value before playing any sounds in an if block.
if (MySingletonClass.isSoundMuted == NO)
{
[self playSound]
}
Here is some background information, otherwise skip ahead to the question in bold. I am building an app and I would like it to have access to the remote control/lock screen events. The tricky part is that this app does not play audio itself, it controls the audio of another device nearby. The communication between devices is not a problem when the app is in the foreground. As I just found out, an app does not assume control of the remote controls until it has played audio with a playback audio session, and was the last do so. This presents a problem because like I said, the app controls ANOTHER device's audio and has no need to play its own.
My first inclination is to have the app play a silent clip every time it is opened in order to assume control of the remote controls. The fact that I have to do this makes me wonder if I am even going to be allowed to do it by Apple or if there is another way to achieve this without fooling the system with fake audio clips.
QUESTION(S): Will Apple approve an app that plays a silent audio clip in order to assume control of the remote/lock screen controls for the purpose of controlling another device's audio? Is there any way of assuming control of the remote controls without an audio session?
P.S. I would prefer to have this functionality on iOS 4.0 and up.
P.P.S I have seen this similar question and it has gotten me brainstorming but the answer provided is not specific to what I need to know.
NOTE: As of iOS 7.1, you should be using MPRemoteCommandCenter instead of the answer below.
You create various system-provided subclasses of MPRemoteCommand and assign them to properties of the [MPRemoteCommandCenter sharedCommandCenter].
I'm keeping the rest of this around for historical reference, but the following is not guaranteed to work on recent iOS versions. In fact, it just might not.
You definitely do need an audio player but not necessarily an explicit session to take control of the remote control events. (AVAudioSession is implicit to any app that plays audio.) I spent a decent amount of time playing with this to confirm this.
I've seen a lot of confusion on the internet about where to set up the removeControlEventRecievedWithEvent: method and various approaches to the responder chain. I know this method works on iOS 6 and iOS 7. Other methods have not. Don't waste your time handling remote control events in the app delegate (where they used to work) or in a view controller which may go away during the lifecycle of your app.
I made a demo project to show how to do this.
Here's a quick rundown of what has to happen:
You need to create a subclass of UIApplication. When the documentation says UIResponder, it means UIApplication, since your application class is a subclass of UIResponder. In this subclass, you're going to implement the remoteControlReceivedWithEvent: and canBecomeFirstResponder methods. You want to return YES from canBecomeFirstResponder. In the remote control method, you'll probably want to notify your audio player that something's changed.
You need to tell iOS to use your custom class to run the app, instead of the default UIApplication. To do so, open main.m and change this:
return UIApplicationMain(argc, argv, nil, NSStringFromClass([RCAppDel`egate class]));
to look like this:
return UIApplicationMain(argc, argv, NSStringFromClass([RCApplication class]), NSStringFromClass([RCAppDelegate class]));
In my case RCApplication is the name of my custom class. Use the name of your subclass instead. Don't forget to #import the appropriate header.
OPTIONAL: You should configure an audio session. It's not required, but if you don't, audio won't play if the phone is muted. I do this in the demo app's delegate, but do so where appropriate.
Play something. Until you do, the remote controls will ignore your app. I just took an AVPlayer and gave it the URL of a streaming site that I expect to be up. If you find that it fails, put your own URL in there and play with it to your heart's content.
This example has a little bit more code in there to log out remote events, but it's not all that complicated. I just define and pass around some string constants.
I bet that a silent looping MP3 file would help work towards your goal.
Moshe's solution worked great for me! However one issue I noticed is when you paused the audio, the media controls would go away and you won't be able to play it again without going back into the app. If you set the Media Info on the lock screen when you play the audio then this won't happen:
NSDictionary *mediaInfo = #{MPMediaItemPropertyTitle: #"My Title",
MPMediaItemPropertyAlbumTitle: #"My Album Name",
MPMediaItemPropertyPlaybackDuration: [NSNumber numberWithFloat:0.30f]};
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:mediaInfo];
I am using the iPad settings app to change some button sounds and a background image. It all works well and the settings are maintained from one app launch to another in the simulator. Now I have implemented a toggle switch to either set sets of sounds off or on. When the app launches, whatever state the switch is in, it works; e.g. if the "Alert Sounds" switch is OFF the alert sounds are silent and if I change it to ON the sounds will start working. However, if I turn the switch back OFF the sounds still keep working. However, if the state is ON when the app launches, the sounds work, but will not be silenced when the switch is set to OFF.
Note that this is different than the settings not taking effect until a second round of settings. That was a previous problem I solved (thanks to stack overflow) by using:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[NSUserDefaults standardUserDefaults] synchronize];
}
I have methods named:
- (void)defaultsChanged:(NSNotification *)NSUserDefaultsDidChangeNotification
(which is called when the notification is sent)
and
-(void)setValuesFromPreferences
(which is called in ViewDidLoad)
The logic looks like this in both:
// Set alert sounds from preferences
NSString *alertSoundPreference = [userDefaults stringForKey:kAlertSound];
BOOL alertSoundEnabled = [userDefaults boolForKey:kAlertSoundEnabled];
if (alertSoundEnabled)
{
// Create the URLs for the alert audio files
// Store the alert sound URLs as a CFURLRef instances
// Create system sound objects representing the alert sound files
}
I do not have an else, because I assume that no sound resources will be specified if alertSoundEnabled is NO.
I have searched for explanations and tutorials that mention this problem but have not found any yet, so I'm asking here. Thanks for any suggestions.
viewDidLoad is not necessarily called when the app becomes active again (nor does viewWill/DidAppear, IIRC), as the whole point of iOS 4+ multitasking is to prevent such loading/unloading and recreation of objects on app-switching.
If I had to guess, the sounds are already allocated when the user had the switch ON at original launch/viewDidLoad; however, if your code does nothing to explicitly disassociate them when it loads back up, they would continue playing, as they are all already set up.
As such, I'd try adding an else clause that (upon alertSoundEnabled == NO) destroys your system sound objects.
In my project AppDelegate file, I have an int declared called correctAnswersCountR1. So in the app they take a small quiz and that variable keeps track of how many correct answers they got. Now somewhere else in the project I use this variable like so:
int r1score=appDelegate.correctAnswersCountR1;
The problem is that apparently if I exit the app and come back, the value isn't remembered, and is set back to its default value. How can I store this number so that it is remembered if the user closes the app and comes back?
You can use NSUserDefaults to store your value:
**Saving**
//Do this right before the app exits
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving an NSInteger
[prefs setInteger:42 forKey:#"integerKey"];
// This is suggested to synch prefs, but is not needed
[prefs synchronize];
**Retrieving**
//Do this when your app is loaded again
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting an NSInteger
NSInteger myInt = [prefs integerForKey:#"integerKey"];
You could set up core data for your project, though unless you plan on storing more information persistently for your app Core Data might be a bit of overkill. You could also try writing it to a file and just reading it on load.
Edit: For this specific scenario Oscar's answer seems more appropriate, though depending on where you plan on going with your app, CoreData or a file may be a good choice.
You'll want to do some research about NSUserDefaults.
Here is the apple documentation
On another note it's not considered best practice to store data in the AppDelegate
You have to use a preferences storing mechanism. I'm not all too familiar with the iPhone toolkit, but on Mac OS X and Cocoa there's NSUserDefaults. When your app launches, you would load the value from the user defaults (preferences) through [[NSUserDefaults standardUserDefaults] intForKey:#"count"], and whenever the value changes, you would call [[NSUserDefaults standardUserDefaults] setInt:answerCount... forKey:#"count"].
I'm pretty sure that you want to write the value into a file to make it persistent.
This is because an application which moved to the background might be terminated for example if the system measures out that it needs some more memory.
So it kills the application and restarts it when the user switches over.