본문 바로가기

예제 및 활용 사례

GitHub

이 가이드는 다음을 활용한 일반적인 watch 앱 패턴의 실용적인 예제를 제공합니다: @capgo/capacitor-watch. Phone 측 TypeScript는 다음 두 경우 모두에서 작동합니다: iOS 및 Android; 아래 watch UI 샘플은 SwiftUI 를 사용합니다 (Apple Watch). Wear OS에서는 Wear OS 설정 가이드의 Data Layer 경로를 사용하여 동일한 페이로드를 미러링하세요.

예제 1: 건강 및 피트니스 트래커

“예제 1: 건강 및 피트니스 트래커” 제목 섹션

워치와 폰 간의 운동 데이터를 동기화합니다.

import SwiftUI
import CapgoWatchSDK
struct WorkoutView: View {
@ObservedObject var connector = WatchConnector.shared
@State private var heartRate: Double = 0
@State private var calories: Double = 0
@State private var isTracking = false
var body: some View {
VStack(spacing: 12) {
// Heart Rate Display
HStack {
Image(systemName: "heart.fill")
.foregroundColor(.red)
Text("\(Int(heartRate)) BPM")
.font(.title2)
}
// Calories
HStack {
Image(systemName: "flame.fill")
.foregroundColor(.orange)
Text("\(Int(calories)) cal")
}
// Start/Stop Button
Button(isTracking ? "Stop" : "Start") {
toggleTracking()
}
.buttonStyle(.borderedProminent)
.tint(isTracking ? .red : .green)
}
.onReceive(Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { _ in
if isTracking {
updateMetrics()
}
}
}
private func toggleTracking() {
isTracking.toggle()
if isTracking {
// Notify phone that workout started
connector.sendMessage([
"event": "workoutStarted",
"timestamp": Date().timeIntervalSince1970
])
} else {
// Send final summary to phone
do {
try connector.updateApplicationContext([
"workoutComplete": true,
"totalCalories": calories,
"avgHeartRate": heartRate,
"endTime": Date().timeIntervalSince1970
])
} catch {
print("Failed to update context: \(error)")
}
}
}
private func updateMetrics() {
// Simulate heart rate (in real app, use HealthKit)
heartRate = Double.random(in: 120...160)
calories += Double.random(in: 0.1...0.3)
// Send real-time updates if phone is reachable
if connector.isReachable {
connector.sendMessage([
"heartRate": heartRate,
"calories": calories,
"timestamp": Date().timeIntervalSince1970
])
}
}
}

워치에서 스마트 홈 기기를 제어하세요.

import SwiftUI
import CapgoWatchSDK
struct SmartHomeView: View {
@ObservedObject var connector = WatchConnector.shared
@State private var devices: [SmartDevice] = []
@State private var isLoading = false
var body: some View {
NavigationView {
Group {
if isLoading {
ProgressView("Loading...")
} else if devices.isEmpty {
Text("No devices")
.foregroundColor(.secondary)
} else {
List(devices) { device in
DeviceRow(device: device, onToggle: { toggleDevice(device) })
}
}
}
.navigationTitle("Home")
.toolbar {
Button(action: refreshDevices) {
Image(systemName: "arrow.clockwise")
}
}
}
.onAppear {
refreshDevices()
}
.onChange(of: connector.lastMessage) { message in
handleMessage(message)
}
}
private func refreshDevices() {
guard connector.isReachable else {
// Load from cached context
loadFromContext()
return
}
isLoading = true
connector.sendMessage(["action": "getDevices"]) { reply in
DispatchQueue.main.async {
isLoading = false
if let deviceList = reply["devices"] as? [[String: Any]] {
devices = deviceList.compactMap { SmartDevice(from: $0) }
}
}
}
}
private func toggleDevice(_ device: SmartDevice) {
connector.sendMessage([
"action": "toggleDevice",
"deviceId": device.id,
"newState": !device.isOn
]) { reply in
DispatchQueue.main.async {
if reply["success"] as? Bool == true {
// Update local state
if let index = devices.firstIndex(where: { $0.id == device.id }) {
devices[index].isOn.toggle()
}
}
}
}
}
private func loadFromContext() {
if let deviceList = connector.applicationContext["devices"] as? [[String: Any]] {
devices = deviceList.compactMap { SmartDevice(from: $0) }
}
}
private func handleMessage(_ message: [String: Any]) {
// Handle push updates from phone
if let update = message["deviceUpdate"] as? [String: Any],
let deviceId = update["id"] as? String,
let isOn = update["isOn"] as? Bool {
if let index = devices.firstIndex(where: { $0.id == deviceId }) {
devices[index].isOn = isOn
}
}
}
}
struct SmartDevice: Identifiable {
let id: String
let name: String
let type: String
var isOn: Bool
init?(from dict: [String: Any]) {
guard let id = dict["id"] as? String,
let name = dict["name"] as? String,
let type = dict["type"] as? String,
let isOn = dict["isOn"] as? Bool else {
return nil
}
self.id = id
self.name = name
self.type = type
self.isOn = isOn
}
}
struct DeviceRow: View {
let device: SmartDevice
let onToggle: () -> Void
var body: some View {
HStack {
Image(systemName: iconName)
.foregroundColor(device.isOn ? .yellow : .gray)
Text(device.name)
Spacer()
Toggle("", isOn: .constant(device.isOn))
.labelsHidden()
.onTapGesture { onToggle() }
}
}
var iconName: String {
switch device.type {
case "light": return device.isOn ? "lightbulb.fill" : "lightbulb"
case "thermostat": return "thermometer"
case "lock": return device.isOn ? "lock.fill" : "lock.open"
default: return "power"
}
}
}

워치로 커스텀 알림을 전송하세요.

import { CapgoWatch } from '@capgo/capacitor-watch';
class NotificationService {
async sendNotification(title: string, body: string, data?: any) {
const info = await CapgoWatch.getInfo();
if (info.isReachable) {
// Send immediately if watch is reachable
await CapgoWatch.sendMessage({
data: {
type: 'notification',
title,
body,
data,
timestamp: Date.now()
}
});
} else {
// Queue for later delivery
await CapgoWatch.transferUserInfo({
userInfo: {
type: 'notification',
title,
body,
data,
timestamp: Date.now()
}
});
}
}
async sendUrgentAlert(message: string) {
// Use sendMessage for urgent alerts (requires watch to be reachable)
const info = await CapgoWatch.getInfo();
if (!info.isReachable) {
console.warn('Watch not reachable for urgent alert');
return false;
}
await CapgoWatch.sendMessage({
data: {
type: 'urgentAlert',
message,
haptic: 'warning',
timestamp: Date.now()
}
});
return true;
}
}
// Usage
const notifications = new NotificationService();
// Regular notification (queued if watch unavailable)
await notifications.sendNotification(
'Order Update',
'Your order has shipped!',
{ orderId: '12345' }
);
// Urgent alert (requires watch to be active)
const sent = await notifications.sendUrgentAlert('Security alert: New login detected');

예제 4: 데이터 동기화 서비스

“예제 4: 데이터 동기화 서비스” 섹션

폰과 워치 간의 데이터를 동기화 상태로 유지합니다.

import { CapgoWatch } from '@capgo/capacitor-watch';
class DataSyncService {
private syncQueue: any[] = [];
private isSyncing = false;
async initialize() {
// Listen for sync requests from watch
await CapgoWatch.addListener('messageReceivedWithReply', async (event) => {
if (event.message.action === 'sync') {
const data = await this.getDataForSync(event.message.lastSyncTime);
await CapgoWatch.replyToMessage({
callbackId: event.callbackId,
data: { items: data, syncTime: Date.now() }
});
}
});
// Listen for data from watch
await CapgoWatch.addListener('userInfoReceived', (event) => {
this.handleWatchData(event.userInfo);
});
// Monitor connectivity for sync opportunities
await CapgoWatch.addListener('reachabilityChanged', (event) => {
if (event.isReachable && this.syncQueue.length > 0) {
this.processSyncQueue();
}
});
}
// Push important data to watch (guaranteed delivery)
async pushToWatch(data: any) {
await CapgoWatch.transferUserInfo({
userInfo: {
type: 'dataUpdate',
data,
timestamp: Date.now()
}
});
}
// Update shared state (latest value only)
async updateSharedState(state: any) {
await CapgoWatch.updateApplicationContext({
context: {
...state,
lastUpdated: Date.now()
}
});
}
// Queue data for sync when watch becomes available
queueForSync(data: any) {
this.syncQueue.push(data);
this.attemptSync();
}
private async attemptSync() {
const info = await CapgoWatch.getInfo();
if (info.isReachable) {
await this.processSyncQueue();
}
}
private async processSyncQueue() {
if (this.isSyncing || this.syncQueue.length === 0) return;
this.isSyncing = true;
while (this.syncQueue.length > 0) {
const item = this.syncQueue.shift();
await CapgoWatch.transferUserInfo({ userInfo: item });
}
this.isSyncing = false;
}
private async getDataForSync(lastSyncTime: number): Promise<any[]> {
// Return data modified since lastSyncTime
// This would query your database/API
return [];
}
private handleWatchData(data: any) {
console.log('Received from watch:', data);
// Process and save data from watch
}
}

1. 적절한 전송 방식 선택하기

“1. 적절한 전송 방식 선택하기” 섹션
메서드사용 사례전송 방식
sendMessage대화형, 실시간성즉시 실행 (연결 불가 시 실패)
updateApplicationContext현재 앱 상태최신 값만 전달
transferUserInfo중요 데이터큐에 저장, 전달 보장

2. 오프라인 상황 대응

“2. 오프라인 상황 대응” 섹션
async function smartSend(data: any, important: boolean) {
const info = await CapgoWatch.getInfo();
if (info.isReachable) {
// Send immediately
await CapgoWatch.sendMessage({ data });
} else if (important) {
// Queue for later
await CapgoWatch.transferUserInfo({ userInfo: data });
} else {
// Update context (will be available when watch opens app)
await CapgoWatch.updateApplicationContext({ context: data });
}
}

3. 데이터 크기 최소화

3. 데이터 크기 최소화
// Bad - sending large objects
connector.sendMessage(["user": fullUserObject])
// Good - send only what's needed
connector.sendMessage([
"userId": user.id,
"name": user.displayName,
"avatarUrl": user.avatarUrl
])

4. 햅틱 적절히 사용하기

4. 햅틱 적절히 사용하기
import WatchKit
// Success feedback
WKInterfaceDevice.current().play(.success)
// Failure feedback
WKInterfaceDevice.current().play(.failure)
// Navigation feedback
WKInterfaceDevice.current().play(.click)
// Notification
WKInterfaceDevice.current().play(.notification)

5. Watch에서 캐싱하기

5. Watch에서 캐싱하기
class DataCache {
private let defaults = UserDefaults.standard
func save(_ data: [String: Any], forKey key: String) {
if let encoded = try? JSONSerialization.data(withJSONObject: data) {
defaults.set(encoded, forKey: key)
}
}
func load(forKey key: String) -> [String: Any]? {
guard let data = defaults.data(forKey: key),
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return nil
}
return dict
}
}

Examples &#x26; Use Cases에서 계속하기

“Examples &#x26; Use Cases에서 계속하기” 섹션

사용 중이라면 예제 및 활용 사례 네이티브 플러그인 작업을 계획하려면, 이를 다음 항목과 연결하세요: @capgo/capacitor-watch 사용하기 @capgo/capacitor-watch를 사용하여 Native Build 기능을 구현하는 방법 Capgo 플러그인 디렉토리 Capgo Plugin Directory의 제품 워크플로우에 맞춰, Capgo의 Capacitor 플러그인 Capgo의 Capacitor 플러그인 구현 상세 내용에 대해서는, 플러그인 추가 또는 업데이트 플러그인 추가 또는 업데이트 구현 상세 내용에 대해서는, Ionic Enterprise 플러그인 대안 Ionic Enterprise 플러그인 대안의 제품 워크플로우에 대해서는.