Setting Property Variables in Class Methods - ios

Currently, I understand that I cannot set Property Variables within a class method.
For Example:
#ISUser.h
#interface ISUser : NSObject
#property (nonatomic, retain) NSString *username;
#property (nonatomic, retain) NSString *password;
#property (nonatomic, retain) NSString *email;
#property (nonatomic, retain) NSString *firstname;
#property (nonatomic, retain) NSString *lastname;
+ (void)logInWithUsernameInBackground:(NSString *)username
password:(NSString *)password
block:(ISUserResultBlock)block;
#end
I am looking through Parse's framework and am trying to get a better understanding of how to implement a login like they have done. The class method (void)logInWithUsernameInBackground:password:block is where I attempt to assign the property variables username and password but it's a no go.
Here is the implementation of the current method:
+ (void)logInWithUsernameInBackground:(NSString *)username password:(NSString *)password block:(ISUserResultBlock)block
{
//self.username = username // Of course, I cannot do this
NSString *preferredLanguageCodes = [[NSLocale preferredLanguages] componentsJoinedByString:#", "];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#%#", kAPIHost, kAPIPath]]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:[NSString stringWithFormat:#"%#, en-us;q=0.8", preferredLanguageCodes] forHTTPHeaderField:#"Accept-Language"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSData * data = [[NSString stringWithFormat: #"command=login&username=%#&password=%#", username, password] dataUsingEncoding: NSUTF8StringEncoding];
[request setHTTPBody:data];
ConnectionBlock *connection = [[ConnectionBlock alloc] initWithRequest:request];
[connection executeRequestOnSuccess: ^(NSHTTPURLResponse *response, NSString *bodyString, NSError *error) {
block([self user], error);
} failure:^(NSHTTPURLResponse *response, NSString *bodyString, NSError *error) {
block([self user], error);
}];
}
Within the parse PFUser.h file, this is a class method... But how do they assign the property variables?
I know that static variables can be assigned/set within a class method but I would like to access such variables from another class.
EDIT: After viewing the first comment, The ISUser class has a singleton already implemented.
+ (instancetype)currentUser
{
static ISUser *sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
But now what? I need to override the init method and set the variables? But how does the init method know what to set the variables to? Will I have to add parameters to the + (instancetype)currentUser like so + (instancetype)currentUser:username password:(NSString *)password then override the init method as well? + (instancetype)currentUser is another class method that I pulled from the PFUser framework.

You can not set properties in class method. You can do it on instance methods. Thats because properties are for instances of the class.
In the parse login method they use some properties as method parameters and use them for the login process, but not manipulate them.
Hope it helps

Related

Objective C UITextField values blank

I am trying to get the values from a few UITextFields I have added to my storyboard and send them as a JSON string to an API. I can hard code the values for the JSON string and everything works fine. Now I want to retrieve the values from the text fields and insert them in place of the hard coded values. The problem is that when I log the values to the console they are showing up as blank. I am not sure that I have the code correct to get the values from the ViewController to the method that sends the data. I followed several tutorials on how to get the data from the ViewController UITextFields. I connected the text fields to the properties in the ViewController.h file. I am hoping someone can help me figure out what I did wrong, hopefully I provided enough information.
I think the problem may be how I am trying to get the values from these lines of code in the timeMethods.m file:
ViewController *controller = [[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
NSString *name = controller.name.text;
NSString *type = controller.type.text;
NSString *date = controller.date.text;
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (nonatomic, retain) IBOutlet UITextField *name;
#property (nonatomic, retain) IBOutlet UITextField *type;
#property (nonatomic, retain) IBOutlet UITextField *date;
#end
ViewController.m
#import "ViewController.h"
#import "timeMethods.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize name;
#synthesize type;
#synthesize date;
- (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)sendRequest:(id)sender {
[[timeMethods alloc] sendRequest];
}
- (IBAction)getRequest:(id)sender {
[[timeMethods alloc] getRequest];
}
#end
timeMethods.m
#import "timeMethods.h"
#implementation timeMethods
- (void)sendRequest {
ViewController *controller = [[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
NSString *name = controller.name.text;
NSString *type = controller.type.text;
NSString *date = controller.date.text;
NSURL *url = [NSURL URLWithString:#"http://throttle.com/my-rest-api/api/robots"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSDictionary *tmp = [[NSDictionary alloc] initWithObjectsAndKeys:
name, #"name",
type, #"type",
date, #"year",
nil];
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:tmp options:0 error:&error];
[request setHTTPBody:postData];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[postData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: postData];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
NSString *strData = [[NSString alloc]initWithData:postData encoding:NSUTF8StringEncoding];
NSLog(#"%#", strData);
}
- (void)getRequest {
NSString *serverAddress = #"http://throttle.com/my-rest-api/api/robots";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverAddress]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
[request setHTTPMethod:#"GET"];
NSError *requestError;
NSURLResponse *urlResponse = nil;
NSData *response1 = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
NSString *strData = [[NSString alloc]initWithData:response1 encoding:NSUTF8StringEncoding];
NSLog(#"%#",strData);
}
#end
timeMethods.h
#import <Foundation/Foundation.h>
#interface timeMethods : NSObject
- (void)sendRequest;
- (void)getRequest;
#end
The trouble is that we've just allocated a brand new VC and asked for its UITextField's values here:
ViewController *controller = [[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
// controller.name is a brand new text field, created in the previous line
NSString *name = controller.name.text; // guaranteed to be #""
Of course this will be an empty text field. It was created in the previous line. Instead, get those values as params to your request from the view controller the user is using...
- (IBAction)sendRequest:(id)sender {
NSString *name = self.name.text;
NSString *type = self.type.text;
NSString *date = self.date.text;
[[timeMethods alloc] sendRequestWithName:name type:type date:date];
}
Naturally, add those parameters to the sendRequest method and remove the local vars.

ASIHTTPRequest login method and reuse creds

I am using ASIHTTPRequest to login to my app and have created a bool method to login and thats working great:
-(BOOL)User:(NSString *)user password:(NSString *)password
{
NSURL *url = [NSURL URLWithString:url];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUseSessionPersistence:YES];
[request setUseKeychainPersistence:NO];
[request setUsername:user];
[request setPassword:password];
[request setDomain:domain];
[request startSynchronous];
NSError *loginError = [request error];
if(loginError == nil){
return true;
}else{
return false;
}
}
but now I am trying to use these creds outside this method. I read the documentation and it appears I can use these creds via:
//Should reuse our username and password
request = [ASIHTTPRequest requestWithURL:url];
but that would throw an error because request would not be defined. How would I do this?
Effective range of the request variable only in the method. You should also define new variable in the outside of the method.
Like below:
//Should reuse our username and password
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
As the documentation says:
If useSessionPersistence is turned on (it is by default), ASIHTTPRequest stores credentials in memory and can re-use them for subsequent requests.
This means that your credentials will be stored in sessionCredentialsStore (and sessionProxyCredentialsStore if you're using a proxy). Unless you overwrite them, they will be automatically applied to all subsequent requests. Both of these variables are static global variables.
So, just make a request as normal, and the credentials will be applied to the next request as long as you don't modify the credentials store.
I am not very clear about what you are asking but as far as I understand your concern is about extracting user credentials after the request has been fired. Is that correct?
I think the ideal way to do this is to never let go of the request in the first place (OR simply cache using Keychain). So basically hold this request variable as an instance variable(ivar) of a particular object (potentially a singleton manager type class that handles login tasks). Example
// LoginManager.h
#import Foundation;
#interface LoginManager : NSObject
#property (nonatomic, readonly, strong) ASIHTTPRequest *request;
#end
// LoginManager.m
#import "LoginManager.h"
#interface LoginManager ()
#property (nonatomic, readwrite, strong) ASIHTTPRequest *request;
#end
#implementation LoginManager
+ (instancetype)sharedManager
{
static LoginManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(BOOL)User:(NSString *)user password:(NSString *)password
{
NSURL *url = [NSURL URLWithString:url];
self.request = [ASIHTTPRequest requestWithURL:url];
[self.request setUseSessionPersistence:YES];
[self.request setUseKeychainPersistence:NO];
[self.request setUsername:user];
[self.request setPassword:password];
[self.request setDomain:domain];
[self.request startSynchronous];
NSError *loginError = [request error];
if(loginError == nil)
{
return true;
}
else
{
return false;
}
}
#end
Access it via:
ASIHTTPRequest *request = [[LoginManager sharedManager] request]
NSLog(#"%#", request.username);
NSLog(#"%#", request.password);

The correct way asynchronously call an object?

I have hobbled together one of my first objects. The goal of the object is to send a text message, which is does. However I'm calling it from the 2nd UIViewController within ViewDidLoad, and its still hanging within the Segue transition. So I know I need to get it asynchronously, but reading some other threads they implied that the proper way to go around it is to make it an "AppDelegate Object", so I would assume I would need to call the object from the AppDelegate, but I'm not really sure about how to go about that as I have not really worked with that in some tutorials I'm doing, and on top of that, is that the correct way to go about using my object?
initializing the object from my view controller
Twilio *twilio = [[Twilio alloc] init];
[twilio sendMessage: self.phoneNumber: [self getRandomNumberBetween:1000 to:9999]];
Header file
#import <Foundation/Foundation.h>
#interface Twilio : NSObject
#property (strong, nonatomic) NSString *TwilioSID;
#property (strong, nonatomic) NSString *TwilioSecret;
#property (strong, nonatomic) NSString *FromNumber;
#property (strong, nonatomic) NSString *ToNumber;
#property (strong, nonatomic) NSString *Message;
-(id)init;
-(id)sendMessage:(NSString *)phoneNumber :(NSString *)message;
#end
Implementation file
#import "Twilio.h"
#implementation Twilio
-(id)init {
self = [super init];
if(self) {
// Twilio Common constants
self.TwilioSID = #"A....3";
self.TwilioSecret = #"e...8";
self.FromNumber = #"5...2";
self.ToNumber = nil;
self.Message = nil;
}
return self;
}
-(id)sendMessage:(NSString *)phoneNumber :(NSString *)message
{
NSLog(#"Sending request.");
self.ToNumber = phoneNumber;
self.Message = message;
// Build request
NSString *urlString = [NSString stringWithFormat:#"https://%#:%##api.twilio.com/2010-04-01/Accounts/%#/SMS/Messages", self.TwilioSID, self.TwilioSecret, self.TwilioSID];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
// Set up the body
NSString *bodyString = [NSString stringWithFormat:#"From=%#&To=%#&Body=%#", self.FromNumber, self.ToNumber, self.Message];
NSData *data = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:data];
NSError *error;
NSURLResponse *response;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// Handle the received data
if (error) {
NSLog(#"Error: %#", error);
} else {
NSString *receivedString = [[NSString alloc]initWithData:receivedData encoding:NSUTF8StringEncoding];
NSLog(#"Request sent. %#", receivedString);
}
return self.Message;
}
#end
Updated
With recommendation below I changed my object implementation like so:
//NSError *error;
//NSURLResponse *response;
//NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[NSURLConnection sendAsynchronousRequest:request queue:self.queue completionHandler:^(NSURLResponse *response, NSData *data, NSError
*error) {
// Handle the received data
if (error) {
NSLog(#"Error: %#", error);
} else {
NSString *receivedString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Request sent. %#", receivedString);
}
NSLog(#"%#",response);
}];
Use method sendAsynchronousRequest:queue:completionHandler:
See it : https://stackoverflow.com/a/9270711/2828120

How to add test friends to Facebook test users programmatically using Facebook iOS SDK 3.14.1

We need that all our test users to be friends of each other. Doing that through the App Dashboard manually is a tremendous amount of work depending on the number of test users you need (in our case more than 50 test users).
Therefore we are looking for a way to make our Facebook test users friends of each other programmatically. We tried this approach following their website here: https://developers.facebook.com/docs/graph-api/reference/v2.0/test-user/friends
The problem is that in order to send a friend request from test user one to test user two you have to be logged in with test user one, and in order to accept the friend request you need to login with test user two, which makes the process even worse than adding manually using the App Dashboard -> Roles
How can we make all our test users friend of each other programmatically using iOS SDK 3.14.1?
If you create your users programmatically you can easily make them friends with one another.
https://developers.facebook.com/docs/graph-api/reference/v2.1/test-user/friends
#import "FBGraphObject.h"
#protocol FBTestGraphUser <FBGraphObject>
#property (nonatomic, readonly) NSString *id;
#property (nonatomic, readonly) NSString *access_token;
#property (nonatomic, readonly) NSString *login_url;
#property (nonatomic, readonly) NSString *email;
#property (nonatomic, readonly) NSString *password;
#property (nonatomic, retain) NSArray *friends;
#end
-(id<FBTestGraphUser>)createTestFacebook
{
NSString *appName = "";
NSString *userPrefix = [NSString stringWithFormat:#"%#User", appName];
NSString *facebookApplicationId = "";
NSString *facebookApplicationAccessToken = "";
NSString *url = [NSString stringWithFormat:#"https://graph.facebook.com/%#/accounts/test-users?installed=true&name=%#&locale=en_US&permissions=email,user_birthday,publish_actions&access_token=%#", facebookApplicationId, userPrefix, facebookApplicationAccessToken];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
return (id<FBTestGraphUser>)[FBGraphObject graphObjectWrappingDictionary:[data objectFromJSONData]];
}
-(void)deleteTestFacebookUser:(id<FBTestGraphUser>)testFacebookUser
{
NSLog(#"Deleting Facebook users...");
NSMutableArray* existingUsers = [NSMutableArray arrayWithArray:testFacebookUser.friends];
[existingUsers addObject:testFacebookUser];
NSOperationQueue* wipeUsers = [NSOperationQueue new];
[existingUsers enumerateObjectsUsingBlock:^(id<FBTestGraphUser> user, NSUInteger idx, BOOL *stop) {
[wipeUsers addOperationWithBlock:^{
[self deleteTestFacebookUser:user];
}];
}];
[wipeUsers waitUntilAllOperationsAreFinished];
NSLog(#"Done deleting Facebook users");
}
-(void)makeUser:(id<FBTestGraphUser>)userA friendsWithUser:(id<FBTestGraphUser>)userB {
// Don't try to parallelize this; you'll get unknown OAuth exceptions. -CS 2013-11-18
[self sendFriendRequestFrom:userA toUser:userB];
[self sendFriendRequestFrom:userB toUser:userA];
}
-(void)sendFriendRequestFrom:(id<FBTestGraphUser>)sender toUser:(id<FBTestGraphUser>)receiver {
NSString *url = [NSString stringWithFormat:#"https://graph.facebook.com/%#/friends/%#?access_token=%#", sender.id, receiver.id, sender.access_token];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
NSURLResponse *response = nil;
NSError *error = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}
-(void)deleteTestFacebookUser:(id<FBTestGraphUser>)testFacebookUser
{
NSString *url = [NSString stringWithFormat:#"https://graph.facebook.com/%#?access_token=%#", testFacebookUser.id, WBTestCaseFacebookAppID];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"DELETE"];
NSError *error = nil;
NSURLResponse *response = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}
It would be easiest to do this with a web server.
E.g. Using the facebook-node-sdk:
Create user
FB.api('/v2.6/{app-id}/accounts/test-users', 'post', { fields: [{ installed: "true", permissions: "user_birthday user_friends email"}] }, function (res) { ... });
Save newly created user id and access_token
Repeat steps 1-2 as desired
Send friend request from userA to userB
FB.api('/v2.6/' + userA.fb_id + '/friends/' + userB.fb_id, { access_token: userA.access_token }, 'post', function (res) { ... });
Send friend request from userB to userA to accept
FB.api('/v2.6/' + userB.fb_id + '/friends/' + userA.fb_id, { access_token: userB.access_token }, 'post', function (res) { ... });
C.f. Connecting friend edges

NSMutableData - unrecognized selector sent to instance

I am trying to get data from response.
I'm using NSURLConnectionDelegate, NSURLConnectionDataDelegate.
The project uses ARC.
#interface MainMenu()
#property (nonatomic, unsafe_unretained) NSMutableData* wpData;
#end
#implementation
-(void)sendRequest{
NSURL* url = [[NSURL alloc] initWithString:#"http://smthing"];
NSMutableURLRequest* request = [[NSMutableURLRequest alloc] init];
NSString* reqBody = #"Block";
NSData* reqData = [reqBody dataUsingEncoding:NSUTF8StringEncoding];
[request setURL:url];
[request setHTTPBody:reqData];
[request setHTTPMethod:#"POST"];
self.wpData = [NSMutableData data];
NSURLConnection* conection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conection start];
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
//Here iI have [__NSMallocBlock__ appendData:]: unrecognized selector sent to instance 0x95f3fb0
[self.wpData setLength:0];
}
#end
Maybe you can found my mistake
Thanks :)
Your data pointer is an unsafe_unretained property,
#property (nonatomic, unsafe_unretained) NSMutableData* wpData;
and you are assigning it a autoreleased instance,
self.wpData = [NSMutableData data]; //Returns autoreleased object
Since you are making asynchronous download request you require to maintain the data object.
You never know when the autorelease pool will be flushed and the unretained object will go out of scope. In such situations you should retain the autoreleased object. Change the property to strong and allocate the data object,
#property (nonatomic, strong) NSMutableData* wpData;
//...
self.wpData = [[NSMutableData alloc] init]; //Better practice
Hope that helps!

Resources