Skip to content

Commit

Permalink
🔀️ Merge branch 'hugo/feature/Display-update-alert-only-if-minimum-iO…
Browse files Browse the repository at this point in the history
…S' into develop
  • Loading branch information
ladislas committed Nov 22, 2024
2 parents 342e848 + e495e4f commit b2e6786
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 97 deletions.
2 changes: 0 additions & 2 deletions Apps/LekaApp/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ let project = Project.app(
.project(target: "LogKit", path: Path("../../Modules/LogKit")),
.project(target: "RobotKit", path: Path("../../Modules/RobotKit")),
.project(target: "UtilsKit", path: Path("../../Modules/UtilsKit")),

.external(name: "AppUpdately"),
.external(name: "DeviceKit"),
.external(name: "Fit"),
.external(name: "MarkdownUI"),
Expand Down
108 changes: 54 additions & 54 deletions Apps/LekaApp/Resources/l10n/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -1748,6 +1748,60 @@
}
}
},
"lekaapp.main_view.app_update_alert.action": {
"comment": "The action button of the alert to inform the user that an update is available",
"extractionState": "extracted_with_value",
"localizations": {
"en": {
"stringUnit": {
"state": "new",
"value": "Update now"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Mettre \u00e0 jour maintenant"
}
}
}
},
"lekaapp.main_view.app_update_alert.message": {
"comment": "The message of the alert to inform the user that an update is available",
"extractionState": "extracted_with_value",
"localizations": {
"en": {
"stringUnit": {
"state": "new",
"value": "Enjoy new features by updating to the latest version of Leka!"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Profitez des nouvelles fonctionnalit\u00e9s en t\u00e9l\u00e9chargeant la nouvelle mise \u00e0 jour de l\u2019app Leka !"
}
}
}
},
"lekaapp.main_view.app_update_alert.title": {
"comment": "The title of the alert to inform the user that an update is available",
"extractionState": "extracted_with_value",
"localizations": {
"en": {
"stringUnit": {
"state": "new",
"value": "New update available"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Nouvelle mise \u00e0 jour disponible"
}
}
}
},
"lekaapp.main_view.detailView.disconnected_library_message": {
"comment": "The message to invite users to connect to display the Library",
"extractionState": "extracted_with_value",
Expand Down Expand Up @@ -2162,60 +2216,6 @@
}
}
},
"lekaapp.main_view.update_alert.action": {
"comment": "The action button of the alert to inform the user that an update is available",
"extractionState": "extracted_with_value",
"localizations": {
"en": {
"stringUnit": {
"state": "new",
"value": "Update now"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Mettre \u00e0 jour maintenant"
}
}
}
},
"lekaapp.main_view.update_alert.message": {
"comment": "The message of the alert to inform the user that an update is available",
"extractionState": "extracted_with_value",
"localizations": {
"en": {
"stringUnit": {
"state": "new",
"value": "Enjoy new features by updating to the latest version of Leka!"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Profitez des nouvelles fonctionnalit\u00e9s en t\u00e9l\u00e9chargeant la nouvelle mise \u00e0 jour de l\u2019app Leka !"
}
}
}
},
"lekaapp.main_view.update_alert.title": {
"comment": "The title of the alert to inform the user that an update is available",
"extractionState": "extracted_with_value",
"localizations": {
"en": {
"stringUnit": {
"state": "new",
"value": "New update available"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Nouvelle mise \u00e0 jour disponible"
}
}
}
},
"lekaapp.news_view.description": {
"comment": "News description",
"extractionState": "extracted_with_value",
Expand Down
21 changes: 6 additions & 15 deletions Apps/LekaApp/Sources/MainApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

import AccountKit
import AnalyticsKit
import AppUpdately
import Combine
import ContentKit
import DesignKit
import FirebaseKit
import LocalizationKit
import LogKit
import SwiftUI
import UtilsKit

let log = LogKit.createLoggerFor(app: "LekaApp")

Expand Down Expand Up @@ -48,7 +48,7 @@ struct LekaApp: App {
class UpdateStatus: ObservableObject {
static let shared = UpdateStatus()

@Published var isUpdateAvailable: Bool = false
@Published var status: UpdateStatusFetcher.Status = .upToDate
}

@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
Expand Down Expand Up @@ -87,23 +87,14 @@ struct LekaApp: App {
guard let status = try? result.get() else {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.showMainView = true
self.appUpdateStatus.isUpdateAvailable = false
self.appUpdateStatus.status = .upToDate
}
return
}

switch status {
case .upToDate,
.newerVersion:
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.showMainView = true
self.appUpdateStatus.isUpdateAvailable = false
}
case .updateAvailable:
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.showMainView = true
self.appUpdateStatus.isUpdateAvailable = true
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.showMainView = true
self.appUpdateStatus.status = status
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions Apps/LekaApp/Sources/Views/MainView/MainView+l10n.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ extension l10n {
static let disconnectedLibraryMessage = LocalizedString("lekaapp.main_view.detailView.disconnected_library_message", value: "Log in to your account to access your personal library.", comment: "The message to invite users to connect to display the Library")
}

enum UpdateAlert {
static let title = LocalizedString("lekaapp.main_view.update_alert.title", value: "New update available", comment: "The title of the alert to inform the user that an update is available")
static let message = LocalizedString("lekaapp.main_view.update_alert.message", value: "Enjoy new features by updating to the latest version of Leka!", comment: "The message of the alert to inform the user that an update is available")
static let action = LocalizedString("lekaapp.main_view.update_alert.action", value: "Update now", comment: "The action button of the alert to inform the user that an update is available")
enum AppUpdateAlert {
static let title = LocalizedString("lekaapp.main_view.app_update_alert.title", value: "New update available", comment: "The title of the alert to inform the user that an update is available")
static let message = LocalizedString("lekaapp.main_view.app_update_alert.message", value: "Enjoy new features by updating to the latest version of Leka!", comment: "The message of the alert to inform the user that an update is available")
static let action = LocalizedString("lekaapp.main_view.app_update_alert.action", value: "Update now", comment: "The action button of the alert to inform the user that an update is available")
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions Apps/LekaApp/Sources/Views/MainView/MainView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ struct MainView: View {
}
.listStyle(.sidebar)
}
.alert(isPresented: self.$showingUpdateAlert) {
.alert(isPresented: self.$showingAppUpdateAlert) {
Alert(
title: Text(l10n.MainView.UpdateAlert.title),
message: Text(l10n.MainView.UpdateAlert.message),
primaryButton: .default(Text(l10n.MainView.UpdateAlert.action), action: {
title: Text(l10n.MainView.AppUpdateAlert.title),
message: Text(l10n.MainView.AppUpdateAlert.message),
primaryButton: .default(Text(l10n.MainView.AppUpdateAlert.action), action: {
AnalyticsManager.logEventAppUpdateOpenAppStore()
if let url = URL(string: "https://apps.apple.com/app/leka/id6446940339") {
UIApplication.shared.open(url)
Expand Down Expand Up @@ -317,8 +317,11 @@ struct MainView: View {
}
.onDisappear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if self.appUpdateStatus.isUpdateAvailable {
self.showingUpdateAlert = true
switch self.appUpdateStatus.status {
case .updateAvailable:
self.showingAppUpdateAlert = true
default:
break
}
}
}
Expand Down Expand Up @@ -417,7 +420,7 @@ struct MainView: View {
@StateObject private var rootAccountViewModel = RootAccountManagerViewModel()
@StateObject var appUpdateStatus: LekaApp.UpdateStatus = .shared

@State private var showingUpdateAlert: Bool = false
@State private var showingAppUpdateAlert: Bool = false

private var persistentDataManager: PersistentDataManager = .shared
private var caregiverManager: CaregiverManager = .shared
Expand Down
7 changes: 6 additions & 1 deletion Apps/LekaApp/Sources/Views/MainView/SettingsLabel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ struct SettingsLabel: View {
.background(.red)
.clipShape(.circle)
.offset(x: 95, y: -20)
.opacity(self.appUpdateStatus.isUpdateAvailable ? 1 : 0)
.opacity({
if case .updateAvailable = self.appUpdateStatus.status {
return 1.0
}
return 0.0
}())
)
}

Expand Down
2 changes: 1 addition & 1 deletion Apps/LekaApp/Sources/Views/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ struct SettingsView: View {

var body: some View {
Form {
if self.appUpdateStatus.isUpdateAvailable {
if case .updateAvailable = self.appUpdateStatus.status {
Section {
VStack(alignment: .center) {
HStack(spacing: 20) {
Expand Down
122 changes: 122 additions & 0 deletions Modules/UtilsKit/Sources/UpdateStatusFetcher.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Leka - iOS Monorepo
// Copyright APF France handicap
// SPDX-License-Identifier: Apache-2.0

// AppUpdate folder is heavily inspired by:
// https://github.com/AvdLee/AppUpdately
// AvdLee/AppUpdately License: MIT

import Combine
import Foundation

// MARK: - AppMetadata

struct AppMetadata: Codable {
let trackViewURL: URL
let version: String
let minimumOsVersion: String
}

// MARK: - AppMetadataResults

struct AppMetadataResults: Codable {
let results: [AppMetadata]
}

// MARK: - UpdateStatusFetcher

public struct UpdateStatusFetcher {
// MARK: Lifecycle

public init(bundleIdentifier: String = Bundle.main.bundleIdentifier!, urlSession: URLSession = .shared) {
self.url = URL(string: self.prefixURL + "\(bundleIdentifier)")!
self.bundleIdentifier = bundleIdentifier
self.urlSession = urlSession
}

// MARK: Public

public enum Status: Equatable {
case newerVersion
case upToDate
case updateAvailable(version: String, storeURL: URL)
case underMinimumOsVersion
}

public enum FetchError: LocalizedError {
case metadata
case bundleShortVersion

// MARK: Public

public var errorDescription: String? {
switch self {
case .metadata:
"Metadata could not be found"
case .bundleShortVersion:
"Bundle short version could not be found"
}
}
}

public func fetch(_ completion: @escaping (Swift.Result<Status, Error>) -> Void) -> AnyCancellable {
self.urlSession
.dataTaskPublisher(for: self.url)
.map(\.data)
.decode(type: AppMetadataResults.self, decoder: self.decoder)
.tryMap { metadataResults -> Status in
guard let appMetadata = metadataResults.results.first else {
throw FetchError.metadata
}
return try self.updateStatus(for: appMetadata)
}
.sink { completionStatus in
switch completionStatus {
case let .failure(error):
completion(.failure(error))
case .finished:
break
}
} receiveValue: { status in
completion(.success(status))
}
}

// MARK: Internal

let url: URL
var currentVersionProvider: () -> String? = {
Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
}

// MARK: Private

private let prefixURL = "https://itunes.apple.com/lookup?bundleId="
private let bundleIdentifier: String
private let decoder: JSONDecoder = .init()
private let urlSession: URLSession

private func updateStatus(for appMetadata: AppMetadata) throws -> Status {
guard let currentVersion = currentVersionProvider() else {
throw UpdateStatusFetcher.FetchError.bundleShortVersion
}

// Get the device's current iOS version
let deviceOSVersion = ProcessInfo.processInfo.operatingSystemVersion
let deviceOSString = "\(deviceOSVersion.majorVersion).\(deviceOSVersion.minorVersion).\(deviceOSVersion.patchVersion)"

// Compare the required iOS version with the device's current version
if deviceOSString.compare(appMetadata.minimumOsVersion, options: .numeric) == .orderedAscending {
return UpdateStatusFetcher.Status.underMinimumOsVersion
}

switch currentVersion.compare(appMetadata.version) {
case .orderedDescending:
return UpdateStatusFetcher.Status.newerVersion
case .orderedSame:
return UpdateStatusFetcher.Status.upToDate
case .orderedAscending:
return UpdateStatusFetcher.Status.updateAvailable(version: appMetadata.version, storeURL: appMetadata.trackViewURL)
}
}
}
9 changes: 0 additions & 9 deletions Tuist/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@
"version" : "11.0.1"
}
},
{
"identity" : "appupdately",
"kind" : "remoteSourceControl",
"location" : "https://github.com/AvdLee/AppUpdately",
"state" : {
"branch" : "main",
"revision" : "697c19e356241dcd097adde7f51398bdf18bf1ca"
}
},
{
"identity" : "audiokit",
"kind" : "remoteSourceControl",
Expand Down
Loading

0 comments on commit b2e6786

Please sign in to comment.