From e0a1b799023e9d24caf1d96e413f55d1e7182ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=8A=E8=97=A4=E8=81=96=E5=A4=9C?= Date: Tue, 19 Aug 2025 09:20:40 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E3=82=BB=E3=83=83=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3API=E5=AE=8C=E4=BA=86=E3=81=BE=E3=81=A7=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=82=A2=E3=82=A6=E3=83=88=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E5=BE=85=E3=81=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AgileWorks/AgileWorks/App/AppShared.swift | 25 +++ .../AgileWorks/App/RootViewController.swift | 145 +++++++++++++----- .../WebView/View/WebViewController.swift | 108 ++++++------- 3 files changed, 189 insertions(+), 89 deletions(-) diff --git a/AgileWorks/AgileWorks/App/AppShared.swift b/AgileWorks/AgileWorks/App/AppShared.swift index da05954..302ecf5 100644 --- a/AgileWorks/AgileWorks/App/AppShared.swift +++ b/AgileWorks/AgileWorks/App/AppShared.swift @@ -14,6 +14,9 @@ class AppShared { private var approvalRefreshIsRequired = false private var approvalDetailRefreshIsRequired = false private var isOnline = true + + private var loadingSessionAPICount = 0 + private var isLoadingLogout = false /// 次回の承認リスト (書類一覧も含む) 表示時に更新するよう要求する。 func requstApprovalRefresh() { @@ -44,6 +47,28 @@ class AppShared { func getOnlineStatus() -> Bool { return self.isOnline } + + // 読み込み中のセッションAPIの数を追加 + func addLoadingSessionAPICount() { + self.loadingSessionAPICount += 1 + } + + // 読み込み中のセッションAPIの数を減少 + func substractLoadingSessionAPICount() { + self.loadingSessionAPICount -= 1 + } + // 読み込み中のセッションAPIの数を取得 + func getLoadingSessionAPICount() -> Int { + return self.loadingSessionAPICount + } + // ログアウト処理の実行状況を設定 + func setLodingLogout(isLoading: Bool) { + self.isLoadingLogout = isLoading + } + // ログアウト処理の実行状況を取得 + func getLoadingLogout() -> Bool { + return self.isLoadingLogout + } } func appVersionLabelText() -> String { diff --git a/AgileWorks/AgileWorks/App/RootViewController.swift b/AgileWorks/AgileWorks/App/RootViewController.swift index 3aa2363..938152a 100644 --- a/AgileWorks/AgileWorks/App/RootViewController.swift +++ b/AgileWorks/AgileWorks/App/RootViewController.swift @@ -11,6 +11,7 @@ import WidgetKit class RootViewController: UIViewController { private var current: UIViewController + private var window: UIWindow? var linkType: LinkType? { didSet { @@ -54,40 +55,105 @@ extension RootViewController { current = new } + // ログアウト中のロード画面 + func showLogoutLoadingScreen() { + guard window == nil else {return} + + let loadingVC = UIViewController() + loadingVC.view.backgroundColor = UIColor(white: 0, alpha: 0.5) + + let indicator = UIActivityIndicatorView(style: .large) + indicator.center = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY - 40) + indicator.startAnimating() + loadingVC.view.addSubview(indicator) + + let newWindow = UIWindow(frame: UIScreen.main.bounds) + newWindow.windowLevel = UIWindow.Level.alert + 1 + newWindow.rootViewController = loadingVC + newWindow.makeKeyAndVisible() + + self.window = newWindow + } + + func hideLogoutLoadingScreen() { + window?.isHidden = true + window = nil + } + + private func waitForLoadingCompletion(completion: @escaping () -> Void) { + let checkInterval: TimeInterval = 0.5 + let startTime = Date() + let timeout: TimeInterval = 60 + + func checkLoadingCompletion() { + if AppShared().getLoadingSessionAPICount() == 0 { + completion() + } else if Date().timeIntervalSince(startTime) >= timeout { + print("セッション情報取得APIの完了待ちタイムアウト") + completion() + } else { + DispatchQueue.main.asyncAfter(deadline: .now() + checkInterval) { + checkLoadingCompletion() + } + } + } + checkLoadingCompletion() + } + // ログアウトアラート デフォルトボタンアクション生成 func logoutDefaultAction(serverRemove: Bool, title: String) -> UIAlertAction { let defaultAction = UIAlertAction(title: title, style: .default) { _ in - self.logout { result in - switch result { - case .success: - break - case .failure(let error): - log.e(error) + self.showLogoutLoadingScreen() + self.waitForLoadingCompletion { + var hasCompletedLogout = false + let timeout = DispatchWorkItem { + if !hasCompletedLogout { + hasCompletedLogout = true + proceedAfterLogout(success: true, serverRemove: serverRemove) + } } - var serverList = UserDefaultsDataStore().readServerList() - let removeServer = serverList.first - self.deleteServerInfo(serverNumber: removeServer) - - if serverRemove { - UserDefaultsDataStore().removeServer() + DispatchQueue.main.asyncAfter(deadline: .now() + 30, execute: timeout) + self.logout { result in + guard !hasCompletedLogout else { return } + hasCompletedLogout = true + timeout.cancel() + switch result { + case .success: + proceedAfterLogout(success: true, serverRemove: serverRemove) + case .failure(let error): + log.e(error) + proceedAfterLogout(success: true, serverRemove: serverRemove) + } + } - - serverList = UserDefaultsDataStore().readServerList() - if serverList.isEmpty { - DispatchQueue.main.async { - self.switchToLogout() - //ウィジェットの更新 - if #available(iOS 14.0, *) { - WidgetCenter.shared.reloadAllTimelines() - } + func proceedAfterLogout(success: Bool, serverRemove: Bool) { + var serverList = UserDefaultsDataStore().readServerList() + let removeServer = serverList.first + self.deleteServerInfo(serverNumber: removeServer) + + if serverRemove { + UserDefaultsDataStore().removeServer() } - } else { - UserDefaultsDataStore().setGroupId(serverNumber: serverList.first!) - DispatchQueue.main.async { - self.switchToMainScreen() - //ウィジェットの更新 - if #available(iOS 14.0, *) { - WidgetCenter.shared.reloadAllTimelines() + + serverList = UserDefaultsDataStore().readServerList() + if serverList.isEmpty { + DispatchQueue.main.async { + self.hideLogoutLoadingScreen() + self.switchToLogout() + //ウィジェットの更新 + if #available(iOS 14.0, *) { + WidgetCenter.shared.reloadAllTimelines() + } + } + } else { + UserDefaultsDataStore().setGroupId(serverNumber: serverList.first!) + DispatchQueue.main.async { + self.hideLogoutLoadingScreen() + self.switchToMainScreen() + //ウィジェットの更新 + if #available(iOS 14.0, *) { + WidgetCenter.shared.reloadAllTimelines() + } } } } @@ -206,16 +272,21 @@ extension RootViewController { // ログアウト処理 private func logout(completion: @escaping(APIResult) -> Void) { let logout = GetLogoutEndpoint() - Session.send(logout) { result in - let completionArg: APIResult - switch result { - case .success: - completionArg = .success(true) - case .failure(let error): - log.e(error) - completionArg = .failure(error) + if !AppShared().getLoadingLogout() { + AppShared().setLodingLogout(isLoading: true) + print("送信ログアウト") + Session.send(logout) { result in + let completionArg: APIResult + switch result { + case .success: + completionArg = .success(true) + case .failure(let error): + log.e(error) + completionArg = .failure(error) + } + completion(completionArg) + AppShared().setLodingLogout(isLoading: false) } - completion(completionArg) } } } diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index 22768dd..7bc1dbe 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -303,64 +303,68 @@ class WebViewController: UIViewController { objc_sync_enter(lockObj) let sessionEndpoint = GetSessionEndpoint() - Session.send(sessionEndpoint) { result in - switch result { - // セッション情報取得成功 - case .success(var response): - KeychainDataStore().writeSessionID(sessionID: response.sessionId) - KeychainDataStore().writeSystemName(systemName: response.systemName) - setStringsName(language: response.user.displayLanguage) - if isInit { - // 画面ロード - DispatchQueue.main.async { - self.loadWebView(url: self.loadURL) - self.drawingComp = true - } - } else { - if self.loadURL == self.nowURL { + if !AppShared().getLoadingLogout() { + AppShared().addLoadingSessionAPICount() + Session.send(sessionEndpoint) { result in + AppShared().substractLoadingSessionAPICount() + switch result { + // セッション情報取得成功 + case .success(var response): + KeychainDataStore().writeSessionID(sessionID: response.sessionId) + KeychainDataStore().writeSystemName(systemName: response.systemName) + setStringsName(language: response.user.displayLanguage) + if isInit { + // 画面ロード DispatchQueue.main.async { - if let mainTabBarVC = self.parent?.parent as? MainTabBarViewController { - mainTabBarVC.setupFixedWording() + self.loadWebView(url: self.loadURL) + self.drawingComp = true + } + } else { + if self.loadURL == self.nowURL { + DispatchQueue.main.async { + if let mainTabBarVC = self.parent?.parent as? MainTabBarViewController { + mainTabBarVC.setupFixedWording() + } } } + // Cookie 変更 + self.setSessionId() } - // Cookie 変更 - self.setSessionId() - } - // セッション情報取得失敗 - case .failure(let error): - var refreshError = false - log.e(error) - switch error { - case .connectionError(let err): - switch err { - case ResponseError.tokenInvalid: - refreshError = true - default: + // セッション情報取得失敗 + case .failure(let error): + var refreshError = false + log.e(error) + switch error { + case .connectionError(let err): + switch err { + case ResponseError.tokenInvalid: + refreshError = true + default: + break + } + case .requestError(_): + break + case .responseStatusError(_): + break + case .responseError(_): break } - case .requestError(_): - break - case .responseStatusError(_): - break - case .responseError(_): - break - } - // リフレッシュトークン更新エラーの場合 - if showError { - if refreshError { - DispatchQueue.main.async { - let title = getDisplayString(key: "RefreshTokenUpdateErrorTitle", comment: "") - let message = getDisplayString(key: "RefreshTokenUpdateError", comment: "") - let defaultAction = AppDelegate.shared.rootViewController.logoutDefaultAction(serverRemove: true, title: getDisplayString(key: "OK", comment: "")) - AppDelegate.shared.rootViewController.showAlertScreen(view: self, title: title, message: message, defaultAction: defaultAction, cancelAction: nil) - } - // その他エラーの場合 - } else { - DispatchQueue.main.async { - let message = getDisplayString(key: "UnknownNetworkError", comment: "") - let defaultAction = UIAlertAction(title: getDisplayString(key: "OK", comment: ""), style: .default) { _ in } - AppDelegate.shared.rootViewController.showAlertScreen(view: self, title: "", message: message, defaultAction: defaultAction, cancelAction: nil) + // リフレッシュトークン更新エラーの場合 + if showError { + if refreshError { + DispatchQueue.main.async { + let title = getDisplayString(key: "RefreshTokenUpdateErrorTitle", comment: "") + let message = getDisplayString(key: "RefreshTokenUpdateError", comment: "") + let defaultAction = AppDelegate.shared.rootViewController.logoutDefaultAction(serverRemove: true, title: getDisplayString(key: "OK", comment: "")) + AppDelegate.shared.rootViewController.showAlertScreen(view: self, title: title, message: message, defaultAction: defaultAction, cancelAction: nil) + } + // その他エラーの場合 + } else { + DispatchQueue.main.async { + let message = getDisplayString(key: "UnknownNetworkError", comment: "") + let defaultAction = UIAlertAction(title: getDisplayString(key: "OK", comment: ""), style: .default) { _ in } + AppDelegate.shared.rootViewController.showAlertScreen(view: self, title: "", message: message, defaultAction: defaultAction, cancelAction: nil) + } } } } -- GitLab From 8e47b54a8556213010240c2dc80d801560e2f144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=8A=E8=97=A4=E8=81=96=E5=A4=9C?= Date: Tue, 2 Sep 2025 15:11:33 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=E8=AA=A4=E5=AD=97=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E3=80=81=E3=83=95=E3=83=A9=E3=82=B0=E3=81=AE=E3=82=BB=E3=83=83?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=82=B2=E3=83=83=E3=82=BF=E3=83=BC=E3=81=AE?= =?UTF-8?q?=E5=91=BC=E3=81=B3=E5=87=BA=E3=81=97=E6=96=B9=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AgileWorks/AgileWorks/App/AppShared.swift | 2 +- AgileWorks/AgileWorks/App/RootViewController.swift | 8 ++++---- .../AgileWorks/WebView/View/WebViewController.swift | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/AgileWorks/AgileWorks/App/AppShared.swift b/AgileWorks/AgileWorks/App/AppShared.swift index 302ecf5..bb122a9 100644 --- a/AgileWorks/AgileWorks/App/AppShared.swift +++ b/AgileWorks/AgileWorks/App/AppShared.swift @@ -62,7 +62,7 @@ class AppShared { return self.loadingSessionAPICount } // ログアウト処理の実行状況を設定 - func setLodingLogout(isLoading: Bool) { + func setLoadingLogout(isLoading: Bool) { self.isLoadingLogout = isLoading } // ログアウト処理の実行状況を取得 diff --git a/AgileWorks/AgileWorks/App/RootViewController.swift b/AgileWorks/AgileWorks/App/RootViewController.swift index 938152a..14a00aa 100644 --- a/AgileWorks/AgileWorks/App/RootViewController.swift +++ b/AgileWorks/AgileWorks/App/RootViewController.swift @@ -86,7 +86,7 @@ extension RootViewController { let timeout: TimeInterval = 60 func checkLoadingCompletion() { - if AppShared().getLoadingSessionAPICount() == 0 { + if AppDelegate.appShared.getLoadingSessionAPICount() == 0 { completion() } else if Date().timeIntervalSince(startTime) >= timeout { print("セッション情報取得APIの完了待ちタイムアウト") @@ -272,8 +272,8 @@ extension RootViewController { // ログアウト処理 private func logout(completion: @escaping(APIResult) -> Void) { let logout = GetLogoutEndpoint() - if !AppShared().getLoadingLogout() { - AppShared().setLodingLogout(isLoading: true) + if !AppDelegate.appShared.getLoadingLogout() { + AppDelegate.appShared.setLoadingLogout(isLoading: true) print("送信ログアウト") Session.send(logout) { result in let completionArg: APIResult @@ -285,7 +285,7 @@ extension RootViewController { completionArg = .failure(error) } completion(completionArg) - AppShared().setLodingLogout(isLoading: false) + AppDelegate.appShared.setLoadingLogout(isLoading: false) } } } diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index 7bc1dbe..307223a 100644 --- a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift +++ b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift @@ -303,10 +303,10 @@ class WebViewController: UIViewController { objc_sync_enter(lockObj) let sessionEndpoint = GetSessionEndpoint() - if !AppShared().getLoadingLogout() { - AppShared().addLoadingSessionAPICount() + if !AppDelegate.appShared.getLoadingLogout() { + AppDelegate.appShared.addLoadingSessionAPICount() Session.send(sessionEndpoint) { result in - AppShared().substractLoadingSessionAPICount() + AppDelegate.appShared.substractLoadingSessionAPICount() switch result { // セッション情報取得成功 case .success(var response): -- GitLab