How to tell if TWRequest performRequestWithHandler failed or succeeded - ios

I am posting a request and checking for errors like so:
// Send request out on a background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
NSLog(#"Tweet Successful");
}else {
NSLog(#"Tweet Failed");
if (responseData) {
// Use the NSJSONSerialization class to parse the returned JSON
NSError *jsonError;
NSArray *requestResponse =
[NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingMutableLeaves
error:&jsonError];
if (requestResponse) {
NSLog(#"%#",requestResponse);
} else {
NSLog(#"%#",jsonError);
}
}
}
}];
});
It seems to work fine, so far as the request is concerned. My intention was to queue the request on failure and try again at a later date, or not depending on the error and how many attempts had failed thus far.
The problem I have is that I find it frequently appears to fail, with error 34 "Page not Found" where in fact the request did succeed, was posted as intended and can be seen clear as day in twitter stream.
Now, if I can't trust the error code returned then I can't really go on and keep retrying. How do I know for sure if it succeeds or fails?
RECENT OBSERVATIONS:
Recently, I tried to post a photo from Apple photo app directly and it sent me an alert that read "Tweet may not have been successful". That is funny, because that is basically what I was coming to as my only option in this case. This makes me think that even Apple had to acknowledge that there is not way to tell for sure that post failed when the return does not confirm it.

according to every example found in researching this problem, none of which use any other API than what you are using, and including this example from twitter's own documentation for posting a photo to the API, none of them check the urlResponse code first.
the most that ever seems to be done is this example from twitter's documentation where a GET is performed and then the response is processed. notice that it checks the responseData first, and if it exists, it is simply treated like success. only if there is no responseData is there an attempt to bother looking at the error. the example does not bother with the urlResponse … and neither does any other example i saw in 10 minutes of googling.
(finally, i doubt this matters, or it may be because you cleaned up the example for the display, but you're processing the error on the main-queue when you are not performing any UI. you could do the processing in the handler immediately, and pass along whatever post-processing you do to whatever UI you are trying to display it with. i doubt post-processing the response in the main-queue as opposed to the queue of the handler (as shown in both examples cited here and all other examples i've seen) is really causing a problem, but i wonder if it might cut down on the incidence of false negatives you are seeing. at the very least, it will make your response and any UI display of the response cleaner and more efficient.)

Related

CKModifyRecordsOperation modifyRecordsCompletionBlock not being called

