mirror of
https://github.com/home-assistant/iOS.git
synced 2026-02-05 06:35:37 -06:00
Fixes #1801 by working around the Apple bug. Refs home-assistant/frontend#10819. Requires core-2021.12.0b6 or newer. ## Summary When the app has been in the background for a while without executing, reset the WebSocket connection in the WebView when execution begins again. Also implements the new `theme-update` external bus command since it will log as an unhandled message. This likely means we're over-updating theme colors, but that is relatively cheap to do and doesn't cause harm. ## Any other notes This works around a bug in NSURLSession's WebSocket implementation -- which WKWebView moved to in iOS 15 -- where it cannot reliably detect network connectivity failures due to extended backgrounding. Eventually it'll get a "connection reset by peer" or other error, but in the meantime it reports itself as still connected, thus leaving our WebView thinking it's connected but anything sent isn't sent and nothing is received.
102 lines
3.5 KiB
Swift
102 lines
3.5 KiB
Swift
import Foundation
|
|
|
|
public class WebSocketMessage: Codable {
|
|
public let MessageType: String
|
|
public var ID: Int?
|
|
public var Success: Bool?
|
|
public var Payload: [String: Any]?
|
|
public var Result: [String: Any]?
|
|
public var Message: String?
|
|
public var HAVersion: String?
|
|
public var command: String?
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case MessageType = "type"
|
|
case ID = "id"
|
|
case Success = "success"
|
|
case Payload = "payload"
|
|
case Result = "result"
|
|
case Message = "message"
|
|
case HAVersion = "ha_version"
|
|
case command = "command"
|
|
}
|
|
|
|
public required init(from decoder: Decoder) throws {
|
|
let values = try decoder.container(keyedBy: CodingKeys.self)
|
|
self.MessageType = try values.decode(String.self, forKey: .MessageType)
|
|
self.ID = try? values.decode(Int.self, forKey: .ID)
|
|
self.Success = try? values.decode(Bool.self, forKey: .Success)
|
|
self.Payload = try? values.decode([String: Any].self, forKey: .Payload)
|
|
self.Result = try? values.decode([String: Any].self, forKey: .Result)
|
|
self.Message = try? values.decode(String.self, forKey: .Message)
|
|
self.HAVersion = try? values.decode(String.self, forKey: .HAVersion)
|
|
self.command = try values.decodeIfPresent(String.self, forKey: .command)
|
|
}
|
|
|
|
public init?(_ dictionary: [String: Any]) {
|
|
guard let mType = dictionary["type"] as? String else {
|
|
return nil
|
|
}
|
|
self.MessageType = mType
|
|
self.ID = dictionary["id"] as? Int
|
|
self.Payload = dictionary["payload"] as? [String: Any]
|
|
self.Result = dictionary["result"] as? [String: Any]
|
|
self.Success = dictionary["success"] as? Bool
|
|
self.command = dictionary["command"] as? String
|
|
}
|
|
|
|
public init(_ incomingMessage: WebSocketMessage, _ result: [String: Any]) {
|
|
self.ID = incomingMessage.ID
|
|
self.MessageType = "result"
|
|
self.Result = result
|
|
self.Success = true
|
|
self.command = nil
|
|
}
|
|
|
|
public init(id: Int, type: String, result: [String: Any], success: Bool = true) {
|
|
self.ID = id
|
|
self.MessageType = type
|
|
self.Result = result
|
|
self.Success = success
|
|
self.command = nil
|
|
}
|
|
|
|
public init(command: String) {
|
|
self.ID = -1
|
|
self.MessageType = "command"
|
|
self.command = command
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
try container.encode(MessageType, forKey: .MessageType)
|
|
if let ID = ID {
|
|
try container.encode(ID, forKey: .ID)
|
|
}
|
|
if let Success = Success {
|
|
try container.encode(Success, forKey: .Success)
|
|
}
|
|
if let Message = Message {
|
|
try container.encode(Message, forKey: .Message)
|
|
}
|
|
if let Result = Result {
|
|
try container.encode(Result, forKey: .Result)
|
|
}
|
|
try container.encodeIfPresent(command, forKey: .command)
|
|
}
|
|
|
|
init(_ messageType: String) {
|
|
self.MessageType = messageType
|
|
}
|
|
}
|
|
|
|
extension WebSocketMessage: CustomStringConvertible, CustomDebugStringConvertible {
|
|
public var description: String {
|
|
"WebSocketMessage(type: \(MessageType), id: \(String(describing: ID)), payload: \(String(describing: Payload)), result: \(String(describing: Result)), success: \(String(describing: Success)))"
|
|
}
|
|
|
|
public var debugDescription: String {
|
|
description
|
|
}
|
|
}
|