From f901436211bfbb0c4100c1db3569b380c9f94a11 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sun, 3 May 2020 21:33:57 +0800 Subject: [PATCH 1/3] mailto links now open on iOS fixes #2036 Extends `URL` with an email address `var` for `mailto` schemes and adds a decisionHandler for `mailto` schemes on `WebViewController`. If the device cannot send mail, an alert is displayed. --- NetNewsWire.xcodeproj/project.pbxproj | 8 ++++++++ Shared/Extensions/URL-Extensions.swift | 17 +++++++++++++++++ iOS/Article/WebViewController.swift | 25 +++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 Shared/Extensions/URL-Extensions.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 2ea40713b..42ec49302 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -674,6 +674,9 @@ D5F4EDB720074D6500B9E363 /* WebFeed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* WebFeed+Scriptability.swift */; }; D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; }; DD82AB0A231003F6002269DF /* SharingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD82AB09231003F6002269DF /* SharingTests.swift */; }; + DF41F3AE245EFCD7004EFB01 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */; }; + DF41F3C8245EFD45004EFB01 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */; }; + DF41F3C9245EFD46004EFB01 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */; }; FF3ABF13232599810074C542 /* ArticleSorterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF09232599450074C542 /* ArticleSorterTests.swift */; }; FF3ABF1523259DDB0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; }; FF3ABF162325AF5D0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; }; @@ -1635,6 +1638,7 @@ D5F4EDB620074D6500B9E363 /* WebFeed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebFeed+Scriptability.swift"; sourceTree = ""; }; D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = ""; }; DD82AB09231003F6002269DF /* SharingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharingTests.swift; sourceTree = ""; }; + DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL-Extensions.swift"; sourceTree = ""; }; FF3ABF09232599450074C542 /* ArticleSorterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorterTests.swift; sourceTree = ""; }; FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorter.swift; sourceTree = ""; }; FFD43E372340F320009E5CA3 /* MarkAsReadAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkAsReadAlertController.swift; sourceTree = ""; }; @@ -2405,6 +2409,7 @@ 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */, 516AE9DE2372269A007DEEAA /* IconImage.swift */, B2B8075D239C49D300F191E0 /* RSImage-AppIcons.swift */, + DF41F3AD245EFCD7004EFB01 /* URL-Extensions.swift */, ); path = Extensions; sourceTree = ""; @@ -3852,6 +3857,7 @@ 65ED401C235DEF6C0081F399 /* FaviconGenerator.swift in Sources */, 65ED401D235DEF6C0081F399 /* RefreshInterval.swift in Sources */, 65ED401E235DEF6C0081F399 /* TimelineCellData.swift in Sources */, + DF41F3C9245EFD46004EFB01 /* URL-Extensions.swift in Sources */, 65ED401F235DEF6C0081F399 /* BuiltinSmartFeedInspectorViewController.swift in Sources */, 65ED4020235DEF6C0081F399 /* AppDelegate+Scriptability.swift in Sources */, 65ED4021235DEF6C0081F399 /* NNW3Document.swift in Sources */, @@ -4038,6 +4044,7 @@ 51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */, 5108F6D22375EED2001ABC45 /* TimelineCustomizerViewController.swift in Sources */, 519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */, + DF41F3C8245EFD45004EFB01 /* URL-Extensions.swift in Sources */, FFD43E412340F488009E5CA3 /* MarkAsReadAlertController.swift in Sources */, 51C452A322650A1E00C03939 /* HTMLMetadataDownloader.swift in Sources */, 51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */, @@ -4156,6 +4163,7 @@ 51EF0F922279CA620050506E /* AccountsAddTableCellView.swift in Sources */, 849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */, 8405DDA522168C62008CE1BF /* TimelineContainerViewController.swift in Sources */, + DF41F3AE245EFCD7004EFB01 /* URL-Extensions.swift in Sources */, 844B5B671FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift in Sources */, 848D578E21543519005FFAD5 /* PasteboardWebFeed.swift in Sources */, 5144EA2F2279FAB600D19003 /* AccountsDetailViewController.swift in Sources */, diff --git a/Shared/Extensions/URL-Extensions.swift b/Shared/Extensions/URL-Extensions.swift new file mode 100644 index 000000000..be121c334 --- /dev/null +++ b/Shared/Extensions/URL-Extensions.swift @@ -0,0 +1,17 @@ +// +// URL-Extensions.swift +// NetNewsWire +// +// Created by Stuart Breckenridge on 03/05/2020. +// Copyright © 2020 Ranchero Software. All rights reserved. +// + +import Foundation + +extension URL { + + /// Extracts email address from a `URL` with a `mailto` scheme, otherwise `nil`. + var emailAddress: String? { + scheme == "mailto" ? URLComponents(url: self, resolvingAgainstBaseURL: false)?.path : nil + } +} diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index d8e59ff05..132dc48d7 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -12,6 +12,7 @@ import RSCore import Account import Articles import SafariServices +import MessageUI protocol WebViewControllerDelegate: class { func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState) @@ -310,6 +311,23 @@ extension WebViewController: WKNavigationDelegate { let vc = SFSafariViewController(url: url) self.present(vc, animated: true) } + } else if components?.scheme == "mailto" { + decisionHandler(.cancel) + + guard let emailAddress = components?.url?.emailAddress else { + return + } + + if MFMailComposeViewController.canSendMail() { + let mailComposeViewController = MFMailComposeViewController() + mailComposeViewController.setToRecipients([emailAddress]) + mailComposeViewController.mailComposeDelegate = self + self.present(mailComposeViewController, animated: true, completion: {}) + } else { + let alert = UIAlertController(title: "Error", message: "This device cannot send emails.", preferredStyle: .alert) + alert.addAction(.init(title: "Dismiss", style: .cancel, handler: nil)) + self.present(alert, animated: true, completion: nil) + } } else { decisionHandler(.allow) } @@ -666,3 +684,10 @@ private extension WebViewController { } } + +// MARK: MFMailComposeViewControllerDelegate +extension WebViewController: MFMailComposeViewControllerDelegate { + func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { + self.dismiss(animated: true, completion: nil) + } +} From e825a5d5161a231cb2d56b378b3a855514234663 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sun, 3 May 2020 22:37:01 +0800 Subject: [PATCH 2/3] Adds handling of `tel` url schemes --- Shared/Extensions/URL-Extensions.swift | 5 +++++ iOS/Article/WebViewController.swift | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Shared/Extensions/URL-Extensions.swift b/Shared/Extensions/URL-Extensions.swift index be121c334..1cac0dc6a 100644 --- a/Shared/Extensions/URL-Extensions.swift +++ b/Shared/Extensions/URL-Extensions.swift @@ -14,4 +14,9 @@ extension URL { var emailAddress: String? { scheme == "mailto" ? URLComponents(url: self, resolvingAgainstBaseURL: false)?.path : nil } + + /// Extracts telephone number from a `URL` with a `tel` scheme, otherwise `nil`. + var telNumber: String? { + scheme == "tel" ? URLComponents(url: self, resolvingAgainstBaseURL: false)?.path : nil + } } diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index 132dc48d7..c25d4b29b 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -314,7 +314,7 @@ extension WebViewController: WKNavigationDelegate { } else if components?.scheme == "mailto" { decisionHandler(.cancel) - guard let emailAddress = components?.url?.emailAddress else { + guard let emailAddress = url.emailAddress else { return } @@ -328,6 +328,13 @@ extension WebViewController: WKNavigationDelegate { alert.addAction(.init(title: "Dismiss", style: .cancel, handler: nil)) self.present(alert, animated: true, completion: nil) } + } else if components?.scheme == "tel" { + decisionHandler(.cancel) + + if UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url, options: [.universalLinksOnly : false], completionHandler: nil) + } + } else { decisionHandler(.allow) } From f58ac4d1608b785d489bc0dad9cc272c489a7497 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Mon, 4 May 2020 13:24:35 +0800 Subject: [PATCH 3/3] Wraps Alert strings in `NSLocalizedString` Also removes `tel` scheme handling. --- Shared/Extensions/URL-Extensions.swift | 4 ---- iOS/Article/WebViewController.swift | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Shared/Extensions/URL-Extensions.swift b/Shared/Extensions/URL-Extensions.swift index 1cac0dc6a..4fb9b8d7f 100644 --- a/Shared/Extensions/URL-Extensions.swift +++ b/Shared/Extensions/URL-Extensions.swift @@ -15,8 +15,4 @@ extension URL { scheme == "mailto" ? URLComponents(url: self, resolvingAgainstBaseURL: false)?.path : nil } - /// Extracts telephone number from a `URL` with a `tel` scheme, otherwise `nil`. - var telNumber: String? { - scheme == "tel" ? URLComponents(url: self, resolvingAgainstBaseURL: false)?.path : nil - } } diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index c25d4b29b..9e6d62555 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -324,8 +324,8 @@ extension WebViewController: WKNavigationDelegate { mailComposeViewController.mailComposeDelegate = self self.present(mailComposeViewController, animated: true, completion: {}) } else { - let alert = UIAlertController(title: "Error", message: "This device cannot send emails.", preferredStyle: .alert) - alert.addAction(.init(title: "Dismiss", style: .cancel, handler: nil)) + let alert = UIAlertController(title: NSLocalizedString("Error", comment: "Error"), message: NSLocalizedString("This device cannot send emails.", comment: "This device cannot send emails."), preferredStyle: .alert) + alert.addAction(.init(title: NSLocalizedString("Dismiss", comment: "Dismiss"), style: .cancel, handler: nil)) self.present(alert, animated: true, completion: nil) } } else if components?.scheme == "tel" {