I'm adding Push Notifications to my Mac app. The notifications are showing up just fine, but for some reason I can't get the sound effects to play the way I would think they should.
I'm passing in a custom sound to play, "custom.caf" in the apns payload. This works currently for our iOS app. It doesn't play anything when passed into the Mac app. Digging around in the very limited Apple documentation, the PushyMac app shows that you have to play the sound manually based on the userInfo dictionary that comes in for the push. If I play the sound manually while the app is running, I get the sound effect to play.
My problem is when the app is closed and not running. I still get push notifications as expected, but no sounds play. From what I can tell, I'm just screwed. I have other Mac apps that still manage to play sounds when they aren't running. Specifically my email app, Spark. So I know it is possible, but I can't figure out what the secret sauce is to pull it off. Please help!!
It's quite simple to implement the custom sound capability for Apple Push Notifications; the problem is that the documentation does not mention what is required to do this.
The critical component that is required is an extension — (eg. using the Today Extension). Creating a new target in your project including the extension, your custom sound(s), and the standard APN functions are all that's required. The key to getting the custom sounds working though while your application isn't running is simply having the extension (and it's included NotificationCenter.framework) present in your application.
Add New Target > Today Extension:
Today Extension > Build Phases > Copy Bundle Resources > Custom Sound:
The only specific APN functions that need to be in your main application are the following:
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSApplication.shared().
registerForRemoteNotifications(matching:
[.alert, .sound, .badge])
NSApp.dockTile.badgeLabel = nil
}
func application(_ application: NSApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
}
func application(_ application: NSApplication, didReceiveRemoteNotification
userInfo: [String : Any]) {
}
func application(_ application: NSApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("could not register for notfications: \(error)")
}
If you are getting two sounds playing at the same time it's likely that you've got code that is calling AudioServicesPlaySystemSound and can be removed as the NotificationCenter.framework seems to handle all that without adding anything additional.
Related
I built a currency converter app, which gets the live currency value through API and display it. Testing on a physical phone my app works fine & no crash.
But whenever I do API call I am getting warning in the simulator as below:
[boringssl] boringssl_metrics_log_metric_block_invoke(144) Failed to log metrics
Here how i used URLSession
func performRequest(finalUrl: String) {
if let url = URL(string: finalUrl) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) {(data, response, error) in
if error != nil {
print("error in network session \(error!)")
return
}
if let safeData = data {
parseJason(dataUrl: safeData)
}
}
task.resume()
}
}
I have tried changing some values but nothing works
OS_ACTIVITY_MODE = default
DEBUG_ACTIVITY_MODE -> Debug -> any iOS simulator SDK = default
here my doubts are:
How to eliminate this warning?
Can I just ignore this warning?
Will Apple accept my app with this warning?
I had a similar issue, ran the simulator on my actual device instead of the Xcode simulator and had no issue. Problem might be that our simulators are not able to connect to internet, I will look for a solution to that and comment if I find one but on the meantime attempt to run it on your physical device.
How to eliminate this warning?
You can't.
Can I just ignore this warning?
I don't know, can you? It depends on how your brain works. I can ignore it, and I do. It's unimportant. So there is certainly no reason not to ignore it.
Will Apple accept my app with this warning?
Apple will not reject the app because of the warning. Whether they will accept the app is another matter. No one knows what they will do.
I'm troubleshooting a location-based app launch cycle that works fine on iOS simulator, but apparently does not launch the app on an actual device.
I've set up UNNotifications for both enter and exit events. Both the simulator and the device(s) register and display these notifications.
The next thing that is supposed to happen is that the app goes through a launch process, setting the app's state in such a way that I can tell without being connected to the debugger whether the app has launched or not when I open it from the home screen.
On simulator, the didEnterRegion code gets called and I can step through the subsequent launch code using the debugger - success.
However when I take the device out (for a walk) all I get are UNNotifications, and no app launch (which I can tell from the UI in the app on the real device)
I'm sure I need to improve my testing strategy (welcome to suggestions!), but at this point I should be able to expect that the app should behave the same on the simulator and the actual device - it is not.
Why is the expected outcome happening on the simulator but not on the device?
LocationService.swift
func handleRegionEnterEvent (for region: CLRegion) {
ApplicationController.sharedInstance.didEnterRegion(region)
}
func handleRegionExitEvent (for region: CLRegion) {
ApplicationController.sharedInstance.didExitRegion(region)
}
ApplicationController.swift
func didEnterRegion (_ region: CLRegion) {
// Present Alert
delegate?.handleUNEvent(note: "ENTER: \(region.identifier)")
// Start launch cycle
}
AppDelegate.swift
extension AppDelegate: AppControllerDelegate {
func handleUNEvent(note: String?) {
// Show an alert if application is active
if UIApplication.shared.applicationState == .active {
guard let message = note else { return }
Alert().showAlert(withMessage: "Application received UN while active: \(message)", title: "UNNotification")
} else {
// Otherwise present a local notification
guard let body = note else { return }
let notificationContent = UNMutableNotificationContent()
notificationContent.body = body
notificationContent.sound = UNNotificationSound.default
notificationContent.badge = UIApplication.shared.applicationIconBadgeNumber + 1 as NSNumber
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: "location_change",
content: notificationContent,
trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Error: \(error)")
}
}
}
}
}
The simulator sadly has a lot of differences from a device both in terms of data reported (you will not get speed for example) and also as you saw call flow may be different...
Something to check is to make sure you have the descriptions for the specific location permissions you are asking for in relation to location set up. For entering regions you probably want to make sure you have "always" access.
It may also be worth enabling wireless debugging, and walking around with laptop tethered to the device to verify with the debugger the didEnterRegion is truly not called, and there is a bug in the code that is supposed to display the UI you are expecting. At the very least perhaps you could get better logging as to the status of various location callbacks.
What happened: Code that executed on simulator was indeed crashing on the device and I was not reviewing crash logs from the device. This question arose from an issue that is neither ios, or core-location specific.
It has everything to do with choosing appropriate testing strategies and using all available information at one's disposal before coming to what may turn out to be premature conclusions.
However when I take the device out (for a walk) all I get are UNNotifications, and no app launch (which I can tell from the UI in the app on the real device)
I was mistaken to think that a) User Notifications that appear when the App is not running are presented by the system (as banners) and b) the App launches in another process. In fact my own code specifically executes the code that makes those notifications appear. Face-to-palm: Copying code from tutorials to achieve quick test results without even trying to understand what the code does can lead to that I suppose.
In Xcode, plug in your device and hit command-shift-2 to open Devices & Simulators window. Select your device on the left and hit "View Logs" button to bring up your crash log.
I am developing a simple spotlight extension on macOs. My app will index some content into spotlight. When I type some keywords, spotlight will trigger app's application:continueUserActivity:restorationHandler. And the application() will send a local notification. The code looks like this:
func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: ([NSUserActivityRestoring]) -> Void) -> Bool {
let notification = NSUserNotification()
notification.title = "title"
notification.subtitle = "subtitle"
notification.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.default
.deliver(notification)
}
And I use macOs 10.14.3.
If my app does not running, and I type the keyword in spotlight, the spotlight will start my app and run the application(). And I will see the notification correctly.
However, if I type keyword again(my app is running background), the spotlight still tigger the application() method but the notification does not appeared. Even worse, I could see the notification in the notification center.
Anyone can make help? Thanks.
To be displayed separately, each notification mush have a unique identifier.
notification.identifier = "different_kind"
From the documentation (emphasis mine):
The identifier is unique to a notification. A notification delivered
with the same identifier as an existing notification replaces the
existing notification rather than causing the display of a new
notification.
There are also limitations on how quickly you can post new notifications, so be aware of that.
Finally, if your'e developing for Mojave, you should probably be looking into UNUserNotificationCenter, as NSUserNotificationCenter is deprecated.
Im actually trying to convert Apple's sample code to swift.
I created an app and an APPID for it in the devcenter. I checked the entitlement for HealthKit (the ones for IAP & GC are greyed and checked automatically).
When the provisioning profile I create for it is downloaded to Xcode and I go into Preferences in Xcode and look at my account's provisioning profiles I can see the name of the profile plus the expiration date and then there are some icons for entitlements. But the provisioning profile I created with HealthKit doesnt have any icon for it, just the 2 default ones, is this normal:
because for some reason the app crashes upon requesting authorization with this error:
2014-10-02 12:16:13.241 SwimFit[549:8824] -[__NSCFConstantString _allowAuthorizationForSharingWithEntitlements:]: unrecognized selector sent to instance 0x107dc1ce0
2014-10-02 12:16:13.251 SwimFit[549:8824] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString _allowAuthorizationForSharingWithEntitlements:]: unrecognized selector sent to instance 0x107dc1ce0'
If I try to run it on the device I get this:
I have created:
AppId for my app
Activated that AppID for HealthKit
Created Dev provisioning profile for that AppID
Activated HealthKit Capabilities in General
I see the entitlements.plist is created with com.apple.developer.healthkit = yes
The info.plist does have the healthkit value for required capabilities
The only weird thing I did this time and I used to do differently for other apps is that when I clicked on build/run a some point, I let Xcode create an AppID and I get this from devcenter...i cant upload the image but basically all my previous AppIDs are named after the app. This one because its made by xcode is named: Xcode iOS App ID com santiapps SwimFit but its bundle identifier is correct at: com.santiapps.SwimFit. And so is the dev profile: iOS Team Provisioning Profile: com.santiapps.SwimFit and its the one in my Build Settings. Originally I had SwimFit because that was the name of the app so Xcode created an automatic AppID for it with a ProvProfile for it as well. I then thought maybe I should create the appID and provprofile so I did it manually and tried calling it SwimFit2. Both give the same error.
What else could I be missing?
Here is the code:
//1. Add healthstore property
var healthStore: HKHealthStore? //error if not optionally unwrapped
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//2. Ask for permissions
if (HKHealthStore.isHealthDataAvailable() == true) {
self.healthStore = HKHealthStore() //needs to be var for it to work?
var writeDataTypes = self.dataTypesToWrite()
var readDataTypes = self.dataTypesToRead()
self.healthStore!.requestAuthorizationToShareTypes(writeDataTypes, readTypes: readDataTypes, completion: { (success: Bool, error: NSError!) -> Void in
if (!success) {
NSLog("You didn't allow HealthKit to access these read/write data types. In your app, try to handle this error gracefully when a user decides not to provide access. The error was: %#. If you're using a simulator, try it on a device.", error)
return
} else {
NSLog("success")
}
// Handle success in your app here.
self.setupHealthStoreForTabBarControllers()
})
}
return true
}
Here is a link with a screen capture: http://youtu.be/BBagkNTpfQA
I had the same crash yesterday. The problem is that you put the wrong data type (NSString) for the data types into the method.
The requestAuthorizationToShareTypes method requires you to pass a set of HKBaseType objects, in my case HKQuantityType objects.
So instead of:
[_healthStore requestAuthorizationToShareTypes:[NSSet setWithObject:HKQuantityTypeIdentifierBodyMassIndex]
readTypes:[NSSet setWithArray:inputDataTypes]
completion:
Use this one:
HKQuantityType* type = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMassIndex];
[_healthStore requestAuthorizationToShareTypes:[NSSet setWithObject:type]
readTypes:[NSSet setWithArray:inputDataTypes]
completion:
I'm trying to authenticate the local player using swift, but every time I get a false value for the .authenticated property. Here is the code I'm using, it is called by the main view controller when the app starts.
func authenticateLocalPlayer(){
var localPlayer = GKLocalPlayer()
localPlayer.authenticateHandler = {(viewController, error) -> Void in
if viewController {
self.presentViewController(viewController, animated: true, completion: nil)
}else{
println((GKLocalPlayer().authenticated))
}
}
}
It brings up the log in view just fine, but when I enter a test account login, it just returns the GKLocalPlayer().authenticatedas false. The bundle identifier in iTunes Connect and the info.plist are exactly the same, as is the version and the app name. Everything is enabled for Game Center on iTunes Connect and in Xcode, but I have a feeling it's not a coding error, it's a setup error in the app record somewhere but I can't for the life of me find where.
After further tinkering, I'm getting this error:
Error Domain=GKErrorDomain Code=15 "The requested operation could not be completed because this application is not recognized by Game Center." UserInfo=0x17006b300 {NSLocalizedDescription=The requested operation could not be completed because this application is not recognized by Game Center.}
I have no idea why this is the case, the bundle ID, name and versions all match...
Any help would be greatly appreciated.
This issue has been resolved by Apple - just call:
GKLocalPlayer.localPlayer()
Previously, the issue was that GKLocalPlayer() does not return the GKLocalPlayer singleton, but instead returns a new GKLocalPlayer instance.
If you were on the Xcode 6 BETA, you could add a C function or Objective-C method that returns the real GKLocalPlayer singleton, then use this in Swift. This is the gist of my workaround (with bad naming conventions):
In an Objective-C header:
GKLocalPlayer *getLocalPlayer(void);
In an Objective-C implementation:
GKLocalPlayer *getLocalPlayer(void) {
return [GKLocalPlayer localPlayer];
}
In your bridging header:
#import "ThatHeader.h"
Then whenever you need to access the GKLocalPlayer singleton in Swift, you can just use getLocalPlayer() instead of GKLocalPlayer(). It's probably a better idea to stick that in an method of a GKLocalPlayer category.
However, this is no longer necessary as detailed above.
Even with Xcode 6 Beta 6, on a device using iOS 8 beta 5, making GKLocalPlayer.localPlayer() available, I was still getting the error:
"NSLocalizedDescription=The requested operation could not be completed
because this application is not recognized by Game Centre"
The solution (discovered through Apple's Dev forum) was to go to "Settings" on the device, and then into "Game Centre" and enable "Sandbox" under the developer section.
You can use that, I create a simple class for iOS game center in GitHub
Easy Class Game Center Swift
https://github.com/DaRkD0G/Easy-Game-Center-Swift