Execute method after response - ios

I am stuck in one interesting problem as i want to execute a method after 3 second but also want to check whether i got response of that method or not.
If i not got the response in 3 second then that method will not be executed.
Using code:
Timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector:#selector(receiveMessageWebservice)userInfo: nil repeats:YES];
This will run receiveMessageWebservice in 3 second of interval. But problem is sometime response not come in 3 second it take more time then i got two or three response same time.
I don't want so.
Please help.

When you request for the Web Service just invalidate the timer and recreate it in the response success/failure block.
In this way you can successfully run the service request after every fixed interval without requesting multiple times

Definitely, network calls cannot be time guaranteed . As i assume , you want to retry for a response till it doesn't come to you.
As i feel you should not use timer here, instead rely on web service completion and failure block to reinitiate the re-fetch of web service. If the response comes in failure block , then only retry.

Related

How to limit number of requests to a database

I have an app with a settings page where the settings of each user are stored in a MySQL database. I was wondering what is the best way to update the database for every setting the user changes while sending the minimal number of requests as I'm worried that it will crash if it sends too many( it has happened before).
I was thinking about setting a timer for ~5 seconds when the user first changes a setting, and then reset the timer to 5 seconds again if another setting is changed. Once that timer is finished it will send a request to the server to update all the settings at once. It would also constantly store the new values locally to the app, so if the user closes the app before the 5 seconds are up it will send the request once/if the app loads up again.
Is this viable/what's the best way to go about this?
You need to make some logic functions in your app, so i will try make an pseudo codes below. Hope it will give you an idea. I don`t know the MySQL details but i am trying to explain native Swift way.
First of all you should fetch data partly, I mean if you try to fetch all data at the same time your app can work very slow.. That is why we are doing pagination in the app.
As well as pagination you want to refresh the data fresh 5 seconds so i will use Timer object, which will trigger every 5 seconds the API function and catch data based on pagination. Check your below codes and implement step by step to your project. Happy Coding.
var timer: Timer?
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "loadNewDataAutomatically" with the interval of 5 seconds
timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.loadNewDataAutomatically), userInfo: nil, repeats: true)
}
#objc func loadNewDataAutomatically(_ pageNumber: Int, _ pageSize: Int, onSuccess: ((Bool) -> Void)?, onError: ((Error) -> Void)?){
// Call your api here
// Send true in onSuccess in case new data exists, sending false will disable pagination
// If page number is first, reset the list
if pageNumber == 1 { self.list = [YourDataModel]() }
// else append the data to list
self.list.append(apiResponseList)
// If Api responds with error
onError?(apiError)
// Else end success with flag true if more data available
let moreDataAvailable = !apiResponseList.isEmpty
onSuccess?(moreDataAvailable)
}
Assuming that the Database (MySQL) is on a server
You can try using WorkManager for this requirement.
When the user changes their settings, save them locally (which you are already doing)
enqueue a Unique Periodic Work Request using WorkManager & set up the time at what interval should the response be sent to the server.
Minimum time interval is around 15 min. but not guaranteed at exactly 15 minutes,
the system fires it when it seems fit, also according to the Constraints that you set on your Work Request.
You can also use a OneTimeWorkRequest, if you don't need periodic, with an Initial Delay of whatever time you need.
Edit: This question was later edited and ios, swift tags were added where previously it was for android.
If anyone comes here searching for something similar for Android, this would work.

Smart way to not kill WebService

It's my code :
for (FlightScopePlayerPlayer *player in self.selectedPlayers) {
BaseballPlayer_GetBaseballProfileRequest *re = [[BaseballPlayer_GetBaseballProfileRequest alloc]initWithPlayerID:player.ID];
[bseModel.myFlightScopeClient.baseballPlayerServiceClient GetBaseballProfile:re
onCompleted:^(id response) {
BaseballPlayer_GetBaseballProfileResponse *res = (BaseballPlayer_GetBaseballProfileResponse *)response;
self.player = res.player;
[self.baseballPlayerList addObject:self.player];
[DelegateHelper performProtocol:#protocol(LoadPlayersFromWSModelCallbackDelegate)
withTarget:self.delegates
withSelector:#selector(isResponseRedyToUseAfterGetProfile:)
withArgument:self];
This construction can kill my webservice. What is the smarter way to send this? The idea is that in array are objects and i want to send request for everyone but if in the array will be 1000 objects Webservice can die so my idea is send request when the previous is finished and send delegate at the end.
Ideally you'd have a different webservice.
Assuming you can't change, you need to queue your requests so you don't make too many at any one time and you can suspend processing if required. Potentially you can also retry failed requests by adding them back on to the end of the queue.
NSOperationQueue is appropriate for this. You can use NSBlockOperation to contain your processing logic to call the webservice. Be sure to set maxConcurrentOperationCount to some value between 1 and 4.
You can use RQOperation
Basically the idea is to store the web API calls in an array and then you can execute them in serial manner. (Once the previous is complete, execute the next one)
It has nice sample within. Hope that helps.
Not enough reps to post as comment, hence answer.

NSRunLoop makes app non-responsive sometimes

In my app, I need to make https calls to a restful web api and process the results upon return. The number of simultaneous service calls is never fixed, hence the related code has been written accordingly. The data fetched from the service is temporarily stored on an SQLite DB within the app. Following is the structure how it works.
When the user navigates to any screen or UI component thereof for which data needs to be fetched, the view controller calls a method on its designated model object. This method then checks whether the data is already present in the DB or it needs to be fetched. In case data is present, it returns the same to the view controller. Otherwise, it initiates an asynchronous service request and waits till the response comes, after which it returns the data to the VC. Therefore, the VC initialises a loading indicator before calling the specified model, and dismisses the same after control is returned from this function.
Here it is important that the function on the model waits till the response is received from the web api. This is done by registering for an NSNotification which will be issued by the service module once returned data is written to the DB. A boolean variable it set to false upon making the service request and set to true once the response is received. An NSRunLoop runs on the false condition of this boolean variable. Hence once the variable is set to true, the rest of the processing can continue.
Following are the relevant pieces of code in which all this is implemented:
[serviceModule initServiceCall:#"25" withDictionary:[NSDictionary dictionaryWithObjects:#[asOfDate] forKeys:#[#"toDate"]]];
dataReady=NO;
NSString *notificationName = #"dataReady";
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(useNotificationFromServiceModule:) name:notificationName object:nil];
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (!dataReady && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
The rest of the function continues after this.
This is the function that handles the notification:
-(void)useNotificationFromServiceModule:(NSNotification *)notification {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
dataReady=YES;
});}
The usual process is that once the notification is sent, the NSRunLoop quits and the rest of the method completes, returning to the view controller which then dismissed the loading indicator. The problem is that sometimes this does not happen. While the notification is issued (I can see the console log), the NSRunLoop does not end. The loading indicator continues to appear on the screen and stays that way until the screen is tapped once. When the screen is tapped, the NSRunLoop ends and the rest of the process continues randomly.
This does not happen always. It happens quite randomly, maybe about 4-5 times out of 10. Kindly provide some inputs/pointers to indicate why this may be happening.
If you are using the run loop directly, you are either very clever or very stupid. In the first case, you'll find the answer yourself. In the second case, it would be much much better if you followed the same pattern as everyone else does, which is running your networking code on a background thread and using dispatch_async when the results arrive.

iOS bonjour programming - wait for a response

I am pretty new to bonjour/networking with ObjC (although well versed in other areas!) I am asking for a bit of advice - I have an iOS app that will run on multiple iPads, in a store. The apps occasionally have to share some data, and the internet isn't always available so a webservice is not an option, hence I decided on using bonjour.
I have setup the Bonjour/NSNetservices and everything is functioning correctly, the ipads basically form an 'ad-hoc network' and connect automatically at app launch, however I am looking for advice for the following situation:
The app normally shares data in the background, without any user intervention - however there is one function where when a button is pressed on one app, data should be returned from another app remotely. The UI then updates when the data has been received from the other device - however if the connection should be lost to the other device, the data will never reach the users device, and the data will not be displayed. I am wanting to implement some form of timout, but unsure how to do this - any suggestions would be much appreciated!
The process flow for this is something like this:
button press on 'dev 1' > 'dev 1' broadcasts 'dev 2 please send data message' > 'dev 2' responds with requested data [timeout required here] > UI is updated if data is received /[if timeout fires, error message is displayed]
So I really just need a timeout for the last section - and I really cannot think of a way to implement it.
I can post code for this if required.
Thanks!
This solution may work if you have the possibility to manually cancel the request.
I believe you can use a simple NSTimer to cancel the request after a wait. I'm sure it's not the best solution, but it will probably work.
Here's how I would do it.
Create your timer and an NSInteger (to store the timer value) in your class :
NSTimer *timer;
NSInteger timerValue;
Call this method with timer = [self startTimer]; when you fire your request :
- (NSTimer*)startTimer {
timerValue = 30;
return [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTicked:) userInfo:nil repeats:YES];
}
Implement the timerTicked: method :
- (void)timerTicked:(NSTimer*)timer {
timerValue --;
if (timerValue <= 0) {
// Cancel the request
// Show an alert
}
}
You can cancel the timer with [timer invalidate]; but remember this will "destroy" your timer, so it won't fire events ever again.
As you've not indicated how you are currently requesting data from your other device, I have had to make some assumptions.
You can use NSURLRequest with a timeout using requestWithURL:cachePolicy:timeoutInterval:
See NSURLRequest documentation

QTP Recovery scenario used to "skip" consecutive FAILED steps with 0 timeout -- how can I restore original timeout value?

Suppose I use QTPs recovery scenario manager to set the playback synchronization timeout to 0. The handler would return with "continue with next statement".
I'd do that to make sure that any following playback statements don't waste their time waiting for the next non-existing/non-matching step before failing:
I have a lot of GUI tests that kind of get stuck because let's say if 10 controls are missing, their (consecutive) playback steps produce 10 timeout waits before failing. If the playback timeout is 30 seconds, I loose 10x30 seconds=5 minutes execution time while it really would be sufficient to wait for 30 seconds ONCE (because the app does not change anymore -- we waited a full timeout period already).
Now if I have 100 test cases (=action iterations), this possibly happens 100 times, wasting 500 minutes of my test exec time window.
That's why I come up with the idea of a recovery scenario function setting the timeout to 0 after/upon the first failed playback step. This would accelerate the speed while skipping the rightly-FAILED step, yet would not compromise the precision/reliability of identifying the next matching GUI context (which creates a PASSED step).
Then of course upon the next passed playback step, I would want to restore the original timeout value. How could I do that? This is my question.
One cannot define a recovery scenario function that is called for PASSED steps.
I am currently thinking about setting a method function for Reporter.ReportEvent, and "sniffing" for PASSED log entries there. I'd install that method function in the scenario recovery function which sets timeout to 0. Then, when the "sniffer" function senses a ReportEvent call with PASSED status during one of the following playback steps, I'd reset everything (i.e. restore the original timeout, and uninstall the method function). (I am 99% sure, however, that .Click and .Set methods do not call ReportEvent to write their result status...so this option might probably not work.)
Better ideas? This really bugs me.
It sounds to me like you tests aren't designed correctly, if you fail to find an object why do you continue?
One possible (non recovery scenario) solution would be to use RegisterUserFunc to override the methods you are using in order to do an obj.Exist(0) before running the required method.
Function MyClick(obj)
If obj.Exist(1) Then
obj.Click
Else
Reporter.ReportEvent micFail, "Click failed, no object", "Object does not exist"
End If
End Function
RegisterUserFunc "Link", "Click", "MyClick"
RegisterUserFunc "WebButton", "Click", "MyClick"
''# etc
If you have many controls of which some may be missing and you know that after 10 seconds you mentioned (when the first timeout occurs), nothing more will show up, then you can use the exists method with a timeout parameter.
Something like this:
timeout = 10
For Each control in controls
If control.exists(timeout) Then
do something with the control
Else
timeout = 0
End If
Next
Now only the first timeout will be 10 seconds. Each and every subsequent timeout in your collection of controls will have the timeout set to 0 which will save your time.

Resources