chakokuのブログ(rev4)

テック・コミック・ごくまれにチャリ

SwiftUIでForEachを使ってarrayをループ処理したい。。怒られる

SwiftUIを使って、Echonet Lite 機器をスキャンして、見つけ次第、リストに追加する実装を試みている。
以下が作成中のアプリの画面。scanの行の下に、検知デバイスをList形式で出したい。

が、、Stringを要素にもつArrayに対してForEachを使ってText()で追加しようとすると、ArrayはIdentifiableでないからダメと怒られる。ForEachで回す対象のデータ型はIDでアクセスできないとダメらしい(Identifiableプロトコルに準拠している必要があると理解)。Arrayもindexでアクセスしていると思うけど。。initを実行した時の要素数と、増減した時の要素数が違うから、配列のIndexは使えないということか??

List() {
     ForEach( 0..<deviceModel.data.count ){ index  in
           Text(deviceModel.data[index])
      }
}

以下がエラーメッセージ

ForEach<Range<Int>, Int, Text> count (4) != its initial count (1). 
`ForEach(_:content:)` should only be used for *constant* data. 
Instead conform data to `Identifiable` or use `ForEach(_:id:content:)` and 
provide an explicit `id`!

KeyPathを引数に加えてForEachのInit処理のエラーを回避させる方法が紹介されているけど、 \.self 表記でエラーが出る。(Swiftが古いから??)
もう一つの解法として、Arrayを使うのをやめて、Identifableな型を定義してそれを使って実装する記事が出ている。自分にはこっちの方が理解しやすい。だから、、新しい型を定義して、ID付きのデバイスリストを作って、ForEachに渡そうと思う。ただし、、新たに作った構造体を要素に持つ配列の宣言等、どうSwiftで書いたらいいのか、ちょっとすぐにわからないので、今日はここまで。。

■追記
先人のブログを参考に、デバイス管理配列をIdentifiableにして、idも付けた。ソースは以下

import Foundation

struct Device: Identifiable {
    var id: Int
    var name: String
}

class DeviceModel: ObservableObject {
    @Published var data:[Device] = [Device(id:0, name:"None")]
    var maxIndex:Int = 1
    var deviceService:DeviceService = DeviceService()
    init(){
        self.data[0].name = "ready to scan"
    }

モデル側はこのように改修して、ViewでForEachするとエラーが解消された。donguriさんに感謝。iOSアプリの表示は以下。ちょっとまだ細かい所はバグっているが、だいたいこんな感じで機器一覧が出たらいいなと。

残る作業は、UDPマルチキャストしてデバイスを探すコードの実装。

■ForEachのエラー回避方法
SwiftUIでいこう! - List,identifiable。|donguri|note
donguriさんによるIdentifiable化する解消案