@@ -3,8 +3,6 @@ import Foundation
33import RxSwift
44import RxCocoa
55
6- import Starscream
7-
86import Core
97import Domain
108import AppNetwork
@@ -13,25 +11,46 @@ protocol HomeDataSource {
1311 func fetchApplyStatus( ) -> Observable < HomeApplyStatusEntity >
1412}
1513
16- class HomeDataSourceImpl : WebSocketDelegate , HomeDataSource {
14+ class HomeDataSourceImpl : NSObject , HomeDataSource {
1715 private let keychain : Keychain
18- private var socket : WebSocket ?
16+ private var task : URLSessionDataTask ?
17+ private var buffer = " "
18+ private var isConnecting = false
1919
2020 private var applyStatusRelay = PublishRelay < HomeApplyStatusEntity > ( )
2121
2222 init ( keychain: Keychain ) {
2323 self . keychain = keychain
24- connectSocket ( )
24+ super. init ( )
25+ connectSSE ( )
2526 }
2627
27- func connectSocket( ) {
28- let url = URL ( string: " \( URLUtil . socketBaseURL) /main " )
29- var request = URLRequest ( url: url!)
30- request. timeoutInterval = 5
31- request. setValue ( " Bearer \( keychain. load ( type: . accessToken) ) " , forHTTPHeaderField: " Authorization " )
32- socket = WebSocket ( request: request)
33- socket? . delegate = self
34- socket? . connect ( )
28+ func connectSSE( ) {
29+ guard !isConnecting else { return }
30+ isConnecting = true
31+
32+ let url = URL ( string: " \( URLUtil . baseURL) /event " ) !
33+ var request = URLRequest ( url: url)
34+
35+ request. httpMethod = " GET "
36+ request. timeoutInterval = . infinity
37+ request. setValue ( " text/event-stream " , forHTTPHeaderField: " Accept " )
38+ request. setValue (
39+ " Bearer \( keychain. load ( type: . accessToken) ) " ,
40+ forHTTPHeaderField: " Authorization "
41+ )
42+
43+ let configuration = URLSessionConfiguration . default
44+ configuration. timeoutIntervalForRequest = . infinity
45+
46+ let session = URLSession (
47+ configuration: configuration,
48+ delegate: self ,
49+ delegateQueue: nil
50+ )
51+
52+ task = session. dataTask ( with: request)
53+ task? . resume ( )
3554 }
3655
3756 func fetchApplyStatus( ) -> Observable < HomeApplyStatusEntity > {
@@ -40,42 +59,76 @@ class HomeDataSourceImpl: WebSocketDelegate, HomeDataSource {
4059
4160}
4261
43- extension HomeDataSourceImpl {
44- func didReceive(
45- event: Starscream . WebSocketEvent ,
46- client: any Starscream . WebSocketClient
62+ extension HomeDataSourceImpl : URLSessionDataDelegate {
63+
64+ func urlSession(
65+ _ session: URLSession ,
66+ dataTask: URLSessionDataTask ,
67+ didReceive response: URLResponse ,
68+ completionHandler: @escaping ( URLSession . ResponseDisposition ) -> Void
4769 ) {
48- switch event {
49- case . connected ( let headers ) :
50- socket ? . write ( string : " " )
51- print ( " websocket is connected: \( headers ) " )
70+ print ( " SSE is connected: \( response ) " )
71+ isConnecting = false
72+ completionHandler ( . allow )
73+ }
5274
53- case . disconnected( let reason, let code) :
54- print ( " websocket is disconnected: \( reason) with code: \( code) " )
75+ func urlSession(
76+ _ session: URLSession ,
77+ dataTask: URLSessionDataTask ,
78+ didReceive data: Data
79+ ) {
80+ guard let chunk = String ( data: data, encoding: . utf8) else { return }
81+ buffer += chunk
5582
56- case . text( let text) :
57- let homeApplyStatus = try ? JSONDecoder ( ) . decode (
58- HomeApplyStatusDTO . self,
59- from: text. data ( using: . utf8) ?? Data ( )
60- )
61-
62- self . applyStatusRelay. accept (
63- homeApplyStatus? . toDomain ( ) ?? . init(
64- userID: nil ,
65- userName: nil ,
66- startTime: nil ,
67- endTime: nil ,
68- classroom: nil ,
69- type: nil
70- )
71- )
72-
73- case . error( let error) :
74- print ( " websocket is error = \( error!) " )
75-
76- default :
77- break
83+ while let range = buffer. range ( of: " \n \n " ) {
84+ let rawEvent = String ( buffer [ ..< range. lowerBound] )
85+ buffer. removeSubrange ( ..< range. upperBound)
86+ handle ( event: rawEvent)
87+ }
88+ }
89+
90+ func urlSession(
91+ _ session: URLSession ,
92+ task: URLSessionTask ,
93+ didCompleteWithError error: Error ?
94+ ) {
95+ isConnecting = false
96+
97+ if let error = error {
98+ print ( " SSE is error = \( error) " )
99+ } else {
100+ print ( " SSE is disconnected " )
101+ }
102+
103+ print ( " Reconnecting SSE " )
104+ DispatchQueue . main. asyncAfter ( deadline: . now( ) ) { [ weak self] in
105+ self ? . buffer = " "
106+ self ? . connectSSE ( )
78107 }
79108 }
109+ }
80110
111+ private extension HomeDataSourceImpl {
112+ func handle( event raw: String ) {
113+ let dataLines = raw
114+ . split ( separator: " \n " )
115+ . filter { $0. hasPrefix ( " data: " ) }
116+ . map {
117+ $0. replacingOccurrences ( of: " data: " , with: " " )
118+ . trimmingCharacters ( in: . whitespaces)
119+ }
120+
121+ let jsonString = dataLines. joined ( separator: " \n " )
122+ print ( " SSE received text: \( jsonString) " )
123+
124+ guard let data = jsonString. data ( using: . utf8) ,
125+ let dto = try ? JSONDecoder ( ) . decode (
126+ HomeApplyStatusDTO . self,
127+ from: data
128+ ) else {
129+ return
130+ }
131+
132+ applyStatusRelay. accept ( dto. toDomain ( ) )
133+ }
81134}
0 commit comments