Skip to content

Commit aaf0db7

Browse files
authored
Merge pull request #234 from DSM-PICK/feature/(#232)-sse
🔗 :: (#232) WebSocket -> SSE로 변경
2 parents ebfdd28 + 360c18a commit aaf0db7

16 files changed

Lines changed: 129 additions & 89 deletions

File tree

Plugins/DependencyPlugin/ProjectDescriptionHelpers/Dependency+SPM.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ public extension TargetDependency.PackageType {
1717
static let FSCalendar = TargetDependency.external(name: "FSCalendar")
1818
static let RxDataSources = TargetDependency.external(name: "RxDataSources")
1919
static let ReactorKit = TargetDependency.external(name: "ReactorKit")
20-
static let Starscream = TargetDependency.external(name: "Starscream")
2120
static let Realm = TargetDependency.external(name: "Realm")
2221
static let RealmSwift = TargetDependency.external(name: "RealmSwift")
2322
static let GoogleSignIn = TargetDependency.external(name: "GoogleSignIn")

Projects/App/Support/Info.plist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@
4343
<string></string>
4444
<key>NSUserTrackingUsageDescription</key>
4545
<string>더 나은 경험 제공 및 앱 안정성 향상을 위해 사용자 활동 및 오류 데이터 수집에 동의해주세요.</string>
46-
<key>SOCKET_BASE_URL</key>
47-
<string>$(SOCKET_BASE_URL)</string>
4846
<key>UIApplicationSceneManifest</key>
4947
<dict>
5048
<key>UIApplicationSupportsMultipleScenes</key>

Projects/App/WatchApp/Support/Info.plist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
<string>1</string>
2525
<key>LSRequiresIPhoneOS</key>
2626
<true/>
27-
<key>SOCKET_BASE_URL</key>
28-
<string>$(SOCKET_BASE_URL)</string>
2927
<key>UISupportedInterfaceOrientations</key>
3028
<array>
3129
<string>UIInterfaceOrientationPortrait</string>

Projects/Core/Sources/Util/Enum/OutingType.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public enum OutingType: String, Codable {
44
case application = "APPLICATION"
55
case earlyReturn = "EARLY_RETURN"
66
case classroom = "CLASSROOM"
7+
case none = "NONE"
78
}
89

910
extension OutingType {
@@ -15,6 +16,8 @@ extension OutingType {
1516
return "조기귀가 수락 대기 중입니다"
1617
case .classroom:
1718
return "교실 이동 수락 대기 중입니다"
19+
case .none:
20+
return ""
1821
}
1922
}
2023

@@ -24,6 +27,8 @@ extension OutingType {
2427
return "외출증 보기"
2528
case .classroom:
2629
return "돌아가기"
30+
case .none:
31+
return ""
2732
}
2833
}
2934
}

Projects/Core/Sources/Util/URL/URLUtil.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ public extension URLUtil {
88
forInfoDictionaryKey: "API_BASE_URL"
99
) as? String ?? "") ?? URL(string: "")!
1010

11-
static let socketBaseURL: String = Bundle.main.object(
12-
forInfoDictionaryKey: "SOCKET_BASE_URL"
13-
) as? String ?? ""
14-
1511
static let neisBaseURL: URL = URL(string: "https://open.neis.go.kr/hub")!
1612

1713
static let neisAPIKey: String = Bundle.main.object(
@@ -25,5 +21,4 @@ public extension URLUtil {
2521
static let neisSdSchulCode: String = Bundle.main.object(
2622
forInfoDictionaryKey: "NEIS_SD_SCHUL_CODE"
2723
) as? String ?? ""
28-
2924
}

Projects/Data/Sources/DTO/Home/HomeApplyStatusDTO.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ public struct HomeApplyStatusDTO: Decodable {
99
let startTime: String?
1010
let endTime: String?
1111
let classroom: String?
12-
let type: OutingType.RawValue?
12+
let type: OutingType.RawValue
1313

1414
enum CodingKeys: String, CodingKey {
15-
case userName, classroom, type
16-
case userID = "userId"
15+
case userID = "user_id"
16+
case userName = "user_name"
1717
case startTime = "start"
1818
case endTime = "end"
19+
case classroom
20+
case type
1921
}
2022

2123
}

Projects/Data/Sources/DataSource/HomeDataSource.swift

Lines changed: 98 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import Foundation
33
import RxSwift
44
import RxCocoa
55

6-
import Starscream
7-
86
import Core
97
import Domain
108
import 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
}

Projects/Domain/Sources/Entity/Home/HomeApplyStatusEntity.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ public struct HomeApplyStatusEntity {
88
public let startTime: String?
99
public let endTime: String?
1010
public let classroom: String?
11-
public let type: OutingType.RawValue?
11+
public let type: OutingType.RawValue
1212

1313
public init(
1414
userID: UUID?,
1515
userName: String?,
1616
startTime: String?,
1717
endTime: String?,
1818
classroom: String?,
19-
type: OutingType.RawValue?
19+
type: OutingType.RawValue
2020
) {
2121
self.userID = userID
2222
self.userName = userName

Projects/Domain/Sources/Parameter/ClassRoomMove/ClassRoomMoveRequestParams.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
import Foundation
22

33
public struct ClassroomMoveRequestParams: Encodable {
4+
public let move: String
45
public let floor: Int
56
public let classroomName: String
67
public let startPeriod: Int
78
public let endPeriod: Int
89

910
public init(
11+
move: String,
1012
floor: Int,
1113
classroomName: String,
1214
startPeriod: Int,
1315
endPeriod: Int
1416
) {
17+
self.move = move
1518
self.floor = floor
1619
self.classroomName = classroomName
1720
self.startPeriod = startPeriod
1821
self.endPeriod = endPeriod
1922
}
2023

2124
enum CodingKeys: String, CodingKey {
25+
case move
2226
case floor
2327
case classroomName = "classroom_name"
2428
case startPeriod = "start"

Projects/Modules/ThirdPartyLib/Project.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ let project = Project.makeModule(
2222
.PackageType.FSCalendar,
2323
.PackageType.RxDataSources,
2424
.PackageType.ReactorKit,
25-
.PackageType.Starscream,
2625
.PackageType.Realm,
2726
.PackageType.RealmSwift,
2827
.PackageType.GoogleSignIn,

0 commit comments

Comments
 (0)