in my app a pushNotificationManager give me a pushToken equal nil... only with IOS 8.1.3.... device is ipad3. With other device (or with other IOS version) my test it's ok..
i used this code to call getPushToken...
[[PushNotificationManager pushManager] getPushToken]
and in my appDelegate I have
-(void) settingPushwoosh:(NSDictionary *)launchOptions {
// set custom delegate for push handling, in our case - view controller
PushNotificationManager * pushManager = [PushNotificationManager pushManager];
pushManager.delegate = self;
// handling push on app start
[[PushNotificationManager pushManager] handlePushReceived:launchOptions];
// make sure we count app open in Pushwoosh stats
[[PushNotificationManager pushManager] sendAppOpen];
// register for push notifications!
[[PushNotificationManager pushManager] registerForPushNotifications];
}
Do you receive delegate callback when push token received? Sometimes you need to wait a little as this is asynchronous operation.
Related
"[GCController controllers]" does not contain any controllers that were connected prior to application launch
TLDR;
I am trying to implement gamepad input on macOS using the Game Controller Framework. When invoked in my code, [GameController controllers] always returns an empty list until new controllers are connected. It never reflects gamepads connected to macOS prior to application launch, except if you disconnect them and reconnect them while the app is running. Does anyone know what I need to do to make controllers populate with pre-launch connections?
Full question
Now that Apple has added support for Xbox and Playstation controllers to the GameController framework, I'm trying to use it for gamepad input on a C++ game engine I'm developing. I'm using the framework instead of IOKit in order to "future-proof" my games to support additional controller types in the future, as well as to simplify my own input handling code.
Like many other game engines, I've foregone using NSApplicationMain() and nib files in favor of implementing my own event loop and setting up my game window programmatically. While my "Windows style" event loop appears to be working correctly, I've discovered that [GCController controllers] does not. The array it returns is always empty at launch, and will only ever reflect controllers that are connected while the game is running. Disconnecting a pre-connected controller does not trigger my GCControllerDidDisconnectNotification callback.
Here is a simplified version of my event loop:
int main(int argc, const char * argv[])
{
#autoreleasepool
{
// Create application
[NSApplication sharedApplication];
// Set up custom app delegate
CustomAppDelegate * delegate = [[CustomAppDelegate alloc] init];
[NSApp setDelegate:delegate];
// Activate and launch app
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
[NSApp activateIgnoringOtherApps:YES]; // Strictly speaking, not necessary
[NSApp finishLaunching]; // NSMenu is set up at this point in applicationWillFinishLaunching:.
// Initialize game engine (window is created here)
GenericEngineCode_Init(); // <-- Where I want to call [GCController controllers]
NSEvent *e;
do
{
do
{
// Pump messages
e = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: nil
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (e)
{
[NSApp sendEvent: e];
[NSApp updateWindows];
}
} while (e);
} while (GenericEngineCode_Run()); // Steps the engine, returns false when quitting.
GenericEngineCode_Cleanup();
}
return 0;
}
I've confirmed that even when using [NSApp run] instead of [NSApp finishLaunching], the behavior is the same. As best as I can tell, the problem is that there's something NSApplicationMain() does that I'm not doing, but that function is a black box -- I can't identify what I need to do to get controllers to populate correctly. Does anyone know what I'm missing?
The closest thing I could find to an explanation of this problem is this answer, which suggests that my app isn't getting didBecomeActive notifications, or that at the least, the private _GCControllerManager isn't getting a CBApplicationDidBecomeActive message. I'm not a professional macOS developer, though: I don't know if this actually applies to my situation, or how I'd go about correcting the problem if it does.
After a huge amount of time searching, I found the answer on my own. It turns out that my code wasn't the problem -- the problem was that my Info.plist file was having its CFBundleIdentifier value stripped out due to a problem with my build system. It appears that the Game Controller Framework needs the bundle identifier to correctly populate [GCController controllers] at launch. While a missing CFBundleIdentifier would have been a problem anyway, as a Windows person it didn't occur to me that the identifier might be used for things besides the App Store, so I let it slide until now.
If someone else has this problem, make sure that CFBundleIdentifier isn't missing or empty in Info.plist in your assembled app bundle. In my case with Premake, I had to manually set PRODUCT_BUNDLE_IDENTIFIER with xcodebuildsettings so that $(PRODUCT_BUNDLE_IDENTIFIER) would get properly replaced in Info.plist.
I'm building a react-native app. Every time our cloud sends an APNS message, we want to increment the badge by 1.
I'm using the react-native PushNotificationIOS module to handle messages, but there's a known limitation with react-native, where the JS bridge is deactivated when the app is in the background. As a result, react-native application code that wants to manipulate the badge isn't executed, because the notification event is never delivered to the javascript.
It's important that this badge operation occur even when the app is not in the foreground, so I decided to implement the badge-incrementing logic in Objective-C, directly in AppDelegate.m. (Note: I don't understand ObjC, and can only loosely follow it.)
Here's the entirety of didReceiveRemoteNotification from AppDelegate.m:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
{
// --- BEGIN CODE I ADDED --- //
// increment badge number; must be written in ObjC b/c react-native JS bridge is deactivated when app in background
NSLog(#"remote notification LINE 1");
NSUInteger currentBadgeNum = [UIApplication sharedApplication].applicationIconBadgeNumber;
NSLog(#"currentBadgeNum = %d", currentBadgeNum);
NSUInteger newBadgeNum = currentBadgeNum + 1; // to increment by .badge: ... + [[[notification objectForKey:#"aps"] objectForKey: #"badge"] intValue]
NSLog(#"newBadgeNum = %d", newBadgeNum);
[UIApplication sharedApplication].applicationIconBadgeNumber = newBadgeNum;
NSLog(#"done updating badge; now notifying react-native");
// --- END CODE I ADDED --- //
// forward notification on to react-native code
[RCTPushNotificationManager didReceiveRemoteNotification:notification];
}
When I test it, none of the log statements appear in Xcode when the app is backgrounded. However, when the app is active, the log statements appear and the routine works exactly as expected (and the notification is delivered to the javascript code as well).
I believe I've already configured the app to receive push-notifications; here's the relevant section from my Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
What am I missing? Is there a problem with my code, or am I misunderstanding the nature of didReceiveRemoteNotification?
Any help is appreciated.
I'm using dropbox to upload files to a user's Dropbox.
On iPhone it works flawlessly, but on iPad the delegate methods for the DBRestClientDelegate are not being called.
I am still using v1.
The code I'm using is
- (DBRestClient *)restClient {
if (!_restClient) {
_restClient =
[[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
_restClient.delegate = self;
}
return _restClient;
}
[[self restClient] uploadFile:[NSString stringWithFormat:#"%#.jpeg",fileName]
toPath:#"/"
withParentRev:nil
fromPath:imagePath];
After calling this on the iPhone the delegate method
- (void)restClient:(DBRestClient*)client uploadedFile:(NSString*)destPath from:(NSString*)srcPath
is successfully called, allowing me to handle that. On the iPad, however, that, along with the other delegate methods, are not called, does not upload the file and does not throw any errors.
Any thoughts are appreciated.
Thanks,
Luke
There are a few things that might cause your delegate methods to not be called:
Your rest client is nil or is being released (e.g., by ARC) prematurely.
You're making the call in a background thread that doesn't have a run loop.
Your delegate method that should be called back has a typo in it. Unfortunately the SDK doesn't warn you if it can't find a delegate method to call; it just completes without telling anyone.
I got the pop up, i have accepted, i see it in the notifications and it is turned on but this code always returns no and i cant seem to find out why
UIApplication *application = [UIApplication sharedApplication];
BOOL enabled;
// Try to use the newer isRegisteredForRemoteNotifications otherwise use the enabledRemoteNotificationTypes.
if ([application respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
enabled = [application isRegisteredForRemoteNotifications];
}
else
{
UIRemoteNotificationType types = [application enabledRemoteNotificationTypes];
enabled = types & UIRemoteNotificationTypeAlert;
}
if (enabled){
NSLog(#"is registered");
}else{
NSLog(#"is not registered");
}
I think what makes it happen might be:
isRegisteredForRemoteNotifications will always return NO in
simulators.
registerForRemoteNotifications fails.
I was struggling with the same problem, this worked for me.
BOOL enabled = NO;
UIUserNotificationSettings *notificationSettings = [SharedApplication currentUserNotificationSettings];
enabled = notificationSettings.types < 4;
As per the Apple documentation isRegisteredForRemoteNotifications will return NO if registration has not occurred, has failed, or has been denied by the user. YES will be returned if the app has registered for remote notifications and has received is device token. So in answer to your question NO it will not always return no it will also return yes if a your app has registered for remote notifications and it has received it device token.
Checkout the Apple Documentation for a better description
Return Value
YES if the app is registered for remote notifications and received its device token or NO if registration has not occurred, has failed, or has been denied by the user.
Discussion
This method reflects only the successful completion of the remote registration process that begins when you call the registerForRemoteNotifications method. This method does not reflect whether remote notifications are actually available due to connectivity issues. The value returned by this method takes into account the user’s preferences for receiving remote notifications.
I think the simulator will always return no, try running your code on a real device and see if the behavior continues
After iOS10 and to make it work both on the simulator and on a real device you should use something like this:
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.alertStyle == UNAlertStyleNone)
NSLog(#“ACCESS DENIED!”);
else
NSLog(#“ACCESS GRANTED!”);
}];
If you are NOT planning to test on the simulator you may use the code below (unfortunately this will always return NO on the simulator):
[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
Do NOT use the code below if you are planning to compile for iOS 10 or iOS11 because it has been deprecated on iOS10:
[SharedApplication currentUserNotificationSettings];
In my case, [UIApplication sharedApplication].isRegisteredForRemoteNotifications is always NO when capability Background Modes > Remote notifications hasn't been turned on.
Im using MCNearbyServiceBrowser and MCNearbyServiceAdvertiser to join two peers to a MCSession. I am able to send data between them using MCSession's sendData method. All seems to be working as expected until I randomly (and not due to any event I control) receive a MCSessionStateNotConnected via the session's MCSessionDelegate didChangeState handler. Additionally, the MCSession's connectedPeers array no longer has my peers.
Two questions: Why? and How do i keep the MCSession from disconnecting?
This is a bug, which I just reported to Apple. The docs claim the didReceiveCertificate callback is optional, but it's not. Add this method to your MCSessionDelegate:
- (void) session:(MCSession *)session didReceiveCertificate:(NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler
{
certificateHandler(YES);
}
The random disconnects should cease.
UPDATE After using a support ticket to Apple, they confirmed that calling sendData too often and with too much data can cause disconnects.
I have had disconnects when hitting break points and when backgrounding. Since the break points won't happen on the app store, you need to handle the backgrounding case by beginning a background task when your app is about to enter the background. Then end this task when your app comes back to the foreground. On iOS 7 this gives you about 3 background minutes which is better than nothing.
An additional strategy would be to schedule a local notification for maybe 15 seconds before your background time expires by using [[UIApplication sharedApplication] backgroundTimeRemaining], that way you can bring the user back into the app before it suspends and the multi peer framework has to be shutdown. Perhaps the local notification would warn them that their session will expire in 10 seconds or something...
If the background task expires and the app is still in the background, you have to tear down everything related to multi-peer connectivity, otherwise you will get crashes.
- (void) createExpireNotification
{
[self killExpireNotification];
if (self.connectedPeerCount != 0) // if peers connected, setup kill switch
{
NSTimeInterval gracePeriod = 20.0f;
// create notification that will get the user back into the app when the background process time is about to expire
NSTimeInterval msgTime = UIApplication.sharedApplication.backgroundTimeRemaining - gracePeriod;
UILocalNotification* n = [[UILocalNotification alloc] init];
self.expireNotification = n;
self.expireNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:msgTime];
self.expireNotification.alertBody = TR(#"Text_MultiPeerIsAboutToExpire");
self.expireNotification.soundName = UILocalNotificationDefaultSoundName;
self.expireNotification.applicationIconBadgeNumber = 1;
[UIApplication.sharedApplication scheduleLocalNotification:self.expireNotification];
}
}
- (void) killExpireNotification
{
if (self.expireNotification != nil)
{
[UIApplication.sharedApplication cancelLocalNotification:self.expireNotification];
self.expireNotification = nil;
}
}
- (void) applicationWillEnterBackground
{
self.taskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^
{
[self shutdownMultiPeerStuff];
[[UIApplication sharedApplication] endBackgroundTask:self.taskId];
self.taskId = UIBackgroundTaskInvalid;
}];
[self createExpireNotification];
}
- (void) applicationWillEnterForeground
{
[self killExpireNotification];
if (self.taskId != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication] endBackgroundTask:self.taskId];
self.taskId = UIBackgroundTaskInvalid;
}
}
- (void) applicationWillTerminate
{
[self killExpireNotification];
[self stop]; // shutdown multi-peer
}
You'll also want this handler in your MCSession delegate due to Apple bug:
- (void) session:(MCSession*)session didReceiveCertificate:(NSArray*)certificate fromPeer:(MCPeerID*)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler
{
if (certificateHandler != nil) { certificateHandler(YES); }
}
There are many causes of this, and the two answers thus far are both correct in my experience. Another which you'll find in other similar questions is this: Only one peer can accept another's invitation.
So, to clarify, if you set up an app where all devices are both advertisers and browsers, any devices can freely invite any others found to join a session. However, between any two given devices, only one device can actually accept the invitation and connect to the other device. If both devices accept each others' invitations they will disconnect within a minute or less.
Note that this limitation does not prevent the desired behavior because - unlike what my intuition stated before I built my multipeer implementation - when one device accepts an invitation and connects to another device they both become connected and receive connection delegate methods and can send each other messages.
Therefore, if you are connecting devices which both browse and advertise, send invitations freely but only accept one of a pair.
The problem of only accepting one of two invitations can be solved a myriad of ways. To begin, understand that you can pass any arbitrary object or dictionary (archived as data) as the context argument in an invitation. Therefore, both devices have access to any arbitrary information about the other (and of course itself). So, you could use at least these strategies:
simply compare: the display name of the peerID. But there's no guarantee these won't be equal.
store the date your multipeer controller was initialized and use that for comparison
give each peer a UUID and send this for comparison (my technique, in which each device - indeed each user of the app on a device - has a persistent UUID it employs).
etc - any object which supports both NSCoding and compare: will do fine.
I've been having similar problems. It seems though that if I have run my app on one iOS device, and connected to another, then quit and relaunch (say when I rerun from Xcode), then I am in a situation where I get a Connected message and then a Not Connected message a little later. This was throwing me off. But looking more carefully, I can see that the Not Connected message is actually meant for a different peerId than the one that has connected.
I think the problem here is that most samples I've seen just care about the displayName of the peerID, and neglect the fact that you can get multiple peerIDs for the same device/displayName.
I am now checking the displayName first and then verifying that the peerID is the same, by doing a compare of the pointers.
- (void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
MyPlayer *player = _players[peerID.displayName];
if ((state == MCSessionStateNotConnected) &&
(peerID != player.peerID)) {
NSLog(#"remnant connection drop");
return; // note that I don't care if player is nil, since I don't want to
// add a dictionary object for a Not Connecting peer.
}
if (player == nil) {
player = [MyPlayer init];
player.peerID = peerID;
_players[peerID.displayName] = player;
}
player.state = state;
...
I was disconnecting immediately after I accepted the connection request. Observing the state, I saw it change from MCSessionStateConnected to MCSessionStateNotConnected.
I am creating my sessions with:
[[MCSession alloc] initWithPeer:peerID]
NOT the instantiation method dealing with security certificates:
- (instancetype)initWithPeer:(MCPeerID *)myPeerID securityIdentity:(NSArray *)identity encryptionPreference:(MCEncryptionPreference)encryptionPreference
Based on Andrew's tip above, I added the delegate method
- (void) session:(MCSession *)session didReceiveCertificate:(NSArray *)certificate fromPeer:(MCPeerID *)peerID certificateHandler:(void (^)(BOOL accept))certificateHandler {
certificateHandler(YES);
}
and the disconnects stopped.