How to use AXObserver in Swift - swift

I tried to get notified when another Application creates a window using the Accessibility API and AXObserver. Here's my code:
let pid = NSWorkspace.shared.frontmostApplication!.processIdentifier
var observer: AXObserver?
if AXObserverCreate(pid, { (observer, element, notification, userData) in
// Does this get executed when a notification comes in?
print(notification)
}, &observer) == .success {
// This does get printed!
print("Successfully created Observer!")
}
if AXObserverAddNotification(observer!, element, notification, nil) == .success {
// This also gets printed!
print("Successfully added Notification!")
}
CFRunLoopAddSource(RunLoop.current.getCFRunLoop(), AXObserverGetRunLoopSource(observer!), CFRunLoopMode.defaultMode)
Am I missing something? The code compiles and runs, but my calback doesn't get executed.
Does the callback get executed when a notification comes in?

see: https://developer.apple.com/documentation/applicationservices/1459139-axobservergetrunloopsource
Note that releasing the AXObserverRef automatically removes the run loop source from the run loop
your var observer: AXObserver? is deallocated so you'll never get the call. you need to keep a reference to your AXObserver somewhere.
good luck. the AX API is a pain.

Related

How do I wait for a download to complete before continuing?

I have this block of code. It fetches data from the API and adds it to a locationDetails array, which is part of a singleton.
private func DownloadLocationDetails(placeID: String) {
let request = AF.request(GoogleAPI.shared.getLocationDetailsLink(placeID: placeID))
request.responseJSON { (data) in
guard let detail = try? JSONDecoder().decode(LocationDetailsBase.self, from: data.data!),
let result = detail.result else {
print("Something went wrong fetching nearby locations.")
return
}
DownloadManager.shared.locationDetails.append(result)
}
}
This block of code is the block in question. I'm creating a caching system of sorts that only downloads new information and retains any old information. This is being done to save calls to the API and for performance gains. The line DownloadLocationDetails(placeID: placeID) is a problem for me because if I execute this line of code it will continue to loop over and over again using unnecessary API calls while waiting for the download to complete. How do I effectively manage this?
func GetLocationDetail(placeID: String) -> LocationDetail {
for location in locationDetails {
if location.place_id == placeID { return location }
}
DownloadLocationDetails(placeID: placeID)
return GetLocationDetail(placeID: placeID)
}
I expect this GetLocationDetail(....) to be called whenever a user interacts with an interface object, so how do I also ensure that the view that calls this is properly notified that the download is complete?
I attempted using a closure but I can't get it to return the way I'm wanting it to. I have a property on the singleton that I want to set this value so that it can be called globally. I am also considering using GCD but I'm not sure of the structure for that.
Generally the pattern for something like this is to store the request object you created in DownloadLocationDetails so you can check to see if one is active before making another call. If you only want to support one at a time, then it's as simple as keeping the bare reference to the request object, but you could make a dictionary of request objects keyed off the placeID (and you probably want to think about maximum request count, and queue up additional requests).
Then the trick is to get notified when the given request object completes. There are a couple ways you could do this, such as keeping a list of callbacks to invoke when it completes, but the easiest would probably be just to refactor the code a bit so that you always update your UI when the request completes, so something like:
private func DownloadLocationDetails(placeID: String) {
let request = AF.request(GoogleAPI.shared.getLocationDetailsLink(placeID: placeID))
request.responseJSON { (data) in
guard let detail = try? JSONDecoder().decode(LocationDetailsBase.self, from: data.data!),
let result = detail.result else {
print("Something went wrong fetching nearby locations.")
return
}
DownloadManager.shared.locationDetails.append(result)
// Notify the UI to refresh for placeID
}
}

how to detach a listener in a local scope?