I'm using CKModifyRecordsOperation to save a set of records and if I have internet connection all works well and completion block is being called. But when I don't have connection the completion block is not being called and I don't get any information that my operations failed.
I'm using the following code in completion block
modifyOperations.modifyRecordsCompletionBlock = ^(NSArray *savedRecords, NSArray *deletedRecordIDs, NSError *error)
{
if(error){
NSLog(#"Error: %#", error.localizedDescription);
}
item.creatorRecordId = record.recordID;
};
and then I'm performing operation using
[self.publicDB addOperation:modifyOperations];
Any ideas how can I get an information if the operation failed for example in the case where there is no internet connection?
CloudKit operations have their qualityOfService property set to NSQualityOfServiceUtility by default.
Operations that use NSQualityOfServiceUtility or NSQualityOfServiceBackground may be marked as using discretionary network requests. The system can hold discretionary network requests if network connectivity is poor, so you might not get a response from the server until conditions improve and the system sends the request.
If you'd like your request to be sent immediately in all cases, set CKOperation.qualityOfService to NSQualityOfServiceUserInitiated or NSQualityOfServiceUserInteractive.

Request not sent

I'm having a weird problem when i consume my API from my app. Sometimes, for no reason, the request is just not sent, and it fails at the end of the time-out with the following error:
Error Domain=NSURLErrorDomain Code=-1001 "The request timed out."
I have tried many API such as NSURLConnection delegates, NSURLSession and NSURLConnection.sendSynchronousRequest without success.
Here is a sample project i have made to highlight the issue. ConnectionBugApp
Here are the steps to reproduce:
Run the app in Xcode and stop debug just so the app is on your phone
Open the app, click Test Connection (it succeeds, loading wheel stops spinning right after)
Go to other apps like facebook/twitter/network games (somes that are a bit heavy) and switch to airplane mode a few times
Go back to my app and click Test Connection (loading wheel never stops)
A few details that might help:
If I use my server IP instead of my domain name, it succeeds
Issue only appears when on the LTE/4G network
Any ideas or workaround would be greatly appreciated ! Feel free to ask for more details.
Thanks
EDIT
I've edited the description a lot since i first posted it (hoping to make it cleaner and clearer), i'm sorry if some answers or comment don't really make sense anymore.
I have come across this issue when using an asynchronous request. It seems as though iOS limits the number of open connections to a single domain, such that all subsequent connections fail in the manner you have described.
If connections typically complete quickly, this possibly won't be an issue.
The solution is to limit the number of open connections to the same domain to prevent this from happening.
The answer posted by karlos works because the synchronisity of the connection blocks others from being opened.
Like mentioned in comments, I had DNSSEC (cache poisoning protection) enabled on my hosting service.
Disabling it, fixed the issue, even though that might not be a really nice solution. After a few weeks of searching, that'll be good enough.
I'll give the bounty to someone that can explain it, or who can provide a better solution.
In your code your request take default timeout is 60s, but you can change Request time out in your code as below.
in your NetworkItem class change time out.
init(request:NSMutableURLRequest){
self.request = request
self.request.timeoutInterval = 120
super.init()
}
Try the following code for the connection.This would help you.
let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
if error != nil || urlData!.length == 0
{
println("Error happend timeout======\(error?.code)!")
continue
}
else //else1
{if let httpResponse = response as? NSHTTPURLResponse
{
println("Status Code for successful---\(httpResponse.statusCode)")
// For example 502 is for Bad Gateway
if (httpResponse.statusCode == 502)
{
// Check the response code from your server and break
break
}
else
{
//Your code
}
}
}
You can get the list of HTTPS status code in the following link
StatusCode

How return data from an HTTP request in Swift/Objective C

I'm trying to use Coinbase's API to get information about my online bitcoin wallet, and I'm trying to use Swift's NSURLSession object to do so. Perhaps I'm missing something obvious in the Apple docs, but after reading through the information about NSURLSession and NSURLSessionTask I still do not understand how to make an HTTP request and then return the body of the response so that the body can persist throughout the life of my app. As of now I only see the ability to use completion blocks which return void, or delegates which either return void themselves or use completion blocks which also return void. I want to use the data I get from the response later in the app, but because I'm using completion blocks I must handle the response data immediately after the response arrives.
To make it clear, I want to do something along the lines of the pseudocode function below:
func makeHTTPCall(urlString : String) -> String? {
create NSURLSession object
create request with proper headers and using the passed-in urlString
use the session object to send out the request
get the response object, extract the response body as a string, and return it
}
Then later, I could call something like this:
let myObject : MyObject = MyObject()
let respData : String = myObject.makeHTTPCall("https://coinbase.com/api/v1/account/balance")
This data is returning a JSON Object string, which is the String I want to persist beyond the life of the response and its completion block. How can I do this in either Swift or Objective C, since I'll be able to use either in Xcode 6?
EDIT: Two answers have been posted, but they miss the fundamental point of this question. I need to RETURN the data which I receive from the response. Both answers (and all other answers I've seen on SO) simply print the data received. I would like code that doesn't use a void-returning completion handler, but instead returns the data so that it can be used later in the lifecycle of the app. If there is anything unclear about my question, please tell me, though I don't see how this can be made clearer.
In the edit to your question, you say:
I need to RETURN the data which I receive from the response. Both answers (and all other answers I've seen on SO) simply print the data received. I would like code that doesn't use a void-returning completion handler, but instead returns the data so that it can be used later in the lifecycle of the app. If there is anything unclear about my question, please tell me, though I don't see how this can be made clearer.
I understand the appeal of this strategy, because it feels so intuitively logical. The problem is that your networking requests should always run asynchronously (e.g. use that completion handler pattern to which you allude).
While there are techniques making a function "wait" for the asynchronous request to complete (i.e. to make the asynchronous NSURLSession method behave synchronously or use one of the old synchronous network request methods), this is a really bad idea for a number of reasons:
If you do this from the main thread, it results in a horrible user experience (the app will be unresponsive while the request is in progress and the user won't know if the app is busy doing something or whether it's frozen for some unknown reason).
Again, if you do this from the main thread, you also risk having the iOS "watch dog" process kill your app (because if you block the main queue for more than a few seconds at the wrong time, particularly as the app comes to foreground, the OS will unceremoniously terminate your app). See Technical Q&A #1693 for a discussion on the problems of doing synchronous network requests.
We generally prefer the asynchronous network techniques because they offer more features unavailable with synchronous techniques (e.g. making requests cancelable, offer progress updates when using delegate-based network requests, etc.).
You really should use the completion handler pattern that those other questions suggest, and manage the changing state of the app in those handlers. In those situations where you absolutely cannot let the user proceed until some network request is done (e.g. you can't let the user buy something until you confirm their bitcoin balance, and you can't do that until they log in), then change the UI to indicate that such a request is in progress. For example, dim the UI, disable the controls, pop up an activity indicator view (a.k.a., a "spinner"), etc. Only then would you initiate the request. And upon completion of the request, you would restore the UI. I know it seems like a lot, but it's the right way to do it when the user absolutely cannot proceed until the prior request is done.
I'd also think long and hard as to whether it's truly the case that you absolutely have to force the user to wait for the prior network request to complete. Sometimes there are situations where you can let the user do/review something else while the network request is in progress. Yes, sometimes that isn't possible, but if you can find those sorts of opportunities in your app, you'll end up with a more elegant UX.
I know that problem and use this code for synchronous requests:
func synchronousRequest() -> NSDictionary {
//creating the request
let url: NSURL! = NSURL(string: "exampledomain/...")
var request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
var error: NSError?
var response: NSURLResponse?
let urlData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
error = nil
let resultDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(urlData!, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
return resultDictionary
}
What you are asking for is a synchronous network request. There are many ways to do this, such as...
NSData's init(contentsOfURL aURL: NSURL!)
NSURLConnection's synchronous request method
...etc.
These methods will block the current thread until they complete - which can be a potentially long time. Network requests can have very high timeouts, it may be several minutes before the device gives up. NSData's init with contents of URL will return NSData, not void, and does not execute asynchronously. It will block until it is complete, which is why it's recommended to not do these types of requests from the main thread. The UI will be frozen until it completes.
In general the use of synchronous networking methods is discouraged. Asynchronous network requests are greatly preferred for a number of reasons. Using an asynchronous method that takes a completion block as a parameter will not prevent you from using the returned data elsewhere in your application. The block is executed when the network request has finished (wether it succeeds or fails) and it is passed the data, response metadata, and error. You are free to do what you want with that data - nothing prevents you from persisting it, passing it off to another object, etc. Based on your comments it sounds like you want to take the data that was the result of the network request and set it as the value of a property somewhere - that is entirely doable using an asynchronous method that uses a block as a completion handler.
In objective-C you can use __block and get the data when the operation finishes:
__block NSData *myData;
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:urlString]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
myData = data;
}] resume];

