How to Show Firebase Auth User UID and also my Firestore doc uid as Stripe customer id - ios

I am trying to Show Firestore doc uid as Stripe custommer id.and to link Stripe to Firestore cloud but it show error - "error loading page: failed to decode response from the server" as shown in the image -
My cloud function i.e. index.js -
const admin = require('firebase-admin');
const express = require('express')
admin.initializeApp();
const functions = require('firebase-functions');
const stripe = require('stripe') ("sk_________");
// When a user is created, register them with Stripe
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
const customer = await stripe.customers.create({email: user.email});
return admin.firestore().collection('stripe_customers').doc(user.uid).set({customer_id: customer.id});
});
exports.createPaymentIntent = functions.https.onCall(async (req, res) => {
const amount = req.amount;
const customer = req.customer;
console.log(customer)
const paymentIntent = await stripe.paymentIntents.create({
amount: 1099,
currency: 'usd',
});
const clientSecret = paymentIntent.clientSecret
console.log(clientSecret)
return clientSecret
});
exports.getPaymentMethods = functions.https.onCall(async (req, res) => {
const customer = req.customer;
const type = "card"
stripe.paymentMethods.list({customer : customer, type: type}, function(err, paymentMethods) {
if (err !== null) {
console.log("ERROR")
} else {
return paymentMethods
}
return
})
});
exports.createEphemeralKey = functions.https.onCall(async(data, context) => {
const stripeVersion = data.stripe_version;
const customerId = data.customer_id;
return stripe.ephemeralKeys.create(
{customer: customerId},
{stripe_version: stripeVersion}
).then((key) => {
return key
}).catch((err) => {
console.log(err)
})
})
My CheckoutViewController -
import UIKit
import Stripe
import Firebase
let backendUrl = "https://fireupgoods-a0036.firebaseio.com/"
class CheckoutViewController: UIViewController {
var paymentIntentClientSecret: String?
lazy var cardTextField: STPPaymentCardTextField = {
let cardTextField = STPPaymentCardTextField()
return cardTextField
}()
lazy var payButton: UIButton = {
let button = UIButton(type: .custom)
button.layer.cornerRadius = 5
button.backgroundColor = .systemBlue
button.titleLabel?.font = UIFont.systemFont(ofSize: 22)
button.setTitle("Pay now", for: .normal)
button.addTarget(self, action: #selector(pay), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jxpk_test_51HmAsMFzRM1fSBZ2tTT1SxPxRBzParFfjm6s0aXD0F5dYLeOSVCKtiZ4lq0TGwJxPhHpsq6Hga7I0QzRWisPYMdj00bDxAMTKf"
view.backgroundColor = .white
let stackView = UIStackView(arrangedSubviews: [cardTextField, payButton])
stackView.axis = .vertical
stackView.spacing = 20
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leftAnchor.constraint(equalToSystemSpacingAfter: view.leftAnchor, multiplier: 2),
view.rightAnchor.constraint(equalToSystemSpacingAfter: stackView.rightAnchor, multiplier: 2),
stackView.topAnchor.constraint(equalToSystemSpacingBelow: view.safeAreaLayoutGuide.topAnchor, multiplier: 2),
])
startCheckout()
}
func displayAlert(title: String, message: String, restartDemo: Bool = false) {
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel))
self.present(alert, animated: true, completion: nil)
}
}
func startCheckout() {
// Create a PaymentIntent as soon as the view loads
let url = URL(string: backendUrl + "create-payment-intent")!
let json: [String: Any] = [
"items": [
["id": "xl-shirt"]
]
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: json)
let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in
guard let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
let clientSecret = json["clientSecret"] as? String else {
let message = error?.localizedDescription ?? "Failed to decode response from server."
self?.displayAlert(title: "Error loading page", message: message)
return
}
print("Created PaymentIntent")
self?.paymentIntentClientSecret = clientSecret
})
task.resume()
}
#objc
func pay() {
guard let paymentIntentClientSecret = paymentIntentClientSecret else {
return;
}
// Collect card details
let cardParams = cardTextField.cardParams
let paymentMethodParams = STPPaymentMethodParams(card: cardParams, billingDetails: nil, metadata: nil)
let paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntentClientSecret)
paymentIntentParams.paymentMethodParams = paymentMethodParams
// Submit the payment
let paymentHandler = STPPaymentHandler.shared()
paymentHandler.confirmPayment(paymentIntentParams, with: self) { (status, paymentIntent, error) in
switch (status) {
case .failed:
self.displayAlert(title: "Payment failed", message: error?.localizedDescription ?? "")
break
case .canceled:
self.displayAlert(title: "Payment canceled", message: error?.localizedDescription ?? "")
break
case .succeeded:
self.displayAlert(title: "Payment succeeded", message: paymentIntent?.description ?? "")
break
#unknown default:
fatalError()
break
}
}
}
}
extension CheckoutViewController: STPAuthenticationContext {
func authenticationPresentingViewController() -> UIViewController {
return self
}
}
screenshot of the error -
Firebase code which writes my Firebase Auth User UID to Firestore Document ID is -
import UIKit
import Firebase
import FirebaseFirestore
class UserIdtoFirestoreViewController: UIViewController {
let db = Firestore.firestore()
var ref: DocumentReference? = nil
let email = Auth.auth().currentUser!.email
let id = Auth.auth().currentUser!.uid
let fullName = Auth.auth().currentUser!.displayName
#IBAction func GetUserId(_ sender: UIButton) {
db
.collection("users")
.document(id)
.setData([
"email":email,"fullName":fullName])
}
}
The above code writes my Auth User UIDfrom here (see image below):-
to here (see image below):-
As you can see in the 2 images above, the Auth User UID (which begins with 2k3k) gets written to Firestore Document ID. I want the same ID to be shown in Stripe customer ID and logs and events in Stripe, but as you can see below, there is nothing on the Stripe Dashboard: -
How to sort it out ?
I know I am missing out on many things, but my immediate issue is to make the error go away and the Firestore Document ID to be added as Stripe customer id. programmatically.
Any help will be appreciated.
Edit: I am also adding AppDelegate code and MyAPIClient code below:-
AppDelegate code -
import UIKit
import GoogleSignIn
import Firebase
import CoreData
import Stripe
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Stripe.setDefaultPublishableKey( "pk_test_51HmAsMFzRM1fSBZ2tTT1SxPxRBzParFfjm6s0aXD0F5dYLeOSVCKtiZ4lq0TGwJxPhHpsq6Hga7I0QzRWisPYMdj00bDxAMTKf")
FirebaseApp.configure()
// Override point for customization after application launch.
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance().handle(url as URL?)
}
func sign(_ signIn: GIDSignIn!,
didSignInFor user: GIDGoogleUser!,
withError error: Error!) {
// Check for sign in error
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
} else {
print("\(error.localizedDescription)")
}
return
}
// Get credential object using Google ID token and Google access token
guard let authentication = user.authentication else {
return
}
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
// Authenticate with Firebase using the credential object
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print("Error occurs when authenticate with Firebase: \(error.localizedDescription)")
}
// Post notification after user successfully sign in
// NotificationCenter.default.post(name: .signInGoogleCompleted, object: nil)
}
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
// Perform any operations when the user disconnects from app here.
print("User has disconnected")
}
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) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
MyAPIClient code:-
import Stripe
import UIKit
class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider {
let baseURL = URL(string: "https://api.stripe.com")!
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
let url = self.baseURL.appendingPathComponent("ephemeral_keys")
var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)!
urlComponents.queryItems = [URLQueryItem(name: "api_version", value: apiVersion)]
var request = URLRequest(url: urlComponents.url!)
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data,
let json = ((try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]) as [String : Any]??) else {
completion(nil, error)
return
}
completion(json, nil)
})
task.resume()
}
}
Thanks.

