I am trying to set up a very simple app, that takes in a number from an input field, and then POSTS using an NSMutableURLRequest to a Google Spreadsheet, using the action URL from the Google Form.
So far I have a simple text box and button, and this code. The field name I am using is:
name="entry.931001663"
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *inputField;
- (IBAction)submit:(id)sender;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)submit:(id)sender {
NSString *bodyData = self.inputField.text;
NSLog(#"Input = %#", bodyData);
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"https://docs.google.com/forms/d/1rGpFfI2ebyn_SbuDVVUg7Q4yuvKzd3RRXb0vRxeIDxc/formResponse"]];
[postRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[postRequest setHTTPMethod:#"POST"];
[postRequest setHTTPBody:[NSData dataWithBytes:[bodyData UTF8String] length:strlen([bodyData UTF8String])]];
}
#end
I am not sure about the error, but Google's Sample Code can help you. You can also use GData Objective C Client to use it.
Related
I have a browser webgame ported to iOS using UIWebView (storyboard, not xib). The app is already published and works relatively well. My challenge now is to implement GameCenter properly so I can get the local player's playerID and then have a trustable unique id of the user. That's the only feature I really need from GameCenter.
The GameCenter functionality is actually implemented and working well, but for a reason I still couldn't understand I can't make the setAuthenticateHandler, located in a different class file, to access the UIebView so I can load an URL passing the playerId (among other parameters) via POST.
The WebViewController.m has a method who loads an URL in the UIWebView. The setAuthenticateHandler do call this method, but the UIWebView is null and of course it's not possible to load the URL. If I call the method inside WebViewController.m the URL is loaded correctly.
I was using ARC and then changed to MRC according to https://stackoverflow.com/a/14613361/3063226, but the behavior didn't change, the UIWebView is still null when GameCenter setAuthenticateHandler calls the Load_URL method.
Any help is very welcome. I have tried and researched a LOT for days before coming here to ask a question, I'm stuck!
GCHelper.m
- (void)authenticateLocalUser {
if (!gameCenterAvailable) return;
NSLog(#"Authenticating local user...");
if (![GKLocalPlayer localPlayer].isAuthenticated) {
[[GKLocalPlayer localPlayer] setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {
if (!error && [GKLocalPlayer localPlayer].playerID != nil)
{
NSLog(#"Player logged in GameCenter!");
NSLog([NSString stringWithFormat: #"1111 (GCHelper) playerID: [%#]", [GKLocalPlayer localPlayer].playerID);
// --
WebViewController *yourClassInstance = [[WebViewController alloc] init];
[yourClassInstance Load_URL:true]; // this is not working as expected!!
// -
}
else
{
NSLog(error.localizedDescription);
}
})];
} else {
NSLog(#"Already authenticated!");
}
}
WebViewController.h
#import <UIKit/UIKit.h>
#interface WebViewController : UIViewController <UIWebViewDelegate>
-(void) Load_URL:(BOOL)Nativa;
#property (strong, nonatomic) IBOutlet UIWebView *PagPrincipal;
#end
WebViewController.m
#import "WebViewController.h"
#import "NSData+AESCrypt.h"
#import "NSString+AESCrypt.h"
#import "NSString+URL_Encode.h"
#import "GCHelper.h"
#interface WebViewController ()
-(void) Load_URL:(BOOL)Nativa;
#end
-(void) Load_URL :(BOOL)Nativa
{
NSURL *url = [NSURL URLWithString: #"http://www.webgameurl.com"];
NSString *body = [NSString stringWithFormat: #"iac=%#", [self URL_Codigo :Nativa :vip_aux]];
NSMutableURLRequest *requestObj = [[NSMutableURLRequest alloc]initWithURL: url];
[requestObj setHTTPMethod: #"POST"];
[requestObj setHTTPBody: [body dataUsingEncoding: NSUTF8StringEncoding]];
// HERE!! This returns null when called from GCHelper.m, but is ok when called from WebViewController.m !!
NSLog([NSString stringWithFormat:#"Webview >>> >>> >>> [%#]", _PagPrincipal.description]);
_PagPrincipal.delegate = self;
[_PagPrincipal loadRequest:requestObj];
This is it. The _PagPrincipal is the UIWebView itself, it's a storyboard. I'm not an iOS Objective-C specialist, so for sure there are things I just don't master. Using Xcode 6.1.1, the app is designed for iOS8+. Testing in a real iPad Mini 1 (non-retina) and iPhone 5.
I have a UITableView with 2 text field and a button. If I run the simulator without use custom class, I can see the text fields and button:
But when i use a custom class, my UITable view only display a lot of lines without content:
Here is how I've created my properties:
LoginSceneController.h
#import <UIKit/UIKit.h>
#interface LoginSceneController : UITableViewController
#property (nonatomic, strong) IBOutlet UITextField *email;
#property (nonatomic, strong) IBOutlet UITextField *password;
- (IBAction)doLogin;
#end
LoginSceneController.m
#import "LoginSceneController.h"
#interface LoginSceneController ()
#end
#implementation LoginSceneController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)doLogin {
NSURL *url = [NSURL URLWithString:#"http://rest-service.guides.spring.io/greeting"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSDictionary *greeting = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
self.email.text = [[greeting objectForKey:#"id"] stringValue];
self.password.text = [greeting objectForKey:#"content"];
}
}];
}
#end
The problem happens when I use a custom class (or referencing outlet or add a send event on button).
What is wrong?
edit: I think that I need populate my interface using my custom class because the static content is being lost. Is it possible to be the cause of content being lost?
You have two options when it comes to UITableView and Interface Builder. You can have a dynamic table view (pretty common) where your code overrides UITableViewController methods like "numberOfRowsInSection" and "cellForRowAtIndexPath". The other option is a static tableview, and that seems like what you want to do (especially since you haven't overridden the two aforementioned methods, and leads to your blank table). My guess is you need to select "static" for the tableview as shown in the third screenshot in this tutorial.
i'm trying to make a subclass of NSURLConnection where i have an additional property (in this case "connectionName") to help me distinguish between 2 different connections.
i created the subclass, named it CustomURLConnection and gave it the property "connectionName".
then in my file ImagesViewController.m (which is an UICollectionView) i import the header CustomURLConnection and try to give the connections a name and retrieve it afterwards, but it doesn't work, as soon as i enter this collection view the app crashes and gives me the following error:
-[NSURLConnection setConnectionName:]: unrecognized selector sent to instance 0x1090a40f0
Here is some code: (if you want, here's a CLEARER IMAGE)
CustomURLConnection.h
#import <Foundation/Foundation.h>
#interface CustomURLConnection : NSURLConnection
#property (strong, nonatomic) NSString *connectionName;
#end
ImagesViewController.h
#import <UIKit/UIKit.h>
#interface ImagesViewController : UICollectionViewController<NSURLConnectionDelegate>
#property (strong, nonatomic) UIImageView *imageView;
#end
ImagesViewController.m
...
#import "CustomURLConnection.h"
#interface ImagesViewController (){
NSArray *contentStrings;
NSMutableData *contentData; // Holds data from the initial load
NSMutableData *contentImageData; // Holds data when the user scrolls up/down in the collection view
}
#end
...
-(void)loadInitialData{ // Loads data from page
NSString *hostStr = #"http://www.website.com/example";
NSURL *dataURL = [NSURL URLWithString:hostStr];
NSURLRequest *request = [NSURLRequest requestWithURL:dataURL];
CustomURLConnection *connectionData = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self]; // Make connection
connectionData.connectionName = #"InitialData"; // Give it a name
}
...
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
// Do some stuff
NSString *hostStr = #"http://www.website.com/example2";
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,100,100)];
[imageCell addSubview:_imageView]; // Adds an image view to each collection cell
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:hostStr]];
CustomURLConnection *connectionImg = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self]; // Make connection
connectionImg.connectionName = #"ImageData"; // Give it a different name than before
// Do some stuff
return imageCell;
}
...
// Here are the main methods for the connections
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
if([((CustomURLConnection *)connection).connectionName isEqualToString:#"InitialData"]){
contentData = [[NSMutableData alloc] init];
}
else{
contentImageData = [[NSMutableData alloc] init];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if([((CustomURLConnection *)connection).connectionName isEqualToString:#"InitialData"]){
[contentData appendData:data];
}
else{
[contentImageData appendData:data];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if([((CustomURLConnection *)connection).connectionName isEqualToString:#"InitialData"]){
// Do some stuff
}
else{
UIImage *image = [[UIImage alloc] initWithData:contentImageData];
_imageView.image = image;
}
}
am i missing something? i came across this error many times before but the causes are never the same and this time i can't seem to find a solution on my own.
hopefully you can see what i'm doing wrong and help me :)
thanks.
EDIT: turns out there is a better way to achieve my goal, have a look here
Thank again to everyone for the help :)
CustomURLConnection *connectionImg = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self]; // Make connection
creates an NSURLConnection object. Casting to CustomURLConnection does not change
the class of this object. Replace that line with
CustomURLConnection *connectionImg = [CustomURLConnection connectionWithRequest:request delegate:self]; // Make connection
to create an instance of your subclass.
In your delegate methods change NSURLConnection by CustomURLConnection, for instance :
- (void)connection:(CustomURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
and when you create it just do :
CustomURLConnection *connectionImg = [[CustomURLConnection alloc] initWithRequest:request delegate:self];
connectionImg.connectionName = #"ImageData"; // Give it a different name than before
In this line:
CustomURLConnection *connectionData = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self];
you are creating an instance of NSURLConnection, not CustomURLConnection. So, when you cast the result to CustomURLConnection * you are lying to the compiler.
Later, at runtime, when you try to use a feature of CustomURLConnection you get an exception because your connection is the wrong class type and doesn't implement the method.
You need to instantiate CustomURLConnection, not NSURLConnection.
Adding to the other good answers here, your CustomURLConnection class should override +connectionWithRequest:delegate: to return an instance of CustomURLConnection, like this:
+(CustomURLConnection*)connectionWithRequest:(NSURLRequest*)request delegate:(id)delegate
{
return [[CustomURLConnection alloc] initWithRequest:request delegate:delegate];
}
That lets you use the same style you had:
CustomURLConnection *connectionData = [CustomURLConnection connectionWithRequest:request delegate:self]; // Make connection
More importantly, a user of your code (most likely the future you) might assume that sending +connectionWithRequest:delegate: to CustomURLConnection would return an instance of CustomURLConnection. Without the override, they'll get an instance of NSURLConnection instead, and that's a difficult bug to spot.
I am very new to IOS. With the help of another managed to 'get' data from a third-party API and display in my table view, but now l want to write a number of strings from UIText fields, format based on the API docs and write to the server. The existing code that l have uses blocks. Can anyone provide a very simple example?
Below serves as a rough stab in the dark at decomposing what would be required:
Class 1 - VC class:
//User input from UIText field
I am assuming that l need to write the strings to a NSDictionary- object and key pairs, and using a block?
Call a method which resides in Class 2, which will request the data from the block in Class 1?
Class 2 - Model class:
//Format the data as per the API dictates
//'PUT' the string to the server
Here is what l have thus far:
URLOUTViewController.h Class
//
// URLOUTViewController.h
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "URLOUT_Model.h"
#interface URLOUTViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *firstName;
#property (strong, nonatomic) IBOutlet UITextField *secondName;
- (IBAction)submitButton:(id)sender;
#end
URLOUTViewController.m
//
// URLOUTViewController.m
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import "URLOUTViewController.h"
#import "URLOUT_Model.h"
#interface URLOUTViewController ()
//Private properties
#property (strong, nonatomic) URLOUT_Model *modelNewDroplet;
#property (strong, nonatomic) NSDictionary *userInput;
#end
#implementation URLOUTViewController
#synthesize userInput;
#synthesize firstName;
#synthesize secondName;
- (URLOUT_Model *) modelNewDroplet
{
if (!_modelNewDroplet) _modelNewDroplet = [[URLOUT_Model alloc]init];
return _modelNewDroplet;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[URLOUT_Model createDropletWithCompletion:^(NSDictionary *userInput) {
self.modelNewDroplet = userInput;
}];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)submitButton:(id)sender {
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:
self.firstName.text, #"firstName", self.secondName.text, #"secondname",nil];
NSLog(#"%#", [dic objectForKey:#"firstName, secondName"]);
}
#end
URLOUT_Model.h
//
// URLOUT_Model.h
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "URLOUTViewController.h"
typedef void (^NSDictionayBlock)(NSDictionary * params);
#interface URLOUT_Model : NSObject
+ (void)createDropletWithCompletion:(NSDictionary *) params;
#end
URLOUT_Model.m
//
// URLOUT_Model.m
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import "URLOUT_Model.h"
#implementation URLOUT_Model
+ (void)createDropletWithCompletion:(NSDictionary *) params;
{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:#{params: firstName, secondName, nil}];
[urlRequest setHTTPMethod:#"PUT"];
[urlRequest setCachePolicy:NSURLCacheStorageNotAllowed];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *error){
NSError *serializationError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingAllowFragments
error:&serializationError];
}];
}
#end
Creating a HTTP PUT request is simple:
Create an instance of NSMutableURLRequest.
Set the HTTP method to #"PUT" using the -setHTTPMethod: method.
Set the header fields and body using appropriate methods (-setValue:forHTTPHeaderField:, -setHTTPBody:, etc.)
Send the request using an instance of NSURLConnection.
Where the data that you send in the request comes from (text fields, data files, random number generator...) really doesn't make a difference in the creation of the request. You really shouldn't be storing data in your views, though. Generally speaking, your text views and such should trigger an action in a view controller when they change, and the view controller should update the data model. When you want to send data to some server, pull the data from the model, not from UI components.
I want to display a website on an iOS app using a UiWebView. Some components of the site (namely the webservice results loaded using AJAX calls) should be replaced by local data.
Consider the following example:
text.txt:
foo
page1.html:
<html><head>
<title>test</title>
<script type="text/javascript" src="jquery.js"></script>
</head>
<body>
<div id="target"></div>
<script type="text/javascript">
function init(){
$.get("text.txt",function(text){
$("#target").text(text);
});
}
$(init);
</script>
</body></html>
View Controller:
#interface ViewController : UIViewController <UIWebViewDelegate>
#property (nonatomic,assign) IBOutlet UIWebView *webview;
#end
#implementation ViewController
#synthesize webview;
//some stuff here
- (void)viewDidLoad
{
[super viewDidLoad];
[NSURLProtocol registerClass:[MyProtocol class]];
NSString *url = #"http://remote-url/page1.html";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData];
[webview loadRequest:request];
}
#end
MyProtocol:
#interface MyProtocol : NSURLProtocol
#end
#implementation MyProtocol
+ (BOOL) canInitWithRequest:(NSURLRequest *)req{
NSLog(#"%#",[[req URL] lastPathComponent]);
return [[[req URL] lastPathComponent] caseInsensitiveCompare:#"text.txt"] == NSOrderedSame;
}
+ (NSURLRequest*) canonicalRequestForRequest:(NSURLRequest *)req{
return req;
}
- (void) startLoading{
NSLog(#"Request for: %#",self.request.URL);
NSString *response_ns = #"bar";
NSData *data = [response_ns dataUsingEncoding:NSASCIIStringEncoding];
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[self.request URL] MIMEType:#"text/plain" expectedContentLength:[data length] textEncodingName:nil];
[[self client] URLProtocol: self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[[self client] URLProtocol:self didLoadData:data];
[[self client] URLProtocolDidFinishLoading:self];
[response release];
}
- (void) stopLoading{
NSLog(#"stopLoading");
}
#end
If I don't register my custom URLProtocol the page is displayed properly. If I do startLoading() is called, the content is loaded and stopLoading() is triggered afterwards. But on the UIWebView nothing happends at all. I tried to do some error-handling, but neither is a JS AJAX error thrown nor is didFailLoadWithError of the UIWebViewDelegate called.
I tried another scenario and created a HTML page that just loads an image:
<img src="image.png" />
and modified my URLProtocol to just handle the loading of the image - this works properly. Maybe this has anything to do with AJAX calls?
Do you have any idea what the problem might be?
Thanks in advance!
I had the same problem and finally solved it after days of hair pulling :
Your problem comes from the way you create the response, you have to create a status 200 response and force the WebView to allow cross-domain request if necessary :
NSDictionary *headers = #{#"Access-Control-Allow-Origin" : #"*", #"Access-Control-Allow-Headers" : #"Content-Type"};
NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:request.URL statusCode:200 HTTPVersion:#"1.1" headerFields:headers];
You can see my full working implementation in my answer here :
How to mock AJAX call with NSURLProtocol?
Hope this helps,
Vincent