Login with email instead of username. Adding some code for Apple sign in

This commit is contained in:
Selim Mustafaev 2020-09-08 16:31:27 +03:00
parent de6c6a7ac5
commit f7bcaa96a2
10 changed files with 204 additions and 109 deletions

View File

@ -75,6 +75,7 @@
7A7547DD2403180A004E8406 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DB2403180A004E8406 /* SectionHeader.swift */; }; 7A7547DD2403180A004E8406 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DB2403180A004E8406 /* SectionHeader.swift */; };
7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7547DC2403180A004E8406 /* SectionHeader.xib */; }; 7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7547DC2403180A004E8406 /* SectionHeader.xib */; };
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; }; 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; };
7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */; };
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; }; 7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; };
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */; }; 7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */; };
7A96AE2A246AFD6200297C33 /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A96AE29246AFD6200297C33 /* Eureka */; }; 7A96AE2A246AFD6200297C33 /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A96AE29246AFD6200297C33 /* Eureka */; };
@ -157,6 +158,7 @@
7A7547DB2403180A004E8406 /* SectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = "<group>"; }; 7A7547DB2403180A004E8406 /* SectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = "<group>"; };
7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = "<group>"; }; 7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = "<group>"; };
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = "<group>"; }; 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = "<group>"; };
7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AuthenticationServices.framework; sourceTree = DEVELOPER_DIR; };
7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = "<group>"; }; 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = "<group>"; };
7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleReportImage.swift; sourceTree = "<group>"; }; 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleReportImage.swift; sourceTree = "<group>"; };
7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; }; 7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; };
@ -180,6 +182,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */,
7AF58D342402A91C00CE01A0 /* Kingfisher in Frameworks */, 7AF58D342402A91C00CE01A0 /* Kingfisher in Frameworks */,
7A0516162414EC1200FC55AC /* Differentiator in Frameworks */, 7A0516162414EC1200FC55AC /* Differentiator in Frameworks */,
7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */, 7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */,
@ -305,6 +308,7 @@
7A11474C23FFEE8700B424AF /* Frameworks */ = { 7A11474C23FFEE8700B424AF /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */,
7A96AE2E246B2BCD00297C33 /* WebKit.framework */, 7A96AE2E246B2BCD00297C33 /* WebKit.framework */,
7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */, 7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */,
7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */, 7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */,
@ -707,7 +711,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 25; CURRENT_PROJECT_VERSION = 26;
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_FILE = AutoCat/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;
@ -729,7 +733,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 25; CURRENT_PROJECT_VERSION = 26;
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_FILE = AutoCat/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0; IPHONEOS_DEPLOYMENT_TARGET = 13.0;

View File

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.device.audio-input</key> <key>com.apple.security.device.audio-input</key>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pme-aR-UNJ"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pme-aR-UNJ">
<device id="retina4_7" orientation="portrait" appearance="dark"/> <device id="retina4_7" orientation="portrait" appearance="dark"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
@ -261,14 +261,14 @@
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<prototypes> <prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="VehicleCell" id="VEP-QD-i6y" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="VehicleCell" id="VEP-QD-i6y" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="85"/> <rect key="frame" x="0.0" y="28" width="375" height="85.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VEP-QD-i6y" id="8hH-8I-XLB"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VEP-QD-i6y" id="8hH-8I-XLB">
<rect key="frame" x="0.0" y="0.0" width="375" height="85"/> <rect key="frame" x="0.0" y="0.0" width="375" height="85.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Kia (JF) Optima" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AQY-7N-q8D"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Kia (JF) Optima" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AQY-7N-q8D">
<rect key="frame" x="8" y="8" width="124" height="21"/> <rect key="frame" x="8" y="8" width="124" height="21.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/> <fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -280,7 +280,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cvf-vM-QnT" customClass="PlateView" customModule="AutoCat" customModuleProvider="target"> <view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cvf-vM-QnT" customClass="PlateView" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="8" y="37" width="317" height="40"/> <rect key="frame" x="8" y="37.5" width="317" height="40"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="40" id="Xoz-Iw-PCU"/> <constraint firstAttribute="height" constant="40" id="Xoz-Iw-PCU"/>
@ -716,13 +716,13 @@
<stackView opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" axis="vertical" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="TMc-av-b0b"> <stackView opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" axis="vertical" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="TMc-av-b0b">
<rect key="frame" x="56.5" y="235.5" width="262.5" height="196"/> <rect key="frame" x="56.5" y="235.5" width="262.5" height="196"/>
<subviews> <subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Login" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Uey-88-eZL" customClass="CustomTextField" customModule="AutoCat" customModuleProvider="target"> <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Email" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Uey-88-eZL" customClass="CustomTextField" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="262.5" height="34"/> <rect key="frame" x="0.0" y="0.0" width="262.5" height="34"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="34" id="5fP-sM-e5U"/> <constraint firstAttribute="height" constant="34" id="5fP-sM-e5U"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/> <textInputTraits key="textInputTraits" keyboardType="emailAddress" textContentType="email"/>
<userDefinedRuntimeAttributes> <userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth"> <userDefinedRuntimeAttribute type="number" keyPath="borderWidth">
<real key="value" value="1"/> <real key="value" value="1"/>
@ -775,6 +775,19 @@
<action selector="signupTapped:" destination="pme-aR-UNJ" eventType="touchUpInside" id="upt-yE-Xro"/> <action selector="signupTapped:" destination="pme-aR-UNJ" eventType="touchUpInside" id="upt-yE-Xro"/>
</connections> </connections>
</button> </button>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="or" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SXb-1Q-TxY">
<rect key="frame" x="0.0" y="196" width="262.5" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nId-Ov-fVe" customClass="ASAuthorizationAppleIDButton">
<rect key="frame" x="0.0" y="196" width="262.5" height="0.0"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<connections>
<action selector="appleSignInTapped:" destination="pme-aR-UNJ" eventType="touchUpInside" id="LrP-Rx-JeO"/>
</connections>
</view>
</subviews> </subviews>
</stackView> </stackView>
</subviews> </subviews>
@ -787,6 +800,7 @@
<viewLayoutGuide key="safeArea" id="CSC-sQ-Tm5"/> <viewLayoutGuide key="safeArea" id="CSC-sQ-Tm5"/>
</view> </view>
<connections> <connections>
<outlet property="appleSignIn" destination="nId-Ov-fVe" id="ifS-g1-O3I"/>
<outlet property="login" destination="ltG-B1-UBj" id="Fc0-Cd-BKA"/> <outlet property="login" destination="ltG-B1-UBj" id="Fc0-Cd-BKA"/>
<outlet property="password" destination="G1p-Hz-8yn" id="8VI-cA-YrJ"/> <outlet property="password" destination="G1p-Hz-8yn" id="8VI-cA-YrJ"/>
<outlet property="signup" destination="hRD-Ha-MrP" id="VCG-25-bHL"/> <outlet property="signup" destination="hRD-Ha-MrP" id="VCG-25-bHL"/>

View File

@ -2,19 +2,23 @@ import UIKit
import RxSwift import RxSwift
import RxCocoa import RxCocoa
import RealmSwift import RealmSwift
import AuthenticationServices
class AuthController: UIViewController { class AuthController: UIViewController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
@IBOutlet weak var username: UITextField! @IBOutlet weak var username: UITextField!
@IBOutlet weak var password: UITextField! @IBOutlet weak var password: UITextField!
@IBOutlet weak var login: UIButton! @IBOutlet weak var login: UIButton!
@IBOutlet weak var signup: UIButton! @IBOutlet weak var signup: UIButton!
@IBOutlet weak var appleSignIn: ASAuthorizationAppleIDButton!
let bag = DisposeBag() let bag = DisposeBag()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.appleSignIn.cornerRadius = 6
let authValid = Observable.combineLatest(self.username.rx.text, self.password.rx.text) { name, pass -> Bool in let authValid = Observable.combineLatest(self.username.rx.text, self.password.rx.text) { name, pass -> Bool in
guard let name = name, let pass = pass else { return false } guard let name = name, let pass = pass else { return false }
return name.count >= 4 && pass.count >= 5 return name.count >= 4 && pass.count >= 5
@ -23,44 +27,55 @@ class AuthController: UIViewController {
authValid.bind(to: self.login.rx.isEnabled).disposed(by: self.bag) authValid.bind(to: self.login.rx.isEnabled).disposed(by: self.bag)
authValid.bind(to: self.signup.rx.isEnabled).disposed(by: self.bag) authValid.bind(to: self.signup.rx.isEnabled).disposed(by: self.bag)
if Settings.shared.user.login.count > 0 { if Settings.shared.user.email.count > 0 {
self.username.text = Settings.shared.user.login self.username.text = Settings.shared.user.email
} }
} }
@IBAction func loginTapped(_ sender: UIButton) { @IBAction func loginTapped(_ sender: UIButton) {
guard let name = self.username.text, let pass = self.password.text else { return } guard let email = self.username.text, let pass = self.password.text else { return }
IHProgressHUD.show() IHProgressHUD.show()
Api.login(username: name, password: pass) Api.login(email: email, password: pass)
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onSuccess: self.goToMainScreen(user:), onError: self.displayError(error:)) .subscribe(onSuccess: self.goToMainScreen(user:), onError: self.displayError(error:))
.disposed(by: self.bag) .disposed(by: self.bag)
} }
@IBAction func signupTapped(_ sender: UIButton) { @IBAction func signupTapped(_ sender: UIButton) {
guard let name = self.username.text, let pass = self.password.text else { return } guard let email = self.username.text, let pass = self.password.text else { return }
IHProgressHUD.show() IHProgressHUD.show()
Api.signup(username: name, password: pass) Api.signUp(email: email, password: pass)
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onSuccess: self.goToMainScreen(user:), onError: self.displayError(error:)) .subscribe(onSuccess: self.goToMainScreen(user:), onError: self.displayError(error:))
.disposed(by: self.bag) .disposed(by: self.bag)
} }
func goToMainScreen(user: User) { @IBAction func appleSignInTapped(_ sender: ASAuthorizationAppleIDButton) {
guard let realm = try? Realm() else { let appleIDProvider = ASAuthorizationAppleIDProvider()
IHProgressHUD.showError(withStatus: "Database error") let request = appleIDProvider.createRequest()
return request.requestedScopes = [.email]
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
} }
func goToMainScreen(user: User) {
// guard let realm = try? Realm() else {
// IHProgressHUD.showError(withStatus: "Database error")
// return
// }
IHProgressHUD.dismiss() IHProgressHUD.dismiss()
if user.login != Settings.shared.user.login { // if user.email != Settings.shared.user.email {
try? realm.write { // try? realm.write {
realm.deleteAll() // realm.deleteAll()
} // }
} // }
Settings.shared.user = user Settings.shared.user = user
let storyboard = UIStoryboard(name: "Main", bundle: nil) let storyboard = UIStoryboard(name: "Main", bundle: nil)
@ -71,4 +86,38 @@ class AuthController: UIViewController {
IHProgressHUD.showError(withStatus: error.localizedDescription) IHProgressHUD.showError(withStatus: error.localizedDescription)
print(error) print(error)
} }
// MARK: - Apple SignIn
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return self.view.window!
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
switch authorization.credential {
case let appleIDCredential as ASAuthorizationAppleIDCredential:
guard let email = appleIDCredential.email else {
IHProgressHUD.showError(withStatus: "Cannot get email")
return
}
IHProgressHUD.show()
Api.signIn(email: email, password: appleIDCredential.user)
.observeOn(MainScheduler.instance)
.subscribe(onSuccess: self.goToMainScreen(user:), onError: self.displayError(error:))
.disposed(by: self.bag)
if let tokenData = appleIDCredential.identityToken {
let token = String(data: tokenData, encoding: .utf8) ?? ""
_ = Api.fbVerifyAssertion(provider: "apple.com", idToken: token).subscribe(onSuccess: { _ in
print("")
}, onError: { error in
print(error)
})
}
default:
IHProgressHUD.showError(withStatus: "Unsupported authorization credential")
break
}
}
} }

