diff --git a/AgileWorks/AgileWorks/App/AppShared.swift b/AgileWorks/AgileWorks/App/AppShared.swift index da059542c7c10618ba576d7056d98e82b55df9ae..bb122a9bd13566f891c25680acddece8c4c9b4b2 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 setLoadingLogout(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 3aa236373982c79885aa6306f1a32237b8f39747..14a00aa3492252fbc70f01b0d42f7214b9451a43 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 AppDelegate.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 !AppDelegate.appShared.getLoadingLogout() { + AppDelegate.appShared.setLoadingLogout(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) + AppDelegate.appShared.setLoadingLogout(isLoading: false) } - completion(completionArg) } } } diff --git a/AgileWorks/AgileWorks/WebView/View/WebViewController.swift b/AgileWorks/AgileWorks/WebView/View/WebViewController.swift index 22768dd851cc44ba8d7778ba9d05c732caf35535..307223aefe119fbb9ee152aba0ac18b904603c16 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 !AppDelegate.appShared.getLoadingLogout() { + AppDelegate.appShared.addLoadingSessionAPICount() + Session.send(sessionEndpoint) { result in + AppDelegate.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) + } } } }