swift write to screen from delegate - swift

I've only been working with Swift for the last month please forgive me if I missed something everyone should know. I have been tasked with creating an app that will receive an APN (Apple Push Notification) containing a the document number of a sales order that requires approval. The app will display details about the sales order and present the user with an accept or reject option. I built a proof of concept that works perfectly with the APN. When I add the APN functionality I create an instance of the ViewController and use it to get the order details. I stored the order details in a global variable that is available to both classes (delegate and ViewController). Everything works great until I try to write order details to the screen. It fails with:
"fatal error: unexpectedly found nil while unwrapping an Optional
value"
In AppDelegate.swift I set
var vc = ViewController.self
In func
application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler:(UIBackgroundFetchResult) -> Void)
I process the APN and call
vc.init().LogonServiceLayer()
Everything works great until it tries to write to the screen. I try to write to CardNameLabel.text. I have CardNameLabel defined as
#IBOutlet weak var CardNameLabel: UILabel!
I'm certain that I must create some sort of the an initialization function/constructor for my class. I've tried something like:
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)!
// Your intializations
CardNameLabel.text = String ()
}
required init() {
super.init(nibName: nil, bundle: nil)
//Do whatever you want here
CardNameLabel.text = String ()
}
This will compile but now it fails in the constructor instead of when I am trying to update the label on the screen. Please forgive me if I'm missing something really obvious but so far I haven't found a way to update the labels on the screen using the instance of my class created in the delegate module.
What am I missing?
Thank you,
Duncan

Related

How do you detect if an AppKit app has been launched with a document?

I am building an NSDocument-based app and I want to be able to detect if my app is being launched because a user is opening a document, or because the user simply clicked on the dock icon. I tried inspecting the Notification that comes with the applicationDidFinishLaunching method, but it seems not to contain a reference to the document. I also tried querying NSDocumentController.shared.documents from this method, but the array is still empty at this point. I have noticed that by the time applicationDidBecomeActive is called, the document has been instantiated, but of course, this method is also called on occasions not related to app launch, so that's not ideal. My best guess at the moment is to do the following, but it feels like a hack.
class AppDelegate: NSObject, NSApplicationDelegate {
private var hasBecomeActiveOnce = false
func applicationDidBecomeActive(_ notification: Notification) {
if !hasBecomeActiveOnce {
hasBecomeActiveOnce = true
if !NSDocumentController.shared.documents.isEmpty {
print("App launched from document!")
}
}
}
}
There must be a better (more idiomatic) way. What am I missing?
Okay, I think I've found the answer to my specific problem, which might not be fully communicated by the question above. Basically, what I want to do is show an open panel when the app is launched, or when the dock icon is clicked and there isn't a document already open. It seems like in both of these cases, the system calls applicationOpenUntitledFile. Answering my own question in case others are running into this same issue.
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationOpenUntitledFile(_ sender: NSApplication) -> Bool {
print("app launched (or dock clicked) without a document!")
return true
}
}

UIAlertView is deprecated and unavailable for UIScene based applications, please use UIAlertController

I'm new to Swift and I'm getting this Error.
I don't get it because I don't use any type of alert in my code.
Here is my ViewController:
import UIKit
import WebKit
class ViewController: UIViewController{
#IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://www.google.com/maps")!
webView.load(URLRequest(url: url))
}
}
I only want to use WKWebView in my app and use the location.
I already added NSLocationAlwaysAndWhenInUseUsageDescription to the info.plist
You can reproduce this error if you using iOS 13, Xcode and this example and any website which uses the location like google maps.
I was also going through the same issue, i'm able to solve it getting help from a developer, scene base application that is new yet not support ui alert view so it crashes the app, we can remove the scene base application we can use window base app, follow these steps to resolve the issue:
First remove the SceneDelegate file
check this image for first step
Second remove UISceneSession Lifecycle two methods from AppDelegate file
check this image for second step
Third go to your info plist and remove Application Scene Manifest Entry from it
check this image for third step
Go ahead and build the project, run it now, make sure you have window property in AppDelegate

How to get back to the current window from AppDelegate

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
}

activity Indicator as global property

I am still very new to programming and sometimes it bites me with very basic concepts.
I have an activity indicator defined in my tableviewcontroller as an Outlet.
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
The data download to fill the tableview with data is done in a separate file in a class wiht download functions. These functions include the completion handler for the download. Now, if I want to insert the
activityIndicator.stopAnimating()
in the completion part then I get the message "use of unresolved identifier activityIndicator". How can I make the acitivityIndicator a global property, respectively, how can I make the download class/functions recognise the activityIndicator which is defined in the tableViewController? I know this is probably a stupid question for most of you, but I just don't know how to resolve this.
Ideally you don't want the download code to "know" about the activityIndicator. When your viewController calls the download, you could pass another completion handler. Then when the download completion handler runs, call this new completion handler. The viewController knows about the activityIndicator, so it can then stop it. Something (very roughly) along the lines of:
// In ViewController
myThing.doTheDownload(completion: {
dispatch_async(dispatch_get_main_queue(), {
self.activityIndicator.stopAnimating()
})
})
// In download code
func doTheDownload(completion completionHandler: (() -> Void)) {
download(completion: {
completionhandler()
})
}
Note that activityIndicator is a UI element, and therefore its code must run on the main thread.

Refresh Today Widget every time opened

I thought that Today View every time when i open it it calls "viewWillAppear" but its not. When i change something in my app, and then I slide down for Today View it sometimes refresh the view and sometimes not.
I do all logic in viewWillAppear (fetch data from coreData and put that data to labels), but its not called everytime.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
fetchContent()
setLabels()
setContentHeight()
tableView.reloadData()
print("view will appear")
}
how to call fetchContent and setLabels every time when user opens Today Extensions?
For this matter you should be using NSWidgetProvinding's widgetPerformUpdateWithCompletionHandler.
Steps:
1.- Make sure that your UIViewController implements NCWidgetProviding
class MainViewController: UIViewController, NCWidgetProviding
2.- Add the following function:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.NewData)
}
3.- In your case you will be using .NewData.
Just make sure that you retrieve needed data and refresh your views (putting every data in place, filling labels, graphs, etc).
Nevermind the fact that your view is not visible during the call to this function, iOS will fill the view and take a snapshot of it.
Then that's what it shows while you are opening notification center and until you gain control again of your app.
So, in your case would be something like this:
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
fetchContent()
setLabels()
setContentHeight()
tableView.reloadData()
completionHandler(NCUpdateResult.NewData)
}
Swift 2.1 && Xcode 7.2
It looks like some bug appears when you many time recompile this today extension. solution is to remove from notification center and add it again. then it refresh fine everytime opened