1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
//
// LatestChangesNotificationProvider.swift
// MullvadVPN
//
// Created by Mojgan on 2025-01-15.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import Foundation
import UIKit
class LatestChangesNotificationProvider: NotificationProvider, InAppNotificationProvider, @unchecked Sendable {
private var appPreferences: AppPreferencesDataSource
private let appVersion: String = Bundle.main.productVersion
init(appPreferences: AppPreferencesDataSource) {
self.appPreferences = appPreferences
}
var shouldShowNotification: Bool {
// If this is the first installation, no notification will be shown.
guard !appPreferences.lastSeenChangeLogVersion.isEmpty else { return false }
// Display the notification only if the app is updated from a previously installed version.
return appPreferences.lastSeenChangeLogVersion != appVersion
}
override var identifier: NotificationProviderIdentifier {
.latestChangesInAppNotificationProvider
}
override var priority: NotificationPriority {
.low
}
var notificationDescriptor: InAppNotificationDescriptor? {
defer {
// Always update the last seen version
appPreferences.lastSeenChangeLogVersion = appVersion
}
guard shouldShowNotification else { return nil }
return InAppNotificationDescriptor(
identifier: identifier,
style: .success,
title: NSLocalizedString("NEW VERSION INSTALLED", comment: ""),
body: createNotificationBody(),
button: createCloseButtonAction(),
tapAction: createTapAction()
)
}
private func createNotificationBody() -> NSAttributedString {
NSAttributedString(
markdownString: NSLocalizedString("**Tap here** to see what’s new", comment: ""),
options: MarkdownStylingOptions(
font: .preferredFont(forTextStyle: .body)
)
) { _, _ in
[.foregroundColor: UIColor.InAppNotificationBanner.titleColor]
}
}
private func createCloseButtonAction() -> InAppNotificationAction {
InAppNotificationAction(
image: UIImage.Buttons.closeSmall,
handler: { [weak self] in
self?.invalidate()
}
)
}
private func createTapAction() -> InAppNotificationAction {
InAppNotificationAction(
handler: { [weak self] in
guard let self else { return }
self.invalidate()
NotificationManager.shared.notificationProvider(self, didReceiveAction: "\(self.identifier)")
}
)
}
}
|