iPhone などの iOSアプリで並列処理を行う、簡単なサンプルコードを紹介する。
筆者のマシン | |
---|---|
Mac | mac mini (M1, 2020) |
OS | Monterey バージョン 12.4 |
XCode | バージョン 13.4.1 |
Swift | バージョン 5.6.1 |
ここでは次の手順で説明する。
- はじめに
- 並列に実施する処理として複数のTaskを作成する
- 各Taskを実行する
はじめに
iOSアプリにおいて、複数の処理を並列に実行させたいときがある。例えば
- タスクA
- あるURLへアクセスし、データを取得する。
- 取得したデータを用いた表テーブルを画面に表示。
- タスクB
- 別のあるURLへアクセスし、データを取得する。
- 取得したデータを元に作成した文字列を画面に表示。
といったタスクがあるとする。
タスクAの処理が完了した後にタスクBを処理させるのではなく、タスクAとタスクBを並行に処理させたいことがある。
タスクAとタスクBのどちらが先に完了しても問題がない場合は、並列処理をすることで、速やかな画面描画を実現することができる。
ここでは、タスクAとタスクBといった複数タスクを並列処理させる、簡単なサンプルプログラムを紹介する。
尚、本記事で紹介するコード全体(XCodeプロジェクト)は、GitHub で公開している。
(サンプルで用いる音源データはアップしていないため、コード実行時は別途ご準備ください)
並列に実施する処理として複数のTaskを作成する
ここでは、並列に実施する処理として、以下の2つのTaskを作成する。
今回は処理を簡単にするため、非同期処理としてよく扱われるURLへのリクエストとレスポンス受信といった処理ではなく、2秒待つといった処理としている。
- Task1 : 2秒待って、ワンワンと鳴く。更に2秒待って、ワンワンと鳴く。
- Task2 : 2秒待って、ニャンと鳴く。
Task1とTask2を同時に実行開始することで、2秒後にワンワンとニャンが同時に鳴き、更にその2秒後にワンワンだけが鳴くようになる。
まず、下記メソッドは2秒待つという処理である。
// This task is a pseudo asynchronous processing and actually sleeps for 2 seconds.
private func taskSleep(taskName: String) async throws {
print("\(date) \(taskName) Start")
try await Task.sleep(nanoseconds: 2_000_000_000)
print("\(date) \(taskName) End")
}
このサンプルでは、引数taskNameでタスク名を受け取り、2秒を待つ前後にログを出力している。
Task1は次の通りである。
// Dog Task
// after await task, a dog barks, again after await task, a dog barks.
Task {
try await taskSleep(taskName: "Dog-1")
animalSoundPlayer.playDog()
try await taskSleep(taskName: "Dog-2")
animalSoundPlayer.playDog()
}
4行目のtaskSleepメソッドでは、2秒待つと言う処理がなされる。
5行目のanimalSoundPlayer.playDog()は、ワンワンと鳴く自作コードである。
6行目のtaskSleepメソッドでは、更に2秒待つと言う処理がなされる。
7行目のanimalSoundPlayer.playDog()を呼び出すことで、再度ワンワンと鳴く。
Task2は次の通りである。
// Cat Task
// after await task, a cat mews
Task {
try await taskSleep(taskName: "Cat-1")
animalSoundPlayer.playCat()
}
4行目のtaskSleepメソッドでは、2秒待つと言う処理がなされる。
5行目のanimalSoundPlayer.playCat()は、ニャンと鳴く自作コードである。
各Taskを実行する
今回のサンプルコードでは、上記の各Taskを iPhone画面上のボタン「Animal Sound Play」をタップした際に動作させる。
ContentView.swiftの全体コードは以下である。
struct ContentView: View {
private let animalSoundPlayer = AnimalSoundsPlayer()
// Current date
private var date: String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
return dateFormatter.string(from: Date())
}
var body: some View {
Button("Animal Sound Play", action: {
// Dog Task and Cat Task are parallel processes.
// Dog Task
// after await task, a dog barks, again after await task, a dog barks.
Task {
try await taskSleep(taskName: "Dog-1")
animalSoundPlayer.playDog()
try await taskSleep(taskName: "Dog-2")
animalSoundPlayer.playDog()
}
// Cat Task
// after await task, a cat mews
Task {
try await taskSleep(taskName: "Cat-1")
animalSoundPlayer.playCat()
}
})
}
// This task is a pseudo asynchronous processing and actually sleeps for 2 seconds.
private func taskSleep(taskName: String) async throws {
print("\(date) \(taskName) Start")
try await Task.sleep(nanoseconds: 2_000_000_000)
print("\(date) \(taskName) End")
}
}
上記ContentView.swiftコードを用いたアプリは、次のような画面となる。
画面上の「Animal Sound Play」をタップすることで、Task1とTask2が同時に開始される。
XCode の Simulator でアプリを起動し、画面をタップした時のログは以下の通りである。
2022/08/05 10:06:48 Dog-1 Start
2022/08/05 10:06:48 Cat-1 Start
2022/08/05 10:06:50 Cat-1 End
2022/08/05 10:06:50 Dog-1 End
2022/08/05 10:06:50 Dog-2 Start
2022/08/05 10:06:52 Dog-2 End
10:06:48 に、Task1のtaskSleepメソッド(taskNameはDog-1) と Task2のtaskSleepメソッド(taskNameはCat-1)が実行されており、
いずれも2秒後の10:06:50にメソッド処理が完了する。
この後ワンワンとニャンが同時に鳴く。
その後に、Task1のtaskSleepメソッド(taskNameはDog-2)が開始され、2秒後の10:06:52にメソッド処理が完了した後、ワンワンと鳴く。
つまり、2秒後にワンワンとニャンが同時に鳴き、更にその2秒後にニャンだけが鳴く。
このようにして、複数Taskを並列に実行することができる。
以上