I want to detach a snapshotListener in a viewController when a button is pressed. I was reading other stack over flow questions and the documentation and they were calling the remove method for the listener in the same function. I tried to do that in my situation but my snapshotListener just didn't end up working at all.
Here's my function and block of code that I want to tweak.
#objc func doneTapped() {
let updateListener = db.collection("school_users/\(user?.uid)/events").whereField("event_name", isEqualTo: navigationItem.title).addSnapshotListener(includeMetadataChanges: true) { (querySnapshot, error) in
if let error = error {
print("There was an error fetching the documents: \(error)")
} else {
self.eventName = querySnapshot!.documents.map { document in
return EventName(eventName: (document.get("event_name") as! String))
}
self.db.document("school_users/\(self.user?.uid)/events/\(self.docIDUneditableTextF.text!)").updateData(["event_date": self.dateEditableTextF.text, "event_cost": self.costEditableTextF.text, "for_grades": self.gradesEditableTextF.text]) { (error) in
if let error = error {
print("There was an error updating the document: \(error)")
} else {
print("The document was successfully updated."
}
}
}
}
dateEditableTextF.resignFirstResponder()
dateEditableTextF.isEnabled = false
costEditableTextF.resignFirstResponder()
costEditableTextF.isEnabled = false
gradesEditableTextF.resignFirstResponder()
gradesEditableTextF.isEnabled = false
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(editTapped))
}
I tried calling updateListener.remove() but it made my snapshotListener not work at all, also when the document updates, the print statement is never ending, is that also because the listener is still active or is that a different issue?
addSnapshotListener will give you updates whenever the data changes. I'm unclear why you would want to immediately remove the listener, if you really did want to receive updates -- and, as you pointed out, immediately removing it will basically cause to it not function at all. Perhaps post a link to some of those posts/documentation where you saw the code you're referencing and someone can give insight into what's happening.
My suspicion is that you don't actually need the updates to the data. In that case, you can just use .getDocuments() instead. See the Firestore documentation here about different ways to get data: https://firebase.google.com/docs/firestore/query-data/get-data
The second problem (the infinite print) is related to the first. Because you have a listener, which will return updates when the data changes, when you do your second database call (your updateData), that updates your data, triggering the listener again. This will keep looping because they will keep calling each other. This is another sign that perhaps you don't actually want a listener, but a single call to get the data. If you do in fact want updates, you'll have to find a way to decouple your second request so that you don't get in the loop.
Update based on comments: (Example of removing the listener in a different function)
On your view, view controller, etc, declare a property for the listener:
class MyViewController : UIViewController {
private var documentListener: ListenerRegistration? //assuming that ListenerRegistration is the correct type here, but you can check the current type of your updateListener to check
}
Then, in your function, set your listener to that:
documentListener = db.collection("school_users/\(user?.uid)/events").whereField("event_name", isEqualTo: navigationItem.title).addSnapshotListener()...
Then, later (like in viewDidDisappear), you can remove it:
documentListener?.remove()

NotificationCenter handler updates variable but UILabel stays the same

Using Xcode 10.1 running the app on mini iPad OS 12.1.1
I am posting a notification to NotificationCenter and the handler function updates UILabel setupProgress to show the progress of a data import.
This used to work fine but has recently stopped working, very possibly as a result of something I have done, but I can't think what.
PM in the code is a function which prints to the console - and it tells me that self.setupProgress.text is, in fact, set correctly and changes as expected as the data loads, however the corresponding UILabel does not update.
The initial UILabel text is set like this
if !self.update_type.isEmpty && self.update_type == "setup_import" {
self.setupProgress.text = "I've told the server that this is a clean install"
}
and that works fine - but then, as the import progresses, in the handler function (below) I get no updates until
import_progress == "And now the end is upon us ..."
at which point the UILabel updates correctly and everything carries on as expected.
func handleImportNotification(_ notification:Notification) {
self.setupProgress.text = import_progress
// should show something like
// `Importing F4RES : 0 of : 1395`
// `Importing F4RES : 500 of : 1395`
// etc...
PM(#line, function: #function, str1: self.setupProgress.text)
// prints the correct updates to the console
if import_progress == "And now the end is upon us ..." {
self.makeShopOptions()
self.loadingImage.stopAnimating()
self.performSegue(withIdentifier: "setup_to_splash", sender: self)
}
}
The app continues to work just fine - just without the expected UILabel updates in between.
Thanks in advance for any ideas.
You are posting a notification to the Notification Center. Would you mind showing how and when?
Assuming you're using NotificationCenter.default.post notificationName: method, the handler should take an argument of type Notification. Otherwise it won't respond to notification updates.
The handler function should look like:
func handleImportNotification(notification: Notification) { ... }
And the observer:
var observer: NSObjectProtocol?
func someFunction() {
observer = NotificationCenter.default.addObserver(forName: object: queue: using: { (Notification) in
//Update UILabel or call the associated function
}))
}
Try this.

