【Xcode】自動補完 (Auto Completion) の使い方 - trailing closure

ぶっちゃけ、この状態でつらい。

Xcode auto complete trailing closure


公式ドキュメントが見つからなかったので、少しやってみました。

そもそもは、クロージャーをスムーズにに展開したかったので。

 

🎯 結果

いきなり結果です。

説明しづらいので動画です。



一時停止や再生速度を落とすといいかもしれません。

 

🎯 覚えておくべきキー操作

以下、5つだけです。

一般的な操作の順序です。

動画を見ながら確認するといいと思います。


1. 関数の選択 - ↑ ( up ) / ↓ ( down ) 
2. 関数グループの開閉 - → ( right ) /  ← ( left ) 
3. 関数の決定 - ↩ ( return ) 
4. フォーカスの移動 - ⇥ ( tab ) 
5. クロージャーの展開 - ↩ ( return )

あくまで、


- 上下左右のカーソルキー
- タブキー
- リターンキー(エンターキー)

だけで進んでいきます。

その他のキーを押すと、Auto-Completion のモードから外れます。

注意しておくのは、

「青いフォーカス」を意識して選択決定のリターンキー

を押すことだけです。

 

🎯 まとめ

初心者の、

機能が豊富なIDEの操作の習得はつらい。

👉 [Accepted] SE-0279: Multiple Trailing Closures - Evolution / Announcements - Swift Forums hatena-bookmark
👉 xcode - Is there a way to simplify typing Swift trailing closure? - Stack Overflow hatena-bookmark



SwiftData Fatal error: failed to find a currently active container 📦

SwiftData Fatal error: failed to find a currently active container

まったく動かない。画面が真っ白。再起動でも同じ。

エラーメッセージ。

SwiftData/ModelContainer.swift:159: Fatal error: failed to find a currently active container for Task
Failed to find any currently loaded container for Task)

コンテナを渡す前に初期化すると問題が解決されるようです。


@main
struct MyApp: App {
  let modelContainer: ModelContainer
    
  init() {
    do {
      modelContainer = try ModelContainer(for: Item.self)
    } catch {
      fatalError("Could not initialize ModelContainer")
    }
  }
    
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
    .modelContainer(modelContainer)
  }
}

👉 SwiftData Fatal error: failed to find a currently active container | Apple Developer Forums hatena-bookmark

というかんじで、モデルコンテナの初期化位置を最上位にすると直りました。

モデルデータの変更後の整合性の問題でしょうか。

突然動かなくなるのでびっくりします。

 

📦 どこでモデルコンテナを生成するべきか

利用する View につければ、


struct ContentView: View {
  @State private var container = ModelContainer(...)

  var body: some Scene {
    RecipesList()
      .modelContainer(container)
  }
}

それ以下では @Environmentを使って問題なく利用できると思っていましたが。


struct RecipesList: View {
    @Environment(\.modelContext) private var modelContext

The environment’s modelContext property will be assigned a new context associated with this container. All implicit model context operations in this view, such as Query properties, will use the environment’s context.

Environment ののmodelContext プロパティには、このコンテナに関連付けられた新しいコンテキストが割り当てられます。Query プロパティなど、このビューのすべての暗黙のモデルコンテキスト操作は、Environment のコンテキストを使用します。

👉 modelContainer(_:) | Apple Developer Documentation hatena-bookmark

Modifier や Preview などあちこちで 生成していると、利用できる範囲が分かりづらくなるのは確か。



【SwiftUI】NavigationSplitView と TabView の切り替え判定 🔀

マルチプラットフォームアプリで、

Mac/iPad と iPhone/AppleTV でグループ分けしてそれなりに切り替える。


if prefersTabNavigation { // *
//if UIDevice.current.userInterfaceIdiom == .phone { // NG on macOS
//if UserInterface.prefersTabNavigation {
  TabView(selection: $screen) {
    ForEach(Screen.allCases) { screen in
      screen.destination
        .tag(screen as Screen?)
        .tabItem { screen.label }
    }
  }
} else {
  NavigationSplitView { 
    SidebarList(screen: $screen)
  } detail: {
    DetailColumn(screen: screen)
  }
}

判定の条件 (Bool) をどう書くか。

 

🔀 Apple 公式サンプル

WWDC23 でアナウンスの新機能ですか。

👉 Unleash the UIKit trait system - WWDC23 - Videos - Apple Developer hatena-bookmark

便利な SwiftUI Environment に UIKit UITrait をブリッジするという UITraitBridgedEnvironmentKey を使った方法。


// Create custom Environment
struct PrefersTabNavigationEnvironmentKey: EnvironmentKey {
  static var defaultValue: Bool = false
}

extension EnvironmentValues {
  var prefersTabNavigation: Bool {
    get { self[PrefersTabNavigationEnvironmentKey.self] }
    set { self[PrefersTabNavigationEnvironmentKey.self] = newValue }
  }
}

// Bridge UITrait read only
#if os(iOS)
extension PrefersTabNavigationEnvironmentKey: UITraitBridgedEnvironmentKey {
  static func read(from traitCollection: UITraitCollection) -> Bool {
    return traitCollection.userInterfaceIdiom == .phone || traitCollection.userInterfaceIdiom == .tv
  }

  static func write(to mutableTraits: inout UIMutableTraits, value: Bool) {
    // Do not write
  }
}
#endif

👉 sample-backyard-birds/Multiplatform/General/PrefersTabNavigationEnvironmentKey.swift at 1843d5655bf884b501e2889ad9862ec58978fdbe · apple/sample-backyard-birds hatena-bookmark


@Environment(\.prefersTabNavigation) private var prefersTabNavigation

👉 sample-backyard-birds/Multiplatform/ContentView.swift at 1843d5655bf884b501e2889ad9862ec58978fdbe · apple/sample-backyard-birds hatena-bookmark

高級な感じで相互読み書き可能な状態にできるのはいいけども。

 

🔀 その他

なんとなく試しながら書いてみました。

Constants な感じで。書き方はいくつかあるようです。

enum を使う。意見はいろいろありそう。


enum UserInterface { // as namespace
  static var prefersTabNavigation: Bool {
    #if os(iOS) //canImport(UIKit)
    let idiom = UIDevice.current.userInterfaceIdiom
    return idiom == .phone || idiom == .tv
    #else
    return false
    #endif
  }
}

フツーに struct で書く。


struct UserInterface {
  static var prefersTabNavigation: Bool {
    #if os(iOS) //canImport(UIKit)
    let idiom = UIDevice.current.userInterfaceIdiom
    return idiom == .phone || idiom == .tv
    #else
    return false
    #endif
  }
}

👉 `static let` in enum vs struct? - Using Swift - Swift Forums hatena-bookmark

呼び出し側。


if UserInterface.prefersTabNavigation {

 

🔀 まとめ


#if os(iOS)

ての、なんとなく

なるべくは使いたくないです。

Apple 公式サンプルは、新機能を強引に Read Only でサンプルコードに利用した感じに見えるけどもー。