Swift: Forward Keystrokes to a Different Process

Swift: forward keystrokes to a different process

One of the comments suggested that permissions might be the cause, and it was.

My problem was resolved by going to the Capabilities section of my project and disabling App Sandboxing, which was enabled by default.

Keep in mind that App Sandboxing is a requirement for your app to be on the App Store (reference)

Send keypress to specific window on Mac

Demo in swift, using

$ swift --version
Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)
Target: x86_64-apple-darwin17.7.0

simple example code sendspace.swift

import Foundation

let src = CGEventSource(stateID: CGEventSourceStateID.hidSystemState)

let kspd = CGEvent(keyboardEventSource: src, virtualKey: 0x31, keyDown: true) // space-down
let kspu = CGEvent(keyboardEventSource: src, virtualKey: 0x31, keyDown: false) // space-up

let pids = [ 24834, 24894, 24915 ]; // real PID-s from command 'ps -ax' - e.g. for example 3 different processes

for i in 0 ..< pids.count {
print("sending to pid: ", pids[i]);
kspd?.postToPid( pid_t(pids[i]) ); // convert int to pid_t
kspu?.postToPid( pid_t(pids[i]) );
}

After running the code: swift sendspace.swift (from the Terminal) all 3 processes with defined PIDs will get the space keypress, while the Terminal is still the foreground app.

Implement a Swift protocol by automatically mapping method calls to keys in a dictionary

That kind of dynamic handling of arbitrary messages at runtime can only be done in Objective-C. For one thing, calls to pure Swift methods don't even go through the Objective-C dynamic dispatch mechanism. So it would have to be an @objc method, and the forwardInvocation: logic will have to be implemented in Objective-C.

Send a keyboard shortcut to a Mac OS X Window

One way to do this is embedding Applescript in your Objective-C application.
For example executing this apple script, sends Command + M to System Events application:

tell application "System Events" to keystroke "m" using {command down}

You can embed the script above in your Cocoa application with something like this:

//AppControler.h
#import <Cocoa/Cocoa.h>

@interface AppController : NSObject {
NSAppleScript *key;
}
-(IBAction)sendkeys:(id)sender;
@end

//AppControler.m
#import "AppController.h"

@implementation AppController

-(IBAction)sendkeys:(id)sender
{
NSAppleScript *key = [[NSAppleScript alloc] initWithSource:@"tell application "System Events" to keystroke "m" using {command down}"];
[start executeAndReturnError:nil];
}

@end

distinguish between different instances or windows of a process and find their PID instead of their owner PID to send input event in macOS?

This activates each window in TextEdit and sends a space keystroke.

activate application "TextEdit"

tell application "System Events"
tell application process "TextEdit"
repeat with theWindow in windows
perform action "AXRaise" of theWindow
keystroke " "
end repeat
end tell
end tell

But to note how this would work without pressing keys, the following appends "A" to all the currently open documents without bringing anything to the foreground or interfering with the user's input.

tell application "TextEdit"
repeat with theDocument in documents
set text of theDocument to (text of theDocument) & "A"
end repeat
end tell

Whenever possible, you want things like this rather than sending keystrokes.

Swift map nested dictionary to swap outer and inner keys

A possible way is to use reduce(into:_:):

With sample:

let rates: [String: [String: Double]] = ["2021-11-14": ["CAD": 1.1,
"USD": 1.0,
"EUR": 0.9],
"2021-11-15": ["CAD": 1.11,
"USD": 1.01,
"EUR": 0.91]]

This should do the trick:

let target = rates.reduce(into: [String: [String: Double]]()) { partialResult, current in
let date = current.key
let dayRates = current.value
dayRates.forEach { aDayRate in
var currencyRates = partialResult[aDayRate.key, default: [:]]
currencyRates[date] = aDayRate.value
partialResult[aDayRate.key] = currencyRates
}
}

For the logic, we iterate over each elements of rates.
For each [CurrencyCode: Amount], we iterate on them, and set them to partialResult (which at the end of the inner loop of reduce(into:_:) will be the finalResult, ie the returned value).

Outputs for print(rates) & print(target) (I just formatted them to make them easier to read):

$> ["2021-11-14": ["CAD": 1.1, "EUR": 0.9, "USD": 1.0], 
"2021-11-15": ["USD": 1.01, "EUR": 0.91, "CAD": 1.11]]
$> ["EUR": ["2021-11-14": 0.9, "2021-11-15": 0.91],
"USD": ["2021-11-15": 1.01, "2021-11-14": 1.0],
"CAD": ["2021-11-14": 1.1, "2021-11-15": 1.11]]

GeoFire observeReady gets executed prematurely in Swift

What you're seeing is expected behavior. The observeReady is guaranteed to fire after all the corresponding observe(.keyEntered) have been called. You can verify this with some simple logging statements:

query.observe(.keyEntered) { (key: String!, venueLocation: CLLocation!) in 
print(".keyEntered")
}
query.observeReady {
print(".observeReady")
}

When you run this it will print:

.keyEntered

.keyEntered

...

.observeReady

That is in line with how the API is supposed to work. But in the .keyEntered you are loading additional data from Firebase, which happens asynchronously. And those calls may indeed complete after the .observeReady has fired.

So you will need to implement the necessary synchronization yourself. A simple way to detect if you have loaded all the additional data, is to keep a count of all the keys for which you still need to load data. So you +1 that every time you add a key, and -1 every time you've loaded the venue data:

let venuesToLoadCount = 0
query.observe(.keyEntered) { (key: String!, venueLocation: CLLocation!) in
venuesToLoadCount = venuesToLoadCount + 1
self.REF_VENUES.child(key).observeSingleEvent(of: .value, with: { (snapshot) in
venuesToLoadCount = venuesToLoadCount - 1
if venuesToLoadCount == 0 {
print("All done")
}
}
}
query.observeReady {
if venuesToLoadCount == 0 {
print("All done")
}
}


Related Topics



Leave a reply



Submit