Sesame-iOS/Sesame/HistoryItem.swift

169 lines
4.4 KiB
Swift
Raw Normal View History

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
}
}