Add more shop request
This commit is contained in:
@@ -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 = "<group>"; };
|
||||
341F5F0A2A15115400962D48 /* KissShop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissShop.swift; sourceTree = "<group>"; };
|
||||
341F5F0C2A15222E00962D48 /* AuthRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequest.swift; sourceTree = "<group>"; };
|
||||
341F5F0E2A15223A00962D48 /* SeibroRequestRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeibroRequestRequest.swift; sourceTree = "<group>"; };
|
||||
341F5F0E2A15223A00962D48 /* SeibroRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeibroRequest.swift; sourceTree = "<group>"; };
|
||||
341F5F102A1685E700962D48 /* ShopRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopRequest.swift; sourceTree = "<group>"; };
|
||||
/* 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 */,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public enum GeneralError: Error {
|
||||
public enum QueryError: Error {
|
||||
case invalidUrl
|
||||
case invalidJson
|
||||
case invalidXml
|
||||
case missingData
|
||||
}
|
||||
|
||||
|
||||
@@ -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<KResult, Error>) -> 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<KResult>()
|
||||
parser.delegate = handler
|
||||
if parser.parse() {
|
||||
// completion(.success(""))
|
||||
} else {
|
||||
completion(.failure(QueryError.invalidXml))
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
class SeibroResultParser<T>: NSObject, XMLParserDelegate {
|
||||
|
||||
let data: T? = nil
|
||||
|
||||
func parserDidStartDocument(_ parser: XMLParser) {
|
||||
}
|
||||
|
||||
func parserDidEndDocument(_ parser: XMLParser) {
|
||||
}
|
||||
}
|
||||
30
KissMe/Common/ShopRequest.swift
Normal file
30
KissMe/Common/ShopRequest.swift
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)")
|
||||
}
|
||||
|
||||
@@ -60,3 +60,36 @@ func test_json_result() {
|
||||
print("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func test_xml_result() {
|
||||
let str = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><response><header><resultCode>00</resultCode><resultMsg>NORMAL SERVICE.</resultMsg></header><body><items><item><korSecnNm>이스트아시아홀딩스인베스트먼트리미티드</korSecnNm><shotnIsin>900110</shotnIsin></item><item><korSecnNm>삼천당제약</korSecnNm><shotnIsin>000250</shotnIsin></item><item><korSecnNm>중앙에너비스</korSecnNm><shotnIsin>000440</shotnIsin></item><item><korSecnNm>신라섬유</korSecnNm><shotnIsin>001000</shotnIsin></item><item><korSecnNm>안국약품</korSecnNm><shotnIsin>001540</shotnIsin></item><item><korSecnNm>무림에스피</korSecnNm><shotnIsin>001810</shotnIsin></item><item><korSecnNm>이화공영</korSecnNm><shotnIsin>001840</shotnIsin></item><item><korSecnNm>피에스텍</korSecnNm><shotnIsin>002230</shotnIsin></item><item><korSecnNm>삼일기업공사</korSecnNm><shotnIsin>002290</shotnIsin></item><item><korSecnNm>한일사료</korSecnNm><shotnIsin>005860</shotnIsin></item></items><numOfRows>10</numOfRows><pageNo>1</pageNo><totalCount>1637</totalCount></body></response>"
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user