I am trying to detect in my Swift app if an instance is already running, and if so, refrain the user from launching another instance. I am using NSRunningApplication class to detect such behavior but am having trouble getting it to work properly. I have read through some of the older posts and know that flock() is a lower-level way to detect so but I would prefer to "do it the Swift way" if possible. Here is a snippet of the code:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Check if app has already been launched. If so, alert user and terminate the new session
let bundleID = Bundle.main.bundleIdentifier!
if NSRunningApplication.runningApplications(withBundleIdentifier: bundleID).count > 1 {
// warn and terminate
NSApp.terminate(nil)
}
// keep going and finish launching the app
...
}
It appears that the count returned is always of value 1 regardless of how many sessions I am trying to open. Any idea what goes wrong, or am I not calling this check in the right place?
Additionally, my app also allows the user to double-click on a file with my defined file extension to launch the app. I assume that if I can get the above working then it should also work for this scenario as well?
UPDATE:
After reviewing the launch behavior of my app using Activity Monitor, I think I have determined the source of the observed behavior.
Full disclosure, my app creates a new process to launch a console app, and will wait until this process completes and terminates. Updated code snippets below:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Check if app has already been launched. If so, alert user and terminate the new session
let bundleID = Bundle.main.bundleIdentifier!
let num_instances = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID).count
// Debug dialog box to see instances count
let alert = NSAlert()
alert.messageText = "msg"
alert.informativeText = "num instances running: \(num_instances)"
alert.alertStyle = NSAlert.Style.critical
alert.addButton(withTitle: "OK")
alert.runModal()
if num_instances > 1 {
// warn and terminate
NSApp.terminate(nil)
}
// keep going and finish launching the app
let task = Process()
task.launchPath = "myConsoleApp"
task.launch()
task.waitUntilExit()
let status = task.terminationStatus
if status == 0 {
// Done with the console app, exit
...
} else {
// something's wrong; report to user
...
}
}
Now, when the app is first launched, there are two processes created initially: mySwiftApp and myConsoleApp. However, with the presence of the debug dialog box, the mySwiftApp process is terminated after myConsoleApp has launched. As a result, the instance count for mySwiftApp is now 0. Therefore, subsequent launch of mySwiftApp will be successful and now there are two myConsoleApp processes running on the system. It is not clear to me why the presence of the alert box will terminate the mySwiftApp process but I suspect that it sends a termination signal to my app and changes its termination status.
If the debug dialog box is not there, due to the waitUnitlExit() setup, any attempt to launch the second instance will not be seen and essentially got dropped to the floor. Although this is the "desired" behavior since the second instance cannot be launched, it will be nice to have a way to notify the user.
Any idea what's the best way to do so?
Related
I want to execute something in the background the check data every one hour.
For this, I am following the Apple Documentation video.
Here is the App Delegate code
import SwiftUI
import UserNotifications
import BackgroundTasks
#main
struct notificationtestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear(perform: {
AppCheckData()
})
}.backgroundTask(.appRefresh("notifyuser")) {
print("In Background Task")
//Check data
}
}
}
func AppCheckData() {
var startDate = Date()
let calendar = Calendar.autoupdatingCurrent
var checkTime = calendar.date(byAdding: .minute, value: 2, to: startDate)!
print(checkTime)
let notifyrequest = BGAppRefreshTaskRequest(identifier: "notifyuser")
notifyrequest.earliestBeginDate = checkTime
try? BGTaskScheduler.shared.submit(notifyrequest)
print("Done Scheduling")
}
For testing purposes, I changed addition of time to 2 minutes.
According to this code, it should print out the date after adding 2 minutes to the current date, then print out a confirmation that the BGTaskScheduler had been submitted. From there, after 2 minutes, it should print out the "In background task" in the console.
However, my app is not receiving the scheduled background task. I am not sure what I am missing here, is it the limits that Apple creates, or something else.
Please help
A BGAppRefreshTaskRequest is a request, and it is scheduled with earliestBeginDate. You are not requesting a specific time to run. You are indicating the earliest time it makes sense to run so that the OS doesn't call this unnecessarily.
As the docs note (emphasis mine):
The system decides the best time to launch your background task, and provides your app up to 30 seconds of background runtime.
In order to test your background operations, see Starting and Terminating Tasks During Development. IMO it is quite awkward, but it does work.
I want to execute something in the background the check data every one hour.
You will need to send a push notification in that case. This is not possible with BGTaskScheduler. As a rule, however, you should avoid this kind of polling and rather send the push notification only when there is new information. The point of BGAppRefreshTask is to periodically update your app's information at some point soon before the user is anticipated to need it so the user doesn't have to wait. It is not to facilitate polling.
There is no mechanism in iOS to allow you to do something at specific time intervals, without the user's intervention, except silent push notifications (and there are limits on those). This is by design. If you do not have a system for sending push notifications, then this is impossible, and you will need to redesign to not require it.
my application is a listener service that is normally idle, waiting for incoming messages over the network, or for instructions from the user via the Dock icon menus.
however something keeps closing or hiding the Dock icon, unless i keep a window open.
i am working in XCode Swift, with Cocoa with Storyboards, and am developing for MacOS 10.13
i started a new project as above, and edited AppDelegate.swift as follows:
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
print( "app did finish launching" )
// Insert code here to initialize your application
for window in NSApp.windows {
print( window.debugDescription )
window.close()
}
DispatchQueue.global(qos: .userInitiated).async {
var seconds = 0
while true {
sleep( 1 )
print( ( seconds % 2 ) == 0 ? "tick" : "tock", seconds )
seconds += 1
}
}
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
print( "app should terminate after last window - set regular policy" )
sender.setActivationPolicy( .regular )
return false
}
}
with no other changes, the application builds and runs, closing one window, however;
1 - the dock icon vanishes as soon as the application loses focus.
2 - after about sixty seconds the application slows down, and the sleep call is taking much longer than 1 second to return.
i need to prevent both of these behaviours, from which two questions follow:
a - where does Apple document these behaviours?
b - what is the correct way to control these behaviours?
in my experience it seems that most MacOS apps stay open in the dock, even though all of their windows are closed. for example Finder, Terminal, Safari, Notes, etc, etc.
what am i not understanding?
edited - i found this answer a week ago
Mac app disappears when window is closed then another app is selected
and i added both of parameters to my application Info, however it would seem i made an error, because i checked again today, and found they were both set to YES.
setting them both to NO appears to answer my question, my application is now working properly, and i will continue to test.
thank you, very much appreciated.
I am constructing a xcallBack Url like this
"\(scheme)://x-callback-url\(path)?x-source=ApName&ResponseData=\(base64str)"
and after constructing i had open url method
UIApplication.shared.open(callbackUrlN!) { (result) in
if result {
print("success")
}
else {
print("cannot open url")
}
}
when my application is foreground state it is working fine, but when it enters into the background state instead of printing success i am getting "cannot open Url", Eventhough i had tried dispatch global background thread it is not working
Does anybody could help me out on this?
I suppose that you are trying to call this acton with or without time interval after app goes to background, but iOS lifecycle make impossible to interact with app. If i'm not mistaken system gives short amount of time to finish your tasks, when your app goes to background, but you can extend your application lifetime and finish all the stuff using backgroundTask (system gives you about 3 min and then kill it), but it's still wouldn't help you to use callback.
I'm new at Swift 3 and I try to make a print("Test") in a Widget extension.
I tried the same code in ViewController.swift and It works ok. I don't know why it works there but it doesn't on TodayViewController.swift. I can't access to objects too.
func loadData() {
let query = PFQuery(className: "Noticias")
query.whereKey("titulo", equalTo:"Es Navidad")
query.findObjectsInBackground(block: { (objects : [PFObject]?, error: Error?) -> Void in
if error == nil {
// The find succeeded.
print("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects {
for object in objects {
print(object.objectId!)
}
}
} else {
// Log details of the failure
print("bad day homie")
print(error!)
}
})
}
I attach I picture to see it clearly. If I try to print on the file marked as Work, it works. But if I try it on the file marked ad NO, it doesn't.
It is extremely difficult to retrieve print messages from an extension. The problem is that it's an extension! It isn't running in your app, so it doesn't arrive at your console. Sometimes I find you can solve this problem by switching the debugged process in the Debug Bar at the top of the debug area (at the bottom of the screen, not shown in your screen shot), but at other times this doesn't work.
I'll illustrate a possible technique that seems to be pretty reliable. Look at this screen shot:
"Expand" is an action extension. But my containing app is called "bk2ch13...". So how will I ever manage to pause at the breakpoint shown at the right, which is in the action extension? This is what I do.
First, with the screen as shown above, I build and run my containing app.
Then, I switch the target to the action extension:
Now I build and run again. But now I am trying to run an action extension, which you can't do, so Xcode asks me what app to run:
So I choose "bk2ch13...". So now we are running my host app again, but we are debugging the extension. So I use my host app to exercise the extension, and sure enough, we pause at the breakpoints and print statements arrive into the console.
Note, in that screen shot, how the debug bar clearly shows that we are talking to the extension, not the host app.
Starting with a blank OS X application project, I add the following code to applicationDidFinishLaunching.
func applicationDidFinishLaunching(aNotification: NSNotification) {
let app = NSApplication.sharedApplication()
guard let window = app.keyWindow else {
fatalError("No keyWindow\n")
}
print(window)
}
At launch I hit the error case because my local window variable is nil. Yet when I show the contents of the app variable, I see a valid value for _keyWindow. Also notice that the blank GUI Window is being displayed on the screen next to the stack dump.
Why does the keyWindow: NSWindow? property return nil in this case?
Thanks
NSApplication's keyWindow property will be nil whenever the app is not active, since no window is focused for keyboard events. You can't rely on it being active when it finished launching because the user may have activated some other app between when they launched your and when it finished launching, and Cocoa is designed to not steal focus.
To some extent, you may be seeing that happen more when launching from Xcode because app activation is a bit strange in that case. But, still, you must not write your applicationDidFinishLaunching() method to assume that your app is active.
What you're seeing in terms of the app's _keyWindow instance variable is, of course, an implementation detail. One can't be certain about what it signifies and you definitely should not rely on it. However, I believe it's basically the app's "latent" key window. It's the window that will be made key again when the app is activated (unless it is activated by clicking on another of its windows).