From cc1009c987343563c9d69b47caa4e1b5ae056480 Mon Sep 17 00:00:00 2001 From: ened Date: Fri, 29 Sep 2023 20:54:52 +0900 Subject: [PATCH] Long time not working --- KissMe/Sources/Common/GeneralError.swift | 2 + KissMe/Sources/Common/WebSocket.swift | 33 +++ .../Domestic.AskingPriceWebSocket.swift | 2 + .../Domestic.ContractNoticeWebSocket.swift | 4 +- .../Domestic.ContractPriceWebSocket.swift | 2 + KissMe/Sources/KissProfile.swift | 35 ++- KissMe/Sources/Login/Login.swift | 7 +- .../Sources/KissConsole+WebSocket.swift | 264 ++++++++++++++++++ KissMeConsole/Sources/KissConsole.swift | 27 ++ KissMeConsole/Sources/main.swift | 4 +- KissMeConsole/Sources/test_websocket.swift | 94 ++++++- README.md | 1 + .../KissMeConsole.xcodeproj/project.pbxproj | 4 + .../xcschemes/KissMeConsole.xcscheme | 3 +- 14 files changed, 454 insertions(+), 28 deletions(-) create mode 100644 KissMeConsole/Sources/KissConsole+WebSocket.swift diff --git a/KissMe/Sources/Common/GeneralError.swift b/KissMe/Sources/Common/GeneralError.swift index 8fe7a7e..5447001 100644 --- a/KissMe/Sources/Common/GeneralError.swift +++ b/KissMe/Sources/Common/GeneralError.swift @@ -27,6 +27,7 @@ public enum GeneralError: Error { case noData // MARK: WebSocket + case cannotIssueApprovalKey case invalidWebSocket case webSocketError(_ code: String, _ messageCode: String, _ message: String) case notStringButData @@ -36,4 +37,5 @@ public enum GeneralError: Error { case invalidWebSocketData_EncryptionField case invalidWebSocketData_TrIdField case notEnoughWebSocketData + case cannotSubscribeWebSocket } diff --git a/KissMe/Sources/Common/WebSocket.swift b/KissMe/Sources/Common/WebSocket.swift index 0ee5c59..819de83 100644 --- a/KissMe/Sources/Common/WebSocket.swift +++ b/KissMe/Sources/Common/WebSocket.swift @@ -8,6 +8,9 @@ import Foundation +let SecondsForOneDay: TimeInterval = 60 * 60 * 24 + + public protocol WebSocket { var isMockAvailable: Bool { get } var domain: String { get } @@ -81,6 +84,30 @@ extension AuthWebSocket { public var responseDataLoggable: Bool { true } + public func newSession() -> URLSession { + let config = URLSessionConfiguration.ephemeral + config.waitsForConnectivity = false + config.timeoutIntervalForRequest = 60 // 60 seconds + config.timeoutIntervalForResource = SecondsForOneDay * 7 // 7 days + config.allowsCellularAccess = true + config.requestCachePolicy = .reloadIgnoringLocalCacheData +// config.multipathServiceType = .handover +// config.httpShouldSetCookies = true +// config.httpCookieAcceptPolicy = .always + config.httpMaximumConnectionsPerHost = 1 + config.networkServiceType = .responsiveData + + let queue = OperationQueue() + + // 6 -1 + print("\(config.httpMaximumConnectionsPerHost) \(queue.maxConcurrentOperationCount)") + + queue.maxConcurrentOperationCount = 1 + queue.underlyingQueue = self.queue + + let session = URLSession(configuration: config, delegate: nil, delegateQueue: queue) + return session + } public mutating func connect() async throws { return try await withUnsafeThrowingContinuation { continuation in @@ -126,6 +153,7 @@ 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 { continuation in self.streamContinuation = continuation @@ -154,6 +182,7 @@ 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 { continuation in self.streamContinuation = continuation @@ -222,6 +251,10 @@ extension AuthWebSocket { streamContinuation?.yield(string) streamContinuation?.finish() + case "(null)": + streamContinuation?.yield(string) + streamContinuation?.finish() + default: assertionFailure("Unknown trId \(result.header.trId)") } diff --git a/KissMe/Sources/Domestic/Realtime/Domestic.AskingPriceWebSocket.swift b/KissMe/Sources/Domestic/Realtime/Domestic.AskingPriceWebSocket.swift index 4c87c37..328b3d1 100644 --- a/KissMe/Sources/Domestic/Realtime/Domestic.AskingPriceWebSocket.swift +++ b/KissMe/Sources/Domestic/Realtime/Domestic.AskingPriceWebSocket.swift @@ -22,6 +22,7 @@ extension Domestic { "H0STASP0" } + public var session: URLSession { newSession() } public var socket: URLSessionWebSocketTask? public var socketDelegate: URLSessionWebSocketDelegate? { event } public var message: String = "" @@ -33,6 +34,7 @@ extension Domestic { public var credential: WebSocketCredential public var delegate: WebSocketDelegate? public let transactionKey: String + public var productCode: String { transactionKey } var event: Event! public init(credential: WebSocketCredential, productCode: String) { diff --git a/KissMe/Sources/Domestic/Realtime/Domestic.ContractNoticeWebSocket.swift b/KissMe/Sources/Domestic/Realtime/Domestic.ContractNoticeWebSocket.swift index d820ac1..edbb18e 100644 --- a/KissMe/Sources/Domestic/Realtime/Domestic.ContractNoticeWebSocket.swift +++ b/KissMe/Sources/Domestic/Realtime/Domestic.ContractNoticeWebSocket.swift @@ -19,9 +19,10 @@ extension Domestic { "/tryitout/\(transactionId)" } public var transactionId: String { - "H0STCNI0" + credential.isMock ? "H0STCNI9": "H0STCNI0" } + public var session: URLSession { newSession() } public var socket: URLSessionWebSocketTask? public var socketDelegate: URLSessionWebSocketDelegate? { event } public var message: String = "" @@ -33,6 +34,7 @@ extension Domestic { public var credential: WebSocketCredential public var delegate: WebSocketDelegate? public let transactionKey: String + public var htsID: String { transactionKey } var event: Event! public init(credential: WebSocketCredential, htsID: String) { diff --git a/KissMe/Sources/Domestic/Realtime/Domestic.ContractPriceWebSocket.swift b/KissMe/Sources/Domestic/Realtime/Domestic.ContractPriceWebSocket.swift index cf03233..58dfcc0 100644 --- a/KissMe/Sources/Domestic/Realtime/Domestic.ContractPriceWebSocket.swift +++ b/KissMe/Sources/Domestic/Realtime/Domestic.ContractPriceWebSocket.swift @@ -22,6 +22,7 @@ extension Domestic { "H0STCNT0" } + public var session: URLSession { newSession() } public var socket: URLSessionWebSocketTask? public var socketDelegate: URLSessionWebSocketDelegate? { event } public var message: String = "" @@ -33,6 +34,7 @@ extension Domestic { public var credential: WebSocketCredential public var delegate: WebSocketDelegate? public let transactionKey: String + public var productCode: String { transactionKey } var event: Event! public init(credential: WebSocketCredential, productCode: String) { diff --git a/KissMe/Sources/KissProfile.swift b/KissMe/Sources/KissProfile.swift index cab01f8..7e69031 100644 --- a/KissMe/Sources/KissProfile.swift +++ b/KissMe/Sources/KissProfile.swift @@ -37,15 +37,12 @@ public class KissProfile { return profile.recent?.isMock } - public var approvalKey: String? { + public var approvalKeys: [ApprovalKey] { profileLock.lock() defer { profileLock.unlock() } - guard let expired = profile.recent?.approvalKeyExpired, expired > Date() else { - return nil - } - return profile.recent?.approvalKey + return profile.recent?.approvalKeys ?? [] } public init() { @@ -54,18 +51,18 @@ public class KissProfile { func setAccessToken(_ accessToken: String, expired: Date, isMock: Bool) { profileLock.lock() - let approvalKey = profile.recent?.approvalKey - let approvalKeyExpired = profile.recent?.approvalKeyExpired - profile.recent = Recent(isMock: isMock, accessToken: accessToken, accessTokenExpired: expired, approvalKey: approvalKey, approvalKeyExpired: approvalKeyExpired) + let keys = profile.recent?.approvalKeys ?? [] + profile.recent = Recent(isMock: isMock, accessToken: accessToken, accessTokenExpired: expired, approvalKeys: keys) profileLock.unlock() saveProfile() } - func setApprovalKey(_ approvalKey: String, expired: Date) { + func addApprovalKey(_ approvalKey: String, expired: Date) { + guard let recent = profile.recent else { return } + profileLock.lock() - let recent = profile.recent?.setted(approvalKey: approvalKey, approvalKeyExpired: expired) - profile.recent = recent + profile.recent = recent.added(approvalKey: approvalKey, approvalKeyExpired: expired) profileLock.unlock() saveProfile() @@ -93,15 +90,23 @@ extension KissProfile { } } + public struct ApprovalKey: Codable { + public let key: String + public let keyExpired: Date + + public var isExpired: Bool { keyExpired >= Date() } + } + public struct Recent: Codable { public let isMock: Bool public let accessToken: String public let accessTokenExpired: Date - public let approvalKey: String? - public let approvalKeyExpired: Date? + public var approvalKeys: [ApprovalKey] - func setted(approvalKey: String, approvalKeyExpired: Date) -> Recent { - return Recent(isMock: self.isMock, accessToken: self.accessToken, accessTokenExpired: self.accessTokenExpired, approvalKey: approvalKey, approvalKeyExpired: approvalKeyExpired) + func added(approvalKey: String, approvalKeyExpired: Date) -> Recent { + var keys = self.approvalKeys + keys.append(ApprovalKey(key: approvalKey, keyExpired: approvalKeyExpired)) + return Recent(isMock: self.isMock, accessToken: self.accessToken, accessTokenExpired: self.accessTokenExpired, approvalKeys: keys) } } diff --git a/KissMe/Sources/Login/Login.swift b/KissMe/Sources/Login/Login.swift index a49751c..4cec7e4 100644 --- a/KissMe/Sources/Login/Login.swift +++ b/KissMe/Sources/Login/Login.swift @@ -183,18 +183,13 @@ extension KissAccount { public func getApprovalKey() async throws -> String { - if let approvalKey = approvalKey { - return approvalKey - } - return try await withUnsafeThrowingContinuation { continuation in - let SecondsForOneDay: TimeInterval = 60 * 60 * 24 let request = ApprovalKeyAuthRequest(credential: credential) request.query { result in switch result { case .success(let result): - self.setApprovalKey(result.approvalKey, expired: Date().addingTimeInterval(SecondsForOneDay - 60)) + self.addApprovalKey(result.approvalKey, expired: Date().addingTimeInterval(SecondsForOneDay - 60)) continuation.resume(returning: result.approvalKey) case .failure(let error): continuation.resume(throwing: error) diff --git a/KissMeConsole/Sources/KissConsole+WebSocket.swift b/KissMeConsole/Sources/KissConsole+WebSocket.swift new file mode 100644 index 0000000..856d878 --- /dev/null +++ b/KissMeConsole/Sources/KissConsole+WebSocket.swift @@ -0,0 +1,264 @@ +// +// KissConsole+WebSocket.swift +// KissMeConsole +// +// Created by ened-book-m1 on 2023/09/04. +// + +import Foundation +import KissMe + + +extension KissConsole { +} + + +public protocol KissAuctionPriceDelegate: AnyObject { + func auction(_ auction: KissAuction, contactPrice: Domestic.ContractPrice) + func auction(_ auction: KissAuction, askingPrice: Domestic.AskingPrice) +} + + +public protocol KissAutionNoticeDelegate: AnyObject { + func auction(_ auction: KissAuction, contactNotice: Domestic.ContractNotice) +} + + +public class KissAuction: NSObject { + + public class Slot { + var contractPrice: Domestic.ContractPriceWebSocket! + var askingPrice: Domestic.AskingPriceWebSocket! + weak var delegate: KissAuctionPriceDelegate? + + var contractPriceReceiver: ContractPriceReceiver! + var askingPriceReceiver: AskingPriceReceiver! + + init(delegate: KissAuctionPriceDelegate?) { + self.delegate = delegate + } + + func setup(contractPrice: Domestic.ContractPriceWebSocket, askingPrice: Domestic.AskingPriceWebSocket) { + self.contractPrice = contractPrice + self.askingPrice = askingPrice + } + + func cleanup() { + contractPriceReceiver.slot = nil + contractPriceReceiver.owner = nil + askingPriceReceiver.slot = nil + askingPriceReceiver.owner = nil + } + } + + class ContractPriceReceiver { + var slot: Slot? + weak var owner: KissAuction? + + init(slot: Slot?, owner: KissAuction) { + self.slot = slot + self.owner = owner + } + } + + class AskingPriceReceiver { + var slot: Slot? + weak var owner: KissAuction? + + init(slot: Slot?, owner: KissAuction) { + self.slot = slot + self.owner = owner + } + } + + class ContractNoticeReceiver { + weak var delegate: KissAutionNoticeDelegate? + weak var owner: KissAuction? + + init(delegate: KissAutionNoticeDelegate?, owner: KissAuction) { + self.delegate = delegate + self.owner = owner + } + } + + + // TODO: ensure thread-safe + var slots: [Slot] + + let loggable: Bool + let account: KissAccount + var contractNotice: Domestic.ContractNoticeWebSocket? + var contractNoticeReceiver: ContractNoticeReceiver? + + public init(account: KissAccount, loggable: Bool) { + self.slots = [Slot]() + self.loggable = loggable + self.account = account + self.contractNotice = nil + self.contractNoticeReceiver = nil + } + + + public func startNotice(delegate: KissAutionNoticeDelegate?) async throws -> Bool { + //account.approvalKeys. + guard let isMock = account.isMock else { + throw GeneralError.invalidAccessToken + } + let approvalKey = try await account.getApprovalKey() + print("notice price key: \(approvalKey)") + let credential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey) + + contractNotice = Domestic.ContractNoticeWebSocket(credential: credential, htsID: account.accountNo) + contractNoticeReceiver = ContractNoticeReceiver(delegate: delegate, owner: self) + + try await contractNotice!.connect() + let result = try await contractNotice!.subscribe() + return result + } + + + public func stopNotice() async throws -> Bool { + guard var contractNotice = contractNotice else { + return false + } + + let result = try await contractNotice.unsubscribe() + contractNotice.disconnect() + contractNoticeReceiver?.delegate = nil + return result + } + + + public func addSlot(productNo: String, delegate: KissAuctionPriceDelegate?) async throws -> Slot { + guard let isMock = account.isMock else { + throw GeneralError.invalidAccessToken + } + + func createContractPrice() async throws -> Domestic.ContractPriceWebSocket { + let approvalKey = try await account.getApprovalKey() + print("contract price key: \(approvalKey)") + let credential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey) + + let contractPrice = Domestic.ContractPriceWebSocket(credential: credential, productCode: productNo) + return contractPrice + } + + func createAskingPrice() async throws -> Domestic.AskingPriceWebSocket { + let approvalKey = try await account.getApprovalKey() + print("asking price key: \(approvalKey)") + let credential = KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey) + + let askingPrice = Domestic.AskingPriceWebSocket(credential: credential, productCode: productNo) + return askingPrice + } + + let slot = Slot(delegate: delegate) + slot.askingPriceReceiver = AskingPriceReceiver(slot: slot, owner: self) + slot.contractPriceReceiver = ContractPriceReceiver(slot: slot, owner: self) + + var contractPrice = try await createContractPrice() + try await contractPrice.connect() + let result = try await contractPrice.subscribe() + guard result else { + throw GeneralError.cannotSubscribeWebSocket + } + + var askingPrice = try await createAskingPrice() + try await askingPrice.connect() + let result2 = try await askingPrice.subscribe() + guard result2 else { + throw GeneralError.cannotSubscribeWebSocket + } + + slot.setup(contractPrice: contractPrice, askingPrice: askingPrice) + slots.append(slot) + return slot + } + + + public func getSlot(productNo: String) -> Slot? { + guard let slot = slots.first(where: { $0.contractPrice.productCode == productNo }) else { + return nil + } + return slot + } + + + public func removeSlot(productNo: String) async throws -> Bool { + guard let slotIndex = slots.firstIndex(where: { $0.contractPrice.productCode == productNo }) else { + return false + } + let slot = slots.remove(at: slotIndex) + slot.cleanup() + return true + } + + private func getWebSocketKey() async throws -> KissWebSocketCredential { + guard let isMock = account.isMock else { + throw GeneralError.invalidAccessToken + } + + if try await account.login() { + let approvalKey = try await account.getApprovalKey() + return KissWebSocketCredential(isMock: isMock, accountNo: account.accountNo, approvalKey: approvalKey) + } + throw GeneralError.cannotIssueApprovalKey + } +} + + +extension KissAuction.ContractPriceReceiver: WebSocketDelegate { + + func webSocket(_ webSocket: WebSocket, didPingpong dateTime: String) { + } + + func webSocket(_ webSocket: WebSocket, didReceive data: Any) { + guard let owner = owner else { return } + guard let data = data as? Domestic.WebSocketData else { return } + + switch data { + case .contractPrice(let price): + slot?.delegate?.auction(owner, contactPrice: price) + default: + break + } + } +} + + +extension KissAuction.AskingPriceReceiver: WebSocketDelegate { + + func webSocket(_ webSocket: WebSocket, didPingpong dateTime: String) { + } + + func webSocket(_ webSocket: WebSocket, didReceive data: Any) { + guard let owner = owner else { return } + guard let data = data as? Domestic.WebSocketData else { return } + + switch data { + case .askingPrice(let price): + slot?.delegate?.auction(owner, askingPrice: price) + default: + break + } + } +} + + +extension KissAuction.ContractNoticeReceiver: WebSocketDelegate { + + func webSocket(_ webSocket: WebSocket, didPingpong dateTime: String) { + } + + func webSocket(_ webSocket: WebSocket, didReceive data: Any) { + guard let owner = owner else { return } + guard let data = data as? Domestic.WebSocketData else { return } + + switch data { + case .contractNotice(let notice): + delegate?.auction(owner, contactNotice: notice) + default: + break + } + } +} diff --git a/KissMeConsole/Sources/KissConsole.swift b/KissMeConsole/Sources/KissConsole.swift index 3f71fdf..da82195 100644 --- a/KissMeConsole/Sources/KissConsole.swift +++ b/KissMeConsole/Sources/KissConsole.swift @@ -101,6 +101,9 @@ class KissConsole: KissMe.ShopContext { case localizeOnOff = "localize" case test = "test" + // 웹소켓 + case real = "real" + // 뉴스 case news = "news" case newsAll = "news all" @@ -131,6 +134,8 @@ class KissConsole: KissMe.ShopContext { return false case .test: return true + case .real: + return true case .news, .newsAll: return false } @@ -277,6 +282,8 @@ class KissConsole: KissMe.ShopContext { case .localizeOnOff: await onLocalizeOnOff(args) case .test: onTest(args) + case .real: await onReal(args) + case .news: await onNews(args) case .newsAll: onNewsAll(args) @@ -1226,6 +1233,26 @@ extension KissConsole { } + private func onReal(_ args: [String]) async { + guard args.count == 2 else { + print("Missing PNO and on/off") + return + } + let productNo = args[0] + guard let option = OnOff(rawValue: args[1]) else { + print("Invalid on/off option") + return + } + switch option { + case .on: + break + case .off: + break + } + print("WebSocket listening \(option.rawValue) for \(productNo)") + } + + private func onNews(_ args: [String]) async { guard args.count == 1, let day = args[0].yyyyMMdd_toDate else { print("Missing day") diff --git a/KissMeConsole/Sources/main.swift b/KissMeConsole/Sources/main.swift index a0b46a6..beaee89 100644 --- a/KissMeConsole/Sources/main.swift +++ b/KissMeConsole/Sources/main.swift @@ -14,4 +14,6 @@ import KissMe //test_get_websocket_key_and_contact_price() //test_get_websocket_key_and_asking_price() //test_parse_contact_price_response() -test_websocket_dump_data() +//test_websocket_dump_data() + +test_auction() diff --git a/KissMeConsole/Sources/test_websocket.swift b/KissMeConsole/Sources/test_websocket.swift index d803d23..ba8f80e 100644 --- a/KissMeConsole/Sources/test_websocket.swift +++ b/KissMeConsole/Sources/test_websocket.swift @@ -9,10 +9,68 @@ import Foundation import KissMe -class DumpWebSocketData: NSObject { +class TestAution: NSObject { } +extension TestAution: KissAutionNoticeDelegate { + func auction(_ auction: KissAuction, contactNotice: KissMe.Domestic.ContractNotice) { + } +} + +extension TestAution: KissAuctionPriceDelegate { + func auction(_ auction: KissAuction, contactPrice: KissMe.Domestic.ContractPrice) { + } + + func auction(_ auction: KissAuction, askingPrice: KissMe.Domestic.AskingPrice) { + } +} + + +func test_auction() { + let isMock = true + let productNo = "065350" + + let semaphore = DispatchSemaphore(value: 0) + Task { + guard let account = await test_get_account(isMock: isMock) else { + return + } + + let auction = KissAuction(account: account, loggable: true) + + let testAuction = TestAution() + var success: Bool = false + + do { +// success = try await auction.startNotice(delegate: testAuction) +// print("startNotice: \(success)") + + let _ = try await auction.addSlot(productNo: productNo, delegate: testAuction) + print("addSlot: ok") + + try await Task.sleep(nanoseconds: 1_000_000_000 * 60) + + success = try await auction.removeSlot(productNo: productNo) + print("removeSlot: \(success)") + + +// success = try await auction.stopNotice() +// print("stopNotice: \(success)") + + } catch { + print(error) + } + + semaphore.signal() + } + semaphore.wait() +} + + +class DumpWebSocketData: NSObject { +} + extension DumpWebSocketData: WebSocketDelegate { @@ -167,6 +225,29 @@ func test_get_websocket_key_and_contact_price() { } +func test_get_account(isMock: Bool) async -> KissAccount? { + let credential: Credential + + do { + credential = try KissCredential(isMock: isMock) + } catch { + print(error) + return nil + } + + let account = KissAccount(credential: credential) + do { + if try await account.login() { + return account + } + } catch { + print(error) + return nil + } + return nil +} + + func test_get_websocket_key(isMock: Bool) async -> (KissAccount, String)? { let credential: Credential @@ -176,15 +257,20 @@ func test_get_websocket_key(isMock: Bool) async -> (KissAccount, String)? { print(error) return nil } - + let account = KissAccount(credential: credential) do { /// Return existing valid key - if let approvalKey = account.approvalKey { - return (account, approvalKey) + if let approvalKey = account.approvalKeys.first, !approvalKey.isExpired { + return (account, approvalKey.key) } if try await account.login() { + + if let approvalKey = account.approvalKeys.first, !approvalKey.isExpired { + return (account, approvalKey.key) + } + let approvalKey = try await account.getApprovalKey() print("approvalKey : \(approvalKey)") return (account, approvalKey) diff --git a/README.md b/README.md index d337aa6..84e641f 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ WIP `showcase` | 추천 상품을 제안함. `hate (탭) (PNO)` | 관심 종목에서 삭제함. `localize names` | csv field name 에 대해서 한글명을 제공하는 **data/localized-names.csv** 를 저장. `localize (on/off)`| 앞으로 저장하는 모든 csv file 의 field 에 (on) 이면 한글명으로, (off) 이면 영문으로 저장. +`real (PNO) (on/off)` | 실시간 웹소켓을 접속하여 수신된 데이터를 기록합니다. (on) 이면 파일로 기록, (off) 이면 기록하지 않음. * PNO 는 `Product NO` 의 약자이고, 상품의 `단축코드` (shortCode) 와 동일합니다. * ONO 는 `Order NO` 의 약자이고, 고유한 주문번호 입니다. diff --git a/projects/macos/KissMeConsole.xcodeproj/project.pbxproj b/projects/macos/KissMeConsole.xcodeproj/project.pbxproj index 55b6a43..6c9e34f 100644 --- a/projects/macos/KissMeConsole.xcodeproj/project.pbxproj +++ b/projects/macos/KissMeConsole.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 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 */; }; + 34DB3C452AA6071D00B6763E /* KissConsole+WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34DB3C442AA6071D00B6763E /* KissConsole+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, ); }; }; @@ -60,6 +61,7 @@ 349843202A242AC900E85B08 /* KissConsole+CSV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+CSV.swift"; sourceTree = ""; }; 34D3680C2A280801005E6756 /* KissConsole+Candle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Candle.swift"; sourceTree = ""; }; 34DA3EA32A9A176B00BB3439 /* test_websocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = test_websocket.swift; sourceTree = ""; }; + 34DB3C442AA6071D00B6763E /* KissConsole+WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+WebSocket.swift"; sourceTree = ""; }; 34EC4D1E2A7A7365002F947C /* KissConsole+News.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+News.swift"; sourceTree = ""; }; 34F190122A4441F00068C697 /* KissConsole+Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Test.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -108,6 +110,7 @@ 3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */, 34EC4D1E2A7A7365002F947C /* KissConsole+News.swift */, 34F190122A4441F00068C697 /* KissConsole+Test.swift */, + 34DB3C442AA6071D00B6763E /* KissConsole+WebSocket.swift */, 349327F62A20E3E300097063 /* Foundation+Extensions.swift */, ); name = KissMeConsole; @@ -198,6 +201,7 @@ 341F5F052A13B82F00962D48 /* test.swift in Sources */, 349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */, 348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */, + 34DB3C452AA6071D00B6763E /* KissConsole+WebSocket.swift in Sources */, 3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */, 34DA3EA42A9A176B00BB3439 /* test_websocket.swift in Sources */, 34F190132A4441F00068C697 /* KissConsole+Test.swift in Sources */, diff --git a/projects/macos/KissMeConsole.xcodeproj/xcshareddata/xcschemes/KissMeConsole.xcscheme b/projects/macos/KissMeConsole.xcodeproj/xcshareddata/xcschemes/KissMeConsole.xcscheme index 559ffa9..dea89ee 100644 --- a/projects/macos/KissMeConsole.xcodeproj/xcshareddata/xcschemes/KissMeConsole.xcscheme +++ b/projects/macos/KissMeConsole.xcodeproj/xcshareddata/xcschemes/KissMeConsole.xcscheme @@ -41,7 +41,8 @@ debugServiceExtension = "internal" allowLocationSimulation = "YES" viewDebuggingEnabled = "No" - consoleMode = "1"> + consoleMode = "1" + structuredConsoleMode = "3">