Add KissMe framework and console command line tool

This commit is contained in:
2023-05-16 23:35:46 +09:00
commit d7ae2436d2
31 changed files with 3195 additions and 0 deletions

View File

@@ -0,0 +1,582 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
341F5EB02A0A80EC00962D48 /* KissMe.docc in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EAF2A0A80EC00962D48 /* KissMe.docc */; };
341F5EB62A0A80EC00962D48 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EAB2A0A80EC00962D48 /* KissMe.framework */; };
341F5EBB2A0A80EC00962D48 /* KissMeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EBA2A0A80EC00962D48 /* KissMeTests.swift */; };
341F5EBC2A0A80EC00962D48 /* KissMe.h in Headers */ = {isa = PBXBuildFile; fileRef = 341F5EAE2A0A80EC00962D48 /* KissMe.h */; settings = {ATTRIBUTES = (Public, ); }; };
341F5EDE2A0F300100962D48 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EDD2A0F300100962D48 /* Request.swift */; };
341F5EE12A0F373B00962D48 /* Login.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EE02A0F373B00962D48 /* Login.swift */; };
341F5EE52A0F3EF400962D48 /* DomesticStock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EE42A0F3EF400962D48 /* DomesticStock.swift */; };
341F5EE92A0F87FB00962D48 /* DomesticStockPrice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EE82A0F87FB00962D48 /* DomesticStockPrice.swift */; };
341F5EEC2A0F883900962D48 /* ForeignStock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EEB2A0F883900962D48 /* ForeignStock.swift */; };
341F5EEE2A0F884300962D48 /* ForeignStockPrice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EED2A0F884300962D48 /* ForeignStockPrice.swift */; };
341F5EF02A0F886600962D48 /* ForeignFutures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EEF2A0F886600962D48 /* ForeignFutures.swift */; };
341F5EF22A0F887200962D48 /* DomesticFutures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EF12A0F887200962D48 /* DomesticFutures.swift */; };
341F5EF52A0F891200962D48 /* KissAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EF42A0F891200962D48 /* KissAccount.swift */; };
341F5EF72A0F8B0500962D48 /* DomesticStockResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EF62A0F8B0500962D48 /* DomesticStockResult.swift */; };
341F5EF92A0F907300962D48 /* DomesticStockPriceResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EF82A0F907300962D48 /* DomesticStockPriceResult.swift */; };
341F5EFB2A10909D00962D48 /* LoginResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EFA2A10909D00962D48 /* LoginResult.swift */; };
341F5EFD2A10931B00962D48 /* DomesticStockSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EFC2A10931B00962D48 /* DomesticStockSearch.swift */; };
341F5EFF2A10955D00962D48 /* OrderRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5EFE2A10955D00962D48 /* OrderRequest.swift */; };
341F5F012A11155100962D48 /* DomesticStockSearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F002A11155100962D48 /* DomesticStockSearchResult.swift */; };
341F5F032A11A2BC00962D48 /* Credential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F022A11A2BC00962D48 /* Credential.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
341F5EB72A0A80EC00962D48 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 341F5EA22A0A80EC00962D48 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 341F5EAA2A0A80EC00962D48;
remoteInfo = KissMe;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
341F5EAB2A0A80EC00962D48 /* KissMe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KissMe.framework; sourceTree = BUILT_PRODUCTS_DIR; };
341F5EAE2A0A80EC00962D48 /* KissMe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KissMe.h; sourceTree = "<group>"; };
341F5EAF2A0A80EC00962D48 /* KissMe.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = KissMe.docc; sourceTree = "<group>"; };
341F5EB52A0A80EC00962D48 /* KissMeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KissMeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
341F5EBA2A0A80EC00962D48 /* KissMeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissMeTests.swift; sourceTree = "<group>"; };
341F5EDD2A0F300100962D48 /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
341F5EE02A0F373B00962D48 /* Login.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Login.swift; sourceTree = "<group>"; };
341F5EE42A0F3EF400962D48 /* DomesticStock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStock.swift; sourceTree = "<group>"; };
341F5EE82A0F87FB00962D48 /* DomesticStockPrice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockPrice.swift; sourceTree = "<group>"; };
341F5EEB2A0F883900962D48 /* ForeignStock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignStock.swift; sourceTree = "<group>"; };
341F5EED2A0F884300962D48 /* ForeignStockPrice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignStockPrice.swift; sourceTree = "<group>"; };
341F5EEF2A0F886600962D48 /* ForeignFutures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForeignFutures.swift; sourceTree = "<group>"; };
341F5EF12A0F887200962D48 /* DomesticFutures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticFutures.swift; sourceTree = "<group>"; };
341F5EF42A0F891200962D48 /* KissAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissAccount.swift; sourceTree = "<group>"; };
341F5EF62A0F8B0500962D48 /* DomesticStockResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockResult.swift; sourceTree = "<group>"; };
341F5EF82A0F907300962D48 /* DomesticStockPriceResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockPriceResult.swift; sourceTree = "<group>"; };
341F5EFA2A10909D00962D48 /* LoginResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginResult.swift; sourceTree = "<group>"; };
341F5EFC2A10931B00962D48 /* DomesticStockSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockSearch.swift; sourceTree = "<group>"; };
341F5EFE2A10955D00962D48 /* OrderRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderRequest.swift; sourceTree = "<group>"; };
341F5F002A11155100962D48 /* DomesticStockSearchResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticStockSearchResult.swift; sourceTree = "<group>"; };
341F5F022A11A2BC00962D48 /* Credential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credential.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
341F5EA82A0A80EC00962D48 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
341F5EB22A0A80EC00962D48 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
341F5EB62A0A80EC00962D48 /* KissMe.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
341F5EA12A0A80EC00962D48 = {
isa = PBXGroup;
children = (
341F5EAD2A0A80EC00962D48 /* KissMe */,
341F5EB92A0A80EC00962D48 /* KissMeTests */,
341F5EAC2A0A80EC00962D48 /* Products */,
);
sourceTree = "<group>";
};
341F5EAC2A0A80EC00962D48 /* Products */ = {
isa = PBXGroup;
children = (
341F5EAB2A0A80EC00962D48 /* KissMe.framework */,
341F5EB52A0A80EC00962D48 /* KissMeTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
341F5EAD2A0A80EC00962D48 /* KissMe */ = {
isa = PBXGroup;
children = (
341F5EF32A0F88AC00962D48 /* Common */,
341F5EEA2A0F882300962D48 /* Foreign */,
341F5EE62A0F3EFB00962D48 /* Domestic */,
341F5EDF2A0F372000962D48 /* Login */,
341F5EAE2A0A80EC00962D48 /* KissMe.h */,
341F5EAF2A0A80EC00962D48 /* KissMe.docc */,
341F5EF42A0F891200962D48 /* KissAccount.swift */,
);
path = KissMe;
sourceTree = "<group>";
};
341F5EB92A0A80EC00962D48 /* KissMeTests */ = {
isa = PBXGroup;
children = (
341F5EBA2A0A80EC00962D48 /* KissMeTests.swift */,
);
path = KissMeTests;
sourceTree = "<group>";
};
341F5EDF2A0F372000962D48 /* Login */ = {
isa = PBXGroup;
children = (
341F5EE02A0F373B00962D48 /* Login.swift */,
341F5EFA2A10909D00962D48 /* LoginResult.swift */,
);
path = Login;
sourceTree = "<group>";
};
341F5EE62A0F3EFB00962D48 /* Domestic */ = {
isa = PBXGroup;
children = (
341F5EE42A0F3EF400962D48 /* DomesticStock.swift */,
341F5EF62A0F8B0500962D48 /* DomesticStockResult.swift */,
341F5EE82A0F87FB00962D48 /* DomesticStockPrice.swift */,
341F5EF82A0F907300962D48 /* DomesticStockPriceResult.swift */,
341F5EFC2A10931B00962D48 /* DomesticStockSearch.swift */,
341F5F002A11155100962D48 /* DomesticStockSearchResult.swift */,
341F5EF12A0F887200962D48 /* DomesticFutures.swift */,
);
path = Domestic;
sourceTree = "<group>";
};
341F5EEA2A0F882300962D48 /* Foreign */ = {
isa = PBXGroup;
children = (
341F5EEB2A0F883900962D48 /* ForeignStock.swift */,
341F5EED2A0F884300962D48 /* ForeignStockPrice.swift */,
341F5EEF2A0F886600962D48 /* ForeignFutures.swift */,
);
path = Foreign;
sourceTree = "<group>";
};
341F5EF32A0F88AC00962D48 /* Common */ = {
isa = PBXGroup;
children = (
341F5EDD2A0F300100962D48 /* Request.swift */,
341F5F022A11A2BC00962D48 /* Credential.swift */,
341F5EFE2A10955D00962D48 /* OrderRequest.swift */,
);
path = Common;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
341F5EA62A0A80EC00962D48 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
341F5EBC2A0A80EC00962D48 /* KissMe.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
341F5EAA2A0A80EC00962D48 /* KissMe */ = {
isa = PBXNativeTarget;
buildConfigurationList = 341F5EBF2A0A80EC00962D48 /* Build configuration list for PBXNativeTarget "KissMe" */;
buildPhases = (
341F5EA62A0A80EC00962D48 /* Headers */,
341F5EA72A0A80EC00962D48 /* Sources */,
341F5EA82A0A80EC00962D48 /* Frameworks */,
341F5EA92A0A80EC00962D48 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = KissMe;
productName = KissMe;
productReference = 341F5EAB2A0A80EC00962D48 /* KissMe.framework */;
productType = "com.apple.product-type.framework";
};
341F5EB42A0A80EC00962D48 /* KissMeTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 341F5EC22A0A80EC00962D48 /* Build configuration list for PBXNativeTarget "KissMeTests" */;
buildPhases = (
341F5EB12A0A80EC00962D48 /* Sources */,
341F5EB22A0A80EC00962D48 /* Frameworks */,
341F5EB32A0A80EC00962D48 /* Resources */,
);
buildRules = (
);
dependencies = (
341F5EB82A0A80EC00962D48 /* PBXTargetDependency */,
);
name = KissMeTests;
productName = KissMeTests;
productReference = 341F5EB52A0A80EC00962D48 /* KissMeTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
341F5EA22A0A80EC00962D48 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1430;
TargetAttributes = {
341F5EAA2A0A80EC00962D48 = {
CreatedOnToolsVersion = 14.3;
};
341F5EB42A0A80EC00962D48 = {
CreatedOnToolsVersion = 14.3;
};
};
};
buildConfigurationList = 341F5EA52A0A80EC00962D48 /* Build configuration list for PBXProject "KissMe" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 341F5EA12A0A80EC00962D48;
productRefGroup = 341F5EAC2A0A80EC00962D48 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
341F5EAA2A0A80EC00962D48 /* KissMe */,
341F5EB42A0A80EC00962D48 /* KissMeTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
341F5EA92A0A80EC00962D48 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
341F5EB32A0A80EC00962D48 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
341F5EA72A0A80EC00962D48 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
341F5EFB2A10909D00962D48 /* LoginResult.swift in Sources */,
341F5F032A11A2BC00962D48 /* Credential.swift in Sources */,
341F5EB02A0A80EC00962D48 /* KissMe.docc in Sources */,
341F5EFD2A10931B00962D48 /* DomesticStockSearch.swift in Sources */,
341F5EE52A0F3EF400962D48 /* DomesticStock.swift in Sources */,
341F5EF72A0F8B0500962D48 /* DomesticStockResult.swift in Sources */,
341F5EF02A0F886600962D48 /* ForeignFutures.swift in Sources */,
341F5EEC2A0F883900962D48 /* ForeignStock.swift in Sources */,
341F5EFF2A10955D00962D48 /* OrderRequest.swift in Sources */,
341F5EE92A0F87FB00962D48 /* DomesticStockPrice.swift in Sources */,
341F5EEE2A0F884300962D48 /* ForeignStockPrice.swift in Sources */,
341F5EDE2A0F300100962D48 /* Request.swift in Sources */,
341F5F012A11155100962D48 /* DomesticStockSearchResult.swift in Sources */,
341F5EF22A0F887200962D48 /* DomesticFutures.swift in Sources */,
341F5EF92A0F907300962D48 /* DomesticStockPriceResult.swift in Sources */,
341F5EE12A0F373B00962D48 /* Login.swift in Sources */,
341F5EF52A0F891200962D48 /* KissAccount.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
341F5EB12A0A80EC00962D48 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
341F5EBB2A0A80EC00962D48 /* KissMeTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
341F5EB82A0A80EC00962D48 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 341F5EAA2A0A80EC00962D48 /* KissMe */;
targetProxy = 341F5EB72A0A80EC00962D48 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
341F5EBD2A0A80EC00962D48 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
341F5EBE2A0A80EC00962D48 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
341F5EC02A0A80EC00962D48 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = NYU8YAYHF8;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = (
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.3;
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20";
PRODUCT_BUNDLE_IDENTIFIER = com.ened.KissMe;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = auto;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
341F5EC12A0A80EC00962D48 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = NYU8YAYHF8;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_MODULE_VERIFIER = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
LD_RUNPATH_SEARCH_PATHS = (
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = (
"@executable_path/../Frameworks",
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.3;
MARKETING_VERSION = 1.0;
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20";
PRODUCT_BUNDLE_IDENTIFIER = com.ened.KissMe;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = auto;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
341F5EC32A0A80EC00962D48 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = NYU8YAYHF8;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MACOSX_DEPLOYMENT_TARGET = 13.3;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ened.KissMeTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
341F5EC42A0A80EC00962D48 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = NYU8YAYHF8;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MACOSX_DEPLOYMENT_TARGET = 13.3;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ened.KissMeTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
341F5EA52A0A80EC00962D48 /* Build configuration list for PBXProject "KissMe" */ = {
isa = XCConfigurationList;
buildConfigurations = (
341F5EBD2A0A80EC00962D48 /* Debug */,
341F5EBE2A0A80EC00962D48 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
341F5EBF2A0A80EC00962D48 /* Build configuration list for PBXNativeTarget "KissMe" */ = {
isa = XCConfigurationList;
buildConfigurations = (
341F5EC02A0A80EC00962D48 /* Debug */,
341F5EC12A0A80EC00962D48 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
341F5EC22A0A80EC00962D48 /* Build configuration list for PBXNativeTarget "KissMeTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
341F5EC32A0A80EC00962D48 /* Debug */,
341F5EC42A0A80EC00962D48 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 341F5EA22A0A80EC00962D48 /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:KissMeConsole/KissMeConsole.xcodeproj">
</FileRef>
<FileRef
location = "container:KissMe.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,71 @@
//
// Credential.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/15.
//
import Foundation
public protocol Credential {
var isMock: Bool { get }
var accountNo: String { get }
var appKey: String { get }
var appSecret: String { get }
}
extension String {
var digitsOnly: String { filter { ("0"..."9").contains($0) } }
}
public struct KissCredential: Credential, Codable {
public let isMock: Bool
public let accountNo: String
public let appKey: String
public let appSecret: String
public init(isMock: Bool, accountNo: String, appKey: String, appSecret: String) throws {
let digitAccountNo = accountNo.digitsOnly
guard digitAccountNo.count == 10 else {
throw GeneralError.invalidAccountNo
}
self.isMock = isMock
self.accountNo = digitAccountNo
self.appKey = appKey
self.appSecret = appSecret
}
public init(jsonUrl: URL) throws {
do {
let data = try Data(contentsOf: jsonUrl, options: .uncached)
let jsonData = try JSONDecoder().decode(KissCredential.self, from: data)
try self.init(isMock: jsonData.isMock,
accountNo: jsonData.accountNo,
appKey: jsonData.appKey,
appSecret: jsonData.appSecret)
} catch {
throw error
}
}
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)
try self.init(jsonUrl: jsonUrl)
}
}
extension AuthRequest {
public var domain: String {
credential.isMock ?
"https://openapivts.koreainvestment.com:29443":
"https://openapi.koreainvestment.com:9443"
}
}

View File

@@ -0,0 +1,66 @@
//
// OrderRequest.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/14.
//
import Foundation
public protocol OrderRequest: TokenRequest {
var accountNo: String { get }
}
extension OrderRequest {
public var accountNo: String {
credential.accountNo
}
var accountNo8: String {
String(accountNo.prefix(8))
}
var accountNo2: String {
String(accountNo.suffix(2))
}
}
public enum OrderType {
case buy
case sell
}
public enum OrderDivision {
///
case limits
///
case marketPrice
var code: String {
switch self {
case .limits: return "00"
case .marketPrice: return "01"
}
}
}
public enum OrderRevisionType {
///
case modify
///
case cancel
var code: String {
switch self {
case .modify: return "01"
case .cancel: return "02"
}
}
}

160
KissMe/Common/Request.swift Normal file
View File

@@ -0,0 +1,160 @@
//
// Request.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
public enum Method {
case `get`
case post
}
extension Method: CustomStringConvertible {
public var description: String {
switch self {
case .get: return "GET"
case .post: return "POST"
}
}
}
public enum GeneralError: Error {
case noServerJsonFile
case invalidAccessToken
case invalidAccountNo
case unsupportedQueryAtMockServer
}
public enum QueryError: Error {
case invalidUrl
case invalidJson
case missingData
}
public protocol Request {
associatedtype KResult: Decodable
var isMockAvailable: Bool { get }
var domain: String { get }
var url: String { get }
var userAgent: String { get }
var method: Method { get }
var header: [String: String?] { get }
var body: [String: Any] { get }
var result: KResult? { get set }
var timeout: TimeInterval { get }
}
public protocol AuthRequest: Request {
var credential: Credential { get }
}
public protocol TokenRequest: AuthRequest {
var accessToken: String { get }
}
extension Request {
public var isMockAvailable: Bool { true }
public var userAgent: String {
//"KissMe Agent"
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
}
public var method: Method { .get }
public var header: [String: String?] { [:] }
}
extension AuthRequest {
public var timeout: TimeInterval {
15
}
var queryUrl: URL? {
URL(string: domain + url)
}
func query(completion: @escaping (Result<KResult, Error>) -> Void) {
guard let url = queryUrl else {
completion(.failure(QueryError.invalidUrl))
return
}
if isMockAvailable == false, credential.isMock == true {
completion(.failure(GeneralError.unsupportedQueryAtMockServer))
return
}
let jsonBody: Data?
do {
jsonBody = try JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
} catch {
completion(.failure(QueryError.invalidJson))
return
}
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: timeout)
request.httpMethod = String(describing: method)
switch method {
case .post:
request.httpBody = jsonBody
request.setValue("\(jsonBody?.count ?? 0)", forHTTPHeaderField: "Content-Length")
case .get:
var queryItems = [URLQueryItem]()
for item in body {
if let value = item.value as? String {
queryItems.append(URLQueryItem(name: item.key, value: value))
}
}
request.url?.append(queryItems: queryItems)
}
request.setValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")
request.setValue("JSON", forHTTPHeaderField: "Format")
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
for field in header {
request.setValue(field.value, forHTTPHeaderField: field.key)
}
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(QueryError.missingData))
return
}
let stringData = String(data: data, encoding: .utf8) ?? ""
print(stringData)
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
let result = try decoder.decode(KResult.self, from: data)
completion(.success(result))
} catch {
completion(.failure(error))
}
}.resume()
}
}

View File

@@ -0,0 +1,13 @@
//
// DomesticFutures.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
extension Domestic {
}

View File

@@ -0,0 +1,330 @@
//
// DomesticStock.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
public struct Domestic {
/// - ()
///
public struct StockOrderRequest: OrderRequest {
public typealias KResult = OrderResult
public var url: String { "/uapi/domestic-stock/v1/trading/order-cash" }
public var method: Method { .post }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
]
}
public var body: [String: Any] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"PDNO": productNo,
"ORD_DVSN": orderDivision.code,
"ORD_QTY": orderQuantity,
"ORD_UNPR": String(orderPrice),
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
switch orderType {
case .buy: return "VTTC0802U"
case .sell: return "VTTC0801U"
}
}
else {
switch orderType {
case .buy: return "TTTC0802U"
case .sell: return "TTTC0801U"
}
}
}
public let accessToken: String
let productNo: String
let orderType: OrderType
let orderDivision: OrderDivision
let orderQuantity: Int
let orderPrice: Int
public init(credential: Credential, accessToken: String, contract: Contract) {
self.credential = credential
self.accessToken = accessToken
self.productNo = contract.productNo
self.orderType = contract.orderType
self.orderDivision = contract.orderDivision
self.orderQuantity = contract.orderQuantity
self.orderPrice = contract.orderPrice
}
}
/// - ()[v1_-003]
///
public struct StockOrderRevisionRequest: OrderRequest {
public typealias KResult = OrderRevisionResult
public var url: String {
"/uapi/domestic-stock/v1/trading/order-rvsecncl"
}
public var method: Method { .post }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
]
}
public var body: [String: Any] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"KRX_FWDG_ORD_ORGNO": orderOrganizationNo,
"ORGN_ODNO": orderNo,
"ORD_DVSN": orderDivision.code,
"RVSE_CNCL_DVSN_CD": orderRevisionType.code,
"ORD_QTY": String(orderQuantity),
"ORD_UNPR": String(orderPrice),
"QTY_ALL_ORD_YN": isAllQuantity ? "Y": "N"
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
return "VTTC0803U"
}
else {
return "TTTC0803U"
}
}
public let accessToken: String
let productNo: String
let orderOrganizationNo: String
let orderNo: String
let orderDivision: OrderDivision
let orderRevisionType: OrderRevisionType
let orderQuantity: Int
let orderPrice: Int
let isAllQuantity: Bool
public init(credential: Credential, accessToken: String, productNo: String, orderOrganizationNo: String, orderNo: String, orderDivision: OrderDivision, orderRevisionType: OrderRevisionType, orderQuantity: Int = 0, orderPrice: Int) {
self.credential = credential
self.accessToken = accessToken
self.productNo = productNo
self.orderOrganizationNo = orderOrganizationNo
self.orderNo = orderNo
self.orderDivision = orderDivision
self.orderRevisionType = orderRevisionType
self.orderQuantity = orderQuantity
self.orderPrice = orderPrice
self.isAllQuantity = (orderQuantity == 0)
}
}
/// - [v1_-006]
///
public struct StockBalanceRequest: OrderRequest {
public typealias KResult = String
public var url: String {
"/uapi/domestic-stock/v1/trading/inquire-balance"
}
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
]
}
public var body: [String: Any] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"AFHR_FLPR_YN": "N",
"OFL_YN": " ",
"INQR_DVSN": "02",
"UNPR_DVSN": "01",
"FUND_STTL_ICLD_YN": "N",
"FNCG_AMT_AUTO_RDPT_YN": "N",
"PRCS_DVSN": "01",
"CTX_AREA_FK100": " ",
"CTX_AREA_NK100": " ",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
return "VTTC0803U"
}
else {
return "TTTC0803U"
}
}
public let accessToken: String
public init(credential: Credential, accessToken: String) {
self.credential = credential
self.accessToken = accessToken
}
}
/// - [v1_-007]
///
public struct StockPossibleOrderRequest: OrderRequest {
public typealias KResult = OrderPossibleResult
public var url: String {
"/uapi/domestic-stock/v1/trading/inquire-psbl-order"
}
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
]
}
public var body: [String: Any] {
[
"CANO": accountNo8,
"ACNT_PRDT_CD": accountNo2,
"PDNO": productNo,
"ORD_UNPR": String(orderPrice),
"ORD_DVSN": orderDivision.code,
"CMA_EVLU_AMT_ICLD_YN": "",
"OVRS_ICLD_YN": "",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
if credential.isMock {
return "VTTC8908R"
}
else {
return "TTTC8908R"
}
}
public let accessToken: String
let productNo: String
let orderDivision: OrderDivision
let orderPrice: Int
public init(credential: Credential, accessToken: String, productNo: String, orderDivision: OrderDivision, orderPrice: Int) {
self.credential = credential
self.accessToken = accessToken
self.productNo = productNo
self.orderDivision = orderDivision
self.orderPrice = orderPrice
}
}
}
public struct Contract {
public let productNo: String
public let orderType: OrderType
public let orderDivision: OrderDivision
public let orderQuantity: Int
public let orderPrice: Int
}
// MARK: Stock Order
extension KissAccount {
public func orderStock(contract: Contract, completion: @escaping (Result<Bool, Error>) -> Void) {
guard let accessToken = accessToken else {
completion(.failure(GeneralError.invalidAccessToken))
return
}
let request = Domestic.StockOrderRequest(credential: credential, accessToken: accessToken, contract: contract)
request.query { result in
switch result {
case .success(let result):
completion(.success(true))
case .failure(let error):
completion(.failure(error))
}
}
}
public func cancelOrder(completion: @escaping (Result<Bool, Error>) -> Void) {
guard let accessToken = accessToken else {
completion(.failure(GeneralError.invalidAccessToken))
return
}
// TODO: work
}
public func changeOrder(completion: @escaping (Result<Bool, Error>) -> Void) {
guard let accessToken = accessToken else {
completion(.failure(GeneralError.invalidAccessToken))
return
}
// TODO: work
}
public func getStockBalance(completion: @escaping (Result<Bool, Error>) -> Void) {
guard let accessToken = accessToken else {
completion(.failure(GeneralError.invalidAccessToken))
return
}
// TODO: work
}
public func canOrderStock(completion: @escaping (Result<Bool, Error>) -> Void) {
guard let accessToken = accessToken else {
completion(.failure(GeneralError.invalidAccessToken))
return
}
// TODO: work
}
}

