Skip to content

Commit

Permalink
feat: 앱 알림 설정 유무 등록 API 연결
Browse files Browse the repository at this point in the history
  • Loading branch information
JongHoooon committed Nov 4, 2024
1 parent 1f78df6 commit f6eb7ff
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class MoyaLoggerPlugin: PluginType {
log += "header: \(headers)\n"
}
if let body = httpRequest.httpBody, let bodyString = String(bytes: body, encoding: String.Encoding.utf8) {
log += "bodyString: \(bodyString)"
log += "bodyString: \(bodyString)\n"
}

log += "---------------------------------------------"
Expand Down Expand Up @@ -79,7 +79,7 @@ final class MoyaLoggerPlugin: PluginType {
if let data = error.response?.data {
log += "Data - \(data)\n"
} else {
log += "Data - empty"
log += "Data - empty\n"
}

log += "---------------------------------------------"
Expand Down
7 changes: 7 additions & 0 deletions Projects/Domain/User/Interface/Sources/API/UserAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum UserAPI {
case fetchAlertState
case updateAlertState(reqeustData: AlertStateRequestDTO)
case updateBlockContacts(blockContactRequestDTO: BlockContactRequestDTO)
case updatePushNotificationAllowStatus(requestDTO: UpdatePushNotificationAllowStatusRequestDTO)
}

extension UserAPI: BaseTargetType {
Expand All @@ -26,6 +27,8 @@ extension UserAPI: BaseTargetType {
return "api/v1/user/alimy"
case .updateBlockContacts:
return "api/v1/user/block/contact-list"
case .updatePushNotificationAllowStatus:
return "api/v1/user/native-setting"
}
}

Expand All @@ -37,6 +40,8 @@ extension UserAPI: BaseTargetType {
return .post
case .updateBlockContacts:
return .post
case .updatePushNotificationAllowStatus:
return .post
}
}

