Repository: Infomaniak/ios-core-ui
Pull request #89: feat: Move AskForReviewView + SafariWebView to coreUI
Author: login: Matthieu-dgl
Created: 2025-03-04T06:17:44Z | Merged: 2025-03-05T10:26:26Z



Diff:
diff --git a/Sources/InfomaniakCoreSwiftUI/Components/AlertView/AskForReviewView.swift b/Sources/InfomaniakCoreSwiftUI/Components/AlertView/AskForReviewView.swift
new file mode 100644
index 0000000..74229dc
--- /dev/null
+++ b/Sources/InfomaniakCoreSwiftUI/Components/AlertView/AskForReviewView.swift
@@ -0,0 +1,82 @@
+/*
+ Infomaniak Core UI - iOS
+ Copyright (C) 2025 Infomaniak Network SA
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import DesignSystem
+import InfomaniakCore
+import InfomaniakCoreCommonUI
+import InfomaniakCoreUIResources
+import InfomaniakDI
+import SwiftUI
+
+public struct AskForReviewView: View {
+    private let appName: String
+    private let feedbackURL: String
+    private let onLike: () async throws -> Void
+    private let onDislike: (URL) -> Void
+    private let reviewManager: ReviewManageable
+
+    public init(
+        appName: String,
+        feedbackURL: String,
+        reviewManager: ReviewManageable,
+        onLike: @escaping () async throws -> Void,
+        onDislike: @escaping (URL) -> Void
+    ) {
+        self.appName = appName
+        self.feedbackURL = feedbackURL
+        self.reviewManager = reviewManager
+        self.onLike = onLike
+        self.onDislike = onDislike
+    }
+
+    public var body: some View {
+        VStack(alignment: .leading, spacing: 0) {
+            Text(CoreUILocalizable.reviewAlertTitle(appName))
+                .padding(.bottom, IKPadding.large)
+
+            ModalButtonsView(
+                primaryButtonTitle: CoreUILocalizable.buttonYes,
+                secondaryButtonTitle: CoreUILocalizable.buttonNo,
+                primaryButtonAction: {
+                    try await onLike()
+                    reviewManager.requestReview()
+                },
+                secondaryButtonAction: {
+                    if let userReportURL = URL(string: feedbackURL) {
+                        onDislike(userReportURL)
+                    }
+                }
+            )
+        }
+    }
+}
+
+#Preview {
+    AskForReviewView(
+        appName: "InfomaniakCoreUI ",
+        feedbackURL: "https://infomaniak.com",
+        reviewManager: ReviewManager(
+            userDefaults: UserDefaults.standard,
+            openingBeforeFirstReview: 1,
+            openingBeforeNextReviews: 2
+        ),
+        onLike: {},
+        onDislike: { _ in
+        }
+    )
+}
diff --git a/Sources/InfomaniakCoreSwiftUI/Components/SafariWebView.swift b/Sources/InfomaniakCoreSwiftUI/Components/SafariWebView.swift
new file mode 100644
index 0000000..2e3d5c4
--- /dev/null
+++ b/Sources/InfomaniakCoreSwiftUI/Components/SafariWebView.swift
@@ -0,0 +1,34 @@
+/*
+ Infomaniak Core UI - iOS
+ Copyright (C) 2025 Infomaniak Network SA
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import SafariServices
+import SwiftUI
+
+public struct SafariWebView: UIViewControllerRepresentable {
+    let url: URL
+
+    public init(url: URL) {
+        self.url = url
+    }
+
+    public func makeUIViewController(context: Context) -> SFSafariViewController {
+        return SFSafariViewController(url: url)
+    }
+
+    public func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {}
+}
diff --git a/Sources/InfomaniakCoreUIResources/Generated/Strings+Generated.swift b/Sources/InfomaniakCoreUIResources/Generated/Strings+Generated.swift
index 98e52b4..3b8fe18 100644
--- a/Sources/InfomaniakCoreUIResources/Generated/Strings+Generated.swift
+++ b/Sources/InfomaniakCoreUIResources/Generated/Strings+Generated.swift
@@ -18,12 +18,20 @@ public enum CoreUILocalizable {
   public static let buttonLater = CoreUILocalizable.tr("Localizable", "buttonLater", fallback: "Later")
   /// loco:675059634bdf0aedde06df72
   public static let buttonNext = CoreUILocalizable.tr("Localizable", "buttonNext", fallback: "Next")
+  /// loco:67beed573c592678eb085582
+  public static let buttonNo = CoreUILocalizable.tr("Localizable", "buttonNo", fallback: "No")
   /// loco:6729ead76f93fd246802f932
   public static let buttonRetry = CoreUILocalizable.tr("Localizable", "buttonRetry", fallback: "Retry")
+  /// loco:67beed7f8291a886220faf53
+  public static let buttonYes = CoreUILocalizable.tr("Localizable", "buttonYes", fallback: "Yes")
   /// loco:676538295907d1ab51014ae2
   public static let errorDownload = CoreUILocalizable.tr("Localizable", "errorDownload", fallback: "Download error")
   /// loco:67653882015fa8304b01cbb9
   public static let errorDownloadInsufficientSpace = CoreUILocalizable.tr("Localizable", "errorDownloadInsufficientSpace", fallback: "Cannot download : Insufficient space")
+  /// loco:67beee584d2044470e0df152
+  public static func reviewAlertTitle(_ p1: Any) -> String {
+    return CoreUILocalizable.tr("Localizable", "reviewAlertTitle", String(describing: p1), fallback: "Do you like %@?")
+  }
 }
 // swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
 // swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
diff --git a/Sources/InfomaniakCoreUIResources/Localizable/de.lproj/Localizable.strings b/Sources/InfomaniakCoreUIResources/Localizable/de.lproj/Localizable.strings
index e2601b1..a77e9f2 100644
--- a/Sources/InfomaniakCoreUIResources/Localizable/de.lproj/Localizable.strings
+++ b/Sources/InfomaniakCoreUIResources/Localizable/de.lproj/Localizable.strings
@@ -19,11 +19,21 @@
 /* loco:675059634bdf0aedde06df72 */
 "buttonNext" = "Weiter";
 
