Xamarin'de watchOS Egzersiz Uygulamaları
Bu makale, Apple'ın watchOS 3'teki antrenman uygulamalarında yaptığı geliştirmeleri ve bunları Xamarin'de nasıl uygulayacaklarını kapsar.
watchOS 3'te yeni olan antrenmanla ilgili uygulamalar, Apple Watch'ta arka planda çalışabilir ve HealthKit verilerine erişim elde edebilir. Üst iOS 10 tabanlı uygulamaları, watchOS 3 tabanlı uygulamayı kullanıcı müdahalesi olmadan da başlatabilme özelliğine sahiptir.
Aşağıdaki konular ayrıntılı olarak ele alınacaktır:
Antrenman Uygulamaları Hakkında
Fitness ve egzersiz uygulamaları kullanıcıları, günün birkaç saatini sağlık ve fitness hedeflerine ayırarak son derece adanmış olabilir. Sonuç olarak, verileri doğru bir şekilde toplayıp görüntüleyen ve Apple Health ile sorunsuz bir şekilde tümleşen, hızlı yanıt veren, kullanımı kolay uygulamalar beklerler.
İyi tasarlanmış bir fitness veya egzersiz uygulaması, kullanıcıların fitness hedeflerine ulaşmak için etkinliklerini grafiğini oluşturmalarına yardımcı olur. Apple Watch kullanarak, fitness ve egzersiz uygulamaları kalp atış hızına, kalori yanıklarına ve etkinlik algılamaya anında erişime sahiptir.
watchOS 3'te yeni olan Arka Plan Çalıştırma, antrenmanla ilgili uygulamalara Apple Watch'ta arka planda çalışma ve HealthKit verilerine erişim sağlama olanağı sağlar.
Bu belgede Arka Plan Çalıştırma özelliği tanıtılacak, egzersiz uygulaması yaşam döngüsünü kapsayacak ve bir egzersiz uygulamasının Apple Watch'ta kullanıcının Etkinlik Halkalarına nasıl katkıda bulunabileceği gösterilecektir.
Antrenman Oturumları Hakkında
Her egzersiz uygulamasının kalbi, kullanıcının başlatabileceği ve durdurabileceği bir Egzersiz Oturumudur (HKWorkoutSession
). Egzersiz Oturumu API'sinin uygulanması kolaydır ve bir egzersiz uygulaması için aşağıdakiler gibi çeşitli avantajlar sağlar:
- Etkinlik Türüne göre hareket ve kalori yanık algılaması.
- Kullanıcının Etkinlik Halkalarına otomatik katkı.
- Oturumdayken, kullanıcı cihazı her uyandırdığında (bileğini kaldırarak veya Apple Watch ile etkileşim kurarak) uygulama otomatik olarak görüntülenir.
Arka Plan Çalıştırma Hakkında
Yukarıda belirtildiği gibi watchOS 3 ile bir egzersiz uygulaması arka planda çalışacak şekilde ayarlanabilir. Arka Plan'ı kullanma Egzersiz uygulaması çalıştırmak, arka planda çalışırken Apple Watch'un algılayıcılarındaki verileri işleyebilir. Örneğin, bir uygulama artık ekranda görüntülenmese bile kullanıcının kalp atış hızını izlemeye devam edebilir.
Arka Plan Çalıştırma, etkin bir Egzersiz Oturumu sırasında kullanıcıya mevcut ilerleme durumunu bildirmek için dokunsal bir uyarı gönderme gibi herhangi bir zamanda canlı geri bildirim sunma olanağı da sağlar.
Buna ek olarak, Arka Plan Çalıştırma, kullanıcının Apple Watch'larına hızla göz attığında en son verilere sahip olması için uygulamanın Kullanıcı Arabirimini hızla güncelleştirmesine olanak tanır.
Apple Watch'ta yüksek performansı korumak için Arka Plan Çalıştırma kullanan bir izleme uygulaması, pil tasarrufu sağlamak için arka plan çalışması miktarını sınırlamalıdır. Bir uygulama arka planda aşırı CPU kullanıyorsa watchOS tarafından askıya alınabilir.
Arka Plan Çalıştırmayı Etkinleştirme
Arka Plan Çalıştırma'yı etkinleştirmek için aşağıdakileri yapın:
Çözüm Gezgini, düzenleme için açmak üzere İzleme Uzantısının eşlikçisi i Telefon uygulamasının
Info.plist
dosyasına çift tıklayın.Kaynak görünümüne geçin:
adlı
WKBackgroundModes
yeni bir anahtar ekleyin ve TürArray
olarak ayarlayın:Türü
String
ve değeriyleworkout-processing
diziye yeni bir öğe ekleyin:Dosyadaki değişiklikleri kaydedin.
Antrenman Oturumu Başlatma
Antrenman Oturumu başlatmanın üç ana adımı vardır:
- Uygulamanın HealthKit'teki verilere erişmek için yetkilendirme istemesi gerekir.
- Başlatılmakta olan antrenman türü için bir Antrenman Yapılandırması nesnesi oluşturun.
- Yeni oluşturulan Antrenman Yapılandırması'nı kullanarak bir Antrenman Oturumu oluşturun ve başlatın.
Yetkilendirme İsteniyor
Bir uygulamanın kullanıcının HealthKit verilerine erişebilmesi için önce kullanıcıdan yetkilendirme istemesi ve alması gerekir. Antrenman uygulamasının yapısına bağlı olarak aşağıdaki istek türlerini yapabilir:
- Veri yazma yetkisi:
- Egzersiz
- Verileri okumak için yetkilendirme:
- Enerji yandı
- Mesafe
- Kalp Atış Hızı
Bir uygulamanın yetkilendirme isteğinde bulunabilmesi için önce HealthKit'e erişecek şekilde yapılandırılması gerekir.
Aşağıdakileri yapın:
Çözüm Gezgini, dosyayı düzenlemek üzere açmak için çift tıklayın
Entitlements.plist
.En alta kaydırın ve HealthKit'i Etkinleştir'i işaretleyin:
Dosyadaki değişiklikleri kaydedin.
Uygulamayı doğru bir şekilde sağlamak için, HealthKit'e Giriş makalesinin Açık Uygulama Kimliği ve Sağlama Profili ile Uygulama Kimliği ve Sağlama Profilini Xamarin.iOS Uygulamanızla İlişkilendirme bölümündeki yönergeleri izleyin.
Son olarak, Kullanıcının HealthKit veri deposuna erişmek için yetkilendirme istemek için HealthKit'e Giriş makalesinin Programlama Sistem Durumu Seti ve Kullanıcıdan İzin İsteme bölümündeki yönergeleri kullanın.
Antrenman Yapılandırmasını Ayarlama
Antrenman Oturumları, antrenman türünü (örneğinHKWorkoutActivityType.Running
) ve antrenman konumunu (HKWorkoutConfiguration
örneğinHKWorkoutSessionLocationType.Outdoor
) belirten bir Egzersiz Yapılandırma nesnesi () kullanılarak oluşturulur:
using HealthKit;
...
// Create a workout configuration
var configuration = new HKWorkoutConfiguration () {
ActivityType = HKWorkoutActivityType.Running,
LocationType = HKWorkoutSessionLocationType.Outdoor
};
Antrenman Oturumu Temsilcisi Oluşturma
Antrenman Oturumu sırasında gerçekleşebilecek olayları işlemek için uygulamanın bir Antrenman Oturumu Temsilcisi örneği oluşturması gerekir. Projeye yeni bir sınıf ekleyin ve sınıfın dışına dayandırın HKWorkoutSessionDelegate
. Dış mekan çalıştırması örneği için aşağıdaki gibi görünebilir:
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
}
}
Bu sınıf, Antrenman Oturumu'nun durumu değiştikçe () ve Antrenman Oturumu başarısız olursa (DidChangeToState
DidFail
) tetiklenecek birkaç olay oluşturur.
Antrenman Oturumu Oluşturma
Yeni bir Antrenman Oturumu oluşturmak ve kullanıcının varsayılan HealthKit mağazasında başlatmak için yukarıda oluşturulan Antrenman Yapılandırması ve Antrenman Oturumu Temsilcisi'ni kullanarak:
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);
}
Uygulama bu Antrenman Oturumunu başlatırsa ve kullanıcı saat yüzüne geri dönerse, yüzün üzerinde küçük bir yeşil "çalışan adam" simgesi görüntülenir:
Kullanıcı bu simgeye dokunursa uygulamaya geri döner.
Veri Toplama ve Denetim
Bir Antrenman Oturumu yapılandırılıp başlatıldıktan sonra uygulamanın oturum hakkında veri toplaması (kullanıcının kalp atış hızı gibi) ve oturumun durumunu denetlemesi gerekir:
- Örnekleri Gözlemleme - Uygulamanın HealthKit'ten üzerinde işlem yapılacak ve kullanıcıya görüntülenecek bilgileri alması gerekir.
- Olayları Gözlemleme - Uygulamanın HealthKit tarafından veya uygulamanın kullanıcı arabiriminden (egzersizi duraklatan kullanıcı gibi) oluşturulan olaylara yanıt vermesi gerekir.
- Çalışıyor Durumunu Girin - Oturum başlatıldı ve şu anda çalışıyor.
- Duraklatılmış Durum Girin - Kullanıcı geçerli antrenman oturumunu duraklatmıştır ve daha sonraki bir tarihte yeniden başlatabilir. Kullanıcı, tek bir Antrenman Oturumunda çalışan ve duraklatılan durumlar arasında birkaç kez geçiş yapabilir.
- Egzersiz Oturumunu Sonlandır - Herhangi bir noktada kullanıcı Egzersiz Oturumunu sonlandırabilir veya tarifeli bir egzersiz ise (iki mil koşu gibi) süresi dolabilir ve kendi başına sona erebilir.
Son adım, Antrenman Oturumu sonuçlarını kullanıcının HealthKit veri deposuna kaydetmektir.
HealthKit Örneklerini Gözlemleme
Uygulamanın ilgilendiği her HealthKit veri noktası için kalp atış hızı veya yakılan aktif enerji gibi bir Anchor Nesne Sorgusu açması gerekir. Gözlemlenen her veri noktası için, uygulamaya gönderilirken yeni verileri yakalamak için bir güncelleştirme işleyicisi oluşturulması gerekir.
Bu veri noktalarından uygulama toplamları (toplam çalışma mesafesi gibi) birikebilir ve kullanıcı arabirimini gerektiği gibi güncelleştirebilir. Ayrıca uygulama, belirli bir hedefe veya başarıya ulaştıklarında (bir çalıştırmanın sonraki milini tamamlama gibi) kullanıcılara bildirimde bulunabilir.
Aşağıdaki örnek koda göz atın:
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);
}
Yöntemini kullanarak GetPredicateForSamples
veri almak istediği başlangıç tarihini ayarlamak için bir koşul oluşturur. Yöntemi kullanarak GetPredicateForObjectsFromDevices
HealthKit bilgilerini çekmek için bir cihaz kümesi oluşturur, bu durumda yalnızca yerel Apple Watch (HKDevice.LocalDevice
). İki koşul, yöntemi kullanılarak CreateAndPredicate
bir Bileşik Koşulda (NSCompoundPredicate
) birleştirilir.
İstenen veri noktası için bir yeni HKAnchoredObjectQuery
oluşturulur (bu örnekte HKQuantityTypeIdentifier.ActiveEnergyBurned
Etkin Enerji Yakılan veri noktası için), döndürülenHKSampleQuery.NoLimit
() veri miktarına bir sınır uygulanmaz ve HealthKit'ten uygulamaya döndürülen verileri işlemek için bir güncelleştirme işleyicisi tanımlanır.
Güncelleştirme işleyicisi, belirli bir veri noktası için uygulamaya yeni veriler her teslim edilişinde çağrılır. Hata döndürülmezse uygulama verileri güvenle okuyabilir, gerekli hesaplamaları yapabilir ve kullanıcı arabirimini gerektiği gibi güncelleştirebilir.
Kod, dizide döndürülen tüm örnekleri (HKSample
) döngüye addedObjects
alır ve bunları Bir Miktar Örneğine (HKQuantitySample
) dönüştürür. Ardından, örneğin çift değerini bir joule (HKUnit.Joule
) olarak alır ve egzersiz için yakılan aktif enerjinin çalışan toplamına biriktirir ve Kullanıcı Arabirimini güncelleştirir.
Ulaşılan Hedef Bildirimi
Yukarıda belirtildiği gibi, kullanıcı antrenman uygulamasında bir hedefe ulaştığı zaman (bir çalıştırmanın ilk milini tamamlamak gibi), Taptic Engine aracılığıyla kullanıcıya dokunsal geri bildirim gönderebilir. Kullanıcı, geri bildirimi soran olayı görmek için büyük olasılıkla bileğini kaldıracağı için uygulamanın kullanıcı arabirimini de bu noktada güncelleştirmesi gerekir.
Dokunsal geri bildirimi yürütmek için aşağıdaki kodu kullanın:
// Play haptic feedback
WKInterfaceDevice.CurrentDevice.PlayHaptic (WKHapticType.Notification);
Olayları Gözlemleme
Olaylar, uygulamanın kullanıcının antrenmanı sırasında belirli noktaları vurgulamak için kullanabileceği zaman damgalarıdır. Bazı etkinlikler doğrudan uygulama tarafından oluşturulur ve antrenmana kaydedilir ve bazı etkinlikler HealthKit tarafından otomatik olarak oluşturulur.
HealthKit tarafından oluşturulan olayları gözlemlemek için, uygulama yöntemini HKWorkoutSessionDelegate
geçersiz kılarDidGenerateEvent
:
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, watchOS 3'te aşağıdaki yeni olay türlerini eklemiştir:
HKWorkoutEventType.Lap
- Antrenmanı eşit mesafe bölümlerine bölen olaylar içindir. Örneğin, koşu sırasında bir tur boyunca bir tur işaretlemek için.HKWorkoutEventType.Marker
- Egzersiz içinde rastgele ilgi çekici noktalar içindir. Örneğin, bir dış mekan koşusunun rotası üzerinde belirli bir noktaya ulaşmak.
Bu yeni türler uygulama tarafından oluşturulabilir ve grafik ve istatistik oluşturmada daha sonra kullanmak üzere antrenmanda depolanabilir.
İşaretçi Olayı oluşturmak için aşağıdakileri yapın:
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);
}
Bu kod, bir İşaretçi Olayının (HKWorkoutEvent
) yeni bir örneğini oluşturur ve bunu özel bir olay koleksiyonuna kaydeder (daha sonra Antrenman Oturumuna yazılır) ve dokunsal aracılığıyla olayı kullanıcıya bildirir.
Antrenmanları Duraklatma ve Devam Ettir
Bir egzersiz oturumunun herhangi bir noktasında, kullanıcı antrenmanı geçici olarak duraklatabilir ve daha sonra devam ettirebilir. Örneğin, önemli bir çağrı almak ve arama tamamlandıktan sonra çalıştırmayı sürdürmek için iç mekan çalıştırmasını duraklatabilir.
Uygulamanın kullanıcı arabirimi, Kullanıcı etkinliğini askıya alırken Apple Watch'un hem güç hem de veri alanından tasarruf edebilmesi için antrenmanı duraklatmak ve sürdürmek için bir yol sağlamalıdır (HealthKit'e arayarak). Ayrıca uygulama, Antrenman Oturumu duraklatılmış durumdayken alınabilecek yeni veri noktalarını yoksaymalıdır.
HealthKit, Duraklat ve Sürdür olayları oluşturarak duraklatma ve sürdürme çağrılarına yanıt verir. Antrenman Oturumu duraklatılırken, oturum devam edene kadar HealthKit tarafından uygulamaya yeni olay veya veri gönderilmez.
Bir Antrenman Oturumunu duraklatmak ve sürdürmek için aşağıdaki kodu kullanın:
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);
}
HealthKit'ten oluşturulacak Duraklatma ve Sürdürme olayları, yöntemini HKWorkoutSessionDelegate
geçersiz kılarak DidGenerateEvent
işlenebilir:
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;
}
}
Hareket Olayları
WatchOS 3'e yeni eklenenler arasında Hareket Duraklatıldı (HKWorkoutEventType.MotionPaused
) ve Hareket Sürdürüldü (HKWorkoutEventType.MotionResumed
) olayları yer alır. Bu olaylar, kullanıcı hareket etmeye başladığında ve durduğunda çalışan bir antrenman sırasında HealthKit tarafından otomatik olarak oluşturulur.
Uygulama Bir Hareket Duraklatıldı olayı aldığında, kullanıcı hareketi sürdürene ve Hareket Özgeçmişleri olayı alınana kadar veri toplamayı durdurması gerekir. Uygulama, Hareket Duraklatıldı olayına yanıt olarak Antrenman oturumunu duraklatmamalıdır.
Önemli
Hareket Duraklatıldı ve Hareket Sürdürme olayları yalnızca RunningWorkout Etkinlik Türü (HKWorkoutActivityType.Running
) için desteklenir.
Bu olaylar yine yöntemini HKWorkoutSessionDelegate
geçersiz kılarak DidGenerateEvent
işlenebilir:
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;
}
}
Antrenman Oturumunu Sonlandırma ve Kaydetme
Kullanıcı antrenmanını tamamladığında, uygulamanın geçerli Antrenman Oturumu'na son vermesi ve HealthKit veritabanına kaydetmesi gerekir. HealthKit'e kaydedilen antrenmanlar otomatik olarak Antrenman Etkinlik Listesi'nde görüntülenir.
iOS 10'da yeni olan bu liste, kullanıcının i Telefon'sinde Egzersiz Etkinliği Listesi'ni de içerir. Bu nedenle Apple Watch yakında olmasa bile, egzersiz telefonda sunulacaktır.
Enerji Örnekleri içeren egzersizler, 3. taraf uygulamaların artık kullanıcının günlük Taşıma hedeflerine katkıda bulunabilmesi için Etkinlikler uygulamasında kullanıcının Taşıma Halkasını güncelleştirir.
Bir Antrenman Oturumunu sonlandırmak ve kaydetmek için aşağıdaki adımlar gereklidir:
- İlk olarak, uygulamanın Antrenman Oturumu'na son vermesi gerekir.
- Antrenman Oturumu HealthKit'e kaydedilir.
- Kaydedilen Antrenman Oturumuna herhangi bir örneği (yakılan enerji veya mesafe gibi) ekleyin.
Oturumu Sonlandırma
Antrenman Oturumunu sonlandırmak için, öğesini geçirme yöntemini HKHealthStore
çağırın:EndWorkoutSession
HKWorkoutSession
public HKHealthStore HealthStore { get; private set; }
public HKWorkoutSession WorkoutSession { get; private set;}
...
public void EndOutdoorRun ()
{
// End the current workout session
HealthStore.EndWorkoutSession (WorkoutSession);
}
Bu, cihaz algılayıcılarını normal modlarına sıfırlar. HealthKit, antrenmanı sonlandırmayı bitirdiğinde yönteminin geri DidChangeToState
çağrısını HKWorkoutSessionDelegate
alır:
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;
}
}
Oturumu Kaydetme
Uygulama Antrenman Oturumu'nun sona erdikten sonra bir Antrenman () oluşturması ve bunu (HKWorkout
bir etkinlikle birlikte) HealthKit veri deposuna (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
}
});
}
Bu kod, egzersiz için yakılan toplam enerji miktarını ve mesafeyi nesne olarak HKQuantity
oluşturur. Antrenmanı tanımlayan meta veri sözlüğü oluşturulur ve antrenmanın konumu belirtilir:
metadata.Add (new NSString ("HKMetadataKeyIndoorWorkout"), new NSString ("NO"));
yeni HKWorkout
bir nesne, ile aynı HKWorkoutActivityType
HKWorkoutSession
ile oluşturulur, başlangıç ve bitiş tarihleri, olay listesi (yukarıdaki bölümlerden birikir), yakılan enerji, toplam uzaklık ve meta veri sözlüğü. Bu nesne Sistem Durumu Deposu'na kaydedilir ve işlenen tüm hatalar.
Örnek Ekleme
Uygulama bir dizi örneği antrenmana kaydettiğinde HealthKit, örneklerle Antrenmanın kendisi arasında bir bağlantı oluşturur, böylece uygulama belirli bir antrenmanla ilişkili tüm örnekler için Daha sonraki bir tarihte HealthKit'i sorgulayabilir. Bu bilgileri kullanarak uygulama, antrenman verilerinden grafikler oluşturabilir ve bunları bir egzersiz zaman çizelgesine göre çizebilir.
Bir uygulamanın Etkinlik uygulamasının Taşıma Halkasına katkıda bulunması için kaydedilen egzersizle birlikte enerji örneklerini içermesi gerekir. Ayrıca, mesafe ve enerji toplamları, uygulamanın kaydedilmiş bir antrenmanla ilişkilendirdiği örneklerin toplamıyla eşleşmelidir.
Kaydedilmiş bir antrenmana örnek eklemek için aşağıdakileri yapın:
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
}
});
}
İsteğe bağlı olarak, uygulama daha küçük bir örnek alt kümesini veya kaydedilmiş antrenmanla ilişkilendirilen bir mega örneği (tüm antrenman aralığını kapsayan) hesaplayabilir ve oluşturabilir.
Antrenmanlar ve iOS 10
Her watchOS 3 antrenman uygulamasının ana iOS 10 tabanlı bir antrenman uygulaması vardır ve iOS 10'da yeni olan bu iOS uygulaması, Apple Watch'u Antrenman Modu'na yerleştirecek (kullanıcı müdahalesi olmadan) ve watchOS uygulamasını Arka Plan Çalıştırma modunda çalıştıracak bir antrenman başlatmak için kullanılabilir (daha fazla ayrıntı için yukarıdaki Arka Plan Çalıştırma Hakkında bölümüne bakın).
watchOS uygulaması çalışırken, üst iOS uygulamasıyla mesajlaşma ve iletişim için Watch Bağlan ivity kullanabilir.
Bu işlemin nasıl çalıştığına göz atın:
- i Telefon uygulaması bir
HKWorkoutConfiguration
nesne oluşturur ve Antrenman Türü ile Konumu ayarlar. HKWorkoutConfiguration
Nesne, uygulamanın Apple Watch sürümüne gönderilir ve henüz çalışmıyorsa sistem tarafından başlatılır.- Geçirilen Antrenman Yapılandırması'nı kullanarak watchOS 3 uygulaması yeni bir Antrenman Oturumu (
HKWorkoutSession
) başlatır.
Önemli
Ebeveyn i Telefon uygulamasının Apple Watch'ta bir antrenman başlatması için watchOS 3 uygulamasında Arka Plan Çalışıyor özelliğinin etkin olması gerekir. Daha fazla ayrıntı için lütfen yukarıdaki Arka Plan Çalıştırmayı Etkinleştirme bölümüne bakın.
Bu işlem, doğrudan watchOS 3 uygulamasında Egzersiz Oturumu başlatma işlemine çok benzer. i Telefon aşağıdaki kodu kullanın:
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
...
}
});
}
}
Bu kod, uygulamanın watchOS sürümünün yüklenmesini ve i Telefon sürümünün önce buna bağlanmasını sağlar:
if (ConnectivitySession.ActivationState == WCSessionActivationState.Activated && ConnectivitySession.WatchAppInstalled) {
...
}
Ardından her zamanki gibi bir HKWorkoutConfiguration
oluşturur ve apple watch'a göndermek ve uygulamayı ve Antrenman Oturumu'na başlatmak için yöntemini HKHealthStore
kullanırStartWatchApp
.
watch işletim sistemi uygulamasında da WKExtensionDelegate
içinde aşağıdaki kodu kullanın:
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);
}
öğesini alır HKWorkoutConfiguration
ve yeni HKWorkoutSession
bir oluşturur ve özel HKWorkoutSessionDelegate
öğesinin bir örneğini ekler. Kullanıcının HealthKit Health Store'unun antrenman oturumu başlatılır.
Tüm Parçaları Bir Araya Getirme
Bu belgede sunulan tüm bilgileri alarak, watchOS 3 tabanlı bir egzersiz uygulaması ve ana iOS 10 tabanlı egzersiz uygulaması aşağıdaki bölümleri içerebilir:
- iOS 10
ViewController.cs
- Apple Watch'ta watch Bağlan ivity oturumunu ve antrenmanı başlatır. - watchOS 3
ExtensionDelegate.cs
- Antrenman uygulamasının watchOS 3 sürümünü işler. - watchOS 3
OutdoorRunDelegate.cs
- Antrenman olaylarını işlemek için özelHKWorkoutSessionDelegate
bir öğedir.
Önemli
Aşağıdaki bölümlerde gösterilen kod yalnızca watchOS 3'teki Antrenman uygulamalarına sağlanan yeni, gelişmiş özellikleri uygulamak için gereken parçaları içerir. Tüm destekleyici kodlar ve kullanıcı arabirimini sunmak ve güncelleştirmek için kod dahil değildir, ancak diğer watchOS belgelerimiz izlenerek kolayca oluşturulabilir.
ViewController.cs
ViewController.cs
Antrenman uygulamasının üst iOS 10 sürümündeki dosya aşağıdaki kodu içerir:
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
ExtensionDelegate.cs
Antrenman uygulamasının watchOS 3 sürümündeki dosya aşağıdaki kodu içerir:
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
OutdoorRunDelegate.cs
Antrenman uygulamasının watchOS 3 sürümündeki dosya aşağıdaki kodu içerir:
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
}
}
En İyi Uygulamalar
Apple, watchOS 3 ve iOS 10'da Antrenman uygulamaları tasarlarken ve uygularken aşağıdaki en iyi yöntemlerin kullanılmasını önerir:
- watchOS 3 Antrenman uygulamasının i Telefon ve uygulamanın iOS 10 sürümüne bağlanamadığında bile çalışır durumda olduğundan emin olun.
- GPS olmadan mesafe örnekleri oluşturabildiği için GPS kullanılamadığında HealthKit mesafesini kullanın.
- Kullanıcının antrenmanı Apple Watch'tan veya i Telefon'den başlatmasına izin verin.
- Uygulamanın geçmiş veri görünümlerinde diğer kaynaklardan (diğer üçüncü taraf uygulamalar gibi) egzersizleri görüntülemesine izin verin.
- Uygulamanın silinen antrenmanları geçmiş verilerde göstermediğinden emin olun.
Özet
Bu makale, Apple'ın watchOS 3'teki antrenman uygulamaları için yaptığı geliştirmeleri ve bunları Xamarin'de nasıl uygulayacaklarını ele almıştır.