Skip to content

Commit e294b2d

Browse files
authored
[Feature/#339] 앱 알림 설정 유무 등록 API 연결 #342
1 parent 1146d85 commit e294b2d

File tree

10 files changed

+201
-22
lines changed

10 files changed

+201
-22
lines changed

Projects/Core/Network/Sources/MoyaPulgins/MoyaLoggerPlugin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ final class MoyaLoggerPlugin: PluginType {
3333
log += "header: \(headers)\n"
3434
}
3535
if let body = httpRequest.httpBody, let bodyString = String(bytes: body, encoding: String.Encoding.utf8) {
36-
log += "bodyString: \(bodyString)"
36+
log += "bodyString: \(bodyString)\n"
3737
}
3838

3939
log += "---------------------------------------------"
@@ -79,7 +79,7 @@ final class MoyaLoggerPlugin: PluginType {
7979
if let data = error.response?.data {
8080
log += "Data - \(data)\n"
8181
} else {
82-
log += "Data - empty"
82+
log += "Data - empty\n"
8383
}
8484

8585
log += "---------------------------------------------"

Projects/Domain/User/Interface/Sources/API/UserAPI.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public enum UserAPI {
1515
case fetchAlertState
1616
case updateAlertState(reqeustData: AlertStateRequestDTO)
1717
case updateBlockContacts(blockContactRequestDTO: BlockContactRequestDTO)
18+
case updatePushNotificationAllowStatus(requestDTO: UpdatePushNotificationAllowStatusRequestDTO)
1819
}
1920

2021
extension UserAPI: BaseTargetType {
@@ -26,6 +27,8 @@ extension UserAPI: BaseTargetType {
2627
return "api/v1/user/alimy"
2728
case .updateBlockContacts:
2829
return "api/v1/user/block/contact-list"
30+
case .updatePushNotificationAllowStatus:
31+
return "api/v1/user/native-setting"
2932
}
3033
}
3134

@@ -37,6 +40,8 @@ extension UserAPI: BaseTargetType {
3740
return .post
3841
case .updateBlockContacts:
3942
return .post
43+
case .updatePushNotificationAllowStatus:
44+
return .post
4045
}
4146
}
4247

@@ -48,6 +53,8 @@ extension UserAPI: BaseTargetType {
4853
return .requestJSONEncodable(requestData)
4954
case let .updateBlockContacts(blockContactRequestDTO):
5055
return .requestJSONEncodable(blockContactRequestDTO)
56+
case let .updatePushNotificationAllowStatus(requestDTO):
57+
return .requestJSONEncodable(requestDTO)
5158
}
5259
}
5360
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// UpdatePushNotificationAllowStatusRequestDTO.swift
3+
// DomainUserInterface
4+
//
5+
// Created by JongHoon on 11/3/24.
6+
//
7+
8+
import Foundation
9+
10+
public struct UpdatePushNotificationAllowStatusRequestDTO: Encodable {
11+
public let alimyTurnedOn: Bool
12+
public let deviceName: String
13+
public let appVersion: String
14+
public let deviceId: String
15+
16+
public init(
17+
turnOn: Bool,
18+
deviceName: String,
19+
appVersion: String,
20+
deviceId: String
21+
) {
22+
self.alimyTurnedOn = turnOn
23+
self.deviceName = deviceName
24+
self.appVersion = appVersion
25+
self.deviceId = deviceId
26+
}
27+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// NeedUpdatePushNotificationAllowStatusRemotelyType.swift
3+
// DomainUserInterface
4+
//
5+
// Created by JongHoon on 11/2/24.
6+
//
7+
8+
import Foundation
9+
10+
public enum NeedUpdatePushNotificationAllowStatusRemotelyType {
11+
case notNeed
12+
case need(isAllow: Bool)
13+
}

Projects/Domain/User/Interface/Sources/UserClient.swift

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@ public struct UserClient {
1414
private let _isAppDeleted: () -> Bool
1515
private let _isCoachMarkViewed: () -> Bool
1616
private let _fetchFcmToken: () -> String?
17+
var _remotelyUploadedPushNotificationAllowStatus: () -> Bool?
1718
private let updateLoginState: (Bool) -> Void
1819
private let updateDeleteState: (Bool) -> Void
1920
private let updateCoachMarkState: (Bool) -> Void
2021
private let updateFcmToken: (String) -> Void
21-
private let updatePushNotificationAllowStatus: (Bool) -> Void
22+
private let updatePushNotificationAllowStatusLocally: (Bool) -> Void
23+
private let updatePushNotificationAllowStatusRemotely: (Bool) async throws -> Void
24+
private let updateRemotelyUploadedPushNotificationAllowStatus: (Bool) -> Void
25+
private let _isNeedUpdatePushNotificationRemotely: () async -> NeedUpdatePushNotificationAllowStatusRemotelyType
2226
private let _fetchAlertState: () async throws -> [UserAlertState]
23-
private let _fetchPushNotificationAllowStatus: () -> Bool
27+
private let _fetchPushNotificationAllowStatusLocally: () -> Bool
2428
private let updateAlertState: (UserAlertState) async throws -> Void
2529
private let fetchContacts: () async throws -> [String]
2630
private let updateBlockContacts: ([String]) async throws -> Void
@@ -35,13 +39,17 @@ public struct UserClient {
3539
isAppDeleted: @escaping () -> Bool,
3640
isCoachMarkViewed: @escaping () -> Bool,
3741
fetchFcmToken: @escaping () -> String?,
42+
remotelyUploadedPushNotificationAllowStatus: @escaping () -> Bool?,
3843
updateLoginState: @escaping (Bool) -> Void,
3944
updateDeleteState: @escaping (Bool) -> Void,
4045
updateFcmToken: @escaping (String) -> Void,
41-
updatePushNotificationAllowStatus: @escaping (Bool) -> Void,
46+
updatePushNotificationAllowStatusLocally: @escaping (Bool) -> Void,
47+
updatePushNotificationAllowStatusRemotely: @escaping (Bool) async throws -> Void,
48+
updateRemotelyUploadedPushNotificationAllowStatus: @escaping (Bool) -> Void,
49+
isNeedUpdatePushNotificationRemotely: @escaping () async -> NeedUpdatePushNotificationAllowStatusRemotelyType,
4250
updateCoachMarkState: @escaping (Bool) -> Void,
4351
fetchAlertState: @escaping () async throws -> [UserAlertState],
44-
fetchPushNotificationAllowStatus: @escaping () -> Bool,
52+
fetchPushNotificationAllowStatusLocally: @escaping () -> Bool,
4553
updateAlertState: @escaping (UserAlertState) async throws -> Void,
4654
fetchContacts: @escaping () async throws -> [String],
4755
updateBlockContacts: @escaping ([String]) async throws -> Void
@@ -50,13 +58,17 @@ public struct UserClient {
5058
self._isAppDeleted = isAppDeleted
5159
self._isCoachMarkViewed = isCoachMarkViewed
5260
self._fetchFcmToken = fetchFcmToken
61+
self._remotelyUploadedPushNotificationAllowStatus = remotelyUploadedPushNotificationAllowStatus
5362
self.updateLoginState = updateLoginState
5463
self.updateDeleteState = updateDeleteState
5564
self.updateFcmToken = updateFcmToken
56-
self.updatePushNotificationAllowStatus = updatePushNotificationAllowStatus
65+
self.updatePushNotificationAllowStatusLocally = updatePushNotificationAllowStatusLocally
66+
self.updatePushNotificationAllowStatusRemotely = updatePushNotificationAllowStatusRemotely
67+
self.updateRemotelyUploadedPushNotificationAllowStatus = updateRemotelyUploadedPushNotificationAllowStatus
68+
self._isNeedUpdatePushNotificationRemotely = isNeedUpdatePushNotificationRemotely
5769
self.updateCoachMarkState = updateCoachMarkState
5870
self._fetchAlertState = fetchAlertState
59-
self._fetchPushNotificationAllowStatus = fetchPushNotificationAllowStatus
71+
self._fetchPushNotificationAllowStatusLocally = fetchPushNotificationAllowStatusLocally
6072
self.updateAlertState = updateAlertState
6173
self.fetchContacts = fetchContacts
6274
self.updateBlockContacts = updateBlockContacts
@@ -78,6 +90,10 @@ public struct UserClient {
7890
_fetchFcmToken()
7991
}
8092

93+
public func remotelyUploadedPushNotificationAllowStatus() -> Bool? {
94+
_remotelyUploadedPushNotificationAllowStatus()
95+
}
96+
8197
public func updateLoginState(isLoggedIn: Bool) {
8298
updateLoginState(isLoggedIn)
8399
}
@@ -93,17 +109,29 @@ public struct UserClient {
93109
updateFcmToken(fcmToken)
94110
}
95111

96-
public func updatePushNotificationAllowStatus(isAllow: Bool) {
112+
public func updatePushNotificationAllowStatusLocally(isAllow: Bool) {
97113
pushNotificationAllowStatusSubject.send(isAllow)
98-
updatePushNotificationAllowStatus(isAllow)
114+
updatePushNotificationAllowStatusLocally(isAllow)
115+
}
116+
117+
public func updatePushNotificationAllowStatusRemotely(isAllow: Bool) async throws {
118+
try await updatePushNotificationAllowStatusRemotely(isAllow)
119+
}
120+
121+
public func updateRemotelyUploadedPushNotificationAllowStatus(isAllow: Bool) {
122+
updateRemotelyUploadedPushNotificationAllowStatus(isAllow)
123+
}
124+
125+
public func isNeedUpdatePushNotificationRemotely() async -> NeedUpdatePushNotificationAllowStatusRemotelyType {
126+
await _isNeedUpdatePushNotificationRemotely()
99127
}
100128

101129
public func fetchAlertState() async throws -> [UserAlertState] {
102130
try await _fetchAlertState()
103131
}
104132

105-
public func fetchPushNotificationAllowStatus() -> Bool {
106-
_fetchPushNotificationAllowStatus()
133+
public func fetchPushNotificationAllowStatusLocally() -> Bool {
134+
_fetchPushNotificationAllowStatusLocally()
107135
}
108136

109137
public func updateAlertState(alertState: UserAlertState) async throws {

Projects/Domain/User/Sources/UserClient.swift

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@
55
// Created by 임현규 on 8/22/24.
66
//
77

8+
import UIKit
89
import Foundation
10+
import UserNotifications
911
import Contacts
1012

1113
import DomainUserInterface
1214

1315
import CoreKeyChainStore
1416
import CoreNetwork
17+
import CoreLoggerInterface
18+
import SharedUtilInterface
1519

1620
import ComposableArchitecture
1721
import Moya
@@ -22,6 +26,7 @@ extension UserClient: DependencyKey {
2226
case deleteState
2327
case fcmToken
2428
case alertAllowState
29+
case remotelyUploadedPushNotificationAllowStatus
2530
case coachMarkState
2631
}
2732

@@ -47,6 +52,16 @@ extension UserClient: DependencyKey {
4752
return UserDefaults.standard.string(forKey: UserDefaultsKeys.fcmToken.rawValue)
4853
},
4954

55+
remotelyUploadedPushNotificationAllowStatus: {
56+
let status = UserDefaults.standard.object(forKey: UserDefaultsKeys.remotelyUploadedPushNotificationAllowStatus.rawValue)
57+
guard let status = status as? Bool
58+
else {
59+
return nil
60+
}
61+
62+
return status
63+
},
64+
5065
updateLoginState: { isLoggedIn in
5166
UserDefaults.standard.set(isLoggedIn, forKey: UserDefaultsKeys.loginState.rawValue)
5267
},
@@ -59,10 +74,88 @@ extension UserClient: DependencyKey {
5974
UserDefaults.standard.set(fcmToken, forKey: UserDefaultsKeys.fcmToken.rawValue)
6075
},
6176

62-
updatePushNotificationAllowStatus: { isAllow in
77+
updatePushNotificationAllowStatusLocally: { isAllow in
6378
UserDefaults.standard.set(isAllow, forKey: UserDefaultsKeys.alertAllowState.rawValue)
6479
},
6580

81+
updatePushNotificationAllowStatusRemotely: { isAllow in
82+
@Dependency(\.userClient) var userClient
83+
84+
var deviceName: String? {
85+
if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
86+
return simulatorModelIdentifier
87+
} else {
88+
var systemInfo = utsname()
89+
uname(&systemInfo)
90+
let modelIdentifier = withUnsafePointer(to: &systemInfo.machine) {
91+
$0.withMemoryRebound(to: CChar.self, capacity: 1) { ptr in
92+
String(validatingUTF8: ptr)
93+
}
94+
}
95+
return modelIdentifier
96+
}
97+
}
98+
99+
let requestDTO = await UpdatePushNotificationAllowStatusRequestDTO(
100+
turnOn: isAllow,
101+
deviceName: deviceName ?? "",
102+
appVersion: (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "",
103+
deviceId: UIDevice.current.identifierForVendor?.uuidString ?? ""
104+
)
105+
106+
try await networkManager.reqeust(api: .apiType(UserAPI.updatePushNotificationAllowStatus(requestDTO: requestDTO)))
107+
userClient.updateRemotelyUploadedPushNotificationAllowStatus(isAllow: isAllow)
108+
},
109+
110+
updateRemotelyUploadedPushNotificationAllowStatus: { isAllow in
111+
UserDefaults.standard.set(isAllow, forKey: UserDefaultsKeys.remotelyUploadedPushNotificationAllowStatus.rawValue)
112+
},
113+
114+
isNeedUpdatePushNotificationRemotely: {
115+
@Dependency(\.userClient) var userClient
116+
117+
guard userClient.isLoggedIn()
118+
else {
119+
return .notNeed
120+
}
121+
122+
let remotelyUploadedStatus = userClient.remotelyUploadedPushNotificationAllowStatus()
123+
let isAuthorized = await withCheckedContinuation { continuation in
124+
UNUserNotificationCenter.current().getNotificationSettings { settings in
125+
switch settings.authorizationStatus {
126+
case .notDetermined:
127+
continuation.resume(returning: false)
128+
129+
case .denied:
130+
continuation.resume(returning: false)
131+
132+
case .authorized:
133+
continuation.resume(returning: true)
134+
135+
case .provisional:
136+
continuation.resume(returning: false)
137+
138+
case .ephemeral:
139+
continuation.resume(returning: false)
140+
141+
@unknown default:
142+
continuation.resume(returning: false)
143+
Log.assertion(message: "not handled status")
144+
}
145+
}
146+
}
147+
148+
let isNeedType: NeedUpdatePushNotificationAllowStatusRemotelyType = switch remotelyUploadedStatus {
149+
case .none:
150+
.need(isAllow: isAuthorized)
151+
152+
case let .some(localAllowStatus):
153+
(localAllowStatus == isAuthorized) ? .notNeed : .need(isAllow: isAuthorized)
154+
}
155+
156+
return isNeedType
157+
},
158+
66159
updateCoachMarkState: { isViewed in
67160
UserDefaults.standard.set(isViewed, forKey: UserDefaultsKeys.coachMarkState.rawValue)
68161
},
@@ -72,7 +165,7 @@ extension UserClient: DependencyKey {
72165
return responseData.map { $0.toDomain() }
73166
},
74167

75-
fetchPushNotificationAllowStatus: {
168+
fetchPushNotificationAllowStatusLocally: {
76169
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.alertAllowState.rawValue)
77170
},
78171

Projects/Feature/MyPage/Interface/Sources/AlertSetting/AlertSettingFeature.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ extension AlertSettingFeature {
153153
}
154154

155155
func updatePushNotificationAllowStatus(state: inout State) {
156-
let isAllow = userClient.fetchPushNotificationAllowStatus()
156+
let isAllow = userClient.fetchPushNotificationAllowStatusLocally()
157157
state.isAllowPushNotification = isAllow
158158
}
159159
}

Projects/Feature/Sources/App/AppDelegateFeature.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public struct AppDelegateFeature {
5757
}
5858

5959
case let .pushNotificationAllowStatusDidChanged(isAllow):
60-
userClient.updatePushNotificationAllowStatus(isAllow: isAllow)
60+
userClient.updatePushNotificationAllowStatusLocally(isAllow: isAllow)
6161
return .none
6262

6363
default:

Projects/Feature/Sources/SplashView/SplashFeature.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import ComposableArchitecture
1818
@Reducer
1919
public struct SplashFeature {
2020
@Dependency(\.authClient) private var authClient
21+
@Dependency(\.userClient) private var userClient
2122

2223
@ObservableState
2324
public struct State: Equatable {
@@ -59,7 +60,11 @@ public struct SplashFeature {
5960
switch action {
6061
case .onAppear:
6162
return .run { send in
62-
try await authClient.checkUpdateVersion()
63+
async let checkUpdateVersionTask: () = try await authClient.checkUpdateVersion()
64+
async let updatePushNotificationAllowStatusTask: () = try await updatePushNotificationAllowStatusRemotely()
65+
66+
let _ = try await (checkUpdateVersionTask, updatePushNotificationAllowStatusTask)
67+
6368
await send(.delegate(.initialCheckCompleted))
6469
} catch: { error, send in
6570
Log.error(error)
@@ -100,6 +105,17 @@ public struct SplashFeature {
100105
case .alert, .delegate, .destination, .binding:
101106
return .none
102107
}
108+
109+
@Sendable func updatePushNotificationAllowStatusRemotely() async throws {
110+
let isNeed = await userClient.isNeedUpdatePushNotificationRemotely()
111+
switch isNeed {
112+
case let .need(isAllow):
113+
try await userClient.updatePushNotificationAllowStatusRemotely(isAllow: isAllow)
114+
115+
case .notNeed:
116+
return
117+
}
118+
}
103119
}
104120
}
105121

0 commit comments

Comments
 (0)