【SwiftUI】Combineを使ってデジタル時計を作る

# はじめに
以前はTimelineViewでデジタル時計を作成してみました
今回はCombineを使用してデジタル時計を作成してみようと思います。

# 完成形
![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/257b146a-ef0d-f222-e6ed-69b67fab5b3f.gif)

# 完成コード
```ContentView.swift
import SwiftUI

struct ContentView: View {
    @StateObject private var viewModel = ViewModel()
    var body: some View {
        GeometryReader { geo in
            ZStack(alignment: .center) {
                Color(red: 132/255, green: 209/255, blue: 78/255).edgesIgnoringSafeArea(.all)
                VStack(alignment: .center, spacing: 10) {
                    Text("\(viewModel.time.formatted(.iso8601.year().month().day().dateSeparator(.dash)))")
                        .fontWeight(.medium)
                        .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
                        .foregroundColor(.black)
                        .background(
                            Text("8888-88-88")
                                .fontWeight(.medium)
                                .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
                                .foregroundColor(.black.opacity(0.1))
                        )
                    Text("\(viewModel.time.formatted(.iso8601.time(includingFractionalSeconds: false)))")
                        .fontWeight(.medium)
                        .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/6))
                        .foregroundColor(.black)
                        .background(
                            Text("88:88:88")
                                .fontWeight(.medium)
                                .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/6))
                                .foregroundColor(.black.opacity(0.1))
                        )
                }
            }
        }
        .onDisappear {
            viewModel.timer.upstream.connect().cancel()
        }
    }
}
```
```ViewModel.swift
import Combine
import Foundation

final class ViewModel: ObservableObject {
    @Published var time: Date = Date.now

    let timer = Timer.publish(every: 1, on: .main, in: .default).autoconnect()

    private var cancellables = Set<AnyCancellable>()

    init() {
        timer
            .sink { [weak self] date in
                guard let self else { return }
                self.time = Date.now
            }
            .store(in: &cancellables)
    }
}
```

# 解説
## 時間の更新
Combineで定期実行をする場合は下記のコードになります
例は1秒間隔で`sink`の処理を行います
```swift
Timer.publish(every: 1, on: .main, in: .default)
    .autoconnect()
    .sink { _ in
        // 処理
    }
    .store(in: &cancellables)
```
`@Published`で現在時刻を保管します
```swift
@Published var time: Date = Date.now
```
ViewModelにある現在時刻をView側で呼び出します
```swift
Text("\(viewModel.time.formatted(.iso8601.year().month().day().dateSeparator(.dash)))")
Text("\(viewModel.time.formatted(.iso8601.time(includingFractionalSeconds: false)))")
```

## フォント
カスタムフォントを設定しています。
フォントサイズは適当なので各自調整してください。
一応、iPad 11inch, iPhoneSE, iPhone12で正常に表示される事を確認しました
```swift
.font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
```
https://qiita.com/SNQ-2001/items/6531526ab3aa51855247

## 文字背景に焼き付きを表示
88を重ねることによってデジタル時計感が増します
```swift
.background(
    Text("8888-88-88")
        .fontWeight(.medium)
        .font(.custom("DSEG7Modern-BoldItalic", size: geo.size.width/15))
        .foregroundColor(.black.opacity(0.1))
)
```

# おわり
Combineを使うと簡潔に記述できて良いです！

完成プロジェクトを置いときます。

https://github.com/SNQ-2001/Digital-Clock-Demo