Related

How to handle the redirect URLs for the PianoID SDK iOS?

When I try to connect to Facebook in my iOS application, the app redirects me to the Facebook app and then there is a loop between the two apps, meaning that the user is being sent back and forth between the two apps without being able to complete the login process. I suspect that there is something missing in my code to properly handle the redirect URLs for the PianoID SDK and I would like to know what needs to be done to fix this issue.
this is my appDelegate class :
#objc class AppDelegate: FlutterAppDelegate, PianoIDDelegate {
private var flutterResult: FlutterResult? = nil
#Published private(set) var initialized = false
#Published private(set) var token: PianoIDToken?
var cookiessseion = ""
var startdate = ""
var enddate = ""
var pianoToken=""
func pianoID(_ pianoID: PianoOAuth.PianoID, didSignInForToken token: PianoOAuth.PianoIDToken!, withError error: Error!) {}
func pianoID(_ pianoID: PianoOAuth.PianoID, didSignOutWithError error: Error!) {}
func pianoIDSignInDidCancel(_ pianoID: PianoOAuth.PianoID) {}
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
PianoID.shared.endpointUrl = Settings.endpoint.api
PianoID.shared.aid = Settings.aid
PianoID.shared.isSandbox = true
PianoID.shared.signUpEnabled = true
PianoID.shared.googleClientId = Settings.clientId
PianoID.shared.delegate = self
PianoOAuth.PianoIDApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
GeneratedPluginRegistrant.register(with: self)
let name = Bundle.main.infoDictionary?["CFBundleName"]
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"]
GEMConfig.sharedInstance().setAppInfo(name as? String, version: version as? String)
//FirebaseApp.configure()
guard let controller = window?.rootViewController as? FlutterViewController else {
fatalError("rootViewController is not type FlutterViewController")
}
let Channel = FlutterMethodChannel(name: "flutter.native/helper", binaryMessenger: controller.binaryMessenger)
Channel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
if(call.method == "testpiano"){
self?.initPianoID()
self?.flutterResult = result
} else {
result(FlutterMethodNotImplemented)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
/// Sign In callback
func signIn(result: PianoIDSignInResult!, withError error: Error!) {
if let r = result {
token = r.token
do {
let PianoIDTokenString = try JSONParserSwift.getJSON(object: token)
self.flutterResult?(PianoIDTokenString)
} catch {
print("Unexpected data error: \(error)")
}
} else {
}
}
/// Sign Out callback
func signOut(withError error: Error!) {
token = nil
}
/// Cancel callback
func cancel() {
}
private func initPianoID() {
PianoID.shared.signIn()
}
func openSettings(alert: UIAlertAction!) {
if let url = URL.init(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
override public func applicationDidBecomeActive(_ application: UIApplication) {
debugPrint("applicationDidBecomeActive")
if #available(iOS 14, *) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
ATTrackingManager.requestTrackingAuthorization { status in
}
}
}
}
}
extension Date {
func currentTimeMillis() -> Int64 {
return Int64(self.timeIntervalSince1970)
}
}
And this is the documentation : link

