requestAuthorizationToShareTypes function completion block not getting called when asking permission for health kit from apple watch - apple-watch

let healthKitTypesToRead = Set(arrayLiteral:
HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)!,
HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!,
HKObjectType.workoutType()
)
// 2. Set the types you want to write to HK Store
let healthKitTypesToWrite = Set(arrayLiteral:
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)!,
HKQuantityType.workoutType()
)
// 3. If the store is not available (for instance, iPad) return an error and don't go on.
if !HKHealthStore.isHealthDataAvailable()
{
let error = NSError(domain: "com.apple.tutorials.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit is not available in this Device"])
if( completion != nil )
{
completion(success:false, error:error)
}
return;
}
// 4. Request HealthKit authorization
healthKitStore.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes: healthKitTypesToRead) { (success, error) -> Void in
if( completion != nil )
{
completion(success:success,error:error)
}
}

The Apple Watch delegates the request to the user's iPhone, so you will need to add the following method to your iOS AppDelegate:
func applicationShouldRequestHealthAuthorization(application: UIApplication) {
let healthStore = HKHealthStore()
healthStore.handleAuthorizationForExtensionWithCompletion {(success, error) -> Void in
// Add anything you need here after authorization
}
}

Related

I can detect workout started on backgroun with apple watch, but how can I detect workout finished?

I can detect workout started on backgroun with apple watch, with below code
let workoutevent = HKObjectType.workoutType()
if store.authorizationStatus(for: workoutevent) != HKAuthorizationStatus.notDetermined {
store.enableBackgroundDelivery(for: workoutevent, frequency: .immediate, withCompletion: { (worked, error) in
print(worked)
print(error)
print("workoutevent enableBackgroundDelivery")
guard worked else {
self.logger.error("Unable to set up background delivery from HealthKit: \(error!.localizedDescription)")
print("workoutevent unable to set up background ")
fatalError()
}
if error != nil {
print("workoutevent error is ")
print(error)
}
})
backgroundObserver3 =
HKObserverQuery(sampleType: workoutevent,
predicate: nil,
updateHandler: processUpdate3(query:completionHandler3:error:))
if let queryworkout = backgroundObserver3 {
print("Starting workoutevent333 the background observer query.\(queryworkout)")
store.execute(queryworkout)
}
}else{
print("not determined....")
}
whenever I started workout on apple watch, it goes to
processUpdate3
very well,
but what I need to know is to when user finish workout.
how can I detect it ?
func processUpdate3(query: HKObserverQuery,
completionHandler3: #escaping () -> Void,
error: Error?) {
print("come here when work out started ")
...........
}
I don't see it in your code. But somewhere you must have an HKWorkoutSession. My app is set up to track running and I configure the session to begin like so;
let configuration = HKWorkoutConfiguration()
configuration.activityType = .running
do {
// HKWorkoutSession is set up here.
session = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
workoutBuilder = session.associatedWorkoutBuilder()
} catch {
// handle errors
}
When the users taps the end workout button I call session.end()
Here is a link to the documentation.

MacOS App Local Notification Not Showing when testing with XCode

