diff --git a/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate b/AcaMate.xcodeproj/project.xcworkspace/xcuserdata/tanine.xcuserdatad/UserInterfaceState.xcuserstate index 9842947..06a2d11 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/WebView.swift b/AcaMate/0. Setup/WebView.swift new file mode 100644 index 0000000..5939531 --- /dev/null +++ b/AcaMate/0. Setup/WebView.swift @@ -0,0 +1,78 @@ +// +// WebView.swift +// AcaMate +// +// Created by TAnine on 3/24/25. +// + +import SwiftUI +import WebKit + +struct WebView: UIViewControllerRepresentable { + @Binding var isLoding: Bool + func updateUIViewController(_ uiViewController: WebViewController, context: Context) { + } + + func makeUIViewController(context: Context) -> WebViewController { + return WebViewController() + } + + +} + +class WebViewController: UIViewController, WKUIDelegate { + + override func viewDidLoad() { + super.viewDidLoad() + webView() + } + + func webView() { + let url = URL(string: "https://sean-59.github.io/Kakao-Postcode/")! + let request = URLRequest(url: url) + + let configuration = WKWebViewConfiguration() + let contentController = WKUserContentController() + + contentController.add(self, name: "callBackHandler") + configuration.userContentController = contentController + + let webview = WKWebView(frame: view.bounds, configuration: configuration) + webview.uiDelegate = self + webview.navigationDelegate = self + webview.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + webview.isUserInteractionEnabled = true + webview.scrollView.isUserInteractionEnabled = true + webview.scrollView.delaysContentTouches = false + + webview.load(request) + view.addSubview(webview) + + } +} + +extension WebViewController: WKNavigationDelegate { + func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + print("웹뷰 로딩 시작") + } + + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + print("웹뷰 로딩 완료") + // 여기서 추가 작업(예: 로딩 인디케이터 숨기기)을 수행할 수 있습니다. + } +} + +extension WebViewController: WKScriptMessageHandler { + func userContentController(_ userContentController: WKUserContentController, + didReceive message: WKScriptMessage) { + if let data = message.body as? [String: Any] { + print(data) + print(data["jibunAddress"] ?? "jibunAddress 없음") + print(data["roadAddress"] ?? "roadAddress 없음") + print(data["zonecode"] ?? "zonecode 없음") + } + } +} + diff --git a/AcaMate/1. View/10. Common/NavigationView.swift b/AcaMate/1. View/10. Common/NavigationView.swift index 3d25772..6dbb37b 100644 --- a/AcaMate/1. View/10. Common/NavigationView.swift +++ b/AcaMate/1. View/10. Common/NavigationView.swift @@ -22,13 +22,13 @@ struct NavigationView: View { case .NONE: EmptyView() case .Intro: - IntroView(appVM: appVM) + IntroView(appVM) case .Login : - LoginView(appVM: appVM) + LoginView(appVM) case .Register(let type, let id): - RegisterView(type: type, snsID: id) + RegisterView(appVM, type: type, snsID: id) case .SelectAcademy: - SelectAcademyView() + SelectAcademyView(appVM) case .Main: MainView() case .ChatRoom(let id): diff --git a/AcaMate/1. View/11. Intro & Login/IntroView.swift b/AcaMate/1. View/11. Intro & Login/IntroView.swift index f169434..89c39c3 100644 --- a/AcaMate/1. View/11. Intro & Login/IntroView.swift +++ b/AcaMate/1. View/11. Intro & Login/IntroView.swift @@ -8,15 +8,14 @@ import SwiftUI import Combine + struct IntroView: View { -// @EnvironmentObject var appVM: AppViewModel - @StateObject private var introVM : IntroViewModel - + @EnvironmentObject var appVM: AppViewModel + @StateObject var introVM: IntroViewModel @State var cancellables: Set = [] - init(appVM: AppViewModel){ - _introVM = StateObject(wrappedValue: IntroViewModel(appVM: appVM)) -// self.introVM = IntroViewModel(appVM: appVM) + init(_ appVM: AppViewModel) { + _introVM = StateObject(wrappedValue: IntroViewModel(appVM)) } var body: some View { diff --git a/AcaMate/1. View/11. Intro & Login/LoginView.swift b/AcaMate/1. View/11. Intro & Login/LoginView.swift index c06ad42..fa9dae9 100644 --- a/AcaMate/1. View/11. Intro & Login/LoginView.swift +++ b/AcaMate/1. View/11. Intro & Login/LoginView.swift @@ -8,13 +8,16 @@ import SwiftUI import Combine + struct LoginView: View { -// @EnvironmentObject var appVM: AppViewModel - @StateObject private var loginVM: LoginViewModel - init(appVM : AppViewModel) { - _loginVM = StateObject(wrappedValue: LoginViewModel(appVM: appVM)) + @EnvironmentObject var appVM: AppViewModel + @StateObject var loginVM: LoginViewModel + + init(_ appVM: AppViewModel) { + _loginVM = StateObject(wrappedValue: LoginViewModel(appVM)) } + var body: some View { VStack(spacing: 0) { Spacer().frame(height: 100) @@ -54,6 +57,9 @@ struct LoginView: View { } .frame(maxWidth: .infinity,maxHeight: .infinity) .fullDrawView(.Normal.normal) + .onAppear() { + + } // .onChange(of: loginVM.pathName){ _, new in // appVM.naviState.set(act: .ADD, path: new) diff --git a/AcaMate/1. View/11. Intro & Login/RegisterView.swift b/AcaMate/1. View/11. Intro & Login/RegisterView.swift index da555cd..6c7d341 100644 --- a/AcaMate/1. View/11. Intro & Login/RegisterView.swift +++ b/AcaMate/1. View/11. Intro & Login/RegisterView.swift @@ -11,37 +11,146 @@ struct RegisterView: View { @EnvironmentObject var appVM: AppViewModel @StateObject private var topVM = TopViewModel() @StateObject var btnVM = ButtonViewModel() + @StateObject var registerVM: RegisterViewModel + + private let responseValue: (SNSLoginType, String) + + init(_ appVM: AppViewModel, type: SNSLoginType, snsID: String) { + _registerVM = StateObject(wrappedValue: RegisterViewModel(appVM)) + self.responseValue.0 = type + self.responseValue.1 = snsID + } + @State private var scrollOffset: CGPoint = .zero + @State private var showWebView = false - let type: SNSLoginType - let snsID: String - @State private var selectDate: Date = { - let calendar = Calendar.current - return calendar.date(byAdding: .year, value: -12, to: Date()) ?? Date() - }() + + let addressBtnID = UUID() + let registerBtnID = UUID() + var body: some View { // MARK: TO-DO // 회원가입 뷰 만들기 // 이름, 번호, 이메일, 주소, 생년월일 - - VStack(spacing: 0) { TopView(topVM: topVM) OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in - DatePicker("생년월일 입력", selection: $selectDate, displayedComponents: [.date]) + // 이름 + HStack(spacing: 0){ + HStack(spacing: 0) { + Text("이름") + .font(.nps(size: 16)) + Text("*") + .font(.nps(size: 16)) + .foregroundStyle(Color(.Other.red)) + } + .frame(width: 60, alignment: .leading) + Spacer(minLength: 1) + CustomTextField(placeholder: "최대 10글자", text: $registerVM.nameText) + .frame(maxWidth: .infinity,maxHeight: 48) + .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + .background { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(Color(.Normal.light)) + } + } + .padding() + + // 생년월일 + DatePicker("생일", selection: $registerVM.selectDate, displayedComponents: [.date]) .datePickerStyle(.compact) .environment(\.locale, Locale(identifier: "ko_KR")) .font(.nps(size: 16)) .padding() + // E-Mail + HStack(spacing: 0){ + Text("이메일") + .font(.nps(size: 16)) + .frame(width: 60, alignment: .leading) + Spacer(minLength: 1) + CustomTextField(placeholder: "앞부분 입력", text: $registerVM.emailFrontText) + .frame(maxWidth: .infinity,maxHeight: 48) + .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + .background { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(Color(.Normal.light)) + } + // Spacer(minLength: 1) + Text("@") + .font(.nps(font: .bold, size: 16)) + .padding([.leading, .trailing], 4) + // Spacer(minLength: 1) + CustomTextField(placeholder: "뒷부분 입력", text: $registerVM.emailTailText) + .frame(maxWidth: .infinity,maxHeight: 48) + .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + .background { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(Color(.Normal.light)) + } + } + .padding() + + // Phone + HStack(spacing: 0){ + Text("연락처") + .font(.nps(size: 16)) + .frame(width: 60, alignment: .leading) + + CustomTextField(placeholder: "000", text: $registerVM.nameText) + .frame(maxWidth: .infinity,maxHeight: 48) + .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + .background { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(Color(.Normal.light)) + } + Text("-") + .font(.nps(size: 16)) + .padding([.leading, .trailing], 4) + CustomTextField(placeholder: "0000", text: $registerVM.nameText) + .frame(maxWidth: .infinity,maxHeight: 48) + .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + .background { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(Color(.Normal.light)) + } + Text("-") + .font(.nps(size: 16)) + .padding([.leading, .trailing], 4) + CustomTextField(placeholder: "0000", text: $registerVM.nameText) + .frame(maxWidth: .infinity,maxHeight: 48) + .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + .background { + RoundedRectangle(cornerRadius: 24) + .foregroundStyle(Color(.Normal.light)) + } + } + .padding() + + HStack(spacing: 0){ + Text("연락처") + .font(.nps(size: 16)) + .frame(width: 60, alignment: .leading) + Spacer(minLength: 1) + SimpleBtnView(vm: btnVM, id: addressBtnID) + .padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20)) + + } + .padding() + + // address } .frame(maxWidth: .infinity, maxHeight: .infinity) + .sheet(isPresented: $showWebView) { + WebView(isLoding: $showWebView) + .edgesIgnoringSafeArea(.all) + } } .onAppear { @@ -49,6 +158,12 @@ struct RegisterView: View { topVM.setLeftBtn(Image(.Icon.left), size: CGPoint(x: 40, y: 40), action: leftAct) topVM.setRightBtn(size: CGPoint(x: 40, y: 40), action: rightAct) + btnVM.setSize(for: addressBtnID, newWidth: 80, newHeight: 24) + btnVM.setText(for: addressBtnID, newText: "주소 입력", newFont: .nps(size: 16)) + btnVM.setAction(for: addressBtnID) { +// self.appVM.isLoading.toggle() + self.showWebView.toggle() + } } } diff --git a/AcaMate/1. View/11. Intro & Login/SelectAcademyView.swift b/AcaMate/1. View/11. Intro & Login/SelectAcademyView.swift index a1c8eec..db6b489 100644 --- a/AcaMate/1. View/11. Intro & Login/SelectAcademyView.swift +++ b/AcaMate/1. View/11. Intro & Login/SelectAcademyView.swift @@ -9,7 +9,11 @@ import SwiftUI struct SelectAcademyView: View { @EnvironmentObject var appVM: AppViewModel - @StateObject var saVM = SelectAcademyViewModel() + @StateObject var saVM: SelectAcademyViewModel + + init(_ appVM: AppViewModel) { + _saVM = StateObject(wrappedValue: SelectAcademyViewModel(appVM)) + } @State private var scrollOffset: CGPoint = .zero diff --git a/AcaMate/1. View/12. Main/MainView.swift b/AcaMate/1. View/12. Main/MainView.swift index ab28cf3..231b6c8 100644 --- a/AcaMate/1. View/12. Main/MainView.swift +++ b/AcaMate/1. View/12. Main/MainView.swift @@ -10,7 +10,6 @@ import Combine struct MainView: View { @EnvironmentObject var appVM: AppViewModel - @EnvironmentObject var alertController: AlertController @State var cancellables: Set = [] // @Binding var naviState : NaviState diff --git a/AcaMate/3. ViewModel/AlertViewModel.swift b/AcaMate/3. ViewModel/AlertViewModel.swift deleted file mode 100644 index 228379f..0000000 --- a/AcaMate/3. ViewModel/AlertViewModel.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// AlertController.swift -// AcaMate -// -// Created by Sean Kim on 12/13/24. -// - -import SwiftUI -import Combine - - -class AlertViewModel2: ObservableObject { - @Published var showAlert: Bool = false - var alertData: AlertData = .init(body: "") - - let alertAction = CurrentValueSubject(nil) - - -} diff --git a/AcaMate/3. ViewModel/AppViewModel.swift b/AcaMate/3. ViewModel/AppViewModel.swift index ac3eec1..14086fb 100644 --- a/AcaMate/3. ViewModel/AppViewModel.swift +++ b/AcaMate/3. ViewModel/AppViewModel.swift @@ -9,6 +9,8 @@ import SwiftUI import Combine class AppViewModel: ObservableObject { +// public static let shared = AppViewModel() + @Published var isLoading: Bool = false @Published var showAlert: Bool = false @Published var menuName: MenuName = .Home @@ -18,5 +20,10 @@ class AppViewModel: ObservableObject { /// 항상 최신값을 가지고 있다가 구독자 추가 되면 그 즉시 값을 전달하고 이후 업데이트 되는 값을 계속 보내주는 역할을 함 let alertAction = CurrentValueSubject(nil) + var apiManager: APIManager = APIManager() +// private init() { +// +// } +// } diff --git a/AcaMate/3. ViewModel/ChatViewModel.swift b/AcaMate/3. ViewModel/ChatViewModel.swift index 1e1aad1..edeea55 100644 --- a/AcaMate/3. ViewModel/ChatViewModel.swift +++ b/AcaMate/3. ViewModel/ChatViewModel.swift @@ -6,9 +6,17 @@ // import SwiftUI +final class ChatVM: ObservableObject { + @Published var vm: ChatViewModel? +} class ChatViewModel: ObservableObject { + private let appVM: AppViewModel + @Published var messages: [ChatMesage] = [] + init(_ appVM: AppViewModel) { + self.appVM = appVM + } } diff --git a/AcaMate/3. ViewModel/IntroViewModel.swift b/AcaMate/3. ViewModel/IntroViewModel.swift index c11cd61..b7558b3 100644 --- a/AcaMate/3. ViewModel/IntroViewModel.swift +++ b/AcaMate/3. ViewModel/IntroViewModel.swift @@ -8,15 +8,15 @@ import SwiftUI import Combine + class IntroViewModel: ObservableObject { - var appVM : AppViewModel + private let appVM: AppViewModel + private var cancellables = Set() -// @Published var toggleLoading: Bool = false -// @Published var pathName: PathName = .NONE @UserDefault(key: "header", defaultValue: "headerValue") var headerValue - init(appVM: AppViewModel) { + init(_ appVM: AppViewModel) { self.appVM = appVM } @@ -83,7 +83,7 @@ class IntroViewModel: ObservableObject { decoding: APIResponse
.self) - APIManager.shared.loadAPIData(request) + appVM.apiManager.loadAPIData(request) .sink { completion in switch completion { case .failure(let error): @@ -133,7 +133,7 @@ class IntroViewModel: ObservableObject { parameters: ["type":"I"], decoding: APIResponse.self) - APIManager.shared.loadAPIData(request) + appVM.apiManager.loadAPIData(request) .sink { completion in switch completion { case .failure(let error): diff --git a/AcaMate/3. ViewModel/LoginViewModel.swift b/AcaMate/3. ViewModel/LoginViewModel.swift index 313c69c..89ed1dd 100644 --- a/AcaMate/3. ViewModel/LoginViewModel.swift +++ b/AcaMate/3. ViewModel/LoginViewModel.swift @@ -8,9 +8,9 @@ import SwiftUI import Combine - class LoginViewModel: ObservableObject { - let appVM: AppViewModel + private let appVM: AppViewModel + private var cancellables = Set() // @Published var toggleLoading: Bool = false @@ -22,7 +22,8 @@ class LoginViewModel: ObservableObject { var bidArray: [String] = [] - init(appVM: AppViewModel) { + + init(_ appVM: AppViewModel) { self.appVM = appVM } @@ -30,7 +31,7 @@ class LoginViewModel: ObservableObject { appVM.isLoading = true LoginController().login(type) .flatMap{ snsId in - APIManager.shared.loadAPIData(APIRequest(path: "/api/v1/in/user/login", + self.appVM.apiManager.loadAPIData(APIRequest(path: "/api/v1/in/user/login", headers: [API_HEADER : self.headerValue], parameters: [ "acctype": "\(type == .Apple ? "ST00": "ST01")", diff --git a/AcaMate/3. ViewModel/RegisterViewModel.swift b/AcaMate/3. ViewModel/RegisterViewModel.swift new file mode 100644 index 0000000..fe64fe1 --- /dev/null +++ b/AcaMate/3. ViewModel/RegisterViewModel.swift @@ -0,0 +1,31 @@ +// +// RegisterViewModel.swift +// AcaMate +// +// Created by TAnine on 3/24/25. +// + +import SwiftUI +import Combine + +class RegisterViewModel: ObservableObject { + private let appVM: AppViewModel + private var cancellables = Set() + + init(_ appVM: AppViewModel) { + self.appVM = appVM + } + + @State var selectDate: Date = { + let calendar = Calendar.current + return calendar.date(byAdding: .year, value: -12, to: Date()) ?? Date() + }() + + @State var nameText: String = "" + @State var emailFrontText: String = "" + @State var emailTailText: String = "" + @State var phoneArray: [Int] = [] + @State var addressText: String = "" + + +} diff --git a/AcaMate/3. ViewModel/SelectAcademyViewModel.swift b/AcaMate/3. ViewModel/SelectAcademyViewModel.swift index 4f4de90..5d7ce85 100644 --- a/AcaMate/3. ViewModel/SelectAcademyViewModel.swift +++ b/AcaMate/3. ViewModel/SelectAcademyViewModel.swift @@ -9,8 +9,13 @@ import SwiftUI import Combine class SelectAcademyViewModel: ObservableObject { + private var appVM: AppViewModel private var cancellables: Set = [] + init(_ appVM: AppViewModel) { + self.appVM = appVM + } + @Published var academyCode: String = "" @Published var academyList: [AcademyName] = [] @Published var selectNum: Int = -1 @@ -23,7 +28,7 @@ class SelectAcademyViewModel: ObservableObject { parameters: ["token": token, "refresh": refresh], decoding: APIResponse<[AcademyName]>.self) - APIManager.shared.loadAPIData(request) + appVM.apiManager.loadAPIData(request) .sink { completion in switch completion { case .failure(let error): diff --git a/AcaMate/5. Manager/APIManager.swift b/AcaMate/5. Manager/APIManager.swift index ff16eb6..f3a9265 100644 --- a/AcaMate/5. Manager/APIManager.swift +++ b/AcaMate/5. Manager/APIManager.swift @@ -8,18 +8,18 @@ import Foundation import Combine - import Alamofire + public class APIManager { private var cancellables = Set() - public static let shared = APIManager() +// public static let shared = APIManager() @UserDefault(key: "refresh", defaultValue: "refreshToken") var refresh @UserDefault(key: "token", defaultValue: "accToken") var accToken - private init(cancellables: Set = Set()) { - self.cancellables = cancellables - } +// private init(cancellables: Set = Set()) { +// self.cancellables = cancellables +// } public func loadAPIData(_ request: APIRequest) -> Future { let encoding: ParameterEncoding = (request.method == .get) ? URLEncoding.default : JSONEncoding.default @@ -58,7 +58,7 @@ public class APIManager { parameters: ["refresh": refresh], decoding: APIResponse.self) - APIManager.shared.loadAPIData(request) + APIManager().loadAPIData(request) .sink { completion in switch completion { case .failure(let error): @@ -105,7 +105,7 @@ public class APIManager { self.accToken = (response as! Access).access var updateRequest = request updateRequest.parameters["token"] = self.accToken - return APIManager.shared.loadAPIData(updateRequest) + return APIManager().loadAPIData(updateRequest) .eraseToAnyPublisher() } .eraseToAnyPublisher() diff --git a/AcaMate/AcaMateApp.swift b/AcaMate/AcaMateApp.swift index 8c2fade..5a95baa 100644 --- a/AcaMate/AcaMateApp.swift +++ b/AcaMate/AcaMateApp.swift @@ -29,7 +29,7 @@ struct AcaMateApp: App { _ = AuthController.handleOpenUrl(url: url) } } - .environmentObject(self.appVM) + .environmentObject(appVM) } } }