I am completely new to the Objective-C language. All I need to implement is the Push Notifications bit on my app for now. I have written the Client side on XCode 6 and the server side in Java using the javapns library. Now, while the server manages to send the notification (I get the confirmation message), I receive nothing on my device, be it the app is active or running in background.
Can somebody please direct me in the right direction? Thank you!
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert |UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken{
//Store the Device Token
NSLog(#"%#", newDeviceToken);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(#"Failed to register with error: %#", error);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSLog(#"Push received: %#", userInfo);
}
Server side:
public class PushServer {
public static void main(String[] args) {
try {
BasicConfigurator.configure();
Push.alert("Message!", "***.p12", "***", false,
"92ab*************91af4");
} catch (CommunicationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeystoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
This is the output I receive when I try to send the notification:
0 [main] DEBUG javapns.notification.Payload - Adding alert [Message!]
210 [main] DEBUG javapns.communication.ConnectionToAppleServer - Creating SSLSocketFactory
229 [main] DEBUG javapns.communication.ConnectionToAppleServer - Creating SSLSocket to gateway.sandbox.push.apple.com:2195
1077 [main] DEBUG javapns.notification.PushNotificationManager - Initialized Connection to Host: [gateway.sandbox.push.apple.com] Port: [2195]: 735b478[SSL_NULL_WITH_NULL_NULL: Socket[addr=gateway.sandbox.push.apple.com/17.172.232.46,port=2195,localport=53762]]
1079 [main] DEBUG javapns.notification.PushNotificationManager - Building Raw message from deviceToken and payload
1080 [main] DEBUG javapns.notification.PushNotificationManager - Built raw message ID 1 of total length 73
1080 [main] DEBUG javapns.notification.PushNotificationManager - Attempting to send notification: {"aps":{"alert":"Message!"}}
1080 [main] DEBUG javapns.notification.PushNotificationManager - to device: 92a**********1af4
2327 [main] DEBUG javapns.notification.PushNotificationManager - Flushing
2327 [main] DEBUG javapns.notification.PushNotificationManager - At this point, the entire 73-bytes message has been streamed out successfully through the SSL connection
2327 [main] DEBUG javapns.notification.PushNotificationManager - Notification sent on first attempt
2327 [main] DEBUG javapns.notification.PushNotificationManager - Reading responses
2749 [main] DEBUG javapns.notification.PushNotificationManager - Found 0 notifications that must be re-sent
2749 [main] DEBUG javapns.notification.PushNotificationManager - No notifications remaining to be resent
2749 [main] DEBUG javapns.notification.PushNotificationManager - Closing connection
Any help is appreciated.
By chance would you happen to be building with Xcode 7 and targeting iOS 9?
If so, you are likely running into the new security default setting which requires accessing URLs securely, as described here:
Disabling ATS for an in-app browser in iOS 9?
Also, you seem to be mixing old methods with new.
registerUserNotificationSettings is an iOS 8 method that replaced registerForRemoteNotificationTypes.
So, only using the former, it looks like you're only targeting iOS 8 and later.
But then you use didReceiveRemoteNotification:, which is the old version of the method, corresponding to registerForRemoteNotificationTypes. But since you're going with the newer versions, you should be using
didReceiveRemoteNotification:fetchCompletionHandler.
You need to convert NSData to string and get actual device token by implementing following changes in your method.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *myToken = [[[[deviceToken description] stringByReplacingOccurrencesOfString: #"<" withString: #""]
stringByReplacingOccurrencesOfString: #">" withString: #""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
// *** Now user `myToken` to send notification ***
NSLog(#"%#",myToken);
}
Along with it you need to make sure you are running your application with same provisional profile (Developer/Distribution) which is used to send push notification on your server side. both should be same. I hope its clear to you.
Related
I'm trying to write a simple pair of "client app" & "XPC service". I was able to launch xpc service from client (i.e I can see service running in the Activity monitor processes list), but when I try to send any request, that has a response block, I get an error: "Couldn’t communicate with a helper application."
The worst thing here is that error doesn't give me any info about what went wrong. And I'm also unable to debug the service properly. As I understand, the correct way to do this is to attach a debugger to process (Debug->Attach to process, also see here). I have both client and service projects in a single workspace.
When I run client from xcode and try to attach debugger to launched service, that ends with a "Could not attach to pid : X" error.
If I archive the client app run it from app file and then try to attach debugger to service the result is the same.
The only way to record something from the service I could imagine is to write a logger class, that would write data to some file. Haven't tried this approach yet, however that looks insane to me.
So my question is:
a) How to find out what went wrong, when receiving such non-informative response like: "Couldn’t communicate with a helper application"?
b) And also, what's the correct way to debug the xpc service in the first place? The link above is 5 years old from now, however I can see that some people were saying that "attach to debugger" wasn't working.
The code itself is fairly simple:
XPC service, listener implementation:
#import "ProcessorListener.h"
#implementation ProcessorListener
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
[newConnection setExportedInterface: [NSXPCInterface interfaceWithProtocol:#protocol(TestServiceProtocol)]];
[newConnection setExportedObject: self];
self.xpcConnection = newConnection;
newConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol: #protocol(Progress)];
// connections start suspended by default, so resume and start receiving them
[newConnection resume];
return YES;
}
- (void) sendMessageWithResponse:(NSString *)receivedString reply:(void (^)(NSString *))reply
{
reply = #"This is a response";
}
- (void) sendMessageWithNoResponse:(NSString *)mString
{
// no response here, dummy method
NSLog(#"%#", mString);
}
And the main file for service:
#import <Foundation/Foundation.h>
#import "TestService.h"
#interface ServiceDelegate : NSObject <NSXPCListenerDelegate>
#end
#implementation ServiceDelegate
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
// This method is where the NSXPCListener configures, accepts, and resumes a new incoming NSXPCConnection.
// Configure the connection.
// First, set the interface that the exported object implements.
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(TestServiceProtocol)];
// Next, set the object that the connection exports. All messages sent on the connection to this service will be sent to the exported object to handle. The connection retains the exported object.
TestService *exportedObject = [TestService new];
newConnection.exportedObject = exportedObject;
// Resuming the connection allows the system to deliver more incoming messages.
[newConnection resume];
// Returning YES from this method tells the system that you have accepted this connection. If you want to reject the connection for some reason, call -invalidate on the connection and return NO.
return YES;
}
#end
int main(int argc, const char *argv[])
{
// [NSThread sleepForTimeInterval:10.0];
// Create the delegate for the service.
ServiceDelegate *delegate = [ServiceDelegate new];
// Set up the one NSXPCListener for this service. It will handle all incoming connections.
NSXPCListener *listener = [NSXPCListener serviceListener];
listener.delegate = delegate;
// Resuming the serviceListener starts this service. This method does not return.
[listener resume];
return 0;
}
For client app, the UI contains a bunch of buttons:
- (IBAction)buttonSendMessageTap:(id)sender {
if ([daemonController running])
{
[self executeRemoteProcessWithName:#"NoResponse"];
}
else
{
[[self.labelMessageResult cell] setTitle: #"Error"];
}
}
- (IBAction)buttonSendMessage2:(id)sender {
if ([daemonController running])
{
[self executeRemoteProcessWithName:#"WithResponse"];
}
else
{
[[self.labelMessageResult cell] setTitle: #"Error"];
}
}
- (void) executeRemoteProcessWithName: (NSString*) processName
{
// Create connection
NSXPCInterface * myCookieInterface = [NSXPCInterface interfaceWithProtocol: #protocol(Processor)];
NSXPCConnection * connection = [[NSXPCConnection alloc] initWithServiceName: #"bunldeID"]; // there's a correct bundle id there, really
[connection setRemoteObjectInterface: myCookieInterface];
connection.exportedInterface = [NSXPCInterface interfaceWithProtocol:#protocol(Progress)];
connection.exportedObject = self;
[connection resume];
// NOTE that this error handling code is not called, when debugging client, i.e connection seems to be established
id<Processor> theProcessor = [connection remoteObjectProxyWithErrorHandler:^(NSError *err)
{
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle: #"OK"];
[alert setMessageText: err.localizedDescription];
[alert setAlertStyle: NSAlertStyleWarning];
[alert performSelectorOnMainThread: #selector(runModal) withObject: nil waitUntilDone: YES];
}];
if ([processName containsString:#"NoResponse"])
{
[theProcessor sendMessageWithNoResponse:#"message"];
}
else if ([processName containsString:#"WithResponse"])
{
[theProcessor sendMessageWithResponse:#"message" reply:^(NSString* replyString)
{
[[self.labelMessageResult cell] setTitle: replyString];
}];
}
}
Jonathan Levin's XPoCe tool is helpful when you can't attach a debugger.
You can add logging NSLog() or fprintf(stderr,...) to your service and clients, specifically around the status codes. You just have to specify the path of the file to write stdout and stderr. <key>StandardErrorPath</key> <string>/tmp/mystderr.log</string>
There's a section on Debugging Daemons at this article on objc.io .
I have developed a chat application with push notification service. I have tested it on development mode and adhoc mode . Every device is generating device token successfully. But in production mode, report comes that some of the device could not be registered to APNS . I don't know the reason why it is happening. Each and every device on which i am testing, successfully registered with APNS but some of the devices couldn't be registered. Anyone have idea about it? I am stuck and could not find the reason behind it . My code for registering to APNS is-
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UIApplication sharedApplication]registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound ];
// Handle launching from a notification
UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (locationNotification) {
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
}
//some other code//
return YES;
}
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken: (NSData *)deviceToken
{
globalManager.deviceToken = [[[[deviceToken description]
stringByReplacingOccurrencesOfString: #"<" withString: #""]
stringByReplacingOccurrencesOfString: #">" withString: #""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
}
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError: (NSError *)error
{
globalManager.deviceToken=nil;
NSLog(#"fail reg");
}
As #Andrey pointed in the comments above, you should really examine error passed to you app delegate's application:didFailToRegisterForRemoteNotificationsWithError:. It will likely contain the reason for the failure.
Reasons for such failure may range from users not allowing your app to use push notifications, to network problems, to something else.
I am doing something like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSApplication *application = [aNotification object];
NSRemoteNotificationType myTypes = NSRemoteNotificationTypeBadge;
[application registerForRemoteNotificationTypes:myTypes];
}
//Successful
-(void)application:(NSApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
NSLog(#"device token: %#", deviceToken);
}
//Failure to register
-(void)application:(NSApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(#"ERROR: %#\nError object: %#", [error localizedDescription], error);
}
And I am getting the following error:
2014-06-14 20:47:13.656 RajamManagerStation[1878:303] ERROR: The operation couldn’t be completed. (OSStatus error 1.)
Error object: Error Domain=NSOSStatusErrorDomain Code=1 "The operation couldn’t be completed. (OSStatus error 1.)" (kCFHostErrorHostNotFound / kCFStreamErrorSOCKSSubDomainVersionCode / kCFStreamErrorSOCKS5BadResponseAddr / kCFStreamErrorDomainPOSIX / evtNotEnb / siInitSDTblErr / kUSBPending / dsBusError / kStatusIsError / kOTSerialSwOverRunErr / cdevResErr / EPERM: / Error code is the version of SOCKS which the server wishes to use / / POSIX errno; interpret using <sys/errno.h> / event not enabled at PostEvent / slot int dispatch table could not be initialized. / / bus error / / / Couldn't get a needed resource; alert / Operation not permitted)
I dont understand what I can do to fix it.
I was watching the WWDC apple video in 2011 relating to push notifications, and there was a slide in there that showed:
It tells me that if the error delegate callback is called that I should check my privisioning profile for entitlements.
I have done everything I possiby could to get things working but I am receiving this error still. What can I do to fix it?
UPDATE 1 - Just to affirm that I have enabled push notification services in the enabled services list.
UPDATE 2 - Affirming that push notifications is there on the provisioning profile
Switch on the Push Notifications capability for your target. In XCode, go to Target -> Capabilities, and ensure "Push Notifications" is set to ON:
(Thanks to #mateusmaso's comment for this solution!)
I am trying to write a BTLE monitoring app that receives notifications. I have built both the central app and a mock peripheral app using Core Bluetooth (on iOS 7.1). My central can connect fine to the peripheral, which advertises a service that has 2 CBCharacteristicPropertyNotify characteristics. But here is what happens once the central discovers the characteristics (removing non-essential code for clarity).
PD = CBPeripheralDelegate in central
PMD = CBPeripheralManagerDelegate in peripheral
PD callback invoked
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
for (CBCharacteristic *characteristic in service.characteristics)
{
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
}
PMD callback invoked twice for the 2 characteristics
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:FILTER_GENUINE_CHARACTERISTIC_UUID]])
{
BOOL genuineSent = [self.peripheralManager updateValue:genuineValue
forCharacteristic:self.filterGenuineCharacteristic
onSubscribedCentrals:nil];
}
else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:FILTER_LOADING_CHARACTERISTIC_UUID]])
{
BOOL loadingSent = [self.peripheralManager updateValue:loadingValue
forCharacteristic:self.filterLoadingCharacteristic
onSubscribedCentrals:nil];
}
}
Note: The first time this method is invoked the return from updateValue:forCharacteristic:onSubscribedCentrals: is YES. The second time the return is always NO.
PD callback invoked twice for the 2 characteristics
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
Note: characteristic.isNotifying is always YES.
But that¹s it, no other callback methods are invoked!
PMD callback peripheralManagerIsReadyToUpdateSubscribers: is never called for the updateValue:forCharacteristic:onSubscribedCentrals: call that returned NO
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral
PD callback peripheral:didUpdateValueForCharacteristic:error: is never called for the updateValue:forCharacteristic:onSubscribedCentrals: call that returned YES
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
I have been testing these apps on an iPad Air and iPhone 5s, both running iOS 7.1.
Any idea why the notification is never invoked?
Alright im a bit stuck on how to work this.
First ill show you the code.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSString *badge = [apsInfo objectForKey:#"badge"];
NSLog(#"Received Push Badge: %#", badge);
application.applicationIconBadgeNumber = [[apsInfo objectForKey:#"badge"] integerValue];
}
sorry for the abundance of mess, the Code button was not working.
Now my push gateway provides a number each time for how many alerts are being sent, etc, but if there are previous alerts, how would i get this code to just add +1 to the list instead of just setting the new number
APNS doesn't support increment operations for badges; each push notification generated should be setting what the current value should be. (Mainly due to the fact that push notifications aren't guaranteed to be received by a device)
So, you'll need to have a service/server somewhere keeping track of what badges should be for each of your users, unfortunately.
You should try this:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
NSString *badge = [apsInfo objectForKey:#"badge"];
NSLog(#"Received Push Badge: %#", badge);
int currentBadgeNumber = application.applicationIconBadgeNumber;
currentBadgeNumber += [[apsInfo objectForKey:#"badge"] integerValue];
application.applicationIconBadgeNumber = currentBadgeNumber;
}