From 8029d9130a7effcd8f04a4f8cc33052cb90ff28d Mon Sep 17 00:00:00 2001 From: Ian Date: Thu, 30 Nov 2023 10:12:26 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[Feature]=20WKWebView=20=EC=9E=91=EC=84=B1,?= =?UTF-8?q?=20Configuration=20=EC=84=A4=EC=A0=95,=20PlaygroundToken=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Coordinator/Router/Router.swift | 8 ++ .../Sources/WebView/SOPTWebView.swift | 133 ++++++++++++++++++ .../WebView/WebViewConfiguration.swift | 31 ++++ .../WebView/WebViewNavigationBar.swift | 90 ++++++++++++ .../Coordinator/MainCoordinator.swift | 2 +- .../Sources/MainScene/Example/STWebView.swift | 125 ++++++++++++++++ 6 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift create mode 100644 SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewConfiguration.swift create mode 100644 SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift create mode 100644 SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift index d781ab1a..eb66c834 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift @@ -18,6 +18,7 @@ public protocol RouterProtocol: ViewControllable { func present(_ module: ViewControllable?, animated: Bool) func present(_ module: ViewControllable?, animated: Bool, modalPresentationSytle: UIModalPresentationStyle) func present(_ module: ViewControllable?, animated: Bool, completion: (() -> Void)?) + func presentSOPTWebView(url: String) func presentSafari(url: String) func push(_ module: ViewControllable?) @@ -111,6 +112,13 @@ final class Router: NSObject, RouterProtocol { self.rootController?.present(controller, animated: animated, completion: completion) } + public func presentSOPTWebView(url: String) { + let webView = SOPTWebView(startWith: URL(string: url)!) + webView.modalPresentationStyle = .fullScreen + UIApplication.getMostTopViewController()?.present(webView, animated: true) + } + + public func presentSafari(url: String) { let safariViewController = SFSafariViewController(url: URL(string: url)!) safariViewController.playgroundStyle() diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift new file mode 100644 index 00000000..bd2280eb --- /dev/null +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift @@ -0,0 +1,133 @@ +// +// SOPTWebView.swift +// BaseFeatureDependency +// +// Created by Ian on 11/30/23. +// Copyright © 2023 SOPT-iOS. All rights reserved. +// + +import Core +import DSKit + +import UIKit +import WebKit + +import SnapKit + +public final class SOPTWebView: UIViewController { + private enum Metric { + static let navigationBarHeight = 44.f + } + + private lazy var navigationBar = WebViewNavigationBar(frame: self.view.frame) + private let wkwebView: WKWebView + + // MARK: Variables + private let cancelbag = CancelBag() + private var barrier = false + + public init( + config: WebViewConfig = WebViewConfig(), + startWith url: URL + ) { + let configuration = WKWebViewConfiguration().then { + $0.allowsInlineMediaPlayback = config.allowsInlineMediaPlayback + $0.mediaTypesRequiringUserActionForPlayback = config.mediaTypesRequiringUserActionForPlayback + } + + self.wkwebView = WKWebView(frame: .zero, configuration: configuration).then { + $0.allowsBackForwardNavigationGestures = config.allowsBackForwardNavigationGestures + } + + super.init(nibName: nil, bundle: nil) + + DispatchQueue.main.async { + let request = URLRequest(url: url) + self.wkwebView.load(request) + } + } + + public required init?(coder: NSCoder) { + fatalError("coder initializer doesn't implemented.") + } + + public override func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = DSKitAsset.Colors.black100.color + + self.wkwebView.scrollView.delegate = self + self.wkwebView.navigationDelegate = self + self.wkwebView.uiDelegate = self + + self.initializeViews() + self.setupConstraints() + self.setupNavigationButtonActions() + } +} + +extension SOPTWebView { + private func initializeViews() { + self.view.addSubviews(self.navigationBar, self.wkwebView) + } + + private func setupConstraints() { + self.navigationBar.snp.makeConstraints { + $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(Metric.navigationBarHeight) + } + + self.wkwebView.snp.makeConstraints { + $0.top.equalTo(self.navigationBar.snp.bottom) + $0.leading.trailing.bottom.equalToSuperview() + } + } + + private func setupNavigationButtonActions() { + self.navigationBar + .signalForClickLeftButton() + .sink { [weak self] _ in + self?.dismiss(animated: true) + }.store(in: self.cancelbag) + + self.navigationBar + .signalForClickRightButton() + .sink { [weak self] _ in + self?.wkwebView.reload() + }.store(in: self.cancelbag) + } +} + +extension SOPTWebView: WKNavigationDelegate { + public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + guard !self.barrier else { return } + + self.barrier = true + self.wkwebView.evaluateJavaScript( + "localStorage.setItem(\"serviceAccessToken\", \"\(UserDefaultKeyList.Auth.playgroundToken!)\")" + ) + self.wkwebView.reload() + } +} + +extension SOPTWebView: WKUIDelegate { + public func webView( + _ webView: WKWebView, + createWebViewWith configuration: WKWebViewConfiguration, + for navigationAction: WKNavigationAction, + windowFeatures: WKWindowFeatures + ) -> WKWebView? { + if navigationAction.targetFrame == nil { + webView.load(navigationAction.request) + } + + return nil + } +} + +extension SOPTWebView: UIScrollViewDelegate { + public func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) { + scrollView.pinchGestureRecognizer?.isEnabled = false + } +} diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewConfiguration.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewConfiguration.swift new file mode 100644 index 00000000..224a3dde --- /dev/null +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewConfiguration.swift @@ -0,0 +1,31 @@ +// +// WebViewConfiguration.swift +// BaseFeatureDependency +// +// Created by Ian on 11/30/23. +// Copyright © 2023 SOPT-iOS. All rights reserved. +// + +import WebKit + +public struct WebViewConfig { + let javaScriptEnabled: Bool + let allowsBackForwardNavigationGestures: Bool + let allowsInlineMediaPlayback: Bool + let mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes + let isScrollEnabled: Bool + + public init( + javaScriptEnabled: Bool = true, + allowsBackForwardNavigationGestures: Bool = true, + allowsInlineMediaPlayback: Bool = true, + mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes = [], + isScrollEnabled: Bool = true + ) { + self.javaScriptEnabled = javaScriptEnabled + self.allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures + self.allowsInlineMediaPlayback = allowsInlineMediaPlayback + self.mediaTypesRequiringUserActionForPlayback = mediaTypesRequiringUserActionForPlayback + self.isScrollEnabled = isScrollEnabled + } +} diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift new file mode 100644 index 00000000..76839d95 --- /dev/null +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift @@ -0,0 +1,90 @@ +// +// WebViewNavigationBar.swift +// BaseFeatureDependency +// +// Created by Ian on 11/30/23. +// Copyright © 2023 SOPT-iOS. All rights reserved. +// + +import Core +import DSKit + +import UIKit + +internal final class WebViewNavigationBar: UIView { + private enum Metric { + static let navigationBarHeight = 44.f + static let buttonLength = 44.f + static let contentStackViewSpacing = 0.f + static let contentStackViewLeadingTrailing = 8.f + static let securityImageViewLength = 16.f + } + + // MARK: LeftButton + private let leftButton = UIButton().then { + let image: UIImage = DSKitAsset.Assets.icClose.image.withRenderingMode(.alwaysTemplate) + $0.setImage(image, for: .normal) + $0.tintColor = .white + } + + private let contentSpacingView = UIView().then { + $0.setContentHuggingPriority(.defaultLow, for: .horizontal) + } + + // MARK: RightButton + private let rightButton = UIButton().then { + $0.setImage(UIImage(systemName: "arrow.clockwise"), for: .normal) + $0.tintColor = .white + } + + private lazy var contentStackView = UIStackView( + arrangedSubviews: [ + self.leftButton, + self.contentSpacingView, + self.rightButton + ] + ).then { + $0.axis = .horizontal + $0.spacing = Metric.contentStackViewSpacing + $0.distribution = .equalCentering + } + + override init(frame: CGRect) { + super.init(frame: frame) + + self.initializeViews() + self.setupConstraints() + } + + required init(coder: NSCoder) { + fatalError() + } +} + +// MARK: - Private functions +extension WebViewNavigationBar { + private func initializeViews() { + self.addSubview(self.contentStackView) + } + + private func setupConstraints() { + self.contentStackView.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(Metric.securityImageViewLength) + $0.top.bottom.equalToSuperview() + $0.height.equalTo(Metric.navigationBarHeight) + } + self.leftButton.snp.makeConstraints { $0.width.height.equalTo(Metric.buttonLength) } + self.rightButton.snp.makeConstraints { $0.width.height.equalTo(Metric.buttonLength) } + } +} + +// MARK: - Public functions +extension WebViewNavigationBar { + public func signalForClickLeftButton() -> Driver { + self.leftButton.publisher(for: .touchUpInside).mapVoid().asDriver() + } + + public func signalForClickRightButton() -> Driver { + self.rightButton.publisher(for: .touchUpInside).mapVoid().asDriver() + } +} diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift index 7de5c129..eff140d9 100644 --- a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift +++ b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift @@ -45,7 +45,7 @@ final class MainCoordinator: DefaultMainCoordinator { self?.requestCoordinating?(.myPage(userType)) } main.vm.onSafari = { [weak self] url in - self?.router.presentSafari(url: url) + self?.router.presentSOPTWebView(url: url) } main.vm.onNeedSignIn = { [weak self] in self?.requestCoordinating?(.signIn) diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift new file mode 100644 index 00000000..de00ff7a --- /dev/null +++ b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift @@ -0,0 +1,125 @@ +//// +//// STWebView.swift +//// MainFeatureTests +//// +//// Created by Ian on 10/28/23. +//// Copyright © 2023 SOPT-iOS. All rights reserved. +//// +// +//import Core +//import DSKit +//import BaseFeatureDependency +// +//import UIKit +//import WebKit +// +//import SnapKit +// +//public struct WebViewConfig { +// let javaScriptEnabled: Bool +// let allowsBackForwardNavigationGestures: Bool +// let allowsInlineMediaPlayback: Bool +// let mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes +// let isScrollEnabled: Bool +// +// init( +// javaScriptEnabled: Bool = true, +// allowsBackForwardNavigationGestures: Bool = true, +// allowsInlineMediaPlayback: Bool = true, +// mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes = [], +// isScrollEnabled: Bool = true +// ) { +// self.javaScriptEnabled = javaScriptEnabled +// self.allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures +// self.allowsInlineMediaPlayback = allowsInlineMediaPlayback +// self.mediaTypesRequiringUserActionForPlayback = mediaTypesRequiringUserActionForPlayback +// self.isScrollEnabled = isScrollEnabled +// } +//} +// +//public final class STWebView: UIViewController { +// private lazy var navigationBar = OPNavigationBar( +// self, +// type: .oneLeftButton, +// backgroundColor: DSKitAsset.Colors.black100.color, +// ignoreLeftButtonAction: true +// ) +// .addMiddleLabel(title: I18N.MyPage.navigationTitle) +// +// private let wkwebView: WKWebView +// +// init( +// config: WebViewConfig = WebViewConfig(), +// startWith url: URL +// ) { +// let configuration = WKWebViewConfiguration().then { +// $0.allowsInlineMediaPlayback = config.allowsInlineMediaPlayback +// $0.mediaTypesRequiringUserActionForPlayback = config.mediaTypesRequiringUserActionForPlayback +// } +// +// self.wkwebView = WKWebView(frame: .zero, configuration: configuration) +// +// super.init(nibName: nil, bundle: nil) +// +// DispatchQueue.main.async { +// let request = URLRequest(url: url) +// self.wkwebView.load(request) +// } +// } +// +// public required init?(coder: NSCoder) { +// fatalError("coder initializer doesn't implemented.") +// } +// +// public override func loadView() { +// super.loadView() +// +// } +// +// public override func viewDidLoad() { +// super.viewDidLoad() +// +// self.wkwebView.navigationDelegate = self +// self.wkwebView.uiDelegate = self +// +// self.initializeViews() +// self.setupConstraints() +// } +//} +// +//extension STWebView { +// private func initializeViews() { +// self.view.addSubviews(self.navigationBar, self.wkwebView) +// } +// +// private func setupConstraints() { +// self.navigationBar.snp.makeConstraints { +// $0.top.equalToSuperview() +// $0.leading.trailing.equalToSuperview() +// $0.height.equalTo(44.f) +// } +// +// self.wkwebView.snp.makeConstraints { +// $0.top.equalTo(self.navigationBar.snp.bottom) +// $0.leading.trailing.bottom.equalToSuperview() +// } +// } +//} +// +//extension STWebView: WKNavigationDelegate { +//} +// +//extension STWebView: WKUIDelegate { +// public func webView( +// _ webView: WKWebView, +// createWebViewWith configuration: WKWebViewConfiguration, +// for navigationAction: WKNavigationAction, +// windowFeatures: WKWindowFeatures +// ) -> WKWebView? { +// if navigationAction.targetFrame == nil { +// webView.load(navigationAction.request) +// } +// +// return nil +// } +//} From a5efc6517303829166343353cbea36839d9c9728 Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 1 Dec 2023 00:45:35 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[Feature]=20WebView=20NavigationPush?= =?UTF-8?q?=EB=A1=9C=20=EB=8F=99=EC=9E=91=ED=95=98=EB=8F=84=EB=A1=9D,=20X?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20=EB=8C=80=EC=8B=A0=20=EB=8B=AB=EA=B8=B0?= =?UTF-8?q?=20=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/Sources/Literals/StringLiterals.swift | 4 ++++ .../Sources/Coordinator/Router/Router.swift | 12 ++++++------ .../Sources/WebView/SOPTWebView.swift | 2 +- .../Sources/WebView/WebViewNavigationBar.swift | 3 +-- .../MainScene/Coordinator/MainCoordinator.swift | 2 +- .../RootFeature/Sources/ApplicationCoordinator.swift | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/SOPT-iOS/Projects/Core/Sources/Literals/StringLiterals.swift b/SOPT-iOS/Projects/Core/Sources/Literals/StringLiterals.swift index 51908648..6b30a03e 100644 --- a/SOPT-iOS/Projects/Core/Sources/Literals/StringLiterals.swift +++ b/SOPT-iOS/Projects/Core/Sources/Literals/StringLiterals.swift @@ -316,6 +316,10 @@ public struct I18N { public static let expiredLinkDesription = "해당 링크의 유효기간이 만료되어\n더 이상 내용을 확인할 수 없어요." public static let updateAlertButtonTitle = "확인" } + + public struct WebView { + public static let close = "닫기" + } } extension I18N { diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift index eb66c834..8d2cf7c3 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Coordinator/Router/Router.swift @@ -18,7 +18,7 @@ public protocol RouterProtocol: ViewControllable { func present(_ module: ViewControllable?, animated: Bool) func present(_ module: ViewControllable?, animated: Bool, modalPresentationSytle: UIModalPresentationStyle) func present(_ module: ViewControllable?, animated: Bool, completion: (() -> Void)?) - func presentSOPTWebView(url: String) + func pushSOPTWebView(url: String) func presentSafari(url: String) func push(_ module: ViewControllable?) @@ -112,12 +112,12 @@ final class Router: NSObject, RouterProtocol { self.rootController?.present(controller, animated: animated, completion: completion) } - public func presentSOPTWebView(url: String) { - let webView = SOPTWebView(startWith: URL(string: url)!) - webView.modalPresentationStyle = .fullScreen - UIApplication.getMostTopViewController()?.present(webView, animated: true) + public func pushSOPTWebView(url: String) { + guard let url = URL(string: url) else { return } + + let webView = SOPTWebView(startWith: url) + self.rootController?.pushViewController(webView, animated: true) } - public func presentSafari(url: String) { let safariViewController = SFSafariViewController(url: URL(string: url)!) diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift index bd2280eb..280e6cde 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift @@ -88,7 +88,7 @@ extension SOPTWebView { self.navigationBar .signalForClickLeftButton() .sink { [weak self] _ in - self?.dismiss(animated: true) + self?.navigationController?.popViewController(animated: true) }.store(in: self.cancelbag) self.navigationBar diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift index 76839d95..ee8ab701 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift @@ -22,8 +22,7 @@ internal final class WebViewNavigationBar: UIView { // MARK: LeftButton private let leftButton = UIButton().then { - let image: UIImage = DSKitAsset.Assets.icClose.image.withRenderingMode(.alwaysTemplate) - $0.setImage(image, for: .normal) + $0.setTitle(I18N.WebView.close, for: .normal) $0.tintColor = .white } diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift index eff140d9..165b320b 100644 --- a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift +++ b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Coordinator/MainCoordinator.swift @@ -45,7 +45,7 @@ final class MainCoordinator: DefaultMainCoordinator { self?.requestCoordinating?(.myPage(userType)) } main.vm.onSafari = { [weak self] url in - self?.router.presentSOPTWebView(url: url) + self?.router.pushSOPTWebView(url: url) } main.vm.onNeedSignIn = { [weak self] in self?.requestCoordinating?(.signIn) diff --git a/SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator.swift b/SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator.swift index 00bece40..ce749a91 100644 --- a/SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator.swift +++ b/SOPT-iOS/Projects/Features/RootFeature/Sources/ApplicationCoordinator.swift @@ -89,7 +89,7 @@ extension ApplicationCoordinator { private func handleWebLink(webLink: String) { self.router.dismissModule(animated: false) - self.router.presentSafari(url: webLink) + self.router.pushSOPTWebView(url: webLink) } private func handleNotificationLinkError(error: NotificationLinkError) { From ebde4ce51aa529e37ea4f1ac4aeb4354208021a8 Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 1 Dec 2023 00:54:34 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[Feature]=20App=20EnterForeground=20?= =?UTF-8?q?=EC=8B=9C=EC=A0=90=EC=97=90=20token=EC=9D=B4=20=EC=9E=88?= =?UTF-8?q?=EB=8B=A4=EB=A9=B4=20=ED=86=A0=ED=81=B0=EC=9D=84=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/Sources/AppLifecycleAdapter.swift | 53 +++++++++++++++++++ .../WebView/WebViewNavigationBar.swift | 1 - .../Sources/Application/AppDelegate.swift | 7 ++- 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 SOPT-iOS/Projects/Core/Sources/AppLifecycleAdapter.swift diff --git a/SOPT-iOS/Projects/Core/Sources/AppLifecycleAdapter.swift b/SOPT-iOS/Projects/Core/Sources/AppLifecycleAdapter.swift new file mode 100644 index 00000000..fdef1c8e --- /dev/null +++ b/SOPT-iOS/Projects/Core/Sources/AppLifecycleAdapter.swift @@ -0,0 +1,53 @@ +// +// AppLifecycleAdapter.swift +// SOPT-iOS +// +// Created by Ian on 12/1/23. +// Copyright © 2023 SOPT-iOS. All rights reserved. +// + +import Core +import Networks + +import UIKit + +final public class AppLifecycleAdapter { + private let authService: AuthService + private let cancelBag = CancelBag() + + init() { + self.authService = DefaultAuthService() + } +} + +// MARK: - Public functions +extension AppLifecycleAdapter { + public func prepare() { + self.onWillEnterForeground() + self.onWillEnterBackground() + } +} + +// MARK: - Lifecycle Usecases +extension AppLifecycleAdapter { + private func onWillEnterForeground() { + NotificationCenter.default + .publisher(for: UIApplication.willEnterForegroundNotification) + .subscribe(on: DispatchQueue.global()) + .receive(on: DispatchQueue.main) + .sink(receiveValue: { [weak self] _ in + self?.reissureTokens() + }).store(in: self.cancelBag) + } + + private func onWillEnterBackground() { } +} + +// MARK: - Private functions +extension AppLifecycleAdapter { + private func reissureTokens() { + guard UserDefaultKeyList.Auth.appAccessToken != nil else { return } + + self.authService.reissuance { _ in } + } +} diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift index ee8ab701..904b5387 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift @@ -7,7 +7,6 @@ // import Core -import DSKit import UIKit diff --git a/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift b/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift index 6d24dc82..9cc36a19 100644 --- a/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift +++ b/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift @@ -15,9 +15,14 @@ import Core @main class AppDelegate: UIResponder, UIApplicationDelegate { + private var appLifecycleAdapter = AppLifecycleAdapter() + func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { configureSentry() registerDependencies() + + appLifecycleAdapter.prepare() + application.registerForRemoteNotifications() return true } @@ -37,7 +42,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application( _ application: UIApplication, didDiscardSceneSessions sceneSessions: Set - ) {} + ) { } } // MARK: - Sentry & FCM From 75859a89ad9649c53510b5ef3f07582a6836f981 Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 1 Dec 2023 01:39:43 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[Feature]=20MDS=20=EC=BB=AC=EB=9F=AC?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=B0=98=EC=98=81=20:=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=ED=8E=98=EC=9D=B4=EC=A7=80=EC=99=80=20?= =?UTF-8?q?=ED=95=98=EC=9C=84=20=EB=B7=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/MyPageSectionListItemView.swift | 3 +- .../View/MyPageViewSectionGroup.swift | 4 +- .../Sources/Alert/AlertVCTheme.swift | 4 +- .../Sources/MainScene/Example/STWebView.swift | 125 ------------------ .../AppComponents/AppCustomButton.swift | 4 +- 5 files changed, 7 insertions(+), 133 deletions(-) delete mode 100644 SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift diff --git a/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageSectionListItemView.swift b/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageSectionListItemView.swift index 2b70f9cf..d114fd59 100644 --- a/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageSectionListItemView.swift +++ b/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageSectionListItemView.swift @@ -42,7 +42,6 @@ public final class MyPageSectionListItemView: UIView { private lazy var contentStackView = UIStackView().then { $0.axis = .horizontal $0.distribution = .fill - $0.backgroundColor = DSKitAsset.Colors.black80.color } private let titleLabel = UILabel().then { @@ -85,7 +84,7 @@ public final class MyPageSectionListItemView: UIView { self.rightSwitch.isHidden = false } - self.backgroundColor = DSKitAsset.Colors.black80.color + self.backgroundColor = DSKitAsset.Colors.gray900.color self.setupLayouts() self.setupConstraint() diff --git a/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageViewSectionGroup.swift b/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageViewSectionGroup.swift index 9683ca49..38f13a0a 100644 --- a/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageViewSectionGroup.swift +++ b/SOPT-iOS/Projects/Features/AppMyPageFeature/Sources/AppMypageScene/View/MyPageViewSectionGroup.swift @@ -28,7 +28,7 @@ public final class MypageSectionGroupView: UIView { } private let headerView = UIView().then { - $0.backgroundColor = DSKitAsset.Colors.black80.color + $0.backgroundColor = DSKitAsset.Colors.gray900.color $0.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] $0.layer.cornerRadius = Metric.sectionGroupCornerRadius } @@ -44,7 +44,7 @@ public final class MypageSectionGroupView: UIView { } private let bottomInsetView = UIView().then { - $0.backgroundColor = DSKitAsset.Colors.black80.color + $0.backgroundColor = DSKitAsset.Colors.gray900.color $0.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] $0.layer.cornerRadius = Metric.sectionGroupCornerRadius } diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Alert/AlertVCTheme.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Alert/AlertVCTheme.swift index 5468c028..93da5b42 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Alert/AlertVCTheme.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/Alert/AlertVCTheme.swift @@ -21,7 +21,7 @@ extension AlertVC.AlertTheme { var backgroundColor: UIColor { switch self { case .main: - return DSKitAsset.Colors.black60.color + return DSKitAsset.Colors.gray700.color case .soptamp: return DSKitAsset.Colors.white.color } @@ -66,7 +66,7 @@ extension AlertVC.AlertTheme { func cancelButtonColor(isNetworkErr: Bool) -> UIColor { switch self { case .main: - return isNetworkErr ? DSKitAsset.Colors.white100.color : DSKitAsset.Colors.black40.color + return isNetworkErr ? DSKitAsset.Colors.white100.color : DSKitAsset.Colors.gray600.color case .soptamp: return isNetworkErr ? DSKitAsset.Colors.soptampError200.color : DSKitAsset.Colors.soptampGray300.color } diff --git a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift b/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift deleted file mode 100644 index de00ff7a..00000000 --- a/SOPT-iOS/Projects/Features/MainFeature/Sources/MainScene/Example/STWebView.swift +++ /dev/null @@ -1,125 +0,0 @@ -//// -//// STWebView.swift -//// MainFeatureTests -//// -//// Created by Ian on 10/28/23. -//// Copyright © 2023 SOPT-iOS. All rights reserved. -//// -// -//import Core -//import DSKit -//import BaseFeatureDependency -// -//import UIKit -//import WebKit -// -//import SnapKit -// -//public struct WebViewConfig { -// let javaScriptEnabled: Bool -// let allowsBackForwardNavigationGestures: Bool -// let allowsInlineMediaPlayback: Bool -// let mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes -// let isScrollEnabled: Bool -// -// init( -// javaScriptEnabled: Bool = true, -// allowsBackForwardNavigationGestures: Bool = true, -// allowsInlineMediaPlayback: Bool = true, -// mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes = [], -// isScrollEnabled: Bool = true -// ) { -// self.javaScriptEnabled = javaScriptEnabled -// self.allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures -// self.allowsInlineMediaPlayback = allowsInlineMediaPlayback -// self.mediaTypesRequiringUserActionForPlayback = mediaTypesRequiringUserActionForPlayback -// self.isScrollEnabled = isScrollEnabled -// } -//} -// -//public final class STWebView: UIViewController { -// private lazy var navigationBar = OPNavigationBar( -// self, -// type: .oneLeftButton, -// backgroundColor: DSKitAsset.Colors.black100.color, -// ignoreLeftButtonAction: true -// ) -// .addMiddleLabel(title: I18N.MyPage.navigationTitle) -// -// private let wkwebView: WKWebView -// -// init( -// config: WebViewConfig = WebViewConfig(), -// startWith url: URL -// ) { -// let configuration = WKWebViewConfiguration().then { -// $0.allowsInlineMediaPlayback = config.allowsInlineMediaPlayback -// $0.mediaTypesRequiringUserActionForPlayback = config.mediaTypesRequiringUserActionForPlayback -// } -// -// self.wkwebView = WKWebView(frame: .zero, configuration: configuration) -// -// super.init(nibName: nil, bundle: nil) -// -// DispatchQueue.main.async { -// let request = URLRequest(url: url) -// self.wkwebView.load(request) -// } -// } -// -// public required init?(coder: NSCoder) { -// fatalError("coder initializer doesn't implemented.") -// } -// -// public override func loadView() { -// super.loadView() -// -// } -// -// public override func viewDidLoad() { -// super.viewDidLoad() -// -// self.wkwebView.navigationDelegate = self -// self.wkwebView.uiDelegate = self -// -// self.initializeViews() -// self.setupConstraints() -// } -//} -// -//extension STWebView { -// private func initializeViews() { -// self.view.addSubviews(self.navigationBar, self.wkwebView) -// } -// -// private func setupConstraints() { -// self.navigationBar.snp.makeConstraints { -// $0.top.equalToSuperview() -// $0.leading.trailing.equalToSuperview() -// $0.height.equalTo(44.f) -// } -// -// self.wkwebView.snp.makeConstraints { -// $0.top.equalTo(self.navigationBar.snp.bottom) -// $0.leading.trailing.bottom.equalToSuperview() -// } -// } -//} -// -//extension STWebView: WKNavigationDelegate { -//} -// -//extension STWebView: WKUIDelegate { -// public func webView( -// _ webView: WKWebView, -// createWebViewWith configuration: WKWebViewConfiguration, -// for navigationAction: WKNavigationAction, -// windowFeatures: WKWindowFeatures -// ) -> WKWebView? { -// if navigationAction.targetFrame == nil { -// webView.load(navigationAction.request) -// } -// -// return nil -// } -//} diff --git a/SOPT-iOS/Projects/Modules/DSKit/Sources/Components/AppComponents/AppCustomButton.swift b/SOPT-iOS/Projects/Modules/DSKit/Sources/Components/AppComponents/AppCustomButton.swift index 48439607..1bdfbbff 100644 --- a/SOPT-iOS/Projects/Modules/DSKit/Sources/Components/AppComponents/AppCustomButton.swift +++ b/SOPT-iOS/Projects/Modules/DSKit/Sources/Components/AppComponents/AppCustomButton.swift @@ -46,7 +46,7 @@ extension AppCustomButton { @discardableResult public func setColor( bgColor: UIColor = DSKitAsset.Colors.white100.color, - disableColor: UIColor = DSKitAsset.Colors.black40.color, + disableColor: UIColor = DSKitAsset.Colors.gray600.color, disabledTextColor: UIColor = DSKitAsset.Colors.gray60.color, enabledTextColor: UIColor = DSKitAsset.Colors.black100.color ) -> Self { @@ -76,7 +76,7 @@ extension AppCustomButton { self.layer.cornerRadius = 10 self.setBackgroundColor(DSKitAsset.Colors.white100.color, for: .normal) - self.setBackgroundColor(DSKitAsset.Colors.black40.color, for: .disabled) + self.setBackgroundColor(DSKitAsset.Colors.gray600.color, for: .disabled) self.setAttributedTitle( NSAttributedString( string: title, From d5932b871eb25e14445357d01118c8c06ce80432 Mon Sep 17 00:00:00 2001 From: Ian Date: Sat, 2 Dec 2023 15:29:17 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[Feature]=20NavigationBar=20Left,=20Right?= =?UTF-8?q?=EC=97=90=20=EA=B0=81=EA=B0=81=20historyBack,=20=EB=82=98?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=20Action=20=EB=A7=81=ED=81=AC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/WebView/SOPTWebView.swift | 9 +++++++-- .../Sources/WebView/WebViewNavigationBar.swift | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift index 280e6cde..0b4c5dcd 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/SOPTWebView.swift @@ -88,13 +88,18 @@ extension SOPTWebView { self.navigationBar .signalForClickLeftButton() .sink { [weak self] _ in - self?.navigationController?.popViewController(animated: true) + guard self?.wkwebView.canGoBack == true else { + self?.navigationController?.popViewController(animated: true) + return + } + + self?.wkwebView.goBack() }.store(in: self.cancelbag) self.navigationBar .signalForClickRightButton() .sink { [weak self] _ in - self?.wkwebView.reload() + self?.navigationController?.popViewController(animated: true) }.store(in: self.cancelbag) } } diff --git a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift index 904b5387..07c3e9f9 100644 --- a/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/WebView/WebViewNavigationBar.swift @@ -7,6 +7,7 @@ // import Core +import DSKit import UIKit @@ -21,7 +22,8 @@ internal final class WebViewNavigationBar: UIView { // MARK: LeftButton private let leftButton = UIButton().then { - $0.setTitle(I18N.WebView.close, for: .normal) + let image = UIImage(asset: DSKitAsset.Assets.btnArrowLeft)?.withRenderingMode(.alwaysTemplate) + $0.setImage(image, for: .normal) $0.tintColor = .white } @@ -31,7 +33,7 @@ internal final class WebViewNavigationBar: UIView { // MARK: RightButton private let rightButton = UIButton().then { - $0.setImage(UIImage(systemName: "arrow.clockwise"), for: .normal) + $0.setTitle(I18N.WebView.close, for: .normal) $0.tintColor = .white } From ffe38269eb2ad5162b2f6edb66da9ba324ae7270 Mon Sep 17 00:00:00 2001 From: Ian Date: Sat, 2 Dec 2023 15:49:28 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[Feature]=20AppLifecycleAdapter=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20:=20BaseFeatureDependency=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/AppLifecycleAdapter}/AppLifecycleAdapter.swift | 7 ++++--- .../SOPT-iOS/Sources/Application/AppDelegate.swift | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) rename SOPT-iOS/Projects/{Core/Sources => Features/BaseFeatureDependency/Sources/AppLifecycleAdapter}/AppLifecycleAdapter.swift (93%) diff --git a/SOPT-iOS/Projects/Core/Sources/AppLifecycleAdapter.swift b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/AppLifecycleAdapter/AppLifecycleAdapter.swift similarity index 93% rename from SOPT-iOS/Projects/Core/Sources/AppLifecycleAdapter.swift rename to SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/AppLifecycleAdapter/AppLifecycleAdapter.swift index fdef1c8e..f3aece38 100644 --- a/SOPT-iOS/Projects/Core/Sources/AppLifecycleAdapter.swift +++ b/SOPT-iOS/Projects/Features/BaseFeatureDependency/Sources/AppLifecycleAdapter/AppLifecycleAdapter.swift @@ -1,8 +1,8 @@ // // AppLifecycleAdapter.swift -// SOPT-iOS +// BaseFeatureDependency // -// Created by Ian on 12/1/23. +// Created by Ian on 12/2/23. // Copyright © 2023 SOPT-iOS. All rights reserved. // @@ -15,7 +15,7 @@ final public class AppLifecycleAdapter { private let authService: AuthService private let cancelBag = CancelBag() - init() { + public init() { self.authService = DefaultAuthService() } } @@ -51,3 +51,4 @@ extension AppLifecycleAdapter { self.authService.reissuance { _ in } } } + diff --git a/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift b/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift index 9cc36a19..357fc3a4 100644 --- a/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift +++ b/SOPT-iOS/Projects/SOPT-iOS/Sources/Application/AppDelegate.swift @@ -9,8 +9,9 @@ import UIKit import Sentry -import Networks +import BaseFeatureDependency import Core +import Networks @main class AppDelegate: UIResponder, UIApplicationDelegate { From 3e49968f6df76992cc053fb5c7826642f445f9de Mon Sep 17 00:00:00 2001 From: Ian Date: Sat, 2 Dec 2023 16:00:44 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[Improvement]=20Demo=20Target=EC=97=90?= =?UTF-8?q?=EB=8F=84=20lifecycleAdapter=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SOPT-iOS/Projects/Demo/Sources/Application/AppDelegate.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SOPT-iOS/Projects/Demo/Sources/Application/AppDelegate.swift b/SOPT-iOS/Projects/Demo/Sources/Application/AppDelegate.swift index 387d9c9c..742e7fd8 100644 --- a/SOPT-iOS/Projects/Demo/Sources/Application/AppDelegate.swift +++ b/SOPT-iOS/Projects/Demo/Sources/Application/AppDelegate.swift @@ -11,14 +11,19 @@ import Sentry import Networks import Core +import BaseFeatureDependency @main class AppDelegate: UIResponder, UIApplicationDelegate { + private var appLifecycleAdapter = AppLifecycleAdapter() + func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { configureSentry() registerDependencies() application.registerForRemoteNotifications() + + appLifecycleAdapter.prepare() return true }