I am making an iPhone app that will need to communicate with the Sendy API. I believe that it uses some kind of JSON, but I'm not really sure, nor do I know where to start. I'm particularly interested in the subscribe portion of the API. Basically, I need to know how to talk to the Sendy API from my app.
Any help is appreciated.
My code:
- (IBAction)submitButtonPressed:(id)sender
{
self.data = [[NSMutableData alloc] initWithLength:0];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.erwaysoftware.com/sendy/subscribe"]];
[request setHTTPMethod:#"POST"];
[request setValue:#"john#test.com" forHTTPHeaderField:#"email"];
[request setValue:#"john" forHTTPHeaderField:#"name"];
[request setValue:#"LxxxxxxxxxxxxxxxxxxxxQ" forHTTPHeaderField:#"list"];
[request setValue:#"true" forHTTPHeaderField:#"boolean"];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
[conn start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.data setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)d {
[self.data appendData:d];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(#"Error", #"")
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:NSLocalizedString(#"OK", #"")
otherButtonTitles:nil] show];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseText = [[NSString alloc] initWithData:self.data encoding:NSUTF8StringEncoding];
// Do anything you want with it
NSLog(#"%#", responseText);
}
When the log happens, the string is empty. I know through breakpoints that the last method is called.
Looking at the API it's all just plain text response.
Since it's a POST you can use an NSURLConnection to compose the request. See this question for information on formatting the response.
An alternative is to use something like AFNetworking or RestKit that might be a little more friendly if you're doing more work with APIs.
I'm guessing you've already resolved this but in case anyone else gets stuck here (as I did) I thought I'd post what I did to get it to work.
The first thing you need to do is create a category called NSString+URLEncoding (or whatever) which is going to take your email and name fields from blah#blah.com and turn it into blah%40blah.com. I modified this from the handy blog post found here
#interface NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding;
#end
#import "NSString+URLEncoding.h"
#implementation NSString (URLEncoding)
-(NSString *)urlEncodeUsingEncoding:(NSStringEncoding)encoding {
return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)self,
NULL,
(CFStringRef)#"!*'\"();:#&=+$,/?%#[]% ",
CFStringConvertNSStringEncodingToEncoding(encoding)));}
#end
Ok so now just import NSString+URLEncoding.h and add the following code and you'll be in business. This post helped me with this part
- (IBAction)submitButtonPressed:(id)sender
{
NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:#"http://stashdapp.com/sendy/subscribe"]];
[newRequest setHTTPMethod:#"POST"];
[newRequest setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
NSString *email = #"name#domain.com";
NSString *name = #"First Lastname";
NSString *list = #"XXXXXXXXXXXXXXXXXX";
NSString *postData = [NSString stringWithFormat:#"email=%#&boolean=true&name=%#&list=%#", [email urlEncodeUsingEncoding:NSUTF8StringEncoding],[name urlEncodeUsingEncoding:NSUTF8StringEncoding],list];
[newRequest setHTTPBody:[postData dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:newRequest delegate:self];
[conn start];
}
You still include the delegate methods which you quoted in your question.
Hope it helps someone!
Related
I have a little problem with my app. I want to send some http request asynchronously to server. I create this method:
- (void)sendHTTPRequest:(NSString *)urlString type:(NSString *)type idNegozio:(NSNumber *)idNegozio {
self.negozi = [[NSMutableArray alloc] init];
NSData *jsonData;
NSString *jsonString;
if ([type isEqualToString:#"shops"]) {
self.reqNeg = YES;
self.reqApp = NO;
...
jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:0 error:nil];
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
else if ([type isEqualToString:#"appointments"])
{
[self.loadingIconApp startAnimating];
self.reqNeg = NO;
self.reqApp = YES;
...
jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:0 error:nil];
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *requestString = [NSString stringWithFormat:urlString];
NSURL *url = [NSURL URLWithString:requestString];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody: jsonData];
NSURLConnection * conn = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
[conn start];
}
and I use this methods for connection:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (self.reqNeg == YES) {
//here use the responseData for my first http request
}
if (self.reqApp == YES) {
//here use the responseData for second http request
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
}
but in this way only the first connection works and I can use the responseData. While, If I try to send other http request the method connectionDidFinishLoading doesn't work and other methods too.
Anyone have an idea??
If you want to use the async request one by one you can do that:
- (void)request1 {
NSString *requestString = #"your url here";
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc]initWithURL:[NSURL URLWithString: requestString]]
queue:queue
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode <300) {
// call the request2 here which is similar to request 1
// your request2 method here
}
}];
}
hope this help you~ thank you~
Your code looks good to me. Here are my ideas:
Are you sure your second NSURLConnection is being created and sent out?
Maybe it's never being sent.
Are you calling your sendHTTPRequest:type:idNegozio: method with a different type while your second connection is still sent out?
You don't have a check at the beginning of the send function to make sure you're not already sending out a connection. Maybe your flags are being switched mid-connection.
The if statements in your didFinish method should probably be combined with an else. Just in case you wanted to fire off an 'app' connection after handling a 'neg' connection you don't accidentally fall through and try to handle the response twice.
Also, you don't have to explicitly call 'start' on an NSURLConnection unless you pass NO to the startImmediately: parameter in the constructor. That shouldn't cause a problem though.
Just a quick one how do i detect if a login failed here is my code:
- (IBAction)btnTimetable:(id)sender {
NSString *user = _txtUsername.text;
NSString *pass = _txtPassword.text;
NSString *content = [NSString stringWithFormat:#"username=%#&password=%#", user, pass];
NSData *data =[content dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postlenght=[NSString stringWithFormat:#"%lu",(unsigned long)[data length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://moodle.thomroth.ac.uk/login/index.php?mode=login"]];
[request setHTTPMethod:#"POST"];
[request setValue:postlenght forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:data];
//NSError *error=nil;
//NSURLResponse *response=nil;
//NSData *result=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[_webView loadRequest:request];
[self performSelector:#selector(parseTimetable) withObject:nil afterDelay:3.0];
}
i dont even know where to start on this one is there a delegate method to detect such actions ?
As stated in the official Developer forums, UIWebView does not support authentication challenges in iOS. Please read here (requires developer account): UIWebView does not directly support authentication challenges
sendSynchronousRequest:returningResponse:error: should return nil and the status code of the returned response should equal 401 (Unauthorized):
[(NSHTTPURLRequest*)response statusCode] == 401
I would guess, the error parameter should be set to a corresponding error (please check this through printing it to the console).
If you use the delegate approach of NSURLConnection the situation is different:
When NSURLConnection receives a 401 (for example, the connection requires credentials for authentication, or a previous authentication attempt failed), it does invoke the delegate
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
if implemented, otherwise it invokes these (now considered obsolete methods):
- (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
if implemented.
The official documentation provided more information: NSURLConnectionDelegate Protocol Reference.
You can cancel the authentication request, if you decide to do so. As a result, the connection can fail.
If the connection fails, connection:didFailWithError will be invoked.
If you really want to do it using UIWebView you could use it's delegate method and parse code answer of your website.
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *webSource = [_web stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"];
}
I am using the Yelp Search API to basically just get a list of businesses for a search query.
It is pretty much a NSURLConnection is OAuth, but here is the code to initialize the request:
NSURL *URL = [NSURL URLWithString:appDelegate.yelpAdvancedURLString];
OAConsumer *consumer = [[OAConsumer alloc] initWithKey:#"this-is-my-key" secret:#"this-is-my-secret"];
OAToken *token = [[OAToken alloc] initWithKey:#"this-is-my-key" secret:#"this-is-my-secret"];
id<OASignatureProviding, NSObject> provider = [[OAHMAC_SHA1SignatureProvider alloc] init];
NSString *realm = nil;
OAMutableURLRequest *request = [[OAMutableURLRequest alloc] initWithURL:URL
consumer:consumer
token:token
realm:realm
signatureProvider:provider];
[request prepare];
responseData = [[NSMutableData alloc] init];
yelpConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
Then here:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error: %#, %#", [error localizedDescription], [error localizedFailureReason]);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Oops." message: #"Something screwed up. Please search again." delegate: nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == self.yelpConnection) {
[self setYelpString];
}
}
When I run this on iPhone, everything is working fine. However, when I run on iPad, the connection gets timed out. The following is from this line
NSLog(#"Error: %#, %#", [error localizedDescription], [error localizedFailureReason]);
Error: The request timed out., (null)
Also if I use a synchronous request, it seems to work using this:
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSDictionary* JSON = [NSJSONSerialization
JSONObjectWithData:result
options:kNilOptions
error:&error];
However, I want to avoid using synchronous as it freezes the app.
Is this Yelp API specific? Or am I just doing something wrong? Thanks in advance, I would appreciate any help.
If it helps, it times out approximately 10 seconds after sending the request.
create this type of NSMutableURLRequest :
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:240.0];
I think the best approach is to change the init method in http://oauth.googlecode.com from
- (id)initWithURL:(NSURL *)aUrl
consumer:(OAConsumer *)aConsumer
token:(OAToken *)aToken
realm:(NSString *)aRealm
signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
{
if (self = [super initWithURL:aUrl
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:10.0])
{
...
}
}
to
- (id)initWithURL:(NSURL *)aUrl
cachePolicy:(NSURLRequestCachePolicy)cachePolicy
timeoutInterval:(NSTimeInterval)timeoutInterval
consumer:(OAConsumer *)aConsumer
token:(OAToken *)aToken
realm:(NSString *)aRealm
signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
{
if (self = [super initWithURL:aUrl
cachePolicy:cachePolicy
timeoutInterval:timeoutInterval])
{
...
}
and then check again, whether the timeout value which you specify will be honored by the connection.
I am trying create a login screen which sends login info sharepoint server and i expect to be able to successfully login.
There are plenty of old examples and libraries which I am not able to use. But after spending hours I found this link to have the crux of all
http://transoceanic.blogspot.com/2011/10/objective-c-basic-http-authorization.html
My code looks like this now:
- (void) startLogin {
NSURL *url = [NSURL URLWithString:#"http://site-url.com"];
NSString *loginString =(NSMutableString*)[NSString stringWithFormat:#"%#:%#",usernameTextField.text,passwordTextField.text];
NSData *encodedLoginData=[loginString dataUsingEncoding:NSASCIIStringEncoding];
NSString *authHeader=[NSString stringWithFormat:#"Basic %#", [encodedLoginData base64Encoding]];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:3.0];
// [request setValue:authHeader forKey:#"Authorization"];
[request setValue:authHeader forHTTPHeaderField:#"Authorization"];
[request setHTTPMethod:#"POST"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
There are three issues:
the commented out line-code doesn't give any error but it crashes on that line(while debugging)
on [request setValue:authHeader forHTTPHeaderField:#"Authorization"]; i am getting error "No visible interface for NSURLRequest declares selector setHTTPHeaderField"
Also, I am getting warning - unused variable "connection" in last line. I am not sure how this whole thing works and any simple example or correction is appreciated.
I would also like to know if there are any other simple methods for basic auth.
UPDATE: Delegate methods
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
// Access has failed two times...
if ([challenge previousFailureCount] > 1)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Authentication Error"
message:#"Too many unsuccessul login attempts."
delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
else
{
// Answer the challenge
NSURLCredential *cred = [[NSURLCredential alloc] initWithUser:#"admin" password:#"password"
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Connection success.");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"Connection failure.");
}
Change NSURLRequest to NSMutableURLRequest to access it's setValue:forHTTPHeaderField method, and add a HOST header as well if it's a shared web host.
At the end, you have to start the connection:
[connection start];
Also, make sure you've set up your NSURLConnectionDelegate delegate methods for the call backs.
I have 2 separate NSURLConnection.
NSURLConnection * connection_users;
NSURLConnection * connection_cards;
Then i created the data with parameters, etc. and I finish with:
connection_users = [[NSURLConnection alloc] initWithRequest: url_request_users delegate: self startImmediately: YES];
In the delegate method:
- (void) connection: (NSURLConnection *) connection didReceiveData: (NSData *) data
i Checked if the connection is for the connection_users:
if (connection == connection_users) / / do something as an example:
NSDictionary * json_response = [NSJSONSerialization JSONObjectWithData: data options: kNilOptions error: & error];
Use the "data" that came from the method.
Before closing the "if" I create the next connection to "connection_cards", doing the same things
Out of "if" but within the same method I do another "if" to "connection_cards" and do the same thing with JSONObjectWithData.
Only the "data" that comes from the method is always of the first connection.
What is happening differently? For the second connection was initiated then you should receive the "data" corresponding.
Already canceled the first connection before starting the second to see if solved, but no.
How to obtain the "data" correct for second connection?
PS: if you need more codes, please let me know.
EDITED:
As Wain ask
url_request_users = [[NSMutableURLRequest alloc] init];
NSMutableString *post_users = [[NSMutableString alloc] init];
[post_users appendFormat:#"%#", [NSString stringWithFormat:#"email=%#&senha=%#",
[[alert textFieldAtIndex:0] text],
senha_md5]];
[url_request_users setURL:[NSURL URLWithString:WBS_USERS_RECOVER]];
[url_request_users addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[url_request_users setHTTPMethod:#"POST"];
[url_request_users setHTTPBody:[post_users dataUsingEncoding:NSUTF8StringEncoding]];
connection_users = [[NSURLConnection alloc] initWithRequest:url_request_users delegate:self startImmediately:YES];
For n different connections you will need n different NSMutableData which contains result of related NSURLConnection. A basic example for your question;
NSMutableData *data_users;
NSMutableData *data_cards;
Than on your didRecieveData;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (connection == connection_users) {
[data_users appendData:data];
} else if ( connection == connection_cards) {
[data_cards appendData:data];
}
}
This way you can keep track of your data's and connection's seperately. Remember to clear leftovers for your datas when your connection is over
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == connection_users) {
// use data from data_users
NSDictionary * json_response = [NSJSONSerialization JSONObjectWithData:[data_users copy] options: kNilOptions error: & error];
data_users = [[NSMutableData alloc] init]; // clear data users
}
// do the same for cards connection
}
Last thing to do is to allocate your data before you call this function;
url_request_users = [[NSMutableURLRequest alloc] init];
NSMutableString *post_users = [[NSMutableString alloc] init];
[post_users appendFormat:#"%#", [NSString stringWithFormat:#"email=%#&senha=%#",
[[alert textFieldAtIndex:0] text],
senha_md5]];
[url_request_users setURL:[NSURL URLWithString:WBS_USERS_RECOVER]];
[url_request_users addValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[url_request_users setHTTPMethod:#"POST"];
[url_request_users setHTTPBody:[post_users dataUsingEncoding:NSUTF8StringEncoding]];
data_users = [[NSMutableData alloc] init]; // add this line in your code
connection_users = [[NSURLConnection alloc] initWithRequest:url_request_users delegate:self startImmediately:YES];