How to convert Google iOS Sign-In single page sample AppDelegate.h protocol -> to a segue to LoginPage ViewController AppDelegate.swift protocol?

Google's Sign-In sample on GitHub SignInSampleForPod.xcworkspace creates a single page sign-in using the AppDelegate.h SignInViewController.m etc protocol.
However many apps, such as mine, prefer to segue to a Login Page only when a user makes a choice requiring verification. I just want the basic Google Profile info and authentication token.
I have the Google iOS ClientID configured enough so a segue to my LoginPage.swift shows the Google Sign-In button via AppDelegate.swift:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(
_ app: UIApplication,
open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]
) -> Bool {
var handled: Bool
handled = GIDSignIn.sharedInstance.handle(url)
if handled {
return true
}
// Handle other custom URL types.
// If not handled by this app, return false.
return false
}
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
if error != nil || user == nil {
// Show the app's signed-out state.
} else {
// Show the app's signed-in state.
}
}
return true
}
And LoginPage.swift:
class LoginViewController: UIViewController {
let signInConfig = GIDConfiguration.init(clientID: "foo-bar85.apps.googleusercontent.com")
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
let emailAddress = user.profile?.email
let fullName = user.profile?.name
let givenName = user.profile?.givenName
let familyName = user.profile?.familyName
let profilePicUrl = user.profile?.imageURL(withDimension: 320)
}
So my question is what is the AppDelegate.swift Google Sign-In code for the fields shown below to display the basic profile info:
// Show the app's signed-out state.
} else {
// Show the app's signed-in state.
I may not able to understand your problem clearly.
But I am trying to answer based on my understanding.
You can create a class (GoogleLoginManager) for all google login related stuff and create a button in UI then call this method (signIn) from button action.
#IBAction func googleButtonAction(_ sender: Any) {
GoogleLoginManager.shared.signIn(controller: self) { (profile) in
print("GoogleLogin profile : \(String(describing: profile.name)), \(String(describing: profile.email))")
} onFailure: { (error) in
print("GoogleLogin error : \(String(describing: error.localizedDescription))")
}
}
import Foundation
import GoogleSignIn
class GoogleLoginManager: SocialLogin {
fileprivate var onSuccess : success?
fileprivate var onFailure : failure?
static let shared = GoogleLoginManager()
private override init() { }
func signIn(controller: UIViewController, onSuccess : #escaping success, onFailure : #escaping failure) {
self.onSuccess = onSuccess
self.onFailure = onFailure
GIDSignIn.sharedInstance().clientID = GOOGLE_CLIENT_ID
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().presentingViewController = controller
GIDSignIn.sharedInstance().signIn()
// Automatically sign in the user.
// GIDSignIn.sharedInstance()?.restorePreviousSignIn()
}
func signOut() {
GIDSignIn.sharedInstance().signOut()
}
}
extension GoogleLoginManager : GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!,
withError error: Error!) {
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
}
else if (error as NSError).code == GIDSignInErrorCode.canceled.rawValue {
print("user canceled the sign in request")
}
else {
print("\(error.localizedDescription)")
}
self.onFailure?(error)
return
}
var profile = SocialProfileModel.init(user: user)
profile.loginSuccess = true
self.onSuccess?(profile)
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!,
withError error: Error!) {
// Perform any operations when the user disconnects from app here.
print("GIDSignIn : didDisconnectWith")
}
}
I just had to modify my above AppDelegate.swift slightly - adding a standard UIbutton linked to the following action - gets the profile info:
#IBAction func LogInButtonTouched(_ sender: UIButton) {
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
let emailAddress = user.profile?.email
let fullName = user.profile?.name
let givenName = user.profile?.givenName
let familyName = user.profile?.familyName
let profilePicUrl = user.profile?.imageURL(withDimension: 320)
print("GoogleLogin profile : \(String(describing: user.profile?.name)), \(String(describing: user.profile?.email))")
}
}

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;
}
}
}

