- try
- エラーの enum をカスタムしてコードを簡潔にする
- CUICatalog: Invalid asset name supplied: ”
- The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
- rotationEffectを使うときに 中心がずれる問題
- Cannot use mutating member on immutable value: ‘self’ is immutable
- Call to main actor-isolated instance method ‘test()’ in a synchronous nonisolated context
- エラーの回避策と理由
try
| 書き方 | 例外が出たときの挙動 | 典型的な用途 |
|---|---|---|
try | throw されたら そのまま上位に伝播(throw) | 呼び出し元で try or do-catch する場合 |
try? | throw されたら nilを返して処理を続行 | エラーを無視して optional で扱いたい場合 |
try! | throw されたら クラッシュ(fatal error) | 成功が100%確実なときだけ使う(非推奨) |
関数呼び出し元にエラーを投げる時は引数の後ろに throws を宣言する。
|
1 2 3 |
private func getValidToken() async throws -> String { return try refreshToken() } |
この関数自体が throws を宣言している ため、
中で try を使っても 自動的に「上位にエラーを投げる」 挙動になります。
|
1 2 3 4 |
func refreshToken() async throws -> String { let result = try JSONDecoder().decode(TokenResponse.self, from: data) return result.token } |
ここで decode が失敗(例:JSONが壊れている)したら、refreshToken() が throw して中断 → 呼び出し元 (getValidToken()) にエラーが伝わります。
その場でエラー処理をする場合
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
do { let result = try JSONDecoder().decode(TokenResponse.self, from: data) print("トークン取得成功: \(result.token)") } catch { print("デコードエラー: \(error)") throw error // ここで再スローも可能 } //例2 do { if let _ = UserDefaults.standard.string(forKey: "test"){ print("ok") } else { throw NSError(domain: "test", code: 0, userInfo: nil) } } catch { print("sss") print(error) } //結果 sss Error Domain=test Code=0 "(null)" |
🔸補足:SwiftUIでの使い方のコツ
非同期 (async/await) と throws を組み合わせるときは、
呼び出し側で Task の中に do-catch を書くのが定石です👇
|
1 2 3 4 5 6 7 8 |
Task { do { let token = try await TokenManager.shared.getValidToken() print("取得したトークン: \(token)") } catch { print("トークン取得に失敗しました: \(error.localizedDescription)") } } |
throwはdoの中ならどこでも書ける、テストでエラーの条件分岐とかの前に書いてとりあえずエラーだけのテストも可能
エラーの enum をカスタムしてコードを簡潔にする
ここでは LoadError となっていますがただの enum 名です。
① LoadError にメッセージを持たせる
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
enum LoadError: Error { case noData case decodeFailed var message: String { switch self { case .noData: return "保存データが存在しないため初期化しました。" case .decodeFailed: return "保存データの読み込みに失敗したため初期化しました。" } } } |
② catch が超シンプルになる
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
do { self.attendance = try repo.load() } catch let error as LoadError { // エラーメッセージは enum が持つ self.errorMessage = error.message // デフォルト値に初期化 self.attendance = AttendanceData( leaveType: [:], behindTime: [:], paidDays: 0 ) } catch { // 想定外 self.errorMessage = "予期せぬエラーが発生しました。" } |
🟦 シンプルバージョン(とにかく短い)
エラー区別しないならこれでも良い:
|
1 2 3 4 5 6 |
do { self.attendance = try repo.load() } catch { self.attendance = AttendanceData(leaveType: [:], behindTime: [:], paidDays: 0) self.errorMessage = "読み込みに失敗したため初期化しました。" } |
🟣 応用:LoadError を localizedDescription に統合する
Swift の Error には localizedDescription を持たせられます。
|
1 2 3 4 5 6 7 8 9 10 |
extension AttendanceRepository.LoadError: LocalizedError { var errorDescription: String? { switch self { case .noData: return "保存データが存在しません。" case .decodeFailed: return "データの読み込みに失敗しました。" } } } |
すると catch 側はこう書ける:
|
1 2 3 |
catch { errorMessage = error.localizedDescription } |
✨ まとめ
LoadError の扱い方は以下のどれでもOKですが、
SwiftUI では enum がメッセージを持つパターンが一番綺麗でメンテしやすいです。
| パターン | 特徴 |
|---|---|
型スイッチ catch let error as LoadError | 条件ごとの細かい処理ができる |
| LoadError に message プロパティを持たせる | ViewModel がスッキリ、最もオススメ |
| LocalizedError にする | catch 側で localizedDescription だけ扱えば良い |
| エラーまとめてキャッチする | 小規模アプリ向け、簡潔 |
CUICatalog: Invalid asset name supplied: ”
このエラーが出る時は image表示View関係に “” 空文字を入れているため。
回避するには nil または初めに何かの画像を入れておく無地の画像とか。
The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
意味は「Swift の型推論エンジンが複雑すぎて処理できなくなった」
主な原因
- 1つの ViewBuilder 内に大量のネストや修飾子(.modifier, .padding, .background など)がある
- ForEach や if/else の入れ子が深い
- クロージャ内の型推論が複雑になりすぎている
- 長大な式を一気に書いている
- Viewにオプショナル型を使用している
例
|
1 |
Text(selectedData.time) // ← selectedDataは Data? 型(オプショナル) |
・selectedData は @State private var selectedData: Data? なので nil の可能性がある
・それを .time や .level に直接アクセスするとコンパイラが「これOptionalじゃん、どうするの?」と悩みまくります。
・さらに .font() .foregroundColor() .background() と複数修飾子を連ねるので、型推論が間に合わなくなり、例のエラーが出ます。
よくある解決策
サブビューに切り出す
大きな VStack / HStack / ZStack の中身を、小さな View に分ける。
|
1 2 3 4 5 6 7 |
var body: some View { VStack { HeaderView() ContentView() FooterView() } } |
計算を事前に変数に代入
長い式をそのまま渡すのではなく、先にローカル変数にしてから渡す。
|
1 2 3 4 |
let filtered = items.filter { $0.isActive } ForEach(filtered) { item in Text(item.name) } |
型を明示する
SwiftUI は型推論に頼りがちなので、ジェネリクスやクロージャで型を明示すると軽くなることがある。
ViewBuilder の入れ子を減らす
たとえば .if の代わりに Group や AnyView を使うことも手。
if let や map を使って、Optional を安全に取り出す。
rotationEffectを使うときに 中心がずれる問題
✅ 原因
- SwiftUI の
rotationEffectは View の座標空間の原点(デフォルトでは View の中心)を基準に回転します。 - ただし、SVG や Image の描画自体に余白があったり、Canvas のサイズが意図通りでない場合、見た目の中心がずれます。
✅ 対応策
1. 回転のアンカーを明示
|
1 2 3 4 |
Image(systemName: "arrow.up") .resizable() .frame(width: 24, height: 24) .rotationEffect(.degrees(45), anchor: .center) // anchorを中心に指定 |
デフォルトは .center ですが、.topLeading などにすると回転軸を変えられます。
SVG を Image にしている場合、元のSVGの余白で中心がずれることが多いので要注意。
2. GeometryReader で正確に中心を計算
|
1 2 3 4 5 6 7 |
GeometryReader { geo in Image("icon") .resizable() .frame(width: geo.size.width, height: geo.size.height) .rotationEffect(.degrees(45), anchor: .center) } .frame(width: 24, height: 24) |
3. SVG の余白を除去
- 元SVGに余白や透明部分がある場合、SwiftUI の
rotationEffectは バウンディングボックス中心を基準に回転するのでズレます。 - Illustrator や Figma などで アートボードの中心にパスを配置して余白を取ると回転が正しくなる
4. オフセットで微調整
どうしてもズレる場合は offset(x:y:) で微調整も可能。
|
1 2 3 4 5 |
Image("icon") .resizable() .frame(width: 24, height: 24) .rotationEffect(.degrees(45)) .offset(x: -2, y: 1) // 微調整 |
5. padding等を使っている場合は外に出す
|
1 2 3 4 5 |
Image("WindDirection") .resizable() .frame(width: 50.0, height: 50.0) .padding(.trailing, 20) .rotationEffect(.degrees(200)) |
padding は View のサイズを拡張するので、回転の中心は padding を含めた矩形の中心になります。
そのため、見た目上は回転軸がずれているように見えるのです。
対策1:padding を外に出す
|
1 2 3 4 5 6 7 8 |
HStack { Spacer() Image("WindDirection") .resizable() .frame(width: 50, height: 50) .rotationEffect(.degrees(200)) .padding(.trailing, 20) // rotation の後に padding } |
対策2:anchor を指定
|
1 |
.rotationEffect(.degrees(200), anchor: .center) |
対策3:ZStack で中心を揃える
|
1 2 3 4 5 6 7 |
ZStack { Image("WindDirection") .resizable() .frame(width: 50, height: 50) .rotationEffect(.degrees(200)) } .frame(width: 50, height: 50) // ZStackの中心を正しく設定 |
Cannot use mutating member on immutable value: ‘self’ is immutable
View構造体内で@State等がついていない変数の値をbody内で変更しようとすると出ます。
以下は解説
エラーメッセージ Cannot use mutating member on immutable value: 'self' is immutable は、SwiftUIの View 構造体の基本的な性質と、プロパティの定義方法に起因しています。
エラーの原因
SwiftUIの View はイミュータブル(不変)
SwiftUIの View は、パフォーマンスと予測可能性のために、基本的に struct (構造体) として定義され、そのインスタンス(self)は**イミュータブル(不変)**として扱われます。
bodyプロパティ内でビューを構築する際、selfの状態を直接変更する操作(ミューテーション)は許可されていません。
ビューの body の役割
body プロパティの役割は、現在のデータ(状態)に基づいてビューの階層を定義し、返すことです。データの変更(状態管理)を行う場所ではありません。
解決策1: @State を使用する (ビュー専用の一時的な状態の場合)
favorites 配列がこのビュー内でのみ必要とされる一時的なデータであれば、@State を使用してミューテーションを許可します。
解決策2: データを body の前に準備する(推奨)
ForEach の内部でデータを処理し、それを別の配列に追加する行為は、ビューの構築フェーズで行うべきではない副作用です。データをピッカーで使用するために必要な形に変換する場合は、body が評価される前に計算する計算プロパティを使用するのが最もクリーンな方法です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
struct FavoriteSheet: View { @EnvironmentObject var SV: SharedVariables @Environment(\.presentationMode) var presentationMode @State var selected_name: String = "" // MARK: - 計算プロパティ: データを body の外部で計算し、結果を返す var processedFavorites: [String] { return SV.favorite_list.compactMap { name in let parts = name.split(separator: ",") // カンマ区切りが存在し、2つ目の要素がある場合のみStringとして返す return parts.count > 1 ? String(parts[1]) : nil } } var body: some View { VStack { // 計算プロパティを使用して、データが存在するかを確認 if processedFavorites.isEmpty { Text("お気に入りの場所がありません") } else { // MARK: - ピッカーにデータを使用 Picker("", selection: $selected_name) { ForEach(processedFavorites, id: \.self) { favoriteName in Text(favoriteName).tag(favoriteName) // tagを付けることで、選択値が String になる } } .pickerStyle(WheelPickerStyle()) Button(action: { self.presentationMode.wrappedValue.dismiss() }){ Text("OK") } // ... } } // ピッカーの初期値を設定したい場合は onAppear で行う .onAppear { if let firstFavorite = processedFavorites.first { selected_name = firstFavorite } } } } |
Call to main actor-isolated instance method ‘test()’ in a synchronous nonisolated context
該当コード
|
1 2 3 4 5 6 7 8 9 |
@MainActor class test3: ObservableObject { func test() { print("test3") } deinit { test() } } |
このエラーが出る理由は、deinit が実行されるコンテキスト(実行環境)と、test() メソッドの実行コンテキストが衝突しているためです。
1. deinit の実行コンテキスト
Swiftの deinit(デイニシャライザ)は、オブジェクトがメモリから解放されるときに呼び出される処理です。この解放処理は、どのスレッド(またはアクター)で行われるか保証されていません。多くの場合、オブジェクトへの最後の参照が解放されたその場で同期的に実行されます。
つまり、deinit は **非アクター分離(nonisolated)**の、同期的なコンテキストで実行されます。
2. @MainActor の役割
@MainActor は、そのクラスやメソッドを**メインスレッド(メインアクター)**でのみ実行するように強制するマークです。
test3クラス全体に@MainActorが付いているため、そのインスタンスメソッドであるtest()も自動的にメインアクターに分離されます。
3. エラーの原因 💥
エラーメッセージの通り、同期的な非アクター分離のコンテキスト(deinit)の中から、メインアクター分離のメソッド(test())を呼び出そうとしています。
Swiftのコンパイラは、deinit がメインスレッド以外のスレッドで実行されている可能性があるため、メインスレッドへの切り替え(非同期処理)なしに test() を同期的に呼び出すことを禁止しています。これは、スレッドのデッドロックや競合状態を防ぐための安全策です。
エラーの回避策と理由
1. @MainActor を取る
|
1 2 3 4 5 6 7 8 |
class test3: ObservableObject { // @MainActor を削除 func test() { print("test3") } deinit { test() // OK: test() は非アクター分離になる } } |
@MainActor を削除すると、test() はどのスレッドからでも呼び出し可能な非アクター分離のメソッドになります。したがって、deinit から同期的に呼び出してもコンテキストの衝突は発生せず、エラーは消えます。2. 処理を直接書く
2. 処理を直接書く
|
1 2 3 4 5 6 7 8 9 |
@MainActor class test3: ObservableObject { func test() { print("test3") } deinit { print("test3") // OK: deinit 内の処理は非アクター分離として扱われる } } |
deinit 内に直接処理を書いた場合、そのクロージャ内のコードは、deinit が実行されている非アクター分離のコンテキストに属します。そのため、アクター分離されたメソッドの呼び出しは発生せず、エラーは出ません。
3. 正しい修正方法(非同期呼び出し)
もし deinit の中でどうしてもメインアクターで実行したい処理がある場合は、タスクをメインアクターに切り替えて非同期に実行する必要があります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@MainActor class test3: ObservableObject { func test() { print("test3") } deinit { // Taskでメインアクターに切り替えて非同期に実行 Task { @MainActor in self.test() } // 注意: deinitは待たずに終了するため、このTaskが完了する保証はない } } |
ただし、deinit はオブジェクトのライフサイクルの終了時に行うクリーンアップ処理(ファイルの削除、通知の解除など)のために使用されるべきであり、非同期処理をキックする場所としては不適切です。通常、deinit 内の処理はシンプルで同期的なものが望ましく、アクター分離が必要な処理は deinit の外で行うことが推奨されます。
Adding ‘UIKitToolbar’ as a subview of UIHostingController.view is not supported and may result in a broken view hierarchy. Add your view above UIHostingController.view in a common superview or insert it into your SwiftUI content in a UIViewRepresentable instead.
これは iOS 17 以降(特に iOS 26 シミュレータ)で厳格化された SwiftUI + UIKit の階層チェック による警告です。
結論から言うと👇
🔥 あなたの SwiftUI 画面のどこかで UIKit の UIToolbar を「直接」SwiftUI の下層に入れようとしている状態
(=UIKit view を UIHostingController.view の“子”として追加しようとしている)
これは 公式に禁止されており、iOS 26 でさらに検査が強くなった ため出ています。
✔️ 具体的にどういう時に出る?
以下のいずれかをやっていると 100% 出ます:
① SwiftUI の上に .toolbar を使わず UIKit の UIToolbar() を手動で設置している
(特に UIHostingController(rootView:) の view の下に追加した時)
NG 例(UIKit 側)👇
|
1 2 |
let toolbar = UIToolbar() hostingController.view.addSubview(toolbar) // ❌ これが原因 |
② SwiftUI の view が UIKit のカスタム view に差し込まれ、その中で UIToolbar を勝手に追加している
例えば:
- カスタムキーボード
- キーボードのアクセサリビュー
- 入力フォームの上にツールバーを無理やり追加
- SwiftUI 内で
UIViewControllerRepresentableを使い、その中で toolbar を addSubview してる
③ SwiftUI の .sheet や .popover 内に UIKit 操作で toolbar を追加している
(iOS 26 からポップオーバー階層も厳しくチェックされます)
✔️ SwiftUI は「自分の階層に UIKit を直接ネストする」のが禁止
SwiftUI の View 階層は UIHostingController で管理されています。
|
1 2 |
UIHostingController └─ SwiftUI root view |
この SwiftUI の root view の“下”に UIKit view を追加するのが NG。
SwiftUI は内部制御があるので、 UIKit view が割り込むと
レイアウト破壊やイベント伝播バグを引き起こすためです。
🔥 iOS 26 の新しい仕様(重要)
以前は「まあ大丈夫」だったケースが、
iOS 26 から 明確に警告 → 将来的にはエラー化 される傾向があります。
なので今回の警告は “将来のクラッシュ予告” だと思っていいです。

コメント