How to get back to the current window from AppDelegate - swift

In my macOS application, I am following a OAuth-Login procedure.
I am authenticating successfully by receiving a code within a url through my custom url, with which I already can get hold of an access_token.
I start the login procedure with the simple click of a button in a ViewController.
The system I log in to then answers with my registered custom url scheme, providing me with a code I have to then use to get an access_token with POST request. This access_token than can be used to make api calls.
My Problem now is that lacking knowledge of how to implement other techniques, I am currently doing all the latter procedure within the AppDelegate function application(_ application: NSApplication, open urls: [URL]).
From my limited understanding this is the way to handle this, but now
how can I get back from there to get hold of the current view? I am really struggling with the view controller life cycle part of things here I guess...

In AppDelegate, you can get a reference to the current window's ViewController as follows. Replace "MainViewController" with the name of the one you use.
iOS Swift:
if let vc = window?.rootViewController as? MainViewController {
// use `vc` here by calling public functions
// and accessing public properties
}
macOS Swift:
if let vc = NSApplication.shared.mainWindow?.contentViewController as? MainViewController {
// use `vc` here by calling public functions
// and accessing public properties
}

OK, found it: since there is no rootViewController in macOS-land as there is with iOS, it works a little different:
in macOS you can get hold of the window currently "under keyboard" like so:
in application(_:open:), where the redirect_uri gets called:
if let viewController = NSApplication.shared.keyWindow?.contentViewController as? ViewController {
// make changes to the view
}

Related

How to get NSView from SwiftUI2 App entry

I'm migrating my macOS app built with SwiftUI 1 to SwiftUI 2. There is a feature that allows user to save the main view as pdf using NSPrinterOperation, which takes an NSView instance.
Currently, the app is on SwiftUI 1 with AppDelegate, and I can get the main view through this code
Button("Print") {
let appDelegate = NSApp.delegate as! AppDelegate
let view = appDelegate.window.contentView! // returns the NSHostingView set up in AppDelegate.swift
...
NSPrintOperation(view: view, printInfo: newInfo).run()
}
Since upgrading to SwiftUI2, the AppDelegate is no longer there, and how can I get the view now?
See my comment for reference how to set app delegate (which you will be able then access), however also you can content view directly from window via NSApp, like
NSApp.mainWindow?.contentView
or
NSApp.keyWindow?.contentView
depending on your windows configuration.

Swift Stripe Access DidCreateToken

