I tried to detect if screen capture is on for the application for iOS 11, to detect this the UIScreen.mainScreen.isCaptured property is used to check if it is recorded.
It works fine for the first launch, when the app is terminated and launched again, then the API returns NO though the screen capture is on.
Code:
//In viewWillAppear block
__block ViewController *weakSelf = self;
[NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf screenCaptureDetection];
}];
- (void) screenCaptureDetection {
if (#available(iOS 11.0, *)) {
for (UIScreen *screen in [UIScreen screens]) {
if([screen performSelector:#selector(isCaptured)]){
//Detected YES
}
}
}
Use case scenario:
Launch the app
Start screen recorder using the apple screen recording option
The screen recorder is detected
Terminate the app
Repeat the step 1 and 2
The screen recording is not detected, the API UIScreen.mainScreen.isCaptured returns NO
Please suggest
You should check for recording repeatedly.
I used this code and it worked for me.
check it out:
https://gist.github.com/abhimuralidharan/8db55dff9023028867b719f251372bd7#file-screenrecordingdetector-m
Related
I am developing apple watch application. my application working like every second it's notify sound and vibrate on my watch app.
Otherwise How can i stop watch from going to lock screen after 15 second?
My code is as follows.
- (void)willActivate {
[super willActivate];
soundAlert = [NSTimer scheduledTimerWithTimeInterval:1.2f target:self selector:#selector(SoundAlertNotification) userInfo:nil repeats:YES];
}
- (void)SoundAlertNotification
{
if (!isRechableFlag) {
[self playsound];
}
}
#pragma mark - Play Sound Methods -
- (void) playsound
{
[[WKInterfaceDevice currentDevice] playHaptic:WKHapticTypeRetry];
}
But when watch is going to lockscreen around 15 secode my sound method stop to call rather then it's work well.
What am I doing wrong?
This is a user preference setting, https://www.imore.com/how-keep-your-apple-watch-screen-longer
Otherwise you have to set your application up as a workout app to keep the screen from hibernating, but if it is not actually a workout app then it will get denied when you try to publish to the app store.
Hey I am developing an app in which i have to make API call every 30 sec, so i created NSTimer for it.
But when my app goes into background timer stops firing after 3-4 minutes. So it works only 3-4 minutes in background,but not after that. How can i modify my code so that timer would not stop.
Here is some of my code.
- (IBAction)didTapStart:(id)sender {
NSLog(#"hey i m in the timer ..%#",[NSDate date]);
[objTimer invalidate];
objTimer=nil;
UIBackgroundTaskIdentifier bgTask = UIBackgroundTaskInvalid;
UIApplication *app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
}];
objTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self
selector:#selector(methodFromTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:objTimer forMode:UITrackingRunLoopMode];
}
-(void)methodFromTimer{
[LOG debug:#"ViewController.m ::methodFromTimer " Message:[NSString stringWithFormat:#"hey i m from timer ....%#",[NSDate date] ]];
NSLog(#"hey i m from timer ....%#",[NSDate date]);
}
I even changed the code with the following:
[[NSRunLoop mainRunLoop] addTimer:objTimer forMode:NSRunLoopCommonModes];
This didn't work either.
Don't create UIBackgroundTaskIdentifier task as local and make it global as below:
Step -1
Step -2
Step -3
Step -4
As local one loose scope and global one won't ,and I created a demo and ran it for sometime with 1 sec repeating timer ,and worked smooth.
Still if u face issue pls let me know.
I ran again demo and here are logs of it running.
So its working fine and more than 3 minutes. Also that 3 minute logic is right but as uibackgroundtask is initiated so it shouldn't let it kill this task of timer.
Edited Part:-
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask]; //Remove this line and it will run as long as timer is running and when app is killed then automatically all vairbles and scopes of it are dumped.
}];
Check it and let me know if it works out or not.
Hey I run ur code and I reached the expirationHandler but after released debug point ,the timer was running smooth.
No, don't do background tasks with NSTimer. It will not work as you might expect. You should be making use of background fetch APIs provided by Apple only. You can set the duration at which you want it to be called in that API. Though usually it is not recommended setting duration of the call you would like to make. Take a look at this apple background programming documentation
Also, to get you started quickly, you can follow this Appcoda tutorial
This worked for me, so I'm adding it to StackOverflow for any future answer seekers.
Add the following utility method to be called before you start your timer. When we call AppDelegate.RestartBackgroundTimer() it will ensure that your app will remain active - even if it's in the background or if the screen is locked. However, this will only ensure that for 3 minutes (as you mentioned):
class AppDelegate: UIResponder, UIApplicationDelegate {
static var backgroundTaskIdentifier: UIBackgroundTaskIdentifier? = nil;
static func RestartBackgroundTimer() {
if (AppDelegate.backgroundTaskIdentifier != nil) {
print("RestartBackgroundTimer: Ended existing background task");
UIApplication.sharedApplication().endBackgroundTask(AppDelegate.backgroundTaskIdentifier!);
AppDelegate.backgroundTaskIdentifier = nil;
}
print("RestartBackgroundTimer: Started new background task");
AppDelegate.backgroundTaskIdentifier = UIApplication.sharedApplication().beginBackgroundTaskWithExpirationHandler({
UIApplication.sharedApplication().endBackgroundTask(AppDelegate.backgroundTaskIdentifier!);
AppDelegate.backgroundTaskIdentifier = nil;
})
}
}
Also, when starting your app, ensure the following runs. It will ensure that audio is played even if the app is in the background (and while you're at it, also ensure that your Info.plist contains "Required background modes" and that "App plays audio or streams audio/video using AirPlay" a.k.a. "audio" is in its collection):
import AVFoundation;
// setup audio to not stop in background or when silent
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback);
try AVAudioSession.sharedInstance().setActive(true);
} catch { }
Now, in the class that needs the timer to run more than 3 minutes (if in the background), you need to play a sound when only 30 seconds remains of background time. This will reset the background time remaining to 3 minutes (just create a "Silent.mp3" with e.g. AudaCity and drag & drop it to your XCode project).
To wrap it all up, do something like this:
import AVFoundation
class MyViewController : UIViewController {
var timer : NSTimer!;
override func viewDidLoad() {
// ensure we get background time & start timer
AppDelegate.RestartBackgroundTimer();
self.timer = NSTimer.scheduledTimerWithTimeInterval(0.25, target: self, selector: #selector(MyViewController.timerInterval), userInfo: nil, repeats: true);
}
func timerInterval() {
var bgTimeRemaining = UIApplication.sharedApplication().backgroundTimeRemaining;
print("Timer... " + NSDateComponentsFormatter().stringFromTimeInterval(bgTimeRemaining)!);
if NSInteger(bgTimeRemaining) < 30 {
// 30 seconds of background time remaining, play silent sound!
do {
var audioPlayer = try AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Silent", ofType: "mp3")!));
audioPlayer.prepareToPlay();
audioPlayer.play();
} catch { }
}
}
}
It is normal behavior.
After iOS7, you got exactly 3 minutes of background time. Before that there was 10 minutes if i remember correctly. To extend that, your app needs to use some special services like location, audio or bluetooth which will keep it "alive" in the background.
Also, even if you use one of these services the "Background app refresh" setting must be enabled on your device for the app.
See this answer for details or the background execution part of the documentatio.
I need to create a iOS app where the app has to continuously check for the updates from the server(may be every 30 secs). But only when the app is running on the foreground.
I know this will drain the battery, but this will run on a environment where there's no internet. So we can't use push notifications.
Only option I can think of is sending a request to the server every 30 secs or so and get the updates. What is the best way to do this? Using NSTimer and NSURLConnection or any other better approaches?
Also if I use a timer, when the app goes to the background will it pause and will it start running as it comes to the foreground again? Is there a chance that app get killed while its on background?
Thanks
Using NSTimer and NSURLConnection or any other better approaches?
My first thought was also to use NSTimer and NSURLConnection.
Also if I use a timer, when the app goes to the background will it pause and will it start running as it comes to the foreground again?
Yes, it will. It doesn't exactly pause, but based on my testing in the simulator, the effect is similar. Let's say the timer is set to go off at 00:00:00, 00:00:30, 00:00:60, ... and you background the app at 00:00:15 and resume it at 00:00:45. The timer that was supposed to fire at 00:00:30 fires immediately when you resume (at 00:00:45), and the next firing (at 00:00:60) and subsequent firings are back on schedule.
Is there a chance that app get killed while its on background?
Yes, there is. But if you start the timer whenever the app launches, this shouldn't be a problem, right?
Your best bet is to setup a separate object that manages these operations on a background thread. Then in your app delegate, when
- (void)applicationWillResignActive:(UIApplication *)application
is called, have this special object stop all of it's synchronizing and clean up anything it needs to.
Then when:
- (void)applicationDidBecomeActive:(UIApplication *)application
gets called as the app gets active again, signal your object to query / poll on its background thread again.
Your custom object could have an interface like this
#interface PollingObject : NSObject
{
NSTimer* _timer;
NSUinteger _interval;
BOOL _cancel;
BOOL _isPolling;
dispatch_queue_t _pollQueue;
}
- (void)startPolling;
- (void)stopPolling;
#end
The implementation can be something like this:
#implementation PollingObject : NSObject
- (id)init
{
if (self = [super init])
{
_interval = 1; // 1 second interval
_cancel = NO; // default to NO
_isPolling = NO; // default to NO
// init your background queue
_pollQueue = dispatch_queue_create("com.yourconame.yourappname.pollQueue", NULL);
}
return self;
}
- (void)heartbeat
{
if (_cancel)
{
// stop the timer
[_timer invalidate];
_isPolling = NO;
return;
}
// Runs the polling method ONCE on a background queue
dispatch_async(_pollQueue, ^{
[self pollingMethod];
});
}
- (void)pollingMethod
{
// Do actual network polling work here...but only run it once. (don't loop)
}
- (void)startPolling
{
_cancel = NO;
if (_isPolling)
{
NSLog(#"Already polling");
return;
}
// schedule the method heartbeat to run every second
_timer = [NSTimer scheduledTimerWithTimeInterval:_interval target:self selector:#selector(heartbeat) userInfo:nil repeats:YES];
}
- (void)stopPolling
{
// we set the flag here and the next second the heartbeat will stop the timer
_cancel = YES;
}
#end
Look at Rocket real-time networking which looks easy to setup through AFNetworking 2.0.
https://github.com/AFNetworking/AFNetworking/wiki/AFNetworking-2.0-Migration-Guide
See the last part of this wiki. I have not used it but it would be something I would try if I had your requirements.
I am trying to start playing a sound from a background task via an AVAudioPlayer that is instantiated then, so here's what I've got.
For readability I cut out all user selected values.
- (void)restartHandler {
bg = 0;
bg = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bg];
}];
tim = [NSTimer timerWithTimeInterval:.1 target:self selector:#selector(upd) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:tim forMode:NSRunLoopCommonModes];
}
- (void)upd {
if ([[NSDate date] timeIntervalSinceDate:startDate] >= difference) {
[self playSoundFile];
[tim invalidate];
[[UIApplication sharedApplication] endBackgroundTask:bg];
}
}
- (void)playSoundFile {
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
// new player object
_player = [[AVQueuePlayer alloc] init];
[_player insertItem:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"Manamana" withExtension:#"m4a"]] afterItem:nil];
[_player setVolume:.7];
[_player play];
NSLog(#"Testing");
}
Explanation: bg, tim, startDate, difference and _player are globally declared variables. I call - (void)restartHandler from a user-fired method and inside it start a 10Hz repeating timer for - (void)upd. When a pre-set difference is reached, - (void)playSoundFile gets called and initiates and starts the player. The testing NSLog at the end of the method gets called.
Now the strange thing is if I call - (void)playSoundFile when the app is active, everything works out just fine, but if I start it from the background task it just won't play any sound.
Edit
So I tried using different threads at runtime and as it seems, if the Player is not instantiated on the Main Thread this same problem will appear.
I also tried using AVPlayer and AVAudioPlayer where the AVAudioPlayer's .playing property did return YES and still no sound was playing.
From what I've learned after writing an player App, it seems that you can not start playing audio when your App is already in background for longer than X seconds, even if you have configured everything right.
To fix it, you have to use background task wisely.
The most important thing is that you must NOT call endBackgroundTask immediately after playSoundFile. Delay it for about 5-10 seconds.
Here is how I managed doing it for iOS6 and iOS7.
1, Add audio UIBackgroundModes in plist.
2, Set audio session category:
3, Create an AVQueuePlayer in the main thread and enqueue an audio asset before the App enter background.
*For continues playing in background (like playing a playlist)*
4, Make sure the audio queue in AVQueuePlayer never become empty, otherwise your App will be suspended when it finishes playing the last asset.
5, If 5 seconds is not enough for you to get the next asset you can employ background task to delay the suspend. When the asset is ready you should call endBackgroundTask after 10 seconds.
include your playSoundFile call in the below, this should always run it in main thread
dispatch_async(dispatch_get_main_queue(), ^{
});
add this if to your - (void) playSoundFile to check if player is created, if not, then create it
if(!_player) {
_player = [[AVQueuePlayer alloc] init];
}
Seems to me as if you do not have the appropriate background mode set?
Also, looks like another user had the same issue and fixed it with an Apple Docs post. Link to Post
You may need to just do the step to set the audio session as active:
[audioSession setActive:YES error:&activationError];
Hope it helps!
If you need to start playing a sound in the background (without a trick such as keeping on playing at volume zero until you need the sound):
Set Background mode Audio in your p.list;
Set AudioSession category to AVAudioSessionCategoryPlayback
Set AudioSession to active
When your backgrounded app becomes running, start a background task before playing the sound (apparently when the remaining background time is less than 10 seconds, the sound is not played)
This now works for me.
What fixed it for me was to start playing some audio just as application quits, so I added 1sec blank audio in applicationWillResignActive in the AppDelegate
func applicationWillResignActive(_ application: UIApplication) {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask(true)
})
let secSilence = URL(fileURLWithPath: Bundle.main.path(forResource: "1secSilence", ofType: "mp3")!)
do {
audioPlayer = try AVAudioPlayer(contentsOf: secSilence)
}
catch {
print("Error in loading sound file")
}
audioPlayer.prepareToPlay()
audioPlayer.play()
}
I am developing an iphone app with phonegap and jquery mobile. When the app is closed and enters the background mode, I am trying to blur the screen so that when the app is resumed, sensitive data on the screen will be blurred out.
I am trying to do this in the on pause event, but it looks like IOS is taking a screenshot of the app before the pause event, thus my blur code does not get captured in the screen shot that IOS shows when resuming the app.
Does anyone have any ideas on how to get this to work?
The pause event is triggered by UIApplicationDidEnterBackgroundNotification, which means that apps already in the background the screen shot has been taken. Is there event before this that I can hook into?
This will be even more important in ios7 when the screen shot is shown when you double click the home button. In ios6 it is only shown for a split second while the app resumes and is loaded.
Thanks!
Code that I have tried in pause and resume phonegap listeners.
// listen for events
document.addEventListener("resume", onResume, false);
document.addEventListener("pause", onPause, false);
// show passcode if enabled, maybe even re-fresh app to start new session and clean up memory issues?
function onResume() {
// unblur page
var filterVal = 'blur(0px)';
$('.ui-page').delay(1000).css('webkitFilter', filterVal);
}
function onPause() {
var filterVal = 'blur(10px)';
$('.ui-page').css('webkitFilter', filterVal);
}
The phonegap plugin cordova-plugin-privacyscreen does the trick - it replaces the view with the splash image prior to backgrounding and clears it afterwards.
https://www.npmjs.com/package/cordova-plugin-privacyscreen
I am facing the same problem here. If your app is enabled multitasking. You can change the native code in appdelegate by adding the current code
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application Did Enter Background");
self.viewController.webView.hidden = YES;
NSString *splashImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
splashImage = #"Default-Portrait~ipad.png";
}
else {
splashImage = #"Default~iphone.png";
}
UIImageView *splash = [[UIImageView alloc]initWithFrame:[self.window frame]];
[splash setImage:[UIImage imageNamed:splashImage]];
[self.window addSubview:splash];
}
- (void) applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"Application Did Become Active");
if([[self.window subviews] count]>1) {
[NSThread sleepForTimeInterval:0.3];
[[[self.window subviews] lastObject] removeFromSuperview];
}
self.viewController.webView.hidden = NO;
}
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(#"Application Did Resign Active");
self.viewController.webView.hidden = YES;
NSString *splashImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
splashImage = #"Default-Portrait~ipad.png";
}
else {
splashImage = #"Default~iphone.png";
}
UIImageView *splash = [[UIImageView alloc]initWithFrame:[self.window frame]];
[splash setImage:[UIImage imageNamed:splashImage]];
[self.window addSubview:splash];
}
However, make sure your app is support Multitasking. Otherwise this won't work due to the applicationDidEnterBackground is NOT called when the multitasking feature is turned off. And the function(applicationWillTerminate) which should be called and it is NOT called neither.