All asynchronous calls succeed or none, how to handle

I'm trying to create an online mobile application and can't figure out the best way to handle functions with multiple asynchronous calls. Say I have a function for example that updates a user in some way, but involved multiple asynchronous calls in the single function call. So for example:
// Function caller
update(myUser) { (updatedUser, error) in
if let error = error {
// Present some error UI to the user
}
if let updatedUser = updatedUser {
// Do something with the user
}
}
// Function implementation
public func updateUser(user: User, completion: #escaping (User?, Error?) -> () {
// asynchronous call A
updateUserTable(user: User) { error in
if let error = error {
completion(nil, error)
} else {
// create some new user object
completion(user, nil)
}
}
// asynchronous call B
uploadMediaForUser(user: User) { error in
if let error = error {
completion(nil, error)
}
}
// asynchronous call C
removeOldReferenceForUser(user: User) { error in
if let error = error {
completion(nil, error)
}
}
// Possibly any additional amount of asynchronous calls...
}
In a case like this, where one function call like updating a user involved multiple asynchronous calls, is this an all or nothing situation? Say for example the updateUserTable() call completes, but the user disconnects from the internet as uploadMediaForUser() was running, and that throws an error. Since updateUserTable() completed fine, my function caller thinks this method succeeded when in fact not everything involved in updating the user completed. Now I'm stuck with a user that might have mismatched references or wrong information in my database because the user's connection dropped mid call.
How do I handle this all or nothing case? If EVERY asynchronous call completed without an error, I know updating the user was a success. If only a partial amount of asynchronous calls succeeded and some failed, this is BAD and I need to either undo the changes that succeeded or attempt the failed methods again.
What do I do in this scenario? And also, and how do I use my completion closure to help identify the actions needed depending on the success or failure of the method. Did all them succeed? Good, tell the user. Do some succeed and some failed? Bad, revert changes or try again (i dont know)??
Edit:
Just calling my completion with the error doesn't seem like enough. Sure the user sees that something failed, but that doesn't help with the application knowing the steps needed to fix the damage where partial changes were made.
I would suggest adding helper enums for your tasks and returned result, things like (User?, Error?) have a small ambiguity of the case when for example both are nil? or you have the User and the Error set, is it a success or not?
Regarding the all succeeded or some failed - I would suggest using the DispatchGroup to notify when all tasks finished (and check how they finished in the end).
Also from you current code, when some request fails it's not clear for which user - as you pass nil, so it might bring difficulties in rolling it back after failure.
So in my point of view something like below (not tested the code, but think you should catch the idea from it) could give you control about the issues you described:
public enum UpdateTask {
case userTable
case mediaUpload
// ... any more tasks you need
}
public enum UpdateResult {
case success
case error([UpdateTask: Error])
}
// Function implementation
public func updateUser(user: User, completion: #escaping (User, UpdateResult) -> ()) {
let updateGroup = DispatchGroup()
var tasksErrors = [UpdateTask: Error]()
// asynchronous call A
updateGroup.enter()
updateUserTable(user: User) { error in
if let error = error {
tasksErrors[.userTable] = error
}
updateGroup.leave()
}
// ... any other similar tasks here
updateGroup.notify(queue: DispatchQueue.global()) { // Choose the Queue that suits your needs here by yourself
if tasksErrors.isEmpty {
completion(user, .success)
} else {
completion(user, .error(tasksErrors))
}
}
}
Keep a “previous” version of everything changed, then if something failed revert back to the “previous” versions. Only change UI once all returned without failure, and if one failed, revert to “previous” version.
EX:
var temporary = “userName”
getChanges(fromUser) {
If error {
userName = temporary //This reverts back due to failure.
}
}

