Swift: How do I store an array of object by reference from completion handler (closure)? - ios

There's a API callback returns a Json format result. The requirement in short is that I need to keep calling this API and keeps implement Breadth First Search on the results it returns.
Imaging it's a map with many nodes and connections. Every time I call API call for a node, it gonna return me list of its connected nodes. All I need now, is an array, which saves all the node that has been visited to avoid repeated visits.
But this is Swift and I am new to it. I was using Array and pass as inout inside the completion handler. But there's an error: escaping closures can only capture inout parameters explicitly by value which means I cannot do it like this.
Now you may ask why the array I have has to be stored by reference. Because the API call is async, which means I have to wait until it comes back to keep progressing Breadth First Search, means I have to pass this array by reference in order to do the recursion.
What other solutions I may have?

Swift Arrays are value types (not reference types) so you will need to store you array in an object. You can then pass the object to your handler and set the array content inside the object which is carried as a reference in the closures.

Related

UMDF: Access device registry key inside a EVT_WDF_TIMER callback function

I am having issues with a UMDF driver retrieving data stored in the device registry from a EVT_WDF_TIMER callback function. Such callback function only has WDFTIMER object as its sole parameter. To be able to access registry in UMDF, WdfDeviceOpenRegistryKey requires a pointer to the WDFDEVICE object. Prior to the callback function being called, my WDFTimer, which calls the callback function after the timer has elapsed, is created by WdfTimerCreate. I have provided the appropriate WDF_OBJECT_ATTRIBUTES as well, one of the attributes being a WDFOBJECT ParentObject where I assigned the WDFDEVICE object into.
Now, I was hoping I could retrieve the WDFDEVICE object I added in WDF_OBJECT_ATTRIBUTES, but it seems inaccessible from the WDFTIMER parameter. I am left wondering, is there any way I could retrieve a pointer to my WDFDEVICE from the EVT_WDF_TIMER callback function?
You can use WdfTimerGetParentObject to retrieve the WDFOBJECT.

Firebase: What's the correct way to update values?

If my database structure is like this:
and I have this:
ref = Database.database().reference().child(passUserID)
ref?.child("wins").updateChildValues("wins": numerator)
where numerator = (games played beforehand) + (games played after logging in), for the second statement it is giving me an error in the expression that I don't know how to fix.
Also if I try to do this instead:
ref?.child("wins").setValue(numerator)
it messes up my data bad and gives a bad instruction error.
The updateChildValues method takes a Dictionary. So you just need to call the method with a dictionary containing the key-value pairs you want to update, i.e.
ref = Database.database().reference().child(passUserID)
ref?.updateChildValues(["wins": numerator])
For more on how to read/write data to firebase realtime database, refer to: https://firebase.google.com/docs/database/ios/read-and-write
If you use .setValue triggers only in the beginning. Instead of .setValue use updateChildValues(.values/.childAdded/.childRemoved etc..) to update the values inside your tree. It will be executed whenever changes occurs to your node.
How to update particular value of child in Firebase DB

Parse.com findObjects() get data

