How do I replace MPMoviePlayer notifications? - ios

In iOS 9 MPMoviePlayer and all his components are deprecated.
We used MPMoviePlayerController notifications, like MPMoviePlayerLoadStateDidChangeNotification, MPMovieDurationAvailableNotification, MPMoviePlayerPlaybackStateDidChangeNotification, MPMoviePlayerReadyForDisplayDidChangeNotification, to track video service quality. But now with AVPlayerViewController I can't find properly replacement for these notifications.
How do I replace these notifications now?

AVPlayerViewController is a lot different in its usage from the MPMoviePlayerViewController. Instead of using notifications you use Key Value Observing to determine the current characteristics of the AVPlayer object associated with the AVPlayerViewController. According to the docs:
You can observe the status of a player using key-value observing. So
that you can add and remove observers safely, AVPlayer serializes
notifications of changes that occur dynamically during playback on a
dispatch queue. By default, this queue is the main queue (see
dispatch_get_main_queue). To ensure safe access to a player’s
nonatomic properties while dynamic changes in playback state may be
reported, you must serialize access with the receiver’s notification
queue. In the common case, such serialization is naturally achieved by
invoking AVPlayer’s various methods on the main thread or queue.
For instance, if you want to know when your player has been paused add an observer on the rate property of the AVPlayer object:
[self.player addObserver:self forKeyPath:#"rate" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context: &PlayerRateContext];
Then in the observe method check if the new value is equal to zero:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if (context == &PlayerRateContext) {
if ([[change valueForKey:#"new"] integerValue] == 0) {
// summon Sauron here (or whatever you want to do)
}
return;
}
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
A lot of properties on the AVPlayer are observable. Go through the Class reference.
Also apart from this there are several Notifications available for the AVPlayerItem object which are limited but still helpful.
Notifications
AVPlayerItemDidPlayToEndTimeNotification
AVPlayerItemFailedToPlayToEndTimeNotification
AVPlayerItemTimeJumpedNotification
AVPlayerItemPlaybackStalledNotification
AVPlayerItemNewAccessLogEntryNotification
AVPlayerItemNewErrorLogEntryNotification
I find AVPlayerItemDidPlayToEndTimeNotification particularly useful to seek the Item to the start once playback has finished.
Using these two options together you should be able to replace most if not all notifications for the MPMoviePlayerController

I looked at the documentation for both MPMoviePlayerNotifications and AVPlayerItemNotifications and I notice two things.
MPMoviePlayerNotifications don't show they were deprecated:
AVPlayerItemNotifications don't have any replacements that I could see:
So, I am confused that you are saying MPMoviePlayerNotifications are deprecated, because the docs are saying they are available. Also, I don't think AVPlayerItemNotifications has a replacement for MPMoviePlayerNotifications.

Related

how does "idleTimerDisabled" work in didFinishLaunchingWithOptions?

I'm not sure how does the below code work in the didFinishLaunchingWithOptions in appdelegate ?
[[UIApplication sharedApplication] addObserver: self forKeyPath: #"idleTimerDisabled" options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context: nil];
what does it exactly do with "idleTimerDisabled" ?
thanks
Apple Documentation for idleTimerDisabled
The default value of this property is NO. When most apps have no touches as user input for a short period, the system puts the device into a "sleep” state where the screen dims. This is done for the purposes of conserving power. However, apps that don't have user input except for the accelerometer—games, for instance—can, by setting this property to YES, disable the “idle timer” to avert system sleep.
By assigning true to this value, iOS won't dim the screen and lock the iPhone when user doesn't make any action (touches, press, scroll, etc...). Example of this can be found in games vs other normal apps. Games make your iPhone awake for a lot longer than other apps.
For
[[UIApplication sharedApplication] addObserver: forKeyPath: options: context:]
This is an Objective-c key value observing aka KVO.
What your code means is that, when someone assigns or make changes to UIApplication.sharedApplication.idleTimerDisabled with an arbitrary value, true or false in this case, you wish to receive an invocation about the assignment or change in [self observeValueForKeyPath: ofObject: change: context:] method signature.
The option NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew means, you want to receive more information about changed old value and new value in the change dictionary which you can access later in the observing method.
For more information about KVO, checkout this post by NSHipster.
AppCoda also has a nice explanation for this.
Conclusion
What your line of code means is, you want to receive a notification about the changes made to UIApplication.shared.idleTimerDisabled attribute in KVO observing method and you want to access old value and new value through change dictionary.

How to avoid deallocated objects being accessed in callbacks/etc?

The issue has been discussed here and here, but I wonder if there is a more solid way to solve this whether you have delegates or not - when a function is called after a delay.
At a certain point in a program, at a button push, an object - a CCLayer - is created. That layer creates several objects, some of them at callbacks. That created object layer has a "back" button which destroys it. I am running into a problem when the callbacks, etc are triggered AFTER that object is destructed and try to access objects that don't exist anymore - where the "message sent to deallocated instance 0x258ba480" gives me this good news. How do I avoid that?
1) Is there a way to kill the callbacks (because I obviously don't need them anymore)
2) should/can I test for the existence of these possibly non-existent objects at the callbacks themselves
3) something else?
(My callback is code for checking for an internet connection that I copied from this illustrious website - may it live long and prosper-, using Reachability, and I could solve the problem by simply moving it to the main view and setting a flag on the child view, but I don't want to.)
- (void)testInternetConnection
{
internetReachableFoo = [Reachability reachabilityWithHostname:#"www.google.com"];
// Internet is reachable
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Yayyy, we have the interwebs!");
//I do the net stuff here
});
};
// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Someone broke the internet :(");
noNetMessageLabel.visible=true; //<-------this goes kaboom
noNetFlag=true;
});
};
[internetReachableFoo startNotifier];
}
There are basically two ways to avoid deallocated delegates from being messaged:
Hold onto the objects you want to message later. That way they won’t get deallocated. This is the case with block callbacks – if a block references some object, the object gets retained until the block ceases to exist. If you message some objects from a block and hit a deallocated object, you must have screwed up the memory management somewhere.
Clear the delegation link before you release the delegate. Nowadays this is usually done using weak, zeroing properties that are automatically set to nil when the referenced object is deallocated. Very convenient. Not your case.
You might consider several options:
First, you may just check for existence of an object before passing message to it:
if (noNetMessageLabel)
noNetMessageLabel.visible = true;
But personally I consider that as a bad architecture.
More wise decision, from my point of view, would be move the code of displaying any alert regarding internet connectivity to the model.
Create method like this in AppDelegate or in the model:
- (NSError*)presentConnectivityAlert
{
if () //any error condition checking appropriate
[[NSNotificationCenter defaultCenter]
postNotificationName:#"connectivityAlert"
object:self
userInfo:userInfo];
}
Also you may consider moving internet checking code to the model too.
In the ViewControllers of your app implement listening to this notification.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didReceiveRemoteNotification:)
name:#"connectivityAlert"
object:nil];
}
- (void)viewDidUnload {
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:#"connectivityAlert"
object:nil];
}
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (self.isViewLoaded && self.view.window) {
//Present the user with alert
}
}
Thus you have more general and quite versatile approach to handle connectivity issues throughout all your application.
Is there a way to kill the callbacks
It's not possible to cancel block (in your case), but it's possible to cancel NSOperation in NSOperationQueue. But that will require to rewrite your implementation of Reachability.

