Stripe - retrieveCustomer callback infinite loading - ios

I had implement the Stripe to my project.I'm using an extension of default STPPaymentMethodsViewController like this:
class PaymentMethodVC: STPPaymentMethodsViewController {
convenience init()
{
let theme = STPTheme()
theme.primaryBackgroundColor = UIColor.pintHubDarkBrown
theme.secondaryBackgroundColor = UIColor.pintHubHeaderColor
theme.accentColor = UIColor.white
theme.primaryForegroundColor = UIColor.pintHubOrange
theme.secondaryForegroundColor = UIColor.pintHubOrange
theme.font = UIFont.mainRegular()
let paymentdelegate = PaymentMethodVCDelegate()
let paymentConfig = STPPaymentConfiguration.shared()
paymentConfig.publishableKey = "stripePublickToken"
let apiAdapter = PaymentApiAdapter()
self.init(configuration: paymentConfig, theme: theme, apiAdapter: apiAdapter, delegate: paymentdelegate)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
}
PaymentMethodVCDelegate is an object that implements STPPaymentMethodsViewControllerDelegate that methods are never called and
PaymentApiAdapter is other object that implements STPBackendAPIAdapter protocol which methods are:
public func retrieveCustomer(_ completion: #escaping Stripe.STPCustomerCompletionBlock)
public func attachSource(toCustomer source: STPSource, completion: #escaping Stripe.STPErrorBlock)
public func selectDefaultCustomerSource(_ source: STPSource, completion: #escaping Stripe.STPErrorBlock)
everything works fine expect when i want to return an error to the callback method func retrieveCustomer(_ completion: #escaping Stripe.STPCustomerCompletionBlock) that is a method of the STPBackendAPIAdapter protocol more details here.
this is my code:
func retrieveCustomer(_ completion: #escaping (STPCustomer?, Error?) -> Swift.Void)
{
stripeEndpoint.getStripeCustomer(for: "myStrypeCustomerId") { (status, JSON) in
if !status.success()
{
let userInfo = [NSLocalizedDescriptionKey:status.error,
NSLocalizedFailureReasonErrorKey: status.code,
NSLocalizedRecoverySuggestionErrorKey: ""
] as [String : Any]
let error = NSError(domain: "MyDomain", code: Int(status.error) ?? 0, userInfo: userInfo)
completion(nil, error)
}
else
{
var customer:STPCustomer? = nil
if let jsonData = JSON
{
let deserializer = STPCustomerDeserializer(jsonResponse: jsonData)
customer = deserializer.customer!
}
completion(customer, nil)
}
}
and when i receive an error the screen displays and infinite loading indicator.
and if i call completion(nil, nil) the loading disappear but i when i press cancel the ViewController don't pop from stack

Related

I am trying to create a base class for Apple Pay payment processing

I have created a base class for Apple Pay functionality. But that base class is inherited by NSObject . I have created the payment authorise viewcontroller with in this class only and assigned it’s delegate to self. Now it’s not invoking the delegate methods of pkpaymentauthorizationviewcontrollerdelegate. How can I fix this situation and implement delegate method just once in base class and reuse it else where in application. Please help.
class ApplePay: NSObject {
var applePayItem: PKPaymentSummaryItem?
var baseVC: UIViewController?
let payNetworks = [PKPaymentNetwork.masterCard, .visa, .amex, .discover]
init(forItem: Product) {
applePayItem = PKPaymentSummaryItem.init(label: forItem.name ?? "",
amount: NSDecimalNumber(value: forItem.price ?? 0))
}
func initiatePayment(complete: #escaping (Bool) -> Void) {
if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: payNetworks) {
let request = PKPaymentRequest()
request.currencyCode = "USD"
request.countryCode = "US"
request.merchantIdentifier = <*identifier from my development account*>
request.merchantCapabilities = PKMerchantCapability.capability3DS
request.supportedNetworks = payNetworks
request.paymentSummaryItems = [applePayItem!]
guard let paymentVC = PKPaymentAuthorizationViewController(paymentRequest: request) else {
return
}
baseVC = UIApplication.shared.keyWindow?.rootViewController?.children.last
baseVC?.present(paymentVC, animated: true, completion: nil)
paymentVC.delegate = self
complete(true)
} else {
complete(false)
CaAssembly.resolve(CaAlertProtocol.self)!.showAlert(CaConstant.alertTitles.addCardApplePay, dismiss: {})
}
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss {
}
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
controller.dismiss(animated: true, completion: nil)
}
}
extension ApplePay: PKPaymentAuthorizationViewControllerDelegate,
PKPaymentAuthorizationControllerDelegate {
#available(iOS 11.0, *)
func paymentAuthorizationViewController(_ controller:
PKPaymentAuthorizationViewController,
didAuthorizePayment payment: PKPayment,
handler completion: #escaping
(PKPaymentAuthorizationResult) -> Void) {
print(“PKPaymentAuthorizationViewController didAuthorizePayment” )
}
}
Just for the update ... I was able to implement it by UIViewController inherited base class ... using library [github.com/IcaliaLabs/Presentr] using which I presented base class as modal with dimensions (w:0,h:0) and initiated Apple Pay on its viewdidappear :
import UIKit
import PassKit
import Alamofire
import Presentr
struct Product {
var name: String
var price: Double
}
class ApplePay: UIViewController , PKPaymentAuthorizationControllerDelegate,
PKPaymentAuthorizationViewControllerDelegate{ //
var applePayItem: PKPaymentSummaryItem?
let output = ""
var baseVC: UIViewController?
let payNetworks = [PKPaymentNetwork.masterCard, .visa, .amex, .discover]
var presenter: Presentr = {
let customPresenter = Presentr(
presentationType: .custom(
width: ModalSize.custom(size: 0),
height: ModalSize.custom(size: 0),
center: ModalCenterPosition.bottomCenter
)
)
customPresenter.keyboardTranslationType = .compress
return customPresenter
}()
var forItem: Product?
override func viewDidLoad() {
applePayItem = PKPaymentSummaryItem.init(label: forItem?.name ?? "",
amount: NSDecimalNumber(value: forItem?.price ?? 0))
}
override func viewDidAppear(_ animated: Bool) {
initiatePayment()
}
func initiatePayment() {
if PKPaymentAuthorizationViewController.canMakePayments(usingNetworks: payNetworks) {
let request = PKPaymentRequest()
request.currencyCode = "USD"
request.countryCode = "US"
request.merchantIdentifier = <merchant id created on developer account>
request.merchantCapabilities = PKMerchantCapability.capability3DS
request.supportedNetworks = payNetworks
request.paymentSummaryItems = [applePayItem!]
guard let paymentVC = PKPaymentAuthorizationViewController(paymentRequest: request) else {
return
}
self.present(paymentVC, animated: true, completion: nil)
paymentVC.delegate = self
}
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss {
}
}
func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
controller.dismiss(animated: true, completion: nil)
}
#available(iOS 11.0, *)
func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, handler completion: #escaping (PKPaymentAuthorizationResult) -> Void) {
}
}

Stripe iOS didCreatePaymentResult never gets called

The problem seems simple, didCreatePaymentResult never gets called.
BUT, in my old sample project, taken from your iOS example for payment intent, that didCreatePaymentResult gets called every single time I create or select a card, here's the repo of the working project: https://github.com/glennposadas/stripe-example-ios-nodejs
BUT again, my main concern is my current project.
I use v19.2.0 in both of these projects, I even tried the v19.3.0.
I wanted to use Stripe Charge really, but I believe Stripe does not support Apple pay for that. So I have no choice but to use Stripe Payment Intent.
CoreService.swift (conforms to STPCustomerEphemeralKeyProvider)
extension CoreService: STPCustomerEphemeralKeyProvider {
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
orderServiceProvider.request(.requestEphemeralKey(stripeAPIVersion: apiVersion)) { (result) in
switch result {
case let .success(response):
guard let json = ((try? JSONSerialization.jsonObject(with: response.data, options: []) as? [String : Any]) as [String : Any]??) else {
completion(nil, NSError(domain: "Error parsing stripe data", code: 300, userInfo: nil))
return
}
completion(json, nil)
default:
UIViewController.current()?.alert(title: "Error stripe", okayButtonTitle: "OK", withBlock: nil)
}
}
}
}
PaymentController.swift
class PaymentViewController: BaseViewController {
// MARK: - Properties
private var paymentContext: STPPaymentContext!
private let paymentConstantValue: Int = 3000
// MARK: - Functions
// MARK: Overrides
override func viewDidLoad() {
super.viewDidLoad()
self.setupStripe()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.hideNavBar(animated: true)
}
#IBAction func creditCardButtonTapped(_ sender: Any) {
self.paymentContext.presentPaymentOptionsViewController()
}
private func setupStripe() {
let config = STPPaymentConfiguration.shared()
config.appleMerchantIdentifier = "merchant.com.gsample.app"
config.companyName = "Scoutd LLC"
config.requiredBillingAddressFields = .none
config.requiredShippingAddressFields = .none
config.additionalPaymentOptions = .applePay
let customerContext = STPCustomerContext(keyProvider: CoreService())
let paymentContext = STPPaymentContext(
customerContext: customerContext,
configuration: config,
theme: STPTheme.default()
)
let userInformation = STPUserInformation()
paymentContext.prefilledInformation = userInformation
paymentContext.paymentAmount = self.paymentConstantValue
paymentContext.paymentCurrency = "usd"
self.paymentContext = paymentContext
self.paymentContext.delegate = self
self.paymentContext.hostViewController = self
}
}
// MARK: - STPPaymentContextDelegate
extension PaymentViewController: STPPaymentContextDelegate {
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
print("paymentContextDidChange")
}
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
// error alert....
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPPaymentStatusBlock) {
print("didCreatePaymentResult ✅")
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
switch status {
case .success:
// success
case .error:
// error alert....
default:
break
}
}
}
SOLVED! This should help engineers struggling with Stripe implementation in the future.
So in my case, I have two buttons:
Apple Pay
Credit card.
The absolute solution for me is handle the selectedPaymentOption of the paymentContext.
Scenarios:
If the apple pay button is tapped, present apple pay sheet and don't present add/select card UI of Stripe.
If the credit card button is tapped, don't present apple pay sheet and instead present select card.
Related to #2, call requestPayment() if there's a selected option.
Voila! The didCreatePaymentResult now gets invoked!
// MARK: IBActions
#IBAction func applePayButtonTapped(_ sender: Any) {
if self.paymentContext.selectedPaymentOption is STPApplePayPaymentOption {
self.paymentContext.requestPayment()
}
}
#IBAction func creditCardButtonTapped(_ sender: Any) {
if let selectedPaymentOption = self.paymentContext.selectedPaymentOption,
!(selectedPaymentOption is STPApplePayPaymentOption) {
self.paymentContext.requestPayment()
return
}
self.paymentContext.presentPaymentOptionsViewController()
}

