Cannot get Plaid sandbox to work on iOS with Swift 3 - ios

When I use the un:user_good and pw:pass_good I get the attached screen
And it does not return a accessToken
I have already added my public key in my AppDelegate
[![#IBAction fu][1]][1]nc plaidConnectButton(_ sender: Any) {
let plaidLink = PLDLinkNavigationViewController(environment: .tartan, product: .connect)!
plaidLink.linkDelegate = self
plaidLink.providesPresentationContextTransitionStyle = true
plaidLink.definesPresentationContext = true
plaidLink.modalPresentationStyle = .custom
self.present(plaidLink, animated: true, completion: nil)
}
func linkNavigationContoller(_ navigationController: PLDLinkNavigationViewController!, didFinishWithAccessToken accessToken: String!) {
print("success \(accessToken)")
myAPIClient.connectAddBank(bankToken: accessToken, completion:{
(error) in
if(error != nil) {
print(error.debugDescription)
} else {
print("successfully added bank")
self.dismiss(animated: true, completion: nil)
}
})
}
func linkNavigationControllerDidFinish(withBankNotListed navigationController: PLDLinkNavigationViewController!) {
print("Manually enter bank info?")
self.performSegue(withIdentifier: "unlistedBankSegue", sender: self)
}
func linkNavigationControllerDidCancel(_ navigationController: PLDLinkNavigationViewController!) {
self.dismiss(animated: true, completion: nil)
}
Please help

Set your environment to .sandbox during the init process.
Plaid support link for this: https://github.com/plaid/link/issues/140

Related

How to determine when a user has succesfully logged in using FirebaseUI in swift?

To log a user in using the firebaseUI library you can do the following:
let authUI = FUIAuth.defaultAuthUI()
guard authUI != nil else {
return
}
authUI?.providers = [
FUIEmailAuth(), //works
FUIGoogleAuth(), //works
FUIOAuth.appleAuthProvider() //does not work
]
authUI?.delegate = self
authUI?.shouldHideCancelButton = true
let authViewController = authUI!.authViewController()
authViewController.modalPresentationStyle = .overCurrentContext
self.present(authViewController, animated: true, completion: nil)
and according to the documentation you include the following function on completion of user log in:
func authUI(_ authUI: FUIAuth,didSignInWith authDataResult: AuthDataResult?, error: Error?) {
if let user = authDataResult?.user{
print("Nice! You've signed in as \(user.uid)")
performSegue(withIdentifier: K.welcomeSegue, sender: self)
}
}
However, in my code, when I use the Apple sign-in option this function never runs. Do I need to call this function somewhere? If so how do I call it?
Here is the full code to my view controller:
class WelcomeViewController: UIViewController, FUIAuthDelegate {
#IBOutlet weak var titleLabel: UILabel!
override func viewWillAppear(_ animated: Bool){
if(Auth.auth().currentUser != nil){
self.performSegue(withIdentifier: K.welcomeSegue, sender: self)
}
}
#IBAction func loginPressed(_ sender: UIButton) {
let authUI = FUIAuth.defaultAuthUI()
guard authUI != nil else {
return
}
authUI?.providers = [
FUIEmailAuth(),
FUIGoogleAuth(),
FUIOAuth.appleAuthProvider()
]
authUI?.delegate = self
authUI?.shouldHideCancelButton = true
let authViewController = authUI!.authViewController()
authViewController.modalPresentationStyle = .overCurrentContext
self.present(authViewController, animated: true, completion: nil)
}
func authUI(_ authUI: FUIAuth,didSignInWith authDataResult: AuthDataResult?, error: Error?) {
if let user = authDataResult?.user{
print("Nice! You've signed in as \(user.uid)")
performSegue(withIdentifier: K.welcomeSegue, sender: self)
}
}
}

rootViewController not loading after calling it, using -> present(viewControllerToPresent: UIViewController, animated: true, completion: nil)

