From e01c539adcd4d6470cfbbfb46e6bc056907c43f8 Mon Sep 17 00:00:00 2001 From: ened Date: Fri, 19 May 2023 01:17:09 +0900 Subject: [PATCH] Add more shop request --- KissMe.xcodeproj/project.pbxproj | 12 ++- KissMe/Common/Credential.swift | 4 +- KissMe/Common/Request.swift | 1 + ...questRequest.swift => SeibroRequest.swift} | 30 ++++--- KissMe/Common/ShopRequest.swift | 30 +++++++ KissMe/KissShop.swift | 80 ++++++++++++++++--- KissMeConsole/KissMeConsole/KissConsole.swift | 22 ++++- KissMeConsole/KissMeConsole/test.swift | 33 ++++++++ 8 files changed, 180 insertions(+), 32 deletions(-) rename KissMe/Common/{SeibroRequestRequest.swift => SeibroRequest.swift} (82%) create mode 100644 KissMe/Common/ShopRequest.swift diff --git a/KissMe.xcodeproj/project.pbxproj b/KissMe.xcodeproj/project.pbxproj index 551d970..976fb74 100644 --- a/KissMe.xcodeproj/project.pbxproj +++ b/KissMe.xcodeproj/project.pbxproj @@ -30,7 +30,8 @@ 341F5F072A14634F00962D48 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F062A14634F00962D48 /* Extensions.swift */; }; 341F5F0B2A15115400962D48 /* KissShop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0A2A15115400962D48 /* KissShop.swift */; }; 341F5F0D2A15222E00962D48 /* AuthRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0C2A15222E00962D48 /* AuthRequest.swift */; }; - 341F5F0F2A15223A00962D48 /* SeibroRequestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0E2A15223A00962D48 /* SeibroRequestRequest.swift */; }; + 341F5F0F2A15223A00962D48 /* SeibroRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0E2A15223A00962D48 /* SeibroRequest.swift */; }; + 341F5F112A1685E700962D48 /* ShopRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F102A1685E700962D48 /* ShopRequest.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -68,7 +69,8 @@ 341F5F062A14634F00962D48 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 341F5F0A2A15115400962D48 /* KissShop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissShop.swift; sourceTree = ""; }; 341F5F0C2A15222E00962D48 /* AuthRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequest.swift; sourceTree = ""; }; - 341F5F0E2A15223A00962D48 /* SeibroRequestRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeibroRequestRequest.swift; sourceTree = ""; }; + 341F5F0E2A15223A00962D48 /* SeibroRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeibroRequest.swift; sourceTree = ""; }; + 341F5F102A1685E700962D48 /* ShopRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopRequest.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -170,7 +172,8 @@ 341F5EDD2A0F300100962D48 /* Request.swift */, 341F5F0C2A15222E00962D48 /* AuthRequest.swift */, 341F5EFE2A10955D00962D48 /* OrderRequest.swift */, - 341F5F0E2A15223A00962D48 /* SeibroRequestRequest.swift */, + 341F5F0E2A15223A00962D48 /* SeibroRequest.swift */, + 341F5F102A1685E700962D48 /* ShopRequest.swift */, 341F5F022A11A2BC00962D48 /* Credential.swift */, 341F5F062A14634F00962D48 /* Extensions.swift */, ); @@ -294,7 +297,7 @@ 341F5EFD2A10931B00962D48 /* DomesticStockSearch.swift in Sources */, 341F5EE52A0F3EF400962D48 /* DomesticStock.swift in Sources */, 341F5EF72A0F8B0500962D48 /* DomesticStockResult.swift in Sources */, - 341F5F0F2A15223A00962D48 /* SeibroRequestRequest.swift in Sources */, + 341F5F0F2A15223A00962D48 /* SeibroRequest.swift in Sources */, 341F5EF02A0F886600962D48 /* ForeignFutures.swift in Sources */, 341F5EEC2A0F883900962D48 /* ForeignStock.swift in Sources */, 341F5EFF2A10955D00962D48 /* OrderRequest.swift in Sources */, @@ -303,6 +306,7 @@ 341F5EDE2A0F300100962D48 /* Request.swift in Sources */, 341F5F012A11155100962D48 /* DomesticStockSearchResult.swift in Sources */, 341F5EF22A0F887200962D48 /* DomesticFutures.swift in Sources */, + 341F5F112A1685E700962D48 /* ShopRequest.swift in Sources */, 341F5EF92A0F907300962D48 /* DomesticStockPriceResult.swift in Sources */, 341F5EE12A0F373B00962D48 /* Login.swift in Sources */, 341F5EF52A0F891200962D48 /* KissAccount.swift in Sources */, diff --git a/KissMe/Common/Credential.swift b/KissMe/Common/Credential.swift index 24da56f..a4612de 100644 --- a/KissMe/Common/Credential.swift +++ b/KissMe/Common/Credential.swift @@ -53,9 +53,7 @@ public struct KissCredential: Credential, Codable { public init(isMock: Bool) throws { let serverFileName = isMock ? "mock-server.json": "real-server.json" - let path = "\(FileManager.default.currentDirectoryPath)/\(serverFileName)" - let jsonUrl = URL(filePath: path) - + let jsonUrl = URL.currentDirectory().appending(path: serverFileName) try self.init(jsonUrl: jsonUrl) } } diff --git a/KissMe/Common/Request.swift b/KissMe/Common/Request.swift index 9b2f0c9..b75a9ac 100644 --- a/KissMe/Common/Request.swift +++ b/KissMe/Common/Request.swift @@ -32,6 +32,7 @@ public enum GeneralError: Error { public enum QueryError: Error { case invalidUrl case invalidJson + case invalidXml case missingData } diff --git a/KissMe/Common/SeibroRequestRequest.swift b/KissMe/Common/SeibroRequest.swift similarity index 82% rename from KissMe/Common/SeibroRequestRequest.swift rename to KissMe/Common/SeibroRequest.swift index 15013ca..2b9c065 100644 --- a/KissMe/Common/SeibroRequestRequest.swift +++ b/KissMe/Common/SeibroRequest.swift @@ -13,6 +13,7 @@ protocol SeibroRequest: Request { } +/// 한국예탁결제원_주식정보서비스 extension SeibroRequest { public var domain: String { @@ -27,7 +28,6 @@ extension SeibroRequest { URL(string: domain + url) } - /* func query(completion: @escaping (Result) -> Void) { guard let url = queryUrl else { completion(.failure(QueryError.invalidUrl)) @@ -86,16 +86,26 @@ extension SeibroRequest { let stringData = String(data: data, encoding: .utf8) ?? "" print(stringData) - do { - let parser = XMLParser() - - parser.delegate = - let result = try decoder.decode(KResult.self, from: data) - completion(.success(result)) - } catch { - completion(.failure(error)) + let parser = XMLParser(data: data) + let handler = SeibroResultParser() + parser.delegate = handler + if parser.parse() { +// completion(.success("")) + } else { + completion(.failure(QueryError.invalidXml)) } }.resume() } - */ +} + + +class SeibroResultParser: NSObject, XMLParserDelegate { + + let data: T? = nil + + func parserDidStartDocument(_ parser: XMLParser) { + } + + func parserDidEndDocument(_ parser: XMLParser) { + } } diff --git a/KissMe/Common/ShopRequest.swift b/KissMe/Common/ShopRequest.swift new file mode 100644 index 0000000..0fd67bf --- /dev/null +++ b/KissMe/Common/ShopRequest.swift @@ -0,0 +1,30 @@ +// +// ShopRequest.swift +// KissMe +// +// Created by ened-book-m1 on 2023/05/19. +// + +import Foundation + + +protocol ShopRequest: AuthRequest { + var openApiKey: String { get } +} + + +/// 금융위원회_KRX상장종목정보 +extension ShopRequest { + + public var domain: String { + "https://apis.data.go.kr" + } + + public var timeout: TimeInterval { + 15 + } + + var queryUrl: URL? { + URL(string: domain + url) + } +} diff --git a/KissMe/KissShop.swift b/KissMe/KissShop.swift index 4d3ff3b..50a9628 100644 --- a/KissMe/KissShop.swift +++ b/KissMe/KissShop.swift @@ -9,47 +9,101 @@ import Foundation public class KissShop { - - struct Key: Codable { + + struct ShopCredential: Codable { let openApiKey: String + + init(openApiKey: String) { + self.openApiKey = openApiKey + } + + init(jsonUrl: URL) throws { + do { + let data = try Data(contentsOf: jsonUrl, options: .uncached) + let jsonData = try JSONDecoder().decode(ShopCredential.self, from: data) + self.init(openApiKey: jsonData.openApiKey) + } catch { + throw error + } + } } - init(jsonUrl: URL) { - //self.openApiKey = openApiKey + let credential: ShopCredential + + public init(jsonUrl: URL) throws { + credential = try ShopCredential(jsonUrl: jsonUrl) } } -extension KissShop { +public struct DomesticShop { +} + + +extension DomesticShop { + + public enum MarketCode: Int { + /// 유가증권시장 + case stock = 11 + /// 코스닥 + case kosdaq = 12 + /// K-OTC + case kotc = 13 + /// 코넥스 + case konex = 14 + /// 기타시장 + case etc = 50 + } public struct ProductResult: Codable { // TODO: work } + /// 한국예탁결제원_주식정보서비스 - 시장구분을 기준으로 단축종목번호와 종목명 조회 public struct ProductRequest: SeibroRequest { public typealias KResult = String public var url: String { - "/openapi/service/StockSvc/getKDRSecnInfo" + "/openapi/service/StockSvc/getShotnByMartN1" } public var method: Method { .get } public var body: [String : Any] { [ "ServiceKey": openApiKey, - "caltotMartTpcd": String(market.rawValue), + "martTpcd": String(market.rawValue), + "pageNo": 1, + "numOfRows": 10000, ] } public var result: KResult? = nil - public enum MarketCode: Int { - case stock = 11 - case kosdaq = 12 - case konex = 13 - } - public let openApiKey: String let market: MarketCode + + init(openApiKey: String, market: MarketCode) { + self.openApiKey = openApiKey + self.market = market + } + } +} + + +extension KissShop { + + public func getProduct(market: DomesticShop.MarketCode) async throws -> String { + return try await withUnsafeThrowingContinuation { continuation in + + let request = DomesticShop.ProductRequest(openApiKey: self.credential.openApiKey, market: market) + request.query { result in + switch result { + case .success(let result): + continuation.resume(returning: result) + case .failure(let error): + continuation.resume(throwing: error) + } + } + } } } diff --git a/KissMeConsole/KissMeConsole/KissConsole.swift b/KissMeConsole/KissMeConsole/KissConsole.swift index f2752da..4ea11a3 100644 --- a/KissMeConsole/KissMeConsole/KissConsole.swift +++ b/KissMeConsole/KissMeConsole/KissConsole.swift @@ -12,6 +12,7 @@ import KissMe class KissConsole { var credential: Credential? = nil var account: KissAccount? = nil + var shop: KissShop? = nil enum KissCommand: String { case quit = "quit" @@ -21,13 +22,16 @@ class KissConsole { case search = "search" case buy = "buy" case sell = "sell" + case loadShop = "load shop" var needLogin: Bool { switch self { - case .quit: + case .quit, .loginMock, .loginReal: return false - case .loginMock, .loginReal, .logout, .search, .buy, .sell: + case .logout, .search, .buy, .sell: return true + case .loadShop: + return false } } } @@ -36,6 +40,10 @@ class KissConsole { account != nil } + init() { + let jsonUrl = URL.currentDirectory().appending(path: "shop-server.json") + shop = try? KissShop(jsonUrl: jsonUrl) + } func onLogin(isMock: Bool) async { guard !isLogined else { @@ -85,6 +93,15 @@ class KissConsole { } + func onLoadShop() async { + do { + _ = try await shop?.getProduct(market: .kosdaq) + } catch { + print("\(error)") + } + } + + func run() { print("Enter command:") let semaphore = DispatchSemaphore(value: 0) @@ -114,6 +131,7 @@ class KissConsole { case .search: await onSearch(args.suffixStrings(from: 1)) case .buy: await onBuy(args.suffixStrings(from: 1)) case .sell: await onSell(args.suffixStrings(from: 1)) + case .loadShop: await onLoadShop() default: print("Unknown command: \(single)") } diff --git a/KissMeConsole/KissMeConsole/test.swift b/KissMeConsole/KissMeConsole/test.swift index 926ba09..8e86bb8 100644 --- a/KissMeConsole/KissMeConsole/test.swift +++ b/KissMeConsole/KissMeConsole/test.swift @@ -60,3 +60,36 @@ func test_json_result() { print("\(error)") } } + + +func test_xml_result() { + let str = "
00NORMAL SERVICE.
이스트아시아홀딩스인베스트먼트리미티드900110삼천당제약000250중앙에너비스000440신라섬유001000안국약품001540무림에스피001810이화공영001840피에스텍002230삼일기업공사002290한일사료0058601011637
" + + let data = Data(str.utf8) + + class ResultHelper: NSObject, XMLParserDelegate { + + struct Result: Codable { + + } + + var result: Result? + + func parserDidStartDocument(_ parser: XMLParser) { + } + + func parserDidEndDocument(_ parser: XMLParser) { + } + } + + + do { + let helper = ResultHelper() + let parser = XMLParser(data: data) + parser.shouldProcessNamespaces = true + parser.delegate = helper + if parser.parse() { + print(helper.result) + } + } +}