2017-02-20 3 views
3

My watchos app опирается на тактильную обратную связь, работающую в фоновом режиме. Я искал и искал в Интернете, как это сделать, и только научился добавлять вещи в свой info.plistNo haptic feedback

Я не хочу использовать оповещения, чтобы предупредить моего пользователя о его громоздкости с точки зрения пользовательского интерфейса, потому что это будет сделано слишком часто.

Я читал о сеансах тренировки, но не могу заставить это работать.

Как разрешить тактическую обратную связь работать, когда запястье опущено? Простейшего способа будет достаточно, и я не самый продвинутый с быстрым, поэтому, пожалуйста, попробуйте объяснить полностью!

Пример моего кода ниже. Пожалуйста, покажите мне, где поставить его, если я хочу фоновую тактильную обратную связь для работы:

Мой таймер:

_ = Timer.scheduledTimer(timeInterval: 88, target: self, selector: "action", userInfo: nil, repeats: true) 

Мои действия:

func action() { 
    print("action") 
    WKInterfaceDevice.current().play(.success) 
    imageObject.setImageNamed("number2")  
} 

Это то, что я делаю с HKWorkout сессии но абсолютно не знаю, что происходит.

func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) { 
    switch toState { 
    case .running: 
     workoutDidStart(date) 
    case .ended: 
     workoutDidEnd(date) 
    default: 
     print("Unexpected state \(toState)") 
    } 
} 

func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) { 
    // Do nothing for now 
    print("Workout error") 
} 


func workoutDidStart(_ date : Date) { 
    if let query = createHeartRateStreamingQuery(date) { 
     self.currenQuery = query 
     healthStore.execute(query) 
    } else { 
     label.setText("cannot start") 
    } 
} 

func workoutDidEnd(_ date : Date) { 
    healthStore.stop(self.currenQuery!) 
    label.setText("---") 
    session = nil 
} 

// MARK: - Actions 
@IBAction func startBtnTapped() { 
    if (self.workoutActive) { 
     //finish the current workout 
     self.workoutActive = false 
     self.startStopButton.setTitle("Start") 
     if let workout = self.session { 
      healthStore.end(workout) 
     } 
    } else { 
     //start a new workout 
     self.workoutActive = true 
     self.startStopButton.setTitle("Stop") 
     _ = Timer.scheduledTimer(timeInterval: 5, target: self, selector: "firsts", userInfo: nil, repeats: false) 


     startWorkout() 
    } 

} 

func startWorkout() { 

    // If we have already started the workout, then do nothing. 
    if (session != nil) { 
     return 
    } 

    // Configure the workout session. 
    let workoutConfiguration = HKWorkoutConfiguration() 
    workoutConfiguration.activityType = .crossTraining 
    workoutConfiguration.locationType = .indoor 

    do { 
     session = try HKWorkoutSession(configuration: workoutConfiguration) 
     session?.delegate = self 
    } catch { 
     fatalError("Unable to create the workout session!") 
    } 

    healthStore.start(self.session!) 
} 

func createHeartRateStreamingQuery(_ workoutStartDate: Date) -> HKQuery? { 


    guard let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return nil } 
    let datePredicate = HKQuery.predicateForSamples(withStart: workoutStartDate, end: nil, options: .strictEndDate) 
    //let devicePredicate = HKQuery.predicateForObjects(from: [HKDevice.local()]) 
    let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate]) 


    let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in 
     //guard let newAnchor = newAnchor else {return} 
     //self.anchor = newAnchor 
     self.updateHeartRate(sampleObjects) 
    } 

    heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in 
     //self.anchor = newAnchor! 
     self.updateHeartRate(samples) 
    } 
    return heartRateQuery 
} 

func updateHeartRate(_ samples: [HKSample]?) { 
    guard let heartRateSamples = samples as? [HKQuantitySample] else {return} 

    DispatchQueue.main.async { 
     guard let sample = heartRateSamples.first else{return} 
     let value = sample.quantity.doubleValue(for: self.heartRateUnit) 
     self.label.setText(String(UInt16(value))) 

     // retrieve source from sample 
     let name = sample.sourceRevision.source.name 
     self.updateDeviceName(name) 
     self.animateHeart() 
    } 
} 

func updateDeviceName(_ deviceName: String) { 
    deviceLabel.setText(deviceName) 
} 

func animateHeart() { 
    self.animate(withDuration: 0.5) { 
     self.heart.setWidth(60) 
     self.heart.setHeight(90) 
    } 

    let when = DispatchTime.now() + Double(Int64(0.5 * double_t(NSEC_PER_SEC)))/Double(NSEC_PER_SEC) 

    DispatchQueue.global(qos: .default).async { 
     DispatchQueue.main.asyncAfter(deadline: when) { 
      self.animate(withDuration: 0.5, animations: { 
       self.heart.setWidth(50) 
       self.heart.setHeight(80) 
      })   } 


    } 
} 