My rootViewController works and loads fine. However, when I call it using the present(viewControllerToPresent: UIViewController, animated: true, completion: nil) from another viewController I get nothing but a black screen.
I looked all over the stackOverflow but only found solutions for storyboard users. I am doing this programmatically
#objc func handleLogin() {
print("LOGIN BUTTON TOUCHED")
guard let email = emailTextField.text, let password = passwordTextField.text else {
print("Form is not valid.")
return
}
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if error != nil {
print(error!)
return
}
let viewController = RootViewController()
*** self.present(viewController, animated: true, completion: nil)
print("Logged in")
}
}
There is nothing wrong with self.present(viewController, animated: true, completion: nil)
I think your RootViewController() is already presented. The black screen you see is might be the one without any data? I am not sure, will need your code for that class.
Another approach you can consider is to replace the real rootViewController from current uiWindow like this
#objc func handleLogin() {
print("LOGIN BUTTON TOUCHED")
guard let email = emailTextField.text, let password = passwordTextField.text else {
print("Form is not valid.")
return
}
Auth.auth().signIn(withEmail: email, password: password) { [weak self] (user, error) in
guard let strongSelf = self else { return }
guard error == nil else { return }
guard let user = user else { return }
UIApplication.shared.keyWindow?.rootViewController = RootViewController()
print("Logged in")
}
}
you've created a new instance(RootViewController) and it must be black .. your not referring to your RootViewController

perform segue after admob ad is dismissed

