diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 6775c52f513d..d6b4e6a4a11a 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -275,7 +275,7 @@ 58BDEB992A98F4ED00F578F2 /* AnyTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BDEB982A98F4ED00F578F2 /* AnyTransport.swift */; }; 58BDEB9B2A98F58600F578F2 /* TimeServerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BDEB9A2A98F58600F578F2 /* TimeServerProxy.swift */; }; 58BDEB9D2A98F69E00F578F2 /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BDEB9C2A98F69E00F578F2 /* MemoryCache.swift */; }; - 58BE4B9D2B18A85B007EA1D3 /* NSAttributedString+Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7CF2B02560400F864E0 /* NSAttributedString+Markdown.swift */; }; + 58BE4B9D2B18A85B007EA1D3 /* NSAttributedString+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7CF2B02560400F864E0 /* NSAttributedString+Extensions.swift */; }; 58BFA5C622A7C97F00A6173D /* RelayCacheTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5C522A7C97F00A6173D /* RelayCacheTracker.swift */; }; 58BFA5CC22A7CE1F00A6173D /* ApplicationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */; }; 58C3A4B222456F1B00340BDB /* AccountInputGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C3A4B122456F1A00340BDB /* AccountInputGroupView.swift */; }; @@ -390,7 +390,7 @@ 58D22435294C975B0029F5F8 /* Operations.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223A5294C8A480029F5F8 /* Operations.framework */; platformFilter = ios; }; 58DDA18F2ABC32380039C360 /* Timings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DDA18E2ABC32380039C360 /* Timings.swift */; }; 58DF28A52417CB4B00E836B0 /* StorePaymentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DF28A42417CB4B00E836B0 /* StorePaymentManager.swift */; }; - 58DFF7D02B02560400F864E0 /* NSAttributedString+Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7CF2B02560400F864E0 /* NSAttributedString+Markdown.swift */; }; + 58DFF7D02B02560400F864E0 /* NSAttributedString+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7CF2B02560400F864E0 /* NSAttributedString+Extensions.swift */; }; 58DFF7D22B0256A300F864E0 /* MarkdownStylingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */; }; 58DFF7D32B02570000F864E0 /* MarkdownStylingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */; }; 58DFF7D82B02774C00F864E0 /* ListItemPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D72B02774C00F864E0 /* ListItemPickerViewController.swift */; }; @@ -605,6 +605,7 @@ 7ADCB2DA2B6A730400C88F89 /* IPOverrideRepositoryStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */; }; 7AE044BB2A935726003915D8 /* Routing.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A88DCD02A8FABBE00D2FF0E /* Routing.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7AE2414A2C20682B0076CE33 /* FormsheetPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE241482C20682B0076CE33 /* FormsheetPresentationController.swift */; }; + 7AE90B682C2D726000375A60 /* NSParagraphStyle+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE90B672C2D726000375A60 /* NSParagraphStyle+Extensions.swift */; }; 7AEBA52C2C22C65B0018BEC5 /* TimeInterval+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEBA52B2C22C65B0018BEC5 /* TimeInterval+Timeout.swift */; }; 7AED35CC2BD13F60002A67D1 /* ApplicationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */; }; 7AED35CD2BD13FC4002A67D1 /* ApplicationTarget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58C76A072A33850E00100D75 /* ApplicationTarget.swift */; }; @@ -1796,7 +1797,7 @@ 58D229B6298D1D5200BB5A2D /* URLRequestProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestProxy.swift; sourceTree = ""; }; 58DDA18E2ABC32380039C360 /* Timings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timings.swift; sourceTree = ""; }; 58DF28A42417CB4B00E836B0 /* StorePaymentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorePaymentManager.swift; sourceTree = ""; }; - 58DFF7CF2B02560400F864E0 /* NSAttributedString+Markdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Markdown.swift"; sourceTree = ""; }; + 58DFF7CF2B02560400F864E0 /* NSAttributedString+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Extensions.swift"; sourceTree = ""; }; 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownStylingOptions.swift; sourceTree = ""; }; 58DFF7D72B02774C00F864E0 /* ListItemPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListItemPickerViewController.swift; sourceTree = ""; }; 58DFF7D92B02862E00F864E0 /* ShadowsocksCipherOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksCipherOptions.swift; sourceTree = ""; }; @@ -1991,6 +1992,7 @@ 7ADCB2D72B6A6EB300C88F89 /* AnyRelay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyRelay.swift; sourceTree = ""; }; 7ADCB2D92B6A730400C88F89 /* IPOverrideRepositoryStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPOverrideRepositoryStub.swift; sourceTree = ""; }; 7AE241482C20682B0076CE33 /* FormsheetPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormsheetPresentationController.swift; sourceTree = ""; }; + 7AE90B672C2D726000375A60 /* NSParagraphStyle+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSParagraphStyle+Extensions.swift"; sourceTree = ""; }; 7AEBA52B2C22C65B0018BEC5 /* TimeInterval+Timeout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Timeout.swift"; sourceTree = ""; }; 7AEF7F192AD00F52006FE45D /* AppMessageHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppMessageHandler.swift; sourceTree = ""; }; 7AF10EB12ADE859200C090B9 /* AlertViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertViewController.swift; sourceTree = ""; }; @@ -3011,9 +3013,10 @@ 7AF9BE8F2A39F26000DBFEDB /* Collection+Sorting.swift */, 7A0C0F622A979C4A0058EFCE /* Coordinator+Router.swift */, 5811DE4F239014550011EB53 /* NEVPNStatus+Debug.swift */, - 58DFF7CF2B02560400F864E0 /* NSAttributedString+Markdown.swift */, + 58DFF7CF2B02560400F864E0 /* NSAttributedString+Extensions.swift */, 5827B0C42B14D3E800CCBBA1 /* NSDiffableDataSourceSnapshot+Reconfigure.swift */, 587D9675288989DB00CD8F1C /* NSLayoutConstraint+Helpers.swift */, + 7AE90B672C2D726000375A60 /* NSParagraphStyle+Extensions.swift */, 5871FB9F254C26BF0051A0A4 /* NSRegularExpression+IPAddress.swift */, 06FAE67828F83CA50033DD93 /* RESTCreateApplePaymentResponse+Localization.swift */, 58B9EB142489139B00095626 /* RESTError+Display.swift */, @@ -5494,7 +5497,7 @@ A9A5FA162ACB05160083449F /* RotateKeyOperation.swift in Sources */, F072D3CF2C07122400906F64 /* MultihopUpdaterTests.swift in Sources */, F09D04B52AE93CB6003D4F89 /* OutgoingConnectionProxy+Stub.swift in Sources */, - 58BE4B9D2B18A85B007EA1D3 /* NSAttributedString+Markdown.swift in Sources */, + 58BE4B9D2B18A85B007EA1D3 /* NSAttributedString+Extensions.swift in Sources */, A9A5FA172ACB05160083449F /* SendTunnelProviderMessageOperation.swift in Sources */, 7A83A0C62B29A750008B5CE7 /* APIAccessMethodsTests.swift in Sources */, A9A5FA182ACB05160083449F /* SetAccountOperation.swift in Sources */, @@ -5784,6 +5787,7 @@ 5820EDA9288FE064006BF4E4 /* DeviceManagementInteractor.swift in Sources */, 7A9CCCB92A96302800DD6A34 /* LocationCoordinator.swift in Sources */, 58FB865A26EA214400F188BC /* RelayCacheTrackerObserver.swift in Sources */, + 7AE90B682C2D726000375A60 /* NSParagraphStyle+Extensions.swift in Sources */, 581DFAEC2B1770C1005D6D1C /* AccessMethodViewModel+NavigationItem.swift in Sources */, 58ACF64D26567A5000ACE4B7 /* CustomSwitch.swift in Sources */, F0DA874B2A9CBACB006044F1 /* AccountNumberRow.swift in Sources */, @@ -5942,7 +5946,7 @@ 5878A27329091D6D0096FC88 /* TunnelBlockObserver.swift in Sources */, A9E034642ABB302000E59A5A /* UIEdgeInsets+Extensions.swift in Sources */, 58CEB2E92AFBBA4A00E6E088 /* AddAccessMethodCoordinator.swift in Sources */, - 58DFF7D02B02560400F864E0 /* NSAttributedString+Markdown.swift in Sources */, + 58DFF7D02B02560400F864E0 /* NSAttributedString+Extensions.swift in Sources */, 58E0A98827C8F46300FE6BDD /* Tunnel.swift in Sources */, 7A12D0762B062D5C00E9602D /* URLSessionProtocol.swift in Sources */, 58ACF64F26567A7100ACE4B7 /* CustomSwitchContainer.swift in Sources */, diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift index 0201cfd29f05..066ca9f00629 100644 --- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift @@ -258,7 +258,7 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo Continues application flow by evaluating what route to present next. */ private func continueFlow(animated: Bool) { - var nextRoutes = evaluateNextRoutes() + let nextRoutes = evaluateNextRoutes() for nextRoute in nextRoutes { router.present(nextRoute, animated: animated) @@ -413,7 +413,9 @@ final class ApplicationCoordinator: Coordinator, Presenting, RootContainerViewCo coordinator.didLogout = { [weak self] preferredAccountNumber in guard let self else { return } router.dismissAll(.primary, animated: true) - continueFlow(animated: true) + DispatchQueue.main.async { + self.continueFlow(animated: true) + } preferredAccountNumberSubject.send(preferredAccountNumber) } diff --git a/ios/MullvadVPN/Coordinators/WelcomeCoordinator.swift b/ios/MullvadVPN/Coordinators/WelcomeCoordinator.swift index eefe8ce9d1f5..7ae0f7876e84 100644 --- a/ios/MullvadVPN/Coordinators/WelcomeCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/WelcomeCoordinator.swift @@ -90,9 +90,7 @@ extension WelcomeCoordinator: WelcomeViewControllerDelegate { This is the name assigned to the device. Each device logged in on a \ Mullvad account gets a unique name that helps \ you identify it when you manage your devices in the app or on the website. - You can have up to 5 devices logged in on one Mullvad account. - If you log out, the device and the device name is removed. \ When you log back in again, the device will get a new name. """, diff --git a/ios/MullvadVPN/Extensions/NSAttributedString+Extensions.swift b/ios/MullvadVPN/Extensions/NSAttributedString+Extensions.swift new file mode 100644 index 000000000000..fa5cf22fccd0 --- /dev/null +++ b/ios/MullvadVPN/Extensions/NSAttributedString+Extensions.swift @@ -0,0 +1,52 @@ +// +// NSAttributedString+Markdown.swift +// MullvadVPN +// +// Created by pronebird on 19/11/2021. +// Copyright © 2021 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +extension NSAttributedString { + enum MarkdownElement { + case bold + } + + convenience init( + markdownString: String, + options: MarkdownStylingOptions, + applyEffect: ((MarkdownElement, String) -> [NSAttributedString.Key: Any])? = nil + ) { + let attributedString = NSMutableAttributedString() + let components = markdownString.components(separatedBy: "**") + + for (stringIndex, string) in components.enumerated() { + var attributes: [NSAttributedString.Key: Any] = [:] + + if stringIndex % 2 == 0 { + attributes[.font] = options.font + } else { + attributes[.font] = options.boldFont + attributes.merge(applyEffect?(.bold, string) ?? [:], uniquingKeysWith: { $1 }) + } + + attributedString.append(NSAttributedString(string: string, attributes: attributes)) + } + + attributedString.addAttribute( + .paragraphStyle, + value: options.paragraphStyle, + range: NSRange(location: 0, length: attributedString.length) + ) + + self.init(attributedString: attributedString) + } +} + +extension NSMutableAttributedString { + func apply(paragraphStyle: NSParagraphStyle) { + let attributeRange = NSRange(location: 0, length: length) + addAttribute(.paragraphStyle, value: paragraphStyle, range: attributeRange) + } +} diff --git a/ios/MullvadVPN/Extensions/NSAttributedString+Markdown.swift b/ios/MullvadVPN/Extensions/NSAttributedString+Markdown.swift deleted file mode 100644 index 173888f6065e..000000000000 --- a/ios/MullvadVPN/Extensions/NSAttributedString+Markdown.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// NSAttributedString+Markdown.swift -// MullvadVPN -// -// Created by pronebird on 19/11/2021. -// Copyright © 2021 Mullvad VPN AB. All rights reserved. -// - -import UIKit - -extension NSAttributedString { - enum MarkdownElement { - case bold - } - - convenience init( - markdownString: String, - options: MarkdownStylingOptions, - applyEffect: ((MarkdownElement, String) -> [NSAttributedString.Key: Any])? = nil - ) { - let attributedString = NSMutableAttributedString() - let paragraphs = markdownString.replacingOccurrences(of: "\r\n", with: "\n").components(separatedBy: "\n\n") - - for (paragraphIndex, paragraph) in paragraphs.enumerated() { - let attributedParagraph = NSMutableAttributedString() - - // Replace \n with \u2028 to prevent attributed string from picking up single line breaks as paragraphs. - let components = paragraph.replacingOccurrences(of: "\n", with: "\u{2028}") - .components(separatedBy: "**") - - if paragraphIndex > 0 { - // Add single line break to add spacing between paragraphs. - attributedParagraph.append(NSAttributedString(string: "\n")) - } - - for (stringIndex, string) in components.enumerated() { - var attributes: [NSAttributedString.Key: Any] = [:] - - if stringIndex % 2 == 0 { - attributes[.font] = options.font - } else { - attributes[.font] = options.boldFont - attributes.merge(applyEffect?(.bold, string) ?? [:], uniquingKeysWith: { $1 }) - } - - attributedParagraph.append(NSAttributedString(string: string, attributes: attributes)) - } - - attributedString.append(attributedParagraph) - } - - attributedString.addAttribute( - .paragraphStyle, - value: options.paragraphStyle, - range: NSRange(location: 0, length: attributedString.length) - ) - - self.init(attributedString: attributedString) - } -} diff --git a/ios/MullvadVPN/Extensions/NSParagraphStyle+Extensions.swift b/ios/MullvadVPN/Extensions/NSParagraphStyle+Extensions.swift new file mode 100644 index 000000000000..eb6249172d2f --- /dev/null +++ b/ios/MullvadVPN/Extensions/NSParagraphStyle+Extensions.swift @@ -0,0 +1,20 @@ +// +// NSParagraphStyle+Extensions.swift +// MullvadVPN +// +// Created by Jon Petersson on 2024-06-27. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import UIKit + +extension NSParagraphStyle { + static var alert: NSParagraphStyle { + let style = NSMutableParagraphStyle() + + style.paragraphSpacing = 16 + style.lineBreakMode = .byWordWrapping + + return style + } +} diff --git a/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift b/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift index c1003313b4e4..cc717a618053 100644 --- a/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift +++ b/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift @@ -225,16 +225,11 @@ class AlertViewController: UIViewController { private func addMessage(_ message: String) { let label = UILabel() - let font = UIFont.preferredFont(forTextStyle: .body) - let style = NSMutableParagraphStyle() - style.paragraphSpacing = 16 - style.lineBreakMode = .byWordWrapping - - label.attributedText = NSAttributedString( - string: message, - attributes: [.paragraphStyle: style] - ) - label.font = font + let message = NSMutableAttributedString(string: message) + message.apply(paragraphStyle: .alert) + + label.attributedText = message + label.font = .preferredFont(forTextStyle: .body) label.textColor = .white.withAlphaComponent(0.8) label.adjustsFontForContentSizeCategory = true label.numberOfLines = 0 @@ -245,14 +240,9 @@ class AlertViewController: UIViewController { private func addMessage(_ message: NSAttributedString) { let label = UILabel() - let style = NSMutableParagraphStyle() - style.paragraphSpacing = 16 - style.lineBreakMode = .byWordWrapping - let message = NSMutableAttributedString(attributedString: message) - let attributeRange = NSRange(location: 0, length: message.length) - message.removeAttribute(.paragraphStyle, range: attributeRange) - message.addAttribute(.paragraphStyle, value: style, range: attributeRange) + message.removeAttribute(.paragraphStyle, range: NSRange(location: 0, length: message.length)) + message.apply(paragraphStyle: .alert) label.attributedText = message label.textColor = .white.withAlphaComponent(0.8) diff --git a/ios/MullvadVPN/View controllers/CreationAccount/Completed/SetupAccountCompletedContentView.swift b/ios/MullvadVPN/View controllers/CreationAccount/Completed/SetupAccountCompletedContentView.swift index 6ea08e39145c..20504078bbe4 100644 --- a/ios/MullvadVPN/View controllers/CreationAccount/Completed/SetupAccountCompletedContentView.swift +++ b/ios/MullvadVPN/View controllers/CreationAccount/Completed/SetupAccountCompletedContentView.swift @@ -32,22 +32,25 @@ class SetupAccountCompletedContentView: UIView { private let commentLabel: UILabel = { let label = UILabel() - label.font = .preferredFont(forTextStyle: .body) - label.textColor = .white - label.adjustsFontForContentSizeCategory = true - label.lineBreakMode = .byWordWrapping - label.numberOfLines = .zero - label.text = NSLocalizedString( + + let message = NSMutableAttributedString(string: NSLocalizedString( "CREATED_ACCOUNT_CONFIRMATION_PAGE_BODY", tableName: "CreatedAccountConfirmation", value: """ Go ahead and start using the app to begin reclaiming your online privacy. - To continue your journey as a privacy ninja, \ visit our website to pick up other privacy-friendly habits and tools. """, comment: "" - ) + )) + message.apply(paragraphStyle: .alert) + + label.attributedText = message + label.font = .preferredFont(forTextStyle: .body) + label.textColor = .white + label.adjustsFontForContentSizeCategory = true + label.numberOfLines = .zero + return label }() diff --git a/ios/MullvadVPN/View controllers/RedeemVoucher/LogoutDialogueView.swift b/ios/MullvadVPN/View controllers/RedeemVoucher/LogoutDialogueView.swift index c1c192ad667b..0e082914949f 100644 --- a/ios/MullvadVPN/View controllers/RedeemVoucher/LogoutDialogueView.swift +++ b/ios/MullvadVPN/View controllers/RedeemVoucher/LogoutDialogueView.swift @@ -20,11 +20,8 @@ class LogoutDialogueView: UIView { private let messageLabel: UILabel = { let label = UILabel() - label.font = .preferredFont(forTextStyle: .callout, weight: .semibold) - label.numberOfLines = .zero - label.lineBreakMode = .byWordWrapping - label.textColor = .white - label.text = NSLocalizedString( + + let message = NSMutableAttributedString(string: NSLocalizedString( "ACCOUNT_NUMBER_AS_VOUCHER_INPUT_ERROR_BODY", tableName: "CreateAccountRedeemingVoucher", value: """ @@ -33,7 +30,14 @@ class LogoutDialogueView: UIView { If so, click log out below to log in with the other account number. """, comment: "" - ) + )) + message.apply(paragraphStyle: .alert) + + label.attributedText = message + label.font = .preferredFont(forTextStyle: .callout, weight: .semibold) + label.numberOfLines = .zero + label.textColor = .white + return label }() diff --git a/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceContentView.swift b/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceContentView.swift index 945b2bccc573..dc70124b446b 100644 --- a/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceContentView.swift +++ b/ios/MullvadVPN/View controllers/TermsOfService/TermsOfServiceContentView.swift @@ -29,24 +29,27 @@ class TermsOfServiceContentView: UIView { let bodyLabel: UILabel = { let bodyLabel = UILabel() - bodyLabel.translatesAutoresizingMaskIntoConstraints = false - bodyLabel.font = UIFont.systemFont(ofSize: 18) - bodyLabel.textColor = .white - bodyLabel.numberOfLines = 0 - bodyLabel.text = NSLocalizedString( + + let message = NSMutableAttributedString(string: NSLocalizedString( "PRIVACY_NOTICE_BODY", tableName: "TermsOfService", value: """ You have a right to privacy. That’s why we never store activity logs, don’t ask for personal \ information, and encourage anonymous payments. - In some situations, as outlined in our privacy policy, we might process personal data that you \ choose to send, for example if you email us. - We strongly believe in retaining as little data as possible because we want you to remain anonymous. """, comment: "" - ) + )) + message.apply(paragraphStyle: .alert) + + bodyLabel.attributedText = message + bodyLabel.translatesAutoresizingMaskIntoConstraints = false + bodyLabel.font = UIFont.systemFont(ofSize: 18) + bodyLabel.textColor = .white + bodyLabel.numberOfLines = 0 + return bodyLabel }() diff --git a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift index 34fdf670d8f9..b94420dd67ad 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/CustomDNSViewController.swift @@ -113,14 +113,13 @@ extension CustomDNSViewController: DNSSettingsDataSourceDelegate { When this feature is enabled it stops the device from contacting certain \ domains or websites known for distributing ads, malware, trackers and more. \ This might cause issues on certain websites, services, and apps. - Attention: this setting cannot be used in combination with **Use custom DNS**. """, comment: "" ), options: MarkdownStylingOptions(font: .preferredFont(forTextStyle: .body))) case .blockMalware: - message = NSAttributedString(string: NSLocalizedString( + message = NSAttributedString(markdownString: NSLocalizedString( "VPN_SETTINGS_CONTENT_BLOCKERS_MALWARE", tableName: "ContentBlockers", value: """ @@ -128,7 +127,7 @@ extension CustomDNSViewController: DNSSettingsDataSourceDelegate { be treated as such, this is just an extra layer of protection. """, comment: "" - )) + ), options: MarkdownStylingOptions(font: .preferredFont(forTextStyle: .body))) default: assertionFailure("No matching InfoButtonItem") diff --git a/ios/MullvadVPNTests/MullvadLogging/LoggingTests.swift b/ios/MullvadVPNTests/MullvadLogging/LoggingTests.swift index 7661c2714f9f..d22222ea3b97 100644 --- a/ios/MullvadVPNTests/MullvadLogging/LoggingTests.swift +++ b/ios/MullvadVPNTests/MullvadLogging/LoggingTests.swift @@ -12,9 +12,11 @@ import XCTest class MullvadLoggingTests: XCTestCase { let fileManager = FileManager.default - let directoryPath = FileManager.default.temporaryDirectory.appendingPathComponent("LoggingTests", isDirectory: true) + var directoryPath: URL! override func setUpWithError() throws { + directoryPath = FileManager.default.temporaryDirectory.appendingPathComponent("LoggingTests", isDirectory: true) + try fileManager.createDirectory( at: directoryPath, withIntermediateDirectories: true