how does "idleTimerDisabled" work in didFinishLaunchingWithOptions? - ios

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.

Related

Swift NotificationCenter remove observer quickest way

I am adding a number of observers in my viewController -- applicationWillResignActive, applicationDidEnterBackground, and many others. I want to remove self as observer to all registered notifications in one line. My question is whether the following line is enough to do that, or are there issues with this code?
deinit {
NotificationCenter.default.removeObserver(self)
}
#Sh_Khan is right:
NotificationCenter.default.removeObserver(self)
You can get even further, as mentioned in the Apple Documentation:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method.
So I'm working with this in an app right now and the answer might not be as straightforward.
In the documentation, it does state that for iOS 9 and above you are no longer required to explicitly remove the observer in the deinit/dealloc methods for objects. https://developer.apple.com/documentation/foundation/notificationcenter/1413994-removeobserver
However, it appears that this is only true for selector based notification observers. I'll be referencing this blog post: https://oleb.net/blog/2018/01/notificationcenter-removeobserver/.
If you are using block based observers you must still manually remove the observers.
addObserver(forName:object:queue:using:)
The best general way to do this is to capture the tokens in an array, append them when you add the observer and use them for removal when you deinit/dealloc or otherwise need to remove observer behavior for your object.
in your VC/object properties create an array to store observer 'tokens'
var notifObservers = [NSObjectProtocol]()
Register for a block based notification by capturing the function return object and storing it as a token
let observer = NotificationCenter.default.addObserver(forName: , object: , queue:) { [weak self] notification in
// do a thing here
}
notifObservers.append(observer)
Removal
for observer in notifObservers {
NotificationCenter.default.removeObserver(observer)
}
notifObservers.removeAll()
Yes
NotificationCenter.default.removeObserver(self)
this line is sufficient to remove the vc observation as long as all are added with
NotificationCenter.default.addObserver

AVPlayer removing observer crash in Swift 2.2

I have a video app that I built a while back in Swift 1 and I've been trying to migrate to Swift 2.2. It all (finally) works apart from a weird crash to do with observers.
func removeObservers()
{
print("REMOVING OBSERVERS")
if ( !self.is_image && self.player != nil ) {
if (self.player?.observationInfo != nil) {
self.player?.removeObserver(self, forKeyPath: "currentItem.status")
self.player?.removeObserver(self, forKeyPath: "readyForDisplay")
}
}
NSNotificationCenter.defaultCenter().removeObserver(self)
}
This worked previously using SwiftTryCatch but with the lines in place crashes with "'Cannot remove an observer for the key path "readyForDisplay" from because it is not registered as an observer.'" OR with that an observer is registered on a deallocated object if I comment it out.
If I add a do { } catch {} to it I get an error that "this does not throw" and it just crashes the same. How do I go about putting this in some form of try-catch format?
In Swift 2, the libs got annoyingly strict about errors that are truly unexpected (which throw) versus errors that the programmer could have prevented (which do not throw, but just crash your app).
(I’m not a fan of this distinction, or at least not of all the specific decisions Apple made about which errors fall in which category. The JSON API verges on the nonsensical in this department. But…we work with the API we’ve got.)
The NSKeyValueObserving docs say:
It is an error to call removeObserver:forKeyPath: if the object has not been registered as an observer.
“It is an error” is Apple code for “you are responsible for never doing this, and if you do, your app will crash in an uncatchable way.”
In these situations, there is usually an API call you can make to check the validity of the thing you’re about to do. However, AFAIK, there’s no KVO API call you can make to ask, “Is X observing key path Y of object Z?” That means you have three options:
Figure out why you’re trying to remove an observer from something you’re not observing, and prevent that using your program’s own internal logic.
Keep a weak instance var for “player I’m observing,” and check that for a match before attempting to remove the observer.
Add self as an observer before removing it. (I’m pretty sure that a redundant add is OK.)
Since you are making a call removeObserver(self) at the end of the method, why cant you uncomment above code? Because removeObserver(self) removes all the observers if registered any. I hope this solves your issue.
NSNotificationCenter.defaultCenter().removeObserver(self)
status is a property of either AVPlayer or AVPlayerItem.
readyForDisplay is a property of AVPlayerLayer

Observing values for a primitive pointer

I have a class with a property
#property (nonatomic) double* myDoubles
This property has 3 doubles in it
myDoubles[0]; //0.02
myDoubles[1]; //0.42
myDoubles[2]; //0.99
If the values change, I'd like the following method to be called
[self setNeedsDisplay];
I tried using FBKVOController, but that didn't work..
_observer = [FBKVOController controllerWithObserver:self];
[_observer observe:self
keyPath:#"myDoubles"
options:NSKeyValueObservingOptionNew
action:#selector(setNeedsDisplay)];
I don't want to start an NSTimer and just check for changes.
This is not possible.
Notifications work because the code making changes does so through some method that knows to notify listeners of the change. If that same code were simply to write to the memory location backing the data, the notification would never be triggered.
What you want to do is simply declare a memory location that code will write to; no notification can happen from this (unless you have very system-dependent support making it possible - a memory watchpoint - and then your question changes significantly. Such support, when available, is very limited and not of good generic value).

NSNotificationCenter callback while app in background

One question and one issue:
I have the following code:
- (void) registerForLocalCalendarChanges
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(localCalendarStoreChanged) name:EKEventStoreChangedNotification object:store ];
}
- (void) localCalendarStoreChanged
{
// This gets call when an event in store changes
// you have to go through the calendar to look for changes
[self getCalendarEvents];
}
These methods are in a class/object called CalendarEventReporter which contains the method getCalendarEvents (in the callback).
Two things:
1) If the app is in the background the callback does not run. Is there a way to make it do that?
2) When I bring the app back into the foreground (after having changed the calendar on the device) the app crashes without any error message in the debug window or on the device. My guess is that the CalendarEventReporter object that contains the callback is being garbage-collected. Is that possible? Any other thoughts on what might be causing the crash? Or how to see any error messages?
1) In order for the app to run in the background you should be using one of the modes mentioned in the "Background Execution and Multitasking section here:
uses location services
records or plays audio
provides VOIP
services
background refresh
connection to external devices
like through BLE
If you are not using any of the above, it is not possible to get asynchronous events in the background.
2) In order to see the crash logs/call stack place an exception breakpoint or look into the "Device Logs" section here: Window->Organizer->Devices->"Device Name" on left->Device Logs on Xcode.
To answer your first question, take a look at https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
What I did to get code running in the background is to do something like
In the .h file
UIBackgroundTaskIdentifier backgroundUploadTask;
In the .m file
-(void) functionYouWantToRunInTheBackground
{
self.backgroundUploadTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundUpdateTask];
}];
//code to do something
}
-(void) endBackgroundUpdateTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundUploadTask];
self.backgroundUploadTask = UIBackgroundTaskInvalid;
}
The code above I pretty much learned from objective c - Proper use of beginBackgroundTaskWithExpirationHandler
As for your second question, you should set a breakpoint where code is supposed to run when you bring the app back to the foreground. No one can figure out why an app crashes if not given enough code or information.
The solution to the second part of the question was to raise the scope of the object containing the callback code. I raised it to the level of the containing ViewController. This seems to work. I still can't figure out how to raise the Notification (i.e. execute the call back) if the notification comes while the app is in the background/suspended. This prevented the object containing the callback from being cleaned up.

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