any idea on how to perform this segue. Once users sign up and they are authenticated they will be shown an interstitial ad. however once the ad is done or dismissed. the segue to the next view controller should be performed. I not exactly sure what im missing in my code:
#IBAction func signUpBtn_TouchUpInside(_ sender: Any) {
view.endEditing(true)
ProgressHUD.show("Waiting...", interaction: false)
if let profileImg = self.selectedImage, let imageData = UIImageJPEGRepresentation(profileImg, 0.1) {
AuthService.signUp(username: usernameTextField.text!, email: emailTextField.text!, password: passwordTextField.text!, imageData: imageData, onSuccess: {
ProgressHUD.showSuccess("Success")
if self.interstitial.isReady {
self.interstitial.present(fromRootViewController: self)
} else {
print("Ad wasn't ready")
self.performSegue(withIdentifier: "signUpToTabbarVC", sender: nil)
}
self.performSegue(withIdentifier: "signUpToTabbarVC", sender: nil)
}, onError: { (errorString) in
ProgressHUD.showError(errorString!)
})
} else {
ProgressHUD.showError("Profile Image can't be empty")
}
}
and help or feedback is always greatly appreciated
You should conform your ViewController to interstitial delegate and perform the segue when interstitial is dismissed
extension ViewController: GADInterstitialDelegate {
func interstitialDidDismissScreen(_ ad: GADInterstitial) {
self.performSegue(withIdentifier: "signUpToTabbarVC", sender: nil)
}
}
And update signUpBtn_TouchUpInside method as below,
#IBAction func signUpBtn_TouchUpInside(_ sender: Any) {
view.endEditing(true)
ProgressHUD.show("Waiting...", interaction: false)
if let profileImg = self.selectedImage, let imageData = UIImageJPEGRepresentation(profileImg, 0.1) {
AuthService.signUp(username: usernameTextField.text!, email: emailTextField.text!, password: passwordTextField.text!, imageData: imageData, onSuccess: {
self.handleSignupSuccess()
}, onError: { (errorString) in
ProgressHUD.showError(errorString!)
})
} else {
ProgressHUD.showError("Profile Image can't be empty")
}
}
private func handleSignupSuccess() {
ProgressHUD.showSuccess("Success")
if self.interstitial.isReady {
self.interstitial.delegate = self
self.interstitial.present(fromRootViewController: self)
} else {
print("Ad wasn't ready"
self.performSegue(withIdentifier: "signUpToTabbarVC", sender: nil)
}
}

ViewController loads but does not show

I'm using the Spotify iOS SDK. When a user logs into Spotify using the app, on call back loginVC transitions to musicPlayerVC. But, when a user logs into the app using a web view, once the web view dismisses and the loginVC is shown, the musicPlayerVC is loaded (print statements from viewDidLoad occur), but loginVC does not dismiss and musicPlayerVC does not show.
loginVC:
class loginVC: UIViewController, SPTStoreControllerDelegate, WebViewControllerDelegate {
#IBOutlet weak var statusLabel: UILabel!
var authViewController: UIViewController?
var firstLoad: Bool!
var Information: [String:String]?
override func viewDidLoad() {
super.viewDidLoad()
// NotificationCenter.default.addObserver(self, selector: #selector(self.sessionUpdatedNotification), name: NSNotification.Name(rawValue: "sessionUpdated"), object: nil)
self.statusLabel.text = ""
self.firstLoad = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.sessionUpdatedNotification), name: NSNotification.Name(rawValue: "sessionUpdated"), object: nil)
let auth = SPTAuth.defaultInstance()
// Uncomment to turn off native/SSO/flip-flop login flow
//auth.allowNativeLogin = NO;
// Check if we have a token at all
if auth!.session == nil {
self.statusLabel.text = ""
return
}
// Check if it's still valid
if auth!.session.isValid() && self.firstLoad {
// It's still valid, show the player.
print("View did load, still valid, showing player")
self.showPlayer()
return
}
// Oh noes, the token has expired, if we have a token refresh service set up, we'll call tat one.
self.statusLabel.text = "Token expired."
if auth!.hasTokenRefreshService {
self.renewTokenAndShowPlayer()
return
}
// Else, just show login dialog
}
override var prefersStatusBarHidden: Bool {
return true
}
func getAuthViewController(withURL url: URL) -> UIViewController {
let webView = WebViewController(url: url)
webView.delegate = self
return UINavigationController(rootViewController: webView)
}
func sessionUpdatedNotification(_ notification: Notification) {
self.statusLabel.text = ""
let auth = SPTAuth.defaultInstance()
self.presentedViewController?.dismiss(animated: true, completion: { _ in })
if auth!.session != nil && auth!.session.isValid() {
self.statusLabel.text = ""
print("Session updated, showing player")
self.showPlayer()
}
else {
self.statusLabel.text = "Login failed."
print("*** Failed to log in")
}
}
func showPlayer() {
self.firstLoad = false
self.statusLabel.text = "Logged in."
self.Information?["SpotifyUsername"] = SPTAuth.defaultInstance().session.canonicalUsername
OperationQueue.main.addOperation {
[weak self] in
self?.performSegue(withIdentifier: "ShowPlayer", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowPlayer" {
if let destination = segue.destination as? PlayController {
destination.Information = self.Information
}
}
}
internal func productViewControllerDidFinish(_ viewController: SPTStoreViewController) {
self.statusLabel.text = "App Store Dismissed."
viewController.dismiss(animated: true, completion: { _ in })
}
func openLoginPage() {
self.statusLabel.text = "Logging in..."
let auth = SPTAuth.defaultInstance()
if SPTAuth.supportsApplicationAuthentication() {
self.open(url: auth!.spotifyAppAuthenticationURL())
} else {
self.authViewController = self.getAuthViewController(withURL: SPTAuth.defaultInstance().spotifyWebAuthenticationURL())
self.definesPresentationContext = true
self.present(self.authViewController!, animated: true, completion: { _ in })
}
}
func open(url: URL) {
if #available(iOS 10, *) {
UIApplication.shared.open(url, options: [:],
completionHandler: {
(success) in
print("Open \(url): \(success)")
})
} else {
let success = UIApplication.shared.openURL(url)
print("Open \(url): \(success)")
}
}
func renewTokenAndShowPlayer() {
self.statusLabel.text = "Refreshing token..."
SPTAuth.defaultInstance().renewSession(SPTAuth.defaultInstance().session) { error, session in
SPTAuth.defaultInstance().session = session
if error != nil {
self.statusLabel.text = "Refreshing token failed."
print("*** Error renewing session: \(error)")
return
}
self.showPlayer()
}
}
func webViewControllerDidFinish(_ controller: WebViewController) {
// User tapped the close button. Treat as auth error
}
}
webController :
import UIKit
import WebKit
#objc protocol WebViewControllerDelegate {
func webViewControllerDidFinish(_ controller: WebViewController)
/*! #abstract Invoked when the initial URL load is complete.
#param success YES if loading completed successfully, NO if loading failed.
#discussion This method is invoked when SFSafariViewController completes the loading of the URL that you pass
to its initializer. It is not invoked for any subsequent page loads in the same SFSafariViewController instance.
*/
#objc optional func webViewController(_ controller: WebViewController, didCompleteInitialLoad didLoadSuccessfully: Bool)
}
class WebViewController: UIViewController, UIWebViewDelegate {
var loadComplete: Bool = false
var initialURL: URL!
var webView: UIWebView!
var delegate: WebViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
print(initialURL)
let initialRequest = URLRequest(url: self.initialURL)
self.webView = UIWebView(frame: self.view.bounds)
self.webView.delegate = self
self.webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(self.webView)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.done))
self.webView.loadRequest(initialRequest)
}
func done() {
self.delegate?.webViewControllerDidFinish(self)
self.presentingViewController?.dismiss(animated: true, completion: { _ in })
}
func webViewDidFinishLoad(_ webView: UIWebView) {
if !self.loadComplete {
delegate?.webViewController?(self, didCompleteInitialLoad: true)
self.loadComplete = true
}
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {
if !self.loadComplete {
delegate?.webViewController?(self, didCompleteInitialLoad: true)
self.loadComplete = true
}
}
init(url URL: URL) {
super.init(nibName: nil, bundle: nil)
self.initialURL = URL as URL!
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

Sending SMS in iOS with Swift

First of all, I'm really surprised that this is not a duplicate, because there are TONS of stackoverflow questions that solve this in Objective-C, but I have yet to see a good answer that used Swift.
What I'm looking for is a code snippet in Swift that sends an arbitrary string as a the body of a text message to given phone number. Essentially, I'd like something like this from Apple's official documentation, but in Swift instead of Objective-C.
I imagine this isn't too difficult, as it can be done in just a couple of lines of code in Android.
EDIT: What I'm looking for is 5-20 lines of Swift code, I do not agree that this is too broad. In Java (for Android), the solution looks like this:
package com.company.appname;
import android.app.Activity;
import android.telephony.SmsManager;
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public static final mPhoneNumber = "1111111111";
public static final mMessage = "hello phone";
SmsManager.getDefault().sendTextMessage(mPhoneNumber, null, mMessage, null, null);
}
}
Now this is the android solution, and it's only 11 lines. Java tends to be much more verbose than Swift, so I doubt what I'm asking is "too broad", it is more likely that I don't know how to use the Objective-C MessageComposer object, because the documentation that I linked to above is unclear with regard to usage in Swift.
Not sure if you really got the answer. I was in a similar hunt and came across this solution and got it to work.
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
#IBOutlet weak var phoneNumber: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sendText(sender: UIButton) {
if (MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = "Message Body"
controller.recipients = [phoneNumber.text]
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
//... handle sms screen actions
self.dismissViewControllerAnimated(true, completion: nil)
}
override func viewWillDisappear(animated: Bool) {
self.navigationController?.navigationBarHidden = false
}
}
Swift 3.0 Solution:
func sendSMSText(phoneNumber: String) {
if (MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = ""
controller.recipients = [phoneNumber]
controller.messageComposeDelegate = self
self.present(controller, animated: true, completion: nil)
}
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
//... handle sms screen actions
self.dismiss(animated: true, completion: nil)
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
}
For sending iMessage in Swift 5 I use following code
Just MessageUI package and implement MFMessageComposeViewControllerDelegate
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sendNewIMessage(_ sender: Any) {
let messageVC = MFMessageComposeViewController()
messageVC.body = "Enter a message details here";
messageVC.recipients = ["recipients_number_here"]
messageVC.messageComposeDelegate = self
self.present(messageVC, animated: true, completion: nil)
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch (result) {
case .cancelled:
print("Message was cancelled")
case .failed:
print("Message failed")
case .sent:
print("Message was sent")
default:
return
}
dismiss(animated: true, completion: nil)
}
}
Simpler solution may be opening html link:
let mPhoneNumber = "1111111111";
let mMessage = "hello%20phone";
if let url = URL(string: "sms://" + mPhoneNumber + "&body="+mMessage) {
UIApplication.shared.open(url)
}
Make sure you replaced spaces with "%20"
Swift 3, 4, 5
#IBAction func sendSmsClick(_ sender: AnyObject) {
guard MFMessageComposeViewController.canSendText() else {
return
}
let messageVC = MFMessageComposeViewController()
messageVC.body = "Enter a message";
messageVC.recipients = ["Enter tel-nr"]
messageVC.messageComposeDelegate = self;
self.present(messageVC, animated: false, completion: nil)
}
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResult.cancelled.rawValue:
print("Message was cancelled")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
print("Message failed")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
print("Message was sent")
self.dismiss(animated: true, completion: nil)
default:
break;
}
}
UI will look like:
If you do not want to depend on an UIViewController, follows a Swift 3.0 solution:
import UIKit
import MessageUI
class ECMMessageComposerBuilder: NSObject {
private dynamic var customWindow: UIWindow?
private var body: String?
private var phoneNumber: String?
fileprivate var messageController: MFMessageComposeViewController?
var canCompose: Bool {
return MFMessageComposeViewController.canSendText()
}
func body(_ body: String?) -> ECMMessageComposerBuilder {
self.body = body
return self
}
func phoneNumber(_ phone: String?) -> ECMMessageComposerBuilder {
self.phoneNumber = phone
return self
}
func build() -> UIViewController? {
guard canCompose else { return nil }
messageController = MFMessageComposeViewController()
messageController?.body = body
if let phone = phoneNumber {
messageController?.recipients = [phone]
}
messageController?.messageComposeDelegate = self
return messageController
}
func show() {
customWindow = UIWindow(frame: UIScreen.main.bounds)
customWindow?.rootViewController = MNViewController()
// Move it to the top
let topWindow = UIApplication.shared.windows.last
customWindow?.windowLevel = (topWindow?.windowLevel ?? 0) + 1
// and present it
customWindow?.makeKeyAndVisible()
if let messageController = build() {
customWindow?.rootViewController?.present(messageController, animated: true, completion: nil)
}
}
func hide(animated: Bool = true) {
messageController?.dismiss(animated: animated, completion: nil)
messageController = nil
customWindow?.isHidden = true
customWindow = nil
}
}
extension ECMMessageComposerBuilder: MFMessageComposeViewControllerDelegate {
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
hide()
}
}
You call the composer this way:
let phoneNumber = "987654321"
let composer = MNMessageComposerBuilder()
composer.phoneNumber(phoneNumber).show()
or using a lazy var
let phoneNumber = "987654321"
private lazy var messageComposer: MNMessageComposerBuilder = {
let composer = MNMessageComposerBuilder()
return composer
}()
messageComposer.phoneNumber(phoneNumber).show()
#IBAction func sendMessageBtnClicked(sender: AnyObject) {
var messageVC = MFMessageComposeViewController()
messageVC.body = "Enter a message";
messageVC.recipients = ["Enter tel-nr"]
messageVC.messageComposeDelegate = self;
self.presentViewController(messageVC, animated: false, completion: nil)
}
func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
switch (result.value) {
case MessageComposeResultCancelled.value:
println("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.value:
println("Message failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.value:
println("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}

Resources