Why is KVO sending a change notification when both the new and old values are the same?

I have an entity which is a subclass of NSManagedObject called Event. I have also registered a few modeled attributes of this entity for KVO change notifications:
[self.event addObserver:self
forKeyPath:#"numGuests"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:&numGuestsContext];
[self.event addObserver:self
forKeyPath:#"checkedIn"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:&checkedInContext];
[self.event addObserver:self
forKeyPath:#"seatedCount"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:&seatedContext];
However it seems the observeValueForKeyPath method notification is getting triggered even though the value of NSKeyValueChangeOldKey and NSKeyValueChangeNewKey in the change dictionary are equal???
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSNumber *oldValue = [change valueForKey:NSKeyValueChangeOldKey];
NSNumber *newValue = [change valueForKey:NSKeyValueChangeNewKey];
if ([oldValue isEqualToNumber:newValue])
{
return;
}
For now i have resorted to just doing a quick sanity check to see if they are equal but i would like to understand why this notification is getting fired to begin with?
UPDATE: #jszumski mentioned in the comments that this most likely is happening because the objects are different although logically equal. The Event entity object always has the same address however the object i am observing, which is an attribute within the entity, keeps changing addresses although i am not sure why??
I am wondering if accessing this value in a bg query thread could cause Core Data to create new internal objects within the entity with the same value?
Simply, you are assuming the functionality of the NSKeyValueObservingOptions. They do not work as you are expecting them to. The flags you send only decide what the contents of the change dictionary are. The observer is always triggered whenever the setters are called.
Besides, when you are sending flags old and new, that really just says "I would like the old value, and new value. I don't care if they are equal or not".
Please read the reference to learn more:
NSKeyValueObservingOptions
These constants are passed to addObserver:forKeyPath:options:context:
and determine the values that are returned as part of the change
dictionary passed to an
observeValueForKeyPath:ofObject:change:context:. You can pass 0 if you
require no change dictionary values.

Sending Notifications from Model to Controller in iOS

I made a class, called Timer. Its designated initializer starts a timer with a value in seconds. It works great. However I am having trouble updating the controller w/e the timer ticks.
Right now, for every tick I am sending a NSNotificationCenter with a userInfo that is a simple dictionary with the current time, which does not sound the best way to do it...
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:self.timerCount] forKey:#"timerCount"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"TimerCountChanged"
object:self
userInfo:dict];
Should I be using some other technique or am I doing it the right way?
Thank you in advance!
EDIT:
I need to initialize different Timers, using different values. I tried to use Delegates, but I only had one method in my controller to update the UI for all those Timers!
Would it be bad if I do something like? Passing a UIButton to my Model also does not seem to be the best solution but it works.
-(void)timer:(Timer *)timer didTriggerAt:(NSTimeInterval)time andButton:(UIButton *)button
{
[button setTitle:[NSString stringWithFormat:#"%.0f", time] forState:UIControlStateNormal];
}
- (IBAction)startCountDown:(UIButton *)sender
{
self.timer1 = [[Timer alloc] initWithTimeInSeconds:10 andButton:sender];
self.timer1.delegate = self;
}
I have 3 Timers in my MainView, the user can start them whenever he wants. They can also have different times, which is also defined by the user.
Sending Notifications is good, but you may not observe it as in regular time.
Sometimes it gets delayed and you may observe them in irregular time interval.
You can use
Delegate Pattern.
Call method by selector
EDIT:
From
Apple documentation on Performance CodeSpeed on Notifications.
The fewer notifications you send, the smaller the impact on your
application’s performance. Depending on the implementation, the cost
to dispatch a single notification could be very high. For example, in
the case of Core Foundation and Cocoa notifications, the code that
posts a notification must wait until all observers finish processing
the notification. If there are numerous observers, or each performs a
significant amount of work, the delay could be significant.
If you only have one client object for each Timer instance, then you should use the delegate pattern. You would define a TimerDelegate protocol with a method that a Timer object can call whenever the timer ticks.
e.g.
#class Timer;
#protocol TimerDelegate
- (void) timer:(Timer *)timer didTriggerAt:(NSTimeInterval)time;
#end
#interface Timer
...
#property (assign) id<TimerDelegate> delegate;
...
#end
If you indeed require multiple listeners each time a Timer instance ticks, then the NSNotificationCenter approach would be a better fit. Instead of passing info in the userInfo dictionary, I probably would expose an #property on Timer called currentTime, so that when a client object gets the notification, they could simply access currentTime on the notifying Timer, instead of (IMO clunkily) reading data out of userInfo.

iOS event/notification for network activity up/down/off

I want a event/callback for my iOS app when the network activity goes from none to up (and the other way around). Similar to how Android does with onDataActivity(). I'm not talking about Reachability but when data actually starts or stops transmitting.
The app is not for App Store and not for jailbreak. I have got a similar functionality of detecting when the screen goes on/off working by using
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
NULL, // observer
displayStatusChanged, // callback
CFSTR("com.apple.iokit.hid.displayStatus"), // event name
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
along with other events such as
com.apple.springboard.hasBlankedScreen
com.apple.springboard.lockstate
Now I wonder if there is a event for when data is started or stopped to transmit? Or if someone could point me the direction of a complete list of all events that can be monitored in the way above.
I monitored both the standard Darwin notifications and the Core Telphony notifications on a jailbroken iOS 5 iPhone.
I did not see any notifications that really do what you want.
There are a few Core Telephony notifications that come out, but not on every transmit start and end. It looks like when the data service connects, there might be some notifications, but again, they really aren't exactly what you asked for:
kCTIndicatorRadioTransmitNotification
kCTRegistrationDataStatusChangedNotification
If you want to try monitoring all the Core Telephony notifications for yourself, you can use the Core Telephony framework, and the CT notification center:
-(void) registerCallback {
id ct = CTTelephonyCenterGetDefault();
CTTelephonyCenterAddObserver(ct, // center
NULL, // observer
telephonyEventCallback, // callback
NULL, // event name (or all)
NULL, // object
CFNotificationSuspensionBehaviorDeliverImmediately);
}
static void telephonyEventCallback(CFNotificationCenterRef center, void* observer, CFStringRef name, const void* object, CFDictionaryRef userInfo)
{
//NSLog(#"telephonyEventCallback()");
NSString* notifyName = (__bridge NSString*)name;
if ([notifyName isEqualToString:#"kCTMessageReceivedNotification"]) { // received SMS
} /* look for other notification names here */
}
In the above call, I pass NULL to the CTTelephonyCenterAddObserver() call, which registers for all notifications. You can of course pass the name of one particular notification if you know what you're looking for, like the example you posted for com.apple.iokit.hid.displayStatus.
Regarding john.k.doe's option, you might try using Key Value Observing on that property, to get notified when it changes:
UIApplication* app = [UIApplication sharedApplication];
[app addObserver: self forKeyPath: #"networkActivityIndicatorVisible" options: NSKeyValueObservingOptionNew context: nil];
where your observer callback is:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:#"networkActivityIndicatorVisible"]) {
// process here
NSLog(#"network activity indicator change!");
BOOL active = [UIApplication sharedApplication].networkActivityIndicatorVisible;
}
}
I'm not sure if KVO will still work in the background, and it might depend on how your app manages being backgrounded.
But, of course, that requires apps to actually use that property when they access the network, which not all of them do. It's unfortunate that Apple even made that indicator something that 3rd-party developers need to control. On Android and BlackBerry, the OS is smart enough to know when it's transmitting/receiving.
So, this is still only partly what you need :(
there's really not a system Notification for this.
if you are talking about monitoring activity to/from your own app, you obviously have control of that, and should be able to bracket any calls you know to access the network …
if you are talking about monitoring activity to/from all other apps, and you are willing to presume that they are in strict compliance with Apple guidelines and turn on/off the network activity status indicator in the status bar, you can call:
[[UIApplication sharedApplication] isNetworkActivityIndicatorVisible];
but this obviously requires that you do so in a polling fashion, probably best done on a background thread / GCD queue. then you can post your own notification, to be seen elsewhere within your app.

Resources