I need to run a SYNCHRONOUS call to parse.com. This is what I got:
var query = PFQuery(className:"myClass")
query.whereKey("groupClassId", equalTo:self.currentGroupId)
query.selectKeys(["objectId", "firstName", "lastName"])
self.arrayCurrentData = query.findObjects() as Array<myData>
This return the correct number of rows from parse.com and fills up my local array. But how can I extract the data from the array? If I look at the array at runtime it shows that all the data I need is in 'serverData' in self.arrayCurrentData.
Normally if I loop an async(findObjectsInBackgroundWithBlock) filled array I would ask
self.arrayCurrentData[i].lastName
to get the lastName, but that is not the case in the sync array. There I can't ask directly for values (or so it seems).
Anyone who know what I am talking about and how to get data synchronous from parse.com?
Get the PFObject's attributes with valueForKey(). This is true whether or not the object was fetched synchronously. In other words...
self.arrayCurrentData[i].valueForKey("lastName")
EDIT - This approach generates a compiler message because you've typed the response as Array<myData>. But find returns PFObjects, so ...
self.arrayCurrentData = query.findObjects() as [PFObject]
... is the correct cast. I'm not a swift speaker, but the expression self.arrayCurrentData[i].lastName pleases the compiler because arrayCurrentData[i] is typed as myData. But this fails at run time because the real returned objects are PFObjects.
As an aside, I'd take a hard look at the rationale for fetching synchronously. I can't think of a case where its a good idea on the main thread. (off the main okay, but then you've already opted for asynch vs. the main, and the block-based methods provide a good way to encapsulate the post-fetch logic).

Sending NSNotifications to all objects of a class

I have an object that can be selected by a user click. With the current requirements of the app, at any time, there is no more than one of these items selected at any point during app execution.
I implemented a mechanism to enforce this, as follows:
Each of these objects has a unique identifier as a property.
When each object is created, it subscribes to the NSNotificationCenter listening for the MY_OBJECT_SELECTED notification.
When each object is selected, it posts the MY_OBJECT_SELECTED notification, with its unique Id as part of the userInfo dictionary.
Then, when each object receives the notification, it checks to see if its id is the same as the one in the userInfo. If it is, it does nothing, but if it isn't, it sets itself to unselected.
Is this a decent approach to the problem? If not, how would you do it?
It is a decent way of doing it, although it is not very efficient. The more objects you have, the more time you spend comparing IDs. The easiest way is to store your object pointers and IDs in a map table (or similar) and remember the last selected object. Whenever you select a new object, you clear the selection flag of the last selected object, then look up the new object and set its selection flag. It requires you to keep a collection of your objects, though.
The time required to update selections with this approach is independent of the number of objects you have.
If the object is spread all over the app,i.e. if it is a member in various classes. You can have a global object of same type and assign it to only that object which has been touched. In steps it will be like:
Have a global variable of object type.
At any object touch assign globalObject = currentObject;
do all operations on globalObject throughout app like calling methods and modifying object members(have a check for nil to ensure safety).
Reassign to different object with new touch.

Cancelling NSJSONSerialization - Search as you type, requests overlapping

Similar to the iPhone Facebook app search function, I am implementing search as you type functionality into my application although I have a problem when decoding the data into JSON format.
Basically what happens is because some searches take longer than others, they return at different intervals and this causes some small visual issues when the data is presenting on the screen.
I have set an NSLOG after each decode using NSJSONSerialization for the keyword 'industry'
2013-04-09 23:38:18.941 Project Name [42836:1d03] http://fooWebAddress/json/?method=search&limit=10&q=indus
2013-04-09 23:38:19.776 Project Name [42836:3e07] http://fooWebAddress/json/?method=search&limit=10&q=indu
2013-04-09 23:38:20.352 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=indust
2013-04-09 23:38:21.814 Project Name [42836:4e03] http://fooWebAddress/json/?method=search&limit=10&q=industr
2013-04-09 23:38:23.434 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=ind
2013-04-09 23:38:24.070 Project Name [42836:7503] http://fooWebAddress/json/?method=search&limit=10&q=industry
As you can see it is all out of order.
Does anyone have any way of stopping NSJSONSerialization for the previous connection.
Or possibly any other way to go about this problem?
Steps up to NSJSONSerialization...
NSURLRequest (initwithURL)
NSOperationQueue
NSURLConnection (asynchronous)
NSJSONSerialization
Thanks in advance.
When the user starts typing more text, you could cancel your previous connections and ignore any further delegate callbacks you receive from them. Then make the new request for the current text.
You can do this by maintaining some sort of lastRequest or lastOperation reference. When the user starts typing, call [self.lastRequestOrOperation cancel] and ignore any further notifications from that request with a check like if (request != self.lastRequest) { return; } in whatever callbacks you have.
However this has the problem that if the user keeps typing for a while you are constantly cancelling requests and they may not see any results until they have stopped typing.
A better solution would be to add sequencing so that each request is associated with an increasing sequence ID. You then only parse the result and update the UI when the sequence of the response is higher than the last one you received. If you receive any out-of-band responses from earlier, you just ignore them.
This is a much more complex issue than just being able to cancel the NSJSONSerialization. My suggestion is to use NSFetchedResultsController to populate your table view that shows the search results. Use the search term as one of the predicate variable in the NSFetchRequest attached to NSFetchedResultsController. And then, when you parse the results using NSJSONSerialization, store the results with the search term associated with that request. As soon as the search term changed (which you can detect when the user types more characters), re-create the NSFetchedResultsController and reload your table view. In addition, you can also try to cancel the call to parse the previous results if you launched it using performSelector:withObject:afterDelay. Beware that this cannot be always relied upon as the call may have been initiated by the time you are trying to cancel.
Kinda basic, but you could always maintain an nsdictionary of sub-classed NSURLRequests (sub-classed to provide a tag).
Start request - add request to dicationary with tag = array.count - 1, with key matching tag
Connection returns - is the request the most recent request, if so, parse json
Parse JSON - is the request the most recent request, if so, show results, if not, only display if there are no previous results displayed
Request handling - remove key from dictionary
most recent request = does the dictionary contain an object with a higher key value
Currently what you are doing is, you type each character and calling web-service. Why to call web-service for each letter you type. If user is type continuously, then it will increase the load, so call the web-service only when user stops for a particular interval of time. and then pass that string to call web-service or what ever method you are calling.
[NSObject cancelPerformSelectorsWithTarget:self]; // This will cancel your all req which is going to make when user typing without stopping
[self performSelector:#selector(sendSearchRequest) withObject:searchText afterDelay:0.1f]; // This will pass the string to call a web-service method, on which user hold for some time.

Resources