Xcode: Validate a URL before Loading it - objective-c

Im having some trouble findig a way to validate a url on my app.
My intention is to load a URL and at the same time see if other webpage exist for example.
Load http://mysite.com/folder1/1.pdf
validate http://mysite.com/folder1/2.pdf
if folder1/2.pdf exists then load it, else validate /folder2/1.pdf
so far im loading the first pdf like this in order to be able to change the pdf number and the folder:
int numpag = 1;
NSString *baseUrl =#"http://www.cronica.com.mx/iphone/pdf_iphone/";
[pdfView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[baseUrl stringByAppendingFormat:#"folder1/%d.pdf", numpag]]]];
Thanks so much in advance!

how about this:
+ (BOOL)isValidURL:(NSURL*)url
{
NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSHTTPURLResponse *res = nil;
NSError *err = nil;
[NSURLConnection sendSynchronousRequest:req returningResponse:&res error:&err];
return err!=nil && [res statusCode]!=404;
}
let me know if it works for you!
(keep in mind that this is a synchronous request and should not be executed on the main thread)

I had to change the line:
return err!=nil && [res statusCode]!=404;
to
return err==nil && [res statusCode]!=404;
for the correct Bool return. The error should remain nil.

This approach is NOT correct, You should avoid Synchronous calls as they are blocking.
Apple says: simply try and wait down to wait for response.

Related

Retrieve All Recordings on Twilio Using Programable Voice SDK

I'm attempting to download all Recording objects associated with a Twilio account but do not see any methods in TwilioVoiceClient.h or VoiceClient.h that achieve this. I'm using the ProgramableVoice iOS SDK (beta 5), but the online documentation doesn't show any objective-c or Swift. I'm able to playback individual recordings with no problems if the Recording SID is known like so:
NSString *baseString = #"https://api.twilio.com/2010-04-01/Accounts/";
NSString *recordingSID = #"RExxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
NSURL *recordingUrl = [NSURL URLWithString:[NSString stringWithFormat:#"%#%#/Recordings/%#.mp3", baseString, kTwilAccount, recordingSID]];
avPlayer = [[AVPlayer alloc]initWithURL:recordingUrl];
What I'd like to do though, is download all associated recording objects for an account. For this, I've turned to NSUrlConnection:
NSString *baseStr = [NSString stringWithFormat:#"https://api.twilio.com/2010-04-01/Accounts/%#/Recordings.json", kTwilAccount];
NSURL *url = [NSURL URLWithString:baseStr];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:1000.0];
NSString *authStr = [NSString stringWithFormat:#"%#:%#", kTwilAccount, authToken];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:#"Base %#", [authData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
[theRequest setValue:authValue forHTTPHeaderField:#"Authorization"];
[NSURLConnection sendAsynchronousRequest:theRequest queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *responseCode, NSData *responseData, NSError *responseError) {
if ([responseData length] > 0 && responseError == nil){
// do work
} else if ([responseData length] == 0 && responseError == nil){
NSLog(#"data error: %#", responseError);
} else if (responseError != nil && responseError.code == NSURLErrorTimedOut){
NSLog(#"data timeout: %ld", (long)NSURLErrorTimedOut);
} else if (responseError != nil){
NSLog(#"data download error: %#", responseError);
}
}];
This results in a download error with console output:
data download error: Error Domain=NSURLErrorDomain Code=-1012 "(null)" UserInfo={NSErrorFailingURLStringKey=https://api.twilio.com/2010-04-01/Accounts/ACdee27262ef5fd27a593a697d80e7f7b0/Recordings.json, NSUnderlyingError=0x17084aa70 {Error Domain=kCFErrorDomainCFNetwork Code=-1012 "(null)" UserInfo={_kCFURLErrorAuthFailedResponseKey={url = https://api.twilio.com/2010-04-01/Accounts/ACdeedkfdjieireijrekjrkejrk4kj4/Recordings.json}}}, NSErrorFailingURLKey=https://api.twilio.com/2010-04-01/Accounts/ACdeedkfdjieireijrekjrkejrk4kj4/Recordings.json}
Obviously, it either doesn't recognize the endpoint or doesn't like the way I'm authenticating. How should I be requesting this information in objective-c?
Thanks for reading.
Twilio developer evangelist here.
We actually don't recommend that you put your account credentials into the application and make API calls from your application. It would be possible for a malicious attacker to decompile your application, retrieve your credentials and abuse your account.
We recommend instead that you implement a server side solution to make the call to the API and retrieve your Recordings. Then you can communicate with your server from the application.
Here's a blog post that explains more and includes an example for sending an SMS from an iOS application.

Addresponse - adding to memory only (XCode)

I'm trying to connect my project to a php file but every time this shows:
ADDRESPONSE - ADDING TO MEMORY ONLY
I have never seen it before.
this is the code i Use:
//Gets the php
FindURL = [NSString stringWithFormat:#"http://domain.com/Myfile.php?username=%#", Username.text];
// to execute php code
URLData = [NSData dataWithContentsOfURL:[NSURL URLWithString:FindURL]];
// to receive the returend value
DataResult = [[NSString alloc] initWithData:URLData encoding:NSUTF8StringEncoding];
NSLog(#"%#",DataResult);
What can i do?
Thanks in advance
Update code:
- (IBAction)UpdateList:(id)sender{
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://domain.com/Myfile.php"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:15.0];
NSURLConnection *Connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (Connection) {
//Connect
}else{
//Error
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
DataResult = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", DataResult);
}
I've had the same issue - check out the following, this might help you:
Log Messages I didn't asked for in Xcode 4.5 with iOS 6.0
I haven't found a solution so far, but since I now know that this Log is produced by the OS, I no longer look for its origin in my source :)
I got this log by making an NSURLRequest to an invalid URL. My app is supposed to show a UIAlertView when it catches an error on the server side, so I asked our system admin to take our entire site down so I can see what happens. He declined my request, so I typed random letters and put a .com at the end of the request URL.
Turns out we have an internal test server that doesn't seem to be accessible from my WiFi connection, so when I changed the URL to that instead, the log stopped appearing.

JSON Payload doesnt seem to be sending

My problem I'm pretty positive is simple, I must just be missing something.. just not sure what.
I can send GET and POST for granular elements (this=that kind of stuff), but a web service call I need to send data too, takes a raw JSON block, with no "key"
Heres the method I wrote:
-(NSData *)execute {
// Smart Chooser ?
if(PostData.count >0 || Payload != nil)
[self setMethod:UPLINK_METHOD_POST];
else
[self setMethod:UPLINK_METHOD_GET];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.connectionUrl
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
if([UPLINK_METHOD_GET isEqualToString:self.connectionMethod])
[request setHTTPMethod:#"GET"];
else
[request setHTTPMethod:#"POST"];
NSString *gData = [self compileGetData];
NSString *pData = [self compilePostData];
// if we have get data, set it into the URL string
if(GetData.count > 0) {
[self setURLWithString:[[self.connectionUrl absoluteString] stringByAppendingString:[#"?" stringByAppendingString:gData]]];
[request setURL:self.connectionUrl];
}
// if we have post data, set it in the body
if(PostData.count > 0) {
const char *bytes = [[NSString stringWithString:pData] UTF8String];
[request setHTTPBody:[NSData dataWithBytes:bytes length:strlen(bytes)]];
}
// Override any post data if a payload is already defined.
if(Payload != nil) {
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]];
}
NSLog(#"URL : %#", request.URL);
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if(err != nil)
NSLog(#"here was an error: %#", err);
return responseData;
}
-(NSDictionary *)executeAsJSON
{
NSData *responseData = [self execute];
NSError *e;
return [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:&e];
}
Ok SO, the way this thing works, is that it automatically sets whether the request is POST or GET depending on the data provided in the GetData, PostData, and Payload vars.
The request is GET by default, but turns into POST if PostData or Payload have anything in them.
The compileGetData and compilePostData mostly just bring back formatted strings with arrays of information combined, nothing special there.
But thats not where the problem is.
See, "Payload" overrides anything "PostData" had in it. If you had provided PostData elements into the class, it would just be overridden by a provided Payload if that does exist.
I needed to provide this to demonstrate the "workarea" as it exists right now, its not linearly provided information.
This is the area of interest:
// Override any post data if a payload is already defined.
if(Payload != nil) {
//const char *plbytes = [[NSString stringWithString:Payload] UTF8String]; // this didn't work
[request setHTTPBody:[Payload dataUsingEncoding:NSUTF8StringEncoding]]; // inline, doesn't work either
}
When I say "doesnt work", what I mean is, im getting back an error JSON array from the webservice that basically means "hey, wheres the payload?". If the request is not POST it comes back as a general error, so thats all working, the URL is then obviously correct.
I've used RESTConsole for Chrome to test the webservice to make sure its working properly, and it does.
I've also checked through the debugger the exact payload im sending, i copy+pasted that into RESTConsole, and it works there.
I'm.. honestly at a loss here...
Try using a web proxy like Charles or Wireshark (I personally preferr Charles due to it's ease of use, it's a 30-day trial though) and monitor the request you make from RESTConsole and the one you make from your app and see if they look the same.
Check any headers, line returns and anything else that looks different.
That's the best I can think of to start with

Constantly calling a method

So I have a method that checks for internet connection, but only during the -(id):init method. Can I set it up so that it constantly checks for connection? If it helps, here is the code:
- (id) checkConnected
{
NSError *error = nil;
NSString *URLString = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.google.com"] encoding:NSASCIIStringEncoding error:&error];
if (URLString != NULL)
{
connected = YES;
}
else connected = NO;
if(connected == YES)
NSLog(#"Connected");
else if (connected == NO)
NSLog(#"NotConnected");
return self;
}
While Reachability is a good first-pass check as others have suggested, it only tests the negative case: is it impossible to make a connection? If a firewall is blocking you, or the remote server is down, or any of a thousand other things happens, Reachability might tell you a system is in principle reachable (i.e. you have a network connection and the host if routeable) but the host is not in fact reachable.
So for some applications what you are asking is not unreasonable. The thing you have to be careful about is not to block your main thread with constant tests. Here is some code that will repeatedly run tests in the background:
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *req = [NSURLRequest requestWithURL:url];
__block NSHTTPURLResponse *response = nil;
__block NSError *error = nil;
dispatch_queue_t netQueue = dispatch_queue_create("com.mycompany.netQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(netQueue, ^{
while (! [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error]) {
NSLog(#"Connection failed.");
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Connection succeeded");
});
});
dispatch_release(netQueue);
Where "Connection succeeded" is logged you could instead write some main thread code that runs when a connection is successful. Note that I am passing in *response and *error from outside the block so they too will be available on your main thread inside or outside the block (assuming you keep them in scope) for your use.
You may want to throttle (i.e. just not use while()), but this is an implementation detail. Using NSTimer() as Richard suggested would work.
Finally, even with this code you still need to handle a potential failure of a subsequent connection. Just because it worked once doesn't mean the connection is available a millisecond later.

HTTP server works in Cocoa application but not test case -- run loop issue?

I'm trying to add a GHUnit test case to this SimpleHTTPServer example. The example include a Cocoa application that works fine for me. But I can't duplicate the behavior in a test case.
Here is the test class:
#import <GHUnit/GHUnit.h>
#import "SimpleHTTPServer.h"
#interface ServerTest : GHTestCase
{
SimpleHTTPServer *server;
}
#end
#implementation ServerTest
-(void)setUpClass
{
[[NSRunLoop currentRunLoop] run];
}
- (NSString*)requestToURL:(NSString*)urlString error:(NSError**)error
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:1];
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error];
NSString *page = nil;
if (error == nil)
{
NSStringEncoding responseEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)[response textEncodingName]));
page = [[NSString alloc] initWithData:data encoding:responseEncoding];
[page autorelease];
}
return page;
}
- (void)testPortReuse
{
unsigned int port = 50001;
NSError *error = nil;
NSString *path, *url;
server = [[SimpleHTTPServer alloc] initWithTCPPort:port delegate:self];
sleep(10);
path = #"/x/y/z";
url = [NSString stringWithFormat:#"http://localhost:%u%#", port, path];
[self requestToURL:url error:&error];
GHAssertNil(error, #"%# : %#", url, error);
[server release];
}
- (void)processURL:(NSURL *)path connection:(SimpleHTTPConnection *)connection
{
NSLog(#"processURL");
}
- (void)stopProcessing
{
NSLog(#"stopProcessing");
}
#end
I've tried sending requests via NSURLRequest and also (during the sleep) via a web browser. The delegate methods -processURL and -stopProcessing are never called. The problem seems to be that [fileHandle acceptConnectionInBackgroundAndNotify] in SimpleHTTPServer -initWithTCPPort:delegate: is not causing any NSFileHandleConnectionAcceptedNotifications to reach the NSNotificationCenter -- so I suspect a problem involving run loops.
The problem seems to be with the NSFileHandle, not the NSNotificationCenter, because when [nc postNotificationName:NSFileHandleConnectionAcceptedNotification object:nil] is added to the end of initWithTCPPort:delegate:, the NSNotificationCenter does get the notification.
if (error == nil)
That should be:
if (data != nil)
error here is the passed-in pointer to an NSError* - it will only be nil if the caller passed nil instead of a reference to an NSError* object, which isn't what your -testPortReuse method does.
It would also be incorrect to dereference it (as in if (*error == nil)), because error arguments are not guaranteed to be set to nil upon error. The return value indicates an error condition, and the value returned in the error argument is only meaningful or reliable if there is an error. Always check the return value to determine if an error happened, then check the error parameter for details only if something did in fact go wrong.
In other words, as it's written above, your -requestToURL:error: method is incapable of handling success. Much like Charlie Sheen. :-)