Why is that NSURLConnection's didCancelAuthenticationChallenge delegate method is never called, even after manually cancelling the Auth challenge (which in fact gets cancelled as supposed) ?
I paste some bits of the relevant code below, keep in mind that all other delegate methods are called as supposed EXCEPT for - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
Thanks for any help.
//Diego
...
NSURLConnection *serviceConnection = [NSURLConnection connectionWithRequest:serviceRequest delegate:self];
...
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
if ([challenge previousFailureCount]) {
NSLog(#"Invalid credentials... Cancelling.");
[[challenge sender] cancelAuthenticationChallenge:challenge];
// AT THIS POINT cancelAuthenticationChallenge HAS BEEN CALLED, YET, DELEGATE METHOD IS NOT CALLED.
} else {
if ([ud stringForKey:#"username"] && [ud stringForKey:#"password"]) {
NSLog(#"Service is trying to login with locally stored user and password from NSUserDefaults");
NSURLCredential *credential = [NSURLCredential credentialWithUser:[ud stringForKey:#"username"]
password:[ud stringForKey:#"password"]
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender]useCredential:credential forAuthenticationChallenge:challenge];
} else {
[delegate STServiceNeedsLoginInfo:self];
}
}
}
- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
NSLog(#"Failed login with status code: %d", [(NSHTTPURLResponse*)[challenge failureResponse]statusCode]);
// THIS METHOD IS NEVER CALLED. WHY ?
}
I believe that delegate method is provided for if the connection cancels authentication, not you. e.g. if you took too long responding to a challenge, the connection could theoretically cancel auth.
Related
System call interface active hanging call proxy method : provider(provider: CXProvider, performEndCallAction action: CXEndCallAction),but the system call interface passively hangs how to realize the system interface call end?
(void)performEndCallActionWithUUID:(NSUUID *)uuid {
if (uuid == nil) {
return;
}
CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"EndCallAction transaction request failed: %#", [error localizedDescription]);
}
else {
[endCallAction fail];
NSLog(#"EndCallAction transaction request successful");
}
}];
}
Call this function when you end the call
I am using the following CKNotification Info and this seems to work fine:
CKNotificationInfo *note = [[CKNotificationInfo alloc] init];
note.alertBody = #"Something Happened";
note.shouldBadge = NO;
note.shouldSendContentAvailable = NO;
When something changes on an iOS device, my Mac app receives a Push notification based on a subscription with this notification. However, didReceiveRemoteNotification is never called so I can't process the event. I need to be able to refresh and fetch new changes. How do I do that?
Calling registerForRemoteNotificationTypes: and implementing didRegisterForRemoteNotificationsWithDeviceToken:
should be enough code, and the App ID should include the Push Notifications service.
I'm using CloudKit in a cross-platform (iOS/OS X) app to synchronize favorites between devices like so:
// OS X specific code
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp registerForRemoteNotificationTypes:NSRemoteNotificationTypeNone];// silent push notification!
}
- (void)application:(NSApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[self.favCon handleCloudKitNotificationWithUserInfo:userInfo];
}
Note the usage of NSRemoteNotificationTypeNone which means silent push notification! This is how I set up CloudKit in the FavController class:
- (void)getOrCreateFavZoneWithCompletionHandler:(successCompletionHandler)handler {
// check if FavZone exists op
__block int createZone = 0;
CKFetchRecordZonesOperation *fetchRecZonesOp = [[CKFetchRecordZonesOperation alloc] initWithRecordZoneIDs:#[[FavController favRecordZoneID]]];
CKModifyRecordZonesOperation *saveRecZoneOp = [[CKModifyRecordZonesOperation alloc] initWithRecordZonesToSave:nil recordZoneIDsToDelete:nil];
fetchRecZonesOp.fetchRecordZonesCompletionBlock = ^(NSDictionary *recordZonesByZoneID, NSError *operationError) {
if (recordZonesByZoneID.count == 0) {// zone doesn't exist
createZone = 1;
CKRecordZone *favZone = [[CKRecordZone alloc] initWithZoneName:UTXAFavZoneName];
saveRecZoneOp.recordZonesToSave = #[favZone];
NSLog(#"Creating new Zone %#", favZone.zoneID.zoneName);
} else {
NSLog(#"Zone %# already exists.", [FavController favRecordZoneID].zoneName);
}
};
// create FavZone op
saveRecZoneOp.modifyRecordZonesCompletionBlock = ^(NSArray *savedRecordZones, NSArray *deletedRecordZoneIDs, NSError *operationError) {
[self successCompletionHandler:(savedRecordZones.count == createZone) error:operationError informDelegate:YES handler:handler];
};
[saveRecZoneOp addDependency:fetchRecZonesOp];
[[FavController favDatabase] addOperation:fetchRecZonesOp];
[[FavController favDatabase] addOperation:saveRecZoneOp];
}
- (void)subscribeToFavChanges:(successCompletionHandler)handler {
// get current subscription
[[FavController favDatabase] fetchSubscriptionWithID:UTXAFavConCKSubscriptionID completionHandler:^(CKSubscription *subscription, NSError *error) {
if (subscription) {
NSLog(#"using existing subscription: %#", subscription);
[self successCompletionHandler:YES error:nil informDelegate:NO handler:handler];
} else {
CKSubscription *sub = [[CKSubscription alloc] initWithZoneID:[FavController favRecordZoneID]
subscriptionID:UTXAFavConCKSubscriptionID
options:0];// "You must specify 0 for this parameter. Zone subscriptions currently do not support any options."
[[FavController favDatabase] saveSubscription:sub completionHandler:^(CKSubscription *subscription, NSError *error) {
NSLog(#"created new subscription: %# %#", subscription, error);
[self successCompletionHandler:(error == nil) error:error informDelegate:YES handler:handler];
}];
}
}];
}
As soon as I add or remove a record on one device, I'll get a notification on all other device, which I handle like so in the FavController class:
/// #abstract Handle push notifications sent by iCloud.
/// #discussion App delegates call this method when they receive a push notification through didReceiveRemoteNotification.
/// Currently, only airport favorites produce a PN, it is of type CKNotificationTypeRecordZone.
/// #param userInfo The userInfo dict tied to each push notification.
- (void)handleCloudKitNotificationWithUserInfo:(NSDictionary *)userInfo {
[self recursivelyCheckForPreviousCloudKitNotifications];
}
- (void)recursivelyCheckForPreviousCloudKitNotifications {
CKFetchNotificationChangesOperation *fetchOp = [[CKFetchNotificationChangesOperation alloc] initWithPreviousServerChangeToken:_defCon.notificationChangeToken];
__weak CKFetchNotificationChangesOperation *weakOp = fetchOp;
fetchOp.notificationChangedBlock = ^(CKNotification *notification) {
[self handleNotification:notification];
};
fetchOp.fetchNotificationChangesCompletionBlock = ^( CKServerChangeToken *serverChangeToken, NSError *operationError) {
NSLog(#"new notification change token: %#", serverChangeToken);
_defCon.notificationChangeToken = serverChangeToken;
if (weakOp.moreComing) {
NSLog(#"more coming!!");
[self recursivelyCheckForPreviousCloudKitNotifications];
} else {
NSLog(#"done handling notification changes.");
}
};
[[FavController favContainer] addOperation:fetchOp];
}
- (void)handleNotification:(CKNotification *)notification {// withCompletionHandler:(successCompletionHandler)handler {
if (notification.notificationType == CKNotificationTypeRecordZone) {// make sure we handle only zone changes
CKRecordZoneNotification *noti = (CKRecordZoneNotification *)notification;
if ([noti.recordZoneID.zoneName isEqualToString:[FavController favRecordZoneID].zoneName]) {
// received an update for the fav zone
[self queuedFavUpdateFromCloud];
} else {
// received an update for an unknown zone
NSLog(#"WARNING: received an update for an unknown zone: %#", noti.recordZoneID.zoneName);
}
} else {
NSLog(#"WARNING: received unknown notification: %#", notification);
}
}
Okay I've finally figured it out. If you use a CKNotificationInfo for your alerts, didReceiveRemoteNotification will NOT be called on the Mac until and unless you set CKNotificationInfo.soundName to an empty string! This looks like a bug only in OS X (10.10 & 10.11 so far) but can be worked around by this simple change.
First time I am using coreBluetooth.
Here is my implementation is there any thing wrong in it.
#synthesize CM,activePeripheral;
- (id)init
{
if ((self = [super init]))
{
CM = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (int) scanForPeripherals
{
if (self.CM.state != CBCentralManagerStatePoweredOn)
{
NSLog(#"CoreBluetooth is %s",[self centralManagerStateToString:self.CM.state]);
return -1;
}
[self.CM scanForPeripheralsWithServices:[NSArray arrayWithObject:[CBUUID UUIDWithString:#"180D"]] options:#{CBCentralManagerScanOptionAllowDuplicatesKey: #YES}];
return 0;
}
- (const char *) centralManagerStateToString: (int)state
{
switch(state)
{
case CBCentralManagerStateUnknown:
return "State unknown (CBCentralManagerStateUnknown)";
case CBCentralManagerStateResetting:
return "State resetting (CBCentralManagerStateUnknown)";
case CBCentralManagerStateUnsupported:
return "State BLE unsupported (CBCentralManagerStateResetting)";
case CBCentralManagerStateUnauthorized:
return "State unauthorized (CBCentralManagerStateUnauthorized)";
case CBCentralManagerStatePoweredOff:
return "State BLE powered off (CBCentralManagerStatePoweredOff)";
case CBCentralManagerStatePoweredOn:
return "State powered up and ready (CBCentralManagerStatePoweredOn)";
default:
return "State unknown";
}
return "Unknown state";
}
- (void) connectPeripheral:(CBPeripheral *)peripheral {
printf("Connecting to peripheral with UUID : %s\r\n",[self UUIDToString:peripheral.UUID]);
self.activePeripheral = peripheral;
self.activePeripheral.delegate = self;
[self.CM connectPeripheral:self.activePeripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
}
-(const char *) UUIDToString:(CFUUIDRef)UUID
{
if (!UUID)
return "NULL";
CFStringRef s = CFUUIDCreateString(NULL, UUID);
return CFStringGetCStringPtr(s, 0);
}
#pragma mark -Central manager delegate method
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(#"hits it");
if (central.state != CBCentralManagerStatePoweredOn) {
return;
}
isOn=YES;
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(#"Received periferal :%#",peripheral);
NSLog(#"Ad data :%#",advertisementData);
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(#"Connected peripheral %#",peripheral);
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(#"Error occured :%#",[error localizedDescription]);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)scanDevices:(id)sender {
if (isOn) {
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
[self scanForPeripherals];
}
}
Every time it gives log
CoreBluetooth is State unknown (CBCentralManagerStateUnknown)
update to question
After suggestion given by #Michael Dautermann , I come to know that my centralManagerDidUpdateState delegate method is not hitting.I dunno why this is happening.If anybody finds the reason please notify me.
Thanks you.
CoreBluetooth is simply in the process of starting up.
You need this delegate method to hit first:
- (void) centralManagerDidUpdateState: (CBCentralManager *) central;
And once it does, then you can start scanning for peripherals.
After few days, I checked out the initialization of CentralManager and I come to know that init method on firstViewController never called.I checked out by printing log in init method.
- (id)init
{
NSLog(#"hello init");
if ((self = [super init]))
{
CM = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
}
return self;
}
- (void)viewDidLoad
{ CM = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
isOn=NO;
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
So , I initialize the central manager in viewDidLoad and now it is initialized and centralManagerDidUpdateState finally called.Now printing CBCentralManagerStateUnsupported and that's not the issue because iPhone 4 doesn't support it.Thanks to michael help me a lot.
Where do you call scanDevices? I think you're calling it too early. You should call it inside centralManagerDidUpdateState.
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
NSLog(#"hits it");
if (central.state != CBCentralManagerStatePoweredOn) {
[self scanDevices]:
}
isOn=YES;
}
I have 2 SOAP services that I want to call from an IPad app.
One is used to Log the user in (SecurityASMX), the other is one that returns the current username (SecuredCalls) once logged in.
I can call the SecurityASMX no problem using the following code. The Async call callback is operation :
- (IBAction) OnButtonClick:(id) sender {
bindingSecurity = [[SecurityASMXSvc SecurityASMXSoapBinding] initWithAddress:#"http://myserver/Azur.IPADTest.Web.Services/public/Security.asmx"];
bindingSecurity.logXMLInOut = YES;
SecurityASMXSvc_Login *requestLogin = [[SecurityASMXSvc_Login alloc] init];
requestLogin.strUsername = #"test";
requestLogin.strPassword = #"testpass";
[bindingSecurity LoginAsyncUsingParameters:requestLogin delegate:self];
[requestLogin release];
self.label.text = #"Login in progress";
}
- (void) operation:(SecurityASMXSoapBindingOperation *)operation completedWithResponse:(SecurityASMXSoapBindingResponse *)response
{
[NSThread sleepForTimeInterval:2.0];
self.label.text = #"Login Done!";
}
This works fine.
However, in the same code file, I have a binding to my second web service to return the username with the following code. The async call callback is operationSecure :
- (IBAction) OnButtonSecureCallClick:(id) sender {
bindingSecuredCalls = [[SecureCallsSvc SecureCallsSoapBinding] initWithAddress:#"http://myserver/Azur.IPADTest.Web.Services/private/SecureCalls.asmx"];
bindingSecuredCalls.logXMLInOut = YES;
SecureCallsSvc_ReturnUserName *requestReturnUserName = [[SecureCallsSvc_ReturnUserName alloc] init];
[bindingSecuredCalls ReturnUserNameAsyncUsingParameters:requestReturnUserName delegate:self];
[requestReturnUserName release];
self.label.text = #"Get UserName In Progress";
}
- (void) operationSecure:(SecureCallsSoapBindingOperation *)operation completedWithResponse:(SecureCallsSoapBindingResponse *)response
{
[NSThread sleepForTimeInterval:2.0];
self.label.text = #"Get Username Done!";
}
The problem is that when the call to ReturnUserName returns, the method that gets called is the one for the login (operation) and not the one I want (operationSecure).
How can I tell my second webservice binding to call the second callback?
Thanks!
First thing would be to check if the API you're using (I assume it's a third party API) allows you to specify the callback method.
If not, you can work with the operation parameter and use isKindOfClass to see what is actually being passed.
- (void) operation:(SecurityASMXSoapBindingOperation *)operation completedWithResponse:(SecurityASMXSoapBindingResponse *)response
{
[NSThread sleepForTimeInterval:2.0];
if([operation isKindOfClass:[SecurityASMXSoapBindingOperation class]])
{
self.label.text = #"Login Done!";
}
else if([operation isKindOfClass:[SecureCallsSoapBindingOperation class]])
{
self.label.text = #"Get Username Done!";
}
}
Ideally you'd set the type of operation and response parameters to be the superclass of the respective objects returned.
I have an NSDocument based app in which I want to limit the number of documents open at the same time (for a Lite version). I just want to have n documents, and if the user tries to open more than n, show a message with a link to the full app download.
I have managed to count the number of documents using NSDocumentController and, inside readFromFileWrapper, I can return FALSE. That prevents the new doc from opening, but it shows a standard error message. I don't know how to avoid that. I would like to open a new window from a nib.
Is there any way to prevent NSDocument showing the standard error message when returning FALSE from readFromFileWrapper? Or is there any other way to prevent the document from opening before readFromFileWrapper is called?
Try the init method, which is called both when creating a new document and when opening a saved document. You simply return nil if the limit has been reached. However, I have not tried this, and it might cause the same error to be displayed.
- (id)init {
if([[NSDocumentController documents] count] >= DOCUMENT_LIMIT) {
[self release];
return nil;
}
...
}
In case the same error is displayed, you could use a custom NSDocumentController. Your implementations would check the number of open documents, display the message at the limit, and call the normal implementation otherwise.
- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError {
if([[self documents] count] >= DOCUMENT_LIMIT) {
// fill outError
return nil;
}
return [super openUntitledDocumentAndDisplay:displayDocument error:outError];
}
- (id)openDocumentWithContentsOfURL:(NSURL *)absoluteURL display:(BOOL)displayDocument error:(NSError **)outError {
NSDocument *doc = [self documentForURL:absoluteURL];
if(doc) { // already open, just show it
[doc showWindows];
return doc;
}
if([[self documents] count] >= DOCUMENT_LIMIT) {
// fill outError
return nil;
}
return [super openDocumentWithContentsOfURL:absoluteURL display:displayDocument];
}