處理申購 VC 的資料,是由 StockSubscriptionModel 處理的。
在前面,我們在 AppDelegate 的 didFinishLaunch 下載了台股上市收盤資料,並存在 UserDefaults 內,所以這個 model 在 init 的時候,理論上就可以拿到上市收盤資料了,如果沒有,那你可以在這個時機點再發動一次下載。
註: 這邊有優化空間,但更細膩的操作,就留待讀者在實務上各自發揮
1-選擇存放資料結構
在 StockSubscriptionModel 中,可以放日收的結構有 Array, Dictionary。這邊我選擇了 Dictionary,並把股票代號當成 key 值,value 為日收的 data model - StockDayTick。
2-實作
先在 StockSubscriptionModel 加上 key vs. value 的 property
private var twMarketDayTicks: [String: StockDayTick] = [:]
再加上日收的 manager,而且有要從 String 轉成 NSNumber, Double 的地方,所以也要一個 NumberFormatter
private lazy var dayPriceManager: StockDayPriceManager = {
return StockDayPriceManager()
}()
private lazy var numberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
在 Model 一開始,就把上市日收從 UserDefaults 中拿出,並存起來
init() {
setupDayTicks()
}
// MARK: - private func
// 將 UserDefaults 中的 day ticks 拿出,並把 stockCode 當 key 值
private func setupDayTicks() {
let dayTicks = dayPriceManager.getTwAllStockDayPriceFromUserDefaults()
let keys = dayTicks.map { tick in
return tick.stockCode
}
for (index, tick) in dayTicks.enumerated() {
let key = keys[index]
self.twMarketDayTicks[key] = tick
}
}
然後做出讓 VC 呼叫的接口,當 VC 把 subscription 輸入,就可以得到價差的 String
/// 命名可以用 rename 改得更符合變數意義,請自行變更
func getPricePercentDifferenctString(subscription: StockSubscriptionInfo) -> String? {
let code = subscription.stockCode
let subscriptionPrice = subscription.price
if let tick = getTick(from: code),
let subscriptionPriceDouble = numberFormatter.number(from: subscriptionPrice)?.doubleValue,
let closePrice = tick.close {
let percentage = (closePrice - subscriptionPriceDouble) / subscriptionPriceDouble
let string = String(format: "%.1f", percentage * 100)
return string
}
return nil
}
整個 Model 的 Code
//
// StockSubscriptionModel.swift
// ITIronMan
//
// Created by Marvin on 2021/9/4.
//
import Foundation
protocol StockSubscriptionModelDelegate: AnyObject {
func didRecieveList(_ subscriptionList: [StockSubscriptionInfo], error: Error?)
}
/// 股票申購 VC 所需的 Model
class StockSubscriptionModel {
weak var delegate: StockSubscriptionModelDelegate?
var subscriptionList: [StockSubscriptionInfo] = []
private var twMarketDayTicks: [String: StockDayTick] = [:]
private lazy var subscriptionManager: StockSubscriptionManager = {
return StockSubscriptionManager()
}()
private lazy var dayPriceManager: StockDayPriceManager = {
return StockDayPriceManager()
}()
private lazy var numberFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
var count: Int {
return subscriptionList.count
}
init() {
setupDayTicks()
}
// MARK: - private func
// 將 UserDefaults 中的 day ticks 拿出,並把 stockCode 當 key 值
private func setupDayTicks() {
let dayTicks = dayPriceManager.getTwAllStockDayPriceFromUserDefaults()
let keys = dayTicks.map { tick in
return tick.stockCode
}
for (index, tick) in dayTicks.enumerated() {
let key = keys[index]
self.twMarketDayTicks[key] = tick
}
}
private func filterNotAvailable(_ subscriptionList: [StockSubscriptionInfo]) -> [StockSubscriptionInfo] {
let list = subscriptionList.filter { info in
let code = info.stockCode
let firstCharacter = code.first ?? "0"
return firstCharacter != "A"
}
return list
}
private func getQueryYear() -> Int {
let dateUtility = DateUtility()
return dateUtility.getIntFromDate(component: .year)
}
private func getTick(from stockCode: String) -> StockDayTick? {
if let tick = twMarketDayTicks[stockCode] {
return tick
}
return nil
}
// MARK: - public func
func getSubscriptionInfo(at indexPath: IndexPath) -> StockSubscriptionInfo? {
let index = indexPath.row
if subscriptionList.indices.contains(index) {
return subscriptionList[index]
}
return nil
}
func requestStockSubscription() {
let year = getQueryYear()
subscriptionManager.requestStockSubscriptionInfo(year: year) { [weak self] subscriptionList, error in
// 需要去掉中央債的資料
self?.subscriptionList = self?.filterNotAvailable(subscriptionList) ?? []
self?.delegate?.didRecieveList(subscriptionList, error: error)
}
}
/// 命名可以用 rename 改得更符合變數意義,請自行變更
func getPricePercentDifferenctString(subscription: StockSubscriptionInfo) -> String? {
let code = subscription.stockCode
let subscriptionPrice = subscription.price
if let tick = getTick(from: code),
let subscriptionPriceDouble = numberFormatter.number(from: subscriptionPrice)?.doubleValue,
let closePrice = tick.close {
let percentage = (closePrice - subscriptionPriceDouble) / subscriptionPriceDouble
let string = String(format: "%.1f", percentage * 100)
return string
}
return nil
}
}
extension StockSubscriptionModel {
enum SubscriptionState {
case beforeSubscription
case duringSubscription
case finishedSubscription
case notDefined
}
}
extension StockSubscriptionModel {
func getSubscriptionState(info: StockSubscriptionInfo) -> SubscriptionState {
let currentTime = Date().timeIntervalSince1970
if let startTime = info.subscriptionStart?.timeIntervalSince1970,
let endTime = info.subscriptionEnd?.timeIntervalSince1970 {
if currentTime < startTime {
return .beforeSubscription
} else if currentTime > endTime {
return .finishedSubscription
} else {
return .duringSubscription
}
}
return .notDefined
}
}
台股申購日曆
IT鐵人賽Demo App
下方是這次 D1 ~ D12 的完成品,可以下載來試
App Store - 台股申購日曆