I am trying to run loadViews() after the pullData() completes and I am wondering what the best way of doing this is? I would like to set a 10 sec timeout on it as well so I can display a network error if possible. From what I have read, GCD looks like it is the way to accomplish this but I am confused on the implementation of it. Thanks for any help you can give!
//1
pullData()
//2
loadViews()
What you need is a completion handler with a completion block.
Its really simple to create one:
func firstTask(completion: (success: Bool) -> Void) {
// Do something
// Call completion, when finished, success or faliure
completion(success: true)
}
And use your completion block like this:
firstTask { (success) -> Void in
if success {
// do second task if success
secondTask()
}
}
You can achieve like this :-
func demo(completion: (success: Bool) -> Void) {
// code goes here
completion(success: true)
}
I had a similar situation where I had to init a view once the data is pulled from Parse server. I used the following:
func fetchQuestionBank(complete:()->()){
let userDefault = NSUserDefaults.standardUserDefaults()
let username = userDefault.valueForKey("user_email") as? String
var query = PFQuery(className:"QuestionBank")
query.whereKey("teacher", equalTo: username!)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
var questionTitle:String?
var options:NSArray?
for (index, object) in enumerate(objects) {
questionTitle = object["question_title"] as? String
options = object["options"] as? NSArray
var aQuestion = MultipleChoiceQuestion(questionTitle: questionTitle!, options: options!)
aQuestion.questionId = object.objectId!
InstantlyModel.sharedInstance.questionBank.append(aQuestion)
}
complete()
}
}else{
println(" Question Bank Error \(error) ")
}
}
}
And this is you call the method:
self.fetchQuestionBank({ () -> () in
//Once all the data pulled from server. Show Teacher View.
self.teacherViewController = TeacherViewController(nibName: "TeacherViewController", bundle: nil)
self.view.addSubview(self.teacherViewController!.view)
})
function1();
function2();
Use functions!! Once function1() function completed, function2() will execute.
This question already has answers here:
NSURLConnection sendAsynchronousRequest can't get variable out of closure
(1 answer)
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I understand how completion handlers work, but im a bit confused on the syntax. Below is a function that, given a username, calls a parse query to find out the corresponding userId. The query ends after the function is returned (so it returns nil), which is why we need the completion handler. How do i implement it?
func getUserIdFromUsername(username: String) -> String {
var returnValue = String()
let query = PFQuery(className: "_User")
query.whereKey("username", equalTo: username)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
returnValue = object.objectId!
}
}
}
return returnValue
}
NOTE: I know examples similar to this exist, but they are either not swift, or extremely lengthy. This is a short and concise version that contains Parse.
Here's how to implement it:
func getUserIdFromUsername(username: String, completionHandler: String -> Void) {
let query = PFQuery(className: "_User")
query.whereKey("username", equalTo: username)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
completionHandler(object.objectId!)
}
}
}
}
And here's how to use it:
getUserIdFromUsername("myUser") { id in
doSomethingWithId(id)
}
I am trying to populate my tableview with data from Health app. The problem is that this data comes in different order every time I load the screen.
I have functions for getting the data from the Health app:
func readWalkrunDistance(startDate: NSDate) {
healthManager.readRunningAndWalkingDistance(startDate, endDate: selectedDate!) { (results, error) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
var dict: [String: Double] = [String: Double]()
dict["Walkrun Distance"] = results
dict["number"] = 13
if let result = results {
self.walkrunDistance = result
}
self.healthData.append(dict)
self.updateData()
})
}
}
func readBodyMass(startDate: NSDate) {
healthManager.readMedianBodyMass(startDate, endDate: selectedDate!) { (results, error) -> Void in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
var dict: [String: Double] = [String: Double]()
dict["Body Mass"] = results
dict["number"] = 7
if let result = results {
self.bodyMass = result
}
self.healthData.append(dict)
self.updateData()
})
}
}
And another 10 functions like these for other Health data. The problem is that the data comes every time in different order, sometimes walkrunDistance is shown twice and etc. I think it is because of the async calls, but I don't know how to wait for these functions to get the data, then go to cellForRowAtIndexPath...
Here is my function for calling all the Health functions:
func readAllData() {
selectedDate = ProjectManager.sharedInstance.chosenDate
selectedDate = NSCalendar.currentCalendar().dateByAddingUnit(.Day, value: 1, toDate: selectedDate, options: [])
let startDate = NSCalendar.currentCalendar().dateByAddingUnit(.Day, value: -1, toDate: selectedDate, options: [])
self.readActiveEnergyBurned(startDate!)
self.readBasalEnergyBurned(startDate!)
self.readBloodAlcoholContent(startDate!)
self.readBloodPressureDiastolic(startDate!)
self.readBloodPressureSystolic(startDate!)
self.readBodyFatPercentage(startDate!)
self.readBodyMass(startDate!)
self.readDistanceCycling(startDate!)
self.readFlightsClimbed(startDate!)
self.readAverageHeartRate(startDate!)
self.readMinimumHeartRate(startDate!)
self.readMaximumHeartRate(startDate!)
self.readLeanBodyMass(startDate!)
self.readStepCount(startDate!)
self.readWorkouts(startDate!)
self.tableView.reloadData()
}
I want first to get the data, then load it into the tableview in the correct way.
You can try to use dispatch_group.
Here's link to tutorial:
http://www.raywenderlich.com/79150/grand-central-dispatch-tutorial-swift-part-2
Just add var group = dispatch_group_create() variable into your class.
Add dispatch_group_enter(group) into all of your functions and dispatch_group_leave(group) to the end of your async calls.
func readWalkrunDistance(startDate: NSDate) {
healthManager.readRunningAndWalkingDistance(startDate, endDate: selectedDate!) { (results, error) -> Void in
dispatch_group_enter(group)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
var dict: [String: Double] = [String: Double]()
dict["Walkrun Distance"] = results
dict["number"] = 13
if let result = results {
self.walkrunDistance = result
}
self.healthData.append(dict)
self.updateData()
dispatch_group_enter(group)
})
}
}
Add before self.tableView.reloadData() - dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
This will resolve your "async" problem. -reloadData will be called after all functions.
I'm experimenting a bit to familiarize myself with the HKAnchoredObjectQuery and getting results when my app is inactive.
I start the app, switch away to Apple Health, enter a blood glucose result; sometimes the results handler is called right away (as evidenced by the print to the console) but other times the handler isn't called until I switch back to my app. Same is true for deleted results as well as added results. Anybody have any guidance?
Most of this code is from a question from thedigitalsean adapted here to get updates while app is in the background and logging to the console. See: Healthkit HKAnchoredObjectQuery in iOS 9 not returning HKDeletedObject
class HKClient : NSObject {
var isSharingEnabled: Bool = false
let healthKitStore:HKHealthStore? = HKHealthStore()
let glucoseType : HKObjectType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)!
override init(){
super.init()
}
func requestGlucosePermissions(authorizationCompleted: (success: Bool, error: NSError?)->Void) {
let dataTypesToRead : Set<HKObjectType> = [ glucoseType ]
if(!HKHealthStore.isHealthDataAvailable())
{
// let error = NSError(domain: "com.test.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey: "Healthkit is not available on this device"])
self.isSharingEnabled = false
return
}
self.healthKitStore?.requestAuthorizationToShareTypes(nil, readTypes: dataTypesToRead){(success, error) -> Void in
self.isSharingEnabled = true
authorizationCompleted(success: success, error: error)
}
}
func getGlucoseSinceAnchor(anchor:HKQueryAnchor?, maxResults:uint, callback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!) {
let queryEndDate = NSDate(timeIntervalSinceNow: NSTimeInterval(60.0 * 60.0 * 24))
let queryStartDate = NSDate.distantPast()
let sampleType: HKSampleType = glucoseType as! HKSampleType
let predicate: NSPredicate = HKAnchoredObjectQuery.predicateForSamplesWithStartDate(queryStartDate, endDate: queryEndDate, options: HKQueryOptions.None)
var hkAnchor: HKQueryAnchor
if(anchor != nil){
hkAnchor = anchor!
} else {
hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
}
let onAnchorQueryResults : ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, NSError?) -> Void)! = {
(query:HKAnchoredObjectQuery, addedObjects:[HKSample]?, deletedObjects:[HKDeletedObject]?, newAnchor:HKQueryAnchor?, nsError:NSError?) -> Void in
var added = [String]()
var deleted = [String]()
if (addedObjects?.count > 0){
for obj in addedObjects! {
let quant = obj as? HKQuantitySample
if(quant?.UUID.UUIDString != nil){
let val = Double( (quant?.quantity.doubleValueForUnit(HKUnit(fromString: "mg/dL")))! )
let msg : String = (quant?.UUID.UUIDString)! + " " + String(val)
added.append(msg)
}
}
}
if (deletedObjects?.count > 0){
for del in deletedObjects! {
let value : String = del.UUID.UUIDString
deleted.append(value)
}
}
if(callback != nil){
callback(source:self, added: added, deleted: deleted, newAnchor: newAnchor, error: nsError)
}
}
// remove predicate to see deleted objects
let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: nil, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults)
// added - query should be always running
anchoredQuery.updateHandler = onAnchorQueryResults
// added - allow query to pickup updates when app is in backgroun
healthKitStore?.enableBackgroundDeliveryForType(sampleType, frequency: .Immediate) {
(success, error) in
if (!success) {print("enable background error")}
}
healthKitStore?.executeQuery(anchoredQuery)
}
let AnchorKey = "HKClientAnchorKey"
func getAnchor() -> HKQueryAnchor? {
let encoded = NSUserDefaults.standardUserDefaults().dataForKey(AnchorKey)
if(encoded == nil){
return nil
}
let anchor = NSKeyedUnarchiver.unarchiveObjectWithData(encoded!) as? HKQueryAnchor
return anchor
}
func saveAnchor(anchor : HKQueryAnchor) {
let encoded = NSKeyedArchiver.archivedDataWithRootObject(anchor)
NSUserDefaults.standardUserDefaults().setValue(encoded, forKey: AnchorKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
class ViewController: UIViewController {
let debugLabel = UILabel(frame: CGRect(x: 10,y: 20,width: 350,height: 600))
override func viewDidLoad() {
super.viewDidLoad()
self.view = UIView();
self.view.backgroundColor = UIColor.whiteColor()
debugLabel.textAlignment = NSTextAlignment.Center
debugLabel.textColor = UIColor.blackColor()
debugLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
debugLabel.numberOfLines = 0
self.view.addSubview(debugLabel)
let hk = HKClient()
hk.requestGlucosePermissions(){
(success, error) -> Void in
if(success){
let anchor = hk.getAnchor()
hk.getGlucoseSinceAnchor(anchor, maxResults: 0)
{ (source, added, deleted, newAnchor, error) -> Void in
var msg : String = String()
if(deleted?.count > 0){
msg += "Deleted: \n" + (deleted?[0])!
for s in deleted!{
msg += s + "\n"
}
}
if (added?.count > 0) {
msg += "Added: "
for s in added!{
msg += s + "\n"
}
}
if(error != nil) {
msg = "Error = " + (error?.description)!
}
if(msg.isEmpty)
{
msg = "No changes"
}
debugPrint(msg)
if(newAnchor != nil && newAnchor != anchor){
hk.saveAnchor(newAnchor!)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.debugLabel.text = msg
})
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I also added print()'s at the various application state changes. A sample of the console log (this is running on iPhone 6s device from XCode) shows the handler being called sometimes after I entered background but before reentering foreground and other times only after reentering foreground.
app did become active
"No changes"
app will resign active
app did enter background
app will enter foreground
"Added: E0340084-6D9A-41E4-A9E4-F5780CD2EADA 99.0\n"
app did become active
app will resign active
app did enter background
"Added: CEBFB656-0652-4109-B994-92FAA45E6E55 98.0\n"
app will enter foreground
"Added: E2FA000A-D6D5-45FE-9015-9A3B9EB1672C 97.0\n"
app did become active
app will resign active
app did enter background
"Deleted: \nD3124A07-23A7-4571-93AB-5201F73A4111D3124A07-23A7-4571-93AB-5201F73A4111\n92244E18-941E-4514-853F-D890F4551D76\n"
app will enter foreground
app did become active
app will resign active
app did enter background
app will enter foreground
"Added: 083A9DE4-5EF6-4992-AB82-7CDDD1354C82 96.0\n"
app did become active
app will resign active
app did enter background
app will enter foreground
"Added: C7608F9E-BDCD-4CBC-8F32-94DF81306875 95.0\n"
app did become active
app will resign active
app did enter background
"Deleted: \n15D5DC92-B365-4BB1-A40C-B870A48A70A415D5DC92-B365-4BB1-A40C-B870A48A70A4\n"
"Deleted: \n17FB2A43-0828-4830-A229-7D7DDC6112DB17FB2A43-0828-4830-A229-7D7DDC6112DB\n"
"Deleted: \nCEBFB656-0652-4109-B994-92FAA45E6E55CEBFB656-0652-4109-B994-92FAA45E6E55\n"
app will enter foreground
"Deleted: \nE0340084-6D9A-41E4-A9E4-F5780CD2EADAE0340084-6D9A-41E4-A9E4-F5780CD2EADA\n"
app did become active
I suggest using an HKObserverQuery and setting it up carefully.
There is an algorithm that watches how and when you call the "completion" handler of the HKObserverQuery when you have background delivery enabled. The details of this are vague unfortunately. Someone on the Apple Dev forums called it the "3 strikes" rule but Apple hasn't published any docs that I can find on it's behavior.
https://forums.developer.apple.com/thread/13077
One thing I have noticed is that, if your app is responding to a background delivery with an HKObserverQuery, creating an HKAnchoredObjectQuery, and setting the UpdateHandler in that HKAnchoredObjectQuery, this UpdateHandler will often cause multiple firings of the callback. I suspected that perhaps since these additional callbacks are being executed AFTER you have already told Apple that you have completed you work in response to the background delivery, you are calling the completion handler multiple times and maybe they ding you some "points" and call you less often for bad behavior.
I had the most success with getting consistent callbacks by doing the following:
Using an ObserverQuery and making the sure the call of the "completion" handler gets called once and at the very end of your work.
Not setting an update handler in my HKAnchoredObjectQuery when running in the background (helps achieve 1).
Focusing on making my query handlers, AppDelegate, and ViewController are as fast as possible. I noticed that when I reduced all my callbacks down to just a print statement, the callbacks from HealthKit came immediately and more consistently. So that says Apple is definitely paying attention to execution time. So try to statically declare things where possible and focus on speed.
I have since moved on to my original project which uses Xamarin.iOS, not swift, so I haven't kept up with the code I originally posted. But here is an updated (and untested) version of that code that should take these changes into account (except for the speed improvements):
//
// HKClient.swift
// HKTest
import UIKit
import HealthKit
class HKClient : NSObject {
var isSharingEnabled: Bool = false
let healthKitStore:HKHealthStore? = HKHealthStore()
let glucoseType : HKObjectType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)!
override init(){
super.init()
}
func requestGlucosePermissions(authorizationCompleted: (success: Bool, error: NSError?)->Void) {
let dataTypesToRead : Set<HKObjectType> = [ glucoseType ]
if(!HKHealthStore.isHealthDataAvailable())
{
// let error = NSError(domain: "com.test.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey: "Healthkit is not available on this device"])
self.isSharingEnabled = false
return
}
self.healthKitStore?.requestAuthorizationToShareTypes(nil, readTypes: dataTypesToRead){(success, error) -> Void in
self.isSharingEnabled = true
authorizationCompleted(success: success, error: error)
}
}
func startBackgroundGlucoseObserver( maxResultsPerQuery: Int, anchorQueryCallback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!)->Void {
let onBackgroundStarted = {(success: Bool, nsError : NSError?)->Void in
if(success){
//Background delivery was successfully created. We could use this time to create our Observer query for the system to call when changes occur. But we do it outside this block so that even when background deliveries don't work,
//we will have the observer query working when are in the foreground at least.
} else {
debugPrint(nsError)
}
let obsQuery = HKObserverQuery(sampleType: self.glucoseType as! HKSampleType, predicate: nil) {
query, completion, obsError in
if(obsError != nil){
//Handle error
debugPrint(obsError)
abort()
}
var hkAnchor = self.getAnchor()
if(hkAnchor == nil) {
hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
}
self.getGlucoseSinceAnchor(hkAnchor, maxResults: maxResultsPerQuery, callContinuosly:false, callback: { (source, added, deleted, newAnchor, error) -> Void in
anchorQueryCallback(source: self, added: added, deleted: deleted, newAnchor: newAnchor, error: error)
//Tell Apple we are done handling this event. This needs to be done inside this handler
completion()
})
}
self.healthKitStore?.executeQuery(obsQuery)
}
healthKitStore?.enableBackgroundDeliveryForType(glucoseType, frequency: HKUpdateFrequency.Immediate, withCompletion: onBackgroundStarted )
}
func getGlucoseSinceAnchor(anchor:HKQueryAnchor?, maxResults:Int, callContinuosly:Bool, callback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!){
let sampleType: HKSampleType = glucoseType as! HKSampleType
var hkAnchor: HKQueryAnchor;
if(anchor != nil){
hkAnchor = anchor!
} else {
hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
}
let onAnchorQueryResults : ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, NSError?) -> Void)! = {
(query:HKAnchoredObjectQuery, addedObjects:[HKSample]?, deletedObjects:[HKDeletedObject]?, newAnchor:HKQueryAnchor?, nsError:NSError?) -> Void in
var added = [String]()
var deleted = [String]()
if (addedObjects?.count > 0){
for obj in addedObjects! {
let quant = obj as? HKQuantitySample
if(quant?.UUID.UUIDString != nil){
let val = Double( (quant?.quantity.doubleValueForUnit(HKUnit(fromString: "mg/dL")))! )
let msg : String = (quant?.UUID.UUIDString)! + " " + String(val)
added.append(msg)
}
}
}
if (deletedObjects?.count > 0){
for del in deletedObjects! {
let value : String = del.UUID.UUIDString
deleted.append(value)
}
}
if(callback != nil){
callback(source:self, added: added, deleted: deleted, newAnchor: newAnchor, error: nsError)
}
}
let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: nil, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults)
if(callContinuosly){
//The updatehandler should not be set when responding to background observerqueries since this will cause multiple callbacks
anchoredQuery.updateHandler = onAnchorQueryResults
}
healthKitStore?.executeQuery(anchoredQuery)
}
let AnchorKey = "HKClientAnchorKey"
func getAnchor() -> HKQueryAnchor? {
let encoded = NSUserDefaults.standardUserDefaults().dataForKey(AnchorKey)
if(encoded == nil){
return nil
}
let anchor = NSKeyedUnarchiver.unarchiveObjectWithData(encoded!) as? HKQueryAnchor
return anchor
}
func saveAnchor(anchor : HKQueryAnchor) {
let encoded = NSKeyedArchiver.archivedDataWithRootObject(anchor)
NSUserDefaults.standardUserDefaults().setValue(encoded, forKey: AnchorKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
Apologies that I couldn't think of a better way to title this.
Basically I have an app which connects to Parse. I have specified an action, which runs when a text field is changed (i.e when the user types a letter)
within this action I'm calling a PFQuery to Parse, which I then ask to findObjectsInBackgroundWithBlock.
The problem is that if a user were to type another letter before this query has finished running, then 2 queries are now running and the results of both end up populating the tableView.
So my question is simply, if the user were to type in another letter before the first findObjectsInBackgroundWithBlock has finished, how would I cancel the first and run a new one?
I have tried inserting PFQuery.cancel(query) at the start of the action, but the code gets confused as there isn't a query running yet when the action runs for the first time.
My code, incase it may help:
#IBAction func textFieldChanged (sender: AnyObject) {
let query = PFUser.Query()
query!.whereKey("Postcode", containsString: searchField.text)
query?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.citiesArray.append(object["city"] as! String)
}
}
})
Many thanks for your patience!
You can try wrapping those requests in NSOperation and adding them to a dedicated (for such search requests) NSOperationQueue and calling cancelAllOperations() when a new character is typed.
In NSOperation's inner Parse block check for self.cancelled and return doing nothing if cancelled. Should work fine.
UPDATE:
class ViewController: UIViewController {
#IBOutlet weak var searchField: UITextField!
var citiesArray = [String]()
lazy var textRequestQueue: NSOperationQueue = {
var queue = NSOperationQueue()
queue.maxConcurrentOperationCount = 1
queue.qualityOfService = NSQualityOfService.UserInteractive
return queue
}()
#IBAction func textFieldChanged(sender: AnyObject) {
textRequestQueue.cancelAllOperations()
let query = PFQuery()
query.whereKey("Postcode", containsString: searchField.text)
textRequestQueue.addOperation(TextRequestOperation(query: query, resultBlock: { (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.citiesArray.append(object["city"] as! String)
}
}
}))
}
}
class TextRequestOperation: NSOperation {
typealias ResultBlock = ((result: String)->())
var _resultBlock: PFArrayResultBlock
var _query: PFQuery
init(query: PFQuery, resultBlock: PFArrayResultBlock) {
self._resultBlock = resultBlock
self._query = query
}
override func main()
{
if self.cancelled { return }
_query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if self.cancelled { return }
self._resultBlock(objects, error)
}
}
}
NSOperation is one option. ReativeCocoa is another option that could help you solve this problem quite easily if you know how to use it.
However the easiest way (and hackiest) would prob be to keep some state of the search and use it to only apply the most recent searches results.
var mostRecentSearchQuery: String = ""
#IBAction func textFieldChanged (sender: AnyObject) {
var queryString: String?
if let textField: UITextField = sender as? UITextField {
queryString = textField.text
self.mostRecentSearchQuery = textField.text
}
let query = PFUser.Query()
query!.whereKey("Postcode", containsString: searchField.text)
query?.findObjectsInBackgroundWithBlock({[weak self] (objects, error) -> Void in
if let objects = objects where queryString == self?.mostRecentSearchQuery {
for object in objects {
self.citiesArray.append(object["city"] as! String)
}
}
})
This will only update your results if block used the most recent text typed.
ps. I am assuming the sender passed into the method is the textField which text has changed.