View File

@@ -0,0 +1,128 @@
//
// DomesticStockPrice.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
extension Domestic {
/// - [v1_-008]
///
public struct StockCurrentPriceRequest: TokenRequest {
public typealias KResult = CurrentPriceResult
public var url: String {
"/uapi/domestic-stock/v1/quotations/inquire-price"
}
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
]
}
public var body: [String: Any] {
[
"FID_COND_MRKT_DIV_CODE": "J",
"FID_INPUT_ISCD": productNo,
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
"FHKST01010100"
}
public let accessToken: String
let productNo: String
public init(credential: Credential, accessToken: String, productNo: String) {
self.credential = credential
self.accessToken = accessToken
self.productNo = productNo
}
}
/// - [v1_-022]
///
public struct StockTodayMinutePriceRequest: TokenRequest {
public typealias KResult = MinutePriceResult
public var url: String {
"/uapi/domestic-stock/v1/quotations/inquire-time-itemchartprice"
}
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"custtype": CustomerType.personal.rawValue,
]
}
public var body: [String: Any] {
[
"FID_ETC_CLS_CODE": "",
"FID_COND_MRKT_DIV_CODE": "J",
"FID_INPUT_ISCD": productNo,
"FID_INPUT_HOUR_1": startTime,
"FID_PW_DATA_INCU_YN": "N",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
"FHKST03010200"
}
public let accessToken: String
let productNo: String
let startTime: String // HHMMSS
public init(credential: Credential, accessToken: String, productNo: String, startTime: String) {
self.credential = credential
self.accessToken = accessToken
self.productNo = productNo
self.startTime = startTime
}
}
}
// MARK: Stock Price
extension KissAccount {
public func getCurrentPrice(completion: @escaping (Result<Bool, Error>) -> Void) {
guard let accessToken = accessToken else {
completion(.failure(GeneralError.invalidAccessToken))
return
}
// TODO: work
}
public func getMinutePrice(completion: @escaping (Result<Bool, Error>) -> Void) {
guard let accessToken = accessToken else {
completion(.failure(GeneralError.invalidAccessToken))
return
}
// TODO: work
}
}