+/* loco:67beed573c592678eb085582 */
+"buttonNo" = "Nein";
+
 /* loco:6729ead76f93fd246802f932 */
 "buttonRetry" = "Wiederholen";
 
+/* loco:67beed7f8291a886220faf53 */
+"buttonYes" = "Ja";
+
 /* loco:676538295907d1ab51014ae2 */
 "errorDownload" = "Fehler beim Download";
 
 /* loco:67653882015fa8304b01cbb9 */
 "errorDownloadInsufficientSpace" = "Download nicht möglich: Unzureichender Speicherplatz";
+
+/* loco:67beee584d2044470e0df152 */
+"reviewAlertTitle" = "Mögen Sie %@?";
+
diff --git a/Sources/InfomaniakCoreUIResources/Localizable/en.lproj/Localizable.strings b/Sources/InfomaniakCoreUIResources/Localizable/en.lproj/Localizable.strings
index 5ccc080..8d83370 100644
--- a/Sources/InfomaniakCoreUIResources/Localizable/en.lproj/Localizable.strings
+++ b/Sources/InfomaniakCoreUIResources/Localizable/en.lproj/Localizable.strings
@@ -19,11 +19,21 @@
 /* loco:675059634bdf0aedde06df72 */
 "buttonNext" = "Next";
 
+/* loco:67beed573c592678eb085582 */
+"buttonNo" = "No";
+
 /* loco:6729ead76f93fd246802f932 */
 "buttonRetry" = "Retry";
 
+/* loco:67beed7f8291a886220faf53 */
+"buttonYes" = "Yes";
+
 /* loco:676538295907d1ab51014ae2 */
 "errorDownload" = "Download error";
 
 /* loco:67653882015fa8304b01cbb9 */
 "errorDownloadInsufficientSpace" = "Cannot download : Insufficient space";
+
+/* loco:67beee584d2044470e0df152 */
+"reviewAlertTitle" = "Do you like %@?";
+
diff --git a/Sources/InfomaniakCoreUIResources/Localizable/es.lproj/Localizable.strings b/Sources/InfomaniakCoreUIResources/Localizable/es.lproj/Localizable.strings
index 5d3e8e8..f452fb3 100644
--- a/Sources/InfomaniakCoreUIResources/Localizable/es.lproj/Localizable.strings
+++ b/Sources/InfomaniakCoreUIResources/Localizable/es.lproj/Localizable.strings
@@ -19,11 +19,21 @@
 /* loco:675059634bdf0aedde06df72 */
 "buttonNext" = "Siguiente";
 
+/* loco:67beed573c592678eb085582 */
+"buttonNo" = "No";
+
 /* loco:6729ead76f93fd246802f932 */
 "buttonRetry" = "Reintentar";
 