How to send image from framework to sample project in iOS Swift?

I have implemented document scanner using visionKit. Initially i have faced camera dismiss issue and it fixed. Now i am trying to send image after camera dismiss from framework to sample project.
I have tried using completion handler, but it does not work.
Here is the code for framework:
public class A8Scan: NSObject, VNDocumentCameraViewControllerDelegate {
var imageNew: UIImage?
var statusImage: UIImageView?
private var clientView: UIViewController?
public init(_ viewController:UIViewController){
self.clientView = viewController
}
public func showScanner(imgData: UIImage?){
self.createTaskController(img: imgData)
print("Called Build")
}
private func createTaskController(img: UIImage?){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: {
self.imageNew = img
})
}
public func imageFromFile(result: #escaping (_ image: UIImage?) -> Void){
//the image
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: nil)
if imageNew != nil {
result(imageNew)
}
else{
//callback nil so the app does not pause infinitely if
//the error != nil
result(nil)
}
}
func set(image: UIImage) {
self.statusImage?.image = image
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
let originalImage = scan.imageOfPage(at: 0)
let newImage = compressedImage(originalImage)
imageNew = newImage
set(image: imageNew!)
print("new image::\(newImage.size)")
controller.dismiss(animated: true, completion: nil)
// processImage(newImage)
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print(error)
controller.dismiss(animated: true)
}
public func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
controller.dismiss(animated: true)
}
func compressedImage(_ originalImage: UIImage) -> UIImage {
guard let imageData = originalImage.jpegData(compressionQuality: 1),
let reloadedImage = UIImage(data: imageData) else {
return originalImage
}
return reloadedImage
}
}
Here is the code sample project:
#IBAction func btnAction(_ sender: Any) {
// A8Scan(self).showScanner()
// A8Scan(self).showScanner(imgData: im)
// print("nn", A8Scan(self).showScanner(imgData: im))
A8Scan(self).imageFromFile { (image) in
if image != nil {
print(image!)
} else {
print("something went wrong")
}
}
// p()
}
func p (){
ScannerViewController().imageFromFile{(image: UIImage?) -> Void in
//use the image that was just retrieved
print("image data", image)
}
}
My issue is after camera dismiss from framework it does not send image from framework to sample project.
Any help much appreciated pls....
1-
Add this inside your framework
var callBack((UIImage)-())?
public func show() {
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: nil)
}
2-
let newImage = compressedImage(originalImage)
callBack?(newImage)
3-
Then inside the vc that uses it
let vc = A8Scan(self)
vc.show()
vc.callBack = { [weak self] image in
}
As you are using a middle man (A8Scan) NSObject to present a controller. the A8Scan will get deallocated immediately after presenting the controller which you can confirm by keeping a deinit function inside A8Scan.
A workaround for this is to make your NSObject (A8Scan) as a singleton ie add
static let shared = A8Scan()
var callBack: ((_ image: UIImage?) -> Void)?
remove the existing init method and add the viewcontroller property into the imageFromFile: method, your function will look like this
public func imageFromFile(sender: UIViewController, result: #escaping (_ image: UIImage?) -> Void){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.callBack = result
sender.present(scannerViewController,animated:true,completion: nil)
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
let originalImage = scan.imageOfPage(at: 0)
let newImage = compressedImage(originalImage)
imageNew = newImage
set(image: imageNew!)
callBack?(imageNew!)
controller.dismiss(animated: true, completion: nil)
}
and finally in your sample project,
#IBAction func btnAction(_ sender: Any) {
A8Scan.shared.imageFromFile(sender: self) { (image) in
if image != nil {
print(image!)
} else {
print("something went wrong")
}
}
}