View File

@@ -0,0 +1,502 @@
//
// DomesticStockPriceResult.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
public enum YesNo: String, Codable {
case yes = "Y"
case no = "N"
}
///
public enum MarginalRateClass: String, Codable {
/// 20%, 30%, 40%
case _40 = "40"
/// 50%
case _50 = "50"
/// 60%
case _60 = "60"
}
///
public enum MarketWarning: String, Codable {
///
case none = "00"
///
case investmentCaution = "01"
///
case investmentWarning = "02"
///
case investmentDanger = "03"
}
public struct CurrentPriceResult: Codable {
public let resultCode: String
public let messageCode: String
public let message: String
public let output: [OutputDetail]?
private enum CodingKeys: String, CodingKey {
case resultCode = "rt_cd"
case messageCode = "msg_cd"
case message = "msg1"
case output
}
///
public enum StateClass: String, Codable {
///
case etc = "00"
///
case underAdmin = "51"
///
case investmentOption = "52"
///
case investmentWarning = "53"
///
case investmentCaution = "54"
///
case creditWorthy = "55"
/// 100%
case depositAll = "57"
///
case tradingHalt = "58"
///
case shortTermOverheating = "59"
}
public struct OutputDetail: Codable {
///
public let itemStateCode: StateClass
///
public let marginalRate: String
///
public let koreanMarketName: String
///
public let newHighLowPriceClassCode: String
///
public let koreanBusinessTypeName: String
///
public let temporaryStopped: YesNo
///
public let marketPriceRangeExtended: YesNo
///
public let closingPriceRangeExtended: YesNo
///
public let creditAllowable: YesNo
///
public let marginalRateClassCode: MarginalRateClass
/// ELW
public let elwPublished: YesNo
///
public let currentStockPrice: String
///
public let previousDayVariableRatio: String
///
public let previousDayVariableRatioSign: String
///
public let previousDayDiffRatio: String
///
public let accumulatedTradingAmount: String
///
public let accumulatedVolume: String
///
public let previousDayDiffVolumeRatio: String
///
public let stockPrice: String
///
public let highestStockPrice: String
///
public let lowestStockPrice: String
///
public let maximumStockPrice: String
///
public let minimumStockPrice: String
///
public let standardStockPrice: String
///
public let weightedAverageStockPrice: String
/// HTS
public let htsForeignRunoutRate: String
///
public let foreignNetBuyingQuantity: String
///
public let programTradeNetBuyingQuantity: String
/// 2
public let pivotSecondDResistancePrice: String
/// 1
public let pivotFirstDResistancePrice: String
///
public let pivotPointValue: String
/// 1
public let pivotFirstDSupportPrice: String
/// 2
public let pivotSecondDSupportPrice: String
///
public let dResistanceValue: String
///
public let dSupportValue: String
///
public let capital: String
///
public let limitWidthPrice: String
///
public let stockFacePrice: String
///
public let stockSubstitudePrice: String
///
public let askingPriceUnit: String
/// HTS
public let htsDealQuantityUnit: String
///
public let listedStockCount: String
/// HTS
public let htsTotalMarketValue: String
/// PER
public let per: String
/// PBR
public let pbr: String
///
public let settlingMonth: String
///
public let volumeTurnoverRate: String
/// EPS
public let eps: String
/// BPS
public let bps: String
/// 250
public let day250HighestPrice: String
/// 250
public let day250HighestPriceDate: String
/// 250
public let day250HighestPriceDiffRatio: String
/// 250
public let day250LowestPrice: String
/// 250
public let day250LowestPriceDate: String
/// 250
public let day250LowestPriceDiffRatio: String
///
public let annualHighestPrice: String
///
public let annualHighestPriceDiffRatio: String
///
public let annualHighestPriceDate: String
///
public let annualLowestPrice: String
///
public let annualLowestPriceDiffRatio: String
///
public let annualLowestPriceDate: String
/// 52
public let week52HighestPrice: String
/// 52
public let week52HighestPriceDiffRatio: String
/// 52
public let week52HighestPriceDate: String
/// 52
public let week52LowestPrice: String
/// 52
public let week52LowestPriceDiffRatio: String
/// 52
public let week52LowestPriceDate: String
///
public let totalOutstandingloanRate: String
///
public let shortSellingAllowable: YesNo
///
public let shortProductCode: String
///
public let facePriceCurrency: String
///
public let capitalCurrency: String
///
public let approachRate: String
///
public let foreignHoldQuantity: String
/// VI
public let viClassCode: String
/// VI
public let viClassCodeForOvertimeMarketPrice: String
///
public let lastShortSellingConclusionQuantity: String
///
public let investmentCareful: YesNo
///
public let marketWarningCode: MarketWarning
///
public let shortOverheated: YesNo
private enum CodingKeys: String, CodingKey {
case itemStateCode = "iscd_stat_cls_code"
case marginalRate = "marg_rate"
case koreanMarketName = "rprs_mrkt_kor_name"
case newHighLowPriceClassCode = "new_hgpr_lwpr_cls_code"
case koreanBusinessTypeName = "bstp_kor_isnm"
case temporaryStopped = "temp_stop_yn"
case marketPriceRangeExtended = "oprc_rang_cont_yn"
case closingPriceRangeExtended = "clpr_rang_cont_yn"
case creditAllowable = "crdt_able_yn"
case marginalRateClassCode = "grmn_rate_cls_code"
case elwPublished = "elw_pblc_yn"
case currentStockPrice = "stck_prpr"
case previousDayVariableRatio = "prdy_vrss"
case previousDayVariableRatioSign = "prdy_vrss_sign"
case previousDayDiffRatio = "prdy_ctrt"
case accumulatedTradingAmount = "acml_tr_pbmn"
case accumulatedVolume = "acml_vol"
case previousDayDiffVolumeRatio = "prdy_vrss_vol_rate"
case stockPrice = "stck_oprc"
case highestStockPrice = "stck_hgpr"
case lowestStockPrice = "stck_lwpr"
case maximumStockPrice = "stck_mxpr"
case minimumStockPrice = "stck_llam"
case standardStockPrice = "stck_sdpr"
case weightedAverageStockPrice = "wghn_avrg_stck_prc"
case htsForeignRunoutRate = "hts_frgn_ehrt"
case foreignNetBuyingQuantity = "frgn_ntby_qty"
case programTradeNetBuyingQuantity = "pgtr_ntby_qty"
case pivotSecondDResistancePrice = "pvt_scnd_dmrs_prc"
case pivotFirstDResistancePrice = "pvt_frst_dmrs_prc"
case pivotPointValue = "pvt_pont_val"
case pivotFirstDSupportPrice = "pvt_frst_dmsp_prc"
case pivotSecondDSupportPrice = "pvt_scnd_dmsp_prc"
case dResistanceValue = "dmrs_val"
case dSupportValue = "dmsp_val"
case capital = "cpfn"
case limitWidthPrice = "rstc_wdth_prc"
case stockFacePrice = "stck_fcam"
case stockSubstitudePrice = "stck_sspr"
case askingPriceUnit = "aspr_unit"
case htsDealQuantityUnit = "hts_deal_qty_unit_val"
case listedStockCount = "lstn_stcn"
case htsTotalMarketValue = "hts_avls"
case per
case pbr
case settlingMonth = "stac_month"
case volumeTurnoverRate = "vol_tnrt"
case eps
case bps
case day250HighestPrice = "d250_hgpr"
case day250HighestPriceDate = "d250_hgpr_date"
case day250HighestPriceDiffRatio = "d250_hgpr_vrss_prpr_rate"
case day250LowestPrice = "d250_lwpr"
case day250LowestPriceDate = "d250_lwpr_date"
case day250LowestPriceDiffRatio = "d250_lwpr_vrss_prpr_rate"
case annualHighestPrice = "stck_dryy_hgpr"
case annualHighestPriceDiffRatio = "dryy_hgpr_vrss_prpr_rate"
case annualHighestPriceDate = "dryy_hgpr_date"
case annualLowestPrice = "stck_dryy_lwpr"
case annualLowestPriceDiffRatio = "dryy_lwpr_vrss_prpr_rate"
case annualLowestPriceDate = "dryy_lwpr_date"
case week52HighestPrice = "w52_hgpr"
case week52HighestPriceDiffRatio = "w52_hgpr_vrss_prpr_ctrt"
case week52HighestPriceDate = "w52_hgpr_date"
case week52LowestPrice = "w52_lwpr"
case week52LowestPriceDiffRatio = "w52_lwpr_vrss_prpr_ctrt"
case week52LowestPriceDate = "w52_lwpr_date"
case totalOutstandingloanRate = "whol_loan_rmnd_rate"
case shortSellingAllowable = "ssts_yn"
case shortProductCode = "stck_shrn_iscd"
case facePriceCurrency = "fcam_cnnm"
case capitalCurrency = "cpfn_cnnm"
case approachRate = "apprch_rate"
case foreignHoldQuantity = "frgn_hldn_qty"
case viClassCode = "vi_cls_code"
case viClassCodeForOvertimeMarketPrice = "ovtm_vi_cls_code"
case lastShortSellingConclusionQuantity = "last_ssts_cntg_qty"
case investmentCareful = "invt_caful_yn"
case marketWarningCode = "mrkt_warn_cls_code"
case shortOverheated = "short_over_yn"
}
}
}
public struct MinutePriceResult: Codable {
public let resultCode: String
public let messageCode: String
public let message: String
public let output1: [OutputSummary]?
public let output2: [OutputPrice]?
private enum CodingKeys: String, CodingKey {
case resultCode = "rt_cd"
case messageCode = "msg_cd"
case message = "msg1"
case output1
case output2
}
public struct OutputSummary: Codable {
///
public let previousDayVariableRatio: String
///
public let previousDayVariableRatioSign: String
///
public let previousDayDiffRatio: String
///
public let previousDayStockClosingPrice: String
///
public let accumulatedVolume: String
///
public let accumulatedTradingAmount: String
/// HTS
public let htsProductName: String
///
public let currentStockPrice: String
private enum CodingKeys: String, CodingKey {
case previousDayVariableRatio = "prdy_vrss"
case previousDayVariableRatioSign = "prdy_vrss_sign"
case previousDayDiffRatio = "prdy_ctrt"
case previousDayStockClosingPrice = "stck_prdy_clpr"
case accumulatedVolume = "acml_vol"
case accumulatedTradingAmount = "acml_tr_pbmn"
case htsProductName = "hts_kor_isnm"
case currentStockPrice = "stck_prpr"
}
}
public struct OutputPrice: Codable {
///
public let stockBusinessDate: String
///
public let stockConclusionTime: String
///
public let accumulatedTradingAmount: String
///
public let currentStockPrice: String
/// 2
public let secondStockPrice: String
///
public let highestStockPrice: String
///
public let lowestStockPrice: String
///
public let conclusionVolume: String
private enum CodingKeys: String, CodingKey {
case stockBusinessDate = "stck_bsop_date"
case stockConclusionTime = "stck_cntg_hour"
case accumulatedTradingAmount = "acml_tr_pbmn"
case currentStockPrice = "stck_prpr"
case secondStockPrice = "stck_oprc"
case highestStockPrice = "stck_hgpr"
case lowestStockPrice = "stck_lwpr"
case conclusionVolume = "cntg_vol"
}
}
}

