Sponsor Link
環境&対象
- macOS14.2 RC
- Xcode 15.1 RC
- iOS 17.2
- Swift 5.9
Duration
Duration は、Swift5.7 から導入された 時間を表現する型の1つです。
Apple のドキュメントは、こちら。
Duration は、時間の長さを表現するために使用されます。以前から使われている TimeInterval と同じ様な目的で使用されます。
TimerInterval は、秒を単位として保持していましたが、Duration は、単位を指定して 初期化することができます。
例えば、10 秒は、以下のように 定義します。
import Foundation
let du = Duration.seconds(10)
print(du) // print-out: 10.0 seconds
.seconds だけでなく、.milliseconds という ミリ秒単位で使いやすい initializer も用意されています。
Duration と TimeInterval
Swift5.7 以前は、TimeInterval という型が、時間の長さを表現するために使用されていました。
Duration と TimeInterval の関係は以下の記事で説明しています。
[Swift] Duration と TimeInterval の変換
extend Duration
時間長を表現する時に使う Duration は、初期化時に seconds/ milliseconds/ microseconds/ nanoseconds を使って初期化することができます。つまり、秒/ ミリ秒/ マイクロ秒/ ナノ秒 で初期化できます。
なお、Duration を使って保持できる 最小単位は、アト秒 (10 ^ (-18) 秒)です。
Duration を使うケースでは、もう少し大きい単位でも指定できたら便利そうです。
ということで、minutes/ hours/ days を追加してみます。
その名の通り、分/ 時間/ 日 です。 Double を受け取ることで、例えば、1.5日 = 36 時間 という使い方をできる様にしてみます。
Duration の extension として static func を定義してみました。
//
// Duration+Extension.swift
//
// Created by : Tomoaki Yagishita on 2023/12/09
// © 2023 SmallDeskSoftware
//
import Foundation
extension Duration {
static public func minutes(_ value: Double) -> Duration {
return Duration.seconds(value * 60)
}
static public func hours(_ value: Double) -> Duration {
return Duration.seconds(value * 60 * 60)
}
static public func days(_ value: Double) -> Duration {
return Duration.seconds(value * 60 * 60 * 24)
}
}
以下のテストコードで 動作を確認しました。
final class DurationExampleTests: XCTestCase {
func test_Inits() async throws {
let seconds90FromSeconds = Duration.seconds(90)
let hours2_5FromSeconds = Duration.seconds(2.5 * 60 * 60)
let days1_5FromSeconds = Duration.seconds(1.5 * 24 * 60 * 60)
// Duration.minutes
let seconds90FromMinutes = Duration.minutes(1.5)
XCTAssertEqual(seconds90FromSeconds, seconds90FromMinutes)
// Duration.hours
let hours2_5FromHours = Duration.hours(2.5)
XCTAssertEqual(hours2_5FromSeconds, hours2_5FromHours)
// Duration.days
let days1_5FromDays = Duration.days(1.5)
XCTAssertEqual(days1_5FromSeconds, days1_5FromDays)
}
}
# 上記は、XCTAssertEqual でチェックしていますが、一般的に 計算結果を Equal でチェックするのは危険です。
FormatStyle
FormatStyle は、iOS15/ macOS12 から導入された データを特定の書式に変換するための 仕組みです。
FormatStyle 以前は、DateFormatter 等の Formatter を使うのが通常でしたが、新しく導入された FormatStyle は、Formatter に比べて直感的に記述できるようになっています。
データを特定の書式に変換するための FormatStyle、特定の書式からデータに変換する ParseableFormatStyle は、以下の記事で説明しています。
[Swift] FormatStyle の使い方 [Swift] ParseableFormatStyle の使い方 [Swift] FormatStyle の作り方 [Swift] ParseableFormatStyle の作り方
上記では、電話番号の書式を例に 書式への変換方法から、書式からデータへの変換方法を説明しています。
Duration 向け FormatStyle
Duration に対して FormatStyle も用意されています。
以下の記事で説明しました。
[Swift] Duration を FormatStyle 指定で表示する
デフォルトでは、hourMinute, hourMinuteSecond, minuteSecond が用意されています。
それぞれ、時分、時分秒、分秒 という表示で、最も大きい単位が 必要に応じて(繰り上がらずに)大きくなります。
Duration 向け FormatStyle 拡張
Duration 向けに用意されている FormatStyle では 最大の単位は時間でした。
以降では、最大単位を 日まで拡大してみます。
つまり、26時間30分を表す Duration については、日数+hourMinuteSecond 表記 をするということです。
具体的には 最初に日数を表示し、その後に hourMinuteSecond 相当を表示する “01:02:30:00” という表示にしたいということです。
# 月まで拡大すると、1ヶ月の日数をどうするかという問題を考えなければいけないので、日までにしています。
DayHourMinuteSecondStyle という FormatStyle を以下のように作りました。
struct DayHourMinuteSecondStyle: FormatStyle, Codable {
typealias FormatInput = Duration
typealias FormatOutput = String
enum Pattern: Codable {
case dayHour
case dayHourMinute
case dayHourMinuteSecond
}
let pattern: Pattern
let padding: Int
init(pattern: Pattern = .dayHour, padding: Int = 2) {
self.pattern = pattern
self.padding = padding
}
func format(_ value: Duration) -> String {
let (second, atto) = value.components
let secDay = second / (24*60*60)
let restDuration = Duration(secondsComponent: second - secDay*24*60*60, attosecondsComponent: atto)
switch pattern {
case .dayHour:
let restHour = (second - secDay*24*60*60)/(60*60)
return paddedInt(secDay, minLength: padding) + ":" + paddedInt(restHour, minLength: 2)
case .dayHourMinute:
return paddedInt(secDay, minLength: padding) + ":" + restDuration.formatted(.time(pattern: .hourMinute(padHourToLength: 2)))
case .dayHourMinuteSecond:
return paddedInt(secDay, minLength: padding) + ":" + restDuration.formatted(.time(pattern: .hourMinuteSecond(padHourToLength: 2)))
}
}
func paddedInt(_ value: Int64, minLength: Int) -> String {
let valueString = value.formatted(.number)
if valueString.count >= minLength { return valueString }
let padding = String(repeating: "0", count: minLength - valueString.count)
return padding + valueString
}
}
確認に使用した テストコードは以下です。
func test_FormatStyle() async throws {
let hours25 = Duration.hours(25)
XCTAssertEqual(hours25.formatted(.time(pattern: .hourMinute)), "25:00")
XCTAssertEqual(hours25.formatted(DayHourMinuteSecondStyle(pattern: .dayHour, padding: 2)), "01:01")
XCTAssertEqual(hours25.formatted(.dayHour), "01:01")
XCTAssertEqual(hours25.formatted(.dayHourMinute), "01:01:00")
XCTAssertEqual(hours25.formatted(.dayHourMinuteSecond), "01:01:00:00")
XCTAssertEqual(hours25.formatted(DayHourMinuteSecondStyle(pattern: .dayHour, padding: 5)), "00001:01")
}
.formatted ごとに、DayHourMinuteSecondStyle… と書くのは大変なので、以下のように、FormatStyle に対しての extension を定義すると使いやすくなります。(テストコードでも使用しています。)
extension FormatStyle where Self == DayHourMinuteSecondStyle {
static var dayHour : DayHourMinuteSecondStyle { .init(pattern: .dayHour) }
static var dayHourMinute : DayHourMinuteSecondStyle { .init(pattern: .dayHourMinute) }
static var dayHourMinuteSecond: DayHourMinuteSecondStyle { .init(pattern: .dayHourMinuteSecond) }
}
まとめ
Duration の拡張と カスタムFormatStyleの作成 をしてみました。
- FormatStyle は、Duration.TimeFormatStyle.time
- おおきく3パターン( hourMinuteSecond, hourMinute, minuteSecond ) が用意されている
- 最上位の単位については、0 padding できる
- 最下位の単位については、処理方法を指定できる
- 独自 FormatStyle を作るのは、独自型を作るのと同様
- let (second, attoSec) = duration.components とすると Duration から 秒とアト秒が取得できる
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
SwiftUI ViewMatery
SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。
英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
SwiftUI 徹底入門
# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。
Swift学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Sponsor Link