一般的なTwitterクライアントの時間の相対表示をSwiftUIで実現する

2022/12/18に公開

この記事はSwiftUI Advent Calendar 2022の18日目になります。
https://qiita.com/advent-calendar/2022/swiftui

以前は結構スロットが空いてたので軽い気持ちで2回目書くかぁと思ってたんですが意外と埋まって本当にこれでいいのだろうかと恐縮してます。

2回目なのでかなり軽めの記事です。前回TwitterクライアントをSwiftUIで作った記事を書きました。そこで詰まった所を書いたのですが、今回はその中で書かなかったところを書きます。

実装された物


※便宜上、倍速にしているのとツイートが送信されてから読み込まれるまでの時間をカットしています。

読み込まれたツイートの右上を見て分かる通り、時間がカウントアップされているのでAPIで取得した投稿時間から相対的にどれだけ時間が経過しているかが分かります。

実際のコード

以下が実際のコードです。

struct PostedDate: View {
    let date: Date
    var body: some View {
        TimelineView(.periodic(from: date, by: 1)) { timeline in
            let seconds = Int(floor(timeline.date.timeIntervalSince((date))))
            let minutes = seconds / 60
            let hours = minutes / 60
            let days = hours / 24
            let weeks = days / 7
            let months = weeks / 4
            let years = months / 12
            
            Group {
                if seconds > 60 && seconds < 3600 {
                    Text("\(minutes)m")
                } else if seconds > 3600 && seconds < 86400 {
                    Text("\(hours)h")
                } else if seconds > 86400 && seconds < 604800 {
                    Text("\(days)d")
                } else if seconds > 604800 && seconds < 2419200 {
                    Text("\(weeks)w")
                } else if seconds > 2419200 && seconds < 29030400 {
                    Text("\(months)mo")
                } else if seconds > 29030400 {
                    Text("\(years)y")
                } else {
                    Text("\(seconds)s")
                }
            }
            .font(.system(size: 16, weight: .regular, design: .rounded))
            .lineLimit(1)
            .foregroundColor(Color(UIColor.systemGray3))
            .frame(width: 40, alignment: .trailing)
        }
    }
}

これはiOS15で実装されたTimelineViewを使っています。このView自体に特別な意匠はありませんがクロージャー内で使用できるTimelineView.Contextを渡すことで任意のスケジュールでクロージャー内のViewを更新することができます。

TimelineView自体に渡せるスケジュールタイプはTimelineScheduleプロトコルを実装するかBuilt-inのスケジュールタイプを渡すことができます。今回は任意の頻度でViewを更新させたいので.periodic(from:by:)を渡します。関数の中のbyに1を渡すことで1秒ごとに更新するようにします。

このままだと単純にAPIで取得した時間にカウントアップしていくだけです。今回実装したいのはあくまで現在時刻からの相対時間を表示したいのでDate.timeIntervalSince()を使い引数にAPIで取得した投稿時間を渡します。そしてそれをsecondsに格納してこれをベースに実装していきます。ここで基本機能はできました。

ここからは位が上がる度に表示形式を変えたいのでsecondsの範囲を指定して表示形式を変えていきます。(多分相当ここは汚い実装していて全く自信がないので効率的な実装方法どなたか知っていれば教えて下さい...。)

最後に

最初に少し詰まりましたが、SwiftUIの簡単さ、柔軟さを知ることができる実装でした。またDateの扱いにも少し詳しくなれたので良かったです。同じところに詰まった方は是非参考にしてみてください。

Discussion