+/* loco:67beed7f8291a886220faf53 */
+"buttonYes" = "Sí";
+
 /* loco:676538295907d1ab51014ae2 */
 "errorDownload" = "Error al descargar";
 
 /* loco:67653882015fa8304b01cbb9 */
 "errorDownloadInsufficientSpace" = "No se puede descargar: espacio insuficiente";
+
+/* loco:67beee584d2044470e0df152 */
+"reviewAlertTitle" = "¿Te gusta %@?";
+
diff --git a/Sources/InfomaniakCoreUIResources/Localizable/fr.lproj/Localizable.strings b/Sources/InfomaniakCoreUIResources/Localizable/fr.lproj/Localizable.strings
index 0cf1c68..f3e1ba4 100644
--- a/Sources/InfomaniakCoreUIResources/Localizable/fr.lproj/Localizable.strings
+++ b/Sources/InfomaniakCoreUIResources/Localizable/fr.lproj/Localizable.strings
@@ -19,11 +19,21 @@
 /* loco:675059634bdf0aedde06df72 */
 "buttonNext" = "Suivant";
 
+/* loco:67beed573c592678eb085582 */
+"buttonNo" = "Non";
+
 /* loco:6729ead76f93fd246802f932 */
 "buttonRetry" = "Réessayer";
 
+/* loco:67beed7f8291a886220faf53 */
+"buttonYes" = "Oui";
+
 /* loco:676538295907d1ab51014ae2 */
 "errorDownload" = "Erreur lors du téléchargement";
 
 /* loco:67653882015fa8304b01cbb9 */
 "errorDownloadInsufficientSpace" = "Téléchargement impossible : Espace insuffisant";
+
+/* loco:67beee584d2044470e0df152 */
+"reviewAlertTitle" = "Vous aimez les %@ ?";
+
diff --git a/Sources/InfomaniakCoreUIResources/Localizable/it.lproj/Localizable.strings b/Sources/InfomaniakCoreUIResources/Localizable/it.lproj/Localizable.strings
index b7c72ce..45ad6ee 100644
--- a/Sources/InfomaniakCoreUIResources/Localizable/it.lproj/Localizable.strings
+++ b/Sources/InfomaniakCoreUIResources/Localizable/it.lproj/Localizable.strings
@@ -19,11 +19,21 @@
 /* loco:675059634bdf0aedde06df72 */
 "buttonNext" = "Avanti";
 
+/* loco:67beed573c592678eb085582 */
+"buttonNo" = "No";
+
 /* loco:6729ead76f93fd246802f932 */
 "buttonRetry" = "Riprova";
 
+/* loco:67beed7f8291a886220faf53 */
+"buttonYes" = "Sì";
+
 /* loco:676538295907d1ab51014ae2 */
 "errorDownload" = "Errore durante il download";
 
 /* loco:67653882015fa8304b01cbb9 */
 "errorDownloadInsufficientSpace" = "Impossibile scaricare: spazio insufficiente";
+
+/* loco:67beee584d2044470e0df152 */
+"reviewAlertTitle" = "Ti piace %@?";
+


Reviews:
user: login: adrien-coye
body: I did a first review.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T08:23:00Z
user: login: adrien-coye
body: LGTM 👍 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T10:11:38Z

Review comments:
diff_hunk: @@ -0,0 +1,82 @@
+/*
+ Infomaniak Core UI - iOS
+ Copyright (C) 2025 Infomaniak Network SA
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import DesignSystem
+import InfomaniakCore
+import InfomaniakCoreCommonUI
+import InfomaniakCoreUIResources
+import InfomaniakDI
+import SwiftUI
+
+public struct AskForReviewView: View {
+    private let appName: String
+    private let feedbackURL: String
+    private let onLike: () async throws -> Void
+    private let onDislike: (URL) -> Void
+    private let reviewManager: ReviewManageable
+
+    public init(
path: Sources/InfomaniakCoreSwiftUI/Components/AlertView/AskForReviewView.swift
user: login: adrien-coye
body: This looks like something we could use from our UIKit code as well as our SwiftUI code 👍 
created_at: 2025-03-05T08:22:51Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-03-04T08:59:33Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=Infomaniak_ios-core-ui&pullRequest=89) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=Infomaniak_ios-core-ui&pullRequest=89&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=Infomaniak_ios-core-ui&pullRequest=89&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=Infomaniak_ios-core-ui&pullRequest=89&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=Infomaniak_ios-core-ui&pullRequest=89&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=Infomaniak_ios-core-ui&pullRequest=89&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=Infomaniak_ios-core-ui&pullRequest=89)


author_association: NONE