Посмотрите, мне даже не нужны данные о частоте сердечных сокращений или что-нибудь вообще, кроме жужжащего в фоновом режиме.

ответ

4

Как addition to my previous ответа я создал образец проект, который показывает, как вызвать колебания в то время как в фоновом режиме:

Для полного примера о том, как использовать HKWorkoutSession чек my sample project on GitHub. Пример приложения будет запускать вибрацию каждые пять секунд, даже если приложение работает в фоновом режиме. HKWorkoutSession, и, таким образом, образец работает только тогда, когда приложение было подписано с профилем подготовки, содержащим право на получение HealthKit. Обязательно измените команду разработчиков на свою собственную команду для всех трех доступных целей. Xcode попытается создать необходимые профили подготовки. Если есть какие-либо проблемы с подписью или вы используете профиль подстановки подкачки, работающий в фоновом режиме, не будет работать.

+0

Большое вам спасибо за то, что нашли время, чтобы создать это и помочь мне. Наслаждайтесь своей щедростью! Однако у меня есть пара вопросов. Можно ли отключить датчик частоты сердечных сокращений при использовании приложения? И как вы заканчиваете тренировку? Спасибо еще раз за помощь. –

+0

У меня есть более важный вопрос, надеюсь, вы можете ответить. В чем смысл использования частных и файловых частных функций в этом контексте? Как остановить таймер в делегате расширения: func applicationWillResignActive(), если это частная функция? –

+0

Это всего лишь пример. Эти методы не обязательно должны быть 'private' или' fileprivate'. – naglerrr

1

Это возможно только с использованием HKWorkoutSessions. Apple App Programming Guide for watchOS четко определяет доступные фоновые режимы без HKWorkoutSession, и ни один из них не позволит вам запускать тактическую обратную связь в фоновом режиме.

Используя HKWorkoutSession, вы можете реализовать приложения, отслеживающие работу пользователей с помощью приложения. В то время как HKWorkoutSession работает ваше приложение имеет несколько привилегий:

приложение продолжает работать на протяжении всего сеанса тренировки, даже , когда пользователь опускает их запястье или взаимодействует с другим приложением. Когда пользователь поднимает их запястье, приложение снова появляется, позволяя пользователю быстро и легко проверить их текущий прогресс и производительность.

Приложение может продолжать получать данные от датчиков Apple Watch на фоне , позволяя вам постоянно обновлять приложение. Например, приложение может продолжать отслеживать частоту сердечных сокращений пользователя, , гарантируя, что самые последние данные о частоте сердечных сокращений отображаются всякий раз, когда пользователь поднимает их запястье.

Приложение может предупреждать пользователя, используя звуковую или тактильную обратную связь, в то время как работает в фоновом режиме.

Для того, чтобы использовать HKWorkoutSession и обеспечивает тактильную обратную связь, вам необходимо добавить WKBackgroundModes ключ и UIBackgroundModes к Info.plist вашего расширения WatchKit в.

<key>WKBackgroundModes</key> 
<array> 
    <string>workout-processing</string> 
</array> 
<key>UIBackgroundModes</key> 
<array> 
    <string>audio</string> 
</array> 

Есть несколько предостережений к этому:

  • Когда другой WKWorkoutSession запускается другое приложение сеанс будет завершен
  • Если вы используете ресурсы чрезмерно в то время как в фоновом режиме приложение может быть приостановлено watchOS
  • Ваше приложение может способствовать заполнению циклов активности в приложении «Активность» на парном iPhone
  • В зависимости от вашего приложения Тип ication Apple, может отклонить ваше приложение при публикации в App Store
  • HealthKit права необходимо (Вы можете добавить его в Возможности зрения в Xcode)

Для более подробного руководства о том, как реализовать HKWorkoutSession Проверьте API Reference.

Полный пример использования HKWorkoutSessionmy sample project on GitHub. Пример приложения будет запускать вибрацию каждые пять секунд, даже если приложение работает в фоновом режиме. HKWorkoutSession, и, таким образом, образец работает только тогда, когда приложение было подписано с профилем подготовки, содержащим право на получение HealthKit. Обязательно измените команду разработчиков на свою собственную команду для всех трех доступных целей. Xcode попытается создать необходимые профили подготовки. Если есть какие-либо проблемы с подписью или вы используете профиль подстановки подкачки, работающий в фоновом режиме, не будет работать.

+0

Я пробовал это и не могу заставить его работать. Возможно ли кому-то отправить мне пример с тренировкой, которая не делает ничего, кроме тактильной обратной связи в фоновом режиме? –

+0

Что вы пробовали? Можете ли вы разместить свой код? – naglerrr

+0

Спасибо, я отредактировал выше. –