LoginSignup
0
0

Apple Watch用メモアプリを作ってみた

Posted at

はじめに

せっかくMac mini M2を買ったのでアプリを作ってみようかと思いました
WatchOS6からWatch単体アプリを作れるようになったということで、Apple Watch単体アプリを作ってみました

アプリ内容

Apple Watchにはメモ帳アプリがなかったのでメモ帳アプリを作りました
最初は複数のメモを管理できるような(いわゆる普通の?iPhoneのメモアプリ的な)ものを考えたのですが、Apple Watchの小さい画面でたくさんメモ取らないだろうと思い、ブキーボード的なちょっと書いてすぐ消すようなメモ用紙的なアプリを作ることにしました

開発環境

  • Mac mini M2
  • MacOS Sonoma 14.4.1
  • Xcode 15.3 (15E204a)
  • WatchOS 10

アプリのコード

今回作成したアプリはGithubに置きました
全体のコードはこちらを参照ください

Xcodeプロジェクトの作成

Xcodeを起動し新規プロジェクト作成からWatchOS -> Appを選びます
20240409-1.png
次の画面でWatch-only Appを選んでプロジェクトを作成します
20240409-2.png

Widget Extensionの追加

Xcodeのメニュー -> File -> New -> TargetからWatchOSのWidget Extensionを追加します
Product Nameはプロジェクトの中でExtensionとわかりやすい名前をつけば良いかと思います
20230409-3.png

App Groupsの作成

プロジェクトを選択してTargetからWatchOSアプリを選択します
Signing & Capabilitiesのタブを選択し、+ CapabilityをクリックしApp Groupsを追加します
20240409-4.png
App Groupsのセクションから、「+」をクリックしてconteinerを追加します
この時決めたコンテナーIDを使ってアプリとウィジェット間でデータをやり取りすることになります
20240409-5.png

これと同様の操作を先ほど追加したWidget Extensionでも行います
この時App Groupsを追加した際のコンテナーIDはWatchOSアプリ側で作成したconteinerと同じコンテナーIDにします
20240409-6.png

メモデータの格納

メモデータの格納はUserDefaultsを使っています
今回は1つだけのメモを管理するので、「memo」というキーでUserDefaultsを作ります
またWidgetKitでウィジェットも作るのでApp GroupsのコンテナーIDを指定するようにしています

UserDefaultsの定義
import Foundation

class MemoData: ObservableObject {
    @Published var memo: String {
        willSet {
            print("willset")
        }
        didSet {
            UserDefaults(suiteName: "group.com.watch.memo")?.set(memo, forKey: "memo")
            print("didset")
        }
    }

    // 初期化
    init() {
        memo = UserDefaults(suiteName: "group.com.watch.memo")?.string(forKey: "memo") ?? " "
    }
}

メインView

WatchOSではTextEditorが使えないのでメイン画面にTextを配置し、NavigationStackの遷移先にTextFiledをおいてメモを編集する形式にしています
メイン画面にはNavigationStackとListの中にTextとメモ削除用のButtonを配置します
メモを削除する処理もこのViewに書いています

NavigationStack -> NavigationLinkのdestinationに遷移したいView(ここではEditVew)を指定し、渡したい値(value -> Textに表示させている文字列)を引数として指定します
Textの.onAppear()処理でUserDefaultsの値を表示させます

メインView
NavigationStack {
    List {
        NavigationLink(destination: EditView(value: "\(value)")) {
            Text(value)
                .onAppear() {
                    guard let defaultMemo = UserDefaults(suiteName: "group.com.watch.memo")?.object(forKey: "memo")
                    else{return}
                    // memoがNullの場合、値に空文字を挿入する
                    value = defaultMemo as! String
                    if value == "" {
                        value = " "
                    }
                }
        }

        Button(action: {erase()}) {
            Label("erase", systemImage: "trash")
        }
        // 背景色を透明にする
        .listRowBackground(Color.clear)
    }
}
メモの削除処理
private func erase() {
    let eraseMemo = " "
    self.memoData.memo = eraseMemo
    self.value = eraseMemo
    // Widgetを更新
    WidgetCenter.shared.reloadAllTimelines()
}

編集View

メモの編集はメインViewから遷移させたViewで行うようにしました
メモの編集 -> UserDefaultsのアップデートを行なっています

UserDefaultsの定義
func setMemoData(){
        guard let memoData = UserDefaults(suiteName: "group.com.watch.memo")?.object(forKey: "memo")
        else{return}
        self.value = memoData as! String
}
編集用View
var body: some View {
    TextField("Input Text", text: $value)
        .onAppear {
            if $value.wrappedValue == " " {
                value.self = ""
            }
        }
        .onSubmit {
            update(memo: value)
        }
}
メモの編集処理
    private func update(memo: String) {
        self.memoData.memo = memo
        setMemoData()
        // Widgetを更新
        WidgetCenter.shared.reloadAllTimelines()
    }

WidgetExtensionの定義

WidgetExtensionは追加されたテンプレートを割と素直に使っています
ただApple Watchに設定するウォッチフェイスによって表示されるWidgetが異なるので分岐を作って切り替えられるようにしました

Widget分岐
struct WatchMemoWidgetEntryView : View {
    @Environment(\.widgetFamily) var widgetFamily
    @State var entry: Provider.Entry

    var body: some View {
        switch widgetFamily {
            case .accessoryCorner:
                CornerComplication(memo: entry.memo)
            case .accessoryCircular:
                CircularComplication(memo: entry.memo)
            case .accessoryInline:
                InlineComplication(memo: entry.memo)
            case .accessoryRectangular:
                RectangularComplication(memo: entry.memo)
            @unknown default:
                Text("No Complication")
        }
    }
}
コンプリケーションに応じたView
struct InlineComplication : View {
    @State var memo: String
    var body: some View {
        HStack {
            Image(systemName:"note.text")
            Text(": \(memo)")
                .lineLimit(1)
        }
        .containerBackground(.fill.tertiary, for: .widget)
    }
}

struct CircularComplication : View {
    @Environment(\.showsWidgetLabel) var showsWidgetLabel
    @State var memo: String
    // 表示される場所によってアイコンイメージ的なものが一緒に表示される時と
    // メモの内容のみが表示される時を分けています
    var body: some View {
        if showsWidgetLabel {
            Image(systemName:"note.text")
                .widgetLabel {
                    Text(memo)
                }
                .containerBackground(.fill.tertiary, for: .widget)
        } else {
            Text(memo)
                .lineLimit(1)
                .containerBackground(.fill.tertiary, for: .widget)
        }
    }
}

struct CornerComplication : View {
    @State var memo: String
    var body: some View {
        Image(systemName:"note.text")
            .widgetLabel {
                Text(memo)
                    .lineLimit(1)
                    .containerBackground(.fill.tertiary, for: .widget)
            }
    }
}

struct RectangularComplication : View {
    @State var memo: String
    var body: some View {
        VStack {
            HStack {
                Image(systemName:"note.text")
                    .font(.system(size: 10.0))
                Spacer()
            }
            Text(memo)
                .lineLimit(2)
                .widgetAccentable()
        }
        .containerBackground(.fill.tertiary, for: .widget)
    }
}

おわりに

現状Apple Watchで使えるSwiftUIではTextEditorが使えないので改行を入力することができないようです
Apple Watchはそもそも画面が小さいので複雑な画面構成は向いていないのでいかにシンプルに作るかが重要ですね
Watch OnlyアプリがiPadのSwiftPlaygoundで作れるようになるといいなと思いました

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0