Key Value Observing not allowing Switch statement on enum - ios

I'm trying to use Key Value Observation to detect a change in the torchMode variable for an AVCaptureDevice. I'm having an issue where the switch statement doesn't recognize the value (which is an enum). Any idea whats going on? I know that the program gets to the line with the switch, then immediately skips all the cases. Here's the relevant code:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == CapturingStillImageContext)
{
BOOL isCapturingStillImage = [change[NSKeyValueChangeNewKey] boolValue];
if (isCapturingStillImage)
{
[self runStillImageCaptureAnimation];
}
}
else if (context == RecordingContext)
{
BOOL isRecording = [change[NSKeyValueChangeNewKey] boolValue];
dispatch_async(dispatch_get_main_queue(), ^{
if (isRecording)
{
[[self record] setTitle:NSLocalizedString(#"-", #"Recording button stop title") forState:UIControlStateNormal];
[[self record] setEnabled:YES];
}
else
{
[[self record] setTitle:NSLocalizedString(#"+", #"Recording button record title") forState:UIControlStateNormal];
[[self record] setEnabled:YES];
}
});
}
else if (context == SessionRunningAndDeviceAuthorizedContext)
{
BOOL isRunning = [change[NSKeyValueChangeNewKey] boolValue];
dispatch_async(dispatch_get_main_queue(), ^{
if (isRunning)
[[self record] setEnabled:YES];
else
[[self record] setEnabled:NO];
});
}
else if (context == TorchContext) {
id torchValue = [change objectForKey: NSKeyValueChangeNewKey];
NSString* title;
switch((AVCaptureTorchMode)torchValue) {
case AVCaptureTorchModeAuto: title = #"Auto"; break;
case AVCaptureTorchModeOff: title = #"Off"; break;
case AVCaptureTorchModeOn: title = #"On"; break;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.torch setTitle:title forState:UIControlStateNormal];
//NSLog(self.torch);
});
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
Code for adding observer for key value:
- (void)viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:NO];
dispatch_async([self sessionQueue], ^{
[self addObserver:self forKeyPath:#"sessionRunningAndDeviceAuthorized" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:SessionRunningAndDeviceAuthorizedContext];
[self addObserver:self forKeyPath:#"stillImageOutput.capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext];
[self addObserver:self forKeyPath:#"movieFileOutput.recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext];
[self addObserver:self forKeyPath:#"videoDeviceInput.device.torchMode" options:(NSKeyValueObservingOptionNew) context:TorchContext];
...
}

I solved the issue. I'm not sure if this is the most elegant solution, but I just casted the value to an NSNumber and got the integer value, then put it in the switch. Here's the code:
int torchValue = ((NSNumber*)[change objectForKey: NSKeyValueChangeNewKey]).integerValue;
NSString* title;
switch(torchValue) {
case AVCaptureTorchModeAuto: title = #"Auto"; break;
case AVCaptureTorchModeOff: title = #"Off"; break;
case AVCaptureTorchModeOn: title = #"On"; break;
}

Related

iOS: How to check if a variable has changed his value in another ViewController in a dispatch_async?

I would like to know how to check if a value changed in another ViewController that is running in background. I have that code in that Viewcontroller that is downloading in background, and it puts me the isSyncAutoOnGoing to NO when the download is finished.
ViewController.m
#interface ViewController ()
{
BOOL isSyncAutoOnGoing;
}
- (void) sync:(id) sender
{
isSyncAutoOnGoing = YES;
dispatch_queue_t downloadQueue = dispatch_queue_create("downloader", NUL
L);
dispatch_async(downloadQueue, ^{
// do our long running process here
ListOfValueSync * lovSync = [[ListOfValueSync alloc] init];
// Synchronization
BOOL ret = [lovSync getAllListOfValueAll];
// do any UI stuff on the main UI thread
dispatch_async(dispatch_get_main_queue(), ^{
[spinner stopAnimating];
[spinner removeFromSuperview];
isSyncAutoOnGoing = NO;
NSLog(isSyncAutoOnGoing ? #"sync Yes" : #"sync No");
if ([sender isKindOfClass:[UIButton class]])
{
self.loginField.text = #"";
(!ret) ? [self alertStatus:#"Can not synchronize" :#"Sync Failed!" :0] : [self alertStatus:#"Synchronization OK" :#"Sync OK" :0];
[sender setEnabled:TRUE];
}
});
});
}
}
Here is the FormViewController where I want to check when the isSyncAutoOnGoing is equal to NO. I know it is not the good way I think, and more, the value of isSyncAutoOnGoing is always equal to YES.
FormViewController.m
NSArray *viewControllers = [[self navigationController] viewControllers];
id obj;
for (int i = 0; i < [viewControllers count]; i ++)
{
obj = [viewControllers objectAtIndex:i];
if ([obj isKindOfClass:NSClassFromString(#"ViewController")])
{
BOOL isSyncAutoOnGoing = [[obj valueForKey:#"isSyncAutoOnGoing"] boolValue];
if (isSyncAutoOnGoing)
{
do{
[NSThread sleepForTimeInterval:1.0];
NSLog([[obj valueForKey:#"isSyncAutoOnGoing"] boolValue] ? #"isSyncAuto: YES" : #"isSyncAuto: NO");
}while([[obj valueForKey:#"isSyncAutoOnGoing"] boolValue]);
}
}
}
Any ideas?
Thanks in advance.
Suppose the array is in AppDelegate
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
self.allNewsItems = appDelegate.allNewsItems;
[appDelegate addObserver:self
forKeyPath:#"allNewsItems"
options:NSKeyValueObservingOptionNew
context:NULL];
and implement
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context{
if ([keyPath isEqualToString:#"allNewsItems"]){ if ([self isBeingPresented]){
[self.tableView reloadData]; }else{
self.mustReloadView = YES;
}
} }

How to get notified when user allows the camera access alert in iOS?

I want to record a video after a count down 3 2 1. But on first time without allowing the camera the video has started recording without showing to camera . I want to call the recording code in the block which is called when the user allow to camera on camera access alert.
self.recordingManager.previewLayer.frame = CGRectMake(47, 2000, 280, 154);
// set code for count down
imageArr = [[NSArray alloc]initWithObjects:#"countdown_three",#"countdown_two",#"countdown_one", nil];
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:0]]];
[_countImageView setHidden:NO];
NSLog(#"%#",[imageArr objectAtIndex:0]);
count = 4;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(setCountDownDuration) userInfo:nil repeats:YES];
// customize progressbar
_captureProgress.hidden = NO;
_captureProgress.progress = 0.0;
_captureProgress.layer.borderWidth = 0.2;
_captureProgress.layer.borderColor = [UIColor colorWithRed:167.0f/255 green:188.0f/255 blue:219.0f/255 alpha:1].CGColor;
CGAffineTransform transform = CGAffineTransformMakeScale(1.6f, 8.0f);
_captureProgress.transform = transform;
count--;
if (count == 3) {
[self.progressStatusLbl setText:#"GET READY"];
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:0]]];
}
if (count == 2) {
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:1]]];
}
if (count == 1) {
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:2]]];
}
if (count == 0) {
[timer invalidate];
timer = nil;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusAuthorized)
{
[self.progressStatusLbl setText:#"RECORDING"];
[self openCamera];
}
else if(authStatus == AVAuthorizationStatusNotDetermined)
{
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
if(granted)
{
NSLog(#"Granted access to %#", AVMediaTypeVideo);
[self.progressStatusLbl setText:#"RECORDING"];
[self openCamera];
}
else
{
NSLog(#"Not granted access to %#", AVMediaTypeVideo);
[self camDenied];
}
}];
}
else if (authStatus == AVAuthorizationStatusRestricted)
{
// My own Helper class is used here to pop a dialog in one simple line.
//[Helper popAlertMessageWithTitle:#"Error" alertText:#"You've been restricted from using the camera on this device. Without camera access this feature won't work. Please contact the device owner so they can give you access."];
[Utils showAlertMessageWithTitle:#"Error" msg:#"You've been restricted from using the camera on this device. Without camera access this feature won't work. Please contact the device owner so they can give you access." firstButtonTitle:#"Ok" secontButtonTitle:nil];
}
else
{
[self camDenied];
}
For AVAuthorizationStatusNotDetermined state, add observer for a key (boolean), and update this flag in completion of requestAccessForMediaType then start camera or call camDenied deoending on user's actions.
So you code should look like as below:
Add a new private property in your class
#property (nonatomic, strong) NSNumber *granted;
then add following methods
- (void)askForPermission {
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[self addObserver:self forKeyPath:#"granted" options:NSKeyValueObservingOptionNew context:nil];
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
self.granted = #(granted);
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"granted"]) {
BOOL granted = [object boolValue];
if(granted)
{
NSLog(#"Granted access to %#", AVMediaTypeVideo);
[self.progressStatusLbl setText:#"RECORDING"];
[self openCamera];
}
else
{
NSLog(#"Not granted access to %#", AVMediaTypeVideo);
[self camDenied];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
call askForPermission in AVAuthorizationStatusNotDetermined case.

How to clean buffer or stop and start player from AVPlayer - iOS

I have this issue and this is not so easy as I thought.... I lost some of time and nothing yet.
And I know, I have a method pause, but I want to stop and start and avoid crash the application..
Could anyone help, very thanks.
//First.
-(void) setupAVPlayerForURL{
#try {
NSURL *url = [[NSURL alloc] initWithString:URL_RADIO_STREAM];
AVAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
anItem = [AVPlayerItem playerItemWithAsset:asset];
[anItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
[anItem addObserver:self forKeyPath:#"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[anItem addObserver:self forKeyPath:#"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
player = [AVPlayer playerWithPlayerItem:anItem];
//[player addObserver:self forKeyPath:#"status" options:0 context:nil];
}
#catch (NSException *exception) {
NSLog(#"%#", exception.reason);
[[self textDebug]setText : exception.description];
}
}
//After.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
/*if (!player)
{
return;
}*/
if ([object isKindOfClass:[AVPlayerItem class]])
{
AVPlayerItem *playerItem = (AVPlayerItem *)object;
if ([keyPath isEqualToString:#"status"])
{ //yes->check it...
switch(playerItem.status)
{
case AVPlayerItemStatusFailed:
NSLog(#"player item status failed");
self.BtnPlay.userInteractionEnabled = FALSE;
break;
case AVPlayerItemStatusReadyToPlay:
NSLog(#"player item status is ready to play");
self.BtnPlay.userInteractionEnabled = TRUE;
break;
case AVPlayerItemStatusUnknown:
NSLog(#"player item status is unknown");
self.BtnPlay.userInteractionEnabled = FALSE;
break;
}
}
if ([keyPath isEqualToString:#"playbackBufferEmpty"])
{
NSLog(#"player item status playbackBufferEmpty");
if (playerItem.playbackBufferEmpty) {
//Your code here
[[NSNotificationCenter defaultCenter] postNotificationName:#"message" object:#"Buffering..."];
if([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)
{
task = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^(void) {
[self setupAVPlayerForURL];
}];
}
}
}
if ([keyPath isEqualToString:#"playbackLikelyToKeepUp"])
{
NSLog(#"player item status playbackLikelyToKeepUp");
if (playerItem.playbackLikelyToKeepUp)
{
//Your code here
[player play];
if([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)
{
[[UIApplication sharedApplication] endBackgroundTask:task];
task = 0;
}
}
}
}
}
//Finally.
-(IBAction) BtnPlay:(id)sender {
if(self.BtnPlay.touchInside){
if (player.rate == 1.0) {
[player pause];
[BtnPlay setTitle:#"Play" forState:UIControlStateNormal];
} else {
[player play];
[BtnPlay setTitle:#"Pause" forState:UIControlStateNormal];
}
}
}
//But I don't know how to Stop this instead pause...

App behaving differently over Mobile Data and Wifi

I have an app to play online music. I have added code in that to detect a call and pause the music while on call and as soon as the call ends, it should again start the song. The problem is that it is behaving properly when the phone is connected via Wifi but not doing the same when connected over mobile data. How to make the song play again after call in Mobile Data as well.?
Here is my code:
Reachability.m
NSString *kReachabilityChangedNotification = #"kNetworkReachabilityChangedNotification";
#pragma mark - Supporting functions
#define kShouldPrintReachabilityFlags 1
static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
{
#if kShouldPrintReachabilityFlags
NSLog(#"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
(flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
(flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
(flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
(flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
(flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
(flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
(flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-',
comment
);
#endif
}
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
{
#pragma unused (target, flags)
NSCAssert(info != NULL, #"info was NULL in ReachabilityCallback");
NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], #"info was wrong class in ReachabilityCallback");
Reachability* noteObject = (__bridge Reachability *)info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];
}
#pragma mark - Reachability implementation
#implementation Reachability
{
BOOL localWiFiRef;
SCNetworkReachabilityRef reachabilityRef;
}
+ (instancetype)reachabilityWithHostName:(NSString *)hostName;
{
Reachability* returnValue = NULL;
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
if (reachability != NULL)
{
returnValue= [[self alloc] init];
if (returnValue != NULL)
{
returnValue->reachabilityRef = reachability;
returnValue->localWiFiRef = NO;
}
}
return returnValue;
}
+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;
{
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);
Reachability* returnValue = NULL;
if (reachability != NULL)
{
returnValue = [[self alloc] init];
if (returnValue != NULL)
{
returnValue->reachabilityRef = reachability;
returnValue->localWiFiRef = NO;
}
}
return returnValue;
}
+ (instancetype)reachabilityForInternetConnection;
{
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
return [self reachabilityWithAddress:&zeroAddress];
}
+ (instancetype)reachabilityForLocalWiFi;
{
struct sockaddr_in localWifiAddress;
bzero(&localWifiAddress, sizeof(localWifiAddress));
localWifiAddress.sin_len = sizeof(localWifiAddress);
localWifiAddress.sin_family = AF_INET;
// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0.
localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
Reachability* returnValue = [self reachabilityWithAddress: &localWifiAddress];
if (returnValue != NULL)
{
returnValue->localWiFiRef = YES;
}
return returnValue;
}
#pragma mark - Start and stop notifier
- (BOOL)startNotifier
{
BOOL returnValue = NO;
SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
{
if (SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
{
returnValue = YES;
}
}
return returnValue;
}
- (void)stopNotifier
{
if (reachabilityRef != NULL)
{
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
}
- (void)dealloc
{
[self stopNotifier];
if (reachabilityRef != NULL)
{
CFRelease(reachabilityRef);
}
}
#pragma mark - Network Flag Handling
- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags
{
PrintReachabilityFlags(flags, "localWiFiStatusForFlags");
BOOL returnValue = NotReachable;
if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
{
returnValue = ReachableViaWiFi;
}
return returnValue;
}
- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
{
PrintReachabilityFlags(flags, "networkStatusForFlags");
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
{
// The target host is not reachable.
return NotReachable;
}
BOOL returnValue = NotReachable;
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
{
/*
If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...
*/
returnValue = ReachableViaWiFi;
}
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
{
/*
... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...
*/
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
{
/*
... and no [user] intervention is needed...
*/
returnValue = ReachableViaWiFi;
}
}
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
{
/*
... but WWAN connections are OK if the calling application is using the CFNetwork APIs.
*/
returnValue = ReachableViaWWAN;
}
return returnValue;
}
- (BOOL)connectionRequired
{
NSAssert(reachabilityRef != NULL, #"connectionRequired called with NULL reachabilityRef");
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
}
- (NetworkStatus)currentReachabilityStatus
{
NSAssert(reachabilityRef != NULL, #"currentNetworkStatus called with NULL reachabilityRef");
NetworkStatus returnValue = NotReachable;
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
if (localWiFiRef)
{
returnValue = [self localWiFiStatusForFlags:flags];
}
else
{
returnValue = [self networkStatusForFlags:flags];
}
}
return returnValue;
}
#end
ViewController.m
- (void)viewDidLoad
{
toggleIsOn=TRUE;
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:self.viewVolume.bounds] ;
[self.viewVolume addSubview:volumeView];
[volumeView sizeToFit];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(IBAction)playButtonPressed:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:TRUE forKey:#"FirstPlay"];
[defaults setBool:YES forKey:#"alertShown"];
if(toggleIsOn)
{
if(noNetwork)
{
[self showAlert];
}
else
{
toggleIsOn=!toggleIsOn;
player = nil;
NSString *stringurl = #"";
stringurl = #"http://something.pls";
NSURL *url = [NSURL URLWithString:stringurl];
asset = [AVURLAsset URLAssetWithURL:url options:nil];
playerItem = [AVPlayerItem playerItemWithAsset:asset];
player = [AVPlayer playerWithPlayerItem:playerItem];
player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[playerItem addObserver:self forKeyPath:#"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
[playerItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
[player play];
isPlaying = TRUE;
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(audioSessionInterrupted:) name:AVAudioSessionInterruptionNotification object:nil];
[self.toggleButton setImage:[UIImage imageNamed:#"reload.png"] forState:UIControlStateNormal];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
}
else
{
[self.toggleButton setImage:[UIImage imageNamed:#"playMusic.png"] forState:UIControlStateNormal];
self->player.rate=0.0;
toggleIsOn=!toggleIsOn;
isPlaying = FALSE;
}
}
- (void)audioSessionInterrupted:(NSNotification *)notification
{
NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
switch (interruptionType.unsignedIntegerValue) {
case AVAudioSessionInterruptionTypeBegan:{
// [self.toggleButton setImage:[UIImage imageNamed:#"playMusic.png"] forState:UIControlStateNormal];
// • Audio has stopped, already inactive
// • Change state of UI, etc., to reflect non-playing state
} break;
case AVAudioSessionInterruptionTypeEnded:{
// • Make session active
// • Update user interface
// • AVAudioSessionInterruptionOptionShouldResume option
if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
// Here you should continue playback.
if(isPlaying)
{
[player play];
}
}
} break;
default:
break;
}
}
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)audioPlayer
{
if(isPlaying)
{
[player pause];
}
}
-(void)audioRecorderEndInterruption:(AVAudioRecorder *)audioPlayer
{
if(isPlaying)
{
[player play];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:TRUE forKey:#"alertShown"];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];
internetReachable = [Reachability reachabilityForInternetConnection];
[internetReachable startNotifier];
// check if a pathway to a random host exists
hostReachable = [Reachability reachabilityWithHostName:#"www.apple.com"];
[hostReachable startNotifier];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context {
[playerItem removeObserver:self forKeyPath:keyPath];
if ([keyPath isEqualToString:#"status"]) {
AVPlayerItem *pItem = (AVPlayerItem *)object;
if (pItem.status == AVPlayerItemStatusReadyToPlay)
{
metadatas.text = #"";
}
}
if ([keyPath isEqualToString:#"timedMetadata"]) {
for (AVAssetTrack *track in playerItem.tracks) {
for (AVPlayerItemTrack *item in player.currentItem.tracks) {
if ([item.assetTrack.mediaType isEqual:AVMediaTypeAudio]) {
NSArray *meta = [playerItem timedMetadata];
for (AVMetadataItem *metaItem in meta) {
NSString *source = metaItem.stringValue;
metadatas.text = source;
}
}
}
}
}
[self.toggleButton setImage:[UIImage imageNamed:toggleIsOn ? #"playMusic.png" :#"stop.png"] forState:UIControlStateNormal];
}
-(IBAction) sliderChanged:(id)sender
{
player.volume = slider.value;
}
-(void) checkNetworkStatus:(NSNotification *)notice
{
// called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
switch (internetStatus)
{
case NotReachable:
{
NSLog(#"The internet is down.");
NSLog(#"%d",[defaults boolForKey:#"alertShown"]);
BOOL isAlertShown = [defaults boolForKey:#"alertShown"];
if(isAlertShown)
{
noNetwork = TRUE;
isPlaying = false;
[self showAlert];
}
break;
}
case ReachableViaWiFi:
{
NSLog(#"The internet is working via WIFI.");
if(self.alert)
{
[self.alert dismissWithClickedButtonIndex:0 animated:YES];
self.alert = nil;
}
noNetwork = FALSE;
BOOL isFirstTimePlayed = [defaults boolForKey:#"FirstPlay"];
if(!isPlaying)
{
if(isFirstTimePlayed)
{
[self playButtonPressed:nil];
}
}
break;
}
case ReachableViaWWAN:
{
NSLog(#"The internet is working via WWAN.");
if(self.alert)
{
[self.alert dismissWithClickedButtonIndex:0 animated:YES];
self.alert = nil;
}
noNetwork = FALSE;
BOOL isFirstTimePlayed = [defaults boolForKey:#"FirstPlay"];
if(!isPlaying)
{
if(isFirstTimePlayed)
{
[self playButtonPressed:nil];
}
}
break;
}
}
}
-(void)showAlert
{
//NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//[defaults setBool:FALSE forKey:#"alertShown"];
//alert = [[UIAlertView alloc] initWithTitle: #"Alert" message: #"You have lost data connectivity. Please wait while we try to establish the connection again." delegate: nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
//[alert show];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:FALSE forKey:#"alertShown"];
self.alert = [[UIAlertView alloc] initWithTitle:#"Alert"
message:#"You have lost data connectivity. Please wait while we try to establish the connection again."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[self.alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(!isPlaying)
{
[player pause];
[self.toggleButton setImage:[UIImage imageNamed:#"playMusic.png"] forState:UIControlStateNormal];
}
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
#end
Actually it's not in your hand. It depends on the ISP you are using for data. I've been using Tata Docomo for a while & they don't resume internet connection after a call is made most of the time. It depends on the Call duration too. If its too short, your app will work without an issue. They might be maintaining some data sessions. So, What basically I wanted to tell is, There is nothing you can do here.. :(

iOS AVPlayer never gets ready

I am using this code to start playing local video chunks referenced to form a play list.
The very same code works on one project, but not on another.
On the project I am working on right now, I can see how the first chunk gets loaded, and the first frame also show up. But the AVPlayer never start playing because it never gets the AVPlayerStatusReadyToPlay notification:
- (void)loadAssetAsync
{
NSLog(#"loadAssetAsync for URL: %#", videoURL);
/**
* Create an asset for inspection of a resource referenced by a given URL.
* Load the values for the asset keys "tracks", "playable".
*/
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey, kPlayableKey, nil];
// Tells the asset to load the values of any of the specified keys that are not already loaded.
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
// IMPORTANT: Must dispatch to main queue in order to operate on the AVPlayer and AVPlayerItem.
[self prepareToPlayAsset:asset withKeys:requestedKeys];
});
}];
}
/**
* Invoked at the completion of the loading of the values for all keys on the asset that required.
*/
- (void)prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys
{
//assert([NSThread isMainThread]);
// Make sure that the value of each key has loaded successfully.
for (NSString *thisKey in requestedKeys)
{
NSError *error = nil;
AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
if (keyStatus == AVKeyValueStatusFailed)
{
BVLogWarn(#"%#: %#", THIS_FILE, error.localizedDescription);
[self handleErrorForProxy:error];
[self assetFailedToPrepareForPlayback];
return;
}
}
if (!asset.playable)
{
BVLogWarn(#"%#: Item cannot be played", THIS_FILE);
[self handleErrorForProxy:nil];
[self assetFailedToPrepareForPlayback];
return;
}
// Create a new instance of AVPlayerItem from the now successfully loaded AVAsset.
playerItem = [[AVPlayerItem alloc] initWithAsset:asset];
// Observe the player item "status" key to determine when it is ready to play.
[playerItem addObserver:self
forKeyPath:kStatusKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:BVPlayerItemStatusObserverContext];
[playerItem addObserver:self
forKeyPath:kBufferEmpty
options:NSKeyValueObservingOptionNew
context:BVPLayerBufferEmptyObserverContext];
[playerItem addObserver:self
forKeyPath:kLikelyToKeepUp
options:NSKeyValueObservingOptionNew
context:BVPlayerLikelyToKeepUpObserverContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:playerItem];
// Get a new AVPlayer initialized to play the specified player item.
player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
// Do nothing if the item has finished playing
[player setActionAtItemEnd:AVPlayerActionAtItemEndNone];
/* Observe the AVPlayer "currentItem" property to find out when any
AVPlayer replaceCurrentItemWithPlayerItem: replacement will/did
occur.*/
[player addObserver:self
forKeyPath:kCurrentItemKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:BVCurrentItemObserverContext];
// Observe the AVPlayer "rate" property to update the scrubber control.
[player addObserver:self
forKeyPath:kRateKey
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:BVRateObserverContext];
[player replaceCurrentItemWithPlayerItem:playerItem];
}
- (void)observeValueForKeyPath:(NSString*) keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context
{
// AVPlayerItem "status" property value observer.
if (context == BVPlayerItemStatusObserverContext)
{
AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
switch (status)
{
case AVPlayerStatusUnknown:
{
[self removeTimeObserver];
[self syncTimeScrubber];
[timeControl setEnabled:NO];
[playButton setEnabled:NO];
[fullscreenButton setEnabled:NO];
[loadingIndicator startAnimating];
}
break;
case AVPlayerStatusReadyToPlay:
{
if (firstPlayback|becomeActive)
{
[timeControl setEnabled:YES];
[playButton setEnabled:YES];
[fullscreenButton setEnabled:YES];
[upperControls setHidden:NO];
[lowerControls setHidden:NO];
[loadingIndicator stopAnimating];
if (firstPlayback) {
[playbackView setNeedsDisplay];
}
if (self.shouldAutoplay)
[player play];
if (firstPlayback) {
timeRemaining.text = [NSString stringWithFormat:#"-%#", timeStringForSeconds(CMTimeGetSeconds(playerItem.duration) )];
}
firstPlayback = NO;
controlsHidden = NO;
if (!isSeeking)
[self startHideControlsTimer];
}
if (becomeActive) {
dispatch_async(dispatch_get_main_queue(), ^{
[player seekToTime:CMTimeMakeWithSeconds(lastTimeStop, NSEC_PER_SEC)
toleranceBefore:kCMTimeZero
toleranceAfter:kCMTimeZero
completionHandler:^(BOOL finished) {
if (finished && rateToRestoreAfterScrubbing)
{
[player setRate:rateToRestoreAfterScrubbing];
rateToRestoreAfterScrubbing = 0.f;
}
[self addTimeObserver];
[playbackView setPlayer:player];
becomeActive = NO;
}];
});
}else{
[self addTimeObserver];
}
}
break;
case AVPlayerStatusFailed:
{
AVPlayerItem *thePlayerItem = (AVPlayerItem *)object;
BVLogWarn(#"%#: %#", THIS_FILE, thePlayerItem.error.localizedDescription);
[self handleErrorForProxy:thePlayerItem.error];
[self assetFailedToPrepareForPlayback];
}
break;
}
}
// AVPlayer "rate" property value observer.
else if (context == BVRateObserverContext)
{
[self updatePlayPauseButton];
}
// AVPlayer "currentItem" buffer is empty observer
else if (context == BVPLayerBufferEmptyObserverContext)
{
[loadingIndicator startAnimating];
}
// AVPlayer "currentItem" is likely to keep up observer
else if (context == BVPlayerLikelyToKeepUpObserverContext)
{
[loadingIndicator stopAnimating];
}
// AVPlayer "currentItem" property observer.
else if (context == BVCurrentItemObserverContext)
{
AVPlayerItem *newPlayerItem = [change objectForKey:NSKeyValueChangeNewKey];
// New player item null?
if (newPlayerItem == (id)[NSNull null])
{
[playButton setEnabled:NO];
[timeControl setEnabled:NO];
} else // Replacement of player currentItem has occurred
{
if (!becomeActive) {
[playbackView setPlayer:player];
}else{
}
[playbackView setVideoFillMode:[self scalingMode]];
[self updatePlayPauseButton];
}
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
return;
}
Did you try to put some log before your if ? Maybe the notification is working but it's stucked because your if?
case AVPlayerStatusReadyToPlay:
{
NSLog(#"NOTIFICATION TEST PASSED");
if (firstPlayback|becomeActive) {}
}

Resources