To know date and time settings in device from an iPhone application

I want to know whether the user has set their device to automatically set the date and time in the Settings.app in iPhone. I need this information because I want to make user to access some informations in the app only for a day, which he can access again only on second day. But an smart user can change the date and access the information. So I want to check whether they set the date/time automatically for the device or not.
Is there any way for doing so. Please suggest any other alternate way if exist.
Thanks in advance.
I don't believe there exists a way to detect the user's preference regarding the automatic update of the date time settings. To determine the length of time from a given point, you have a few options none of which are absolute or without any error.
Use NSDate, which you already know can be circumvented by manually changing the date on the device.
Use a time server. By querying a time server you can get an independent date time value. Store that value and compare against it to determine if the right amount of time has passed. The drawback of course, is this requires an internet connection to function property.
Use a system clock value or series of values to roughly calculate the time elapsed. A functions like CACurrentMediaTime or mach_absolute_time() can provide a count to compare against. You can take the values and increment them until you have reached a specified duration to reset a flag for your second day check.
None of the options alone will provide an ideal solution, but by combining the approaches, you might achieve an acceptable level of assurance the user is accessing the information only during the time allowed.
This is an alternate suggestion, I had similar problem for a conference app I was developing. I wanted to show some data to user only in the second day of conference.
After discussing getting date option from device with our team. We decided not to use this approach because as you said some user may change date settings and also some user may be reluctantly set their device to other countries time and date zones where there may be date conflict issues for users.
Finally we decided to create a simple web page and get http 200 and http 404 request for that info. You dont have to put your data or whatever to the server, we just wanted to get the response code from web.
So lets say you have a webpage http://xxxxxxxxxxxxxx.pdf or .html we dont put any pdf files to server so user always get http 404 response in that case we disable or hide the related data/button/row whatever. In the second day of conference we put a dummy page so now user can get http 200 response, so we enable button/row/data.
if you decide to use this approach download block for nsurlconnection from here
then use following code:
NSURL *theFileURL=[NSURL URLWithString:#"yoururl"];
NSURLRequest *request = [NSURLRequest requestWithURL:theFileURL];
[URLConnection asyncConnectionWithRequest:request
completionBlock:^(NSData *data, NSURLResponse *response) {
//get data and response
if ([response isKindOfClass:[NSHTTPURLResponse class]])
{
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *) response;
//NSString *fileMIMEType = [[httpResponse MIMEType] lowercaseString];
NSLog(#"httpResponse.statusCode %i",httpResponse.statusCode);
if (httpResponse.statusCode==200) {
//correct respond show data
}
else if (httpResponse.statusCode==404)
{
//no file this
NSLog(#"no file at the server");
}
}
//[HUD removeFromSuperview];
} errorBlock:^(NSError *error) {
//get error
NSLog(#"error %#",error);
} uploadPorgressBlock:^(float progress) {
//Upload progress (0..1)
} downloadProgressBlock:^(float progress) {
//Download progress (0.1)
//progress += 0.01;
//HUD.progress = progress;
}];

Facebook api batch request

Now I've got simple request to Facebook api:
me/feed?fields=type,story,caption,message,picture,comments&limit=20&offset=0
It works fine, but in the comments section, there are message, name and user id who post this comment, but I also need a photo-link. Of course I can send more requests to get user-photo. But I think it would be silly decision. Maybe there is way to send fql or batch request to get all fields I need in one request?
P.S. I already know how to do it with ruby, but i need some example which I can use in ios app.
Edit: Also I've got simple example, which do a little more detailed listing, but also without photo. What I should add to request2, to get photo?
JSFacebookRequest *request1 = [JSFacebookRequest requestWithGraphPath:#"me/feed? fields=story,message,comments&offset=15&limit=5"];
[request1 setName:#"get-field"];
JSFacebookRequest *request2 = [JSFacebookRequest requestWithGraphPath:#"?ids={result=get-field:$.data..id}"];
[facebook fetchRequests:[NSArray arrayWithObjects:request1, request2, nil] onSuccess:^(NSArray *responseObjects) {
NSLog(#"Responses:\n%#", responseObjects);
} onError:^(NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
}];
Perhaps I'm misunderstanding your situation, but it seems to me that you're over-complicating things. If you've got the user id, you can build the needed URL like so:
https://graph.facebook.com/<user_id>/picture
If that doesn't give you the desired image size, there are other options listed here. (See the 'images' parameter, for example.)

Resources