I can't get this delegate to fire and I'm not sure what I'm missing

I've got a login screen that authenticates with Amazon Cognito using the SDK. Once complete, it's supposed to call a delegate (or extension in iOS Swift). It is supposed to call the didCompleteWithError or the getDetails methods.
I've tried taking the examples I've been trying to follow this example but I haven't had luck (https://github.com/awslabs/aws-sdk-ios-samples/tree/master/CognitoYourUserPools-Sample/Swift). Any ideas? See my code for just the log in screen and AppDelegate.swift below. What am I doing wrong?
// LoginViewController.swift
import UIKit
import AWSCognitoIdentityProvider
import AWSCognitoAuth
import AWSMobileClient
import AWSUserPoolsSignIn
import AWSAuthUI
class LoginViewController: BaseViewController {
#IBOutlet var password: UITextField!
#IBOutlet var email: UITextField!
var pool: AWSCognitoIdentityUserPool?
var passwordAuthenticationCompletion: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>?
var usernameText: String?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.password.text = nil
self.email.text = usernameText
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
#IBAction func login_Tap(_ sender: Any) {
if (self.email.text != nil && self.password.text != nil) {
let authDetails = AWSCognitoIdentityPasswordAuthenticationDetails(username: self.email.text!, password: self.password.text! )
self.passwordAuthenticationCompletion?.set(result: authDetails)
} else {
let alertController = UIAlertController(title: "Missing information",
message: "Please enter a valid user name and password",
preferredStyle: .alert)
let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
alertController.addAction(retryAction)
}
}
}
extension LoginViewController: AWSCognitoIdentityPasswordAuthentication {
public func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource<AWSCognitoIdentityPasswordAuthenticationDetails>) {
print(passwordAuthenticationCompletionSource)
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
DispatchQueue.main.async {
if (self.usernameText == nil) {
self.usernameText = authenticationInput.lastKnownUsername
}
}
}
public func didCompleteStepWithError(_ error: Error?) {
print(error)
DispatchQueue.main.async {
if let error = error as NSError? {
let alertController = UIAlertController(title: error.userInfo["__type"] as? String,
message: error.userInfo["message"] as? String,
preferredStyle: .alert)
let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
alertController.addAction(retryAction)
self.present(alertController, animated: true, completion: nil)
} else {
self.email.text = nil
self.dismiss(animated: true, completion: nil)
}
}
}
}
AppDelegate.swift I have this
//
// AppDelegate.swift
import UIKit
import AWSCognitoAuth
import AWSSNS
import AWSCognitoIdentityProvider
import UserNotifications
import ESTabBarController_swift
import AWSMobileClient
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
var navigationController: UINavigationController?
var storyboard: UIStoryboard?
var loginViewController: LoginViewController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// setup logging
AWSDDLog.sharedInstance.logLevel = .verbose
// setup service configuration
let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil)
// create pool configuration
let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: Constants.APIKeys.AWSClientID,
clientSecret: Constants.APIKeys.AWSSecret,
poolId: Constants.APIKeys.AWSPoolID)
// initialize user pool client
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: "UserPool")
// fetch the user pool client we initialized in above step
let pool = AWSCognitoIdentityUserPool(forKey: "UserPool")
self.window = UIWindow(frame: UIScreen.main.bounds)
self.storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
pool.delegate = self
AppController.sharedInstance.launchInWindow(aWindow: self.window)
return true
}
//MARK: Push Notification
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
/// Attach the device token to the user defaults
var token = ""
for i in 0..<deviceToken.count {
token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
}
print(token)
UserDefaults.standard.set(token, forKey: "deviceTokenForSNS")
/// Create a platform endpoint. In this case, the endpoint is a
/// device endpoint ARN
let sns = AWSSNS.default()
let request = AWSSNSCreatePlatformEndpointInput()
request?.token = token
request?.platformApplicationArn = Constants.APIKeys.AWSSSNSARN
sns.createPlatformEndpoint(request!).continueWith(executor: AWSExecutor.mainThread(), block: { (task: AWSTask!) -> AnyObject? in
if task.error != nil {
print("Error: \(String(describing: task.error))")
} else {
let createEndpointResponse = task.result! as AWSSNSCreateEndpointResponse
if let endpointArnForSNS = createEndpointResponse.endpointArn {
print("endpointArn: \(endpointArnForSNS)")
Settings.setPushArn(endpointArnForSNS)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "RegisteredForPush"), object: nil)
}
}
return nil
})
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let visible = window?.visibleViewController()
if let data = userInfo["aps"] as? [AnyHashable: Any] {
if let route = data["route"] as? String {
switch route {
case "matchRequested":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
var matchId = ""
if let match = data["matchId"] as? String {
matchId = match
}
let projectMatches = MatchRequestedViewController(withNotificationPayload: projectId, matchId: matchId)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
case "projectDetails":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
let projectMatches = ProjectDetailsViewController(withProject: TERMyProject(), orProjectId: projectId)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
case "matched":
var projectId = ""
if let project = data["projectId"] as? String {
projectId = project
}
var matchId = ""
if let match = data["matchId"] as? String {
matchId = match
}
var originProject: TERMyProject = TERMyProject()
var matchedProject: TERMatchedProject = TERMatchedProject()
AppController.sharedInstance.AWSClient?.projectsGet(id:projectId).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
print("Error: \(error)")
} else if let result = task.result {
if result is NSDictionary {
DispatchQueue.main.async {
let array = [result]
let parsedProject: [TERMyProject] = TERMyProject.parseFromAPI(array:array as! [NSDictionary])
for project in parsedProject {
originProject = project
}
// self.initialSetup()
}
AppController.sharedInstance.AWSClient?.projectsGet(id:matchId).continueWith(block: { (task: AWSTask) -> Any? in
if let error = task.error {
print("Error: \(error)")
} else if let result = task.result {
if result is NSDictionary {
DispatchQueue.main.async {
let array = [result]
let parsedProject: [TERMatchedProject] = TERMatchedProject.parseFromAPI(array:array as! [NSDictionary])
for project in parsedProject {
matchedProject = project
}
let projectMatches = MatchedViewController(withProject: originProject, match: matchedProject, isComplete: false)
visible?.navigationController?.pushViewController(projectMatches, animated: true)
}
}
}
return nil
})
}
}
return nil
})
default:
break
}
}
}
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error.localizedDescription)
}
// Called when a notification is delivered to a foreground app.
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("User Info = ",notification.request.content.userInfo)
completionHandler([.alert, .badge, .sound])
}
//MARK: Boiler-plate methods
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
if (self.navigationController == nil) {
self.navigationController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? UINavigationController
}
if (self.loginViewController == nil) {
self.loginViewController = self.navigationController?.viewControllers[0] as? LoginViewController
}
DispatchQueue.main.async {
self.navigationController!.popToRootViewController(animated: true)
if (!self.navigationController!.isViewLoaded
|| self.navigationController!.view.window == nil) {
self.window?.rootViewController?.present(self.navigationController!,
animated: true,
completion: nil)
}
}
return self.loginViewController!
}
}
// MARK:- AWSCognitoIdentityRememberDevice protocol delegate
extension AppDelegate: AWSCognitoIdentityRememberDevice {
func didCompleteStepWithError(_ error: Error?) {
}
func getRememberDevice(_ rememberDeviceCompletionSource: AWSTaskCompletionSource<NSNumber>) {
}
}
extension UIWindow {
func visibleViewController() -> UIViewController? {
if let rootViewController: UIViewController = self.rootViewController {
return UIWindow.getVisibleViewControllerFrom(vc: rootViewController)
}
return nil
}
class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
switch(vc){
case is UINavigationController:
let navigationController = vc as! UINavigationController
return UIWindow.getVisibleViewControllerFrom( vc: navigationController.visibleViewController!)
break;
case is UITabBarController:
let tabBarController = vc as! UITabBarController
return UIWindow.getVisibleViewControllerFrom(vc: tabBarController.selectedViewController!)
break;
default:
if let presentedViewController = vc.presentedViewController {
//print(presentedViewController)
if let presentedViewController2 = presentedViewController.presentedViewController {
return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController2)
}
else{
return vc;
}
}
else{
return vc;
}
break;
}
}
}