View File

@@ -0,0 +1,136 @@
//
// DomesticStockResult.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
public enum CustomerType: String {
case corporation = "B"
case personal = "P"
}
public struct OrderResult: Codable {
public let resultCode: String
public let messageCode: String
public let message: String
public let output: [OutputDetail]?
private enum CodingKeys: String, CodingKey {
case resultCode = "rt_cd"
case messageCode = "msg_cd"
case message = "msg"
case output
}
public struct OutputDetail: Codable {
public let orderOrganizationNo: String
public let orderNo: String
public let orderTime: String
private enum CodingKeys: String, CodingKey {
case orderOrganizationNo = "KRX_FWDG_ORD_ORGNO"
case orderNo = "ODNO"
case orderTime = "ORD_TMD"
}
}
}
public struct OrderRevisionResult: Codable {
public let resultCode: String
public let messageCode: String
public let message: String
public let output: [OutputDetail]?
private enum CodingKeys: String, CodingKey {
case resultCode = "rt_cd"
case messageCode = "msg_cd"
case message = "msg1"
case output
}
public struct OutputDetail: Codable {
public let orderOrganizationNo: String
public let orderNo: String
public let orderTime: String
private enum CodingKeys: String, CodingKey {
case orderOrganizationNo = "KRX_FWDG_ORD_ORGNO"
case orderNo = "ODNO"
case orderTime = "ORD_TMD"
}
}
}
public struct OrderPossibleResult: Codable {
public let resultCode: String
public let messageCode: String
public let message: String
public let output: [OutputPossibleDetail]?
private enum CodingKeys: String, CodingKey {
case resultCode = "rt_cd"
case messageCode = "msg_cd"
case message = "msg1"
case output
}
public struct OutputPossibleDetail: Codable {
///
public let orderPossibleCash: String
///
public let orderPossibleSubstitute: String
///
public let reusePossibleAmount: String
///
public let fundRepurchaseChanges: String
///
public let possibleQuantityCalcPrice: String
///
public let nonrechargeableBuyAmount: String
///
public let nonrechargeableBuyQuantity: String
///
public let maxBuyAmount: String
///
public let maxBuyQuantity: String
/// CMA
public let cmaEvaluationAmount: String
///
public let overseasReuseAmountWon: String
///
public let orderPossibleForeignCurrencyAmountWon: String
private enum CodingKeys: String, CodingKey {
case orderPossibleCash = "ord_psbl_cash"
case orderPossibleSubstitute = "ord_psbl_sbst"
case reusePossibleAmount = "ruse_psbl_amt"
case fundRepurchaseChanges = "fund_rpch_chgs"
case possibleQuantityCalcPrice = "psbl_qty_calc_unpr"
case nonrechargeableBuyAmount = "nrcvb_buy_amt"
case nonrechargeableBuyQuantity = "nrcvb_buy_qty"
case maxBuyAmount = "max_buy_amt"
case maxBuyQuantity = "max_buy_qty"
case cmaEvaluationAmount = "cma_evlu_amt"
case overseasReuseAmountWon = "ovrs_re_use_amt_wcrc"
case orderPossibleForeignCurrencyAmountWon = "ord_psbl_frcr_amt_wcrc"
}
}
}

