【SwiftUI】MapでannotationContentにMapAnnotationを使用すると紫色の警告が出たので対処法を調べた

2022.12.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

先日、SwiftUIでマップを使用したアプリを作成しました。

【iOS】サンタが来るのを子どもがとても楽しみにしていたので、サンタが近づいている感を体感できるアプリを作ってみた

最初はSwiftUIのMapを使用して作成を進めていたのですが、表題通りMapのannotationContentにMapAnnotationを使用すると紫色の警告が表示されました。この時はMKMapViewを使用することで警告を回避したのですが、せっかくなのでこの紫色の警告を対処する方法を調べてみることにしました。

環境

  • Xcode 14.2

エラー内容

Publishing changes from within view updates is not allowed, this will cause undefined behavior.

ビュー更新内からのパブリッシングの変更は許可されていません。これにより、未定義の動作が発生します。

この警告はXcode 14以降から発生するのようで、ネットを調べてみると多くの方がこの警告に頭を悩ませているようでした。

補足

今回は表題の現象時のみの紫色の警告の対応について調べました。他の事象での対応の参考にはならないと思われます。

サンプルコード

今回は下記のコードを例に進めていきます。

このコードでXcode 14.2で実行すると、紫色の警告が表示されます。(Xcode 14.1でも同様に発生します。)

import SwiftUI
import MapKit

struct ContentView: View {

    /// 広島駅周辺
    @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 34.39783518267202,
                                                                                  longitude: 132.47571119130762),
                                                   span: MKCoordinateSpan(latitudeDelta: 0.01,
                                                                          longitudeDelta: 0.01))

    let hiroshimaStationAnnotation = AnnotationItem(title: "広島駅",
                                                    coordinate: CLLocationCoordinate2D(
                                                        latitude: 34.39783518267202,
                                                        longitude: 132.47571119130762))

    var body: some View {
        Map(coordinateRegion: $region,
            annotationItems: [hiroshimaStationAnnotation]) { item in
            MapAnnotation(coordinate: item.coordinate) {
                Image(systemName: "house")
            }
        }
    }
}

struct AnnotationItem: Identifiable {
    let id = UUID()
    let title: String
    let coordinate: CLLocationCoordinate2D
}

対応策1

こちらのstack overflowの記事内にも同じ問題に悩まされている方がいました。

この記事内の見解ではライブラリ自体のバグである可能性が高いとコメントしている方もいました。

この記事の解決策としては、MapAnnotationの代わりにMapMarkerを使用すると警告は消えるとのことです。

var body: some View {
    Map(coordinateRegion: $region,
        annotationItems: [hiroshimaStationAnnotation]) { item in
        // MapAnnotation(coordinate: item.coordinate) {
        //     Image(systemName: "house")
        // }
        // MapAnnotationの代わりに使用
        MapMarker(coordinate: item.coordinate)
    }
}

確かに警告は消えましたが、そもそもアノテーションの表示形式が変わっている為、元々やりたかったアノテーションに何かしらの画像を使用する方法を出来ません。

対応策2

こちらの記事内ではMapにバインディング変数として渡しているcoordinateRegionが悪さをしている調査結果がありました。

これは、自分たちで解決できないという点で何らかの問題に違いないとしか言​​えません。

こんな諦めのコメントも残されていました、、。

このcoordinateRegionが悪さをしているというこの記事を参考に対策案を考えました。

coordinateRegionに渡していた@State変数を定数に変更して、.constantで渡す方法です。

struct ContentView: View {

    // 定数に変更
    private let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 34.39783518267202,
                                                                           longitude: 132.47571119130762),
                                            span: MKCoordinateSpan(latitudeDelta: 0.01,
                                                                   longitudeDelta: 0.01))

    let hiroshimaStationAnnotation = AnnotationItem(title: "広島駅",
                                                    coordinate: CLLocationCoordinate2D(
                                                        latitude: 34.39783518267202,
                                                        longitude: 132.47571119130762))

    var body: some View {
        // バインディングではなくconstantの値を渡す
        Map(coordinateRegion: .constant(region),
            annotationItems: [hiroshimaStationAnnotation]) { item in
            MapAnnotation(coordinate: item.coordinate) {
                Image(systemName: "house")
            }
        }
    }
}

MapAnnotationを使用しても紫色の警告が出なくなりました。

ですが、この方法だとcoordinateRegion.constantになっている為、何らかのアクションによってcoordinateRegionを変更するといった処理等を行いたい時は厳しそうですね、、

おわりに

紫色の警告は消せたもののどれも完璧な対処法にはならず、最終的にはMKMapViewUIViewRepresentableでラップしたものを使用するという方法で紫色の警告を逃れました。

Xcodeのアップデートで根本的に解決されたら嬉しいですね、、。

もっと良い解決策がありましたら教えていただけると嬉しいです。

参考