美股記帳 App / Part 4. Websocket 抓stock real time price

Julia
彼得潘的 Swift iOS / Flutter App 開發教室
6 min readMar 21, 2022

Websocket

  • 只要 shakeHand 成功過後,兩者就可以,兩者就可以持久性的連接,並且能雙向傳遞資料,不用一直 get post
  • Finnhub 提供了用 websocket 連線的 Last Price Updates,比在 watchList 裡取得的股價資訊更即時,但提供的資訊更即時,但提供的資訊比較少,因此決定將即時的價格放在各個股票的頁面

建立 WebSocket

  1. 遵從 URLSessionWebSocketDelegate
  2. 建立取得資料的 struct,等一下用 JSONEncoder 得到資料
struct websocketStockInfo : Codable{
var type : String
var data : [priceData]
}
struct priceData : Codable{
var s : String // symbol
var p : Double // price
}
  • func setSession() :開啟連線
  • func ping() :確保連線正常,並回傳一個pong
  • func send() :送字串訊息過去,讓他知道你要什麼
  • func receive():收到 result type 的訊息,在 string的結過裡解 json 並把得到的價格 update 到 label 裡
  • func close : 中斷連線
  • func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) :當 websocket 連線成功後 依序啟動 ping send receive function

判斷股票市場開市了沒

使用 websocket 所得到的價格資訊,只有在美股開市的時候才有,所以在不是開市的時間,我會用 https api (watch List 裡的方法)取的價格資訊

  • 美股開市時間 :紐約時間週一到週五的早上九點半到下午四點
  • 記得 now variable 裡 取的時間也要是紐約時間
  • 用 NSCalandar ().isDateInWeekend 判斷是不是週末
let newYorkTimeZone = TimeZone(identifier: "America/New_York")!let now = Date().addingTimeInterval(TimeInterval.init(newYorkTimeZone.secondsFromGMT()))let nineAM = now.dateAt(hours: 9, minutes: 30).addingTimeInterval(TimeInterval.init(newYorkTimeZone.secondsFromGMT()))let fourPM = now.dateAt(hours: 16, minutes: 0).addingTimeInterval(TimeInterval.init(newYorkTimeZone.secondsFromGMT()))  

if !NSCalendar(identifier: .gregorian)!.isDateInWeekend(now) &&
now >= nineAM &&
now <= fourPM{
print("stockMarketOpen")
setSession()
}else{
print("stockMarketClose")

if let stockSymbol = stockSymbol {
fetchStockPrice(stockSymbol: stockSymbol)
}
}
  • function to get 9:00 and 16:00
extension Date{func dateAt(hours: Int, minutes: Int) -> Date{

let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!
calendar.timeZone = TimeZone(identifier: "America/New_York")!

var dateComponents = calendar.components([NSCalendar.Unit.year,
NSCalendar.Unit.month,
NSCalendar.Unit.day], from: self)
dateComponents.hour = hours
dateComponents.minute = minutes

let newDate = calendar.date(from:dateComponents)!
return newDate
}
}

Github:

Link:

--

--