import Foundation struct SyncPlayPayload: Codable { let trackID: String let startUptime: TimeInterval let startPosition: TimeInterval } struct TrackInfoPayload: Codable { let trackID: String let displayName: String } enum SessionMessage: Codable { case hello case libraryRequest case trackInfo(TrackInfoPayload) case trackRequest(String) case play(SyncPlayPayload) case pause case seek(TimeInterval) case syncRequest(TimeInterval) case syncResponse(hostUptime: TimeInterval, peerUptime: TimeInterval) case driftCorrection(position: TimeInterval, hostUptime: TimeInterval) private enum CodingKeys: String, CodingKey { case type case payload case position case hostUptime case peerUptime case trackID case displayName } private enum MessageType: String, Codable { case hello case libraryRequest case trackInfo case trackRequest case play case pause case seek case syncRequest case syncResponse case driftCorrection } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let type = try container.decode(MessageType.self, forKey: .type) switch type { case .hello: self = .hello case .libraryRequest: self = .libraryRequest case .trackInfo: let payload = try container.decode(TrackInfoPayload.self, forKey: .payload) self = .trackInfo(payload) case .trackRequest: let trackID = try container.decode(String.self, forKey: .trackID) self = .trackRequest(trackID) case .play: let payload = try container.decode(SyncPlayPayload.self, forKey: .payload) self = .play(payload) case .pause: self = .pause case .seek: let position = try container.decode(TimeInterval.self, forKey: .position) self = .seek(position) case .syncRequest: let hostUptime = try container.decode(TimeInterval.self, forKey: .hostUptime) self = .syncRequest(hostUptime) case .syncResponse: let hostUptime = try container.decode(TimeInterval.self, forKey: .hostUptime) let peerUptime = try container.decode(TimeInterval.self, forKey: .peerUptime) self = .syncResponse(hostUptime: hostUptime, peerUptime: peerUptime) case .driftCorrection: let position = try container.decode(TimeInterval.self, forKey: .position) let hostUptime = try container.decode(TimeInterval.self, forKey: .hostUptime) self = .driftCorrection(position: position, hostUptime: hostUptime) } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) switch self { case .hello: try container.encode(MessageType.hello, forKey: .type) case .libraryRequest: try container.encode(MessageType.libraryRequest, forKey: .type) case .trackInfo(let payload): try container.encode(MessageType.trackInfo, forKey: .type) try container.encode(payload, forKey: .payload) case .trackRequest(let trackID): try container.encode(MessageType.trackRequest, forKey: .type) try container.encode(trackID, forKey: .trackID) case .play(let payload): try container.encode(MessageType.play, forKey: .type) try container.encode(payload, forKey: .payload) case .pause: try container.encode(MessageType.pause, forKey: .type) case .seek(let position): try container.encode(MessageType.seek, forKey: .type) try container.encode(position, forKey: .position) case .syncRequest(let hostUptime): try container.encode(MessageType.syncRequest, forKey: .type) try container.encode(hostUptime, forKey: .hostUptime) case .syncResponse(let hostUptime, let peerUptime): try container.encode(MessageType.syncResponse, forKey: .type) try container.encode(hostUptime, forKey: .hostUptime) try container.encode(peerUptime, forKey: .peerUptime) case .driftCorrection(let position, let hostUptime): try container.encode(MessageType.driftCorrection, forKey: .type) try container.encode(position, forKey: .position) try container.encode(hostUptime, forKey: .hostUptime) } } }