I'm trying to integrate Stripe into my ios (Swift) app by using Firebase-Cloud-Functions. Now I'm want to get the token to a created card to save it into my Firestore-Database.
I've followed this tutorial on how to implement it. It's working when I'm displaying my addCardViewController on my own since I have the method didCreateToken. But now that I'm just showing it programmatically like shown in their provided example (row 158 is where they're showing the view controller) I don't know how to implement this method and get the token of the card if a user creates/adds a new one.
Thats what Id normally do:
func addCardViewController(_ addCardViewController: STPAddCardViewController, didCreateToken token: STPToken, completion: #escaping STPErrorBlock)
{
STRIPE_CUSTOMERS_REF.document(userId).collection("tokens").addDocument(data: ["token": tokenId]) // Calls Firebase-Cloud-Function and adds payment method to Stripe
navigationController?.popViewController(animated: true)
}
But like I said Im not able to implement this method.
I want to get the token when a user adds a new card.
Id really appreciates any kind of help. If you need any additional information let me know.
-Marie
It looks like you using STPPaymentContext (Standard Integration) to present the STPPaymentMethodsViewController (line 158 in the example above).
STPPaymentContext actually implements its own instances of STPPaymentMethodsViewController and STPAddCardViewController. Therefore, STPPaymentContext handles the delegate methods for those 2 view controllers and those aren't exposed to the user when using the Standard Integration. Which explains why that delegate method is not triggering for you.
Instead, your view controller should become the delegate for STPPaymentContext and implement the all the required delegate methods [0], including paymentContextDidChange method.
paymentContextDidChange method is triggered whenever a user adds a new card or selects a new payment method [1].
When a user enters new card details, you should be able to get the token ID with the following:
func paymentContextDidChange(_ paymentContext: STPPaymentContext)
{
if let card = paymentContext.selectedPaymentMethod as? STPCard {
let token = card.stripeID
// store token as required
}
}
Hope that helps!
[0] https://stripe.github.io/stripe-ios/docs/Protocols/STPPaymentContextDelegate.html
[1] https://stripe.github.io/stripe-ios/docs/Protocols/STPPaymentMethod.html

Trigger In-App-Browser after tapping on Annotation with PDFKit - iOS 11

I´m working on an app with Apple's PDFKit and have managed to put some Annotation-Buttons with a working PDFActionURL, which open the iOS standard browser after tapping on them.
Unfortunately, I did not find a working solution, how to open the associated links in an In-App-Browser or to load it in a webview. After tapping the AnnotationButton PDFKit automatically opens Safari and I haven´t found a property or another way concerning iOS to influence this behavior by manipulating f.e. the PDFAction.
let urlString = "https://www.apple.com"
let urlAction = PDFActionURL(url: urlString)
urlButton.action = urlAction
pdfPage.addAnnotation(urlButton)
Is there a way to force the call of an In-App-Browser at every UIApplication.shared.open() or to manipulate the execution of a PDFAction?
Finally, I figured it out for myself ;) I´ve overlooked one function of the PDFViewDelegate
By setting the delegate of the pdfView to the involved ViewController and using this function
func pdfViewWillClick(onLink sender: PDFView, with url: URL) {
print(url)
}
you are able to use the associated URL of the PDFActionButton and to trigger an In-App-Browser (e.g. SFViewController).
You want to instantiate the view like so:
let webV:UIWebView = UIWebView(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
webV.loadRequest(NSURLRequest(URL: NSURL(string: "http://www.google.co.in")))
webV.delegate = self;
self.view.addSubview(webV)
Found HERE
To do it right, I would create a new ViewController in your storyboard, nested in a navigationController and segue to the view on annotation selection. Display your content on viewDidLoad using a similar set of code as seen above. That code is probably in an older version of swift so you'll need to update it. There is more detail about using the delegate associated in the link provided.

How to programmatically show a window/view controller in Swift 4 for macOS application

I'm trying to programmatically show a window in my macOS application. The reason I want to do it programmatically is because the user clicks a Login button and the resulting function depends on the success of the login. If it was successful, the window is shown; otherwise, it is not.
Here is a screenshot of my Xcode window:
Here's what I'm trying in the code (Alert is a class I created to make showing NSAlert dialogs easier):
#IBAction func btnLogin_Click(_ sender: Any) {
let email = txtEmail.stringValue
if(email.isEmpty) {
Alert.show(parent: view.window!, message: "Email is required.")
return
}
let password = txtPassword.stringValue
if(password.isEmpty) {
Alert.show(parent: view.window!, message: "Password is required.")
return
}
// this does not work, even though I gave the window and view controllers
// both identifiers in the storyboard
self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("wcStores"))
}
Apple's documentation for NSStoryboard.SceneIdentifier is pretty much nonexistent, and other methods I've seen seem to pertain to earlier versions of Swift.
What am I doing wrong?
Alright, so this is not what I originally was trying to do, but this way is much better, and what I was really looking for.
let vcStores = self.storyboard?.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("vcStores"))
as! NSViewController
self.view.window?.contentViewController = vcStores
This will just replace the window's contents with the contents in the view vcStores as defined in Interface Builder.
This video also helped, albeit for iOS and Swift 3. I was trying to create a new NSWindow at first because I'm so used to doing desktop development in Java Swing or C# Windows Forms. But I love how easy it is to just switch out the window contents like this.

Need to use a NSTextView (or NSTextField) for a clickable URL

I'm given an arbitrary NSAttributedString (parsed from markdown, not that it matters here) which may contain URLs that I want to be clickable in a text field within an NSTableView cell. The requirements state that if the user clicks the URL, they be taken to it with the default browser. IF they click anywhere else in the cell, we have default behavior (displaying an additional info popup).
I'm attempting to use a NSTextView to display the content. However, clicking outside the URL but within the view selects the text and eats the mouse click. Making the view not selectable won't allow clicking the URL either. I also don't really want the text to be selectable but that's a minor side problem.
So... I decided to make my view controller an NSTextViewDelegate so I could use some of those callbacks. But my app crashes if I set the NSTextView's delegate property to 'self'. This happens even if I don't implement any of the functions, even though they are all optional.
I'm using Swift 3 and wonder if there's some bug or other issue there? The call stack appears to be sending a textView:willChangeSelectionFromCharacterRanges:toCharacterRanges: message even though it's not implemented. And incidentally, implementing that method isn't helping either.
Any help, or sample code in Swift 3 using the delegate protocol, would be greatly appreciated.
Here's the crash I get by simply setting the delegate property on NSTextView:
By request, here's the code that set's the delegate. Currently I just set it whenever the message changes. This can obviously be optimized but for now I just want to see it work.
var notification: SSNotification! {
didSet {
guard let notificationCellView = self.view as? SSNotificationCellView else { return }
notificationCellView.subjectLabel.stringValue = notification.subject
if let description = notification.message , description != "" {
let attrString = TSMarkdownParser.standard().attributedString(fromMarkdown: description)
notificationCellView.messageLabel.textStorage?.setAttributedString(attrString)
notificationCellView.messageLabel.isHidden = false
notificationCellView.messageLabel.delegate = self
} else {
notificationCellView.messageLabel.isHidden = true
}
}
}
I never did figure out why I was crashing but I was able to come up with a workaround. I was originally trying to make the view controller for the table cell which contained NSTextView be the delegate. I changed it so that the cell's view subclass itself was the delegate and all is well.
I don't get it but it works, so that's what matters.