Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hugo/feature/Display update alert only if minimum iOS #1591

Merged
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: 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 @@
}
.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 @@
}
.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 @@
@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 All @@ -428,4 +431,4 @@

#Preview {
MainView()
}

Check warning on line 434 in Apps/LekaApp/Sources/Views/MainView/MainView.swift

View workflow job for this annotation

GitHub Actions / swiftlint

File should contain 400 lines or less: currently contains 434 (file_length)
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 @@ -11,7 +11,7 @@

// MARK: - SettingsView

struct SettingsView: View {

Check warning on line 14 in Apps/LekaApp/Sources/Views/Settings/SettingsView.swift

View workflow job for this annotation

GitHub Actions / swiftlint

Type body should span 250 lines or less excluding comments and whitespace: currently spans 251 lines (type_body_length)
@Environment(\.openURL) private var openURL
@Environment(\.dismiss) var dismiss

Expand All @@ -23,7 +23,7 @@

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
Loading