I have tried to add a banner notification generator to my macOS swift app and the banner does not appear when test running in XCode and neither are there any new notifications visible in the notification centre. Other apps on my computer are generating notifications regularly. What have I missed? I have granted permission when requested
My app delegate is as follows
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
#IBOutlet weak var mainMenu: NSMenu!
func applicationDidFinishLaunching(_ aNotification: Notification)
{
NSUserNotificationCenter.default.delegate = self ;
}
func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool
{
return true
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
On app startup I run the following method and I see the console line "Notifications allowed"
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge, .provisional])
{ granted, error in
if error != nil
{
print ("Request notifications permission Error");
};
if granted
{
self.allowNotifications = true ;
print ("Notifications allowed");
}
else
{
self.allowNotifications = false ;
print ("Notifications denied");
};
}
The method I have added to my ViewController is as follows and I have tested that the print statement at the end is reached
func generateNotification (summary:String, sound:String, title:String , body:String)
{
let notification = NSUserNotification()
if !allowNotifications {return};
notification.title = summary ;
notification.subtitle = title ;
notification.informativeText = body ;
if (sound == "YES") {notification.soundName = NSUserNotificationDefaultSoundName};
NSUserNotificationCenter.default.deliver (notification);
print ("notification generated");
};
Please help me
I believe that my problem here was asking permission to use UNUserNotification and then using NSUserNotification to create the notification itself, which of course I had not requested permission to use. Requesting permission is now mandatory in Catalina (and perhaps it was in earlier versions of macOS as well.)
So I replaced the generateNotification function with the following and it all works correctly.
let notificationCenter = UNUserNotificationCenter.current();
notificationCenter.getNotificationSettings
{ (settings) in
if settings.authorizationStatus == .authorized
{
//print ("Notifications Still Allowed");
// build the banner
let content = UNMutableNotificationContent();
content.title = summary ;
content.body = title ;
if sound == "YES" {content.sound = UNNotificationSound.default};
// could add .badge
// could add .userInfo
// define when banner will appear - this is set to 1 second - note you cannot set this to zero
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false);
// Create the request
let uuidString = UUID().uuidString ;
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger);
// Schedule the request with the system.
notificationCenter.add(request, withCompletionHandler:
{ (error) in
if error != nil
{
// Something went wrong
}
})
//print ("Notification Generated");
}
In addition to the answer of Steve Brooker, for me it was working only when I set the delegate for UNUserNotificationCenter. I've spent half a day trying to make it work with NSUserNotificationCenter / NSUserNotification without any success. So thanks for your answer, Steve :)
My working version is as follows:
if #available(OSX 10.14, *) {
UNUserNotificationCenter.current().delegate = self // must have delegate, otherwise notification won't appear
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) {
[weak self] granted, error in
print("Permission granted: \(granted)")
guard granted else { return }
let sound = "NO"
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.getNotificationSettings
{ (settings) in
if settings.authorizationStatus == .authorized {
//print ("Notifications Still Allowed");
// build the banner
let content = UNMutableNotificationContent();
content.title = "summary" ;
content.body = "title" ;
if sound == "YES" {content.sound = UNNotificationSound.default};
// could add .badge
// could add .userInfo
// define when banner will appear - this is set to 1 second - note you cannot set this to zero
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false);
// Create the request
let uuidString = UUID().uuidString ;
let request = UNNotificationRequest(identifier: uuidString, content: content, trigger: trigger);
// Schedule the request with the system.
notificationCenter.add(request, withCompletionHandler:
{ (error) in
if error != nil
{
// Something went wrong
}
})
//print ("Notification Generated");
}
}
}
} else {
// Fallback on earlier versions
}

Continuous listen the user voice and detect end of speech silence in SpeechKit framework

