先日、SwiftUIでマップを使用したアプリを作成しました。
最初は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
を変更するといった処理等を行いたい時は厳しそうですね、、
おわりに
紫色の警告は消せたもののどれも完璧な対処法にはならず、最終的にはMKMapView
をUIViewRepresentable
でラップしたものを使用するという方法で紫色の警告を逃れました。
Xcodeのアップデートで根本的に解決されたら嬉しいですね、、。
もっと良い解決策がありましたら教えていただけると嬉しいです。
参考
- init(coordinateRegion:interactionModes:showsUserLocation:userTrackingMode:annotationItems:annotationContent:)
- Using MapKit causes Publishing changes from within view updates is not allowed, this will cause undefined behavior
- Youtube - Publishing changes from within view updates is not allowed, this will cause undefined behaviour
- Xcode 14 “Publishing changes from within view updates is not allowed, this will cause undefined behavior”