diff --git a/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate b/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate index d006572..133d6a6 100644 Binary files a/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate and b/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/AcaMate/0. Setup/AppDelegate.swift b/AcaMate/0. Setup/AppDelegate.swift index 42b56ec..dd852c5 100644 --- a/AcaMate/0. Setup/AppDelegate.swift +++ b/AcaMate/0. Setup/AppDelegate.swift @@ -83,7 +83,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { // 디바이스 토큰 등록 성공 시 func application(_ application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02.2hX", $1)}) - @UserDefault(key: "pushToken", defaultValue: "") var pushToken + @UserDefault(key: "pushToken", defaultValue: "pushToken") var pushToken pushToken = deviceTokenString printLog("APNs 디바이스 푸시 토큰: \(deviceTokenString)") // 서버로 디바이스 토큰 전달 로직 추가 diff --git a/AcaMate/1. View/11. Intro & Login/RegisterView.swift b/AcaMate/1. View/11. Intro & Login/RegisterView.swift index 9358e4f..fa20391 100644 --- a/AcaMate/1. View/11. Intro & Login/RegisterView.swift +++ b/AcaMate/1. View/11. Intro & Login/RegisterView.swift @@ -261,10 +261,6 @@ struct RegisterView: View { dropdownManager.font = .nps(size: 16) } - // .onChange(of: dropdownManager.onSelect) { _, new in - // printLog("CHANGE: \(new)") - // - // } } func leftAct() { @@ -276,3 +272,18 @@ struct RegisterView: View { } } +struct selectPolicyView: View { + @ObservedObject var registerVM: RegisterViewModel + @ObservedObject var btnVM: ButtonViewModel + + init(_ registerVM: RegisterViewModel, btnVM: ButtonViewModel) { + self.registerVM = registerVM + self.btnVM = btnVM + } + + var body: some View { + HStack(spacing: 0) { + CircleBtnView(vm: btnVM, id: registerVM.policyBtn1ID) + } + } +} diff --git a/AcaMate/2. Model/API Response.swift b/AcaMate/2. Model/API Response.swift index c8ce403..c7fc75c 100644 --- a/AcaMate/2. Model/API Response.swift +++ b/AcaMate/2. Model/API Response.swift @@ -86,13 +86,15 @@ class User: Codable { } // /api/v1/in/user/login ---------------- - +// /api/v1/in/user/register ---------------- class User_Token: Codable { let token: String? let refresh: String? // let bids: [String] } + + // /api/v1/in/member/academy ---------------- class AcademyName: Codable { let bid: String? diff --git a/AcaMate/2. Model/SNS Data.swift b/AcaMate/2. Model/SNS Data.swift index a7cc5ad..5ff1a77 100644 --- a/AcaMate/2. Model/SNS Data.swift +++ b/AcaMate/2. Model/SNS Data.swift @@ -8,9 +8,9 @@ import Foundation -enum SNSLoginType{ - case Kakao - case Apple +enum SNSLoginType: String{ + case Apple = "ST00" + case Kakao = "ST01" } struct SNSID: Codable { diff --git a/AcaMate/3. ViewModel/AppViewModel.swift b/AcaMate/3. ViewModel/AppViewModel.swift index 8c68cbe..295d458 100644 --- a/AcaMate/3. ViewModel/AppViewModel.swift +++ b/AcaMate/3. ViewModel/AppViewModel.swift @@ -7,6 +7,11 @@ import SwiftUI import Combine +// +//import AVFoundation +//import Photos +//import CoreLocation +//import UserNotifications class AppViewModel: ObservableObject { // public static let shared = AppViewModel() @@ -21,9 +26,11 @@ class AppViewModel: ObservableObject { /// 항상 최신값을 가지고 있다가 구독자 추가 되면 그 즉시 값을 전달하고 이후 업데이트 되는 값을 계속 보내주는 역할을 함 let alertAction = CurrentValueSubject(nil) var apiManager: APIManager = APIManager() + var permissionManager = PermissionManager() -// private init() { -// +// init() { +// permissionManager.location // } -// + + } diff --git a/AcaMate/3. ViewModel/RegisterViewModel.swift b/AcaMate/3. ViewModel/RegisterViewModel.swift index 5f605f2..5069bc6 100644 --- a/AcaMate/3. ViewModel/RegisterViewModel.swift +++ b/AcaMate/3. ViewModel/RegisterViewModel.swift @@ -18,8 +18,14 @@ class RegisterViewModel: ObservableObject { self.responseValue = (type, snsID) } + @UserDefault(key: "token", defaultValue: "accToken") var accToken + @UserDefault(key: "refresh", defaultValue: "refreshToken") var refresh + @UserDefault(key: "header", defaultValue: "headerValue") var headerValue + @UserDefault(key: "pushToken", defaultValue: "pushToken") var pushToken + let addressBtnID = UUID() let registerBtnID = UUID() + let policyBtn1ID = UUID() @Published var selectDate: Date = { let calendar = Calendar.current @@ -57,16 +63,61 @@ class RegisterViewModel: ObservableObject { func registerUser() { // 필수 값 + let birth = "\(selectDate.convertString("yyyy-MM-dd"))" + printLog("\(birth)") if nameText != "" && emailFrontText != "" && emailTailText != "" && phoneTextSet.0 != "" && phoneTextSet.1 != "" && phoneTextSet.2 != "" { + var param: [String:Any] = [:] + + param["name"] = "\(nameText)" + if changeDate { param["birth"] = "\(selectDate.convertString("yyyy-MM-dd"))"} + param["type"] = "UT02" + + if let deviceId = UIDevice.current.identifierForVendor?.uuidString, + let bundleId = Bundle.main.bundleIdentifier { + param["device_id"] = "\(deviceId)" + } + + param["auto_login_yn"] = false + param["login_date"] = Date() + param["push_token"] = "\(pushToken)" + param["email"] = "\(emailFrontText)@\(emailTailText)" + + param["phone"] = "\(phoneTextSet.0)\(phoneTextSet.1)\(phoneTextSet.2)" + if addressText != "주소 입력" { + if addrDetailText == "" { param["address"] = "\(self.addressText)" } + else { param["address"] = "\(self.addressText) \(self.addrDetailText)"} + } + param["location_yn"] = appVM.permissionManager.checkLocationPermission() + param["camera_yn"] = appVM.permissionManager.checkCameraPermission() + param["photo_yn"] = appVM.permissionManager.checkPhotoPermission() + appVM.permissionManager.checkPushPermission(completion: { status in + param["push_yn"] = status + }) + + param["market_app_yn"] = true + param["market_sms_yn"] = true + param["market_email_yn"] = true + + param["sns_id"] = self.responseValue.1 + param["sns_type"] = self.responseValue.0.rawValue + + if !changeDate || addressText == "주소 입력" { appVM.alertData = AlertData( title: "알림", body: "\(changeDate ? "":"[생일]")\((addressText != "주소 입력") ? "":"[주소]")의 내용이 없습니다.\n 계속해서 진행할까요?", - button: [ButtonType(name: "확인", role: .cancel, function: nil)]) + button: [ + ButtonType(name: "돌아가기", role: .destructive, function: nil), + ButtonType(name: "가입하기", role: .cancel) { + self.callAPI(param) + } + ] + ) appVM.showAlert.toggle() // 넘어가는 로직도 추가 } else { + self.callAPI(param) //정상 동작 } @@ -77,8 +128,43 @@ class RegisterViewModel: ObservableObject { button: [ButtonType(name: "확인", role: .cancel, function: nil)]) appVM.showAlert.toggle() } + } + + private func callAPI(_ param: [String: Any]) { - + self.appVM.apiManager.loadAPIData( + APIRequest(path: "/api/v1/in/user/register", + headers: [API_HEADER : self.headerValue], + parameters: param, + decoding: APIResponse.self) + ) + .sink { [weak self] completion in + guard let self = self else { return } + // API 자체적으로 내보내는 에러는 여기서 거를거고 + switch completion { + case .failure(let error): + printLog("\(error)") + case .finished: + break + } + } receiveValue: { [weak self] response in + guard let self = self else { return } + switch response.status.code { + case .success(let code): + if code == "000" { + if let data = response.data, let accToken = data.token, let refresh = data.refresh{ + self.accToken = accToken + self.refresh = refresh + appVM.naviState.set(act: .RESET, path: .Main) + } + } + default: + break + } + + } + .store(in: &cancellables) + } } diff --git a/AcaMate/5. Manager/PermissionManager.swift b/AcaMate/5. Manager/PermissionManager.swift new file mode 100644 index 0000000..61b3a64 --- /dev/null +++ b/AcaMate/5. Manager/PermissionManager.swift @@ -0,0 +1,86 @@ +// +// PermissionManager.swift +// AcaMate +// +// Created by TAnine on 3/28/25. +// + +import Foundation +import AVFoundation +import Photos +import CoreLocation +import UserNotifications + +class PermissionManager: NSObject, ObservableObject { + + private let locationManager = CLLocationManager() + private var locationRequestCallback: ((CLAuthorizationStatus) -> Void)? + + override init() { + super.init() + locationManager.delegate = self + } + + // MARK: - 푸시 권한 요청 + func requestPushPermission(completion: @escaping (Bool) -> Void) { + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in + DispatchQueue.main.async { + completion(granted) + } + } + } + + // MARK: - 카메라 권한 요청 + func requestCameraPermission(completion: @escaping (Bool) -> Void) { + AVCaptureDevice.requestAccess(for: .video) { granted in + DispatchQueue.main.async { + completion(granted) + } + } + } + + // MARK: - 앨범 권한 요청 + func requestPhotoPermission(completion: @escaping (PHAuthorizationStatus) -> Void) { + PHPhotoLibrary.requestAuthorization { status in + DispatchQueue.main.async { + completion(status) + } + } + } + + // MARK: - 위치 권한 요청 + func requestLocationPermission(completion: @escaping (CLAuthorizationStatus) -> Void) { + locationRequestCallback = completion + locationManager.requestWhenInUseAuthorization() + } + + // MARK: - 현재 권한 상태 확인 + func checkPushPermission(completion: @escaping (Bool) -> Void) { + UNUserNotificationCenter.current().getNotificationSettings { settings in + DispatchQueue.main.async { + completion(settings.authorizationStatus == .authorized) + } + } + } + + func checkCameraPermission() -> Bool { + return AVCaptureDevice.authorizationStatus(for: .video) == .authorized + } + + func checkPhotoPermission() -> Bool { + let status = PHPhotoLibrary.authorizationStatus(for: .readWrite) + return status == .authorized || status == .limited + } + + func checkLocationPermission() -> Bool { + let status = locationManager.authorizationStatus + return status == .authorizedAlways || status == .authorizedWhenInUse + } +} +extension PermissionManager: CLLocationManagerDelegate { + func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { + let status = manager.authorizationStatus + locationRequestCallback?(status) + locationRequestCallback = nil + } +} diff --git a/AcaMate/Info.plist b/AcaMate/Info.plist index c94ef61..6ac1b78 100644 --- a/AcaMate/Info.plist +++ b/AcaMate/Info.plist @@ -50,5 +50,14 @@ fetch external-accessory + NSCameraUsageDescription + 카메라 접근이 필요합니다. + NSPhotoLibraryUsageDescription + 사진 앨범 접근이 필요합니다. + NSLocationWhenInUseUsageDescription + 앱 사용 중 위치 접근이 필요합니다. + NSUserTrackingUsageDescription + 푸시 알림 권한을 요청합니다. +