Add parser with pending message handling
This commit is contained in:
@@ -18,15 +18,26 @@ public protocol WebSocket {
|
||||
var session: URLSession { get }
|
||||
var socket: URLSessionWebSocketTask? { get set }
|
||||
var socketDelegate: URLSessionWebSocketDelegate? { get }
|
||||
var message: String { get set }
|
||||
|
||||
var queue: DispatchQueue { get }
|
||||
var timer: DispatchSourceTimer? { get set }
|
||||
var streamContinuation: AsyncStream<String>.Continuation? { get set }
|
||||
|
||||
var parser: WebSocketParser { get }
|
||||
var responseDataLoggable: Bool { get }
|
||||
var credential: WebSocketCredential { get }
|
||||
var delegate: WebSocketDelegate? { get set }
|
||||
}
|
||||
|
||||
|
||||
public protocol WebSocketParser {
|
||||
var pendingMessage: String { get set }
|
||||
func parseData(message: String) -> [Any]
|
||||
}
|
||||
|
||||
|
||||
public protocol WebSocketDelegate: AnyObject {
|
||||
func webSocket(_ webSocket: WebSocket, didPingpong dateTime: String)
|
||||
func webSocket(_ webSocket: WebSocket, didReceive data: Any)
|
||||
}
|
||||
|
||||
@@ -83,6 +94,16 @@ extension AuthWebSocket {
|
||||
socket.resume()
|
||||
self.socket = socket
|
||||
|
||||
let timer = DispatchSource.makeTimerSource(flags: .strict, queue: queue)
|
||||
timer.schedule(deadline: .now(), repeating: 0.02, leeway: .nanoseconds(1_000_000_000 / 100))
|
||||
|
||||
let _self = self
|
||||
timer.setEventHandler {
|
||||
_self.receiveRepeating()
|
||||
}
|
||||
timer.resume()
|
||||
self.timer = timer
|
||||
|
||||
continuation.resume(returning: ())
|
||||
}
|
||||
}
|
||||
@@ -91,6 +112,9 @@ extension AuthWebSocket {
|
||||
public mutating func disconnect() {
|
||||
socket?.cancel(with: .normalClosure, reason: nil)
|
||||
socket = nil
|
||||
|
||||
timer?.cancel()
|
||||
timer = nil
|
||||
}
|
||||
|
||||
|
||||
@@ -102,14 +126,23 @@ extension AuthWebSocket {
|
||||
let request = Domestic.SubscriptionRequest(approvalKey: credential.approvalKey, type: .subscribed, trId: transactionId, trKey: transactionKey)
|
||||
let requestData = try JSONEncoder().encode(request)
|
||||
let requestJson = String(data: requestData, encoding: .utf8)!
|
||||
print(requestJson)
|
||||
|
||||
let stream = AsyncStream<String> { continuation in
|
||||
self.streamContinuation = continuation
|
||||
}
|
||||
|
||||
try await socket.send(.string(requestJson))
|
||||
let r: Domestic.SubscriptionResult = try await receive(stopString: "0|")
|
||||
guard r.body.output != nil, r.body.resultCode == "0" else {
|
||||
throw GeneralError.webSocketError(r.body.resultCode, r.body.messageCode, r.body.message)
|
||||
|
||||
for try await response in stream {
|
||||
let data = Data(response.utf8)
|
||||
let r = try JSONDecoder().decode(Domestic.SubscriptionResult.self, from: data)
|
||||
|
||||
guard r.body.output != nil, r.body.resultCode == "0" else {
|
||||
throw GeneralError.webSocketError(r.body.resultCode, r.body.messageCode, r.body.message)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -121,61 +154,81 @@ extension AuthWebSocket {
|
||||
let request = Domestic.SubscriptionRequest(approvalKey: credential.approvalKey, type: .unsubscribed, trId: transactionId, trKey: transactionKey)
|
||||
let requestData = try JSONEncoder().encode(request)
|
||||
let requestJson = String(data: requestData, encoding: .utf8)!
|
||||
print(requestJson)
|
||||
|
||||
let stream = AsyncStream<String> { continuation in
|
||||
self.streamContinuation = continuation
|
||||
}
|
||||
|
||||
try await socket.send(.string(requestJson))
|
||||
let r: Domestic.SubscriptionResult = try await receive(stopString: "0|")
|
||||
guard r.body.output != nil, r.body.resultCode == "0" else {
|
||||
throw GeneralError.webSocketError(r.body.resultCode, r.body.messageCode, r.body.message)
|
||||
|
||||
for try await response in stream {
|
||||
let data = Data(response.utf8)
|
||||
let r = try JSONDecoder().decode(Domestic.SubscriptionResult.self, from: data)
|
||||
|
||||
guard r.body.output != nil, r.body.resultCode == "0" else {
|
||||
throw GeneralError.webSocketError(r.body.resultCode, r.body.messageCode, r.body.message)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
mutating func receive<T>(stopString: String) async throws -> T where T: Decodable {
|
||||
guard let socket = socket else {
|
||||
throw GeneralError.invalidWebSocket
|
||||
}
|
||||
private func receiveRepeating() {
|
||||
guard let socket = socket else { return }
|
||||
|
||||
let newMessage = try await socket.receive()
|
||||
guard let string = newMessage.string else {
|
||||
throw GeneralError.notStringButData
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
do {
|
||||
let newMessage = try await socket.receive()
|
||||
guard let messageString = newMessage.string else {
|
||||
return
|
||||
}
|
||||
|
||||
if responseDataLoggable {
|
||||
let logName = "log/\(Date().yyyyMMdd_HHmmssSSSS_forFile)_\(transactionId).json"
|
||||
let logUrl = URL.currentDirectory().appending(path: logName)
|
||||
try? messageString.write(toFile: logUrl.path, atomically: true, encoding: .utf8)
|
||||
}
|
||||
try postMessage(messageString)
|
||||
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
||||
if responseDataLoggable {
|
||||
let logName = "log/\(Date().yyyyMMdd_HHmmssSSSS_forFile)_\(transactionId).json"
|
||||
let logUrl = URL.currentDirectory().appending(path: logName)
|
||||
try? string.write(toFile: logUrl.path, atomically: true, encoding: .utf8)
|
||||
}
|
||||
print(string)
|
||||
|
||||
let fragmentData: Data
|
||||
if let r = string.range(of: stopString) {
|
||||
let fragment = message + string[string.startIndex ..< r.upperBound]
|
||||
fragmentData = Data(fragment.utf8)
|
||||
message += string[r.upperBound ..< string.endIndex]
|
||||
print("fragment: \(fragment)")
|
||||
}
|
||||
else {
|
||||
let fragment = message + string
|
||||
fragmentData = Data(fragment.utf8)
|
||||
}
|
||||
|
||||
let t = try JSONDecoder().decode(T.self, from: fragmentData)
|
||||
return t
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
public func receive() async throws -> String? {
|
||||
guard let socket = socket else {
|
||||
throw GeneralError.invalidWebSocket
|
||||
}
|
||||
|
||||
let message = try await socket.receive()
|
||||
let str = message.string
|
||||
private func postMessage(_ message: String) throws {
|
||||
print(message)
|
||||
|
||||
return str
|
||||
let dataArray = parser.parseData(message: message)
|
||||
for data in dataArray {
|
||||
if let data = data as? Domestic.WebSocketResult, case .json(let string) = data {
|
||||
let data = Data(string.utf8)
|
||||
let result = try JSONDecoder().decode(Domestic.GeneralResult.self, from: data)
|
||||
|
||||
switch result.header.trId {
|
||||
case "PINGPONG":
|
||||
if let dateTime = result.header.dateTime {
|
||||
delegate?.webSocket(self, didPingpong: dateTime)
|
||||
}
|
||||
|
||||
case "H0STCNT0", "H0STASP0", "H0STCNI0":
|
||||
//let subscription = try JSONDecoder().decode(Domestic.SubscriptionResult.self, from: data)
|
||||
//print("got message...")
|
||||
streamContinuation?.yield(string)
|
||||
streamContinuation?.finish()
|
||||
|
||||
default:
|
||||
assertionFailure("Unknown trId \(result.header.trId)")
|
||||
}
|
||||
}
|
||||
|
||||
delegate?.webSocket(self, didReceive: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ extension Domestic {
|
||||
public var socketDelegate: URLSessionWebSocketDelegate? { event }
|
||||
public var message: String = ""
|
||||
public var queue: DispatchQueue = DispatchQueue(label: "kissme.asking_price_websocket")
|
||||
public var timer: DispatchSourceTimer?
|
||||
|
||||
public var streamContinuation: AsyncStream<String>.Continuation?
|
||||
public var parser: WebSocketParser = KissWebSocketParser()
|
||||
public var credential: WebSocketCredential
|
||||
public var delegate: WebSocketDelegate?
|
||||
public let transactionKey: String
|
||||
|
||||
@@ -26,6 +26,10 @@ extension Domestic {
|
||||
public var socketDelegate: URLSessionWebSocketDelegate? { event }
|
||||
public var message: String = ""
|
||||
public var queue: DispatchQueue = DispatchQueue(label: "kissme.contact_notice_websocket")
|
||||
public var timer: DispatchSourceTimer?
|
||||
|
||||
public var streamContinuation: AsyncStream<String>.Continuation?
|
||||
public var parser: WebSocketParser = KissWebSocketParser()
|
||||
public var credential: WebSocketCredential
|
||||
public var delegate: WebSocketDelegate?
|
||||
public let transactionKey: String
|
||||
|
||||
@@ -26,6 +26,10 @@ extension Domestic {
|
||||
public var socketDelegate: URLSessionWebSocketDelegate? { event }
|
||||
public var message: String = ""
|
||||
public var queue: DispatchQueue = DispatchQueue(label: "kissme.contact_price_websocket")
|
||||
public var timer: DispatchSourceTimer?
|
||||
|
||||
public var streamContinuation: AsyncStream<String>.Continuation?
|
||||
public var parser: WebSocketParser = KissWebSocketParser()
|
||||
public var credential: WebSocketCredential
|
||||
public var delegate: WebSocketDelegate?
|
||||
public let transactionKey: String
|
||||
|
||||
@@ -606,7 +606,7 @@ extension Domestic.MarketOperationCode {
|
||||
|
||||
extension Domestic.WebSocketResult {
|
||||
|
||||
static public func parse(_ str: String) throws -> [Domestic.WebSocketResult] {
|
||||
static public func parse(_ str: String) throws -> ([Domestic.WebSocketResult], String.Index) {
|
||||
var dataArray = [Domestic.WebSocketResult]()
|
||||
var nextAt = str.startIndex
|
||||
let charset = CharacterSet(charactersIn: "{}")
|
||||
@@ -662,7 +662,7 @@ extension Domestic.WebSocketResult {
|
||||
}
|
||||
}
|
||||
|
||||
return dataArray
|
||||
return (dataArray, nextAt)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// Domestic.WebSocketParser.swift
|
||||
// KissMe
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/08/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension Domestic {
|
||||
|
||||
class KissWebSocketParser: WebSocketParser {
|
||||
var pendingMessage: String
|
||||
|
||||
init() {
|
||||
pendingMessage = ""
|
||||
}
|
||||
|
||||
func parseData(message: String) -> [Any] {
|
||||
pendingMessage += message
|
||||
|
||||
do {
|
||||
let (results, endAt) = try Domestic.WebSocketResult.parse(pendingMessage)
|
||||
pendingMessage = String(pendingMessage[endAt ..< pendingMessage.endIndex])
|
||||
return results
|
||||
|
||||
} catch {
|
||||
switch error {
|
||||
case GeneralError.notEnoughWebSocketData:
|
||||
// TODO: work more exception handling
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,136 +14,4 @@ import KissMe
|
||||
//test_get_websocket_key_and_contact_price()
|
||||
//test_get_websocket_key_and_asking_price()
|
||||
//test_parse_contact_price_response()
|
||||
|
||||
|
||||
func test_parse_contact_price_response() {
|
||||
let str = "{\"header\":{\"tr_id\":\"H0STCNT0\",\"tr_key\":\"005930\",\"encrypt\":\"N\"},\"body\":{\"rt_cd\":\"0\",\"msg_cd\":\"OPSP0000\",\"msg1\":\"SUBSCRIBE SUCCESS\",\"output\":{\"iv\":\"dcc3c442acfb8b9a\",\"key\":\"vcvxscahuklwkiawiuxbsfcmsulqjejf\"}}}0|H0STCNT0|001|005930^134305^68100^2^1000^1.49^68305.67^68300^68700^67900^68100^68000^1^11393808^778261604700^32559^26679^-5880^79.02^6084367^4807987^1^0.43^119.32^090027^5^-200^091809^5^-600^113615^2^200^20230824^20^N^309354^354766^2143494^2041321^0.19^6698642^170.09^0^^68300{\"header\":{\"tr_id\":\"PINGPONG\",\"datetime\":\"20230824212922\"}}"
|
||||
|
||||
do {
|
||||
let dataArray = try Domestic.WebSocketResult.parse(str)
|
||||
for data in dataArray {
|
||||
switch data {
|
||||
case .json(let str):
|
||||
print("json: \(str)")
|
||||
case .contractPrice(let price):
|
||||
print("contractPrice: \(price)")
|
||||
case .askingPrice(let price):
|
||||
print("askingPrice: \(price)")
|
||||
case .contractNotice(let notice):
|
||||
print("contractNotice: \(notice)")
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func test_get_websocket_key_and_asking_price() {
|
||||
let isMock = false
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
guard let (account, approvalKey) = await test_get_websocket_key(isMock: isMock) else {
|
||||
return
|
||||
}
|
||||
|
||||
let webSocketCredential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
var socket = Domestic.AskingPriceWebSocket(credential: webSocketCredential, productCode: KissConsole.defaultProductNo)
|
||||
|
||||
do {
|
||||
try await socket.connect()
|
||||
let result = try await socket.subscribe()
|
||||
print(result)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
let result2 = try await socket.unsubscribe()
|
||||
print(result2)
|
||||
|
||||
if let message = try await socket.receive() {
|
||||
print(message)
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
socket.disconnect()
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 1)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
func test_get_websocket_key_and_contact_price() {
|
||||
let isMock = false
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
guard let (account, approvalKey) = await test_get_websocket_key(isMock: isMock) else {
|
||||
return
|
||||
}
|
||||
|
||||
let webSocketCredential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
var socket = Domestic.ContractPriceWebSocket(credential: webSocketCredential, productCode: KissConsole.defaultProductNo)
|
||||
|
||||
do {
|
||||
try await socket.connect()
|
||||
let result = try await socket.subscribe()
|
||||
print(result)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
let result2 = try await socket.unsubscribe()
|
||||
print(result2)
|
||||
|
||||
if let message = try await socket.receive() {
|
||||
print(message)
|
||||
}
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
socket.disconnect()
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 1)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
func test_get_websocket_key(isMock: Bool) async -> (KissAccount, String)? {
|
||||
let credential: Credential
|
||||
|
||||
do {
|
||||
credential = try KissCredential(isMock: isMock)
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
|
||||
let account = KissAccount(credential: credential)
|
||||
do {
|
||||
/// Return existing valid key
|
||||
if let approvalKey = account.approvalKey {
|
||||
return (account, approvalKey)
|
||||
}
|
||||
|
||||
if try await account.login() {
|
||||
let approvalKey = try await account.getApprovalKey()
|
||||
print("approvalKey : \(approvalKey)")
|
||||
return (account, approvalKey)
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
test_websocket_dump_data()
|
||||
|
||||
197
KissMeConsole/Sources/test_websocket.swift
Normal file
197
KissMeConsole/Sources/test_websocket.swift
Normal file
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// test_websocket.swift
|
||||
// KissMeConsole
|
||||
//
|
||||
// Created by ened-book-m1 on 2023/08/26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KissMe
|
||||
|
||||
|
||||
class DumpWebSocketData: NSObject {
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension DumpWebSocketData: WebSocketDelegate {
|
||||
|
||||
func webSocket(_ webSocket: KissMe.WebSocket, didPingpong dateTime: String) {
|
||||
}
|
||||
|
||||
func webSocket(_ webSocket: WebSocket, didReceive data: Any) {
|
||||
guard let data = data as? Domestic.WebSocketData else { return }
|
||||
|
||||
switch data {
|
||||
case .contractPrice(let price):
|
||||
print("contractPrice \(price.shortCode)")
|
||||
case .askingPrice(let price):
|
||||
print("askingPrice \(price.shortCode)")
|
||||
case .contractNotice(let notice):
|
||||
print("askingPrice \(notice.shortCode)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func test_websocket_dump_data() {
|
||||
let isMock = false
|
||||
|
||||
let dumpData = DumpWebSocketData()
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
guard let (account, approvalKey) = await test_get_websocket_key(isMock: isMock) else {
|
||||
return
|
||||
}
|
||||
|
||||
let webSocketCredential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
var socket = Domestic.ContractPriceWebSocket(credential: webSocketCredential, productCode: KissConsole.defaultProductNo)
|
||||
socket.delegate = dumpData
|
||||
|
||||
do {
|
||||
try await socket.connect()
|
||||
let result = try await socket.subscribe()
|
||||
print(result)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
let result2 = try await socket.unsubscribe()
|
||||
print(result2)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
socket.disconnect()
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 1)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
func test_parse_contact_price_response() {
|
||||
let str = "{\"header\":{\"tr_id\":\"H0STCNT0\",\"tr_key\":\"005930\",\"encrypt\":\"N\"},\"body\":{\"rt_cd\":\"0\",\"msg_cd\":\"OPSP0000\",\"msg1\":\"SUBSCRIBE SUCCESS\",\"output\":{\"iv\":\"dcc3c442acfb8b9a\",\"key\":\"vcvxscahuklwkiawiuxbsfcmsulqjejf\"}}}0|H0STCNT0|001|005930^134305^68100^2^1000^1.49^68305.67^68300^68700^67900^68100^68000^1^11393808^778261604700^32559^26679^-5880^79.02^6084367^4807987^1^0.43^119.32^090027^5^-200^091809^5^-600^113615^2^200^20230824^20^N^309354^354766^2143494^2041321^0.19^6698642^170.09^0^^68300{\"header\":{\"tr_id\":\"PINGPONG\",\"datetime\":\"20230824212922\"}}"
|
||||
|
||||
do {
|
||||
let (dataArray, _) = try Domestic.WebSocketResult.parse(str)
|
||||
for data in dataArray {
|
||||
switch data {
|
||||
case .json(let str):
|
||||
print("json: \(str)")
|
||||
case .contractPrice(let price):
|
||||
print("contractPrice: \(price)")
|
||||
case .askingPrice(let price):
|
||||
print("askingPrice: \(price)")
|
||||
case .contractNotice(let notice):
|
||||
print("contractNotice: \(notice)")
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func test_get_websocket_key_and_asking_price() {
|
||||
let isMock = false
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
guard let (account, approvalKey) = await test_get_websocket_key(isMock: isMock) else {
|
||||
return
|
||||
}
|
||||
|
||||
let webSocketCredential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
var socket = Domestic.AskingPriceWebSocket(credential: webSocketCredential, productCode: KissConsole.defaultProductNo)
|
||||
|
||||
do {
|
||||
try await socket.connect()
|
||||
let result = try await socket.subscribe()
|
||||
print(result)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
let result2 = try await socket.unsubscribe()
|
||||
print(result2)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
socket.disconnect()
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 1)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
func test_get_websocket_key_and_contact_price() {
|
||||
let isMock = false
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
Task {
|
||||
guard let (account, approvalKey) = await test_get_websocket_key(isMock: isMock) else {
|
||||
return
|
||||
}
|
||||
|
||||
let webSocketCredential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey)
|
||||
|
||||
var socket = Domestic.ContractPriceWebSocket(credential: webSocketCredential, productCode: KissConsole.defaultProductNo)
|
||||
|
||||
do {
|
||||
try await socket.connect()
|
||||
let result = try await socket.subscribe()
|
||||
print(result)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
let result2 = try await socket.unsubscribe()
|
||||
print(result2)
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 3)
|
||||
socket.disconnect()
|
||||
|
||||
try await Task.sleep(nanoseconds: 1_000_000_000 * 1)
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
||||
semaphore.signal()
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
func test_get_websocket_key(isMock: Bool) async -> (KissAccount, String)? {
|
||||
let credential: Credential
|
||||
|
||||
do {
|
||||
credential = try KissCredential(isMock: isMock)
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
|
||||
let account = KissAccount(credential: credential)
|
||||
do {
|
||||
/// Return existing valid key
|
||||
if let approvalKey = account.approvalKey {
|
||||
return (account, approvalKey)
|
||||
}
|
||||
|
||||
if try await account.login() {
|
||||
let approvalKey = try await account.getApprovalKey()
|
||||
print("approvalKey : \(approvalKey)")
|
||||
return (account, approvalKey)
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -56,6 +56,7 @@
|
||||
34DA3E9D2A9A028200BB3439 /* Domestic.WebSocketData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DA3E9C2A9A028200BB3439 /* Domestic.WebSocketData.swift */; };
|
||||
34DA3E9F2A9A0B8D00BB3439 /* Domestic.WebSocketAES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DA3E9E2A9A0B8D00BB3439 /* Domestic.WebSocketAES.swift */; };
|
||||
34DA3EA22A9A10A100BB3439 /* Crypto in Frameworks */ = {isa = PBXBuildFile; productRef = 34DA3EA12A9A10A100BB3439 /* Crypto */; };
|
||||
34DA3EA62A9B1D7500BB3439 /* Domestic.WebSocketParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DA3EA52A9B1D7500BB3439 /* Domestic.WebSocketParser.swift */; };
|
||||
34E7B9112A49BD2800B3AB9F /* DomesticIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E7B9102A49BD2800B3AB9F /* DomesticIndex.swift */; };
|
||||
34EC4D212A7ACB07002F947C /* CompanyPartitionMergerDecisionResult.json in Resources */ = {isa = PBXBuildFile; fileRef = 34EC4D202A7ACB06002F947C /* CompanyPartitionMergerDecisionResult.json */; };
|
||||
34EC4D242A7F27A8002F947C /* CompanyPartitionDecisionResult.json in Resources */ = {isa = PBXBuildFile; fileRef = 34EC4D232A7F27A8002F947C /* CompanyPartitionDecisionResult.json */; };
|
||||
@@ -200,6 +201,7 @@
|
||||
34D3680E2A2AA0BE005E6756 /* PropertyIterable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyIterable.swift; sourceTree = "<group>"; };
|
||||
34DA3E9C2A9A028200BB3439 /* Domestic.WebSocketData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Domestic.WebSocketData.swift; sourceTree = "<group>"; };
|
||||
34DA3E9E2A9A0B8D00BB3439 /* Domestic.WebSocketAES.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Domestic.WebSocketAES.swift; sourceTree = "<group>"; };
|
||||
34DA3EA52A9B1D7500BB3439 /* Domestic.WebSocketParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Domestic.WebSocketParser.swift; sourceTree = "<group>"; };
|
||||
34E7B9102A49BD2800B3AB9F /* DomesticIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticIndex.swift; sourceTree = "<group>"; };
|
||||
34EC4D202A7ACB06002F947C /* CompanyPartitionMergerDecisionResult.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CompanyPartitionMergerDecisionResult.json; sourceTree = "<group>"; };
|
||||
34EC4D232A7F27A8002F947C /* CompanyPartitionDecisionResult.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = CompanyPartitionDecisionResult.json; sourceTree = "<group>"; };
|
||||
@@ -415,6 +417,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
34DA3E9C2A9A028200BB3439 /* Domestic.WebSocketData.swift */,
|
||||
34DA3EA52A9B1D7500BB3439 /* Domestic.WebSocketParser.swift */,
|
||||
34DA3E9E2A9A0B8D00BB3439 /* Domestic.WebSocketAES.swift */,
|
||||
34BC447C2A86635A0052D8EB /* Domestic.ContractPriceWebSocket.swift */,
|
||||
34BC447A2A8663430052D8EB /* Domestic.AskingPriceWebSocket.swift */,
|
||||
@@ -824,6 +827,7 @@
|
||||
3435A7F72A35D82000D604F1 /* DomesticShortSelling.swift in Sources */,
|
||||
34C1BA552A5B033E00423D64 /* DomesticDartListedCompany.swift in Sources */,
|
||||
341F5F072A14634F00962D48 /* Foundation+Extensions.swift in Sources */,
|
||||
34DA3EA62A9B1D7500BB3439 /* Domestic.WebSocketParser.swift in Sources */,
|
||||
341F5F032A11A2BC00962D48 /* Credential.swift in Sources */,
|
||||
34BC44792A8657D50052D8EB /* WebSocket.swift in Sources */,
|
||||
341F5EB02A0A80EC00962D48 /* KissMe.docc in Sources */,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
349327F72A20E3E300097063 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349327F62A20E3E300097063 /* Foundation+Extensions.swift */; };
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349843202A242AC900E85B08 /* KissConsole+CSV.swift */; };
|
||||
34D3680D2A280801005E6756 /* KissConsole+Candle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D3680C2A280801005E6756 /* KissConsole+Candle.swift */; };
|
||||
34DA3EA42A9A176B00BB3439 /* test_websocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DA3EA32A9A176B00BB3439 /* test_websocket.swift */; };
|
||||
34EC4D1F2A7A7365002F947C /* KissConsole+News.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EC4D1E2A7A7365002F947C /* KissConsole+News.swift */; };
|
||||
34EE76862A1C391B009761D2 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EDB2A0A8C4600962D48 /* KissMe.framework */; };
|
||||
34EE76872A1C391B009761D2 /* KissMe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EDB2A0A8C4600962D48 /* KissMe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@@ -58,6 +59,7 @@
|
||||
3498431E2A24287600E85B08 /* KissMeConsoleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KissMeConsoleTests.swift; sourceTree = "<group>"; };
|
||||
349843202A242AC900E85B08 /* KissConsole+CSV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+CSV.swift"; sourceTree = "<group>"; };
|
||||
34D3680C2A280801005E6756 /* KissConsole+Candle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Candle.swift"; sourceTree = "<group>"; };
|
||||
34DA3EA32A9A176B00BB3439 /* test_websocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = test_websocket.swift; sourceTree = "<group>"; };
|
||||
34EC4D1E2A7A7365002F947C /* KissConsole+News.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+News.swift"; sourceTree = "<group>"; };
|
||||
34F190122A4441F00068C697 /* KissConsole+Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Test.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -97,6 +99,7 @@
|
||||
children = (
|
||||
341F5ED32A0A8B9000962D48 /* main.swift */,
|
||||
341F5F042A13B82F00962D48 /* test.swift */,
|
||||
34DA3EA32A9A176B00BB3439 /* test_websocket.swift */,
|
||||
348168482A2F92AC00A50BD3 /* KissContext.swift */,
|
||||
341F5F082A1463A100962D48 /* KissConsole.swift */,
|
||||
34D3680C2A280801005E6756 /* KissConsole+Candle.swift */,
|
||||
@@ -196,6 +199,7 @@
|
||||
349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */,
|
||||
348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */,
|
||||
3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */,
|
||||
34DA3EA42A9A176B00BB3439 /* test_websocket.swift in Sources */,
|
||||
34F190132A4441F00068C697 /* KissConsole+Test.swift in Sources */,
|
||||
34EC4D1F2A7A7365002F947C /* KissConsole+News.swift in Sources */,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user