Commit a640a537 authored by Jose Blaya's avatar Jose Blaya
Browse files

Sign in with apple button to use AppleID email in the purchase flow

parent 1f08d17b
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="AJf-iF-18P">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="AJf-iF-18P">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
......@@ -818,6 +818,7 @@ You can come back to the app later to finish the process.</string>
<viewLayoutGuide key="safeArea" id="Jrv-85-gga"/>
</view>
<connections>
<outlet property="addEmailContainer" destination="A8t-3o-Y1d" id="1R7-ct-rfy"/>
<outlet property="buttonConfirm" destination="9Hh-df-yby" id="HSF-Np-NCm"/>
<outlet property="labelSubtitle" destination="3b5-dH-9MA" id="9nW-wR-AEb"/>
<outlet property="labelTitle" destination="8dA-qn-p1Q" id="zDW-ML-hpx"/>
......
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="VHM-bG-giz">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="VHM-bG-giz">
<device id="retina4_0" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
......@@ -713,11 +713,11 @@ You will not be charged during this process.</string>
<objects>
<viewController storyboardIdentifier="PIAWelcomeViewController" automaticallyAdjustsScrollViewInsets="NO" id="vXZ-lx-hvc" customClass="PIAWelcomeViewController" customModule="PIALibrary" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="548"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NEF-LS-PZ6">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="548"/>
<connections>
<segue destination="lku-HF-3OI" kind="embed" id="UXm-xH-vgv"/>
</connections>
......@@ -1037,6 +1037,10 @@ You will not be charged during this process.</string>
<point key="canvasLocation" x="-2973.75" y="-203.87323943661971"/>
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="aVb-49-Y5l"/>
<segue reference="kNL-g4-6nK"/>
</inferredMetricsTieBreakers>
<resources>
<image name="close-icon" width="50" height="50"/>
<image name="icon-selected" width="16" height="16"/>
......@@ -1046,8 +1050,4 @@ You will not be charged during this process.</string>
<image name="plan-unselected" width="46" height="46"/>
<image name="scrollableMap-dark" width="1740" height="870"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="tth-4B-3Z0"/>
<segue reference="kNL-g4-6nK"/>
</inferredMetricsTieBreakers>
</document>
......@@ -31,6 +31,7 @@
"purchase.confirm.plan" = "You are purchasing the %@ plan";
"purchase.email.why" = "We need your email to send your username and password.";
"purchase.submit" = "Submit";
"purchase.or" = "or";
"redeem.title" = "Redeem gift card";
"redeem.subtitle" = "Type in your email address and the %lu digit PIN from your gift card or trial card below.";
......
......@@ -97,4 +97,7 @@ protocol PlainStore: class {
var ikeV2EncryptionAlgorithm: String { get set }
//MARK: Sign in with Apple
var signInWithAppleFakeEmail: String? { get set }
}
......@@ -56,6 +56,8 @@ private protocol PreferencesStore: class {
var ikeV2EncryptionAlgorithm: String { get set }
var signInWithAppleFakeEmail: String? { get set }
func vpnCustomConfiguration(for vpnType: String) -> VPNCustomConfiguration?
func setVPNCustomConfiguration(_ customConfiguration: VPNCustomConfiguration, for vpnType: String)
......@@ -83,6 +85,7 @@ private extension PreferencesStore {
nmtRulesEnabled = source.nmtRulesEnabled
ikeV2IntegrityAlgorithm = source.ikeV2IntegrityAlgorithm
ikeV2EncryptionAlgorithm = source.ikeV2EncryptionAlgorithm
signInWithAppleFakeEmail = source.signInWithAppleFakeEmail
}
}
......@@ -301,6 +304,17 @@ extension Client {
accessedDatabase.plain.nmtRulesEnabled = newValue
}
}
/// Sign in with Apple generates only once the fake email. We store it just in case the user tries to use it multiple time
public fileprivate(set) var signInWithAppleFakeEmail: String? {
get {
return accessedDatabase.plain.signInWithAppleFakeEmail
}
set {
accessedDatabase.plain.signInWithAppleFakeEmail = newValue
}
}
}
}
......@@ -330,6 +344,7 @@ extension Client.Preferences {
nmtRulesEnabled = false
ikeV2IntegrityAlgorithm = IKEv2IntegrityAlgorithm.defaultIntegrity.value()
ikeV2EncryptionAlgorithm = IKEv2EncryptionAlgorithm.defaultAlgorithm.value()
signInWithAppleFakeEmail = nil
}
/**
......@@ -400,6 +415,9 @@ extension Client.Preferences {
/// :nodoc:
public var ikeV2EncryptionAlgorithm: String
/// :nodoc:
public var signInWithAppleFakeEmail: String?
/// :nodoc:
public func vpnCustomConfiguration(for vpnType: String) -> VPNCustomConfiguration? {
return vpnCustomConfigurations[vpnType]
......
......@@ -80,6 +80,8 @@ class UserDefaultsStore: PlainStore, ConfigurationAccess {
static let ikeV2EncryptionAlgorithm = "IKEV2EncryptionAlgorithm"
static let signInWithAppleFakeEmail = "SignInWithAppleFakeEmail"
}
private let backend: UserDefaults
......@@ -472,6 +474,15 @@ class UserDefaultsStore: PlainStore, ConfigurationAccess {
}
}
var signInWithAppleFakeEmail: String? {
get {
return backend.string(forKey: Entries.signInWithAppleFakeEmail)
}
set {
backend.set(newValue, forKey: Entries.signInWithAppleFakeEmail)
}
}
// MARK: Lifecycle
func reset() {
......@@ -492,6 +503,7 @@ class UserDefaultsStore: PlainStore, ConfigurationAccess {
backend.removeObject(forKey: Entries.ikeV2IntegrityAlgorithm)
backend.removeObject(forKey: Entries.ikeV2EncryptionAlgorithm)
backend.removeObject(forKey: Entries.serverNetwork)
backend.removeObject(forKey: Entries.signInWithAppleFakeEmail)
backend.synchronize()
}
......
......@@ -327,6 +327,8 @@ internal enum L10n {
internal enum Purchase {
/// Continue
internal static let `continue` = L10n.tr("Welcome", "purchase.continue")
/// or
internal static let or = L10n.tr("Welcome", "purchase.or")
/// Submit
internal static let submit = L10n.tr("Welcome", "purchase.submit")
/// 7-day money back guarantee
......
......@@ -22,6 +22,7 @@
import UIKit
import SwiftyBeaver
import AuthenticationServices
private let log = SwiftyBeaver.self
......@@ -31,7 +32,9 @@ public class ConfirmVPNPlanViewController: AutolayoutViewController, BrandableNa
@IBOutlet private weak var textEmail: BorderedTextField!
@IBOutlet private weak var labelTitle: UILabel!
@IBOutlet private weak var labelSubtitle: UILabel!
@IBOutlet private weak var addEmailContainer: UIView!
private var labelOr = UILabel()
private var signupEmail: String?
private var signupTransaction: InAppTransaction?
var metadata: SignupMetadata!
......@@ -61,6 +64,10 @@ public class ConfirmVPNPlanViewController: AutolayoutViewController, BrandableNa
textEmail.text = preset.purchaseEmail
self.styleConfirmButton()
if #available(iOSApplicationExtension 13.0, *) {
setupAppleSignInUI()
}
}
@IBAction private func signUp(_ sender: Any?) {
......@@ -157,6 +164,50 @@ public class ConfirmVPNPlanViewController: AutolayoutViewController, BrandableNa
}
private func setupAppleSignInUI() {
if #available(iOSApplicationExtension 13.0, *) {
labelOr.text = L10n.Welcome.Purchase.or.uppercased()
labelOr.textAlignment = .center
let signInWithAppleButton = ASAuthorizationAppleIDButton(type: .signIn, style: Theme.current.palette.appearance == .dark ? .whiteOutline : .black)
signInWithAppleButton.addTarget(self, action: #selector(handleAuthorizationAppleID), for: .touchUpInside)
self.addEmailContainer.addSubview(signInWithAppleButton)
self.addEmailContainer.addSubview(labelOr)
labelOr.translatesAutoresizingMaskIntoConstraints = false
signInWithAppleButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
labelOr.topAnchor.constraint(equalTo: self.buttonConfirm.bottomAnchor, constant: 15),
labelOr.leftAnchor.constraint(equalTo: self.addEmailContainer.leftAnchor),
labelOr.rightAnchor.constraint(equalTo: self.addEmailContainer.rightAnchor),
labelOr.heightAnchor.constraint(equalToConstant: 15),
signInWithAppleButton.topAnchor.constraint(equalTo: self.labelOr.bottomAnchor, constant: 15),
signInWithAppleButton.leftAnchor.constraint(equalTo: self.addEmailContainer.leftAnchor),
signInWithAppleButton.rightAnchor.constraint(equalTo: self.addEmailContainer.rightAnchor),
signInWithAppleButton.heightAnchor.constraint(equalToConstant: 50)
])
}
}
// MARK: Actions
@IBAction private func handleAuthorizationAppleID() {
if #available(iOSApplicationExtension 13.0, *) {
let request = ASAuthorizationAppleIDProvider().createRequest()
request.requestedScopes = [.email]
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.presentationContextProvider = self
controller.performRequests()
}
}
// MARK: Restylable
override public func viewShouldRestyle() {
super.viewShouldRestyle()
......@@ -166,6 +217,7 @@ public class ConfirmVPNPlanViewController: AutolayoutViewController, BrandableNa
Theme.current.applyInput(textEmail)
Theme.current.applyTitle(labelTitle, appearance: .dark)
Theme.current.applySubtitle(labelSubtitle)
Theme.current.applySubtitle(labelOr)
}
private func styleConfirmButton() {
......@@ -189,3 +241,25 @@ extension ConfirmVPNPlanViewController: GDPRDelegate {
}
}
@available(iOSApplicationExtension 13.0, *)
extension ConfirmVPNPlanViewController: ASAuthorizationControllerPresentationContextProviding {
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
guard let appleIDCredentials = authorization.credential as? ASAuthorizationAppleIDCredential else { return }
if let email = appleIDCredentials.email {
textEmail.text = email
let preferences = Client.preferences.editable()
preferences.signInWithAppleFakeEmail = email
preferences.commit()
} else {
textEmail.text = Client.preferences.signInWithAppleFakeEmail
}
}
}
@available(iOSApplicationExtension 13.0, *)
extension ConfirmVPNPlanViewController: ASAuthorizationControllerDelegate {
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return self.view.window!
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment