From bbf7d03827a6bb2554114005128cb27b1eb81f9d Mon Sep 17 00:00:00 2001 From: ened Date: Tue, 20 Jun 2023 18:30:04 +0900 Subject: [PATCH] Add KMI-0005 initial code --- .../Common/Foundation+Extensions.swift | 225 ++++++++++++ KissMe/Sources/Common/LocalContext.swift | 50 +++ .../Domestic/DomesticStockSearch.swift | 10 + KissMe/Sources/Index/IndexResult.swift | 33 ++ .../Sources/Foundation+Extensions.swift | 213 ----------- KissMeConsole/Sources/KissConsole+CSV.swift | 12 - .../Sources/KissConsole+Candle.swift | 16 +- KissMeConsole/Sources/KissConsole.swift | 8 +- KissMeConsole/Sources/LocalContext.swift | 55 --- KissMeIndex/Package.swift | 34 ++ KissMeIndex/Sources/KissIndex+0001.swift | 16 + KissMeIndex/Sources/KissIndex+0002.swift | 16 + KissMeIndex/Sources/KissIndex+0003.swift | 16 + KissMeIndex/Sources/KissIndex+0004.swift | 16 + KissMeIndex/Sources/KissIndex+0005.swift | 112 ++++++ KissMeIndex/Sources/KissIndex+0006.swift | 17 + KissMeIndex/Sources/KissIndex.swift | 111 ++++++ KissMeIndex/Sources/main.swift | 10 + KissMeMatrix/Sources/KissMatrix.swift | 16 + KissMeMatrix/Sources/main.swift | 3 +- README.md | 40 ++ bin/data | 2 +- documents/KMI/KMI-0001.md | 11 + documents/KMI/KMI-0002.md | 2 + documents/KMI/KMI-0003.md | 2 + documents/KMI/KMI-0004.md | 2 + documents/KMI/KMI-0005.md | 31 ++ documents/KMI/KMI-0006.md | 2 + documents/README.md | 9 + .../macos/KissMe.xcodeproj/project.pbxproj | 16 + .../contents.xcworkspacedata | 6 +- .../KissMeConsole.xcodeproj/project.pbxproj | 4 - .../KissMeIndex.xcodeproj/project.pbxproj | 343 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/KissMeIndex.xcscheme | 85 +++++ .../KissMeMatrix.xcodeproj/project.pbxproj | 4 + 37 files changed, 1271 insertions(+), 292 deletions(-) create mode 100644 KissMe/Sources/Common/LocalContext.swift create mode 100644 KissMe/Sources/Index/IndexResult.swift delete mode 100644 KissMeConsole/Sources/LocalContext.swift create mode 100644 KissMeIndex/Package.swift create mode 100644 KissMeIndex/Sources/KissIndex+0001.swift create mode 100644 KissMeIndex/Sources/KissIndex+0002.swift create mode 100644 KissMeIndex/Sources/KissIndex+0003.swift create mode 100644 KissMeIndex/Sources/KissIndex+0004.swift create mode 100644 KissMeIndex/Sources/KissIndex+0005.swift create mode 100644 KissMeIndex/Sources/KissIndex+0006.swift create mode 100644 KissMeIndex/Sources/KissIndex.swift create mode 100644 KissMeIndex/Sources/main.swift create mode 100644 KissMeMatrix/Sources/KissMatrix.swift create mode 100644 documents/KMI/KMI-0001.md create mode 100644 documents/KMI/KMI-0002.md create mode 100644 documents/KMI/KMI-0003.md create mode 100644 documents/KMI/KMI-0004.md create mode 100644 documents/KMI/KMI-0005.md create mode 100644 documents/KMI/KMI-0006.md create mode 100644 documents/README.md create mode 100644 projects/macos/KissMeIndex.xcodeproj/project.pbxproj create mode 100644 projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 projects/macos/KissMeIndex.xcodeproj/xcshareddata/xcschemes/KissMeIndex.xcscheme diff --git a/KissMe/Sources/Common/Foundation+Extensions.swift b/KissMe/Sources/Common/Foundation+Extensions.swift index 6533b34..3945655 100644 --- a/KissMe/Sources/Common/Foundation+Extensions.swift +++ b/KissMe/Sources/Common/Foundation+Extensions.swift @@ -40,6 +40,14 @@ extension Date { public static var appTime: TimeInterval { ProcessInfo.processInfo.systemUptime } + + public static func date(yyyyMMdd: String, HHmmss: String) -> Date? { + let dateFormatter = DateFormatter() + dateFormatter.timeZone = TimeZone(abbreviation: "KST") + dateFormatter.dateFormat = "yyyyMMddHHmmss" + let fullDate = yyyyMMdd + HHmmss + return dateFormatter.date(from: fullDate) + } } @@ -105,6 +113,25 @@ extension URL { #endif +extension URL { + public var isDirectoryExists: Bool? { + var isDir: ObjCBool = false + if FileManager.default.fileExists(atPath: path, isDirectory: &isDir) { + return isDir.boolValue + } + return false + } + + public var isFileExists: Bool? { + var isDir: ObjCBool = false + if FileManager.default.fileExists(atPath: path, isDirectory: &isDir) { + return isDir.boolValue == false + } + return false + } +} + + @_silgen_name("swift_EnumCaseName") func _getEnumCaseName(_ value: T) -> UnsafePointer? @@ -114,3 +141,201 @@ public func getEnumCaseName(for value: T) -> String? { } return nil } + + +extension FileManager { + public static func subPathFiles(_ subpath: String) -> FileManager.DirectoryEnumerator? { + let baseUrl = URL.currentDirectory().appending(path: subpath) + let manager = FileManager.default + let resourceKeys : [URLResourceKey] = [] + let enumerator = manager.enumerator(at: baseUrl, includingPropertiesForKeys: resourceKeys, options: [.skipsHiddenFiles]) { (url, error) -> Bool in + print("directoryEnumerator error at \(url): ", error) + return true + } + return enumerator + } +} + + +public func valueToString(_ any: Any) -> String { + switch any { + case let s as String: return s + case let i as Int8: return String(i) + case let i as UInt8: return String(i) + case let i as Int16: return String(i) + case let i as UInt16: return String(i) + case let i as Int32: return String(i) + case let i as UInt32: return String(i) + case let i as Int: return String(i) + case let i as UInt: return String(i) + case let i as Int64: return String(i) + case let i as UInt64: return String(i) + case let f as Float16: return String(f) + case let f as Float32: return String(f) + case let f as Float: return String(f) + case let f as Float64: return String(f) + case let d as Double: return String(d) + case let raw as any RawRepresentable: + switch raw.rawValue { + case let s as String: return s + case let i as Int8: return String(i) + case let i as UInt8: return String(i) + case let i as Int16: return String(i) + case let i as UInt16: return String(i) + case let i as Int32: return String(i) + case let i as UInt32: return String(i) + case let i as Int: return String(i) + case let i as UInt: return String(i) + case let i as Int64: return String(i) + case let i as UInt64: return String(i) + default: + return "" + } + case let c as CustomStringConvertible: return c.description + default: + return "" + } +} + + +extension Array where Element: PropertyIterable { + public func writeCsv(toFile file: URL, appendable: Bool = false, localized: Bool) throws { + if appendable, file.isFileExists == true { + try appendAtEnd(ofCsv: file) + } + else { + try overwrite(toCsv: file, localized: localized) + } + return + + // Nested function + func appendAtEnd(ofCsv file: URL) throws { + let oldHeader = try String.readCsvHeader(fromFile: file) + + var stringCsv = "" + for item in self { + let all = try item.allProperties() + + if stringCsv.isEmpty { + let header = all.map{ $0.0 } + if oldHeader != header { + let (_, field) = oldHeader.getDiff(from: header) ?? (-1, "") + throw GeneralError.incorrectCsvHeaderField(field) + } + } + + let values = all.map{ valueToString($0.1) }.joined(separator: ",").appending("\n") + stringCsv.append(values) + } + try stringCsv.writeAppending(toFile: file.path) + } + + // Nested function + func overwrite(toCsv file: URL, localized: Bool) throws { + var stringCsv = "" + for item in self { + let all = try item.allProperties() + if stringCsv.isEmpty { + let header = all.map { prop in + localized ? localizeString(prop.0): prop.0 + }.joined(separator: ",").appending("\n") + + stringCsv.append(header) + } + let values = all.map{ valueToString($0.1) }.joined(separator: ",").appending("\n") + stringCsv.append(values) + } + try stringCsv.write(toFile: file.path, atomically: true, encoding: .utf8) + } + } + + public static func readCsv(fromFile: URL, verifyHeader: Bool = true) throws -> [Element] where Element: ArrayDecodable { + let stringCsv = try String(contentsOfFile: fromFile.path, encoding: .utf8) + let items = stringCsv.split(separator: "\n") + guard items.count > 0 else { + return [] + } + + var headerItems = [String]() + var elements = [Element]() + + for (index, item) in items.enumerated() { + if index == 0 { + headerItems = item.split(separator: ",").map { String($0) } + continue + } + let array = item.split(separator: ",").map { String($0) } + let element = try Element(array: array) + + if index == 1, verifyHeader { + // Validate property with header + let properties = try element.allProperties() + for (label, _) in properties { + if false == headerItems.contains(where: { $0 == label }) { + throw GeneralError.headerNoFiendName(label) + } + } + } + elements.append(element) + } + return elements + } +} + + +extension Array where Element == String { + func getDiff(from: [Element]) -> (Int, String)? { + for (index, s) in enumerated() { + guard index < from.count else { + return (index, s) + } + if s != from[index] { + return (index, s) + } + } + return nil + } +} + + +extension String { + public init(firstLineOfFile path: String) throws { + guard let filePointer = fopen(path, "r") else { + throw GeneralError.cannotReadFile + } + + var cLineBytes: UnsafeMutablePointer? = nil + defer { + fclose(filePointer) + cLineBytes?.deallocate() + } + + var lineCap: Int = 0 + let bytesRead = getline(&cLineBytes, &lineCap, filePointer) + + guard bytesRead > 0, let cLineBytes = cLineBytes else { + throw GeneralError.cannotReadFileLine + } + guard let str = String(cString: cLineBytes, encoding: .utf8) else { + throw GeneralError.cannotReadFileToConvertString + } + self = str.trimmingCharacters(in: .whitespacesAndNewlines) + } + + public static func readCsvHeader(fromFile: URL) throws -> [String] { + let header = try String(firstLineOfFile: fromFile.path) + return header.split(separator: ",").map { String($0) } + } + + public func writeAppending(toFile path: String) throws { + guard let handle = FileHandle(forWritingAtPath: path) else { + throw GeneralError.cannotReadFile + } + defer { + try? handle.close() + } + + _ = handle.seekToEndOfFile() + handle.write(Data(utf8)) + } +} diff --git a/KissMe/Sources/Common/LocalContext.swift b/KissMe/Sources/Common/LocalContext.swift new file mode 100644 index 0000000..d5d50bb --- /dev/null +++ b/KissMe/Sources/Common/LocalContext.swift @@ -0,0 +1,50 @@ +// +// LocalContext.swift +// KissMeConsole +// +// Created by ened-book-m1 on 2023/06/10. +// + +import Foundation + + +public struct LocalName: Codable, PropertyIterable, ArrayDecodable { + public let fieldName: String + public let localizedName: String + + public init(array: [String]) throws { + guard array.count == 2 else { + throw GeneralError.incorrectArrayItems + } + fieldName = array[0] + localizedName = array[1] + } +} + +public struct LocalContext { + public static var shared = LocalContext() + + public var localNamesDic: [String: LocalName] = .init() + + mutating public func load(_ localNamesUrl: URL) { + do { + let names = try [LocalName].readCsv(fromFile: localNamesUrl) + var nameDic = [String: LocalName]() + for name in names { + nameDic[name.fieldName] = name + if name.localizedName.isEmpty { + assertionFailure("Cannot load \(localNamesUrl.path) - no localized name for \(name.fieldName)") + } + } + localNamesDic = nameDic + } catch { + print(error) + assertionFailure("Cannot load \(localNamesUrl.path)") + localNamesDic = [:] + } + } +} + +public func localizeString(_ str: String) -> String { + return LocalContext.shared.localNamesDic[str]?.localizedName ?? str +} diff --git a/KissMe/Sources/Domestic/DomesticStockSearch.swift b/KissMe/Sources/Domestic/DomesticStockSearch.swift index 6a84cf0..e34f3b3 100644 --- a/KissMe/Sources/Domestic/DomesticStockSearch.swift +++ b/KissMe/Sources/Domestic/DomesticStockSearch.swift @@ -58,6 +58,16 @@ public enum BelongClassCode: String, CustomStringConvertible { case .averageTransactionValueTurnoverRate: return "4:평균거래금액회전율" } } + + public var fileBelong: String { + switch self { + case .averageVolume: return "av" + case .volumeIncreaseRate: return "vir" + case .averageVolumeTurnoverRate: return "avtr" + case .transactionValue: return "tv" + case .averageTransactionValueTurnoverRate: return "atvtr" + } + } } diff --git a/KissMe/Sources/Index/IndexResult.swift b/KissMe/Sources/Index/IndexResult.swift new file mode 100644 index 0000000..a037c72 --- /dev/null +++ b/KissMe/Sources/Index/IndexResult.swift @@ -0,0 +1,33 @@ +// +// IndexResult.swift +// KissMe +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation + + +public struct KissIndexResult: Codable { + public let code: Int + public let message: String + public let kmi: String + public let output: [Output] + + public struct Output: Codable { + public let shortCode: String + public let weight: Double + + public init(shortCode: String, weight: Double) { + self.shortCode = shortCode + self.weight = weight + } + } + + public init(code: Int, message: String, kmi: String, output: [Output]) { + self.code = code + self.message = message + self.kmi = kmi + self.output = output + } +} diff --git a/KissMeConsole/Sources/Foundation+Extensions.swift b/KissMeConsole/Sources/Foundation+Extensions.swift index 2b8188c..c78d5c7 100644 --- a/KissMeConsole/Sources/Foundation+Extensions.swift +++ b/KissMeConsole/Sources/Foundation+Extensions.swift @@ -120,220 +120,7 @@ extension Date { } -func valueToString(_ any: Any) -> String { - switch any { - case let s as String: return s - case let i as Int8: return String(i) - case let i as UInt8: return String(i) - case let i as Int16: return String(i) - case let i as UInt16: return String(i) - case let i as Int32: return String(i) - case let i as UInt32: return String(i) - case let i as Int: return String(i) - case let i as UInt: return String(i) - case let i as Int64: return String(i) - case let i as UInt64: return String(i) - case let f as Float16: return String(f) - case let f as Float32: return String(f) - case let f as Float: return String(f) - case let f as Float64: return String(f) - case let d as Double: return String(d) - case let raw as any RawRepresentable: - switch raw.rawValue { - case let s as String: return s - case let i as Int8: return String(i) - case let i as UInt8: return String(i) - case let i as Int16: return String(i) - case let i as UInt16: return String(i) - case let i as Int32: return String(i) - case let i as UInt32: return String(i) - case let i as Int: return String(i) - case let i as UInt: return String(i) - case let i as Int64: return String(i) - case let i as UInt64: return String(i) - default: - return "" - } - case let c as CustomStringConvertible: return c.description - default: - return "" - } -} - - -extension Array where Element: PropertyIterable { - func writeCsv(toFile file: URL, appendable: Bool = false, localized: Bool) throws { - if appendable, file.isFileExists == true { - try appendAtEnd(ofCsv: file) - } - else { - try overwrite(toCsv: file, localized: localized) - } - return - - // Nested function - func appendAtEnd(ofCsv file: URL) throws { - let oldHeader = try String.readCsvHeader(fromFile: file) - - var stringCsv = "" - for item in self { - let all = try item.allProperties() - - if stringCsv.isEmpty { - let header = all.map{ $0.0 } - if oldHeader != header { - let (_, field) = oldHeader.getDiff(from: header) ?? (-1, "") - throw GeneralError.incorrectCsvHeaderField(field) - } - } - - let values = all.map{ valueToString($0.1) }.joined(separator: ",").appending("\n") - stringCsv.append(values) - } - try stringCsv.writeAppending(toFile: file.path) - } - - // Nested function - func overwrite(toCsv file: URL, localized: Bool) throws { - var stringCsv = "" - for item in self { - let all = try item.allProperties() - if stringCsv.isEmpty { - let header = all.map { prop in - localized ? localizeString(prop.0): prop.0 - }.joined(separator: ",").appending("\n") - - stringCsv.append(header) - } - let values = all.map{ valueToString($0.1) }.joined(separator: ",").appending("\n") - stringCsv.append(values) - } - try stringCsv.write(toFile: file.path, atomically: true, encoding: .utf8) - } - } - - static func readCsv(fromFile: URL, verifyHeader: Bool = true) throws -> [Element] where Element: ArrayDecodable { - let stringCsv = try String(contentsOfFile: fromFile.path, encoding: .utf8) - let items = stringCsv.split(separator: "\n") - guard items.count > 0 else { - return [] - } - - var headerItems = [String]() - var elements = [Element]() - - for (index, item) in items.enumerated() { - if index == 0 { - headerItems = item.split(separator: ",").map { String($0) } - continue - } - let array = item.split(separator: ",").map { String($0) } - let element = try Element(array: array) - - if index == 1, verifyHeader { - // Validate property with header - let properties = try element.allProperties() - for (label, _) in properties { - if false == headerItems.contains(where: { $0 == label }) { - throw GeneralError.headerNoFiendName(label) - } - } - } - elements.append(element) - } - return elements - } -} - - -extension Array where Element == String { - func getDiff(from: [Element]) -> (Int, String)? { - for (index, s) in enumerated() { - guard index < from.count else { - return (index, s) - } - if s != from[index] { - return (index, s) - } - } - return nil - } -} - - -extension String { - init(firstLineOfFile path: String) throws { - guard let filePointer = fopen(path, "r") else { - throw GeneralError.cannotReadFile - } - - var cLineBytes: UnsafeMutablePointer? = nil - defer { - fclose(filePointer) - cLineBytes?.deallocate() - } - - var lineCap: Int = 0 - let bytesRead = getline(&cLineBytes, &lineCap, filePointer) - - guard bytesRead > 0, let cLineBytes = cLineBytes else { - throw GeneralError.cannotReadFileLine - } - guard let str = String(cString: cLineBytes, encoding: .utf8) else { - throw GeneralError.cannotReadFileToConvertString - } - self = str.trimmingCharacters(in: .whitespacesAndNewlines) - } - - static func readCsvHeader(fromFile: URL) throws -> [String] { - let header = try String(firstLineOfFile: fromFile.path) - return header.split(separator: ",").map { String($0) } - } - - func writeAppending(toFile path: String) throws { - guard let handle = FileHandle(forWritingAtPath: path) else { - throw GeneralError.cannotReadFile - } - defer { - try? handle.close() - } - - _ = handle.seekToEndOfFile() - handle.write(Data(utf8)) - } -} - - -extension URL { - var isDirectoryExists: Bool? { - var isDir: ObjCBool = false - if FileManager.default.fileExists(atPath: path, isDirectory: &isDir) { - return isDir.boolValue - } - return false - } - - var isFileExists: Bool? { - var isDir: ObjCBool = false - if FileManager.default.fileExists(atPath: path, isDirectory: &isDir) { - return isDir.boolValue == false - } - return false - } -} - - extension FileManager { - static func subPathFiles(_ subpath: String) -> FileManager.DirectoryEnumerator? { - let baseUrl = URL.currentDirectory().appending(path: subpath) - let manager = FileManager.default - let resourceKeys : [URLResourceKey] = [] - let enumerator = manager.enumerator(at: baseUrl, includingPropertiesForKeys: resourceKeys, options: [.skipsHiddenFiles]) { (url, error) -> Bool in - print("directoryEnumerator error at \(url): ", error) - return true - } - return enumerator - } /// period: If nil, all period of csv collected. /// candleDate: If nil, all date of csv collected. diff --git a/KissMeConsole/Sources/KissConsole+CSV.swift b/KissMeConsole/Sources/KissConsole+CSV.swift index 15013a7..e200d3e 100644 --- a/KissMeConsole/Sources/KissConsole+CSV.swift +++ b/KissMeConsole/Sources/KissConsole+CSV.swift @@ -225,15 +225,3 @@ extension KissConsole { return .ok } } - -extension BelongClassCode { - var fileBelong: String { - switch self { - case .averageVolume: return "av" - case .volumeIncreaseRate: return "vir" - case .averageVolumeTurnoverRate: return "avtr" - case .transactionValue: return "tv" - case .averageTransactionValueTurnoverRate: return "atvtr" - } - } -} diff --git a/KissMeConsole/Sources/KissConsole+Candle.swift b/KissMeConsole/Sources/KissConsole+Candle.swift index 06ef8f9..f51960e 100644 --- a/KissMeConsole/Sources/KissConsole+Candle.swift +++ b/KissMeConsole/Sources/KissConsole+Candle.swift @@ -185,7 +185,21 @@ extension KissConsole { let result = try await account!.getHolyday(baseDate: day) do { - try result.output?.writeCsv(toFile: KissConsole.holidayUrl, localized: localized) + var olds = try [HolidyResult.OutputDetail].readCsv(fromFile: KissConsole.holidayUrl) + if let output = result.output { + /// Merge current holidays and new holidays + var recents = output.map { $0 } + for old in olds { + if let _ = recents.first(where: { $0.baseDate == old.baseDate }) { + continue + } + recents.append(old) + } + recents.sort(by: { $0.baseDate < $1.baseDate }) + + /// Write back with merged one + try recents.writeCsv(toFile: KissConsole.holidayUrl, localized: localized) + } } catch { print(error) } diff --git a/KissMeConsole/Sources/KissConsole.swift b/KissMeConsole/Sources/KissConsole.swift index dd0dd37..6a35b96 100644 --- a/KissMeConsole/Sources/KissConsole.swift +++ b/KissMeConsole/Sources/KissConsole.swift @@ -328,7 +328,7 @@ extension KissConsole { } private func loadLocalName() { - _ = LocalContext.shared.localNamesDic + LocalContext.shared.load(KissConsole.localNamesUrl) } func setCurrent(productNo: String) { @@ -1118,3 +1118,9 @@ private extension Array { return suffix(from: from).map { String($0) } } } + + +enum OnOff: String { + case on + case off +} diff --git a/KissMeConsole/Sources/LocalContext.swift b/KissMeConsole/Sources/LocalContext.swift deleted file mode 100644 index 175ca10..0000000 --- a/KissMeConsole/Sources/LocalContext.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// LocalContext.swift -// KissMeConsole -// -// Created by ened-book-m1 on 2023/06/10. -// - -import Foundation -import KissMe - - -struct LocalName: Codable, PropertyIterable, ArrayDecodable { - let fieldName: String - let localizedName: String - - init(array: [String]) throws { - guard array.count == 2 else { - throw GeneralError.incorrectArrayItems - } - fieldName = array[0] - localizedName = array[1] - } -} - -struct LocalContext { - static var shared = LocalContext() - - lazy var localNamesDic: [String: LocalName] = { - do { - let names = try [LocalName].readCsv(fromFile: KissConsole.localNamesUrl) - var nameDic = [String: LocalName]() - for name in names { - nameDic[name.fieldName] = name - if name.localizedName.isEmpty { - assertionFailure("Cannot load \(KissConsole.localNamesUrl.path) - no localized name for \(name.fieldName)") - } - } - return nameDic - } catch { - print(error) - assertionFailure("Cannot load \(KissConsole.localNamesUrl.path)") - return [:] - } - }() -} - -func localizeString(_ str: String) -> String { - return LocalContext.shared.localNamesDic[str]?.localizedName ?? str -} - - -enum OnOff: String { - case on - case off -} diff --git a/KissMeIndex/Package.swift b/KissMeIndex/Package.swift new file mode 100644 index 0000000..3bb4b7e --- /dev/null +++ b/KissMeIndex/Package.swift @@ -0,0 +1,34 @@ +// swift-tools-version: 5.8 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "KissMeIndex", + platforms: [ + .macOS(.v13), .iOS(.v14), .tvOS(.v14) + ], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .executable( + name: "KissMeIndex", + targets: ["KissMeIndex"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + //.package(url: "../KissMe", from: "1.0.0"), + .package(path: "../KissMe"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .executableTarget( + name: "KissMeIndex", + dependencies: ["KissMe"], + path: "Sources"), + .testTarget( + name: "KissMeIndexTests", + dependencies: ["KissMeIndex"], + path: "Tests"), + ] +) diff --git a/KissMeIndex/Sources/KissIndex+0001.swift b/KissMeIndex/Sources/KissIndex+0001.swift new file mode 100644 index 0000000..ccb8f6c --- /dev/null +++ b/KissMeIndex/Sources/KissIndex+0001.swift @@ -0,0 +1,16 @@ +// +// KissIndex+0001.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation + + +extension KissIndex { + + func indexSet_0001(date: Date, config: String?, kmi: KissIndexType) { + // TODO: work + } +} diff --git a/KissMeIndex/Sources/KissIndex+0002.swift b/KissMeIndex/Sources/KissIndex+0002.swift new file mode 100644 index 0000000..be35094 --- /dev/null +++ b/KissMeIndex/Sources/KissIndex+0002.swift @@ -0,0 +1,16 @@ +// +// KissIndex+0002.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation + + +extension KissIndex { + + func indexSet_0002(date: Date, config: String?, kmi: KissIndexType) { + // TODO: work + } +} diff --git a/KissMeIndex/Sources/KissIndex+0003.swift b/KissMeIndex/Sources/KissIndex+0003.swift new file mode 100644 index 0000000..4be960c --- /dev/null +++ b/KissMeIndex/Sources/KissIndex+0003.swift @@ -0,0 +1,16 @@ +// +// KissIndex+0003.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation + + +extension KissIndex { + + func indexSet_0003(date: Date, config: String?, kmi: KissIndexType) { + // TODO: work + } +} diff --git a/KissMeIndex/Sources/KissIndex+0004.swift b/KissMeIndex/Sources/KissIndex+0004.swift new file mode 100644 index 0000000..874f837 --- /dev/null +++ b/KissMeIndex/Sources/KissIndex+0004.swift @@ -0,0 +1,16 @@ +// +// KissIndex+0004.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation + + +extension KissIndex { + + func indexSet_0004(date: Date, config: String?, kmi: KissIndexType) { + // TODO: work + } +} diff --git a/KissMeIndex/Sources/KissIndex+0005.swift b/KissMeIndex/Sources/KissIndex+0005.swift new file mode 100644 index 0000000..f44e01f --- /dev/null +++ b/KissMeIndex/Sources/KissIndex+0005.swift @@ -0,0 +1,112 @@ +// +// KissIndex+0005.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation +import KissMe + + +extension KissIndex { + + func indexSet_0005(date: Date, config: String?, kmi: KissIndexType) { + //let belongs: [BelongClassCode] = [.averageVolume, .volumeIncreaseRate, .averageVolumeTurnoverRate, .transactionValue, .averageTransactionValueTurnoverRate] + let belongs: [BelongClassCode] = [.averageVolume] + + do { + var scoreMap = [String: Int]() + + for belong in belongs { + let topUrl = try KissIndex.pickNearTopProductsUrl(belong, date: date) + let data = try [VolumeRankResult.OutputDetail].readCsv(fromFile: topUrl, verifyHeader: true) + + for (index, item) in data.enumerated() { + let score = (30 - index) + if let _ = scoreMap[item.shortProductNo] { + scoreMap[item.shortProductNo]! += score + } + else { + scoreMap[item.shortProductNo] = score + } + } + } + + let totalScores = scoreMap.reduce(0, { $0 + $1.value }) + let scoreArray = scoreMap.map { ($0.key, $0.value) }.sorted(by: { $0.1 > $1.1 }) + + var outputs = [KissIndexResult.Output]() + for array in scoreArray { + let weight = Double(array.1) / Double(totalScores) + let output = KissIndexResult.Output(shortCode: array.0, weight: weight) + outputs.append(output) + } + + writeOutput(outputs, kmi: kmi) + } + catch { + writeError(error, kmi: kmi) + } + } + + + static func pickNearTopProductsUrl(_ belong: BelongClassCode, date: Date) throws -> URL { + let subPath = "data/top30/\(date.yyyyMMdd)" + let dayFile = "top30-\(belong.fileBelong)-\(date.yyyyMMdd)-" + + guard let enumerator = FileManager.subPathFiles(subPath) else { + throw GeneralError.noCsvFile + } + + let dateHHmmss = date.HHmmss + var csvUrls = [URL]() + for case let fileUrl as URL in enumerator { + guard fileUrl.pathExtension == "csv" else { + continue + } + + let dayPrefixFile = fileUrl.lastPathComponent.prefix(dayFile.utf8.count) + let hourOfFile = fileUrl.lastPathComponent.suffix(10).split(separator: ".") + if dayPrefixFile == dayFile, hourOfFile.count == 2 { + let hourFrag = String(hourOfFile[0]) + if hourFrag <= dateHHmmss { + csvUrls.append(fileUrl) + } + } + } + + /// Sorted by near date + csvUrls.sort(by: { dateHHmmss.diffSecondsTwoCsvHHmmss($0.lastPathComponent) < dateHHmmss.diffSecondsTwoCsvHHmmss($1.lastPathComponent) }) + + //csvUrls.forEach( { print($0) }) + guard let nearestFile = csvUrls.first else { + throw GeneralError.noCsvFile + } + + //print("nearest file: \(nearestFile) at \(date.yyyyMMdd_HHmmss_forTime)") + return nearestFile + } +} + + +extension String { + var csvHHmmssBySeconds: TimeInterval? { + let csvTopProductsFrags = split(separator: "-") + guard csvTopProductsFrags.count == 4 else { + return nil + } + guard csvTopProductsFrags[0] == "top30" else { + return nil + } + let hourNames = csvTopProductsFrags[3].split(separator: ".") + guard hourNames.count == 2, let (hour, min, sec) = String(hourNames[0]).HHmmss else { + return nil + } + return TimeInterval(hour * 60 * 60 + min * 60 + sec) + } + + func diffSecondsTwoCsvHHmmss(_ another: String) -> TimeInterval { + (csvHHmmssBySeconds ?? 0) - (another.csvHHmmssBySeconds ?? 0) + } +} diff --git a/KissMeIndex/Sources/KissIndex+0006.swift b/KissMeIndex/Sources/KissIndex+0006.swift new file mode 100644 index 0000000..aafb16c --- /dev/null +++ b/KissMeIndex/Sources/KissIndex+0006.swift @@ -0,0 +1,17 @@ +// +// KissIndex+0006.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation +import KissMe + + +extension KissIndex { + + func indexSet_0006(date: Date, config: String?, kmi: KissIndexType) { + // TODO: work + } +} diff --git a/KissMeIndex/Sources/KissIndex.swift b/KissMeIndex/Sources/KissIndex.swift new file mode 100644 index 0000000..c1ab547 --- /dev/null +++ b/KissMeIndex/Sources/KissIndex.swift @@ -0,0 +1,111 @@ +// +// KissIndex.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation +import KissMe + + +enum KissIndexType: String { + case _0001 = "KMI-0001" + case _0002 = "KMI-0002" + case _0003 = "KMI-0003" + case _0004 = "KMI-0004" + case _0005 = "KMI-0005" + case _0006 = "KMI-0006" +} + + +class KissIndex { + + func run() { + guard CommandLine.argc >= 4 else { + let appName = (CommandLine.arguments[0] as NSString).lastPathComponent + print("\(appName) KMI-0001 yyyyMMdd HHmmss [config.json]") + return + } + + guard let kmi = CommandLine.arguments[1].kmiIndex else { + print("Invalid KMI index name") + return + } + + let day = CommandLine.arguments[2] + let hour = CommandLine.arguments[3] + guard let date = Date.date(yyyyMMdd: day, HHmmss: hour) else { + print("Invalid timestamp: \(day) \(hour)") + return + } + + let config: String? + if CommandLine.argc >= 5 { + config = CommandLine.arguments[4] + } + else { + config = nil + } + + let kmiType = KissIndexType(rawValue: kmi) + switch kmiType { + case ._0001: + indexSet_0001(date: date, config: config, kmi: kmiType!) + case ._0002: + indexSet_0002(date: date, config: config, kmi: kmiType!) + case ._0003: + indexSet_0003(date: date, config: config, kmi: kmiType!) + case ._0004: + indexSet_0004(date: date, config: config, kmi: kmiType!) + case ._0005: + indexSet_0005(date: date, config: config, kmi: kmiType!) + case ._0006: + indexSet_0006(date: date, config: config, kmi: kmiType!) + + default: + print("Unsupported index set: \(kmi)") + } + } + + + func writeError(_ error: Error, kmi: KissIndexType) { + let result = KissIndexResult(code: 300, message: error.localizedDescription, kmi: kmi.rawValue, output: []) + do { + let jsonData = try JSONEncoder().encode(result) + let jsonString = String(data: jsonData, encoding: .utf8)! + print(jsonString) + } catch { + assertionFailure(error.localizedDescription) + } + } + + func writeOutput(_ output: [KissIndexResult.Output], kmi: KissIndexType) { + let result = KissIndexResult(code: 200, message: "OK", kmi: kmi.rawValue, output: output) + do { + let jsonData = try JSONEncoder().encode(result) + let jsonString = String(data: jsonData, encoding: .utf8)! + print(jsonString) + } catch { + assertionFailure(error.localizedDescription) + } + } + +// private func isHoliday(_ date: Date) -> Bool { +// +// } +} + + +extension String { + var kmiIndex: String? { + guard utf8.count == 8, String(prefix(4)).uppercased() == "KMI-" else { + return nil + } + let index = String(suffix(4)) + guard let _ = Int(index) else { + return nil + } + return "KMI-\(index)" + } +} diff --git a/KissMeIndex/Sources/main.swift b/KissMeIndex/Sources/main.swift new file mode 100644 index 0000000..c425fce --- /dev/null +++ b/KissMeIndex/Sources/main.swift @@ -0,0 +1,10 @@ +// +// main.swift +// KissMeIndex +// +// Created by ened-book-m1 on 2023/06/19. +// + +import Foundation + +KissIndex().run() diff --git a/KissMeMatrix/Sources/KissMatrix.swift b/KissMeMatrix/Sources/KissMatrix.swift new file mode 100644 index 0000000..691a6f4 --- /dev/null +++ b/KissMeMatrix/Sources/KissMatrix.swift @@ -0,0 +1,16 @@ +// +// KissMatrix.swift +// KissMeMatrix +// +// Created by ened-book-m1 on 2023/06/20. +// + +import Foundation + + +class KissMatrix { + + func run() { + + } +} diff --git a/KissMeMatrix/Sources/main.swift b/KissMeMatrix/Sources/main.swift index 629e95f..1eafc6d 100644 --- a/KissMeMatrix/Sources/main.swift +++ b/KissMeMatrix/Sources/main.swift @@ -7,5 +7,4 @@ import Foundation -print("Hello, World!") - +KissMatrix().run() diff --git a/README.md b/README.md index 543530c..9c29661 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,46 @@ WIP `showcase` | 추천 상품을 제안함. * 평균거래금액회전율: atvtr (average-transaction-value-turnover-rate) +# KissMeIndex + +KissMeIndex 는 지표 집합(index set)을 추출하는 도구입니다. + +## INPUT + +INPUT 으로는 다음과 같은 값을 제공합니다. + +* 환경설정 : config.json 파일로 특정 지표에서 보정으로 필요로 하는 설정을 기입합니다. +* 현재시간 : timestamp 값을 반드시 필요로 합니다. simulator 에서도 이 기능을 활용할 수 있습니다. + +## OUTPUT + +OUTPUT 은 다음과 같은 값을 json 형태로 제공합니다. + +* shortCode : 추천종목 코드 번호입니다. +* weight : [-1.0, 1.0] 사이의 가중치 값입니다. 음수이면 매도 성향이고, 양수이면 매수성향입니다. + +## Example + +```bash +./KissMeIndex KMI-0001 20230616 100000 config.json +{ + "code": 200, + "message": "OK", + "kmi": "KMI-0005", + "output": [ + { + "shortCode": "005930", + "weight": -1.0 + }, + { + "shortCode": "247540", + "weight": 0.5 + } + ] +} +``` + + # KissMeMatrix KissMeMatrix 는 다양한 주식의 지표 집합(index set) 통해서 교집합 종목을 찾아내는 데이터 모델 도구입니다. diff --git a/bin/data b/bin/data index 27ffa93..239afe4 160000 --- a/bin/data +++ b/bin/data @@ -1 +1 @@ -Subproject commit 27ffa93d3b4dd673379fff99f10e583bec26c25e +Subproject commit 239afe4f22e558a5457da5a2c965ad98710cc4ad diff --git a/documents/KMI/KMI-0001.md b/documents/KMI/KMI-0001.md new file mode 100644 index 0000000..d37986e --- /dev/null +++ b/documents/KMI/KMI-0001.md @@ -0,0 +1,11 @@ +# KMI-0001 + +일봉을 기준으로 최근의 변화량을 분석한 지표입니다. + +RSI, MACD 를 계산해서 종목을 추천합니다. + +## 변경내역 + +* 2023-06-17 + * RSI 의 분석을 실시간이 아닌 어제 데이터 기준으로 처리 대응. + * 실시간 데이터로 처리하는 것은 현재 ROI 가 적절하지 않음. diff --git a/documents/KMI/KMI-0002.md b/documents/KMI/KMI-0002.md new file mode 100644 index 0000000..b181662 --- /dev/null +++ b/documents/KMI/KMI-0002.md @@ -0,0 +1,2 @@ +# KMI-0002 + diff --git a/documents/KMI/KMI-0003.md b/documents/KMI/KMI-0003.md new file mode 100644 index 0000000..e64545c --- /dev/null +++ b/documents/KMI/KMI-0003.md @@ -0,0 +1,2 @@ +# KMI-0003 + diff --git a/documents/KMI/KMI-0004.md b/documents/KMI/KMI-0004.md new file mode 100644 index 0000000..690bbe4 --- /dev/null +++ b/documents/KMI/KMI-0004.md @@ -0,0 +1,2 @@ +# KMI-0004 + diff --git a/documents/KMI/KMI-0005.md b/documents/KMI/KMI-0005.md new file mode 100644 index 0000000..4d715af --- /dev/null +++ b/documents/KMI/KMI-0005.md @@ -0,0 +1,31 @@ +# KMI-0005 + +## How to +한국투자증권에서 제공하는 [거래량 순위 API](https://apiportal.koreainvestment.com/apiservice/apiservice-domestic-stock-quotations#L_6df56964-f22b-43d4-9457-f06264018e5b) 로부터 TOP 30 종목을 10분마다 얻은 데이터를 기반으로 산출합니다. + +* 거래량순위[v1_국내주식-047] +* /uapi/domestic-stock/v1/quotations/volume-rank + +여기에는 다음의 5가지의 분류 항목이 있습니다. + +1. 평균거래량 +2. 거래증가율 +3. 평균거래회전율 +4. 거래금액순 +5. 평균거래금액회전율 + +1~30등까지 각 30점에서 1점까지 부여하고, 5가지 분류에 대해서 합집합을 구합니다. 합집합을 계산할 때 정규화하여 얻습니다. + +## Usage + +다음은 Index 데이터를 추출하는 방법입니다. + +```bash +./KissMeIndex KMI-0005 20230618 105922 config.json + + +``` + +## Configuration + +현재 여기에는 환경설정 정보가 없습니다. diff --git a/documents/KMI/KMI-0006.md b/documents/KMI/KMI-0006.md new file mode 100644 index 0000000..0008f23 --- /dev/null +++ b/documents/KMI/KMI-0006.md @@ -0,0 +1,2 @@ +# KMI-0006 + diff --git a/documents/README.md b/documents/README.md new file mode 100644 index 0000000..860c5c4 --- /dev/null +++ b/documents/README.md @@ -0,0 +1,9 @@ + +# KMI (Kiss Me Index) + +* [KMI-0001](KMI/KMI-0001.md) +* [KMI-0002](KMI/KMI-0002.md) +* [KMI-0003](KMI/KMI-0003.md) +* [KMI-0004](KMI/KMI-0004.md) +* [KMI-0005](KMI/KMI-0005.md) +* [KMI-0006](KMI/KMI-0006.md) diff --git a/projects/macos/KissMe.xcodeproj/project.pbxproj b/projects/macos/KissMe.xcodeproj/project.pbxproj index f71dab6..e524571 100644 --- a/projects/macos/KissMe.xcodeproj/project.pbxproj +++ b/projects/macos/KissMe.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ 3435A7F72A35D82000D604F1 /* DomesticShortSelling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F62A35D82000D604F1 /* DomesticShortSelling.swift */; }; 349C26AB2A1EAE2400F3EC91 /* KissProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349C26AA2A1EAE2400F3EC91 /* KissProfile.swift */; }; 34D3680F2A2AA0BE005E6756 /* PropertyIterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D3680E2A2AA0BE005E6756 /* PropertyIterable.swift */; }; + 34F190092A418E130068C697 /* LocalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F190082A418E130068C697 /* LocalContext.swift */; }; + 34F1900C2A41982A0068C697 /* IndexResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F1900B2A41982A0068C697 /* IndexResult.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -79,6 +81,8 @@ 3435A7F62A35D82000D604F1 /* DomesticShortSelling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomesticShortSelling.swift; sourceTree = ""; }; 349C26AA2A1EAE2400F3EC91 /* KissProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissProfile.swift; sourceTree = ""; }; 34D3680E2A2AA0BE005E6756 /* PropertyIterable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyIterable.swift; sourceTree = ""; }; + 34F190082A418E130068C697 /* LocalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalContext.swift; sourceTree = ""; }; + 34F1900B2A41982A0068C697 /* IndexResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexResult.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -121,6 +125,7 @@ 341F5EAD2A0A80EC00962D48 /* KissMe */ = { isa = PBXGroup; children = ( + 34F1900A2A41981A0068C697 /* Index */, 341F5EF32A0F88AC00962D48 /* Common */, 341F5EEA2A0F882300962D48 /* Foreign */, 341F5EE62A0F3EFB00962D48 /* Domestic */, @@ -190,6 +195,7 @@ 341F5F022A11A2BC00962D48 /* Credential.swift */, 341F5F062A14634F00962D48 /* Foundation+Extensions.swift */, 34D3680E2A2AA0BE005E6756 /* PropertyIterable.swift */, + 34F190082A418E130068C697 /* LocalContext.swift */, ); path = Common; sourceTree = ""; @@ -210,6 +216,14 @@ path = ShortSelling; sourceTree = ""; }; + 34F1900A2A41981A0068C697 /* Index */ = { + isa = PBXGroup; + children = ( + 34F1900B2A41982A0068C697 /* IndexResult.swift */, + ); + path = Index; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -330,12 +344,14 @@ 341F5EF72A0F8B0500962D48 /* DomesticStockResult.swift in Sources */, 341F5F0F2A15223A00962D48 /* SeibroRequest.swift in Sources */, 341F5EF02A0F886600962D48 /* ForeignFutures.swift in Sources */, + 34F1900C2A41982A0068C697 /* IndexResult.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 */, 349C26AB2A1EAE2400F3EC91 /* KissProfile.swift in Sources */, + 34F190092A418E130068C697 /* LocalContext.swift in Sources */, 341F5F012A11155100962D48 /* DomesticStockSearchResult.swift in Sources */, 341F5F142A16CD7A00962D48 /* DomesticShopProduct.swift in Sources */, 341F5EF22A0F887200962D48 /* DomesticFutures.swift in Sources */, diff --git a/projects/macos/KissMe.xcworkspace/contents.xcworkspacedata b/projects/macos/KissMe.xcworkspace/contents.xcworkspacedata index dc89c12..d81ad0d 100644 --- a/projects/macos/KissMe.xcworkspace/contents.xcworkspacedata +++ b/projects/macos/KissMe.xcworkspace/contents.xcworkspacedata @@ -1,9 +1,6 @@ - - @@ -13,6 +10,9 @@ + + diff --git a/projects/macos/KissMeConsole.xcodeproj/project.pbxproj b/projects/macos/KissMeConsole.xcodeproj/project.pbxproj index 478d9d8..376a242 100644 --- a/projects/macos/KissMeConsole.xcodeproj/project.pbxproj +++ b/projects/macos/KissMeConsole.xcodeproj/project.pbxproj @@ -13,7 +13,6 @@ 3435A7F22A35A8A900D604F1 /* KissConsole+Investor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */; }; 3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3435A7F32A35B4D000D604F1 /* KissConsole+Price.swift */; }; 348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348168482A2F92AC00A50BD3 /* KissContext.swift */; }; - 348168692A3420BD00A50BD3 /* LocalContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 348168682A3420BD00A50BD3 /* LocalContext.swift */; }; 349327F72A20E3E300097063 /* Foundation+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349327F62A20E3E300097063 /* Foundation+Extensions.swift */; }; 349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349843202A242AC900E85B08 /* KissConsole+CSV.swift */; }; 34D3680D2A280801005E6756 /* KissConsole+Candle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D3680C2A280801005E6756 /* KissConsole+Candle.swift */; }; @@ -53,7 +52,6 @@ 3435A7F12A35A8A900D604F1 /* KissConsole+Investor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Investor.swift"; sourceTree = ""; }; 3435A7F32A35B4D000D604F1 /* KissConsole+Price.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+Price.swift"; sourceTree = ""; }; 348168482A2F92AC00A50BD3 /* KissContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissContext.swift; sourceTree = ""; }; - 348168682A3420BD00A50BD3 /* LocalContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalContext.swift; sourceTree = ""; }; 349327F62A20E3E300097063 /* Foundation+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Foundation+Extensions.swift"; sourceTree = ""; }; 3498431E2A24287600E85B08 /* KissMeConsoleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KissMeConsoleTests.swift; sourceTree = ""; }; 349843202A242AC900E85B08 /* KissConsole+CSV.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissConsole+CSV.swift"; sourceTree = ""; }; @@ -95,7 +93,6 @@ children = ( 341F5ED32A0A8B9000962D48 /* main.swift */, 341F5F042A13B82F00962D48 /* test.swift */, - 348168682A3420BD00A50BD3 /* LocalContext.swift */, 348168482A2F92AC00A50BD3 /* KissContext.swift */, 341F5F082A1463A100962D48 /* KissConsole.swift */, 34D3680C2A280801005E6756 /* KissConsole+Candle.swift */, @@ -190,7 +187,6 @@ 341F5F092A1463A100962D48 /* KissConsole.swift in Sources */, 3435A7F22A35A8A900D604F1 /* KissConsole+Investor.swift in Sources */, 341F5F052A13B82F00962D48 /* test.swift in Sources */, - 348168692A3420BD00A50BD3 /* LocalContext.swift in Sources */, 349843212A242AC900E85B08 /* KissConsole+CSV.swift in Sources */, 348168492A2F92AC00A50BD3 /* KissContext.swift in Sources */, 3435A7F42A35B4D000D604F1 /* KissConsole+Price.swift in Sources */, diff --git a/projects/macos/KissMeIndex.xcodeproj/project.pbxproj b/projects/macos/KissMeIndex.xcodeproj/project.pbxproj new file mode 100644 index 0000000..fbaff0c --- /dev/null +++ b/projects/macos/KissMeIndex.xcodeproj/project.pbxproj @@ -0,0 +1,343 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 34F18FEB2A3FA2CA0068C697 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F18FEA2A3FA2CA0068C697 /* main.swift */; }; + 34F18FF22A41299D0068C697 /* KissIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F18FF12A41299D0068C697 /* KissIndex.swift */; }; + 34F18FF52A4148950068C697 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34F18FF42A4148950068C697 /* KissMe.framework */; }; + 34F18FF62A4148950068C697 /* KissMe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34F18FF42A4148950068C697 /* KissMe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 34F18FF92A41544E0068C697 /* KissIndex+0006.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F18FF82A41544E0068C697 /* KissIndex+0006.swift */; }; + 34F18FFB2A4155750068C697 /* KissIndex+0005.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F18FFA2A4155750068C697 /* KissIndex+0005.swift */; }; + 34F18FFD2A4155890068C697 /* KissIndex+0004.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F18FFC2A4155890068C697 /* KissIndex+0004.swift */; }; + 34F18FFF2A4155BF0068C697 /* KissIndex+0003.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F18FFE2A4155BF0068C697 /* KissIndex+0003.swift */; }; + 34F190012A4155D70068C697 /* KissIndex+0002.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F190002A4155D70068C697 /* KissIndex+0002.swift */; }; + 34F190032A4155F90068C697 /* KissIndex+0001.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F190022A4155F90068C697 /* KissIndex+0001.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 34F18FE52A3FA2CA0068C697 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 34F18FF72A4148950068C697 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 34F18FF62A4148950068C697 /* KissMe.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 34F18FE72A3FA2CA0068C697 /* KissMeIndex */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = KissMeIndex; sourceTree = BUILT_PRODUCTS_DIR; }; + 34F18FEA2A3FA2CA0068C697 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 34F18FF12A41299D0068C697 /* KissIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissIndex.swift; sourceTree = ""; }; + 34F18FF42A4148950068C697 /* KissMe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KissMe.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 34F18FF82A41544E0068C697 /* KissIndex+0006.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissIndex+0006.swift"; sourceTree = ""; }; + 34F18FFA2A4155750068C697 /* KissIndex+0005.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissIndex+0005.swift"; sourceTree = ""; }; + 34F18FFC2A4155890068C697 /* KissIndex+0004.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissIndex+0004.swift"; sourceTree = ""; }; + 34F18FFE2A4155BF0068C697 /* KissIndex+0003.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissIndex+0003.swift"; sourceTree = ""; }; + 34F190002A4155D70068C697 /* KissIndex+0002.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissIndex+0002.swift"; sourceTree = ""; }; + 34F190022A4155F90068C697 /* KissIndex+0001.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KissIndex+0001.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 34F18FE42A3FA2CA0068C697 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 34F18FF52A4148950068C697 /* KissMe.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 34F18FDE2A3FA2CA0068C697 = { + isa = PBXGroup; + children = ( + 34F18FE92A3FA2CA0068C697 /* KissMeIndex */, + 34F18FE82A3FA2CA0068C697 /* Products */, + 34F18FF32A4148950068C697 /* Frameworks */, + ); + sourceTree = ""; + }; + 34F18FE82A3FA2CA0068C697 /* Products */ = { + isa = PBXGroup; + children = ( + 34F18FE72A3FA2CA0068C697 /* KissMeIndex */, + ); + name = Products; + sourceTree = ""; + }; + 34F18FE92A3FA2CA0068C697 /* KissMeIndex */ = { + isa = PBXGroup; + children = ( + 34F18FEA2A3FA2CA0068C697 /* main.swift */, + 34F18FF12A41299D0068C697 /* KissIndex.swift */, + 34F190022A4155F90068C697 /* KissIndex+0001.swift */, + 34F190002A4155D70068C697 /* KissIndex+0002.swift */, + 34F18FFE2A4155BF0068C697 /* KissIndex+0003.swift */, + 34F18FFC2A4155890068C697 /* KissIndex+0004.swift */, + 34F18FFA2A4155750068C697 /* KissIndex+0005.swift */, + 34F18FF82A41544E0068C697 /* KissIndex+0006.swift */, + ); + name = KissMeIndex; + path = ../../KissMeIndex/Sources; + sourceTree = ""; + }; + 34F18FF32A4148950068C697 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 34F18FF42A4148950068C697 /* KissMe.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 34F18FE62A3FA2CA0068C697 /* KissMeIndex */ = { + isa = PBXNativeTarget; + buildConfigurationList = 34F18FEE2A3FA2CA0068C697 /* Build configuration list for PBXNativeTarget "KissMeIndex" */; + buildPhases = ( + 34F18FE32A3FA2CA0068C697 /* Sources */, + 34F18FE42A3FA2CA0068C697 /* Frameworks */, + 34F18FE52A3FA2CA0068C697 /* CopyFiles */, + 34F18FF72A4148950068C697 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KissMeIndex; + productName = KissMeIndex; + productReference = 34F18FE72A3FA2CA0068C697 /* KissMeIndex */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 34F18FDF2A3FA2CA0068C697 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1430; + LastUpgradeCheck = 1430; + TargetAttributes = { + 34F18FE62A3FA2CA0068C697 = { + CreatedOnToolsVersion = 14.3.1; + }; + }; + }; + buildConfigurationList = 34F18FE22A3FA2CA0068C697 /* Build configuration list for PBXProject "KissMeIndex" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 34F18FDE2A3FA2CA0068C697; + productRefGroup = 34F18FE82A3FA2CA0068C697 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 34F18FE62A3FA2CA0068C697 /* KissMeIndex */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 34F18FE32A3FA2CA0068C697 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 34F190012A4155D70068C697 /* KissIndex+0002.swift in Sources */, + 34F18FFD2A4155890068C697 /* KissIndex+0004.swift in Sources */, + 34F18FFF2A4155BF0068C697 /* KissIndex+0003.swift in Sources */, + 34F18FF92A41544E0068C697 /* KissIndex+0006.swift in Sources */, + 34F18FFB2A4155750068C697 /* KissIndex+0005.swift in Sources */, + 34F190032A4155F90068C697 /* KissIndex+0001.swift in Sources */, + 34F18FF22A41299D0068C697 /* KissIndex.swift in Sources */, + 34F18FEB2A3FA2CA0068C697 /* main.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 34F18FEC2A3FA2CA0068C697 /* 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; + }; + 34F18FED2A3FA2CA0068C697 /* 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; + }; + 34F18FEF2A3FA2CA0068C697 /* 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; + }; + 34F18FF02A3FA2CA0068C697 /* 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 */ + 34F18FE22A3FA2CA0068C697 /* Build configuration list for PBXProject "KissMeIndex" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 34F18FEC2A3FA2CA0068C697 /* Debug */, + 34F18FED2A3FA2CA0068C697 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 34F18FEE2A3FA2CA0068C697 /* Build configuration list for PBXNativeTarget "KissMeIndex" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 34F18FEF2A3FA2CA0068C697 /* Debug */, + 34F18FF02A3FA2CA0068C697 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 34F18FDF2A3FA2CA0068C697 /* Project object */; +} diff --git a/projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/projects/macos/KissMeIndex.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/projects/macos/KissMeIndex.xcodeproj/xcshareddata/xcschemes/KissMeIndex.xcscheme b/projects/macos/KissMeIndex.xcodeproj/xcshareddata/xcschemes/KissMeIndex.xcscheme new file mode 100644 index 0000000..2763239 --- /dev/null +++ b/projects/macos/KissMeIndex.xcodeproj/xcshareddata/xcschemes/KissMeIndex.xcscheme @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/macos/KissMeMatrix.xcodeproj/project.pbxproj b/projects/macos/KissMeMatrix.xcodeproj/project.pbxproj index 355245d..a718949 100644 --- a/projects/macos/KissMeMatrix.xcodeproj/project.pbxproj +++ b/projects/macos/KissMeMatrix.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 349843492A24DBF700E85B08 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349843482A24DBF700E85B08 /* main.swift */; }; 349843692A24DC7F00E85B08 /* KissMe.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 349843682A24DC7F00E85B08 /* KissMe.framework */; }; 3498436A2A24DC7F00E85B08 /* KissMe.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 349843682A24DC7F00E85B08 /* KissMe.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 34F190052A4156500068C697 /* KissMatrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34F190042A4156500068C697 /* KissMatrix.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -39,6 +40,7 @@ 349843452A24DBF700E85B08 /* KissMeMatrix */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = KissMeMatrix; sourceTree = BUILT_PRODUCTS_DIR; }; 349843482A24DBF700E85B08 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 349843682A24DC7F00E85B08 /* KissMe.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KissMe.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 34F190042A4156500068C697 /* KissMatrix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissMatrix.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -74,6 +76,7 @@ isa = PBXGroup; children = ( 349843482A24DBF700E85B08 /* main.swift */, + 34F190042A4156500068C697 /* KissMatrix.swift */, ); name = KissMeMatrix; path = ../../KissMeMatrix/Sources; @@ -146,6 +149,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 34F190052A4156500068C697 /* KissMatrix.swift in Sources */, 349843492A24DBF700E85B08 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0;