View File

@@ -0,0 +1,117 @@
//
// DomesticStockSearch.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/14.
//
import Foundation
public enum DivisionClassCode: String {
///
case all = "0"
///
case equity = "1"
///
case preferredStock = "2"
}
public enum BelongClassCode: String {
///
case averageVolume = "0"
///
case volumeIncreaseRate = "1"
///
case averageVolumeTurnoverRate = "2"
///
case transactionValue = "3"
///
case averageTransactionValueTurnoverRate = "4"
}
extension Domestic {
/// - [v1_-047]
///
public struct StockVolumeRankRequest: TokenRequest {
public typealias KResult = VolumeRankResult
public var isMockAvailable: Bool { false }
public var url: String {
"/uapi/domestic-stock/v1/quotations/volume-rank"
}
public var method: Method { .get }
public var header: [String: String?] {
[
"authorization": "Bearer \(accessToken)",
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"tr_id": trId,
"custtype": CustomerType.personal.rawValue,
]
}
public var body: [String: Any] {
[
"FID_COND_MRKT_DIV_CODE": "J",
"FID_COND_SCR_DIV_CODE": "20171",
"FID_INPUT_ISCD": "0000", // TODO: ()
"FID_DIV_CLS_CODE": divisionClass.rawValue,
"FID_BLNG_CLS_CODE": belongClass.rawValue,
"FID_TRGT_CLS_CODE": "000000000",
"FID_TRGT_EXLS_CLS_CODE": "000000",
"FID_INPUT_PRICE_1": "",
"FID_INPUT_PRICE_2": "1000000",
"FID_VOL_CNT": "",
"FID_INPUT_DATE_1": "",
]
}
public var result: KResult? = nil
public let credential: Credential
private var trId: String {
"FHPST01710000"
}
public let accessToken: String
let divisionClass: DivisionClassCode
let belongClass: BelongClassCode
public init(credential: Credential, accessToken: String, divisionClass: DivisionClassCode, belongClass: BelongClassCode) {
self.credential = credential
self.accessToken = accessToken
self.divisionClass = divisionClass
self.belongClass = belongClass
}
}
}
// MARK: Stock Search
extension KissAccount {
public func getVolumeRanking(divisionClass: DivisionClassCode, belongClass: BelongClassCode) async throws -> VolumeRankResult {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = Domestic.StockVolumeRankRequest(credential: credential, accessToken: accessToken, divisionClass: divisionClass, belongClass: belongClass)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}

View File

@@ -0,0 +1,106 @@
//
// DomesticStockSearchResult.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/14.
//
import Foundation
public struct VolumeRankResult: Codable {
public let resultCode: String
public let messageCode: String
public let message: String
public let output1: [OutputDetail]?
private enum CodingKeys: String, CodingKey {
case resultCode = "rt_cd"
case messageCode = "msg_cd"
case message = "msg1"
case output1 = "Output1"
}
public struct OutputDetail: Codable {
/// HTS
public let htsProductName: String
///
public let shortProductNo: String
///
public let dataRank: String
///
public let currentStockPrice: String
///
public let previousDayVariableRatioSign: String
///
public let previousDayVariableRatio: String
///
public let previousDayDiffRatio: String
///
public let accumulatedVolume: String
///
public let previousDayVolume: String
///
public let listedStockCount: String
///
public let averageVolume: String
/// N
public let nDayBeforeClosingPriceDiffRatio: String
///
public let volumeIncreaseRate: String
///
public let volumeTurnoverRate: String
/// N
public let nDayVolumeTurnoverRate: String
///
public let averageTradingAmount: String
///
public let tradingAmountTurnoverRate: String
/// N
public let nDayTradingAmountTurnoverRate: String
///
public let accumulatedTradingAmount: String
private enum CodingKeys: String, CodingKey {
case htsProductName = "hts_kor_isnm"
case shortProductNo = "mksc_shrn_iscd"
case dataRank = "data_rank"
case currentStockPrice = "stck_prpr"
case previousDayVariableRatioSign = "prdy_vrss_sign"
case previousDayVariableRatio = "prdy_vrss"
case previousDayDiffRatio = "prdy_ctrt"
case accumulatedVolume = "acml_vol"
case previousDayVolume = "prdy_vol"
case listedStockCount = "lstn_stcn"
case averageVolume = "avrg_vol"
case nDayBeforeClosingPriceDiffRatio = "n_befr_clpr_vrss_prpr_rate"
case volumeIncreaseRate = "vol_inrt"
case volumeTurnoverRate = "vol_tnrt"
case nDayVolumeTurnoverRate = "nday_vol_tnrt"
case averageTradingAmount = "avrg_tr_pbmn"
case tradingAmountTurnoverRate = "tr_pbmn_tnrt"
case nDayTradingAmountTurnoverRate = "nday_tr_pbmn_tnrt"
case accumulatedTradingAmount = "acml_tr_pbmn"
}
}
}

View File

@@ -0,0 +1,10 @@
//
// ForeignFutures.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
// TODO: WORK

View File

@@ -0,0 +1,10 @@
//
// ForeignStock.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
// TODO: WORK

View File

@@ -0,0 +1,10 @@
//
// ForeignStockPrice.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
// TODO: WORK

28
KissMe/KissAccount.swift Normal file
View File

@@ -0,0 +1,28 @@
//
// KissAccount.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
public class KissAccount {
let credential: Credential
var accessTokenLock = NSLock()
var accessToken: String?
public init(credential: Credential) {
self.credential = credential
self.accessToken = nil
}
func setAccessToken(_ accessToken: String?) {
accessTokenLock.lock()
self.accessToken = accessToken
accessTokenLock.unlock()
}
}

13
KissMe/KissMe.docc/KissMe.md Executable file
View File

@@ -0,0 +1,13 @@
# ``KissMe``
<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
## Overview
<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
## Topics
### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->
- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->

18
KissMe/KissMe.h Normal file
View File

@@ -0,0 +1,18 @@
//
// KissMe.h
// KissMe
//
// Created by ened-book-m1 on 2023/05/09.
//
#import <Foundation/Foundation.h>
//! Project version number for KissMe.
FOUNDATION_EXPORT double KissMeVersionNumber;
//! Project version string for KissMe.
FOUNDATION_EXPORT const unsigned char KissMeVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <KissMe/PublicHeader.h>

175
KissMe/Login/Login.swift Normal file
View File

@@ -0,0 +1,175 @@
//
// Login.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/13.
//
import Foundation
///. OAuth - (P)[-001]
///
public struct LoginAuthRequest: AuthRequest {
public typealias KResult = TokenResult
public var url: String { "/oauth2/tokenP" }
public var method: Method { .post }
public var body: [String: Any] {
[
"grant_type": "client_credentials",
"appkey": credential.appKey,
"appsecret": credential.appSecret
]
}
public var result: KResult? = nil
public var credential: Credential
public init(credential: Credential) {
self.credential = credential
}
}
/// OAuth - (P)[-002]
///
public struct LogoutAuthRequest: AuthRequest {
public typealias KResult = MessageResult
public var url: String { "/oauth2/revokeP" }
public var method: Method { .post }
public var body: [String: Any] {
[
"appkey": credential.appKey,
"appsecret": credential.appSecret,
"token": accessToken
]
}
public var result: KResult? = nil
public var credential: Credential
let accessToken: String
public init(credential: Credential, accessToken: String) {
self.credential = credential
self.accessToken = accessToken
}
}
/// OAuth - Hashkey
///
public struct HashKeyAuthRequest: AuthRequest {
public typealias KResult = HashKeyResult
public var url: String { "/uapi/hashkey" }
public var method: Method { .post }
public var header: [String: String?] {
[
"appkey": credential.appKey,
"appsecret": credential.appSecret
]
}
public var body: [String: Any] {
[
"JsonBody": jsonBody
]
}
public var result: KResult? = nil
public let credential: Credential
let jsonBody: String
public init(credential: Credential, jsonBody: String) {
self.credential = credential
self.jsonBody = jsonBody
}
}
/// OAuth - () [-000]
///
public struct ApprovalKeyAuthRequest: AuthRequest {
public typealias KResult = ApprovalKeyResult
public var url: String { "/oauth2/Approval" }
public var method: Method { .post }
public var body: [String: Any] {
[
"grant_type": "client_credentials",
"appkey": credential.appKey,
"appsecret": credential.appSecret
]
}
public var result: KResult? = nil
public let credential: Credential
public init(credential: Credential) {
self.credential = credential
}
}
extension KissAccount {
public func login() async throws -> Bool {
return try await withUnsafeThrowingContinuation { continuation in
let request = LoginAuthRequest(credential: credential)
request.query { result in
switch result {
case .success(let result):
self.setAccessToken(result.accessToken)
continuation.resume(returning: true)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
public func logout() async throws -> Bool {
return try await withUnsafeThrowingContinuation { continuation in
guard let accessToken = accessToken else {
continuation.resume(throwing: GeneralError.invalidAccessToken)
return
}
let request = LogoutAuthRequest(credential: credential, accessToken: accessToken)
request.query { result in
switch result {
case .success(let result):
if result.code == 200 {
self.setAccessToken(nil)
continuation.resume(returning: true)
}
else {
let error = NSError(domain: "KissLogout", code: result.code, userInfo: [
NSLocalizedDescriptionKey: result.message
])
continuation.resume(throwing: error)
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
public func hashKey(json: String, completion: @escaping (Result<HashKeyResult, Error>) -> Void) {
let request = HashKeyAuthRequest(credential: credential, jsonBody: json)
request.query(completion: completion)
}
public func approvalKey(completion: @escaping (Result<ApprovalKeyResult, Error>) -> Void) {
let request = ApprovalKeyAuthRequest(credential: credential)
request.query(completion: completion)
}
}

View File

@@ -0,0 +1,42 @@
//
// LoginResult.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/14.
//
import Foundation
public struct TokenResult: Codable {
public let accessToken: String
public let tokenType: String
public let expiresIn: Int
private enum CodingKeys: String, CodingKey {
case accessToken = "access_token"
case tokenType = "token_type"
case expiresIn = "expires_in"
}
}
public struct MessageResult: Codable {
public let code: Int
public let message: String
}
public struct HashKeyResult: Codable {
public let body: String
public let hash: String
}
public struct ApprovalKeyResult: Codable {
public let approvalKey: String
private enum CodingKeys: String, CodingKey {
case approvalKey = "approval_key"
}
}

View File

@@ -0,0 +1,305 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXBuildFile section */
341F5ED42A0A8B9000962D48 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5ED32A0A8B9000962D48 /* main.swift */; };
341F5EDC2A0A8C4600962D48 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 341F5EDB2A0A8C4600962D48 /* KissMe.framework */; };
341F5F052A13B82F00962D48 /* test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F042A13B82F00962D48 /* test.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
341F5ECE2A0A8B9000962D48 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
341F5ED02A0A8B9000962D48 /* KissMeConsole */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = KissMeConsole; sourceTree = BUILT_PRODUCTS_DIR; };
341F5ED32A0A8B9000962D48 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
341F5EDB2A0A8C4600962D48 /* KissMe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KissMe.framework; sourceTree = BUILT_PRODUCTS_DIR; };
341F5F042A13B82F00962D48 /* test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = test.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
341F5ECD2A0A8B9000962D48 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
341F5EDC2A0A8C4600962D48 /* KissMe.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
341F5EC72A0A8B9000962D48 = {
isa = PBXGroup;
children = (
341F5ED22A0A8B9000962D48 /* KissMeConsole */,
341F5ED12A0A8B9000962D48 /* Products */,
341F5EDA2A0A8C4600962D48 /* Frameworks */,
);
sourceTree = "<group>";
};
341F5ED12A0A8B9000962D48 /* Products */ = {
isa = PBXGroup;
children = (
341F5ED02A0A8B9000962D48 /* KissMeConsole */,
);
name = Products;
sourceTree = "<group>";
};
341F5ED22A0A8B9000962D48 /* KissMeConsole */ = {
isa = PBXGroup;
children = (
341F5ED32A0A8B9000962D48 /* main.swift */,
341F5F042A13B82F00962D48 /* test.swift */,
);
path = KissMeConsole;
sourceTree = "<group>";
};
341F5EDA2A0A8C4600962D48 /* Frameworks */ = {
isa = PBXGroup;
children = (
341F5EDB2A0A8C4600962D48 /* KissMe.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
341F5ECF2A0A8B9000962D48 /* KissMeConsole */ = {
isa = PBXNativeTarget;
buildConfigurationList = 341F5ED72A0A8B9000962D48 /* Build configuration list for PBXNativeTarget "KissMeConsole" */;
buildPhases = (
341F5ECC2A0A8B9000962D48 /* Sources */,
341F5ECD2A0A8B9000962D48 /* Frameworks */,
341F5ECE2A0A8B9000962D48 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = KissMeConsole;
productName = KissMeConsole;
productReference = 341F5ED02A0A8B9000962D48 /* KissMeConsole */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
341F5EC82A0A8B9000962D48 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1430;
LastUpgradeCheck = 1430;
TargetAttributes = {
341F5ECF2A0A8B9000962D48 = {
CreatedOnToolsVersion = 14.3;
};
};
};
buildConfigurationList = 341F5ECB2A0A8B9000962D48 /* Build configuration list for PBXProject "KissMeConsole" */;
compatibilityVersion = "Xcode 14.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 341F5EC72A0A8B9000962D48;
productRefGroup = 341F5ED12A0A8B9000962D48 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
341F5ECF2A0A8B9000962D48 /* KissMeConsole */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
341F5ECC2A0A8B9000962D48 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
341F5ED42A0A8B9000962D48 /* main.swift in Sources */,
341F5F052A13B82F00962D48 /* test.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
341F5ED52A0A8B9000962D48 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 13.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
341F5ED62A0A8B9000962D48 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 13.3;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
};
341F5ED82A0A8B9000962D48 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NYU8YAYHF8;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
341F5ED92A0A8B9000962D48 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = NYU8YAYHF8;
ENABLE_HARDENED_RUNTIME = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
341F5ECB2A0A8B9000962D48 /* Build configuration list for PBXProject "KissMeConsole" */ = {
isa = XCConfigurationList;
buildConfigurations = (
341F5ED52A0A8B9000962D48 /* Debug */,
341F5ED62A0A8B9000962D48 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
341F5ED72A0A8B9000962D48 /* Build configuration list for PBXNativeTarget "KissMeConsole" */ = {
isa = XCConfigurationList;
buildConfigurations = (
341F5ED82A0A8B9000962D48 /* Debug */,
341F5ED92A0A8B9000962D48 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 341F5EC82A0A8B9000962D48 /* Project object */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "341F5ECF2A0A8B9000962D48"
BuildableName = "KissMeConsole"
BlueprintName = "KissMeConsole"
ReferencedContainer = "container:KissMeConsole.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
customWorkingDirectory = "$(SRCROOT)/../bin"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
viewDebuggingEnabled = "No">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "341F5ECF2A0A8B9000962D48"
BuildableName = "KissMeConsole"
BlueprintName = "KissMeConsole"
ReferencedContainer = "container:KissMeConsole.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "341F5ECF2A0A8B9000962D48"
BuildableName = "KissMeConsole"
BlueprintName = "KissMeConsole"
ReferencedContainer = "container:KissMeConsole.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,128 @@
//
// main.swift
// KissMeConsole
//
// Created by ened-book-m1 on 2023/05/09.
//
import Foundation
import KissMe
class KissConsole {
var credential: Credential? = nil
var account: KissAccount? = nil
enum KissCommand: String {
case quit = "quit"
case loginMock = "login mock"
case loginReal = "login real"
case logout = "logout"
case search = "search"
}
var isLogined: Bool {
account != nil
}
func onLoginMock() async {
guard !isLogined else {
print("Already loginged")
return
}
do {
credential = try KissCredential(isMock: true)
account = KissAccount(credential: credential!)
if try await account!.login() {
print("Success")
}
} catch {
print("\(error)")
}
}
func onLoginReal() async {
guard !isLogined else {
print("Already loginged")
return
}
do {
credential = try KissCredential(isMock: false)
account = KissAccount(credential: credential!)
if try await account!.login() {
print("Success")
}
} catch {
print("\(error)")
}
}
func onLogout() async {
guard isLogined else {
print("Already logout")
return
}
do {
_ = try await account?.logout()
credential = nil
account = nil
} catch {
print("\(error)")
}
}
func onSearch(_ arg: [String]) async {
guard isLogined else {
print("Already logout")
return
}
do {
_ = try await account?.getVolumeRanking(divisionClass: .all, belongClass: .averageVolume)
} catch {
print("\(error)")
}
}
func run() {
print("Enter command:")
let semaphore = DispatchSemaphore(value: 0)
var loop = true
while loop {
guard let line = readLine(strippingNewline: true) else {
continue
}
let args = line.split(separator: " ")
let single = args.prefix(upTo: min(2, args.count)).joined(separator: " ")
let cmd = KissCommand(rawValue: single)
Task {
switch cmd {
case .quit: break
case .loginMock: await onLoginMock()
case .loginReal: await onLoginReal()
case .logout: await onLogout()
case .search: await onSearch(args.suffix(from: 1).map { String($0) })
default:
print("Unknown command: \(single)")
}
semaphore.signal()
}
semaphore.wait()
loop = (cmd != .quit)
}
}
}
KissConsole().run()

View File

@@ -0,0 +1,61 @@
//
// test.swift
// KissMeConsole
//
// Created by ened-book-m1 on 2023/05/16.
//
import Foundation
import KissMe
func test_login_get_volume_ranking() async {
let isMock = false
let credential: Credential
do {
/// isMock true , mock-server.json
/// isMock false , real-server.json
/// server.json
/// {
/// "isMock": true,
/// "accountNo": "12345678-90"
/// "appKey": "xxxxxxxx",
/// "appSecret": "yyyyyyyyyyyyyyyyyy"
/// }
///
credential = try KissCredential(isMock: isMock)
} catch {
print("\(error)")
return
}
let account = KissAccount(credential: credential)
do {
if try await account.login() {
let result = try await account.getVolumeRanking(divisionClass: .equity, belongClass: .averageVolume)
print("\(result)")
}
} catch {
print("\(error)")
return
}
}
func test_json_result() {
let str = "{\"rt_cd\":\"1\",\"msg_cd\":\"EGW00205\",\"msg1\":\"credentials_type이 유효하지 않습니다.(Bearer)\"}"
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
let data = Data(str.utf8)
let result = try decoder.decode(VolumeRankResult.self, from: data)
print("\(result)")
}
catch {
print("\(error)")
}
}

View File

@@ -0,0 +1,36 @@
//
// KissMeTests.swift
// KissMeTests
//
// Created by ened-book-m1 on 2023/05/09.
//
import XCTest
@testable import KissMe
class KissMeTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

21
README.md Normal file
View File

@@ -0,0 +1,21 @@
## About KissMe
KissMe 는 KIS API 를 연동한 swift 라이브러리입니다.
KissMeConsole 은 command line 에서 인터렉티브 명령어로 API 호출을 테스트해볼 수 있는 도구입니다.
KissCredential 에서 사용하는 json 의 양식은 다음과 같습니다.
```json
{
"isMock": false,
"accountNo": "12345678-90",
"appKey": "xxxxxxxxxxx",
"appSecret": "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
}
```
* `isMock` 값이 `true` 이면 모의서버, `false` 이면 실전서버를 의미합니다.
* `accountNo` 에는 계좌번호를 의미합니다. 8-2 형태의 숫자로 입력합니다.
* `appKey` 는 한국투자증권 홈페이지에서 발급받은 appkey 입니다.
* `appSecret` 는 한국투자증권 홈페이지에서 발급받은 appsecret 입니다.