watchOS Workout Apps in Xamarin
Tento článek se zabývá vylepšeními, která společnost Apple provedla pro cvičení aplikací ve watchOS 3 a o tom, jak je implementovat v Xamarinu.
Novinkou ve watchOS 3 jsou aplikace související s cvičením, které mají možnost běžet na pozadí na Apple Watch a získat přístup k datům HealthKitu. Jejich nadřazená aplikace založená na iOSu 10 má také možnost spustit aplikaci založenou na watchOS 3 bez zásahu uživatele.
Podrobnosti najdete v následujících tématech:
Informace o aplikaci Workout Apps
Uživatelé fitness a cvičebních aplikací mohou být vysoce vyhrazeni, odvozují několik hodin dne ke svým cílům zdraví a fitness. V důsledku toho očekávají responzivní a snadno použitelné aplikace, které přesně shromažďují a zobrazují data a bezproblémově se integrují se službou Apple Health.
Dobře navržená aplikace pro fitness nebo cvičení pomáhá uživatelům zobrazit jejich aktivity tak, aby dosáhli svých cílů fitness. Pomocí Apple Watch, fitness a cvičební aplikace mají okamžitý přístup k srdeční frekvenci, spálení kalorií a detekci aktivity.
Novinkou ve watchOSu 3 je spuštění na pozadí, které poskytuje aplikacím souvisejícím s cvičením možnost běžet na pozadí na Apple Watch a získat přístup k datům HealthKitu.
V tomto dokumentu se seznámíte s funkcí Spuštěno na pozadí, probereme životní cyklus aplikace cvičení a ukážeme, jak může aplikace cvičení přispívat do okruhů aktivit uživatele na Apple Watch.
O cvičeních
Jádrem každé aplikace cvičení je cvičení relace (HKWorkoutSession
), kterou uživatel může spustit a zastavit. Rozhraní API pro cvičení je snadné implementovat a poskytuje několik výhod aplikace pro cvičení, například:
- Detekce pohybu a spálení kalorií na základě typu aktivity.
- Automatický příspěvek do okruhů aktivit uživatele
- Během relace se aplikace automaticky zobrazí při každém probuzení zařízení uživatelem (buď zvednutím zápěstí nebo interakcí s Apple Watch).
O spuštěném pozadí
Jak jsme uvedli výše, s watchOS 3 může být aplikace cvičení nastavena tak, aby běžela na pozadí. Použití aplikace pro cvičení na pozadí může zpracovávat data ze senzorů Apple Watch na pozadí. Aplikace může například dál monitorovat srdeční frekvenci uživatele, i když se už na obrazovce nezobrazuje.
Spuštění na pozadí také umožňuje uživateli kdykoli během aktivní cvičení prezentovat živou zpětnou vazbu, jako je odeslání haptické výstrahy, která uživatele informuje o aktuálním průběhu.
Spuštění na pozadí navíc umožňuje aplikaci rychle aktualizovat své uživatelské rozhraní, aby uživatel získal nejnovější data, když rychle nahlédne na apple Watch.
Aby se zachoval vysoký výkon na Apple Watch, měla by aplikace kukátku používající spuštěné pozadí omezit množství práce na pozadí, aby se ušetřila baterie. Pokud aplikace používá nadměrné využití procesoru na pozadí, může se pozastavit watchOS.
Povolení spuštění na pozadí
Pokud chcete povolit spouštění na pozadí, postupujte takto:
V Průzkumník řešení poklikejte na soubor rozšíření kukátek i Telefon a otevřete ho
Info.plist
pro úpravy.Přepněte do zobrazení Zdroj :
Přidejte volaný
WKBackgroundModes
nový klíč a nastavte typ naArray
:Přidejte do pole novou položku s typem
String
a hodnotouworkout-processing
:Uložte změny souboru.
Zahájení cvičení
Existují tři hlavní kroky pro zahájení cvičení:
- Aplikace musí požádat o autorizaci pro přístup k datům ve HealthKitu.
- Vytvořte objekt Konfigurace cvičení pro typ zahájení cvičení.
- Vytvořte a zahajte cvičení pomocí nově vytvořené konfigurace cvičení.
Žádost o autorizaci
Aby aplikace získala přístup k datům HealthKitu uživatele, musí požádat a přijmout autorizaci od uživatele. V závislosti na povaze aplikace cvičení může provádět následující typy požadavků:
- Autorizace k zápisu dat:
- Cvičení
- Autorizace ke čtení dat:
- Energie spálila
- Vzdálenost
- Srdeční frekvence
Aby aplikace mohl požádat o autorizaci, musí být nakonfigurovaná pro přístup ke HealthKitu.
Postupujte následovně:
V Průzkumník řešení poklikáním
Entitlements.plist
otevřete soubor pro úpravy.Posuňte se dolů a zkontrolujte povolit HealthKit:
Uložte změny souboru.
Postupujte podle pokynů v části Explicitní ID aplikace a zřizovací profil a přidružte ID aplikace a zřizovací profil s oddíly aplikace Xamarin.iOS článku Úvod do HealthKitu , abyste aplikaci správně zřídili.
Nakonec použijte pokyny v sadě Programming Health Kit a Requesting Permission From the User sections of the Introduction to HealthKit article to request authorization to access the HealthKit datastore.
Nastavení konfigurace cvičení
Cvičení se vytváří pomocí objektu Konfigurace cvičení (HKWorkoutConfiguration
), který určuje typ cvičení (například HKWorkoutActivityType.Running
) a místo cvičení (například HKWorkoutSessionLocationType.Outdoor
):
using HealthKit;
...
// Create a workout configuration
var configuration = new HKWorkoutConfiguration () {
ActivityType = HKWorkoutActivityType.Running,
LocationType = HKWorkoutSessionLocationType.Outdoor
};
Vytvoření delegáta cvičení
Aby bylo možné zvládnout události, ke kterým může dojít během cvičení, aplikace bude muset vytvořit instanci delegáta tréninkové relace. Přidejte do projektu novou třídu a založte ji mimo HKWorkoutSessionDelegate
třídu. V příkladu venkovního běhu by to mohlo vypadat takto:
using System;
using Foundation;
using WatchKit;
using HealthKit;
namespace MonkeyWorkout.MWWatchExtension
{
public class OutdoorRunDelegate : HKWorkoutSessionDelegate
{
#region Computed Properties
public HKHealthStore HealthStore { get; private set; }
public HKWorkoutSession WorkoutSession { get; private set;}
#endregion
#region Constructors
public OutdoorRunDelegate (HKHealthStore healthStore, HKWorkoutSession workoutSession)
{
// Initialize
this.HealthStore = healthStore;
this.WorkoutSession = workoutSession;
// Attach this delegate to the session
workoutSession.Delegate = this;
}
#endregion
#region Override Methods
public override void DidFail (HKWorkoutSession workoutSession, NSError error)
{
// Handle workout session failing
RaiseFailed ();
}
public override void DidChangeToState (HKWorkoutSession workoutSession, HKWorkoutSessionState toState, HKWorkoutSessionState fromState, NSDate date)
{
// Take action based on the change in state
switch (toState) {
case HKWorkoutSessionState.NotStarted:
break;
case HKWorkoutSessionState.Paused:
RaisePaused ();
break;
case HKWorkoutSessionState.Running:
RaiseRunning ();
break;
case HKWorkoutSessionState.Ended:
RaiseEnded ();
break;
}
}
public override void DidGenerateEvent (HKWorkoutSession workoutSession, HKWorkoutEvent @event)
{
base.DidGenerateEvent (workoutSession, @event);
}
#endregion
#region Events
public delegate void OutdoorRunEventDelegate ();
public event OutdoorRunEventDelegate Failed;
internal void RaiseFailed ()
{
if (this.Failed != null) this.Failed ();
}
public event OutdoorRunEventDelegate Paused;
internal void RaisePaused ()
{
if (this.Paused != null) this.Paused ();
}
public event OutdoorRunEventDelegate Running;
internal void RaiseRunning ()
{
if (this.Running != null) this.Running ();
}
public event OutdoorRunEventDelegate Ended;
internal void RaiseEnded ()
{
if (this.Ended != null) this.Ended ();
}
#endregion
}
}
Tato třída vytvoří několik událostí, které budou vyvolány jako stav cvičení relace () aDidChangeToState
pokud cvičení selže (DidFail
).
Vytvoření cvičení
Pomocí konfigurace cvičení a delegáta cvičení vytvořeného výše vytvořte novou tréninkovou relaci a začněte ji s výchozím úložištěm HealthKit uživatele:
using HealthKit;
...
#region Computed Properties
public HKHealthStore HealthStore { get; set;} = new HKHealthStore ();
public OutdoorRunDelegate RunDelegate { get; set; }
#endregion
...
private void StartOutdoorRun ()
{
// Create a workout configuration
var configuration = new HKWorkoutConfiguration () {
ActivityType = HKWorkoutActivityType.Running,
LocationType = HKWorkoutSessionLocationType.Outdoor
};
// Create workout session
// Start workout session
NSError error = null;
var workoutSession = new HKWorkoutSession (configuration, out error);
// Successful?
if (error != null) {
// Report error to user and return
return;
}
// Create workout session delegate and wire-up events
RunDelegate = new OutdoorRunDelegate (HealthStore, workoutSession);
RunDelegate.Failed += () => {
// Handle the session failing
};
RunDelegate.Paused += () => {
// Handle the session being paused
};
RunDelegate.Running += () => {
// Handle the session running
};
RunDelegate.Ended += () => {
// Handle the session ending
};
// Start session
HealthStore.StartWorkoutSession (workoutSession);
}
Pokud aplikace spustí tuto tréninkovou relaci a uživatel se přepne zpět na ciferník, zobrazí se nad obličejem malá zelená ikona "spuštěný muž":
Pokud uživatel klepne na tuto ikonu, vrátí se zpět do aplikace.
Shromažďování a řízení dat
Po nakonfigurování a spuštění cvičení bude aplikace muset shromažďovat data o relaci (například srdeční frekvence uživatele) a řídit stav relace:
- Pozorování ukázek – Aplikace bude muset načíst informace ze sady HealthKit, na které se budou reagovat a zobrazit uživateli.
- Pozorování událostí – Aplikace bude muset reagovat na události, které generuje HealthKit nebo uživatelské rozhraní aplikace (například uživatel pozastavuje cvičení).
- Zadejte spuštěný stav – Relace byla spuštěna a aktuálně je spuštěná.
- Zadejte Pozastavený stav – Uživatel pozastavil aktuální tréninkovou relaci a může ji později restartovat. Uživatel může přepínat mezi spuštěnými a pozastavenými stavy několikrát v jedné cvičení.
- Ukončit tréninkovou relaci - Kdykoli může uživatel ukončit cvičení nebo může vypršet a ukončit samostatně, pokud to byl měřený trénink (například 2 míle běh).
Posledním krokem je uložení výsledků cvičení relace do úložiště dat HealthKit uživatele.
Pozorování ukázek HealthKitu
Aplikace bude muset otevřít dotaz objektu ukotvení pro každý z datových bodů HealthKit, o které se zajímá, jako je srdeční frekvence nebo aktivní energie spálená. Pro každý pozorovaný datový bod bude potřeba vytvořit obslužnou rutinu aktualizace, která bude zachytávat nová data při odesílání do aplikace.
Z těchto datových bodů může aplikace kumulovat celkové součty (například celkovou vzdálenost spuštění) a podle potřeby aktualizovat uživatelské rozhraní. Kromě toho může aplikace uživatele upozornit, když dosáhli konkrétního cíle nebo úspěchu, například dokončení další míle běhu.
Podívejte se na následující ukázkový kód:
private void ObserveHealthKitSamples ()
{
// Get the starting date of the required samples
var datePredicate = HKQuery.GetPredicateForSamples (WorkoutSession.StartDate, null, HKQueryOptions.StrictStartDate);
// Get data from the local device
var devices = new NSSet<HKDevice> (new HKDevice [] { HKDevice.LocalDevice });
var devicePredicate = HKQuery.GetPredicateForObjectsFromDevices (devices);
// Assemble compound predicate
var queryPredicate = NSCompoundPredicate.CreateAndPredicate (new NSPredicate [] { datePredicate, devicePredicate });
// Get ActiveEnergyBurned
var queryActiveEnergyBurned = new HKAnchoredObjectQuery (HKQuantityType.Create (HKQuantityTypeIdentifier.ActiveEnergyBurned), queryPredicate, null, HKSampleQuery.NoLimit, (query, addedObjects, deletedObjects, newAnchor, error) => {
// Valid?
if (error == null) {
// Yes, process all returned samples
foreach (HKSample sample in addedObjects) {
var quantitySample = sample as HKQuantitySample;
ActiveEnergyBurned += quantitySample.Quantity.GetDoubleValue (HKUnit.Joule);
}
// Update User Interface
...
}
});
// Start Query
HealthStore.ExecuteQuery (queryActiveEnergyBurned);
}
Vytvoří predikát pro nastavení počátečního data, které chce získat data pro použití GetPredicateForSamples
metody. Vytvoří sadu zařízení pro načtení informací HealthKitu z metody GetPredicateForObjectsFromDevices
, v tomto případě pouze místní Apple Watch (HKDevice.LocalDevice
). Dva predikáty jsou sloučeny do složeného predikátu (NSCompoundPredicate
) pomocí CreateAndPredicate
metody.
Pro požadovaný datový bod se vytvoří nový HKAnchoredObjectQuery
(v tomto případě HKQuantityTypeIdentifier.ActiveEnergyBurned
pro datový bod Active Energy Spálený), pro množství vrácenýchHKSampleQuery.NoLimit
dat () se neukládá žádný limit a obslužná rutina aktualizace je definována tak, aby zpracovávala data, která se vrací do aplikace z HealthKitu.
Obslužná rutina aktualizace bude volána pokaždé, když se do aplikace doručí nová data pro daný datový bod. Pokud se nevrátí žádná chyba, může aplikace bezpečně číst data, provádět požadované výpočty a podle potřeby aktualizovat uživatelské rozhraní.
Kód smyčky nad všemi vzorky (HKSample
) vrácené v addedObjects
poli a přetypuje je na vzorky množství (HKQuantitySample
). Pak získá dvojitou hodnotu vzorku jako joule (HKUnit.Joule
) a nashromáždí ji do běhu aktivní energie spálené pro cvičení a aktualizuje uživatelské rozhraní.
Oznámení o dosaženém cíli
Jak už bylo zmíněno výše, když uživatel dosáhne cíle v aplikaci cvičení (například dokončení první míle běhu), může uživateli poslat haptickou zpětnou vazbu prostřednictvím Taptic Engine. Aplikace by také měla v tuto chvíli aktualizovat uživatelské rozhraní, protože uživatel bude s větší pravděpodobností zvedat zápěstí, aby se zobrazila událost, která vyvolala zpětnou vazbu.
K přehrávání haptické zpětné vazby použijte následující kód:
// Play haptic feedback
WKInterfaceDevice.CurrentDevice.PlayHaptic (WKHapticType.Notification);
Sledování událostí
Události jsou časové razítka, pomocí kterého může aplikace během cvičení uživatele zvýraznit určité body. Některé události vytvoří přímo aplikace a uloží se do cvičení a healthKit automaticky vytvoří některé události.
Pokud chcete sledovat události vytvořené pomocí HealthKitu, aplikace přepíše DidGenerateEvent
metodu HKWorkoutSessionDelegate
:
using System.Collections.Generic;
...
public List<HKWorkoutEvent> WorkoutEvents { get; set; } = new List<HKWorkoutEvent> ();
...
public override void DidGenerateEvent (HKWorkoutSession workoutSession, HKWorkoutEvent @event)
{
base.DidGenerateEvent (workoutSession, @event);
// Save HealthKit generated event
WorkoutEvents.Add (@event);
// Take action based on the type of event
switch (@event.Type) {
case HKWorkoutEventType.Lap:
break;
case HKWorkoutEventType.Marker:
break;
case HKWorkoutEventType.MotionPaused:
break;
case HKWorkoutEventType.MotionResumed:
break;
case HKWorkoutEventType.Pause:
break;
case HKWorkoutEventType.Resume:
break;
}
}
Apple přidal v watchOS 3 následující nové typy událostí:
HKWorkoutEventType.Lap
- Jsou pro události, které rozdělují cvičení do stejných částí vzdálenosti. Například pro označení jednoho kola kolem stopy při běhu.HKWorkoutEventType.Marker
- Jsou pro libovolné body zájmu v rámci cvičení. Například dosažení konkrétního bodu na trase venkovního běhu.
Tyto nové typy mohou být vytvořeny aplikací a uloženy v cvičení pro pozdější použití při vytváření grafů a statistik.
Pokud chcete vytvořit událost značky, postupujte takto:
using System.Collections.Generic;
...
public float MilesRun { get; set; }
public List<HKWorkoutEvent> WorkoutEvents { get; set; } = new List<HKWorkoutEvent> ();
...
public void ReachedNextMile ()
{
// Create and save marker event
var markerEvent = HKWorkoutEvent.Create (HKWorkoutEventType.Marker, NSDate.Now);
WorkoutEvents.Add (markerEvent);
// Notify user
NotifyUserOfReachedMileGoal (++MilesRun);
}
Tento kód vytvoří novou instanci události značky (HKWorkoutEvent
) a uloží ji do soukromé kolekce událostí (které se později zapíšou do cvičení) a upozorní uživatele události prostřednictvím haptiky.
Pozastavení a obnovení cvičení
V jakémkoli okamžiku cvičení může uživatel dočasně pozastavit cvičení a obnovit ho později. Můžou například pozastavit vnitřní běh, aby provedli důležitý hovor a po dokončení hovoru mohli pokračovat ve spuštění.
Uživatelské rozhraní aplikace by mělo poskytovat způsob, jak pozastavit a obnovit cvičení (voláním do HealthKitu), aby Apple Watch mohl šetřit napájení i datový prostor, zatímco uživatel pozastavil svoji aktivitu. Aplikace by také měla ignorovat všechny nové datové body, které by mohly být přijaty, když je tréninková relace v pozastaveném stavu.
HealthKit bude reagovat na pozastavení a obnovení volání generováním událostí pozastavení a obnovení. Během pozastavení cvičení se do aplikace HealthKit neposílají žádné nové události ani data, dokud se relace neobnoví.
Pomocí následujícího kódu pozastavte a obnovte cvičení:
public HKHealthStore HealthStore { get; set;} = new HKHealthStore ();
public HKWorkoutSession WorkoutSession { get; set;}
...
public void PauseWorkout ()
{
// Pause the current workout
HealthStore.PauseWorkoutSession (WorkoutSession);
}
public void ResumeWorkout ()
{
// Pause the current workout
HealthStore.ResumeWorkoutSession (WorkoutSession);
}
Události Pause a Resume, které budou generovány z HealthKit lze zpracovat přepsáním DidGenerateEvent
metody HKWorkoutSessionDelegate
:
public override void DidGenerateEvent (HKWorkoutSession workoutSession, HKWorkoutEvent @event)
{
base.DidGenerateEvent (workoutSession, @event);
// Take action based on the type of event
switch (@event.Type) {
case HKWorkoutEventType.Pause:
break;
case HKWorkoutEventType.Resume:
break;
}
}
Události pohybu
Novinkou ve watchOS 3 jsou také události Motion Paused (HKWorkoutEventType.MotionPaused
) a Motion Resumed (HKWorkoutEventType.MotionResumed
). Tyto události automaticky vyvolá HealthKit během běhu cvičení, když uživatel začne a přestane se pohybovat.
Když aplikace obdrží událost Motion Paused, měla by zastavit shromažďování dat, dokud uživatel neobnoví pohyb a událost Motion Resumes se přijme. Aplikace by neměla pozastavit cvičení v reakci na událost Pozastavení pohybu.
Důležité
Události Motion Paused a Motion Resume jsou podporovány pouze pro typ aktivity RunningWorkout (HKWorkoutActivityType.Running
).
Tyto události lze opět zpracovat přepsáním DidGenerateEvent
metody HKWorkoutSessionDelegate
:
public override void DidGenerateEvent (HKWorkoutSession workoutSession, HKWorkoutEvent @event)
{
base.DidGenerateEvent (workoutSession, @event);
// Take action based on the type of event
switch (@event.Type) {
case HKWorkoutEventType.MotionPaused:
break;
case HKWorkoutEventType.MotionResumed:
break;
}
}
Ukončení a uložení cvičení
Když uživatel dokončí cvičení, aplikace bude muset ukončit aktuální cvičení a uložit ji do databáze HealthKit. Cvičení uložená do HealthKitu se automaticky zobrazí v seznamu aktivit cvičení.
Novinkou v iOSu 10 je seznam aktivit cvičení na i Telefon uživatele. Takže i když apple Watch není blízko, cvičení se zobrazí na telefonu.
Cvičení, která zahrnují energetické vzorky, aktualizují okruh přesunutí uživatele v aplikaci Aktivity, aby aplikace třetích stran teď mohly přispívat k denním cílům přesunutí uživatele.
K ukončení a uložení cvičení se vyžadují následující kroky:
- Za prvé, aplikace bude muset ukončit cvičení relace.
- Cvičení se uloží do HealthKitu.
- Do uloženého cvičení přidejte všechny vzorky (například energii spálenou nebo vzdálenost).
Ukončení relace
Chcete-li ukončit cvičení relace, zavolejte EndWorkoutSession
metodu HKHealthStore
předávání HKWorkoutSession
v :
public HKHealthStore HealthStore { get; private set; }
public HKWorkoutSession WorkoutSession { get; private set;}
...
public void EndOutdoorRun ()
{
// End the current workout session
HealthStore.EndWorkoutSession (WorkoutSession);
}
Tím se senzory zařízení resetují do normálního režimu. Když HealthKit dokončí ukončení cvičení, obdrží zpětné DidChangeToState
volání metody HKWorkoutSessionDelegate
:
public override void DidChangeToState (HKWorkoutSession workoutSession, HKWorkoutSessionState toState, HKWorkoutSessionState fromState, NSDate date)
{
// Take action based on the change in state
switch (toState) {
...
case HKWorkoutSessionState.Ended:
StopObservingHealthKitSamples ();
RaiseEnded ();
break;
}
}
Uložení relace
Jakmile aplikace ukončí cvičení, bude muset vytvořit cvičení (HKWorkout
) a uložit ho (spolu s událostmi) do úložiště dat HealthKit (HKHealthStore
):
public HKHealthStore HealthStore { get; private set; }
public HKWorkoutSession WorkoutSession { get; private set;}
public float MilesRun { get; set; }
public double ActiveEnergyBurned { get; set;}
public List<HKWorkoutEvent> WorkoutEvents { get; set; } = new List<HKWorkoutEvent> ();
...
private void SaveWorkoutSession ()
{
// Build required workout quantities
var energyBurned = HKQuantity.FromQuantity (HKUnit.Joule, ActiveEnergyBurned);
var distance = HKQuantity.FromQuantity (HKUnit.Mile, MilesRun);
// Create any required metadata
var metadata = new NSMutableDictionary ();
metadata.Add (new NSString ("HKMetadataKeyIndoorWorkout"), new NSString ("NO"));
// Create workout
var workout = HKWorkout.Create (HKWorkoutActivityType.Running,
WorkoutSession.StartDate,
NSDate.Now,
WorkoutEvents.ToArray (),
energyBurned,
distance,
metadata);
// Save to HealthKit
HealthStore.SaveObject (workout, (successful, error) => {
// Handle any errors
if (error == null) {
// Was the save successful
if (successful) {
}
} else {
// Report error
}
});
}
Tento kód vytvoří celkové množství energie spálené a vzdálenost pro cvičení jako HKQuantity
objekty. Vytvoří se slovník metadat definující cvičení a určí se umístění cvičení:
metadata.Add (new NSString ("HKMetadataKeyIndoorWorkout"), new NSString ("NO"));
Vytvoří se nový HKWorkout
objekt se stejným HKWorkoutActivityType
HKWorkoutSession
datem jako počáteční a koncové datum, seznam událostí (kumulovaný z výše uvedených oddílů), spotřebovaná energie, celková vzdálenost a slovník metadat. Tento objekt se uloží do úložiště stavu a všechny chyby, které se zpracovávají.
Přidání ukázek
Když aplikace uloží sadu ukázek do cvičení, HealthKit vygeneruje spojení mezi ukázkami a samotným cvičením, aby se aplikace mohl později dotazovat Na HealthKit pro všechny ukázky spojené s daným cvičením. Pomocí těchto informací může aplikace generovat grafy z dat cvičení a vykreslit je na časové ose cvičení.
Aby aplikace přispěla do pohybového okruhu aplikace aktivity, musí obsahovat energetické vzorky s uloženým cvičením. Kromě toho musí součty vzdálenosti a energie odpovídat součtu všech vzorků, které aplikace přidruží k uloženému cvičení.
Pokud chcete přidat ukázky do uloženého cvičení, postupujte takto:
using System.Collections.Generic;
using WatchKit;
using HealthKit;
...
public HKHealthStore HealthStore { get; private set; }
public List<HKSample> WorkoutSamples { get; set; } = new List<HKSample> ();
...
private void SaveWorkoutSamples (HKWorkout workout)
{
// Add samples to saved workout
HealthStore.AddSamples (WorkoutSamples.ToArray (), workout, (success, error) => {
// Handle any errors
if (error == null) {
// Was the save successful
if (success) {
}
} else {
// Report error
}
});
}
Volitelně může aplikace vypočítat a vytvořit menší podmnožinu vzorků nebo jednu mega ukázku (přes celou řadu cvičení), která se pak přidružuje k uloženému cvičení.
Cvičení a iOS 10
Každá aplikace cvičení watchOS 3 má nadřazenou aplikaci pro cvičení s iOSem 10 a s novými aplikacemi pro iOS 10 se dá tato aplikace pro iOS použít k zahájení cvičení, které umístí Apple Watch do režimu cvičení (bez zásahu uživatele) a spustí aplikaci watchOS v režimu spuštění na pozadí (další podrobnosti najdete v části O spuštění na pozadí výše).
Zatímco je aplikace watchOS spuštěná, může používat watch Připojení ivity pro zasílání zpráv a komunikaci s nadřazenou aplikací pro iOS.
Podívejte se, jak tento proces funguje:
- Aplikace i Telefon vytvoří
HKWorkoutConfiguration
objekt a nastaví typ cvičení a umístění. - Objekt
HKWorkoutConfiguration
se odešle verzi aplikace Apple Watch a pokud ještě není spuštěný, spustí ji systém. - Pomocí předané konfigurace cvičení spustí aplikace watchOS 3 novou tréninkovou relaci (
HKWorkoutSession
).
Důležité
Aby mohla aplikace pro rodiče i Telefon spustit cvičení na Apple Watch, musí mít aplikace watchOS 3 povolené spouštění na pozadí. Další podrobnosti najdete v části Povolení spuštění na pozadí výše.
Tento proces je velmi podobný procesu zahájení cvičení relace přímo v aplikaci watchOS 3. Na i Telefon použijte následující kód:
using System;
using HealthKit;
using WatchConnectivity;
...
#region Computed Properties
public HKHealthStore HealthStore { get; set; } = new HKHealthStore ();
public WCSession ConnectivitySession { get; set; } = WCSession.DefaultSession;
#endregion
...
private void StartOutdoorRun ()
{
// Can the app communicate with the watchOS version of the app?
if (ConnectivitySession.ActivationState == WCSessionActivationState.Activated && ConnectivitySession.WatchAppInstalled) {
// Create a workout configuration
var configuration = new HKWorkoutConfiguration () {
ActivityType = HKWorkoutActivityType.Running,
LocationType = HKWorkoutSessionLocationType.Outdoor
};
// Start watch app
HealthStore.StartWatchApp (configuration, (success, error) => {
// Handle any errors
if (error == null) {
// Was the save successful
if (success) {
...
}
} else {
// Report error
...
}
});
}
}
Tento kód zajistí, že se verze watchOS aplikace nainstaluje a verze i Telefon se k ní může připojit jako první:
if (ConnectivitySession.ActivationState == WCSessionActivationState.Activated && ConnectivitySession.WatchAppInstalled) {
...
}
Pak vytvoří HKWorkoutConfiguration
jako obvykle a použije StartWatchApp
metodu HKHealthStore
odeslání do Apple Watch a spustí aplikaci a cvičení relace.
A v aplikaci watch OS použijte následující kód v:WKExtensionDelegate
using WatchKit;
using HealthKit;
...
#region Computed Properties
public HKHealthStore HealthStore { get; set;} = new HKHealthStore ();
public OutdoorRunDelegate RunDelegate { get; set; }
#endregion
...
public override void HandleWorkoutConfiguration (HKWorkoutConfiguration workoutConfiguration)
{
// Create workout session
// Start workout session
NSError error = null;
var workoutSession = new HKWorkoutSession (workoutConfiguration, out error);
// Successful?
if (error != null) {
// Report error to user and return
return;
}
// Create workout session delegate and wire-up events
RunDelegate = new OutdoorRunDelegate (HealthStore, workoutSession);
RunDelegate.Failed += () => {
// Handle the session failing
};
RunDelegate.Paused += () => {
// Handle the session being paused
};
RunDelegate.Running += () => {
// Handle the session running
};
RunDelegate.Ended += () => {
// Handle the session ending
};
// Start session
HealthStore.StartWorkoutSession (workoutSession);
}
HKWorkoutConfiguration
Vezme a vytvoří novou HKWorkoutSession
a připojí instanci vlastního HKWorkoutSessionDelegate
objektu . Cvičení se spustí v obchodě HealthKit Health Store uživatele.
Spojení všech částí dohromady
Když vezmeme všechny informace uvedené v tomto dokumentu, aplikace watchOS 3 založené na cvičeních a její nadřazená aplikace pro cvičení s iOSem 10 může zahrnovat následující části:
- iOS 10
ViewController.cs
– zpracovává začátek relace Připojení ivity hodinek a cvičení na Apple Watch. - watchOS 3
ExtensionDelegate.cs
– zpracovává verzi watchOS 3 aplikace pro cvičení. - watchOS 3
OutdoorRunDelegate.cs
– vlastní řešeníHKWorkoutSessionDelegate
událostí pro cvičení.
Důležité
Kód zobrazený v následujících částech obsahuje jenom části potřebné k implementaci nových vylepšených funkcí poskytovaných aplikacím Workout v watchOS 3. Veškerý podpůrný kód a kód pro prezentaci a aktualizaci uživatelského rozhraní není zahrnutý, ale můžete ho snadno vytvořit pomocí naší další dokumentace ke watchOS.
ViewController.cs
Soubor ViewController.cs
v nadřazené verzi aplikace pro iOS 10 cvičení by obsahoval následující kód:
using System;
using HealthKit;
using UIKit;
using WatchConnectivity;
namespace MonkeyWorkout
{
public partial class ViewController : UIViewController
{
#region Computed Properties
public HKHealthStore HealthStore { get; set; } = new HKHealthStore ();
public WCSession ConnectivitySession { get; set; } = WCSession.DefaultSession;
#endregion
#region Constructors
protected ViewController (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
#endregion
#region Private Methods
private void InitializeWatchConnectivity ()
{
// Is Watch Connectivity supported?
if (!WCSession.IsSupported) {
// No, abort
return;
}
// Is the session already active?
if (ConnectivitySession.ActivationState != WCSessionActivationState.Activated) {
// No, start session
ConnectivitySession.ActivateSession ();
}
}
private void StartOutdoorRun ()
{
// Can the app communicate with the watchOS version of the app?
if (ConnectivitySession.ActivationState == WCSessionActivationState.Activated && ConnectivitySession.WatchAppInstalled) {
// Create a workout configuration
var configuration = new HKWorkoutConfiguration () {
ActivityType = HKWorkoutActivityType.Running,
LocationType = HKWorkoutSessionLocationType.Outdoor
};
// Start watch app
HealthStore.StartWatchApp (configuration, (success, error) => {
// Handle any errors
if (error == null) {
// Was the save successful
if (success) {
...
}
} else {
// Report error
...
}
});
}
}
#endregion
#region Override Methods
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Start Watch Connectivity
InitializeWatchConnectivity ();
}
#endregion
}
}
ExtensionDelegate.cs
Soubor ExtensionDelegate.cs
ve verzi aplikace watchOS 3 cvičení by obsahoval následující kód:
using System;
using Foundation;
using WatchKit;
using HealthKit;
namespace MonkeyWorkout.MWWatchExtension
{
public class ExtensionDelegate : WKExtensionDelegate
{
#region Computed Properties
public HKHealthStore HealthStore { get; set;} = new HKHealthStore ();
public OutdoorRunDelegate RunDelegate { get; set; }
#endregion
#region Constructors
public ExtensionDelegate ()
{
}
#endregion
#region Private Methods
private void StartWorkoutSession (HKWorkoutConfiguration workoutConfiguration)
{
// Create workout session
// Start workout session
NSError error = null;
var workoutSession = new HKWorkoutSession (workoutConfiguration, out error);
// Successful?
if (error != null) {
// Report error to user and return
return;
}
// Create workout session delegate and wire-up events
RunDelegate = new OutdoorRunDelegate (HealthStore, workoutSession);
RunDelegate.Failed += () => {
// Handle the session failing
...
};
RunDelegate.Paused += () => {
// Handle the session being paused
...
};
RunDelegate.Running += () => {
// Handle the session running
...
};
RunDelegate.Ended += () => {
// Handle the session ending
...
};
RunDelegate.ReachedMileGoal += (miles) => {
// Handle the reaching a session goal
...
};
RunDelegate.HealthKitSamplesUpdated += () => {
// Update UI as required
...
};
// Start session
HealthStore.StartWorkoutSession (workoutSession);
}
private void StartOutdoorRun ()
{
// Create a workout configuration
var workoutConfiguration = new HKWorkoutConfiguration () {
ActivityType = HKWorkoutActivityType.Running,
LocationType = HKWorkoutSessionLocationType.Outdoor
};
// Start the session
StartWorkoutSession (workoutConfiguration);
}
#endregion
#region Override Methods
public override void HandleWorkoutConfiguration (HKWorkoutConfiguration workoutConfiguration)
{
// Start the session
StartWorkoutSession (workoutConfiguration);
}
#endregion
}
}
OutdoorRunDelegate.cs
Soubor OutdoorRunDelegate.cs
ve verzi aplikace watchOS 3 cvičení by obsahoval následující kód:
using System;
using System.Collections.Generic;
using Foundation;
using WatchKit;
using HealthKit;
namespace MonkeyWorkout.MWWatchExtension
{
public class OutdoorRunDelegate : HKWorkoutSessionDelegate
{
#region Private Variables
private HKAnchoredObjectQuery QueryActiveEnergyBurned;
#endregion
#region Computed Properties
public HKHealthStore HealthStore { get; private set; }
public HKWorkoutSession WorkoutSession { get; private set;}
public float MilesRun { get; set; }
public double ActiveEnergyBurned { get; set;}
public List<HKWorkoutEvent> WorkoutEvents { get; set; } = new List<HKWorkoutEvent> ();
public List<HKSample> WorkoutSamples { get; set; } = new List<HKSample> ();
#endregion
#region Constructors
public OutdoorRunDelegate (HKHealthStore healthStore, HKWorkoutSession workoutSession)
{
// Initialize
this.HealthStore = healthStore;
this.WorkoutSession = workoutSession;
// Attach this delegate to the session
workoutSession.Delegate = this;
}
#endregion
#region Private Methods
private void ObserveHealthKitSamples ()
{
// Get the starting date of the required samples
var datePredicate = HKQuery.GetPredicateForSamples (WorkoutSession.StartDate, null, HKQueryOptions.StrictStartDate);
// Get data from the local device
var devices = new NSSet<HKDevice> (new HKDevice [] { HKDevice.LocalDevice });
var devicePredicate = HKQuery.GetPredicateForObjectsFromDevices (devices);
// Assemble compound predicate
var queryPredicate = NSCompoundPredicate.CreateAndPredicate (new NSPredicate [] { datePredicate, devicePredicate });
// Get ActiveEnergyBurned
QueryActiveEnergyBurned = new HKAnchoredObjectQuery (HKQuantityType.Create (HKQuantityTypeIdentifier.ActiveEnergyBurned), queryPredicate, null, HKSampleQuery.NoLimit, (query, addedObjects, deletedObjects, newAnchor, error) => {
// Valid?
if (error == null) {
// Yes, process all returned samples
foreach (HKSample sample in addedObjects) {
// Accumulate totals
var quantitySample = sample as HKQuantitySample;
ActiveEnergyBurned += quantitySample.Quantity.GetDoubleValue (HKUnit.Joule);
// Save samples
WorkoutSamples.Add (sample);
}
// Inform caller
RaiseHealthKitSamplesUpdated ();
}
});
// Start Query
HealthStore.ExecuteQuery (QueryActiveEnergyBurned);
}
private void StopObservingHealthKitSamples ()
{
// Stop query
HealthStore.StopQuery (QueryActiveEnergyBurned);
}
private void ResumeObservingHealthkitSamples ()
{
// Resume current queries
HealthStore.ExecuteQuery (QueryActiveEnergyBurned);
}
private void NotifyUserOfReachedMileGoal (float miles)
{
// Play haptic feedback
WKInterfaceDevice.CurrentDevice.PlayHaptic (WKHapticType.Notification);
// Raise event
RaiseReachedMileGoal (miles);
}
private void SaveWorkoutSession ()
{
// Build required workout quantities
var energyBurned = HKQuantity.FromQuantity (HKUnit.Joule, ActiveEnergyBurned);
var distance = HKQuantity.FromQuantity (HKUnit.Mile, MilesRun);
// Create any required metadata
var metadata = new NSMutableDictionary ();
metadata.Add (new NSString ("HKMetadataKeyIndoorWorkout"), new NSString ("NO"));
// Create workout
var workout = HKWorkout.Create (HKWorkoutActivityType.Running,
WorkoutSession.StartDate,
NSDate.Now,
WorkoutEvents.ToArray (),
energyBurned,
distance,
metadata);
// Save to HealthKit
HealthStore.SaveObject (workout, (successful, error) => {
// Handle any errors
if (error == null) {
// Was the save successful
if (successful) {
// Add samples to workout
SaveWorkoutSamples (workout);
}
} else {
// Report error
...
}
});
}
private void SaveWorkoutSamples (HKWorkout workout)
{
// Add samples to saved workout
HealthStore.AddSamples (WorkoutSamples.ToArray (), workout, (success, error) => {
// Handle any errors
if (error == null) {
// Was the save successful
if (success) {
...
}
} else {
// Report error
...
}
});
}
#endregion
#region Public Methods
public void PauseWorkout ()
{
// Pause the current workout
HealthStore.PauseWorkoutSession (WorkoutSession);
}
public void ResumeWorkout ()
{
// Pause the current workout
HealthStore.ResumeWorkoutSession (WorkoutSession);
}
public void ReachedNextMile ()
{
// Create and save marker event
var markerEvent = HKWorkoutEvent.Create (HKWorkoutEventType.Marker, NSDate.Now);
WorkoutEvents.Add (markerEvent);
// Notify user
NotifyUserOfReachedMileGoal (++MilesRun);
}
public void EndOutdoorRun ()
{
// End the current workout session
HealthStore.EndWorkoutSession (WorkoutSession);
}
#endregion
#region Override Methods
public override void DidFail (HKWorkoutSession workoutSession, NSError error)
{
// Handle workout session failing
RaiseFailed ();
}
public override void DidChangeToState (HKWorkoutSession workoutSession, HKWorkoutSessionState toState, HKWorkoutSessionState fromState, NSDate date)
{
// Take action based on the change in state
switch (toState) {
case HKWorkoutSessionState.NotStarted:
break;
case HKWorkoutSessionState.Paused:
StopObservingHealthKitSamples ();
RaisePaused ();
break;
case HKWorkoutSessionState.Running:
if (fromState == HKWorkoutSessionState.Paused) {
ResumeObservingHealthkitSamples ();
} else {
ObserveHealthKitSamples ();
}
RaiseRunning ();
break;
case HKWorkoutSessionState.Ended:
StopObservingHealthKitSamples ();
SaveWorkoutSession ();
RaiseEnded ();
break;
}
}
public override void DidGenerateEvent (HKWorkoutSession workoutSession, HKWorkoutEvent @event)
{
base.DidGenerateEvent (workoutSession, @event);
// Save HealthKit generated event
WorkoutEvents.Add (@event);
// Take action based on the type of event
switch (@event.Type) {
case HKWorkoutEventType.Lap:
...
break;
case HKWorkoutEventType.Marker:
...
break;
case HKWorkoutEventType.MotionPaused:
...
break;
case HKWorkoutEventType.MotionResumed:
...
break;
case HKWorkoutEventType.Pause:
...
break;
case HKWorkoutEventType.Resume:
...
break;
}
}
#endregion
#region Events
public delegate void OutdoorRunEventDelegate ();
public delegate void OutdoorRunMileGoalDelegate (float miles);
public event OutdoorRunEventDelegate Failed;
internal void RaiseFailed ()
{
if (this.Failed != null) this.Failed ();
}
public event OutdoorRunEventDelegate Paused;
internal void RaisePaused ()
{
if (this.Paused != null) this.Paused ();
}
public event OutdoorRunEventDelegate Running;
internal void RaiseRunning ()
{
if (this.Running != null) this.Running ();
}
public event OutdoorRunEventDelegate Ended;
internal void RaiseEnded ()
{
if (this.Ended != null) this.Ended ();
}
public event OutdoorRunMileGoalDelegate ReachedMileGoal;
internal void RaiseReachedMileGoal (float miles)
{
if (this.ReachedMileGoal != null) this.ReachedMileGoal (miles);
}
public event OutdoorRunEventDelegate HealthKitSamplesUpdated;
internal void RaiseHealthKitSamplesUpdated ()
{
if (this.HealthKitSamplesUpdated != null) this.HealthKitSamplesUpdated ();
}
#endregion
}
}
Osvědčené postupy
Apple navrhuje při navrhování a implementaci aplikací Workout v watchOS 3 a iOS 10 používat následující osvědčené postupy:
- Ujistěte se, že aplikace watchOS 3 Workout je stále funkční, i když se nemůže připojit k i Telefon a verzi aplikace pro iOS 10.
- Použijte vzdálenost HealthKit, pokud GPS není k dispozici, protože je schopen generovat vzorky vzdálenosti bez GPS.
- Umožňuje uživateli zahájit cvičení z Apple Watch nebo i Telefon.
- Umožňuje aplikaci zobrazit cvičení z jiných zdrojů (například z jiných aplikací třetích stran) v historických zobrazeních dat.
- Ujistěte se, že aplikace nezobrazuje odstraněná cvičení v historických datech.
Shrnutí
Tento článek se zabývá vylepšeními, která společnost Apple provedla pro tréninkové aplikace ve watchOS 3 a jak je implementovat v Xamarinu.