I am using ASIHTTPRequest framework for making network calls in my iOS applications. But I don't want to use it directly in all the controllers in my application. So I thought of writing a layer around ASIHTTPRequest. My code use this layer to use ASIHTTPRequest. The benefit wil be in future I should be able to replace this framework with some other framework and my code will be unchanged just the layer would change. I want to know what should be the strategy to do so. Should I subclass my class from ASIHTTPRequest class, or implement my own class. What should be the method I should consider implementing.
Currently I am implementing it like this.
My wrapper is
MyRequestHandler.h : NSObject
#property ASIHTTPRequest *asiHttpReq;
-(void) sendAsyncGetRequest
{
self.asiRequest = [ASIHTTPRequest requestWithURL:self.url];
if(self.tag != 0){
self.asiRequest.tag = self.tag;
}
[self.asiRequest setDelegate:self];
[self.asiRequest startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request{
MyResponseObj *respone = <From request obj>
if([delegate respondsToSelector:#selector(requestFinished:)]){
[delegate performSelector:#selector(requestFinished:) withObject:response];
}
}
And in my viewcontroller I would do this:
MyViewController.h : UIViewContoller
#property MyRequestHandler *reqHandler;
-(void) fireRequest
{
NSString* requestUrl = <create URL>;
if(requestUrl){
// [self showLoadingIndicatorView];
// Proceed for request.
NSURL *url = [NSURL URLWithString:requestUrl];
reqHandler = [MyRequestHandler requestWithURL:url];
reqHandler.tag = 1000;
[reqHandler setDelegate:self];
[reqHandler sendAsyncGetRequest];
}
}
- (void)requestFinished:(MyResponse*) responseData{
// Do Your parsing n all here.
}
- (void)requestFailed:(MyResponse*) responseData{
// Handle the error here.
}
Is this the right way to do it. The problem here is as I have created property of myrequesthandler in viewcontroller I can only make one request at a time, and loosing the capability of ASIHTTPRequest of making multiple request simultaneously.
Can you suggest me how to approach problems like this.
This is what I'm using:
#import "ASIFormDataRequest.h"
#interface RequestPerformer : NSObject {
id localCopy; // to avoid exec_bad_access with arc
ASIHTTPRequest *getRequest;
ASIFormDataRequest *postRequest;
}
#property (nonatomic, retain) id delegate;
#property (nonatomic, readwrite) SEL callback;
#property (nonatomic, readwrite) SEL errorCallback;
- (void)performGetRequestWithString:(NSString *)string stringDictionary:(NSDictionary *)stringDictionary delegate:(id)requestDelegate requestSelector:(SEL)requestSelector errorSelector:(SEL)errorSelector;
- (void)performPostRequestWithString:(NSString *)string stringDictionary:(NSDictionary *)stringDictionary dataDictionary:(NSDictionary *)dataDictionary delegate:(id)requestDelegate requestSelector:(SEL)requestSelector errorSelector:(SEL)errorSelector;
#end
//
#import "RequestPerformer.h"
#import "ASIHTTPRequest.h"
#import "ASIFormDataRequest.h"
#implementation RequestPerformer
#synthesize delegate;
#synthesize callback, errorCallback;
- (void)performGetRequestWithString:(NSString *)string stringDictionary:(NSDictionary *)stringDictionary delegate:(id)requestDelegate requestSelector:(SEL)requestSelector errorSelector:(SEL)errorSelector {
localCopy = self;
self.delegate = requestDelegate;
self.callback = requestSelector;
self.errorCallback = errorSelector;
NSMutableString *requestStringData = [[NSMutableString alloc] init];
if (stringDictionary)
for (NSString *key in [stringDictionary allKeys])
[requestStringData appendFormat:#"%#=%#&", key, [stringDictionary objectForKey:key]];
NSString *resultString = [requestStringData substringToIndex:[requestStringData length]-1];
getRequest = [[ASIFormDataRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#?%#", string, [resultString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];
[getRequest setDelegate:self];
[getRequest setRequestMethod:#"GET"];
//NSLog(#"request url = %#", [getRequest.url absoluteString]);
[getRequest startAsynchronous];
}
- (void)performPostRequestWithString:(NSString *)string stringDictionary:(NSDictionary *)stringDictionary dataDictionary:(NSDictionary *)dataDictionary delegate:(id)requestDelegate requestSelector:(SEL)requestSelector errorSelector:(SEL)errorSelector {
localCopy = self;
self.delegate = requestDelegate;
self.callback = requestSelector;
self.errorCallback = errorSelector;
NSURL *url = [NSURL URLWithString:string];
postRequest = [[ASIFormDataRequest alloc] initWithURL:url];
[postRequest setDelegate:self];
[postRequest setRequestMethod:#"POST"];
if (stringDictionary)
for (NSString *key in [stringDictionary allKeys])
[postRequest setPostValue:[stringDictionary objectForKey:key] forKey:key];
if (dataDictionary)
for (NSString *key in [dataDictionary allKeys])
[postRequest setData:[dataDictionary objectForKey:key] forKey:key];
//NSLog(#"request url = %#", [postRequest.url absoluteString]);
[postRequest startAsynchronous];
}
#pragma mark - ASIHTTPRequest Delegate Implementation
- (void)requestFinished:(ASIHTTPRequest *)crequest {
NSString *status = [crequest responseString];
if (self.delegate && self.callback) {
if([self.delegate respondsToSelector:self.callback])
[self.delegate performSelectorOnMainThread:self.callback withObject:status waitUntilDone:YES];
else
NSLog(#"No response from delegate");
}
localCopy = nil;
}
- (void)requestFailed:(ASIHTTPRequest *)crequest {
if (self.delegate && self.errorCallback) {
if([self.delegate respondsToSelector:self.errorCallback])
[self.delegate performSelectorOnMainThread:self.errorCallback withObject:crequest.error waitUntilDone:YES];
else
NSLog(#"No response from delegate");
}
localCopy = nil;
}
#end
To use it, just import RequestPerformer.h in your UIViewController and do smth like:
[requestManager performGetRequestWithString:tempString stringDictionary:stringDictionary dataDictionary:dataDictionary delegate:self requestSelector:#selector(requestSucceeded:) errorSelector:#selector(requestFailed:)];
Parameters:
(NSString *)string - url string, where to post your request;
(NSDictionary *)stringDictionary - dictionary, which contains all the
text information (such as name, id etc.);
(NSDictionary *)dataDictionary - dictionary, which contains all data information (such as photos, files, etc.);
(id)requestDelegate - delegate to perform selectors below;
(SEL)requestSelector - selector, which will be executed while successfully request;
(SEL)errorSelector - selector, which will be executed, while error occurred.
In above answer (demon9733), wrapper class has been created delegate property with retain. In general, delegate property should be assign to remove retain cycle.
Related
I'm trying to do login page and logout page. I completed the api calling and login successfully, but i am trying to check whether the user is already logged in or not. if the user is logged in and kills the app, and after sometimes the user opens the app means the app should show the home page, but my app shows the sign in page. I tried the below code along with api calling.
NSString *mailID=mailTextField.text;
NSString *password=passwordTextField.text;
NSString *noteDataString = [NSString stringWithFormat:#"email=%#&password=%#",mailID,password];
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfiguration.HTTPAdditionalHeaders = #{#"language": #"en"};
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSURL *url = [NSURL URLWithString:#"https://qa-user.moneyceoapp.com/user/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSData *data = [noteDataString dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody=data;
request.HTTPMethod = #"POST";
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *httpResponse= (NSHTTPURLResponse *)response;
if(httpResponse.statusCode == 200)
{
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"The response is - %#",responseDictionary);
NSInteger status = [[responseDictionary objectForKey:#"status"] integerValue];
if(status == 1)
{
NSLog(#"Login SUCCESS");
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
[defs setObject:mailID forKey:#"email"];
[defs setObject:password forKey:#"password"];
[defs synchronize];
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
if(mailID && password)
{
HomePageVC *homepageVC = [[HomePageVC alloc] initWithNibName:#"HomePageVC" bundle:nil];
[self.navigationController pushViewController:homepageVC animated:YES];
}
}];
}
else
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
NSLog(#"Login FAILURE");
[self alertView:#"Invalid user account"];
}];
}
}
else
{
NSLog(#"Error");
}
}];
[postDataTask resume];
I use class that uses NSUserDefaults to create a dictionary with the parameters of the user's login information. Also, I use Boolean values to check a value equivalently named joinedApp.
*DataModel.h*
//
#class Message;
// The main data model object
#interface DataModel : NSObject
// The complete history of messages this user has sent and received, in
// chronological order (oldest first).
#property (nonatomic, strong) NSMutableArray* messages;
// Loads the list of messages from a file.
- (void)loadMessages;
// Saves the list of messages to a file.
- (void)saveMessages;
// Adds a message that the user composed himself or that we received through
// a push notification. Returns the index of the new message in the list of
// messages.
- (int)addMessage:(Message*)message;
// Get and set the user's nickname.
- (NSString*)nickname;
- (void)setNickname:(NSString*)name;
// Get and set the secret code that the user is registered for.
- (NSString*)secretCode;
- (void)setSecretCode:(NSString*)string;
// Determines whether the user has successfully joined a chat.
- (BOOL)joinedChat;
- (void)setJoinedChat:(BOOL)value;
- (BOOL)joinedApp;
- (void)setJoinedApp:(BOOL)value;
- (NSString*)userId;
- (NSString*)deviceToken;
- (void)setDeviceToken:(NSString*)token;
#end
DataModel.m
#import "DataModel.h"
#import "Message.h"
#import "ViewController.h"
// We store our settings in the NSUserDefaults dictionary using these keys
static NSString* const NicknameKey = #"Nickname";
static NSString* const SecretCodeKey = #"SecretCode";
static NSString* const JoinedChatKey = #"JoinedChat";
static NSString* const JoinedAppKey = #"JoinedApp";
static NSString* const DeviceTokenKey = #"DeviceToken";
static NSString* const UserId = #"UserId";
#implementation DataModel
+ (void)initialize
{
if (self == [DataModel class])
{
// Register default values for our settings
[[NSUserDefaults standardUserDefaults] registerDefaults:
#{NicknameKey: #"",
SecretCodeKey: #"",
JoinedChatKey: #0,
JoinedAppKey: #0,
DeviceTokenKey: #"0",
UserId:#""}];
}
}
// Returns the path to the Messages.plist file in the app's Documents directory
- (NSString*)messagesPath
{
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = paths[0];
return [documentsDirectory stringByAppendingPathComponent:#"Messages.plist"];
}
- (void)loadMessages
{
NSString* path = [self messagesPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
{
// We store the messages in a plist file inside the app's Documents
// directory. The Message object conforms to the NSCoding protocol,
// which means that it can "freeze" itself into a data structure that
// can be saved into a plist file. So can the NSMutableArray that holds
// these Message objects. When we load the plist back in, the array and
// its Messages "unfreeze" and are restored to their old state.
NSData* data = [[NSData alloc] initWithContentsOfFile:path];
NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
self.messages = [unarchiver decodeObjectForKey:#"Messages"];
[unarchiver finishDecoding];
}
else
{
self.messages = [NSMutableArray arrayWithCapacity:20];
}
}
- (void)saveMessages
{
NSMutableData* data = [[NSMutableData alloc] init];
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:self.messages forKey:#"Messages"];
[archiver finishEncoding];
[data writeToFile:[self messagesPath] atomically:YES];
}
- (int)addMessage:(Message*)message
{
[self.messages addObject:message];
[self saveMessages];
return self.messages.count - 1;
}
- (NSString*)nickname
{
return [[NSUserDefaults standardUserDefaults] stringForKey:NicknameKey];
}
- (void)setNickname:(NSString*)name
{
[[NSUserDefaults standardUserDefaults] setObject:name forKey:NicknameKey];
}
- (NSString*)secretCode
{
return [[NSUserDefaults standardUserDefaults] stringForKey:SecretCodeKey];
}
- (void)setSecretCode:(NSString*)string
{
[[NSUserDefaults standardUserDefaults] setObject:string forKey:SecretCodeKey];
}
- (BOOL)joinedApp
{
return [[NSUserDefaults standardUserDefaults] boolForKey:JoinedAppKey];
}
- (void)setJoinedApp:(BOOL)value
{
[[NSUserDefaults standardUserDefaults] setBool:value forKey:JoinedAppKey];
}
- (BOOL)joinedChat
{
return [[NSUserDefaults standardUserDefaults] boolForKey:JoinedChatKey];
}
- (void)setJoinedChat:(BOOL)value
{
[[NSUserDefaults standardUserDefaults] setBool:value forKey:JoinedChatKey];
}
- (NSString*)userId
{
NSString *userId = [[NSUserDefaults standardUserDefaults] stringForKey:UserId];
if (userId == nil || userId.length == 0) {
userId = [[[NSUUID UUID] UUIDString] stringByReplacingOccurrencesOfString:#"-" withString:#""];
[[NSUserDefaults standardUserDefaults] setObject:userId forKey:UserId];
}
return userId;
}
- (NSString*)deviceToken //used in appdelegate
{
return [[NSUserDefaults standardUserDefaults] stringForKey:DeviceTokenKey];
}
- (void)setDeviceToken:(NSString*)token //used in app delegate
{
[[NSUserDefaults standardUserDefaults] setObject:token forKey:DeviceTokenKey];
}
#end
I then call the message in the viewIsLoaded method and print verification in the debugger window.
*ViewController.h*
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#class DataModel;
#interface ViewController : UIViewController
#property (nonatomic, strong, readonly) DataModel* dataModel;
#property (retain, strong) UIImage* Image;
#end
ViewController.m
#import "ViewController.h"
#import "DataModel.h"
#import "PushChatStarter-Swift.h"
#implementation ViewController
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
_dataModel = [[DataModel alloc] init];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
MockApiclient *client = [MockApiclient new];
[client executeRequest];
[self.dataModel setJoinedApp:YES];
if ([_dataModel joinedApp] == YES)
{
NSLog(#"hi from viewcontroller 1 ");//Here you know which button has pressed
}
...
}
NSUserDefaults:
An interface to the user’s defaults database, where you store key-value pairs persistently across launches of your app.
I also call joinedApp from the login method being used along with other messages.
I'm encoding an array of objects that conform to NSSecureCoding successfully like this.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil];
How do I unarchive this data? I'm trying like this but it's returning nil.
NSError *error = nil;
NSArray<MyClass *> *array = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&error];
This is the error it returns.
The data couldn’t be read because it isn’t in the correct format.
Here is a simple example
#interface Secure:NSObject<NSSecureCoding> {
NSString* name;
}
#property NSString* name;
#end
#implementation Secure
#synthesize name;
// Confirms to NSSecureCoding
+ (BOOL)supportsSecureCoding {
return true;
}
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
[coder encodeObject:self.name forKey: #"name"];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
self = [self init];
if (self){
self.name = [coder decodeObjectOfClass:[NSString class] forKey:#"name"];
}
return self;
}
#end
Usage
Secure *archive = [[Secure alloc] init];
archive.name = #"ARC";
NSArray* array = #[archive];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil];
NSArray<Secure *> *unarchive = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSObject class] fromData:data error:nil];
NSLog(#"%#", unarchive.firstObject.name);
am new to iOS, Getting issue with displaying data from below service data
[{
"Name": Rahul,
"FatherName": Ravinder,
"Designation": Engineering,
"Profession": Software Eng,
"Height": "5 ft 3 in",
"Weight": "134.5 lbs"
}]
below is the code what i have tried. Please help me to find the issue. Thanks In Advance.
NameDetails.m
---------------
- (void)viewDidLoad {
[super viewDidLoad];
[self callService:[appDelegate.signUpdata objectForKey:#"id"]];
}
-(void)callService:(NSString *)userid
{
[Utility showIndicator:nil view1:self.view];
JsonServicePostData = [[JsonServiceCls alloc] init];
JsonServicePostData.delegate = self;
[JsonServicePostData Getdata:userid];
}
-(void)DidFinishWebServicesPostData
{
[Utility hideIndicator];
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
_txtName.text=[dict objectForKey:#"Name"];
_txtFName.text=[dict objectForKey:#"FatherName"];
_txtDesg.text=[dict objectForKey:#"Designation"];
_txtprof.text=[dict objectForKey:#"Profession"];
_txtHeight.text=[dict objectForKey:#"Height"];
_txtWeight.text=[dict objectForKey:#"Weight"];
}
}
+(void)makeHttpGETresponceParsingwithSerVer:(NSString *)strServer withCallBack:(void(^)(NSDictionary *dicArr,NSError *error))handler
{
NSURL *urlServer = [NSURL URLWithString:strServer];
NSURLRequest *request = [NSURLRequest requestWithURL:urlServer];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
handler(res,error);
}];
[postDataTask resume];
}
then call your method In viewDidLoad...
[RestClient makeHttpGETresponceParsingwithSerVer:#"YOUR_URL" withCallBack:^(NSDictionary *responceDic, NSError *error) {
_txtName.text =[responceDic objectForKey:#"Name"];
_txtFName.text =[responceDic objectForKey:#"FatherName"];
_txtDesg.text =[responceDic objectForKey:#"Designation"];
_txtprof.text =[responceDic objectForKey:#"Profession"];
_txtHeight.text =[responceDic objectForKey:#"Height"];
_txtWeight.text =[responceDic objectForKey:#"Weight"];
}];
// RestClient is the class name as it is a class method, You can use instance method.
Hi The better approach is for this kind of API call activity you have to go with AFNetworking - https://github.com/AFNetworking/AFNetworking
Its Pretty simple and more powerful. Once you get the json response you have to go for Model Approach.
#import <UIKit/UIKit.h>
#interface NameDetails : NSObject
#property (nonatomic, strong) NSString * designation;
#property (nonatomic, strong) NSString * fatherName;
#property (nonatomic, strong) NSString * height;
#property (nonatomic, strong) NSString * name;
#property (nonatomic, strong) NSString * profession;
#property (nonatomic, strong) NSString * weight;
-(instancetype)initWithDictionary:(NSDictionary *)dictionary;
-(NSDictionary *)toDictionary;
#end
#import "RootClass.h"
NSString *const kRootClassDesignation = #"Designation";
NSString *const kRootClassFatherName = #"FatherName";
NSString *const kRootClassHeight = #"Height";
NSString *const kRootClassName = #"Name";
NSString *const kRootClassProfession = #"Profession";
NSString *const kRootClassWeight = #"Weight";
#interface RootClass ()
#end
#implementation RootClass
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
-(instancetype)initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if(![dictionary[kRootClassDesignation] isKindOfClass:[NSNull class]]){
self.designation = dictionary[kRootClassDesignation];
}
if(![dictionary[kRootClassFatherName] isKindOfClass:[NSNull class]]){
self.fatherName = dictionary[kRootClassFatherName];
}
if(![dictionary[kRootClassHeight] isKindOfClass:[NSNull class]]){
self.height = dictionary[kRootClassHeight];
}
if(![dictionary[kRootClassName] isKindOfClass:[NSNull class]]){
self.name = dictionary[kRootClassName];
}
if(![dictionary[kRootClassProfession] isKindOfClass:[NSNull class]]){
self.profession = dictionary[kRootClassProfession];
}
if(![dictionary[kRootClassWeight] isKindOfClass:[NSNull class]]){
self.weight = dictionary[kRootClassWeight];
}
return self;
}
/**
* Returns all the available property values in the form of NSDictionary object where the key is the approperiate json key and the value is the value of the corresponding property
*/
-(NSDictionary *)toDictionary
{
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
if(self.designation != nil){
dictionary[kRootClassDesignation] = self.designation;
}
if(self.fatherName != nil){
dictionary[kRootClassFatherName] = self.fatherName;
}
if(self.height != nil){
dictionary[kRootClassHeight] = self.height;
}
if(self.name != nil){
dictionary[kRootClassName] = self.name;
}
if(self.profession != nil){
dictionary[kRootClassProfession] = self.profession;
}
if(self.weight != nil){
dictionary[kRootClassWeight] = self.weight;
}
return dictionary;
}
The above one is Model Class.
Your JSON look like a array. So you need to iterate the Dictionary values on it. Other than that you may pass it directly.
Now in your ViewController class initiate the mutable array
and pass the response like
NSArray *arrayData = ResponseFromAFNETWORKING
for (NSDictionary *data in arrayData) {
NameDetails *modelFeed = [[NameDetails alloc] initFromDictinary:data]
[self.YourMutableDictionary addObject:modelFeed]
}
self.updateDisplay:self.YourMutableDictionary[0] // If not array No iteration, you can prepare the model and pass it directly
----------------------------------------
- (void)updateDisplay:(NameDetails *)feed {
_txtName.text =feed.Name;
_txtFName.text =feed.FatherName;
_txtDesg.text =feed.Designation;
_txtprof.text =feed.Profession;
_txtHeight.text =feed.Height;
_txtWeight.text =feed.Weight;
}
Hope this will help. This is a robust and elastic approach, thread safe mechanism too
I am a quite new to IOS development and keep having struggle with it. I would like to display phone list which an user has from my server but tableview does not display items. I have got data from server well and I think settings for UItableView is correct. Here is my code:
STKPhoneHolderViewController.h
#import <UIKit/UIKit.h>
#import "STKSimpleHttpClientDelegate.h"
#interface STKPhoneHolderViewController : UITableViewController <UITableViewDataSource, STKSimpleHttpClientDelegate>
#property (strong, nonatomic) IBOutlet UITableView *phoneTable;
#property (strong, nonatomic) NSMutableArray *phoneArray;
#end
STKPhoneHolderViewController.m
#implementation STKPhoneHolderViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.phoneTable.dataSource = self;
self.phoneArray = [[NSMutableArray alloc]init];
[self loadPhoneList];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.phoneArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"PhoneCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
STKPhoneHolder *phoneHolder = [self.phoneArray objectAtIndex:indexPath.row];
[cell.textLabel setText:phoneHolder.userName];
return cell;
}
#pragma Custom method
- (void) loadPhoneList
{
self.phoneArray = [[NSMutableArray alloc]init];
STKSimpleHttpClient *client = [[STKSimpleHttpClient alloc]init];
client.delegate = self;
NSString *userId = #"your_id_h";
NSString *sUrl = [NSString stringWithFormat:#"%#%#?userid=%#",
MOBILE_API_URL,
PHONEHOLDER_URI,
userId];
[client send:sUrl data:#""];
}
#pragma STKSimpleHttpClientDelegate
-(void) complete:(STKHttpResult*) result
{
if (result.ok != YES){
[STKUtility alert:result.message];
return;
}
self.phoneArray = (NSMutableArray*)result.result;
for (STKPhoneHolder *holder in self.phoneArray) {
NSLog(#"%#", [holder description]);
}
[self.phoneTable reloadData];
NSLog(#" isMainThread(%d)", [NSThread isMainThread] );
}
#end
STKSimpleHttpClient.m
#import "STKSimpleHttpClient.h"
#import "STKSimpleHttpClientDelegate.h"
#implementation STKSimpleHttpClient
NSMutableData *responseData;
STKHttpResult *httpResult;
void (^completeFunction)(STKHttpResult *);
- (void) send:(NSString*)url
data:(NSString*)data
{
httpResult = [[STKHttpResult alloc]init];
dispatch_async(dispatch_get_main_queue(), ^{
if ( data == nil) return;
//Get request object and set properties
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: url]];
//set header for JSON request and response
[urlRequest setValue:#"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[urlRequest setValue:#"application/json" forHTTPHeaderField:#"Accept"];
//set http method to POST
[urlRequest setHTTPMethod:#"POST"];
//set time out
[urlRequest setTimeoutInterval:20];
NSData *body = [data dataUsingEncoding:NSUTF8StringEncoding];
//set request body
urlRequest.HTTPBody = body;
//connect to server
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (conn==nil){
//Do something
}
});
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
[responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable noow
NSError *error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
BOOL ok = [[json objectForKey:#"ok"] boolValue];
NSString *message = [json objectForKey:#"message"];
if (ok == NO) {
[httpResult setError:message];
} else {
[httpResult setSuccess:[json objectForKey:#"result"]];
}
if (self.delegate !=nil) {
[self.delegate complete:httpResult];
}
responseData = nil;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
if (self.delegate !=nil) {
[self.delegate complete:[httpResult setError:#"Connection failed."]];
}
}
STKPhoneHolder.m
#import <Foundation/Foundation.h>
#interface STKPhoneHolder : NSObject
#property NSString *deviceId;
#property NSString *userId;
#property NSString *userName;
#property NSString *msn;
- (id) initWithDeviceId:(NSString*)aDeviceId
userId:(NSString*)aUserId
userName:(NSString*)aUserName
msn:(NSString*)aMsn;
#end
Log:
2013-12-17 16:14:23.447 [5323:70b] {
deviceId = 11111;
email = "";
msn = 11111111;
role = "";
userId = aaaaaa;
userName = "Joshua Pak";
}
2013-12-17 16:14:23.448 [5323:70b] {
deviceId = 22222;
email = "";
msn = 2222222;
role = "";
userId = bbbbb;
userName = "Jasdf Pak";
}
2013-12-17 16:14:23.449 Stalker[5323:70b] isMainThread(1)
I can see the log printing phoneArray with two phones in 'complete' method but tableview just display "No record". Tableview does not render again even though I called reloadData method. I made sure that [self.phoneTable reloadData] is called in debugging mode.
What do I have to do more?
Try to call reloadData in main thread
#pragma STKSimpleHttpClientDelegate
-(void) complete:(STKHttpResult*) result
{
if (result.ok != YES){
[STKUtility alert:result.message];
return;
}
self.phoneArray = (NSMutableArray*)result.result;
for (STKPhoneHolder *holder in self.phoneArray) {
NSLog(#"%#", [holder description]);
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.phoneTable reloadData];
}
}
Or you can use performSelectorOnMainThread
[self.phoneTable performSelectorOnMainThread:#selector(reloadData)
withObject:nil
waitUntilDone:NO];
I am guessing STKSimpleHttpClient class is calling complete delegate function on different thread, all user interface interaction suppose to be called from main thread.
Try this code to see which thread you are in from the complete delegate function
NSLog(#" isMainThread(%d)", [NSThread isMainThread] );
check this. does the code load the tableview before you get information from web services. if so then write the statement [tableview Reload]; next to the web services information getting process. This will help
It's not necessary to specify the number of sections, but you might want to do it with this code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
I see you're using a Table View Controller, which already has a tableView reference self.tableView.
Like #rdelmar said, you could use that reference instead of your phoneTable:
[[self tableView] setDataSource:self];
[[self tableView] setDelegate:self];
[[self tableView] reloadData];
I am trying to download a pdf file from a server to the device. Here is the code that I am using
- (id)initwithURL:(NSString*)remoteFileLocation andFileName:(NSString*)fileName{
//Get path to the documents folder
NSString *resourcePathDoc = [[NSString alloc] initWithString:[[[[NSBundle mainBundle]resourcePath]stringByDeletingLastPathComponent]stringByAppendingString:#"/Documents/"]];
localFilePath = [resourcePathDoc stringByAppendingString:fileName];
BOOL fileExists = [[NSFileManager defaultManager]fileExistsAtPath:localFilePath];
if (fileExists == NO) {
NSURL *url = [NSURL URLWithString:remoteFileLocation];
NSData *data = [[NSData alloc] initWithContentsOfURL: url];
//Write the data to the local file
[data writeToFile:localFilePath atomically:YES];
}
return self;
}
where remoteFileLocation is a NSString and has the value http://topoly.com/optimus/irsocial/Abs/Documents/2009-annual-report.pdf
On running the app crashes, just on NSData giving a SIGABRT error. The only useful information it gives is
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSURL length]: unrecognized selector sent to instance 0xc87b600'
How can this be fixed ?
As your PDF file is too large in size so if you do Synchronous Download, it will take too Long to download, so i insist you to create an Asynchronous Downloader and Use it. I have put code for the same.
Step 1 :Create a file 'FileDownloader.h'
#define FUNCTION_NAME NSLog(#"%s",__FUNCTION__)
#import <Foundation/Foundation.h>
#protocol fileDownloaderDelegate <NSObject>
#optional
- (void)downloadProgres:(NSNumber*)percent forObject:(id)object;
#required
- (void)downloadingStarted;
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data;
- (void)downloadingFailed:(NSURL *)url;
#end
#interface FileDownloader : NSObject
{
#private
NSMutableURLRequest *_request;
NSMutableData *downloadedData;
NSURL *fileUrl;
id <fileDownloaderDelegate> delegate;
double totalFileSize;
}
#property (nonatomic, strong) NSMutableURLRequest *_request;
#property (nonatomic, strong) NSMutableData *downloadedData;
#property (nonatomic, strong) NSURL *fileUrl;
#property (nonatomic, strong) id <fileDownloaderDelegate> delegate;
- (void)downloadFromURL:(NSString *)urlString;
#end
Step 2 : Create a .m file with FileDownloader.m
#import "FileDownloader.h"
#implementation FileDownloader
#synthesize _request, downloadedData, fileUrl;
#synthesize delegate;
- (void)downloadFromURL:(NSString *)urlString
{
[self setFileUrl:[NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
self._request = [NSMutableURLRequest requestWithURL:self.fileUrl cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0f];
NSURLConnection *cn = [NSURLConnection connectionWithRequest:self._request delegate:self];
[cn start];
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if([delegate respondsToSelector:#selector(downloadingStarted)])
{
[delegate performSelector:#selector(downloadingStarted)];
}
totalFileSize = [response expectedContentLength];
downloadedData = [NSMutableData dataWithCapacity:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[downloadedData appendData:data];
if([delegate respondsToSelector:#selector(downloadProgres:forObject:)])
{
[delegate performSelector:#selector(downloadProgres:forObject:) withObject:[NSNumber numberWithFloat:([downloadedData length]/totalFileSize)] withObject:self];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if([delegate respondsToSelector:#selector(downloadingFailed:)])
{
[delegate performSelector:#selector(downloadingFailed:) withObject:self.fileUrl];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if([delegate respondsToSelector:#selector(downloadingFinishedFor:andData:)])
{
[delegate performSelector:#selector(downloadingFinishedFor:andData:) withObject:self.fileUrl withObject:self.downloadedData];
}
}
#end
Step 3 : Import file #import "FileDownloader.h" and fileDownloaderDelegate in your viewController
Step 4: Define following Delegate methods in .m file of your viewCOntroller
- (void)downloadingStarted;
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data;
- (void)downloadingFailed:(NSURL *)url;
Step 5 : Create Object of FileDownloader and set URL to Download thats it.
FileDownloader *objDownloader = [[FileDownloader alloc] init];
[objDownloader setDelegate:self];
[objDownloader downloadFromURL:#"Your PDF Path URL here];
Step 6 : Save your file where you want in
- (void)downloadingFinishedFor:(NSURL *)url andData:(NSData *)data; method.
It appears that your remoteFileLocation parameter value is really an NSURL object and not an NSString. Double check how you get/create remoteFileLocation and verify it really is an NSString.
There are also several other issues with this code. The proper way to create a path to the Documents directory is as follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = paths[0];
NSString *localFilePath = [resourcePathDoc stringByAppendingPathComponent:fileName];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:localFilePath];
if (!fileExists) {
NSURL *url = [NSURL URLWithString:remoteFileLocation];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
//Write the data to the local file
[data writeToFile:localFilePath atomically:YES];
}
GCD can be used for massive files. You can download the file synchronous on a second thread and post back to the main thread if you like. You can also use Operation queues.
You can indeed also use the delegate method from NSURLConnection allowing you to handle the callbacks on the main thread. It is however obsolete to define your own delegate since you can just implement the delegate from NSURLConnection itself.