View File

@ -53,9 +53,9 @@ class GoogleSignInController: UIViewController, WKNavigationDelegate {
if let code = queryItems.first(where: { $0.name == "code" })?.value { if let code = queryItems.first(where: { $0.name == "code" })?.value {
decisionHandler(.cancel) decisionHandler(.cancel)
self.getToken(code: code) self.getToken(code: code)
.flatMap(fbVerifyAssertion) .flatMap { Api.fbVerifyAssertion(provider: "google.com", idToken: $0.id_token, accessToken: $0.access_token) }
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: { _ in .subscribe(onSuccess: { _ in
self.dismiss(animated: true, completion: self.completion) self.dismiss(animated: true, completion: self.completion)
}, onError: { error in }, onError: { error in
IHProgressHUD.showError(withStatus: error.localizedDescription) IHProgressHUD.showError(withStatus: error.localizedDescription)
@ -85,7 +85,7 @@ class GoogleSignInController: UIViewController, WKNavigationDelegate {
return String(data: Data(Base64FS.encode(data: hash)), encoding: .utf8)?.trimmingCharacters(in: CharacterSet(charactersIn: "=")) return String(data: Data(Base64FS.encode(data: hash)), encoding: .utf8)?.trimmingCharacters(in: CharacterSet(charactersIn: "="))
} }
func getToken(code: String) -> Observable<TokenResponse> { func getToken(code: String) -> Single<TokenResponse> {
let tokenUrlString = Constants.googleTokenURL let tokenUrlString = Constants.googleTokenURL
+ "?grant_type=authorization_code" + "?grant_type=authorization_code"
+ "&code=" + code + "&code=" + code
@ -96,54 +96,12 @@ class GoogleSignInController: UIViewController, WKNavigationDelegate {
if let url = URL(string: tokenUrlString) { if let url = URL(string: tokenUrlString) {
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
return URLSession.shared.rx.data(request: request).map { data in return URLSession.shared.rx.data(request: request).asSingle().map { data in
return try JSONDecoder().decode(TokenResponse.self, from: data) return try JSONDecoder().decode(TokenResponse.self, from: data)
} }
} else { } else {
let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Bad URL"]) let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Bad URL"])
return Observable.error(error) return Single.error(error)
}
}
public func fbVerifyAssertion(tokenResp: TokenResponse) -> Observable<Void> {
let signupUrl = Constants.fbVerifyAssertion + "?key=" + (Constants.fbApiKey.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? Constants.fbApiKey)
if let url = URL(string: signupUrl) {
let innerBody = "providerId=google.com"
+ "&id_token=" + tokenResp.id_token
+ "&access_token=" + tokenResp.access_token
let body: [String:Any] = [
"returnIdpCredential": true,
"returnSecureToken": true,
"autoCreate": true,
"requestUri": "http://localhost",
"postBody": innerBody
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(Constants.fbClientVersion, forHTTPHeaderField: "X-Client-Version")
request.addValue(Constants.vin01BUndleId, forHTTPHeaderField: "X-Ios-Bundle-Identifier")
request.addValue(Constants.fbUserAgent, forHTTPHeaderField: "User-Agent")
return URLSession.shared.rx.json(request: request).map { response in
guard let json = response as? [String: Any] else { return }
if let newToken = json["idToken"] as? String {
Settings.shared.user.googleIdToken = newToken
print("Token: \(newToken)")
}
if let newRefreshToken = json["refreshToken"] as? String {
Settings.shared.user.googleRefreshToken = newRefreshToken
print("Refresh token: \(newRefreshToken)")
}
}.catchError { err in
print(err)
return .just(())
}
} else {
return .just(())
} }
} }
} }

View File

@ -1,5 +1,6 @@
import UIKit import UIKit
import Eureka import Eureka
import AuthenticationServices
class SettingsController: FormViewController { class SettingsController: FormViewController {
@ -9,11 +10,11 @@ class SettingsController: FormViewController {
form +++ Section("Profile") form +++ Section("Profile")
<<< LabelRow("AutoCatAccount") { row in <<< LabelRow("AutoCatAccount") { row in
row.title = "AutoCat Account" row.title = "AutoCat Account"
row.value = Settings.shared.user.login row.value = Settings.shared.user.email
} }
<<< LabelRow("GoogleAccount") { row in <<< LabelRow("GoogleAccount") { row in
row.title = "Google" row.title = "Google"
if let jwtString = Settings.shared.user.googleIdToken, let jwt = JWT(string: jwtString) { if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT(string: jwtString) {
row.value = jwt.payload.email row.value = jwt.payload.email
} else { } else {
row.value = "Log In" row.value = "Log In"
@ -22,8 +23,8 @@ class SettingsController: FormViewController {
cell.accessoryType = .disclosureIndicator cell.accessoryType = .disclosureIndicator
} }
}.onCellSelection { cell, row in }.onCellSelection { cell, row in
if Settings.shared.user.googleIdToken != nil { if Settings.shared.user.firebaseIdToken != nil {
self.displayLogoutSheet() self.displayLogoutSheet(for: "GoogleAccount")
} else { } else {
self.loginToGoogle() self.loginToGoogle()
} }
@ -90,18 +91,18 @@ class SettingsController: FormViewController {
self.view.window?.rootViewController = storyboard.instantiateViewController(identifier: "AuthController") self.view.window?.rootViewController = storyboard.instantiateViewController(identifier: "AuthController")
} }
func displayLogoutSheet() { func displayLogoutSheet(for row: String) {
guard let jwtString = Settings.shared.user.googleIdToken, let jwt = JWT(string: jwtString) else { return } guard let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT(string: jwtString) else { return }
let sheet = UIAlertController(title: jwt.payload.name, message: "You are currently signed in with email \(jwt.payload.email). It will help to gather more data about vehicles.", preferredStyle: .actionSheet) let sheet = UIAlertController(title: jwt.payload.name, message: "You are currently signed in with email \(jwt.payload.email). It will help to gather more data about vehicles.", preferredStyle: .actionSheet)
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in sheet.dismiss(animated: true, completion: nil) } let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in sheet.dismiss(animated: true, completion: nil) }
let logout = UIAlertAction(title: "Sign Out", style: .destructive) { _ in let logout = UIAlertAction(title: "Sign Out", style: .destructive) { _ in
Settings.shared.user.googleIdToken = nil Settings.shared.user.firebaseIdToken = nil
Settings.shared.user.googleRefreshToken = nil Settings.shared.user.firebaseRefreshToken = nil
if let googleRow = self.form.rowBy(tag: "GoogleAccount") as? LabelRow { if let row = self.form.rowBy(tag: row) as? LabelRow {
googleRow.value = "Log In" row.value = "Log In"
googleRow.reload() row.reload()
} }
} }
@ -115,7 +116,7 @@ class SettingsController: FormViewController {
if let vc = storyboard.instantiateViewController(identifier: "GoogleSignInController") as? GoogleSignInController { if let vc = storyboard.instantiateViewController(identifier: "GoogleSignInController") as? GoogleSignInController {
vc.completion = { vc.completion = {
guard let googleRow = self.form.rowBy(tag: "GoogleAccount") as? LabelRow else { return } guard let googleRow = self.form.rowBy(tag: "GoogleAccount") as? LabelRow else { return }
if let jwtString = Settings.shared.user.googleIdToken, let jwt = JWT(string: jwtString) { if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT(string: jwtString) {
googleRow.value = jwt.payload.email googleRow.value = jwt.payload.email
} else { } else {
googleRow.value = "Log In" googleRow.value = "Log In"
@ -125,4 +126,8 @@ class SettingsController: FormViewController {
self.present(vc, animated: true) self.present(vc, animated: true)
} }
} }
func loginToApple() {
}
} }

View File

@ -1,13 +1,13 @@
import Foundation import Foundation
struct User: Codable { struct User: Codable {
let login: String let email: String
var token: String var token: String
var googleIdToken: String? var firebaseIdToken: String?
var googleRefreshToken: String? var firebaseRefreshToken: String?
init() { init() {
self.login = "" self.email = ""
self.token = "" self.token = ""
} }
} }

View File

@ -1,7 +1,11 @@
import Foundation import Foundation
import RxSwift import RxSwift
import RxCocoa
class Api { class Api {
// MARK: - Private wrappres
private static func genError(_ msg: String, suggestion: String, code: Int = 0) -> Error { private static func genError(_ msg: String, suggestion: String, code: Int = 0) -> Error {
return NSError(domain: "", code: code, userInfo: [NSLocalizedDescriptionKey: msg, NSLocalizedRecoverySuggestionErrorKey: suggestion]) return NSError(domain: "", code: code, userInfo: [NSLocalizedDescriptionKey: msg, NSLocalizedRecoverySuggestionErrorKey: suggestion])
} }
@ -36,14 +40,14 @@ class Api {
} }
return URLSession.shared.rx.data(request: request).asSingle().map { data in return URLSession.shared.rx.data(request: request).asSingle().map { data in
// let str = String(data: data, encoding: .utf8) let str = String(data: data, encoding: .utf8)
// print("================================") print("================================")
// if let string = str?.replacingOccurrences(of: "\\\"", with: "\"") if let string = str?.replacingOccurrences(of: "\\\"", with: "\"")
// .replacingOccurrences(of: "\\'", with: "'") .replacingOccurrences(of: "\\'", with: "'")
// .replacingOccurrences(of: "\\n", with: "") { .replacingOccurrences(of: "\\n", with: "") {
// print(string) print(string)
// } }
// print("================================") print("================================")
let resp = try JSONDecoder().decode(Response<T>.self, from: data) let resp = try JSONDecoder().decode(Response<T>.self, from: data)
if resp.success { if resp.success {
return resp.data! return resp.data!
@ -73,8 +77,10 @@ class Api {
return self.makeRequest(api: api, method: method, body: body, params: nil as [String:Int]?) return self.makeRequest(api: api, method: method, body: body, params: nil as [String:Int]?)
} }
// MARK: - Firebase API
public static func refreshFbToken() -> Single<Void> { public static func refreshFbToken() -> Single<Void> {
guard let token = Settings.shared.user.googleIdToken, let refreshToken = Settings.shared.user.googleRefreshToken, let jwt = JWT(string: token), jwt.expired else { guard let token = Settings.shared.user.firebaseIdToken, let refreshToken = Settings.shared.user.firebaseRefreshToken, let jwt = JWT(string: token), jwt.expired else {
return .just(()) return .just(())
} }
@ -91,16 +97,16 @@ class Api {
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .prettyPrinted) request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(Constants.fbClientVersion, forHTTPHeaderField: "X-Client-Version") request.addValue(Constants.fbClientVersion, forHTTPHeaderField: "X-Client-Version")
request.addValue(Constants.vin01BUndleId, forHTTPHeaderField: "X-Ios-Bundle-Identifier") request.addValue(Constants.secondProviderBundleId, forHTTPHeaderField: "X-Ios-Bundle-Identifier")
request.addValue(Constants.fbUserAgent, forHTTPHeaderField: "User-Agent") request.addValue(Constants.fbUserAgent, forHTTPHeaderField: "User-Agent")
return URLSession.shared.rx.json(request: request).asSingle().map { resp in return URLSession.shared.rx.json(request: request).asSingle().map { resp in
guard let json = resp as? [String: Any] else { return } guard let json = resp as? [String: Any] else { return }
if let newToken = json["id_token"] as? String { if let newToken = json["id_token"] as? String {
Settings.shared.user.googleIdToken = newToken Settings.shared.user.firebaseIdToken = newToken
print("Token was successfully refresh to: \(newToken)") print("Token was successfully refresh to: \(newToken)")
} }
if let newRefreshToken = json["refresh_token"] as? String { if let newRefreshToken = json["refresh_token"] as? String {
Settings.shared.user.googleRefreshToken = newRefreshToken Settings.shared.user.firebaseRefreshToken = newRefreshToken
} }
}.catchError { error in }.catchError { error in
print(error) print(error)
@ -111,17 +117,72 @@ class Api {
} }
} }
public static func login(username: String, password: String) -> Single<User> { public static func fbVerifyAssertion(provider: String, idToken: String, accessToken: String? = nil) -> Single<Void> {
let signupUrl = Constants.fbVerifyAssertion + "?key=" + (Constants.fbApiKey.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? Constants.fbApiKey)
if let url = URL(string: signupUrl) {
var innerBody = "providerId=" + provider
+ "&id_token=" + idToken
if let accessToken = accessToken {
innerBody += "&access_token=" + accessToken
}
let body: [String:Any] = [
"returnIdpCredential": true,
"returnSecureToken": true,
"autoCreate": true,
"requestUri": "http://localhost",
"postBody": innerBody
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(Constants.fbClientVersion, forHTTPHeaderField: "X-Client-Version")
request.addValue(Constants.secondProviderBundleId, forHTTPHeaderField: "X-Ios-Bundle-Identifier")
request.addValue(Constants.fbUserAgent, forHTTPHeaderField: "User-Agent")
return URLSession.shared.rx.json(request: request).asSingle().map { response in
guard let json = response as? [String: Any] else { return }
if let newToken = json["idToken"] as? String {
Settings.shared.user.firebaseIdToken = newToken
print("Token: \(newToken)")
}
if let newRefreshToken = json["refreshToken"] as? String {
Settings.shared.user.firebaseRefreshToken = newRefreshToken
print("Refresh token: \(newRefreshToken)")
}
}.catchError { err in
print(err)
return .just(())
}
} else {
return .just(())
}
}
// MARK: - AutoCat public API
public static func login(email: String, password: String) -> Single<User> {
let body = [ let body = [
"login": username, "email": email,
"password": password "password": password
] ]
return self.makeBodyRequest(api: "user/login", body: body) return self.makeBodyRequest(api: "user/login", body: body)
} }
public static func signup(username: String, password: String) -> Single<User> { public static func signIn(email: String, password: String) -> Single<User> {
let body = [ let body = [
"login": username, "email": email,
"password": password
]
return self.makeBodyRequest(api: "user/signIn", body: body)
}
public static func signUp(email: String, password: String) -> Single<User> {
let body = [
"email": email,
"password": password "password": password
] ]
return self.makeBodyRequest(api: "user/signup", body: body) return self.makeBodyRequest(api: "user/signup", body: body)
@ -137,7 +198,7 @@ class Api {
"number": number, "number": number,
"forceUpdate": String(force) "forceUpdate": String(force)
] ]
if let token = Settings.shared.user.googleIdToken { if let token = Settings.shared.user.firebaseIdToken {
body["googleIdToken"] = token body["googleIdToken"] = token
} }
return self.makeBodyRequest(api: "vehicles/check", body: body).map { (vehicle: Vehicle) -> Vehicle in return self.makeBodyRequest(api: "vehicles/check", body: body).map { (vehicle: Vehicle) -> Vehicle in

View File

@ -23,7 +23,7 @@ enum Constants {
static let fbRefreshToken = "https://securetoken.googleapis.com/v1/token" static let fbRefreshToken = "https://securetoken.googleapis.com/v1/token"
static let fbUserAgent = "FirebaseAuth.iOS/6.5.1 ru.Vin01/1.0 iPhone/13.5 hw/sim" static let fbUserAgent = "FirebaseAuth.iOS/6.5.1 ru.Vin01/1.0 iPhone/13.5 hw/sim"
static let vin01BUndleId = "ru.Vin01" static let secondProviderBundleId = "ru.Vin01"
static let reportLinkTokenSecret = "#TheTruthIsOutThere" static let reportLinkTokenSecret = "#TheTruthIsOutThere"
static let reportLinkBaseURL = "https://auto.aliencat.pro/report.html" static let reportLinkBaseURL = "https://auto.aliencat.pro/report.html"

View File

@ -87,6 +87,9 @@ class Recorder {
} }
do { do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [])
try AVAudioSession.sharedInstance().setActive(true)
let inFormat = self.engine.inputNode.outputFormat(forBus: 0) let inFormat = self.engine.inputNode.outputFormat(forBus: 0)
ExtAudioFileSetProperty(fileRef, kExtAudioFileProperty_ClientDataFormat, UInt32(MemoryLayout<AudioStreamBasicDescription>.size), inFormat.streamDescription) ExtAudioFileSetProperty(fileRef, kExtAudioFileProperty_ClientDataFormat, UInt32(MemoryLayout<AudioStreamBasicDescription>.size), inFormat.streamDescription)
@ -113,9 +116,6 @@ class Recorder {
self.engine.prepare() self.engine.prepare()
try self.engine.start() try self.engine.start()
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [])
try AVAudioSession.sharedInstance().setActive(true)
} catch { } catch {
observer(.error(error)) observer(.error(error))
} }