I have working an application where we need to open certain screen based on voice command like if user says "Open Setting" then it should open the setting screen, so far that I have used the SpeechKit framework but I am not able to detect the end of speech silence. Like how Siri does it. I want to detect if the user has ended his sentence/phrase.
Please find the below code for same where I have integrate the SpeechKit framework in two ways.
A) Via closure(recognitionTask(with request: SFSpeechRecognitionRequest, resultHandler: #escaping (SFSpeechRecognitionResult?, Error?) -> Swift.Void) -> SFSpeechRecognitionTask)
let audioEngine = AVAudioEngine()
let speechRecognizer = SFSpeechRecognizer()
let request = SFSpeechAudioBufferRecognitionRequest()
var recognitionTask: SFSpeechRecognitionTask?
func startRecording() throws {
let node = audioEngine.inputNode
let recordingFormat = node.outputFormat(forBus: 0)
node.installTap(onBus: 0, bufferSize: 1024,
format: recordingFormat) { [unowned self]
(buffer, _) in
self.request.append(buffer)
}
audioEngine.prepare()
try audioEngine.start()
weak var weakSelf = self
recognitionTask = speechRecognizer?.recognitionTask(with: request) {
(result, error) in
if result != nil {
if let transcription = result?.bestTranscription {
weakSelf?.idenifyVoiceCommand(transcription)
}
}
}
}
But when I say any word/sentence like "Open Setting" then closure(recognitionTask(with:)) called multiple times and I have put the method(idenifyVoiceCommand) inside the closure which call multiple times, so how can I restrict to call only one time.
And I also review the Timer logic while googling it(SFSpeechRecognizer - detect end of utterance) but in my scenarion it does not work beacause I did not stop the audio engine as it continuously listening the user’s voice like Siri does.
B) Via delegate(SFSpeechRecognitionTaskDelegate)
speechRecognizer.recognitionTask(with: self.request, delegate: self)
func speechRecognitionTaskWasCancelled(_ task: SFSpeechRecognitionTask) {
}
func speechRecognitionTask(_ task: SFSpeechRecognitionTask, didFinishSuccessfully successfully: Bool) {
}
And I found that the delegate which handle when the end of speech occurs do not call it and accidentally call it after sometimes.
I had the same issue until now.
I checked your question and I suppose the code below helps you achieve the same thing I did:
recognitionTask = speechRecognizer?.recognitionTask(with: recognitionRequest,
resultHandler: { (result, error) in
var isFinal = false
if result != nil {
self.inputTextView.text = result?.bestTranscription.formattedString
isFinal = (result?.isFinal)!
}
if let timer = self.detectionTimer, timer.isValid {
if isFinal {
self.inputTextView.text = ""
self.textViewDidChange(self.inputTextView)
self.detectionTimer?.invalidate()
}
} else {
self.detectionTimer = Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false, block: { (timer) in
self.handleSend()
isFinal = true
timer.invalidate()
})
}
})
This checks if input wasn't received for 1.5 seconds
To your speech recogniser class add:
private var timer : Timer?
And modify code here:
recognitionTask = speechRecognizer.recognitionTask(with: request) { (result, error) in
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(withTimeInterval: 1.5, repeats:false) { _ in
self.timer = nil
//do here what do you want to do, when detect pause more than 1.5 sec
}
if result != nil {

How to open a watch app from parent iOS app?

I need to open my watch extension from my parent iOS app.
I have seen a similar feature implemented in Nike+ Run Club app. ie, When the User taps on Start button in Parent app will open the watch kit extension instantly.
As #abjurato said, you can only launch it in a "workout mode"
import HealthKit
import WatchConnectivity
let healthStore = HKHealthStore()
func startWatchApp() {
print("method called to open app ")
getActiveWCSession { (wcSession) in
print(wcSession.isComplicationEnabled, wcSession.isPaired)
if wcSession.activationState == .activated && wcSession.isWatchAppInstalled {
print("starting watch app")
self.healthStore.startWatchApp(with: self.configuration, completion: { (success, error) in
// Handle errors
})
}
else{
print("watch not active or not installed")
}
}
}
func getActiveWCSession(completion: #escaping (WCSession)->Void) {
guard WCSession.isSupported() else { return }
let wcSession = WCSession.default()
wcSession.delegate = self
if wcSession.activationState == .activated {
completion(wcSession)
} else {
wcSession.activate()
wcSessionActivationCompletion = completion
}
}

xmpp_messenger_ios Swift MUC swift

I am trying to do a MUC on iOS using xmpp_messenger_ios & XMPPFramework
Here is the code to join the room.
func createOrJoinRoomOnXMPP(){
// location has named array of lat and long
NSLog("Creating room on XMPP")
let roomJID: XMPPJID = XMPPJID.jidWithString(self.roomID + "#conference.ip-172-31-41-100")
let roomData: XMPPRoomCoreDataStorage = XMPPRoomCoreDataStorage.sharedInstance()
let chatRoom = XMPPRoom.init(roomStorage: roomData, jid: roomJID, dispatchQueue: dispatch_get_main_queue())
chatRoom.activate(OneChat.sharedInstance.xmppStream)
chatRoom.addDelegate(self, delegateQueue: dispatch_get_main_queue())
// let history = DDXMLElement.elementWithName("history")
// // Get lst messegs of the room
// history.addAttributeWithName("maxstanzas", stringValue: "10")
chatRoom.joinRoomUsingNickname(OneChat.sharedInstance.xmppStream!.myJID.user, history: nil)
}
as soon as this block executes I get an error in this code:
extension OneMessage: XMPPStreamDelegate {
public func xmppStream(sender: XMPPStream, didSendMessage message: XMPPMessage) {
if let completion = OneMessage.sharedInstance.didSendMessageCompletionBlock {
completion(stream: sender, message: message)
}
//OneMessage.sharedInstance.didSendMessageCompletionBlock!(stream: sender, message: message)
}
public func xmppStream(sender: XMPPStream, didReceiveMessage message: XMPPMessage) {
let user = OneChat.sharedInstance.xmppRosterStorage.userForJID(message.from(), xmppStream: OneChat.sharedInstance.xmppStream, managedObjectContext: OneRoster.sharedInstance.managedObjectContext_roster())
if !OneChats.knownUserForJid(jidStr: user.jidStr) { // <<< ERROR LINE
OneChats.addUserToChatList(jidStr: user.jidStr)
}
if message.isChatMessageWithBody() {
OneMessage.sharedInstance.delegate?.oneStream(sender, didReceiveMessage: message, from: user)
} else {
//was composing
if let _ = message.elementForName("composing") {
OneMessage.sharedInstance.delegate?.oneStream(sender, userIsComposing: user)
}
}
}
}
fatal error: unexpectedly found nil while unwrapping an Optional value
I have noticed that as soon as the connection is made to chat room it fetches previous messages, and thus the above code is executed.
Please help me out is doing a MUC for room chat on ios. I have searched and have not found any solution.
thanks
I solved this by this temporary solution.
extension OneMessage: XMPPStreamDelegate {
public func xmppStream(sender: XMPPStream, didSendMessage message: XMPPMessage) {
if let completion = OneMessage.sharedInstance.didSendMessageCompletionBlock {
completion(stream: sender, message: message)
}
//OneMessage.sharedInstance.didSendMessageCompletionBlock!(stream: sender, message: message)
}
public func xmppStream(sender: XMPPStream, didReceiveMessage message: XMPPMessage) {
NSLog("This is blocked")
// let user = OneChat.sharedInstance.xmppRosterStorage.userForJID(message.from(), xmppStream: OneChat.sharedInstance.xmppStream, managedObjectContext: OneRoster.sharedInstance.managedObjectContext_roster())
//
// if !OneChats.knownUserForJid(jidStr: user.jidStr) {
// OneChats.addUserToChatList(jidStr: user.jidStr)
// }
//
// if message.isChatMessageWithBody() {
// OneMessage.sharedInstance.delegate?.oneStream(sender, didReceiveMessage: message, from: user)
// } else {
// //was composing
// if let _ = message.elementForName("composing") {
// OneMessage.sharedInstance.delegate?.oneStream(sender, userIsComposing: user)
// }
// }
}
}
Blocking the OneMessage.swift code.
and handling the incoming messages in my ViewController.
This is not the right way to do it. but until ProcessOne give support for MUC this can be done.
Unwrapping that causes nil happens on:
user (return value of userForJID method is XMPPUserCoreDataStorageObject! )
jidStr (the type is String!)
Investigate which one happens to be nil.
Possible causes of user to be nil
- Nil value of jid or managedObjectContext is used in userForJID(:xmppStream:managedObjectContext)`
To find out which one is nil, simply do this:
guard let user = OneChat.sharedInstance.xmppRosterStorage.userForJID(message.from(), xmppStream: OneChat.sharedInstance.xmppStream, managedObjectContext: OneRoster.sharedInstance.managedObjectContext_roster())
else { fatalError("user is nil") }
guard let userJIDStr = user.jidStr
else { fatalError("jidStr is nil") }
I think you need to understand XMPP MUC first, read this doc.
When you send a message to MUCRoom, the serve will broadcast the message to all the members, including yourself.
And here message.from() = room.jid BUT NOT user.jid.
That's why the user you tried to get from roster is nil.