Piping NSEventMask together in 'addLocalMonitorForEventsMatchingMask' - swift

I have the following code for listening for mouse events outside the view (subclass of NSView). This works great as it is, but I cannot figure out how to pipe the NSEventMasks together. I basically want to have the same event listener fire on NSEventMask.LeftMouseDownMask, NSEventMask.RightMouseDownMask and NSEventMask.OtherMouseDownMask.
Does anyone know how to pipe them together or if it's even possible in Swift?
Working code
localMouseEventListener = NSEvent.addLocalMonitorForEventsMatchingMask(NSEventMask.LeftMouseDownMask) { (event: NSEvent) -> NSEvent? in
// Event handling...
return event
}

This answer might help you.
In short you can now use arrays, so this seems to make the compiler happy:
localMouseEventListener = NSEvent.addLocalMonitorForEventsMatchingMask([.LeftMouseDownMask, .RightMouseDownMask, .OtherMouseDownMask]) { (event: NSEvent) -> NSEvent? in
// Event handling...
return event
}

Related

Swift Combine publishers vs completion handler and when to cancel

I know in general a publisher is more powerful than a closure, however I want to ask and discuss a specific example:
func getNotificationSettingsPublisher() -> AnyPublisher<UNNotificationSettings, Never> {
let notificationSettingsFuture = Future<UNNotificationSettings, Never> { (promise) in
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
promise(.success(settings))
}
}
return notificationSettingsFuture.eraseToAnyPublisher()
}
I think this is a valid example of a Future publisher and it could be used here instead of using a completion handler. Let's do something with it:
func test() {
getNotificationSettingsPublisher().sink { (notificationSettings) in
// Do something here
}
}
This works, however it will tell me that the result of sink (AnyCancellable) is unused. So whenever I try to get a value, I need to either store the cancellable or assign it until I get a value.
Is there something like sinkOnce or an auto destroy of cancellables? Sometimes I don't need tasks to the cancelled. I could however do this:
func test() {
self.cancellable = getNotificationSettingsPublisher().sink { [weak self] (notificationSettings) in
self?.cancellable?.cancel()
self?.cancellable = nil
}
}
So once I receive a value, I cancel the subscription. (I could do the same in the completion closure of sink I guess).
What's the correct way of doing so? Because if I use a closure, it will be called as many times as the function is called, and if it is called only once, then I don't need to cancel anything.
Would you say normal completion handlers could be replaced by Combine and if so, how would you handle receiving one value and then cancelling?
Last but not least, the completion is called, do I still need to cancel the subscription? I at least need to update the cancellable and set it to nil right? I assume storing subscriptions in a set is for long running subscriptions, but what about single value subscriptions?
Thanks
Instead of using the .sink operator, you can use the Sink subscriber directly. That way you don't receive an AnyCancellable that you need to save. When the publisher completes the subscription, Combine cleans everything up.
func test() {
getNotificationSettingsPublisher()
.subscribe(Subscribers.Sink(
receiveCompletion: { _ in },
receiveValue: ({
print("value: \($0)")
})
))
}

Check for certain statements in Swift closure

I wrote a function which takes a closure as an argument like this:
func doSome(work: () -> Void = { print("sleeping...") } ) {
work()
}
Now I would like to investigate the work done.
Therefore I want to check if the given closure contains any print statements.
Somehow like this:
func doSome(work: () -> Void = { print("doing hard work...") } ) {
work()
if work.contains(print) {
print("we did some hard work there and printed something!")
}
}
How can I achieve that?
EDIT: What I am trying to achieve
An async function tries to connect to an http server - let's call it connect. It takes a closure as its parameter - called finally. As its name already indicates: the closure gets executed after the connecting attempt.
If the connecting attempt succeeds (http response code == 200), I need to call another function ONCE - let's call it so: once.
The connect function therefore looks like this:
func connect(finally: () -> Void = {}) {
httpRepsonse = asyncRequestToServer()
if httpResponse.statusCode == 200 {
once()
}
// and finally:
finally()
}
Other functions call connect and pass over their statements that they need for the connect function to execute finally.
And here comes the problem: there is one function that needs once executed every time, therefore it passes it over in the finally closure. If the connecting now succeeds, once gets called twice.
That's why I wanted to check the given closure already contains the once call, so I could avoid calling it twice.
Interrogating a closure for its contents is not easily done as far as I know.
You could do a workaround (depending on your needs and implementation of course) using one or more Boolean arguments which you would assign when calling the function, if relevant.
For example:
func doSome(work: () -> Void = { print("doing hard work...")}, containsPrint: Bool = false) {
// Call your work closure
work()
// Check conditions
if containsPrint {
print("We printed some stuff")
}
}
I am aware that this is a rather simple solution but it might provide the required functionality.
Use a global variable that you change whenever you print to the console, and check it inside you doSome(work:)
Short answer: You can't. As Alexander says, Swift does not support this. You would have to add some sort of housekeeping, as suggested in Carpsen90's answer.

Swift: forward keystrokes to a different process

