_container = [CKContainer containerWithIdentifier:#"iCloud.com.komocode.TestApp"];
_privateDatabase = [_container privateCloudDatabase];
[_privateDatabase fetchAllSubscriptionsWithCompletionHandler:^(NSArray *subscriptions, NSError *error) {
// Never called
NSLog(#"Test");
}];
This piece of code only works on my iPad, but on my iPhone 6, the completion handler never gets called. Both my iPad and iPhone 6 are logged into the same iCloud account. Any ideas?
Rebooting the phone fixed it... I guess CloudKit is not ready for production?
Related
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
I have this really simple flow, where a notification is received when the app is in killed mode and the user taps on that notification.
Logic will append two controllers to the array of current viewControllers in the rootNavigationController. So, if first view was the usual screen for my app, in this case it will be first>second>third.
If you open the app with the app-icon, you'll only see the first view.
Issue is, when you tap on the notification, third view has an async API call, which is invoked from viewDidLoad. The SVProgressHUD is used for ux purpose, which is hidden in the completion block of the API. Sometimes, the progressHUD keeps on rotating and nothing happens. It gets stuck, but If I set the breakpoint in the API call method itself, everything works fine every time.
Not able to figure out, how to debug this issue. Tried pausing the execution when it gets stuck, but no relevant thread seems to be in the list.
CODE in third VC:
- (void) fetchDataPoints
{
//[SVProgressHUD show];
[[ServerHandler sharedInstance]fetchDataFromServerWithApiUrlString:GET_ALL_DATAPOINTS methodType:#"GET" httpBodyData:nil contentType:nil otherHeaderFields:nil queryStringParams:nil withCompletionBlock:^(BOOL success, NSData *responseData, NSError *error, NSHTTPURLResponse *response,NSDictionary *responseDict)
{
[SVProgressHUD dismiss];
if (success)
{
}
}];
}
CODE: push notification
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateInactive)
{
ChildViewController *childList = ViewControllerWithSBID (#"DATASB",#"ChildScreenID");
DetailViewController *detailVC = ViewControllerWithSBID(#"DATASB", #"DetailScreenID");
NSMutableArray *viewControllers = [self.rootNavigationController.viewControllers mutableCopy];
[viewControllers addObjectsFromArray:#[childList,detailVC]];
self.rootNavigationController.viewControllers=[[NSArray alloc]initWithArray:viewControllers];
}
I have an app with multiple local notifications. When I try to clear all the delivered notifications, I call this removeAllDeliveredNotifications method. It's working fine till ios 11.1. In ios 11.2 and above, it doesn't work as expected. The notification still remains in the notification center. Could someone please help me out on this.
Thanks in advance.
It is still working for us. I just checked it on iOS 11.2.2.
I am using removeDeliveredNotificationsWithIdentifiers: inside getDeliveredNotificationsWithCompletionHandler:, calling getDeliveredNotificationsWithCompletionHandler on Main Thread.
- (void)removePendingNotificationsForObjectID:(SJFObjectID *)objectID {
__weak __typeof(self) weakSelf = self;
[self.userNotificationCenter getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> *notifications) {
__strong __typeof(weakSelf) self = weakSelf;
NSMutableArray <NSString *> *identifiersToRemove = [#[] mutableCopy];
for (UNNotification *notification in notifications) {
SJFObjectID *objectIDFromNotification = [self.notificationToObjectIDMarshaller marshalNotification:notification];
if ([objectID isEqual:objectIDFromNotification]) {
[identifiersToRemove addObject:notification.request.identifier];
}
}
[self.userNotificationCenter removeDeliveredNotificationsWithIdentifiers:identifiersToRemove];
}];
}
Though I experience strange behavior if I am debugging the completionHandler. If a pause too long (whatever that means) the completion handler will not finish (even on continue process execution) resulting in an unresponsive app. Maybe the completionhandler gets terminated.
This appears to be a bug in iOS that was fixed in iOS 11.3.
Did you try using this method:
removeDeliveredNotifications(withIdentifiers:)
You will need to pass an array of all the notification identifiers that you need to delete.
I have added an observer for the interrupt notification when recording audio.
This works fine when performing an outgoing-call, getting an incoming call and not answering, Siri, etc..
Now my app is running in the background with the red bar at the top of the screen, and continuing the recording in the states described above is not a problem.
But when I actually answer an incoming-call. I get another AVAudioSessionInterruptionTypeBegan notification and then when I stop the call, I never get a notification AVAudioSessionInterruptionTypeEnded type.
I have tried using the CTCallCenter to detect when a call has started, but I am unable to restart the recording from that callback.
Does anyone know how to get the interrupt mechanism to work with an incoming call that is actually getting answered?
This is (part of) the code I am using;
CFNotificationCenterAddObserver(
CFNotificationCenterGetLocalCenter(),
this,
&handle_interrupt,
(__bridge CFStringRef) AVAudioSessionInterruptionNotification,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately );
...
static void handle_interrupt( CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo )
{
au_recorder *recorder = static_cast<au_recorder*>( observer );
NSNumber* interruptionType = [( ( __bridge NSDictionary* ) userInfo ) objectForKey:AVAudioSessionInterruptionTypeKey];
switch ( [interruptionType unsignedIntegerValue] )
{
case AVAudioSessionInterruptionTypeBegan:
{
// pause recorder without stopping recording (already done by OS)
recorder->pause();
break;
}
case AVAudioSessionInterruptionTypeEnded:
{
NSNumber* interruptionOption = [( ( __bridge NSDictionary* ) userInfo ) objectForKey:AVAudioSessionInterruptionOptionKey];
if ( interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume )
{
recorder->resume();
}
break;
}
}
}
I have tried binding the notification to either the AppDelegate, a UIViewController and a separate class, but that doesn't appear to help.
Edit
This is what I tried using the CTCallCenter, but this is very flaky. When recorder->resume() is called from the callback, it either works, crashes violently or doesn't do anything at all until the app is put back in the foreground again manually.
callCenter = [[CTCallCenter alloc] init];
callCenter.callEventHandler = ^(CTCall *call)
{
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
recorder->resume();
}
};
UPDATE
If you hackily wait for a second or so, you can restart the recording from your callEventHandler (although you haven't described your violent crashes, it works fine for me):
if ([call.callState isEqualToString:CTCallStateDisconnected])
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.recorder->resume();
});
}
This is all without a background task or changing to an exclusive audio session. The delay works around the fact that the call ending notification comes in before Phone.app deactivates its audio session & 1 second seems to be small enough to fall into some kind of background descheduling grace period. I've lost count of how many implementation details are assumed in this, so maybe you'd like to read on to the
Solution that seems to be backed by documentation:
N.B While there's a link to "suggestive" documentation for this solution, it was mostly guided by these two errors popping up and their accompanying comments in the header files:
AVAudioSessionErrorCodeCannotInterruptOthers
The app's audio session is non-mixable and trying to go active while in the background.
This is allowed only when the app is the NowPlaying app.
AVAudioSessionErrorInsufficientPriority
The app was not allowed to set the audio category because another app (Phone, etc.) is controlling it.
You haven't shown how your AVAudioSession is configured, but the fact that you're not getting an AVAudioSessionInterruptionTypeEnded notification suggests you're not using an exclusive audio session (i.e. you're setting "mix with others"):
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
The obsoleted CTCallCenter and newer CXCallObserver callbacks seem to happen too early, before the interruption ends, and maybe with some further work you could find a way to run a background task that restarts the recording a little while after the call ends (my initial attempts at this failed).
So right now, the only way I know to restart the recording after receiving a phone call is:
Use an exclusive audio session (this gives you an end interruption):
if (!([session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]
&& [session setActive:YES error:&error])) {
NSLog(#"Audio session error: %#", error);
}
and integrate with "Now Playing" (not joking, this is a structural part of the solution):
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTargetWithHandler:^(MPRemoteCommandEvent *event) {
NSLog(#"PLAY");
return MPRemoteCommandHandlerStatusSuccess;
}];
[commandCenter.pauseCommand addTargetWithHandler:^(MPRemoteCommandEvent *event) {
NSLog(#"PAUSE");
return MPRemoteCommandHandlerStatusSuccess;
}];
// isn't this mutually exclusive with mwo? or maybe that's remote control keys
// not seeing anything. maybe it needs actual playback?
NSDictionary* nowPlaying = #{
MPMediaItemPropertyTitle: #"foo",
MPMediaItemPropertyArtist: #"Me",
MPMediaItemPropertyAlbumTitle: #"brown album",
};
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlaying;
Pieces of this solution were cherry picked from the Audio Guidelines for User-Controlled Playback and Recording Apps documentation.
p.s. maybe the lock screen integration or non-exclusive audio session are deal breakers for you, but there are other situations where you'll never get an end interruption, e.g. launching another exclusive app, like Music (although this may not be an issue with mixWithOthers, so maybe you should go with the code-smell delay solution.
I have been working on integrating Touch ID support into an app I am working on. It is however acting very inconsistent. One of the common issues I am seeing is on a fresh app launch it works as expected, but then on backgrounding the app, and bringing it to the foreground I am getting an error back from
evaluatePolicy:localizedReason:reply:
It does not even make a lot of sense (I never see the touchid alert)
Error Domain=com.apple.LocalAuthentication Code=-1004 "User interaction is required." UserInfo=0x171470a00 {NSLocalizedDescription=User interaction is required.}
I have tried presenting the touchid alert when the app is already running, when its just foregrounded, does not seem to matter. Its broken on every time after the initial app launch.
Anyone else running into this?
For reference, here is the code I am using:
if (_useTouchId && [LAContext class]) {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
_didPresentTouchId = YES;
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"Use your Touch ID to open *****" reply:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^ {
if (success) {
_isClosing = YES;
[self hide];
if (_successBlock) {
_successBlock();
}
}
else if (error && error.code != -2 && error.code != -3 && error.code != -1004) {
[[[UIAlertView alloc] initWithTitle:#"Error" message:#"Authentication failed, please enter your Pin" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil] show];
}
else {
if (error) {
DDLogError(#"TouchID error: %#", error.description);
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^ {
[self keyboardButtonTouched];
});
}
});
}];
}
}
Usually PIN view controllers are pushed before entering background in:
- (void)applicationDidEnterBackground:(UIApplication *)application
So app's inner information won't appear when paging through app preview images (home button double tap). I guess you are doing something similar.
The problem is that LocalAuthentication's new API requires the calling viewController to be visible.
This is why you shouldn't call your "showTouchID" function before resigning to background. Instead call "showTouchID" function when entering foreground:
- (void)applicationWillEnterForeground:(UIApplication *)application
And it should work.
Don't forget to call it also when app is first launched (in which case ..willEnterForeground will not get called).
#hetzi answer really helped me, but I have more to add on this.
Basically this error happens when your app is woken up from background and somewhere on your code you are asking for Touch ID (my case is the local authentication type, I haven't tested with the keychain type). There's no way the user can interact with Touch ID prompted while the app is running on background, hence the error message.
User interaction is required.
The reasons my app was coming from background were: Push Notifications or Apple Watch.
My fix is doing something like this on the viewDidLoad method of my initial VC:
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
[self promptForTouchID];
}
I've used != because, when your app first launches it is in the UIApplicationStateInactive state. And that state doesn't generate a Touch ID error because the prompt will appear.
I also call [self promptForTouchID] on a notification of UIApplicationWillEnterForegroundNotification, but since you know that the app will enter foreground, there's no need to check here.