Sdílet prostřednictvím


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.

Příklad aplikace fitness a cvičení

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:

  1. V Průzkumník řešení poklikejte na soubor rozšíření kukátek i Telefon a otevřete ho Info.plist pro úpravy.

  2. Přepněte do zobrazení Zdroj :

    Zobrazení Zdroj

  3. Přidejte volaný WKBackgroundModes nový klíč a nastavte typ naArray:

    Přidání nového klíče s názvem WKBackgroundModes

  4. Přidejte do pole novou položku s typemString a hodnotou workout-processing:

    Přidání nové položky do pole s typem řetězce a hodnotou zpracování cvičení

  5. Uložte změny souboru.

Zahájení cvičení

Existují tři hlavní kroky pro zahájení cvičení:

Tři hlavní kroky k zahájení cvičení

  1. Aplikace musí požádat o autorizaci pro přístup k datům ve HealthKitu.
  2. Vytvořte objekt Konfigurace cvičení pro typ zahájení cvičení.
  3. 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ě:

  1. V Průzkumník řešení poklikáním Entitlements.plist otevřete soubor pro úpravy.

  2. Posuňte se dolů a zkontrolujte povolit HealthKit:

    Kontrola povolení HealthKitu

  3. Uložte změny souboru.

  4. 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.

  5. 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ž":

Malá zelená ikona běžícího muže zobrazená nad obličejem

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:

Diagram shromažďování a řízení dat

  1. Pozorování ukázek – Aplikace bude muset načíst informace ze sady HealthKit, na které se budou reagovat a zobrazit uživateli.
  2. 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í).
  3. Zadejte spuštěný stav – Relace byla spuštěna a aktuálně je spuštěná.
  4. 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í.
  5. 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:

Ukončení a uložení diagramu cvičení

  1. Za prvé, aplikace bude muset ukončit cvičení relace.
  2. Cvičení se uloží do HealthKitu.
  3. 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í HKWorkoutSessionv :

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 HKWorkoutActivityTypeHKWorkoutSessiondatem 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:

Komunikační diagram i Telefon a Apple Watch

  1. Aplikace i Telefon vytvoří HKWorkoutConfiguration objekt a nastaví typ cvičení a umístění.
  2. Objekt HKWorkoutConfiguration se odešle verzi aplikace Apple Watch a pokud ještě není spuštěný, spustí ji systém.
  3. 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 HKWorkoutSessionDelegateobjektu . 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:

  1. iOS 10 ViewController.cs – zpracovává začátek relace Připojení ivity hodinek a cvičení na Apple Watch.
  2. watchOS 3 ExtensionDelegate.cs – zpracovává verzi watchOS 3 aplikace pro cvičení.
  3. 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.