Sesame-iOS/Sesame/HistoryItem.swift

132 lines
3.5 KiB
Swift
Raw Normal View History

import Foundation
struct HistoryItem {
/// The sent/received date (local time, not including compensation offset)
let requestDate: Date
let request: Message.Content
let usedLocalConnection: Bool
let response: ClientState?
let responseMessage: Message.Content?
let responseDate: Date?
init(sent message: Message.Content, date: Date, local: Bool) {
self.requestDate = date
self.request = message
self.responseMessage = nil
self.response = nil
self.responseDate = nil
self.usedLocalConnection = local
}
func didReceive(response: ClientState, date: Date?, message: Message.Content?) -> HistoryItem {
.init(sent: self, response: response, date: date, message: message)
}
func invalidated() -> HistoryItem {
didReceive(response: .responseRejected(.invalidAuthentication), date: responseDate, message: responseMessage)
}
func notAuthenticated() -> HistoryItem {
didReceive(response: .responseRejected(.missingKey), date: responseDate, message: responseMessage)
}
private init(sent: HistoryItem, response: ClientState, date: Date?, message: Message.Content?) {
self.requestDate = sent.requestDate
self.request = sent.request
self.responseDate = date
self.responseMessage = message
self.response = response
self.usedLocalConnection = sent.usedLocalConnection
}
// MARK: Statistics
var roundTripTime: TimeInterval? {
responseDate?.timeIntervalSince(requestDate)
}
var deviceTime: Date? {
guard let timestamp = responseMessage?.time else {
return nil
}
return Date(timestamp: timestamp)
}
var requestLatency: TimeInterval? {
deviceTime?.timeIntervalSince(requestDate)
}
var responseLatency: TimeInterval? {
guard let deviceTime = deviceTime else {
return nil
}
return responseDate?.timeIntervalSince(deviceTime)
}
var clockOffset: Int? {
guard let interval = roundTripTime, let deviceTime = deviceTime else {
return nil
}
let estimatedArrival = requestDate.advanced(by: interval / 2)
return Int(deviceTime.timeIntervalSince(estimatedArrival))
}
}
extension HistoryItem: Codable {
enum CodingKeys: Int, CodingKey {
case requestDate = 1
case request = 2
case usedLocalConnection = 3
case response = 4
case responseMessage = 5
case responseDate = 6
}
}
extension ClientState: Codable {
init(from decoder: Decoder) throws {
let code = try decoder.singleValueContainer().decode(UInt8.self)
self.init(code: code)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(code)
}
}
extension HistoryItem: Identifiable {
var id: UInt32 {
requestDate.timestamp
}
}
extension HistoryItem: Comparable {
static func < (lhs: HistoryItem, rhs: HistoryItem) -> Bool {
lhs.requestDate < rhs.requestDate
}
}
extension HistoryItem {
static var mock: HistoryItem {
let content = Message.Content(time: Date.now.timestamp, id: 123)
let content2 = Message.Content(time: (Date.now + 1).timestamp, id: 124)
return .init(sent: content, date: .now, local: false)
.didReceive(response: .openSesame, date: .now + 2, message: content2)
}
}