present a specific VC when local notification is tapped Swift

I tried to follow various solutions to the same problem in other posts but none seems to be applicable to my code.
I merged an alarm into my app and my goal is to get to NewMapViewControlleron user tapping on alarm local notification and call checkAlerts2() function which is not normally called when going to that VC via the app menu.
Thank you for you help as usual.
This is my AppDelegate:
import UIKit
import CoreData
import Foundation
import AudioToolbox
import AVFoundation
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,AVAudioPlayerDelegate, AlarmApplicationDelegate {
var window: UIWindow?
var audioPlayer: AVAudioPlayer?
let alarmScheduler: AlarmSchedulerDelegate = Scheduler()
var alarmModel: Alarms = Alarms()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
var error: NSError?
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch let error1 as NSError{
error = error1
print("could not set session. err:\(error!.localizedDescription)")
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch let error1 as NSError{
error = error1
print("could not active session. err:\(error!.localizedDescription)")
}
window?.tintColor = UIColor.blue
FirebaseApp.configure()
return true
}
//receive local notification when app in foreground
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
//show an alert window
let storageController = UIAlertController(title: "Check route for alerts?", message: nil, preferredStyle: .alert)
var isSnooze: Bool = false
var soundName: String = ""
var routeName: String = ""
var index: Int = -1
if let userInfo = notification.userInfo {
isSnooze = userInfo["snooze"] as! Bool
soundName = userInfo["soundName"] as! String
routeName = userInfo["routeName"] as! String
index = userInfo["index"] as! Int
}
playSound(soundName)
//schedule notification for snooze
if isSnooze {
let snoozeOption = UIAlertAction(title: "Snooze", style: .default) {
(action:UIAlertAction)->Void in self.audioPlayer?.stop()
self.alarmScheduler.setNotificationForSnooze(snoozeMinute: 9, soundName: soundName, routeName: routeName, index: index)
}
storageController.addAction(snoozeOption)
}
let stopOption = UIAlertAction(title: "OK", style: .default) {
(action:UIAlertAction)->Void in self.audioPlayer?.stop()
AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate)
self.alarmModel = Alarms()
self.alarmModel.alarms[index].onSnooze = false
//change UI
var mainVC = self.window?.visibleViewController as? MainAlarmViewController
// var mainVC = self.window?.visibleViewController as? NewMapViewController
if mainVC == nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
mainVC = storyboard.instantiateViewController(withIdentifier: "Alarm") as? MainAlarmViewController
// mainVC = storyboard.instantiateViewController(withIdentifier: "Alarm") as? NewMapViewController
}
mainVC!.changeSwitchButtonState(index: index)
// MainAlarmViewController?.changeSwitchButtonState(index: index)
}
storageController.addAction(stopOption)
window?.visibleViewController?.navigationController?.present(storageController, animated: true, completion: nil)
}
//snooze notification handler when app in background
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: #escaping () -> Void) {
var index: Int = -1
var soundName: String = ""
var routeName: String = ""
if let userInfo = notification.userInfo {
soundName = userInfo["soundName"] as! String
routeName = userInfo["routeName"] as!String
index = userInfo["index"] as! Int
}
self.alarmModel = Alarms()
self.alarmModel.alarms[index].onSnooze = false
if identifier == Id.snoozeIdentifier {
alarmScheduler.setNotificationForSnooze(snoozeMinute: 9, soundName: soundName, routeName: routeName, index: index)
self.alarmModel.alarms[index].onSnooze = true
}
completionHandler()
}
//AlarmApplicationDelegate protocol
func playSound(_ soundName: String) {
//vibrate phone first
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
//set vibrate callback
AudioServicesAddSystemSoundCompletion(SystemSoundID(kSystemSoundID_Vibrate),nil,
nil,
{ (_:SystemSoundID, _:UnsafeMutableRawPointer?) -> Void in
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
},
nil)
let url = URL(fileURLWithPath: Bundle.main.path(forResource: soundName, ofType: "mp3")!)
var error: NSError?
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
} catch let error1 as NSError {
error = error1
audioPlayer = nil
}
if let err = error {
print("audioPlayer error \(err.localizedDescription)")
return
} else {
audioPlayer!.delegate = self
audioPlayer!.prepareToPlay()
}
//negative number means loop infinity
audioPlayer!.numberOfLoops = -1
audioPlayer!.play()
}
//AVAudioPlayerDelegate protocol
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
}
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
}
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) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
// audioPlayer?.play()
alarmScheduler.checkNotification()
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "fix-it mapView")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}

