Skip to content
This repository was archived by the owner on Feb 5, 2025. It is now read-only.
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
3 changes: 2 additions & 1 deletion WordPressAuthenticator.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "WordPressAuthenticator"
s.version = "1.10.6"
s.version = "1.10.7-beta.1"
s.summary = "WordPressAuthenticator implements an easy and elegant way to authenticate your WordPress Apps."

s.description = <<-DESC
Expand All @@ -21,6 +21,7 @@ Pod::Spec.new do |s|
s.resource_bundles = {
'WordPressAuthenticatorResources': [
'WordPressAuthenticator/Resources/Assets.xcassets',
'WordPressAuthenticator/Resources/SupportedEmailClients/*.plist',
'WordPressAuthenticator/Resources/Animations/*.json',
'WordPressAuthenticator/**/*.{storyboard,xib}'
]
Expand Down
52 changes: 52 additions & 0 deletions WordPressAuthenticator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
020BE74A23B0BD2E007FE54C /* WordPressAuthenticatorDisplayImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020BE74923B0BD2E007FE54C /* WordPressAuthenticatorDisplayImages.swift */; };
1A21EE9822832BC300C940C6 /* WordPressComOAuthClientFacade+Swift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A21EE9722832BC200C940C6 /* WordPressComOAuthClientFacade+Swift.swift */; };
1A4095182271AEFC009AA86D /* WPAuthenticator-Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4095152271AEFC009AA86D /* WPAuthenticator-Swift.h */; settings = {ATTRIBUTES = (Private, ); }; };
3F550D4E23DA429B007E5897 /* AppSelectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F550D4D23DA429B007E5897 /* AppSelectorTests.swift */; };
3F550D5123DA4A9C007E5897 /* LinkMailPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F550D5023DA4A9C007E5897 /* LinkMailPresenter.swift */; };
3F550D5323DA4AC6007E5897 /* URLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F550D5223DA4AC6007E5897 /* URLHandler.swift */; };
3FFF2FC123D7ED7C00D38C77 /* EmailClients.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3FFF2FC023D7ED7C00D38C77 /* EmailClients.plist */; };
3FFF2FC323D7F53200D38C77 /* AppSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFF2FC223D7F53200D38C77 /* AppSelector.swift */; };
7A7A9B9CD2D81959F9AB9AF6 /* Pods_WordPressAuthenticator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C736FF243DE333FCAB1C2614 /* Pods_WordPressAuthenticator.framework */; };
982C8E7923021C20003F1BA0 /* LoginPrologueLoginMethodViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 982C8E7823021C20003F1BA0 /* LoginPrologueLoginMethodViewController.swift */; };
98AA5A5720AA1A7000A5958A /* WPHelpIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98AA5A5620AA1A7000A5958A /* WPHelpIndicatorView.swift */; };
Expand Down Expand Up @@ -149,6 +154,11 @@
276354F054C34AD36CA32AB6 /* Pods-WordPressAuthenticator.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressAuthenticator.release-alpha.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressAuthenticator/Pods-WordPressAuthenticator.release-alpha.xcconfig"; sourceTree = "<group>"; };
33FEF45B466FF8EAAE5F3923 /* Pods-WordPressAuthenticator.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressAuthenticator.release.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressAuthenticator/Pods-WordPressAuthenticator.release.xcconfig"; sourceTree = "<group>"; };
37AFD4EF492B00CA7AEC11A3 /* Pods-WordPressAuthenticatorTests.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressAuthenticatorTests.release-alpha.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressAuthenticatorTests/Pods-WordPressAuthenticatorTests.release-alpha.xcconfig"; sourceTree = "<group>"; };
3F550D4D23DA429B007E5897 /* AppSelectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSelectorTests.swift; sourceTree = "<group>"; };
3F550D5023DA4A9C007E5897 /* LinkMailPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkMailPresenter.swift; sourceTree = "<group>"; };
3F550D5223DA4AC6007E5897 /* URLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLHandler.swift; sourceTree = "<group>"; };
3FFF2FC023D7ED7C00D38C77 /* EmailClients.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = EmailClients.plist; sourceTree = "<group>"; };
3FFF2FC223D7F53200D38C77 /* AppSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSelector.swift; sourceTree = "<group>"; };
5A441EC80D2B8D2209C2E228 /* Pods_WordPressAuthenticatorTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WordPressAuthenticatorTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8F7217C3F7A6285D9C6CF786 /* Pods-WordPressAuthenticator.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressAuthenticator.release-internal.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressAuthenticator/Pods-WordPressAuthenticator.release-internal.xcconfig"; sourceTree = "<group>"; };
982C8E7823021C20003F1BA0 /* LoginPrologueLoginMethodViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginPrologueLoginMethodViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -294,6 +304,39 @@
path = Private;
sourceTree = "<group>";
};
3F550D4B23DA3B59007E5897 /* SupportedEmailClients */ = {
isa = PBXGroup;
children = (
3FFF2FC023D7ED7C00D38C77 /* EmailClients.plist */,
);
path = SupportedEmailClients;
sourceTree = "<group>";
};
3F550D4C23DA4191007E5897 /* UI */ = {
isa = PBXGroup;
children = (
);
path = UI;
sourceTree = "<group>";
};
3F550D4F23DA4A6B007E5897 /* Email Client Picker */ = {
isa = PBXGroup;
children = (
3FFF2FC223D7F53200D38C77 /* AppSelector.swift */,
3F550D5023DA4A9C007E5897 /* LinkMailPresenter.swift */,
3F550D5223DA4AC6007E5897 /* URLHandler.swift */,
);
path = "Email Client Picker";
sourceTree = "<group>";
};
3F550D5423DA5094007E5897 /* Email Client Picker */ = {
isa = PBXGroup;
children = (
3F550D4D23DA429B007E5897 /* AppSelectorTests.swift */,
);
path = "Email Client Picker";
sourceTree = "<group>";
};
6205895375D954F46B1DFE53 /* Pods */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -477,6 +520,7 @@
children = (
B5A5274020B478160065BE81 /* Animations */,
B5E07FF3208FD13800657A9A /* Assets.xcassets */,
3F550D4B23DA3B59007E5897 /* SupportedEmailClients */,
);
path = Resources;
sourceTree = "<group>";
Expand Down Expand Up @@ -519,6 +563,7 @@
children = (
CE1B18CA20EEC31000BECC3F /* Credentials */,
B5609099208A4EAF00399AE4 /* Authenticator */,
3F550D4F23DA4A6B007E5897 /* Email Client Picker */,
B560909B208A4EB000399AE4 /* Extensions */,
B5ED7917207E993E00A8FD8C /* Logging */,
B5609098208A4EAF00399AE4 /* Model */,
Expand All @@ -538,9 +583,11 @@
B5ED7901207E976500A8FD8C /* WordPressAuthenticatorTests */ = {
isa = PBXGroup;
children = (
3F550D5423DA5094007E5897 /* Email Client Picker */,
B501C03D208FC52500D1E58F /* Authenticator */,
B501C03B208FC52400D1E58F /* Model */,
B501C03F208FC52500D1E58F /* Services */,
3F550D4C23DA4191007E5897 /* UI */,
B5ED7904207E976500A8FD8C /* Info.plist */,
);
path = WordPressAuthenticatorTests;
Expand Down Expand Up @@ -699,6 +746,7 @@
B5609118208A555600399AE4 /* SearchTableViewCell.xib in Resources */,
B560913F208A563800399AE4 /* Login.storyboard in Resources */,
B5609137208A563800399AE4 /* EmailMagicLink.storyboard in Resources */,
3FFF2FC123D7ED7C00D38C77 /* EmailClients.plist in Resources */,
FF629D9622393500004C4106 /* WordPressAuthenticator.podspec in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -880,6 +928,7 @@
CE30A2AD2257CECC00DF3CDA /* AuthenticatorCredentials.swift in Sources */,
B5609145208A563800399AE4 /* LoginViewController.swift in Sources */,
B5609139208A563800399AE4 /* LoginEmailViewController.swift in Sources */,
3F550D5323DA4AC6007E5897 /* URLHandler.swift in Sources */,
98C9195B2308E3DA00A90E12 /* AppleAuthenticator.swift in Sources */,
B56090F9208A533200399AE4 /* WordPressAuthenticator+Events.swift in Sources */,
020BE74A23B0BD2E007FE54C /* WordPressAuthenticatorDisplayImages.swift in Sources */,
Expand Down Expand Up @@ -915,11 +964,13 @@
B560911F208A555E00399AE4 /* SignupGoogleViewController.swift in Sources */,
B5609142208A563800399AE4 /* LoginNavigationController.swift in Sources */,
B56090E4208A4F9D00399AE4 /* WPNUXMainButton.m in Sources */,
3FFF2FC323D7F53200D38C77 /* AppSelector.swift in Sources */,
B560913B208A563800399AE4 /* LoginSelfHostedViewController.swift in Sources */,
B5609136208A563800399AE4 /* Login2FAViewController.swift in Sources */,
B56090E1208A4F9D00399AE4 /* WPWalkthroughTextField.m in Sources */,
B56090EF208A527000399AE4 /* WPStyleGuide+Login.swift in Sources */,
B56090D0208A4F5400399AE4 /* NUXViewControllerBase.swift in Sources */,
3F550D5123DA4A9C007E5897 /* LinkMailPresenter.swift in Sources */,
B56090DE208A4F9D00399AE4 /* WPWalkthroughOverlayView.m in Sources */,
B560910A208A54F800399AE4 /* WordPressComAccountService.swift in Sources */,
B56090FA208A533200399AE4 /* WordPressAuthenticator.swift in Sources */,
Expand All @@ -931,6 +982,7 @@
buildActionMask = 2147483647;
files = (
B501C045208FC68700D1E58F /* LoginFieldsValidationTests.swift in Sources */,
3F550D4E23DA429B007E5897 /* AppSelectorTests.swift in Sources */,
CE16177821B70C1A00B82A47 /* WordPressAuthenticatorDisplayTextTests.swift in Sources */,
B501C048208FC79C00D1E58F /* LoginFacadeTests.m in Sources */,
B501C046208FC6A700D1E58F /* WordPressAuthenticatorTests.swift in Sources */,
Expand Down
136 changes: 136 additions & 0 deletions WordPressAuthenticator/Email Client Picker/AppSelector.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import MessageUI
import UIKit

/// App selector that selects an app from a list and opens it
/// Note: it's a wrapper of UIAlertController (which cannot be sublcassed)
class AppSelector {
// the action sheet that will contain the list of apps that can be called
let alertController: UIAlertController

/// initializes the picker with a dictionary. Initialization will fail if an empty/invalid app list is passed
/// - Parameters:
/// - appList: collection of apps to be added to the selector
/// - defaultAction: default action, if not nil, will be the first element of the list
/// - sourceView: the sourceView to anchor the action sheet to
/// - urlHandler: object that handles app URL schemes; defaults to UIApplication.shared
init?(with appList: [String: String],
defaultAction: UIAlertAction? = nil,
sourceView: UIView,
urlHandler: URLHandler = UIApplication.shared) {
/// inline method that builds a list of app calls to be inserted in the action sheet
func makeAlertActions(from appList: [String: String]) -> [UIAlertAction]? {
guard !appList.isEmpty else {
return nil
}

var actions = [UIAlertAction]()
for (name, urlString) in appList {
guard let url = URL(string: urlString), urlHandler.canOpenURL(url) else {
continue
}
actions.append(UIAlertAction(title: AppSelectorTitles(rawValue: name)?.localized ?? name, style: .default) { action in
urlHandler.open(url, options: [:], completionHandler: nil)
})
}

guard !actions.isEmpty else {
return nil
}
//sort the apps alphabetically
actions = actions.sorted { $0.title ?? "" < $1.title ?? "" }
actions.append(UIAlertAction(title: AppSelectorTitles.cancel.localized, style: .cancel, handler: nil))

if let action = defaultAction {
actions.insert(action, at: 0)
}
return actions
}

guard let appCalls = makeAlertActions(from: appList) else {
return nil
}

alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
alertController.popoverPresentationController?.sourceView = sourceView
alertController.popoverPresentationController?.sourceRect = sourceView.bounds
appCalls.forEach {
alertController.addAction($0)
}
}
}


/// Initializers for Email Picker
extension AppSelector {
/// initializes the picker with a plist file in a specified bundle
convenience init?(with plistFile: String,
in bundle: Bundle,
defaultAction: UIAlertAction? = nil,
sourceView: UIView) {

guard let plistPath = bundle.path(forResource: plistFile, ofType: "plist"),
let availableApps = NSDictionary(contentsOfFile: plistPath) as? [String: String] else {
return nil
}
self.init(with: availableApps,
defaultAction: defaultAction,
sourceView: sourceView)
}

/// Convenience init for a picker that calls supported email clients apps, defined in EmailClients.plist
convenience init?(sourceView: UIView) {
guard let bundlePath = Bundle(for: type(of: self))
.path(forResource: "WordPressAuthenticatorResources", ofType: "bundle"),
let wpAuthenticatorBundle = Bundle(path: bundlePath) else {
return nil
}

let plistFile = "EmailClients"
var defaultAction: UIAlertAction?

// if available, prepend apple mail
if MFMailComposeViewController.canSendMail(), let url = URL(string: "message://") {
defaultAction = UIAlertAction(title: AppSelectorTitles.appleMail.localized, style: .default) { action in
UIApplication.shared.open(url)
}
}
self.init(with: plistFile,
in: wpAuthenticatorBundle,
defaultAction: defaultAction,
sourceView: sourceView)
}
}


/// Localizable app selector titles
enum AppSelectorTitles: String {
case appleMail
case gmail
case airmail
case msOutlook
case spark
case yahooMail
case fastmail
case cancel

var localized: String {
switch self {
case .appleMail:
return NSLocalizedString("Mail (Default)", comment: "Option to select the Apple Mail app when logging in with magic links")
case .gmail:
return NSLocalizedString("Gmail", comment: "Option to select the Gmail app when logging in with magic links")
case .airmail:
return NSLocalizedString("Airmail", comment: "Option to select the Airmail app when logging in with magic links")
case .msOutlook:
return NSLocalizedString("Microsoft Outlook", comment: "Option to select the Microsft Outlook app when logging in with magic links")
case .spark:
return NSLocalizedString("Spark", comment: "Option to select the Spark email app when logging in with magic links")
case .yahooMail:
return NSLocalizedString("Yahoo Mail", comment: "Option to select the Yahoo Mail app when logging in with magic links")
case .fastmail:
return NSLocalizedString("Fastmail", comment: "Option to select the Fastmail app when logging in with magic links")
case .cancel:
return NSLocalizedString("Cancel", comment: "Option to cancel the email app selection when logging in with magic links")
}
}
}
49 changes: 49 additions & 0 deletions WordPressAuthenticator/Email Client Picker/LinkMailPresenter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import MessageUI


/// Email picker presenter
class LinkMailPresenter {

private let emailAddress: String

init(emailAddress: String) {
self.emailAddress = emailAddress
}

/// Presents the available mail clients in an action sheet. If none is available,
/// Falls back to Apple Mail and opens it.
/// If not even Apple Mail is available, presents an alert to check your email
/// - Parameters:
/// - viewController: the UIViewController that will present the action sheet
/// - appSelector: the app picker that contains the available clients. Nil if no clients are available
/// reads the supported email clients from EmailClients.plist
func presentEmailClients(on viewController: UIViewController,
appSelector: AppSelector?) {

guard let picker = appSelector else {
// fall back to Apple Mail if no other clients are installed
if MFMailComposeViewController.canSendMail(), let url = URL(string: "message://") {
UIApplication.shared.open(url)
} else {
showAlertToCheckEmail(on: viewController)
}
return
}
viewController.present(picker.alertController, animated: true)
}

private func showAlertToCheckEmail(on viewController: UIViewController) {
let title = NSLocalizedString("Check your email!",
comment: "Alert title for check your email during logIn/signUp.")

let message = String.localizedStringWithFormat(NSLocalizedString("We just emailed a link to %@. Please check your mail app and tap the link to log in.",
comment: "message to ask a user to check their email for a WordPress.com email"), emailAddress)

let alertController = UIAlertController(title: title,
message: message,
preferredStyle: .alert)
alertController.addCancelActionWithTitle(NSLocalizedString("OK",
comment: "Button title. An acknowledgement of the message displayed in a prompt."))
viewController.present(alertController, animated: true, completion: nil)
}
}
13 changes: 13 additions & 0 deletions WordPressAuthenticator/Email Client Picker/URLHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/// Generic type that handles URL Schemes
protocol URLHandler {
/// checks if the specified URL can be opened
func canOpenURL(_ url: URL) -> Bool
/// opens the specified URL
func open(_ url: URL,
options: [UIApplication.OpenExternalURLOptionsKey : Any],
completionHandler completion: ((Bool) -> Void)?)
}

/// conforms UIApplication to URLHandler to allow dependency injection
extension UIApplication: URLHandler {}

Loading