From 0b4808c25a816c5e49211ad98f0a8da2aa8203bf Mon Sep 17 00:00:00 2001 From: longlivedrgn Date: Sun, 20 Oct 2024 22:18:47 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=9B=B9=EB=B7=B0=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=B4=20=ED=91=B8?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boolti/Boolti.xcodeproj/project.pbxproj | 4 ++ .../ConcertListViewController.swift | 6 ++- .../ConcertList/WebViewController.swift | 53 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift diff --git a/Boolti/Boolti.xcodeproj/project.pbxproj b/Boolti/Boolti.xcodeproj/project.pbxproj index d6c6b80e..883b99af 100644 --- a/Boolti/Boolti.xcodeproj/project.pbxproj +++ b/Boolti/Boolti.xcodeproj/project.pbxproj @@ -421,6 +421,7 @@ 87F7DF492B8275D30068A6C9 /* EntryCodeErrorEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87F7DF482B8275D30068A6C9 /* EntryCodeErrorEntity.swift */; }; 87FB0D382B6BE7B900B42AF9 /* SwiftJWT in Frameworks */ = {isa = PBXBuildFile; productRef = 87FB0D372B6BE7B900B42AF9 /* SwiftJWT */; }; 87FB0D3A2B6BE97F00B42AF9 /* IdentityTokenDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FB0D392B6BE97F00B42AF9 /* IdentityTokenDTO.swift */; }; + 87FCC3C72CC4DDFD0029A0C8 /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87FCC3C62CC4DDFD0029A0C8 /* WebViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -786,6 +787,7 @@ 87F7DF482B8275D30068A6C9 /* EntryCodeErrorEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryCodeErrorEntity.swift; sourceTree = ""; }; 87FB0D392B6BE97F00B42AF9 /* IdentityTokenDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityTokenDTO.swift; sourceTree = ""; }; 87FB0D3B2B6C09B500B42AF9 /* TicketItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TicketItemEntity.swift; sourceTree = ""; }; + 87FCC3C62CC4DDFD0029A0C8 /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1800,6 +1802,7 @@ children = ( 840B39532B7667B100E7F8C8 /* Cells */, 84781CCB2B5C003700D37921 /* ConcertListDIContainer.swift */, + 87FCC3C62CC4DDFD0029A0C8 /* WebViewController.swift */, 84781CCD2B5C005600D37921 /* ConcertListViewController.swift */, 84781CCF2B5C006500D37921 /* ConcertViewModel.swift */, 840B39562B76688C00E7F8C8 /* ConcertCollectionViewFlowLayout.swift */, @@ -2748,6 +2751,7 @@ 224BEF0D2C4E6EFF00863970 /* OrderGiftPaymentResponseDTO.swift in Sources */, 84A0248E2B7B997A0095A56E /* QRScannerViewController.swift in Sources */, 846F5F3E2B7BBF60000F86AE /* QRRepository.swift in Sources */, + 87FCC3C72CC4DDFD0029A0C8 /* WebViewController.swift in Sources */, 8757BAA62B5FDED7008503B5 /* OAuthRepository.swift in Sources */, 221393582C88996A00459A20 /* EditProfileViewController.swift in Sources */, 848CBF0A2B65481500239303 /* TicketingDetailDIContainer.swift in Sources */, diff --git a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift index d0d6fb83..79514f6a 100644 --- a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift +++ b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift @@ -175,8 +175,10 @@ extension ConcertListViewController: UICollectionViewDelegate { let viewController = concertDetailViewControllerFactory(self.viewModel.output.topConcerts[indexPath.row].id) self.navigationController?.pushViewController(viewController, animated: true) case .banner: - guard let url = URL(string: "https://boolti.in/login") else { return } - self.openSafari(with: url) + let vc = WebViewController() + self.navigationController?.pushViewController(vc, animated: true) +// guard let url = URL(string: "https://boolti.in/login") else { return } +// self.openSafari(with: url) case .bottomConcerts: let viewController = concertDetailViewControllerFactory(self.viewModel.output.bottomConcerts[indexPath.row].id) self.navigationController?.pushViewController(viewController, animated: true) diff --git a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift new file mode 100644 index 00000000..10347807 --- /dev/null +++ b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift @@ -0,0 +1,53 @@ +import UIKit +import WebKit +import SnapKit + + +class WebViewController: UIViewController, WKUIDelegate { + + var webView: WKWebView! + + override func viewDidLoad() { + super.viewDidLoad() + + let accessToken = UserDefaults.accessToken + let refreshToken = UserDefaults.refreshToken + + // WKWebView 설정 + let webConfiguration = WKWebViewConfiguration() + webView = WKWebView(frame: view.bounds, configuration: webConfiguration) + webView.uiDelegate = self + view.addSubview(webView) + + // URL 설정 + let url = URL(string: "https://dotori.boolti.in/show/add")! + + // 쿠키 생성 및 설정 + let accessTokenCookie = HTTPCookie(properties: [ + .domain: url.host!, + .path: "/", + .name: "x-access-token", + .value: accessToken, + .secure: "TRUE", + .expires: Date().addingTimeInterval(3600) // 1시간 후 만료 + ])! + + let refreshTokenCookie = HTTPCookie(properties: [ + .domain: url.host!, + .path: "/", + .name: "x-refresh-token", + .value: refreshToken, + .secure: "TRUE", + .expires: Date().addingTimeInterval(2592000) // 30일 후 만료 + ])! + + // 쿠키 설정 + webView.configuration.websiteDataStore.httpCookieStore.setCookie(accessTokenCookie) { + self.webView.configuration.websiteDataStore.httpCookieStore.setCookie(refreshTokenCookie) { + // 두 쿠키가 모두 설정된 후 URL 로드 + let request = URLRequest(url: url) + self.webView.load(request) + } + } + } +} From 3295668fda436f1db8f40952dbb72e6f49fe7761 Mon Sep 17 00:00:00 2001 From: longlivedrgn Date: Mon, 21 Oct 2024 02:16:31 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20WKWebViewConfiguration=20extension?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EA=B3=B5=EC=97=B0=20?= =?UTF-8?q?=EC=9B=B9=EB=B7=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Boolti/Boolti.xcodeproj/project.pbxproj | 4 + Boolti/Boolti/Application/Info.plist | 2 + .../Extensions/WKWebViewConfiguration+.swift | 35 +++++++ .../ConcertListViewController.swift | 6 +- .../ConcertList/WebViewController.swift | 91 +++++++++++++------ Boolti/Boolti/Support/Enviroment.swift | 10 +- 6 files changed, 115 insertions(+), 33 deletions(-) create mode 100644 Boolti/Boolti/Sources/Global/Extensions/WKWebViewConfiguration+.swift diff --git a/Boolti/Boolti.xcodeproj/project.pbxproj b/Boolti/Boolti.xcodeproj/project.pbxproj index 883b99af..9f8150f3 100644 --- a/Boolti/Boolti.xcodeproj/project.pbxproj +++ b/Boolti/Boolti.xcodeproj/project.pbxproj @@ -355,6 +355,7 @@ 87A3716D2B76497B0061814E /* TicketReservationsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A3716C2B76497B0061814E /* TicketReservationsTableViewCell.swift */; }; 87A3716F2B76534B0061814E /* TicketReservationItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A3716E2B76534B0061814E /* TicketReservationItemEntity.swift */; }; 87B18FE72BB15A3C005A4800 /* ReversalPolicyConfirmButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B18FE62BB15A3C005A4800 /* ReversalPolicyConfirmButton.swift */; }; + 87B864832CC5711800A9A836 /* WKWebViewConfiguration+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B864822CC5711800A9A836 /* WKWebViewConfiguration+.swift */; }; 87C7594C2BA93EA40009A83E /* NotificationMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C7594B2BA93EA40009A83E /* NotificationMessage.swift */; }; 87C7A5EC2CADABDA0078213E /* UnderlineSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C7A5EB2CADABDA0078213E /* UnderlineSegmentedControl.swift */; }; 87C7A5EE2CADB1CD0078213E /* SegmentedControlContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87C7A5ED2CADB1CD0078213E /* SegmentedControlContainerView.swift */; }; @@ -746,6 +747,7 @@ 87A3716C2B76497B0061814E /* TicketReservationsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TicketReservationsTableViewCell.swift; sourceTree = ""; }; 87A3716E2B76534B0061814E /* TicketReservationItemEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TicketReservationItemEntity.swift; sourceTree = ""; }; 87B18FE62BB15A3C005A4800 /* ReversalPolicyConfirmButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReversalPolicyConfirmButton.swift; sourceTree = ""; }; + 87B864822CC5711800A9A836 /* WKWebViewConfiguration+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKWebViewConfiguration+.swift"; sourceTree = ""; }; 87C7594B2BA93EA40009A83E /* NotificationMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationMessage.swift; sourceTree = ""; }; 87C7A5EB2CADABDA0078213E /* UnderlineSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnderlineSegmentedControl.swift; sourceTree = ""; }; 87C7A5ED2CADB1CD0078213E /* SegmentedControlContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedControlContainerView.swift; sourceTree = ""; }; @@ -1397,6 +1399,7 @@ 87F7DF442B82322E0068A6C9 /* Notification.Name+.swift */, 870C33352BBC29B600ED212C /* UICollectionReusableView+.swift */, 87EFEEDA2C7E1C4300A8993C /* Preview.swift */, + 87B864822CC5711800A9A836 /* WKWebViewConfiguration+.swift */, ); path = Extensions; sourceTree = ""; @@ -2832,6 +2835,7 @@ 22DFC5522C79B1F000C8433D /* ProfileViewModel.swift in Sources */, 221393612C89C17300459A20 /* EditIntroductionView.swift in Sources */, 84EC6A642B6CF973009AC6BB /* BooltiToastView.swift in Sources */, + 87B864832CC5711800A9A836 /* WKWebViewConfiguration+.swift in Sources */, 2213935A2C88997600459A20 /* EditProfileViewModel.swift in Sources */, 87A3716D2B76497B0061814E /* TicketReservationsTableViewCell.swift in Sources */, 84625A162B63C33D00CC9077 /* UITableViewCell+.swift in Sources */, diff --git a/Boolti/Boolti/Application/Info.plist b/Boolti/Boolti/Application/Info.plist index 302698ba..eaa0bc16 100644 --- a/Boolti/Boolti/Application/Info.plist +++ b/Boolti/Boolti/Application/Info.plist @@ -2,6 +2,8 @@ + REGISTER_CONCERT_URL + ${REGISTER_CONCERT_URL} GIFT_URL ${GIFT_URL} UIUserInterfaceStyle diff --git a/Boolti/Boolti/Sources/Global/Extensions/WKWebViewConfiguration+.swift b/Boolti/Boolti/Sources/Global/Extensions/WKWebViewConfiguration+.swift new file mode 100644 index 00000000..7c52ddf6 --- /dev/null +++ b/Boolti/Boolti/Sources/Global/Extensions/WKWebViewConfiguration+.swift @@ -0,0 +1,35 @@ +// +// WKWebViewConfiguration+.swift +// Boolti +// +// Created by Miro on 10/21/24. +// + +import Foundation +import WebKit + +// TODO: 쿠키 넣는 법 다시 학습하기 +extension WKWebViewConfiguration { + + static func includeCookie(cookies: [HTTPCookie], completion: @escaping (WKWebViewConfiguration?) -> Void) { + let config = WKWebViewConfiguration() + let dataStore = WKWebsiteDataStore.nonPersistent() + + DispatchQueue.main.async { + let waitGroup = DispatchGroup() + + for cookie in cookies { + waitGroup.enter() + dataStore.httpCookieStore.setCookie(cookie) { + waitGroup.leave() + } + } + + waitGroup.notify(queue: DispatchQueue.main) { + config.websiteDataStore = dataStore + completion(config) + } + } + } + +} diff --git a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift index 79514f6a..5c01e279 100644 --- a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift +++ b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/ConcertListViewController.swift @@ -175,10 +175,8 @@ extension ConcertListViewController: UICollectionViewDelegate { let viewController = concertDetailViewControllerFactory(self.viewModel.output.topConcerts[indexPath.row].id) self.navigationController?.pushViewController(viewController, animated: true) case .banner: - let vc = WebViewController() - self.navigationController?.pushViewController(vc, animated: true) -// guard let url = URL(string: "https://boolti.in/login") else { return } -// self.openSafari(with: url) + let viewController = WebViewController() + self.navigationController?.pushViewController(viewController, animated: true) case .bottomConcerts: let viewController = concertDetailViewControllerFactory(self.viewModel.output.bottomConcerts[indexPath.row].id) self.navigationController?.pushViewController(viewController, animated: true) diff --git a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift index 10347807..1c4935dc 100644 --- a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift +++ b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift @@ -1,53 +1,88 @@ import UIKit import WebKit + import SnapKit +import RxSwift + +// TODO: 재사용가능하게 변경하기 +final class WebViewController: UIViewController { + private let disposeBag = DisposeBag() -class WebViewController: UIViewController, WKUIDelegate { + private let navigationBar = BooltiNavigationBar(type: .backButton) - var webView: WKWebView! + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.tabBarController?.tabBar.isHidden = true + } override func viewDidLoad() { super.viewDidLoad() - let accessToken = UserDefaults.accessToken - let refreshToken = UserDefaults.refreshToken + self.configureSubviews() + self.bindUIComponent() + } + + private func bindUIComponent() { + self.navigationBar.didBackButtonTap() + .asDriver(onErrorJustReturn: ()) + .drive(with: self) { owner, _ in + owner.navigationController?.popViewController(animated: true) + } + .disposed(by: self.disposeBag) + } + + private func configureSubviews() { + self.view.addSubview(navigationBar) + + self.navigationBar.snp.makeConstraints { make in + make.top.equalToSuperview() + make.horizontalEdges.equalToSuperview() + } - // WKWebView 설정 - let webConfiguration = WKWebViewConfiguration() - webView = WKWebView(frame: view.bounds, configuration: webConfiguration) - webView.uiDelegate = self - view.addSubview(webView) + self.prepareWebConfiguration { [weak self] config in + guard let self = self, let config = config else { return } - // URL 설정 - let url = URL(string: "https://dotori.boolti.in/show/add")! + let webView = WKWebView(frame: .zero, configuration: config) + self.view.addSubview(webView) - // 쿠키 생성 및 설정 - let accessTokenCookie = HTTPCookie(properties: [ - .domain: url.host!, + webView.snp.makeConstraints { make in + make.top.equalTo(self.navigationBar.snp.bottom) + make.horizontalEdges.bottom.equalToSuperview() + } + + guard let url = URL(string: Environment.REGISTER_CONCERT_URL) else { return } + let request = URLRequest(url: url) + webView.load(request) + } + } + + private func prepareWebConfiguration(completion: @escaping (WKWebViewConfiguration?) -> Void) { + let accessToken = UserDefaults.accessToken + let refreshToken = UserDefaults.refreshToken + + guard let authCookie = HTTPCookie(properties: [ + .domain: ".boolti.in", .path: "/", .name: "x-access-token", .value: accessToken, .secure: "TRUE", - .expires: Date().addingTimeInterval(3600) // 1시간 후 만료 - ])! + ]) else { + return + } - let refreshTokenCookie = HTTPCookie(properties: [ - .domain: url.host!, + guard let uuidCookie = HTTPCookie(properties: [ + .domain: ".boolti.in", .path: "/", .name: "x-refresh-token", .value: refreshToken, .secure: "TRUE", - .expires: Date().addingTimeInterval(2592000) // 30일 후 만료 - ])! - - // 쿠키 설정 - webView.configuration.websiteDataStore.httpCookieStore.setCookie(accessTokenCookie) { - self.webView.configuration.websiteDataStore.httpCookieStore.setCookie(refreshTokenCookie) { - // 두 쿠키가 모두 설정된 후 URL 로드 - let request = URLRequest(url: url) - self.webView.load(request) - } + ]) else { + return + } + + WKWebViewConfiguration.includeCookie(cookies: [authCookie, uuidCookie]) { + completion($0) } } } diff --git a/Boolti/Boolti/Support/Enviroment.swift b/Boolti/Boolti/Support/Enviroment.swift index 53c5a5ca..c42754ad 100644 --- a/Boolti/Boolti/Support/Enviroment.swift +++ b/Boolti/Boolti/Support/Enviroment.swift @@ -15,6 +15,7 @@ enum Environment: String { enum Plist { static let baseURL = "BASE_URL" static let giftURL = "GIFT_URL" + static let registerConcertURL = "REGISTER_CONCERT_URL" static let kakaoNativeAppKey = "KAKAO_NATIVE_APP_KEY" static let tossPaymentsKey = "TOSS_PAYMENTS_KEY" } @@ -38,7 +39,14 @@ enum Environment: String { } return string }() - + + static let REGISTER_CONCERT_URL: String = { + guard let string = Environment.infoDictionary[Keys.Plist.registerConcertURL] as? String else { + fatalError("Register Concert URL not set in plist for this environment") + } + return string + }() + static let KAKAO_NATIVE_APP_KEY: String = { guard let string = Environment.infoDictionary[Keys.Plist.kakaoNativeAppKey] as? String else { fatalError("KAKAO_NATIVE_APP_KEY not set in plist for this environment") From d2785fc259d65e748b68d769ea9d1fd4b6dae0a5 Mon Sep 17 00:00:00 2001 From: longlivedrgn Date: Mon, 21 Oct 2024 02:19:03 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20WebView=20=EB=B0=B1=EA=B7=B8?= =?UTF-8?q?=EB=9D=BC=EC=9A=B4=EB=93=9C=20=EC=83=89=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/UILayer/Concert/ConcertList/WebViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift index 1c4935dc..e8a16e84 100644 --- a/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift +++ b/Boolti/Boolti/Sources/UILayer/Concert/ConcertList/WebViewController.swift @@ -33,6 +33,7 @@ final class WebViewController: UIViewController { } private func configureSubviews() { + self.view.backgroundColor = .white00 self.view.addSubview(navigationBar) self.navigationBar.snp.makeConstraints { make in