From 0b5b73dc31a3734a73d0b4f30f42f5cf2a6fdc29 Mon Sep 17 00:00:00 2001 From: Aleksey Belousov Date: Tue, 24 Mar 2020 17:10:47 +0300 Subject: [PATCH] [iOS] add "Sign in with Apple" to login options --- iphone/CoreApi/CoreApi/User/MWMUser.h | 3 +- iphone/CoreApi/CoreApi/User/MWMUser.mm | 3 + iphone/Maps/Bridging-Header.h | 1 + .../Common/Statistics/StatisticsStrings.h | 1 + iphone/Maps/Maps.xcodeproj/project.pbxproj | 4 + .../AuthorizationViewController.swift | 82 +++++++++++++++---- .../AuthorizationViewController.xib | 63 ++++++++------ iphone/Maps/maps.me dbg.entitlements | 4 + iphone/Maps/maps.me full.entitlements | 4 + iphone/Maps/maps.me rel.entitlements | 4 + map/user.cpp | 5 ++ map/user.hpp | 3 +- 12 files changed, 137 insertions(+), 40 deletions(-) diff --git a/iphone/CoreApi/CoreApi/User/MWMUser.h b/iphone/CoreApi/CoreApi/User/MWMUser.h index 571242efe1..0569b7709a 100644 --- a/iphone/CoreApi/CoreApi/User/MWMUser.h +++ b/iphone/CoreApi/CoreApi/User/MWMUser.h @@ -5,7 +5,8 @@ typedef NS_ENUM(NSInteger, MWMSocialTokenType) { MWMSocialTokenTypeGoogle, MWMSocialTokenTypeFacebook, - MWMSocialTokenTypePhone + MWMSocialTokenTypePhone, + MWMSocialTokenTypeApple } NS_SWIFT_NAME(SocialTokenType); typedef NS_ENUM(NSInteger, MWMAuthorizationSource) { diff --git a/iphone/CoreApi/CoreApi/User/MWMUser.mm b/iphone/CoreApi/CoreApi/User/MWMUser.mm index 259ace6a74..cd1bc149dd 100644 --- a/iphone/CoreApi/CoreApi/User/MWMUser.mm +++ b/iphone/CoreApi/CoreApi/User/MWMUser.mm @@ -40,6 +40,9 @@ case MWMSocialTokenTypePhone: socialTokenType = User::SocialTokenType::Phone; break; + case MWMSocialTokenTypeApple: + socialTokenType = User::SocialTokenType::Apple; + break; } auto s = std::make_unique(); s->m_postCallAction = User::Subscriber::Action::RemoveSubscriber; diff --git a/iphone/Maps/Bridging-Header.h b/iphone/Maps/Bridging-Header.h index 8ed3bd6f6a..d5f09ef3ce 100644 --- a/iphone/Maps/Bridging-Header.h +++ b/iphone/Maps/Bridging-Header.h @@ -11,6 +11,7 @@ #import "Pushwoosh/PushNotificationManager.h" #import "UIKit/UIKit.h" #import "CarPlay/CarPlay.h" +#import "AuthenticationServices/AuthenticationServices.h" #import "3party/Alohalytics/src/alohalytics_objc.h" #import "MPNativeAd+MWM.h" diff --git a/iphone/Maps/Common/Statistics/StatisticsStrings.h b/iphone/Maps/Common/Statistics/StatisticsStrings.h index 8af87947e8..c6fcfb1dbd 100644 --- a/iphone/Maps/Common/Statistics/StatisticsStrings.h +++ b/iphone/Maps/Common/Statistics/StatisticsStrings.h @@ -26,6 +26,7 @@ static NSString * const kStatAllMaps = @"all_maps"; static NSString * const kStatAlways = @"Always"; static NSString * const kStatAny = @"any"; static NSString * const kStatApartment = @"apartment"; +static NSString * const kStatApple = @"Apple"; static NSString * const kStatApplication = @"Application"; static NSString * const kStatApplicationColdStartupInfo = @"Application_ColdStartup_info"; static NSString * const kStatApply = @"Apply"; diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index a48d38f127..8212cfef6f 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -401,6 +401,7 @@ 47CF2E6123BA090400D11C30 /* FacilitiesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CF2E6023BA090400D11C30 /* FacilitiesController.swift */; }; 47CF2E6323BA0DD500D11C30 /* CopyLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CF2E6223BA0DD500D11C30 /* CopyLabel.swift */; }; 47D0026721999DA900F651A2 /* PendingTransactionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D0026621999DA900F651A2 /* PendingTransactionsHandler.swift */; }; + 47D48BD0242A475700FEFB1F /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47D48BCF242A475700FEFB1F /* AuthenticationServices.framework */; }; 47DF72B922520CE20004AB10 /* MWMRoutingOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 474902D9224A54EC008D71E0 /* MWMRoutingOptions.mm */; }; 47DF72BB225356BF0004AB10 /* DrivingOptions.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 47DF72BA225356BF0004AB10 /* DrivingOptions.storyboard */; }; 47E3C72121108E9F008B3B27 /* BookmarksLoadedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47E3C71F21108E9F008B3B27 /* BookmarksLoadedViewController.swift */; }; @@ -1502,6 +1503,7 @@ 47CF2E6023BA090400D11C30 /* FacilitiesController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FacilitiesController.swift; sourceTree = ""; }; 47CF2E6223BA0DD500D11C30 /* CopyLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyLabel.swift; sourceTree = ""; }; 47D0026621999DA900F651A2 /* PendingTransactionsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PendingTransactionsHandler.swift; sourceTree = ""; }; + 47D48BCF242A475700FEFB1F /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = System/Library/Frameworks/AuthenticationServices.framework; sourceTree = SDKROOT; }; 47DF72BA225356BF0004AB10 /* DrivingOptions.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = DrivingOptions.storyboard; sourceTree = ""; }; 47E3C71F21108E9F008B3B27 /* BookmarksLoadedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksLoadedViewController.swift; sourceTree = ""; }; 47E3C72021108E9F008B3B27 /* BookmarksLoadedViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BookmarksLoadedViewController.xib; sourceTree = ""; }; @@ -2116,6 +2118,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 47D48BD0242A475700FEFB1F /* AuthenticationServices.framework in Frameworks */, 39CDE69123E1B6C8007CDA58 /* libge0.a in Frameworks */, 47A65CAD2350044800DCD85F /* CoreApi.framework in Frameworks */, 4577B28121F2066A00864FAC /* libvulkan_wrapper.a in Frameworks */, @@ -2295,6 +2298,7 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + 47D48BCF242A475700FEFB1F /* AuthenticationServices.framework */, 39CDE69023E1B6C8007CDA58 /* libge0.a */, 4740184123F5BDD300A93C81 /* minizip.framework */, 450B5C822355F50200E9019E /* libweb_api.a */, diff --git a/iphone/Maps/UI/Authorization/AuthorizationViewController.swift b/iphone/Maps/UI/Authorization/AuthorizationViewController.swift index cf10ecb6d1..4168790a2c 100644 --- a/iphone/Maps/UI/Authorization/AuthorizationViewController.swift +++ b/iphone/Maps/UI/Authorization/AuthorizationViewController.swift @@ -2,6 +2,7 @@ import FBSDKCoreKit import FBSDKLoginKit import GoogleSignIn import SafariServices +import AuthenticationServices @objc enum AuthorizationError: Int { case cancelled @@ -25,12 +26,15 @@ final class AuthorizationViewController: MWMViewController { } } - @IBOutlet private weak var contentView: UIView! - @IBOutlet private weak var titleLabel: UILabel! - @IBOutlet weak var separator: UIView! - @IBOutlet private weak var textLabel: UILabel! + @IBOutlet private var contentView: UIView! + @IBOutlet private var titleLabel: UILabel! + @IBOutlet var separator: UIView! + @IBOutlet private var textLabel: UILabel! - @IBOutlet private weak var googleButton: UIButton! { + @IBOutlet private var signInAppleContainerView: UIView! + private var signInAppleButton: UIControl? + + @IBOutlet private var googleButton: UIButton! { didSet { googleButton.setTitle("Google", for: .normal) googleButton.isEnabled = false @@ -48,7 +52,7 @@ final class AuthorizationViewController: MWMViewController { gid.signIn() } - @IBOutlet private weak var facebookButton: UIButton! { + @IBOutlet private var facebookButton: UIButton! { didSet { facebookButton.isEnabled = false } @@ -78,15 +82,15 @@ final class AuthorizationViewController: MWMViewController { self.present(navVC, animated: true) } - @IBOutlet private weak var phoneSignInButton: UIButton! { + @IBOutlet private var phoneSignInButton: UIButton! { didSet { phoneSignInButton.isEnabled = false } } - @IBOutlet private weak var privacyPolicyCheck: Checkmark! - @IBOutlet private weak var termsOfUseCheck: Checkmark! - @IBOutlet private weak var latestNewsCheck: Checkmark! + @IBOutlet private var privacyPolicyCheck: Checkmark! + @IBOutlet private var termsOfUseCheck: Checkmark! + @IBOutlet private var latestNewsCheck: Checkmark! @IBAction func onCheck(_ sender: Checkmark) { let allButtonsChecked = privacyPolicyCheck.isChecked && @@ -95,9 +99,10 @@ final class AuthorizationViewController: MWMViewController { googleButton.isEnabled = allButtonsChecked; facebookButton.isEnabled = allButtonsChecked; phoneSignInButton.isEnabled = allButtonsChecked; + signInAppleButton?.isEnabled = allButtonsChecked; } - @IBOutlet private weak var privacyPolicyTextView: UITextView! { + @IBOutlet private var privacyPolicyTextView: UITextView! { didSet { let htmlString = String(coreFormat: L("sign_agree_pp_gdpr"), arguments: [User.privacyPolicyLink()]) privacyPolicyTextView.attributedText = NSAttributedString.string(withHtml: htmlString, @@ -106,7 +111,7 @@ final class AuthorizationViewController: MWMViewController { } } - @IBOutlet private weak var termsOfUseTextView: UITextView! { + @IBOutlet private var termsOfUseTextView: UITextView! { didSet { let htmlString = String(coreFormat: L("sign_agree_tof_gdpr"), arguments: [User.termsOfUseLink()]) termsOfUseTextView.attributedText = NSAttributedString.string(withHtml: htmlString, @@ -115,14 +120,14 @@ final class AuthorizationViewController: MWMViewController { } } - @IBOutlet private weak var latestNewsTextView: UITextView! { + @IBOutlet private var latestNewsTextView: UITextView! { didSet { let text = L("sign_agree_news_gdpr") latestNewsTextView.attributedText = NSAttributedString(string: text, attributes: [:]) } } - @IBOutlet private weak var topToContentConstraint: NSLayoutConstraint! + @IBOutlet private var topToContentConstraint: NSLayoutConstraint! typealias SuccessHandler = (SocialTokenType) -> Void typealias ErrorHandler = (AuthorizationError) -> Void @@ -166,6 +171,16 @@ final class AuthorizationViewController: MWMViewController { iPadSpecific { topToContentConstraint.isActive = false } + if #available(iOS 13, *) { + signInAppleContainerView.isHidden = false + let button = ASAuthorizationAppleIDButton(type: .default, style: .whiteOutline) + button.isEnabled = false + button.cornerRadius = 8 + button.addTarget(self, action: #selector(onAppleSignIn), for: .touchUpInside) + signInAppleContainerView.addSubview(button) + button.alignToSuperview() + signInAppleButton = button + } } override func viewDidAppear(_ animated: Bool) { @@ -179,6 +194,18 @@ final class AuthorizationViewController: MWMViewController { } } + @available(iOS 13.0, *) + @objc func onAppleSignIn() { + let appleIDProvider = ASAuthorizationAppleIDProvider() + let request = appleIDProvider.createRequest() + request.requestedScopes = [.fullName, .email] + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + authorizationController.delegate = self + authorizationController.presentationContextProvider = self + authorizationController.performRequests() + } + @IBAction func onCancel() { Statistics.logEvent(kStatUGCReviewAuthDeclined) errorHandler?(.cancelled) @@ -195,6 +222,7 @@ final class AuthorizationViewController: MWMViewController { case .facebook: return kStatFacebook case .google: return kStatGoogle case .phone: return kStatPhone + case .apple: return kStatApple @unknown default: fatalError() } @@ -237,6 +265,8 @@ final class AuthorizationViewController: MWMViewController { provider = kStatFacebook case .phone: provider = kStatPhone + case .apple: + provider = kStatApple @unknown default: fatalError() } @@ -279,3 +309,27 @@ extension AuthorizationViewController: UITextViewDelegate { return false; } } + +@available(iOS 13.0, *) +extension AuthorizationViewController: ASAuthorizationControllerDelegate { + func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { + switch authorization.credential { + case let appleIDCredential as ASAuthorizationAppleIDCredential: + guard let token = appleIDCredential.identityToken, let tokenString = String(data: token, encoding: .utf8) else { return } + process(token: tokenString, type: .apple) + default: + break + } + } + + func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { + process(error: error, type: .apple) + } +} + +@available(iOS 13.0, *) +extension AuthorizationViewController: ASAuthorizationControllerPresentationContextProviding { + func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + return self.view.window! + } +} diff --git a/iphone/Maps/UI/Authorization/AuthorizationViewController.xib b/iphone/Maps/UI/Authorization/AuthorizationViewController.xib index 186f2a4076..a273dfa90a 100644 --- a/iphone/Maps/UI/Authorization/AuthorizationViewController.xib +++ b/iphone/Maps/UI/Authorization/AuthorizationViewController.xib @@ -1,7 +1,9 @@ - + + - + + @@ -16,6 +18,7 @@ + @@ -38,7 +41,7 @@ - + - + - + - + - + - + @@ -93,7 +100,7 @@ - + @@ -104,7 +111,7 @@ - + @@ -123,10 +130,10 @@ - + - + @@ -140,7 +147,7 @@ - + @@ -151,7 +158,7 @@ - + @@ -167,7 +174,7 @@ - + @@ -181,7 +188,7 @@ - + @@ -192,7 +199,7 @@ - + @@ -207,22 +214,29 @@ +