I can't play music with Spotify iOS SDK with Swift 4.0

I have a struggle with Spotify SDK, I followed every step correctly, but I can't play music with my premium account on my project. There is no error or crash, my app directs me to the Spotify login page and after facebook login, It brings me back to my app again. Yesterday I'd get "logged in" print but today I can't. I'm trying to play a song after login and also manually as you see below. I'm wondering, am I lucky to find an answer?
override func viewDidLoad() {
super.viewDidLoad()
self.spotify()
NotificationCenter.default.addObserver(self, selector: #selector(GeneralNewsViewController.updateAfterFirstLogin), name: NSNotification.Name(rawValue: "SpotifySession"), object: nil)
}
func spotify() {
// insert redirect your url and client ID below
auth.redirectURL = URL(string: "correctCallbackURl")
auth.clientID = "correctClientID"
auth.requestedScopes = [SPTAuthStreamingScope, SPTAuthPlaylistReadPrivateScope, SPTAuthPlaylistModifyPublicScope, SPTAuthPlaylistModifyPrivateScope]
loginUrl = auth.spotifyWebAuthenticationURL()
}
func initializaPlayer(authSession:SPTSession){
if self.player == nil {
self.player = SPTAudioStreamingController.sharedInstance()
self.player!.playbackDelegate = self
self.player!.delegate = self
try! player?.start(withClientId: auth.clientID)
self.player!.login(withAccessToken: authSession.accessToken)
}
}
#objc func updateAfterFirstLogin () {
loginButton.isHidden = true
let userDefaults = UserDefaults.standard
if let sessionObj:AnyObject = userDefaults.object(forKey: "SpotifySession") as AnyObject? {
let sessionDataObj = sessionObj as! Data
let firstTimeSession = NSKeyedUnarchiver.unarchiveObject(with: sessionDataObj) as! SPTSession
self.session = firstTimeSession
initializaPlayer(authSession: session)
self.loginButton.isHidden = true
// self.loadingLabel.isHidden = false
}
}
func audioStreamingDidLogin(_ audioStreaming: SPTAudioStreamingController!) {
// after a user authenticates a session, the SPTAudioStreamingController is then initialized and this method called
print("logged in")
self.player?.playSpotifyURI("spotify:track:4aDLPXlzHZm26GppvRwms8", startingWith: 0, startingWithPosition: 0, callback: { (error) in
if (error != nil) {
print("playing!")
} else {
print(error?.localizedDescription)
}
})
}
#objc func play() {
player?.playSpotifyURI("spotify:track:4aDLPXlzHZm26GppvRwms8", startingWith: 0, startingWithPosition: 0, callback: { (err) in
if err != nil {
print(err?.localizedDescription)
} else {
}
})
}
class AppDelegate: UIResponder, UIApplicationDelegate ,UNUserNotificationCenterDelegate{
auth.redirectURL = URL(string: "correctCallbackURl")
auth.sessionUserDefaultsKey = "current session"}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
// 2- check if app can handle redirect URL
if auth.canHandle(auth.redirectURL) {
// 3 - handle callback in closure
auth.handleAuthCallback(withTriggeredAuthURL: url, callback: { (error, session) in
// 4- handle error
if error != nil {
print("error!")
}
// 5- Add session to User Defaults
let userDefaults = UserDefaults.standard
let sessionData = NSKeyedArchiver.archivedData(withRootObject: session!)
userDefaults.set(sessionData, forKey: "SpotifySession")
userDefaults.synchronize()
// 6 - Tell notification center login is successful
NotificationCenter.default.post(name: Notification.Name(rawValue: "loginSuccessfull"), object: nil)
})
return true
}
return false
}}
It looks like you're subscribed to the wrong notification, you're subscribing to "SpotifySession" but posting "loginSuccessfull"
this answer will help you making this kind of mistake in the future by having enums for events in a simple way.
good debugging is also key, you could've probably solved this problem with some breakpoints to see where you went wrong.
cheers

Resources