WidgetKitで初心者の躓きどころ


WidgetKitで初心者の躓きどころ

WidgetKitをみたら、いくつか躓きどころが合ったのでまとめてみました。

特にWidgetはメインのTargeと別なので
Targetが違うと色々参照できるようにする必要があります。

1. メインのターゲットを参照できない

参照したいファイルのTargetMembershipにチェックを入れればOK
※一つのファイルにいろんな依存物があると、それら全てをtチェックしないといけないので、各オブジェクト毎にファイルを分けるべし。

2. メインのTargetとは別でWidgetのTargetを作成するのでLibraryが参照できない。

Testなどと同様LibraryのTargetも追加する必要がある。

Cocoa Podsの場合

podファイルのWidgetのTarget内にinherit! :search_pathsを指定OK

  target 'SampleWidgetExtension' do
    inherit! :search_paths
  end

SwiftPMの場合

プロジェクトのWidgetのTargetのBuild Phasesで必要なLibraryを連携したらOK

3. DB内のデータが参照できない。

CoreDataやRealmを使っている場合
メインアプリのデータを参照するにはAppGroupで同じDBファイルを参照できるようにする必要がある。

AppGroupの設定

アプリの Target を選択し、Signing & Capabilities タブで、App Groups を追加します。
App Groupsの項目が無い場合は左上の+ボタンでダイアログの中からApp Groupsを選択。
新しいGroupを作成して下の更新ボタンをクリック。

※注意
Build Configurationが複数ある場合はそれぞれ必要なBuild ConfigurationでAppGroupの設定が必要

CoreDataの場合

NSPersistentStoreDescriptionのurlにAppGroup名を使って初期化します。


        let container = NSPersistentContainer(name: TodoListStore.containerName)
        container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.SampleWidget")!.appendingPathComponent("Sample.sqlite"))]
        container.loadPersistentStores(completionHandler: { (description, error) in
            if let error = error as NSError? {
                print("Unresolved error \(error), \(error.userInfo)")
            }
        })
        print(container.persistentStoreDescriptions.first?.url ?? "nil.splite")
        return container

Realmの場合

Realmインスタンスを作成する時に同じDBを参照するためにConfigurationのfileURLにAppGroupのURLを設定します。

        let realm = try! Realm()

このようにインスタンスを作っている箇所を下記に変更

        var config = Realm.Configuration()
        let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.SampleWidget")!
        config.fileURL = url.appendingPathComponent("db.realm")
        let realm = try! Realm(configuration: config)

URLから取得して画像を表示する場合は非同期では動かない。

Widgetに置いてURL画像の表示をする場合非同期処理は使えない。
Widgetでは同期的に取得するべし。

  • 非同期処理の場合

            DispatchQueue.global().async {
                let data = try? Data(contentsOf: imageURL)
                DispatchQueue.main.async {
                    self.downloadData = data
                }
            }
  • 同期処理の場合
            let data = try? Data(contentsOf: imageURL)
            self.downloadData = data

最後に

WidgetはSwiftUIでの実装が必要なので、SwiftUIの勉強も兼ねてチャレンジしてみました。
まだ触り始めたばかりなので、また躓き所が出てきたら追記します。

参考