Logout functionality in swift

I am trying to provide in my app the logout functionality, I would like to know if this way is a good approach to continue.Classes involved are described below:
the first one is the AuthViewCoordinator, which class redirects to the user to auth screens
protocol AuthViewCoordinatorDelegate: class {
func authCompleted(coordinator: AuthViewCoordinator)
}
class AuthViewCoordinator: Coordinator {
weak var fromViewController: UIViewController?
weak var navigationController: UINavigationController?
weak var delegate: AuthViewCoordinatorDelegate?
init(fromViewController: UIViewController, delegate: AuthViewCoordinatorDelegate) {
self.fromViewController = fromViewController
self.delegate = delegate
}
func start() {
let authViewController = UIStoryboard.main.instantiateViewController(withIdentifier: "AuthViewController") as! AuthViewController
authViewController.coordinator = self
let navigationController = NavigationController(rootViewController: authViewController)
navigationController.navigationBar.isHidden = true
fromViewController?.present(navigationController, animated: true, completion: nil)
self.navigationController = navigationController
}
func userDidSelectLogin() {
let loginViewController = UIStoryboard.main.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
loginViewController.viewModel.coordinator = self
self.navigationController?.pushViewController(loginViewController, animated: true)
}
func userDidSelectSignUp() {
let signupViewController = UIStoryboard.main.instantiateViewController(withIdentifier: "SignUpViewController") as! SignUpViewController
signupViewController.viewModel.coordinator = self
self.navigationController?.pushViewController(signupViewController, animated: true)
}
func userDidLogin() {
navigationController?.dismiss(animated: true, completion: nil)
self.delegate?.authCompleted(coordinator: self)
}
func userDidSignUp() {
navigationController?.dismiss(animated: true, completion: nil)
self.delegate?.authCompleted(coordinator: self)
}
}
And the 2nd one is an external class called SessionController. In this class I'm trying to call AuthViewCoordinator().start() inside the function logout immediately after the tokens have been removed to show again the auth screen to the user, but the output is
Use of unresolved identifier 'AuthViewCoordinator'
public class SessionController{
public enum SessionState {
case anonymous
case authenticated
case notAuthenticated
}
let service: Service
let sessionProvider: SessionProvider
convenience public init() {
self.init(service: Service.instance, sessionProvider: SessionProvider.instance)
}
init(service: Service, sessionProvider: SessionProvider) {
self.service = service
self.sessionProvider = sessionProvider
}
public func getMe(completion: #escaping (Error?) -> ()){
service.execute(resource: Login.getMe()) { (result) in
if let error = result.error {
completion(error)
} else if let session = result.value {
print("\n session \(session)\n")
completion(nil)
}
}
}
public func logout() {
self.sessionProvider.removeUserToken()
self.sessionProvider.removeInstanceToken()
self.sessionProvider.removeAnonymousToken()
AuthViewController().start()
}
public func state() -> SessionState {
if let _ = sessionProvider.getUserToken() {
print("###### authenticated #########")
return .authenticated
} else if let _ = sessionProvider.getAnonymousToken() {
print("###### anonymous #########")
return .anonymous
} else {
print("###### notAuthenticated #########")
return .notAuthenticated
}
}
}

Resources