169 lines
4.4 KiB
Swift
169 lines
4.4 KiB
Swift
import Foundation
|
|
|
|
struct HistoryItem {
|
|
|
|
let outgoingDate: Date
|
|
|
|
let outgoingMessage: Message
|
|
|
|
let incomingDate: Date?
|
|
|
|
let incomingMessage: Message?
|
|
|
|
let response: ClientState?
|
|
|
|
init(sent message: Message, date: Date) {
|
|
self.outgoingDate = date
|
|
self.outgoingMessage = message
|
|
self.incomingDate = nil
|
|
self.incomingMessage = nil
|
|
self.response = nil
|
|
}
|
|
|
|
func didReceive(response: ClientState, date: Date?, message: Message?) -> HistoryItem {
|
|
.init(sent: self, response: response, date: date, message: message)
|
|
}
|
|
|
|
func invalidated() -> HistoryItem {
|
|
didReceive(response: .responseRejected(.invalidAuthentication), date: incomingDate, message: incomingMessage)
|
|
}
|
|
|
|
func notAuthenticated() -> HistoryItem {
|
|
didReceive(response: .responseRejected(.missingKey), date: incomingDate, message: incomingMessage)
|
|
}
|
|
|
|
private init(sent: HistoryItem, response: ClientState, date: Date?, message: Message?) {
|
|
self.outgoingDate = sent.outgoingDate
|
|
self.outgoingMessage = sent.outgoingMessage
|
|
self.incomingDate = date
|
|
self.incomingMessage = message
|
|
self.response = response
|
|
}
|
|
|
|
// MARK: Statistics
|
|
|
|
var roundTripTime: TimeInterval? {
|
|
incomingDate?.timeIntervalSince(outgoingDate)
|
|
}
|
|
|
|
var deviceTime: Date? {
|
|
guard let timestamp = incomingMessage?.content.time else {
|
|
return nil
|
|
}
|
|
return Date(timestamp: timestamp)
|
|
}
|
|
|
|
var requestLatency: TimeInterval? {
|
|
deviceTime?.timeIntervalSince(outgoingDate)
|
|
}
|
|
|
|
var responseLatency: TimeInterval? {
|
|
guard let deviceTime = deviceTime else {
|
|
return nil
|
|
}
|
|
return incomingDate?.timeIntervalSince(deviceTime)
|
|
}
|
|
|
|
var clockOffset: Int? {
|
|
guard let interval = roundTripTime, let deviceTime = deviceTime else {
|
|
return nil
|
|
}
|
|
let estimatedArrival = outgoingDate.advanced(by: interval / 2)
|
|
return Int(deviceTime.timeIntervalSince(estimatedArrival))
|
|
}
|
|
|
|
// MARK: Coding
|
|
|
|
static func testEncoding() {
|
|
|
|
}
|
|
|
|
var encoded: Data {
|
|
var result = outgoingDate.encoded + outgoingMessage.encoded
|
|
if let date = incomingDate {
|
|
result += Data([1]) + date.encoded
|
|
} else {
|
|
result += Data([0])
|
|
}
|
|
if let message = incomingMessage {
|
|
result += Data([1]) + message.encoded
|
|
} else {
|
|
result += Data([0])
|
|
}
|
|
result += response?.encoded ?? Data([0])
|
|
return result
|
|
}
|
|
|
|
init?(decodeFrom data: Data, index: inout Int) {
|
|
guard let outgoingDate = Date(decodeFrom: data, index: &index) else {
|
|
return nil
|
|
}
|
|
self.outgoingDate = outgoingDate
|
|
|
|
guard let outgoingMessage = Message(decodeFrom: data, index: &index) else {
|
|
return nil
|
|
}
|
|
self.outgoingMessage = outgoingMessage
|
|
|
|
if data[index] > 0 {
|
|
index += 1
|
|
guard let incomingDate = Date(decodeFrom: data, index: &index) else {
|
|
return nil
|
|
}
|
|
self.incomingDate = incomingDate
|
|
} else {
|
|
self.incomingDate = nil
|
|
index += 1
|
|
}
|
|
|
|
if data[index] > 0 {
|
|
index += 1
|
|
guard let incomingMessage = Message(decodeFrom: data, index: &index) else {
|
|
return nil
|
|
}
|
|
self.incomingMessage = incomingMessage
|
|
} else {
|
|
self.incomingMessage = nil
|
|
index += 1
|
|
}
|
|
guard index < data.count else {
|
|
return nil
|
|
}
|
|
self.response = ClientState(code: data[index])
|
|
index += 1
|
|
}
|
|
}
|
|
|
|
private extension Date {
|
|
|
|
static var encodedSize: Int {
|
|
MemoryLayout<Double>.size
|
|
}
|
|
|
|
var encoded: Data {
|
|
.init(from: timeIntervalSince1970)
|
|
}
|
|
|
|
init?(decodeFrom data: Data, index: inout Int) {
|
|
guard index + Date.encodedSize <= data.count else {
|
|
return nil
|
|
}
|
|
self.init(timeIntervalSince1970: data.advanced(by: index).convert(into: .zero))
|
|
index += Date.encodedSize
|
|
}
|
|
}
|
|
|
|
extension HistoryItem: Identifiable {
|
|
|
|
var id: UInt32 {
|
|
outgoingDate.timestamp
|
|
}
|
|
}
|
|
|
|
extension HistoryItem: Comparable {
|
|
|
|
static func < (lhs: HistoryItem, rhs: HistoryItem) -> Bool {
|
|
lhs.outgoingDate < rhs.outgoingDate
|
|
}
|
|
}
|