Wait for Parse Async functions to complete in Swift

I'm trying to wait for Parse async functions in Swift to reload my UITableView
I'm not sure if Completion Handler is useful in this case. or Dispatch Async.
I'm really confused ! Can someone help out with this
var posts = [PFObject]()
for post in posts {
post.fetchInBackground()
}
tableView.reloadData() // I want to execute that when the async functions have finished execution
You want to use fetchAllInBackground:Block I've had issues launching a bunch of parse calls in a loop where it will take a lot longer to return all of them than expected.
fetch documentation
It should look something like this:
PFObject.fetchAllInBackground(posts, block: { (complete, error) in
if (error == nil && complete) {
self.tableView.reloadData()
}
})
One thing to note is that in your example posts are empty and a generic PFObject. I'm assuming this is just for the example. Otherwise if you want to get all posts in Parse (as opposed to updating current ones) you will want to use PFQuery instead of fetching. query documentation
You need to use fetchInBackgroundWithBlock. Alternatively, if you want to wait until all have loaded and then update the UI, use PFObject's +fetchAllInBackground:block:. Note that this is a class method, and would therefore be called as PFObject.fetchAllInBackground(.... See documentation here.
Either way, because you're running in a background thread, you must update the UI on the main thread. This is normally done using dispatch_async.
The other thing to watch out for is if you run fetchInBackgroundWithBlock in a loop and collect all the results in an array, arrays are not thread safe. You will have to use something like dispatch_barrier or your own synchronous queue to synchronise access to the array. Code for the second option is below:
// Declared once and shared by each call (set your own name)...
let queue = dispatch_queue_create("my.own.queue", nil)
// For each call...
dispatch_sync(queue) {
self.myArray.append(myElement)
}
Here's a little class I made to help with coordination of asynchronous processes:
class CompletionBlock
{
var completionCode:()->()
init?(_ execute:()->() )
{ completionCode = execute }
func deferred() {}
deinit
{ completionCode() }
}
The trick is to create an instance of CompletionBlock with the code you want to execute after the last asynchronous block and make a reference to the object inside the closures.
let reloadTable = CompletionBlock({ self.tableView.reloadData() })
var posts = [PFObject]()
for post in posts
{
post.fetchInBackground(){ reloadTable.deferred() }
}
The object will remain "alive" until the last capture goes out of scope. Then the object itself will go out of scope and its deinit will be called executing your finalization code at that point.
Here is an example of using fetchInBackgroundWithBlock which reloads a tableView upon completion
var myArray = [String]()
func fetchData() {
let userQuery: PFQuery = PFUser.query()!
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
var userData = users!
if error == nil {
if userData.count >= 1 {
for i in 0...users!.count-1 {
self.myArray.append(userData[i].valueForKey("dataColumnInParse") as! String)
}
}
self.tableView.reloadData()
} else {
print(error)
}
})
}
My example is a query on the user class but you get the idea...
I have experimented a bit with the blocks and they seem to get called on the main thread, which means that any UI changes can be made there. The code I have used to test looks something like this:
func reloadPosts() {
PFObject.fetchAllIfNeededInBackground(posts) {
[unowned self] (result, error) in
if let err = error {
self.displayError(err)
}
self.tableView.reloadData()
}
}
if you are in doubt about whether or not the block is called on the main thread you can use the NSThread class to check for this
print(NSThread.currentThread().isMainThread)
And if you want it to be bulletproof you can wrap your reloadData inside dispatch_block_tto ensure it is on the main thread
Edit:
The documentation doesn't state anywhere if the block is executed on the main thread, but the source code is pretty clear that it does
+ (void)fetchAllIfNeededInBackground:(NSArray *)objects block:(PFArrayResultBlock)block {
[[self fetchAllIfNeededInBackground:objects] thenCallBackOnMainThreadAsync:block];
}