Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RIADigiDoc.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@
Util/Signature/SignatureUtilProtocol.swift,
Util/Theme/ThemeSettings.swift,
Util/Theme/ThemeSettingsProtocol.swift,
Util/Version/AppVersionUtil.swift,
Util/Version/AppVersionUtilProtocol.swift,
Util/WebEid/WebEidUriUtil.swift,
ViewModel/AdvancedSettingsViewModel.swift,
ViewModel/CertificateDetailViewModel.swift,
Expand Down
8 changes: 7 additions & 1 deletion RIADigiDoc/DI/AppContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ extension Container {
HomeViewModel(
sharedContainerViewModel: self.sharedContainerViewModel(),
fileManager: self.fileManager(),
fileUtil: self.fileUtil()
fileUtil: self.fileUtil(),
appVersionUtil: self.appVersionUtil()
)
}
}
Expand Down Expand Up @@ -163,6 +164,11 @@ extension Container {
self { CertificateUtil() }
}

@MainActor
var appVersionUtil: Factory<AppVersionUtilProtocol> {
self { @MainActor in AppVersionUtil(configurationLoader: self.configurationLoader()) }
}

@MainActor
public var proxyUtil: Factory<ProxyUtilProtocol> {
self { @MainActor in ProxyUtil(
Expand Down
52 changes: 45 additions & 7 deletions RIADigiDoc/RIADigiDocApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ struct RIADigiDocApp: App, Loggable {
private let fileUtil: FileUtilProtocol
private let librarySetup: LibrarySetup
private let documentsMigrator: DocumentsMigratorProtocol
private let appVersionUtil: AppVersionUtilProtocol

@Environment(\.scenePhase) private var scenePhase

init() {
_languageSettings = State(wrappedValue: Container.shared.languageSettings())
Expand All @@ -40,6 +43,7 @@ struct RIADigiDocApp: App, Loggable {
self.fileUtil = Container.shared.fileUtil()
self.librarySetup = Container.shared.librarySetup()
self.documentsMigrator = Container.shared.documentsMigrator()
self.appVersionUtil = Container.shared.appVersionUtil()
}

private func loadBlockingScreenLanguage() async {
Expand All @@ -50,6 +54,41 @@ struct RIADigiDocApp: App, Loggable {
}
}

private func applyAppVersionSupport() async -> Bool {
guard await appVersionUtil.isAppVersionUnsupported() else { return false }

await loadBlockingScreenLanguage()
await MainActor.run {
self.isAppVersionUnsupported = true
}
removePendingSharedFiles()

return true
}

private func removePendingSharedFiles() {
do {
try fileUtil.removeSharedFiles(url: Directories.getSharedFolder(fileManager: fileManager))
} catch {
RIADigiDocApp.logger().error(
"Unable to remove pending shared files: \(String(reflecting: error), privacy: .public)"
)
}
}

private func onScenePhaseActive() {
guard isSetupComplete || isAppVersionUnsupported, !isJailbroken else { return }

Task {
if isAppVersionUnsupported {
removePendingSharedFiles()
return
}

_ = await applyAppVersionSupport()
}
}

private func onLaunchScreenViewAppear() {
Task {
if await JailbreakDetection.isDeviceJailbroken(fileManager: fileManager) {
Expand All @@ -62,13 +101,7 @@ struct RIADigiDocApp: App, Loggable {

await librarySetup.setupLibraries()

if await configurationLoader.getConfiguration()?
.isAppVersionUnsupported(BundleUtil.getAppVersion()) == true {
RIADigiDocApp.logger().error("App version \(BundleUtil.getAppVersion()) is unsupported")
await loadBlockingScreenLanguage()
await MainActor.run {
self.isAppVersionUnsupported = true
}
if await applyAppVersionSupport() {
return
}

Expand Down Expand Up @@ -166,6 +199,11 @@ struct RIADigiDocApp: App, Loggable {
}
}
.hideSensitiveContent()
.onChange(of: scenePhase) { _, newPhase in
if newPhase == .active {
onScenePhaseActive()
}
}
}
}
}
22 changes: 22 additions & 0 deletions RIADigiDoc/Util/Version/AppVersionUtil.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: Estonian Information System Authority
// SPDX-License-Identifier: LGPL-2.1-or-later

import ConfigLib
import UtilsLib

public struct AppVersionUtil: AppVersionUtilProtocol, Loggable {

let configurationLoader: ConfigurationLoaderProtocol

public func isAppVersionUnsupported() async -> Bool {
let appVersion = BundleUtil.getAppVersion()
let isUnsupported = await configurationLoader.getConfiguration()?
.isAppVersionUnsupported(appVersion) == true

if isUnsupported {
AppVersionUtil.logger().error("App version \(appVersion, privacy: .public) is unsupported")
}

return isUnsupported
}
}
8 changes: 8 additions & 0 deletions RIADigiDoc/Util/Version/AppVersionUtilProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Estonian Information System Authority
// SPDX-License-Identifier: LGPL-2.1-or-later

/// @mockable
@MainActor
public protocol AppVersionUtilProtocol: Sendable {
func isAppVersionUnsupported() async -> Bool
}
10 changes: 9 additions & 1 deletion RIADigiDoc/ViewModel/HomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ class HomeViewModel: HomeViewModelProtocol, Loggable {
private let sharedContainerViewModel: SharedContainerViewModelProtocol
private let fileManager: FileManagerProtocol
private let fileUtil: FileUtilProtocol
private let appVersionUtil: AppVersionUtilProtocol

init(
sharedContainerViewModel: SharedContainerViewModelProtocol,
fileManager: FileManagerProtocol,
fileUtil: FileUtilProtocol
fileUtil: FileUtilProtocol,
appVersionUtil: AppVersionUtilProtocol
) {
self.sharedContainerViewModel = sharedContainerViewModel
self.fileManager = fileManager
self.fileUtil = fileUtil
self.appVersionUtil = appVersionUtil
}

func didUserCancelFileOpening(isImportingValue: Bool, isFileOpeningLoading: Bool) -> Bool {
Expand Down Expand Up @@ -72,6 +75,11 @@ class HomeViewModel: HomeViewModelProtocol, Loggable {
}

func getSharedFiles() async -> [URL] {
guard await !appVersionUtil.isAppVersionUnsupported() else {
HomeViewModel.logger().info("App version is unsupported. Ignoring shared files")
return []
}

do {
HomeViewModel.logger().info("Checking for shared files...")
let sharedFolderURL = try await Directories.getSharedFolder(fileManager: fileManager)
Expand Down
68 changes: 68 additions & 0 deletions RIADigiDocTests/Util/Version/AppVersionUtilTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: Estonian Information System Authority
// SPDX-License-Identifier: LGPL-2.1-or-later

import ConfigLib
import ConfigLibMocks
import Foundation
import Testing

@MainActor
final class AppVersionUtilTests {
private let appVersionUtil: AppVersionUtil

private let mockConfigurationLoader: ConfigurationLoaderProtocolMock!

init() {
mockConfigurationLoader = ConfigurationLoaderProtocolMock()
appVersionUtil = AppVersionUtil(configurationLoader: mockConfigurationLoader)
}

private func configuration(unsupportedAppVersion: String?) throws -> ConfigurationProvider {
try TestConfigurationProvider.mockConfigurationProvider(unsupportedAppVersion: unsupportedAppVersion)
}

@Test
func isAppVersionUnsupported_returnFalseWhenConfigurationIsMissing() async {
mockConfigurationLoader.getConfigurationHandler = { nil }

#expect(await !appVersionUtil.isAppVersionUnsupported())
}

@Test
func isAppVersionUnsupported_returnFalseWhenUnsupportedVersionIsNotSet() async throws {
let provider = try configuration(unsupportedAppVersion: nil)
mockConfigurationLoader.getConfigurationHandler = { provider }

#expect(await !appVersionUtil.isAppVersionUnsupported())
}

@Test
func isAppVersionUnsupported_returnTrueWhenAppVersionIsLower() async throws {
let provider = try configuration(unsupportedAppVersion: "999.0.0")
mockConfigurationLoader.getConfigurationHandler = { provider }

#expect(await appVersionUtil.isAppVersionUnsupported())
}

@Test
func isAppVersionUnsupported_returnFalseWhenAppVersionIsHigher() async throws {
let provider = try configuration(unsupportedAppVersion: "0.0.1")
mockConfigurationLoader.getConfigurationHandler = { provider }

#expect(await !appVersionUtil.isAppVersionUnsupported())
}

@Test
func isAppVersionUnsupported_reevaluatesConfigurationOnEveryCall() async throws {
let supported = try configuration(unsupportedAppVersion: nil)
mockConfigurationLoader.getConfigurationHandler = { supported }

#expect(await !appVersionUtil.isAppVersionUnsupported())

let unsupported = try configuration(unsupportedAppVersion: "999.0.0")
mockConfigurationLoader.getConfigurationHandler = { unsupported }

#expect(await appVersionUtil.isAppVersionUnsupported())
#expect(mockConfigurationLoader.getConfigurationCallCount == 2)
}
}
19 changes: 17 additions & 2 deletions RIADigiDocTests/ViewModel/HomeViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,21 @@ struct HomeViewModelTests {
private let mockFileManager: FileManagerProtocolMock
private let mockFileUtil: FileUtilProtocolMock
private let mockContainerUtil: ContainerUtilProtocolMock
private let mockAppVersionUtil: AppVersionUtilProtocolMock
private let viewModel: HomeViewModel

init() async throws {
mockSharedContainerViewModel = SharedContainerViewModelProtocolMock()
mockFileManager = FileManagerProtocolMock()
mockFileUtil = FileUtilProtocolMock()
mockContainerUtil = ContainerUtilProtocolMock()
mockAppVersionUtil = AppVersionUtilProtocolMock()
mockAppVersionUtil.isAppVersionUnsupportedHandler = { false }
viewModel = HomeViewModel(
sharedContainerViewModel: mockSharedContainerViewModel,
fileManager: mockFileManager,
fileUtil: mockFileUtil
fileUtil: mockFileUtil,
appVersionUtil: mockAppVersionUtil
)
}

Expand Down Expand Up @@ -234,7 +238,8 @@ struct HomeViewModelTests {
HomeViewModel(
sharedContainerViewModel: sharedContainerViewModel,
fileManager: mockFileManager,
fileUtil: mockFileUtil
fileUtil: mockFileUtil,
appVersionUtil: mockAppVersionUtil
)
}

Expand All @@ -244,4 +249,14 @@ struct HomeViewModelTests {
containerUtil: mockContainerUtil
)
}

@Test
func getSharedFiles_returnsNothingWhenAppVersionIsUnsupported() async throws {
mockAppVersionUtil.isAppVersionUnsupportedHandler = { true }

let shared = await viewModel.getSharedFiles()

#expect(shared.isEmpty)
#expect(mockFileManager.contentsOfDirectoryCallCount == 0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ final class TimeStampSettingsViewModelTests {

@Test
func saveSettings_successWithManualSettingWithValidURL() async throws {
await viewModel.initializeSettings()

viewModel.selectedOption = .manualSetting
let testURL = "http://valid.url.ee"
viewModel.tsaUrl = testURL
Expand Down
Loading