I am trying to write an application that forwards the keypresses on one of its TextFields to a different process using its processID.
The code (below) that I have got works well in terms of listening to the key presses (right now, all key presses), but the trouble is coming from forwarding the events (keyDown/keyUp) to the other process.
Things I have tried:
CGEvent.postToPid: Does not work (nothing sent to the other process)
CGEvent.postToPSN: Could not get it to work as it requires the Process Serial Number as an UnsafeMutableRawPointer (not that I know how to get the PSN is in the first place)
Manually creating the CGEvent and then trying 1. and 2.
Reading documentation on the classes and their functions (they don't seem to be that popular)
Any help would be greatly appreciated.
import Cocoa
class ViewController: NSViewController {
var pid: Int32 = 12345
override func viewDidLoad() {
super.viewDidLoad()
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
(event) -> NSEvent? in
self.keyDown(with: event)
return event
}
// For some reason, keyUp is triggered twice
NSEvent.addLocalMonitorForEvents(matching: .keyUp) {
(event) -> NSEvent? in
self.keyUp(with: event)
return event
}
}
override func keyDown(with event: NSEvent) {
print("Down " + String(event.keyCode))
var cgEvent = event.cgEvent
cgEvent?.postToPid(pid)
}
override func keyUp(with event: NSEvent) {
print("Up " + String(event.keyCode))
var cgEvent = event.cgEvent
cgEvent?.postToPid(pid)
}
}
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)

How to use multithread in Swift

I have two tasks : task1 and task2. I want to execute task2 after task1 finishes.
let globalQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
dispatch_sync(globalQueueDefault){
self.activityIndicatorView.hidden = false
self.activityIndicatorView.startAnimating()
task1()
sleep(6)
dispatch_sync(globalQueueDefault) { () -> Void in
task2()
}
}
I searched in internet, I find NSLock,NSConditionLock and objc_sync_enter...I have try them, but it doesn't work...
let lock = NSLock()
let globalQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
dispatch_sync(globalQueueDefault){
self.activityIndicatorView.hidden = false
self.activityIndicatorView.startAnimating()
self.lock.lock()
task1()
self.lock.unlock()
sleep(6)
dispatch_sync(globalQueueDefault) { () -> Void in
self.lock.lock()
task2()
self.lock.unlock()
}
}
I also tried NSConditionLock and objc_sync_enter...It doesn't work. How I can use lock in swift ? Could you give me a example base on my code? Thank you.
PS: I don't want to use callback here...because I have tried it, I think multithread is more closer to my answer, Thank you.
I'm going out on a limp and making some guesses about your program structures. The first problem with your code is that it's trying to access a view on a background thread. GUI elements should always be accessed on the main thread. The second problem is sleep: don't use it to write concurrent code. It makes assumptions about how the asynchronous task is going take. You should treat that time as unknown and use a sync pattern or a call back.
Since you mentioned that task1() download JSON, it's likely asynchronous. Here's how I'd do it:
func task1(finish: () -> Void) {
// Set up your connection to the website
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
// Handle the response, parse the json, etc
...
// Now call the completion handler
finish()
}
}
func task2() {
// Do whatever here
}
// In the function that triggers the JSON download
func downloadJSON() {
self.activityIndicatorView.hidden = false
self.activityIndicatorView.startAnimating()
task1(task2)
}

Cocos2d, swift find when Spritebuilder timeline animation completed via callback

I have a callback setup on timeline in Spritebuilder.
and I have the timeline animation triggered. My goal is to disable touch until animation completes.
func triggerTimelineAction(nodeName: CCNode){
self.userInteractionEnabled = false;
nodeName.animationManager.runAnimationsForSequenceNamed("nodeMainAction")
nodeName.animationManager.setCompletedAnimationCallbackBlock(b: ??? )){
self.userInteractionEnabled = true;
}
}
Question: how do I declare callback keyframe and hookup/receive it.
UPD: i was told to pass a function in setCompletedAnim... so i made my code look like:
func animationCompleted(){
self.userInteractionEnabled = true;
}
func triggerTimelineAction(nodeName: CCNode){
self.userInteractionEnabled = false;
nodeName.animationManager.runAnimationsForSequenceNamed("nodeMainAction")
nodeName.animationManager.setCompletedAnimationCallbackBlock(b: #selector(animationCompleted))
}
on which I received error "Cannot convert value of type ()->() to expected argument type (AnyObject)->Void
Apparently this is more related to Cocos2d syntaxis. will dig further in that direction
So after a session with codementor I figured out that I was using the setCompletedAnimationCallbackBlock function improperly.
In this code
activeNode.animationManager.setCompletedAnimationCallbackBlock(b: ((AnyObject!) -> Void)
I was trying to pass arguments as AnyObject
when in fact this is a block that this function returns.
So this code has worked for me
activeNode.animationManager.setCompletedAnimationCallbackBlock { (object: AnyObject!) -> Void in
print(object)
}
where object is just firs time declared name for element that gets returned. The print function showed that object that returns is a CCAnimationManager so it is equal to previous syntax when you had to runAction then assign action.delegate to self and then actually catch callback.
Hope this information helps somebody.