⏲️

【Swift】Combineで定期的なタイマー処理を行う方法

2023/04/02に公開

はじめに

この記事の主張

  • Timer.publishを使って定期的なタイマー処理を実現する
  • Combineのパイプラインを使って指定されたタイマー間隔で処理を実行

やりたいこと

上部の文言(紫色で囲った枠内の文章)を30秒ごとに違う文字に変更したい。

文章一覧
let data: [String] = [
    "お疲れ様です。",
    "いつも頑張っていますね。",
    "ほっと一息ついてみましょう。",
    "無理しないでくださいね。",
    "今日もご苦労様です。",
    "頑張った自分にご褒美してくださいね。",
    "たまにはゆっくりしてみてください。",
    "心を落ち着かせましょう。",
    "深呼吸してみましょう。",
    "今日の自分を褒めてみましょう。",
    "明日もいい日になりますよ。"
]

本題

完成形

import Foundation
import Combine

class EncouragingWordList: ObservableObject {
    
    let data: [String] = [
        "お疲れ様です。",
        "いつも頑張っていますね。",
        "ほっと一息ついてみましょう。",
        "無理しないでくださいね。",
        "今日もご苦労様です。",
        "頑張った自分にご褒美してくださいね。",
        "たまにはゆっくりしてみてください。",
        "心を落ち着かせましょう。",
        "深呼吸してみましょう。",
        "今日の自分を褒めてみましょう。",
        "明日もいい日になりますよ。"
    ]
    
    @Published var currentWord: String = ""
    private var cancellablePipeline: AnyCancellable?
    
    init() {
        self.currentWord = data[0]
        cancellablePipeline = Timer
            .publish(every: 30.0, on: .main, in: .common)
            .autoconnect()
            .sink { [unowned self] _ in
                if let currentData = data.randomElement() {
                    self.currentWord = currentData
                }
            }
    }
    
}

Timer.publishを使って定期的なタイマー処理を実現する

30秒ごとに値(日付)を返すにはTimer.publishを使用します。

Timer
    .publish(every: 30.0, on: .main, in: .common)
    .autoconnect()

.publish(every:on:in:)は指定の間隔で現在の日付を出力するPublisherを返します。
autoconnect()を使用すると、Subscriberが与えられたときにPublisherからSubscriberへのパイプラインを自動的に接続します。

これで、30秒ごとに、上流からパイプラインを通ってデータが流れてきます。
あとは受け口での処理を作るだけです。

Combineのパイプラインを使って指定されたタイマー間隔で処理を実行

次にパイプラインの受け口の処理を作ります。
いつでも受け口側でキャンセル可能なSubscriberであるsinkを使います。
(キャンセル可能とは、水道の蛇口のように、いつでも止めたり開けたりできる仕組みです。)

Timer
    .publish(every: 30.0, on: .main, in: .common)
    .autoconnect()
    .sink { [unowned self] _ in
        if let currentData = data.randomElement() {
            self.currentWord = currentData
        }
    }

.sinkでは以下を実行してます。

  1. 配列dataからランダムに要素を取得して、定数currentDataに格納
  2. currentDatacurrentWordに格納してViewに反映

まとめ

実現したいこと

指定のタイマー間隔ごとに定期的に処理を実行する

実現方法

cancellablePipeline = Timer
            .publish(every: ここにタイマー間隔を指定, on: .main, in: .common)
            .autoconnect()
            .sink { [unowned self] _ in
                // ここに処理を記述
            }

Discussion