SwiftUIとAppKitの相互利用

## 概要
- SwiftUIからAppKitのViewの利用、また逆にAppKitからSwiftUIのViewの利用方法の実装方法です。
- Viewの表示に加えて、呼び出し元から値を渡して、相手先のViewを更新するようにしています。

## SwiftUIからAppKitの使用

<img width="600" alt="image" src="https://i.imgur.com/7lGPO0g.jpg">

### SwiftUIからの呼び出し

```swift
import SwiftUI

struct ContentView: View {
    
    @State private var count = 0
    
    var body: some View {
        VStack {
            Text("AppKit View: \(count)")
            Button("Increment") {
                count += 1
            }
            MyRepresentedCustomView(count: $count)
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

### AppKitのViewの作成
- `NSViewRepresentable`を使ってAppKitのViewをSwiftUIのViewとして利用できるようにする。

```swift
struct MyRepresentedCustomView: NSViewRepresentable {

    @Binding var count: Int

    func makeNSView(context: Context) -> NSTextField {
        let view = NSTextField()
        view.isEditable = false
        return view
    }

    func updateNSView(_ view: NSTextField, context: Context) {
        view.stringValue = "SwiftUI View: \(count)"
    }
    
}
```

## AppKitからSwiftUIの使用
### 概要

<img width="600" alt="image" src="https://i.imgur.com/rt5FMo9.jpg">

### 参考
- AppKit側のデータ変更で、SwiftUI側の表示を更新するには`@ObservableObject`を使用する
    - [How do I update a SwiftUI View that was embedded into UIKit?](https://stackoverflow.com/questions/59219019/how-do-i-update-a-swiftui-view-that-was-embedded-into-uikit)
- `NSHostingView`をカスタムする方法
    - [SwiftUI: NSHostingViewでacceptsFirstMouse\(for:\)を有効にする](https://zenn.dev/kyome/articles/876c384e767faa)

### AppKitからの呼び出し
- `NSHostingView`を使ってSwiftUIのViewをAppKitのViewとして利用する。

```swift
import Cocoa
import SwiftUI

class ViewController: NSViewController {
    
    @IBOutlet weak var countLabel: NSTextField!
    
    private var hogeView: NSHostingView<HogeView>!
    @State private var hogeViewModel = HogeViewModel(count: 0)  // AppKit側からデータを操作することを考える

    override func viewDidLoad() {
        super.viewDidLoad()

        hogeView = NSHostingView(rootView: HogeView(viewModel: hogeViewModel))
        self.view.addSubview(hogeView)
        hogeView.frame = .init(origin: .init(x: 100, y: 100), size: .init(width: 200, height: 200))
        
        Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
            self.hogeViewModel.count += 1
            self.countLabel.stringValue = "AppKit View: \(self.hogeViewModel.count)"
        }
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

}
```

### SwiftUIのViewの作成
- SwiftUIのViewは通常通り作成する。

```swift
import SwiftUI

struct HogeView: View {
    
    @ObservedObject var viewModel: HogeViewModel
    
    var body: some View {
        VStack {
            Text("SwiftUI View: \(viewModel.count)")
            Rectangle()
                .foregroundColor(.blue)
                .frame(width: 100, height: 100)
        }
    }
}

struct HogeView_Previews: PreviewProvider {
    static var previews: some View {
        HogeView(viewModel: HogeViewModel(count: 0))
    }
}
```

```swift
class HogeViewModel: ObservableObject {
    
    @Published var count: Int
    
    init(count: Int) {
        self.count = count
    }
}
```