162 lines
4.1 KiB
Swift
162 lines
4.1 KiB
Swift
//
|
|
// CocoaMQTTStorage.swift
|
|
// CocoaMQTT
|
|
//
|
|
// Created by JianBo on 2019/10/6.
|
|
// Copyright © 2019 emqtt.io. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
protocol CocoaMQTTStorageProtocol {
|
|
|
|
var clientId: String { get set }
|
|
|
|
init?(by clientId: String)
|
|
|
|
func write(_ frame: FramePublish) -> Bool
|
|
|
|
func write(_ frame: FramePubRel) -> Bool
|
|
|
|
func remove(_ frame: FramePublish)
|
|
|
|
func remove(_ frame: FramePubRel)
|
|
|
|
func synchronize() -> Bool
|
|
|
|
/// Read all stored messages by saving order
|
|
func readAll() -> [Frame]
|
|
}
|
|
|
|
final class CocoaMQTTStorage: CocoaMQTTStorageProtocol {
|
|
|
|
var clientId: String = ""
|
|
|
|
var userDefault: UserDefaults = UserDefaults()
|
|
|
|
var versionDefault: UserDefaults = UserDefaults()
|
|
|
|
init?() {
|
|
versionDefault = UserDefaults()
|
|
}
|
|
|
|
init?(by clientId: String) {
|
|
guard let userDefault = UserDefaults(suiteName: CocoaMQTTStorage.name(clientId)) else {
|
|
return nil
|
|
}
|
|
|
|
self.clientId = clientId
|
|
self.userDefault = userDefault
|
|
}
|
|
|
|
deinit {
|
|
userDefault.synchronize()
|
|
versionDefault.synchronize()
|
|
}
|
|
|
|
func setMQTTVersion(_ version: String) {
|
|
versionDefault.set(version, forKey: "cocoamqtt.emqx.version")
|
|
}
|
|
|
|
func queryMQTTVersion() -> String {
|
|
return versionDefault.string(forKey: "cocoamqtt.emqx.version") ?? "3.1.1"
|
|
}
|
|
|
|
func write(_ frame: FramePublish) -> Bool {
|
|
guard frame.qos > .qos0 else {
|
|
return false
|
|
}
|
|
userDefault.set(frame.bytes(version: queryMQTTVersion()), forKey: key(frame.msgid))
|
|
return true
|
|
}
|
|
|
|
func write(_ frame: FramePubRel) -> Bool {
|
|
userDefault.set(frame.bytes( version: queryMQTTVersion()), forKey: key(frame.msgid))
|
|
return true
|
|
}
|
|
|
|
func remove(_ frame: FramePublish) {
|
|
userDefault.removeObject(forKey: key(frame.msgid))
|
|
}
|
|
|
|
func remove(_ frame: FramePubRel) {
|
|
userDefault.removeObject(forKey: key(frame.msgid))
|
|
}
|
|
|
|
func remove(_ frame: Frame) {
|
|
if let pub = frame as? FramePublish {
|
|
userDefault.removeObject(forKey: key(pub.msgid))
|
|
} else if let rel = frame as? FramePubRel {
|
|
userDefault.removeObject(forKey: key(rel.msgid))
|
|
}
|
|
}
|
|
|
|
func synchronize() -> Bool {
|
|
return userDefault.synchronize()
|
|
}
|
|
|
|
func readAll() -> [Frame] {
|
|
return __read(needDelete: false)
|
|
}
|
|
|
|
func takeAll() -> [Frame] {
|
|
return __read(needDelete: true)
|
|
}
|
|
|
|
private func key(_ msgid: UInt16) -> String {
|
|
return "\(msgid)"
|
|
}
|
|
|
|
private static func name(_ clientId: String) -> String {
|
|
return "cocomqtt-\(clientId)"
|
|
}
|
|
|
|
private func parse(_ bytes: [UInt8]) -> (UInt8, [UInt8])? {
|
|
// FramePubRel is 4 bytes long
|
|
guard bytes.count > 3 else {
|
|
return nil
|
|
}
|
|
// bytes 1..<5 may be 'Remaining Length'
|
|
for i in 1 ..< min(5, bytes.count) where (bytes[i] & 0x80) == 0 {
|
|
return (bytes[0], Array(bytes.suffix(from: i + 1)))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
private func __read(needDelete: Bool) -> [Frame] {
|
|
var frames = [Frame]()
|
|
let allObjs = userDefault.dictionaryRepresentation().sorted { (k1, k2) in
|
|
let left = UInt16(k1.key)
|
|
let right = UInt16(k2.key)
|
|
|
|
switch (left, right) {
|
|
case let (l?, r?):
|
|
return l < r
|
|
case (_?, nil):
|
|
return true
|
|
case (nil, _?):
|
|
return false
|
|
case (nil, nil):
|
|
return k1.key < k2.key
|
|
}
|
|
}
|
|
for (k, v) in allObjs {
|
|
guard let bytes = v as? [UInt8] else { continue }
|
|
guard let parsed = parse(bytes) else { continue }
|
|
|
|
if needDelete {
|
|
userDefault.removeObject(forKey: k)
|
|
}
|
|
|
|
if let f = FramePublish(packetFixedHeaderType: parsed.0, bytes: parsed.1) {
|
|
frames.append(f)
|
|
} else if let f = FramePubRel(packetFixedHeaderType: parsed.0, bytes: parsed.1) {
|
|
frames.append(f)
|
|
}
|
|
}
|
|
return frames
|
|
}
|
|
|
|
}
|