Expand All @@ -48,6 +53,8 @@ extension UserAPI: BaseTargetType {
return .requestJSONEncodable(requestData)
case let .updateBlockContacts(blockContactRequestDTO):
return .requestJSONEncodable(blockContactRequestDTO)
case let .updatePushNotificationAllowStatus(requestDTO):
return .requestJSONEncodable(requestDTO)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// UpdatePushNotificationAllowStatusRequestDTO.swift
// DomainUserInterface
//
// Created by JongHoon on 11/3/24.
//

import Foundation

public struct UpdatePushNotificationAllowStatusRequestDTO: Encodable {
public let alimyTurnedOn: Bool
public let deviceName: String
public let appVersion: String
public let deviceId: String

public init(
turnOn: Bool,
deviceName: String,
appVersion: String,
deviceId: String
) {
self.alimyTurnedOn = turnOn
self.deviceName = deviceName
self.appVersion = appVersion
self.deviceId = deviceId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// NeedUpdatePushNotificationAllowStatusRemotelyType.swift
// DomainUserInterface
//
// Created by JongHoon on 11/2/24.
//

import Foundation

public enum NeedUpdatePushNotificationAllowStatusRemotelyType {
case notNeed
case need(isAllow: Bool)
}
48 changes: 38 additions & 10 deletions Projects/Domain/User/Interface/Sources/UserClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ public struct UserClient {
private let _isLoggedIn: () -> Bool
private let _isAppDeleted: () -> Bool
private let _fetchFcmToken: () -> String?
var _remotelyUploadedPushNotificationAllowStatus: () -> Bool?
private let updateLoginState: (Bool) -> Void
private let updateDeleteState: (Bool) -> Void
private let updateFcmToken: (String) -> Void
private let updatePushNotificationAllowStatus: (Bool) -> Void
private let updatePushNotificationAllowStatusLocally: (Bool) -> Void
private let updatePushNotificationAllowStatusRemotely: (Bool) async throws -> Void
private let updateRemotelyUploadedPushNotificationAllowStatus: (Bool) -> Void
private let _isNeedUpdatePushNotificationRemotely: () async -> NeedUpdatePushNotificationAllowStatusRemotelyType
private let _fetchAlertState: () async throws -> [UserAlertState]
private let _fetchPushNotificationAllowStatus: () -> Bool
private let _fetchPushNotificationAllowStatusLocally: () -> Bool
private let updateAlertState: (UserAlertState) async throws -> Void
private let fetchContacts: () async throws -> [String]
private let updateBlockContacts: ([String]) async throws -> Void
Expand All @@ -32,25 +36,33 @@ public struct UserClient {
isLoggedIn: @escaping () -> Bool,
isAppDeleted: @escaping () -> Bool,
fetchFcmToken: @escaping () -> String?,
remotelyUploadedPushNotificationAllowStatus: @escaping () -> Bool?,
updateLoginState: @escaping (Bool) -> Void,
updateDeleteState: @escaping (Bool) -> Void,
updateFcmToken: @escaping (String) -> Void,
updatePushNotificationAllowStatus: @escaping (Bool) -> Void,
updatePushNotificationAllowStatusLocally: @escaping (Bool) -> Void,
updatePushNotificationAllowStatusRemotely: @escaping (Bool) async throws -> Void,
updateRemotelyUploadedPushNotificationAllowStatus: @escaping (Bool) -> Void,
isNeedUpdatePushNotificationRemotely: @escaping () async -> NeedUpdatePushNotificationAllowStatusRemotelyType,
fetchAlertState: @escaping () async throws -> [UserAlertState],
fetchPushNotificationAllowStatus: @escaping () -> Bool,
fetchPushNotificationAllowStatusLocally: @escaping () -> Bool,
updateAlertState: @escaping (UserAlertState) async throws -> Void,
fetchContacts: @escaping () async throws -> [String],
updateBlockContacts: @escaping ([String]) async throws -> Void
) {
self._isLoggedIn = isLoggedIn
self._isAppDeleted = isAppDeleted
self._fetchFcmToken = fetchFcmToken
self._remotelyUploadedPushNotificationAllowStatus = remotelyUploadedPushNotificationAllowStatus
self.updateLoginState = updateLoginState
self.updateDeleteState = updateDeleteState
self.updateFcmToken = updateFcmToken
self.updatePushNotificationAllowStatus = updatePushNotificationAllowStatus
self.updatePushNotificationAllowStatusLocally = updatePushNotificationAllowStatusLocally
self.updatePushNotificationAllowStatusRemotely = updatePushNotificationAllowStatusRemotely
self.updateRemotelyUploadedPushNotificationAllowStatus = updateRemotelyUploadedPushNotificationAllowStatus
self._isNeedUpdatePushNotificationRemotely = isNeedUpdatePushNotificationRemotely
self._fetchAlertState = fetchAlertState
self._fetchPushNotificationAllowStatus = fetchPushNotificationAllowStatus
self._fetchPushNotificationAllowStatusLocally = fetchPushNotificationAllowStatusLocally
self.updateAlertState = updateAlertState
self.fetchContacts = fetchContacts
self.updateBlockContacts = updateBlockContacts
Expand All @@ -68,6 +80,10 @@ public struct UserClient {
_fetchFcmToken()
}

public func remotelyUploadedPushNotificationAllowStatus() -> Bool? {
_remotelyUploadedPushNotificationAllowStatus()
}

public func updateLoginState(isLoggedIn: Bool) {
updateLoginState(isLoggedIn)
}
Expand All @@ -80,17 +96,29 @@ public struct UserClient {
updateFcmToken(fcmToken)
}

public func updatePushNotificationAllowStatus(isAllow: Bool) {
public func updatePushNotificationAllowStatusLocally(isAllow: Bool) {
pushNotificationAllowStatusSubject.send(isAllow)
updatePushNotificationAllowStatus(isAllow)
updatePushNotificationAllowStatusLocally(isAllow)
}

public func updatePushNotificationAllowStatusRemotely(isAllow: Bool) async throws {
try await updatePushNotificationAllowStatusRemotely(isAllow)
}

public func updateRemotelyUploadedPushNotificationAllowStatus(isAllow: Bool) {
updateRemotelyUploadedPushNotificationAllowStatus(isAllow)
}

public func isNeedUpdatePushNotificationRemotely() async -> NeedUpdatePushNotificationAllowStatusRemotelyType {
await _isNeedUpdatePushNotificationRemotely()
}

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

public func fetchPushNotificationAllowStatus() -> Bool {
_fetchPushNotificationAllowStatus()
public func fetchPushNotificationAllowStatusLocally() -> Bool {
_fetchPushNotificationAllowStatusLocally()
}

public func updateAlertState(alertState: UserAlertState) async throws {
Expand Down
97 changes: 95 additions & 2 deletions Projects/Domain/User/Sources/UserClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
// Created by 임현규 on 8/22/24.
//

import UIKit
import Foundation
import UserNotifications
import Contacts

import DomainUserInterface

import CoreKeyChainStore
import CoreNetwork
import CoreLoggerInterface
import SharedUtilInterface

import ComposableArchitecture
import Moya
Expand All @@ -22,6 +26,7 @@ extension UserClient: DependencyKey {
case deleteState
case fcmToken
case alertAllowState
case remotelyUploadedPushNotificationAllowStatus
}

static public var liveValue: UserClient = .live()
Expand All @@ -42,6 +47,16 @@ extension UserClient: DependencyKey {
return UserDefaults.standard.string(forKey: UserDefaultsKeys.fcmToken.rawValue)
},

remotelyUploadedPushNotificationAllowStatus: {
let status = UserDefaults.standard.object(forKey: UserDefaultsKeys.remotelyUploadedPushNotificationAllowStatus.rawValue)
guard let status = status as? Bool
else {
return nil
}

return status
},

updateLoginState: { isLoggedIn in
UserDefaults.standard.set(isLoggedIn, forKey: UserDefaultsKeys.loginState.rawValue)
},
Expand All @@ -54,16 +69,94 @@ extension UserClient: DependencyKey {
UserDefaults.standard.set(fcmToken, forKey: UserDefaultsKeys.fcmToken.rawValue)
},

updatePushNotificationAllowStatus: { isAllow in
updatePushNotificationAllowStatusLocally: { isAllow in
UserDefaults.standard.set(isAllow, forKey: UserDefaultsKeys.alertAllowState.rawValue)
},

updatePushNotificationAllowStatusRemotely: { isAllow in
@Dependency(\.userClient) var userClient

var deviceName: String? {
if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
return simulatorModelIdentifier
} else {
var systemInfo = utsname()
uname(&systemInfo)
let modelIdentifier = withUnsafePointer(to: &systemInfo.machine) {
$0.withMemoryRebound(to: CChar.self, capacity: 1) { ptr in
String(validatingUTF8: ptr)
}
}
return modelIdentifier
}
}

let requestDTO = await UpdatePushNotificationAllowStatusRequestDTO(
turnOn: isAllow,
deviceName: deviceName ?? "",
appVersion: (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "",
deviceId: UIDevice.current.identifierForVendor?.uuidString ?? ""
)

try await networkManager.reqeust(api: .apiType(UserAPI.updatePushNotificationAllowStatus(requestDTO: requestDTO)))
userClient.updateRemotelyUploadedPushNotificationAllowStatus(isAllow: isAllow)
},

updateRemotelyUploadedPushNotificationAllowStatus: { isAllow in
UserDefaults.standard.set(isAllow, forKey: UserDefaultsKeys.remotelyUploadedPushNotificationAllowStatus.rawValue)
},

isNeedUpdatePushNotificationRemotely: {
@Dependency(\.userClient) var userClient

guard userClient.isLoggedIn()
else {
return .notNeed
}

let remotelyUploadedStatus = userClient.remotelyUploadedPushNotificationAllowStatus()
let isAuthorized = await withCheckedContinuation { continuation in
UNUserNotificationCenter.current().getNotificationSettings { settings in
switch settings.authorizationStatus {
case .notDetermined:
continuation.resume(returning: false)

case .denied:
continuation.resume(returning: false)

case .authorized:
continuation.resume(returning: true)

case .provisional:
continuation.resume(returning: false)

case .ephemeral:
continuation.resume(returning: false)

@unknown default:
continuation.resume(returning: false)
Log.assertion(message: "not handled status")
}
}
}

let isNeedType: NeedUpdatePushNotificationAllowStatusRemotelyType = switch remotelyUploadedStatus {
case .none:
.need(isAllow: isAuthorized)

case let .some(localAllowStatus):
(localAllowStatus == isAuthorized) ? .notNeed : .need(isAllow: isAuthorized)
}

return isNeedType
},

fetchAlertState: {
let responseData = try await networkManager.reqeust(api: .apiType(UserAPI.fetchAlertState), dto: [AlertStateResponseDTO].self)
return responseData.map { $0.toDomain() }
},

fetchPushNotificationAllowStatus: {
fetchPushNotificationAllowStatusLocally: {
return UserDefaults.standard.bool(forKey: UserDefaultsKeys.alertAllowState.rawValue)
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ extension AlertSettingFeature {
}

func updatePushNotificationAllowStatus(state: inout State) {
let isAllow = userClient.fetchPushNotificationAllowStatus()
let isAllow = userClient.fetchPushNotificationAllowStatusLocally()
state.isAllowPushNotification = isAllow
}
}
Expand Down
2 changes: 1 addition & 1 deletion Projects/Feature/Sources/App/AppDelegateFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public struct AppDelegateFeature {
}

case let .pushNotificationAllowStatusDidChanged(isAllow):
userClient.updatePushNotificationAllowStatus(isAllow: isAllow)
userClient.updatePushNotificationAllowStatusLocally(isAllow: isAllow)
return .none

default:
Expand Down
18 changes: 17 additions & 1 deletion Projects/Feature/Sources/SplashView/SplashFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ComposableArchitecture
@Reducer
public struct SplashFeature {
@Dependency(\.authClient) private var authClient
@Dependency(\.userClient) private var userClient

@ObservableState
public struct State: Equatable {
Expand Down Expand Up @@ -59,7 +60,11 @@ public struct SplashFeature {
switch action {
case .onAppear:
return .run { send in
try await authClient.checkUpdateVersion()
async let checkUpdateVersionTask: () = try await authClient.checkUpdateVersion()
async let updatePushNotificationAllowStatusTask: () = try await updatePushNotificationAllowStatusRemotely()

let _ = try await (checkUpdateVersionTask, updatePushNotificationAllowStatusTask)

await send(.delegate(.initialCheckCompleted))
} catch: { error, send in
Log.error(error)
Expand Down Expand Up @@ -100,6 +105,17 @@ public struct SplashFeature {
case .alert, .delegate, .destination, .binding:
return .none
}

@Sendable func updatePushNotificationAllowStatusRemotely() async throws {
let isNeed = await userClient.isNeedUpdatePushNotificationRemotely()
switch isNeed {
case let .need(isAllow):
try await userClient.updatePushNotificationAllowStatusRemotely(isAllow: isAllow)

case .notNeed:
return
}
}
}
}

Expand Down
5 changes: 0 additions & 5 deletions Projects/Shared/Util/Interface/Sources/UtilInterface.swift

This file was deleted.

0 comments on commit f6eb7ff

Please sign in to comment.