In my application I am using one static library. In that library I implemented code for establish a connection with the server. For server interaction I used NSURLSession but it's delaying the UI response to avoid it I have started using NSURLConnection delegate methods now I am getting the response from server but here I don't know how to send the response back to actual code from did finish loading method.
In my team I want to distribute this library to both iphone and ipad development engineers. They don't have any control on server related code everything I implemented in static library. So please show me the solution for my problem thanks in advance.
The below is the code which I used in one class of static library:
StaticClass:
.h File
#interface StaticClass : NSObject<NSURLConnectionDelegate,NSURLSessionDelegate>
{
NSMutableDictionary *responseDictionary;
NSUserDefaults *serviceURlInUserDefaults;
NSData *responseData;
}
#property (nonatomic, weak) id <DataReciverDelegate>delegate;
#property(strong,nonatomic)NSData *responseData;
-(void)loginWithUsername:(NSString *)name password:(NSString*)password serviceUrl:(NSString*)serviceUrl domainName:(NSString*)domainName ;
#end
import "StaticClass.h"
#protocol DataReciverDelegate <NSObject>
#required
- (void)responseDictionary:(NSDictionary *)response;
#end
#implementation StaticClass
#synthesize responseData;
-(void)loginWithUsername:(NSString *)name password:(NSString*)password serviceUrl:(NSString*)serviceUrl domainName:(NSString*)domainName
{
NSString *ApiStr=[NSString stringWithFormat:#“http://login.com”];
NSURL *Url=[[NSURL alloc]initWithString:[loginApiStr stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
NSURLRequest *ApiRequest=[NSURLRequest requestWithURL:loginUrl];
NSURLConnection *connection=[[NSURLConnection alloc]initWithRequest:ApiRequest delegate:self];
[connection start];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
self.responseData=data;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
responseDictionary=[NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:nil];
[_delegate responseDictionary:responseDictionary];
}
#end
Response where I want to use is in class1 :
Here please let me know how can i include that delegate which i created in static library class
#interface Class1 : NSObject<NSURLConnectionDelegate,NSURLSessionDelegate>
{
}
#end
#implementation Class1
-(void)login
{
StaticClass *object1=[[StaticClass alloc]init];
[object loginWithUsername:#“AAA” password:#“BBB” serviceUrl:url domainName:dname];
}
You can either offer API to notify that response has been read from the connection, or you can send a notification.
The first can be accomplished by either implementing a delegate protocol and setting the delegate in the using app, or by using block-based API, where the using app would set a block to handle events. You see these two patterns very often in system-provided API, including NSUrlConnection.
Another option is to use notifications. You register for a particular notification name in the using app, and in the lib you post once your connection returns data.
You need to implement a protocol in your static library like:
#protocol DataReciverDelegate <NSObject>
#required
- (void)dataReceived:(NSData *)data;
#end
Also declare a property there like:
#property (nonatomic, weak) id <DataReciverDelegate>delegate;
In your static library implementation, implement the connectionDidFinishLoading like:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[_delegate dataReceived:_dataYouReceived];
}
Now you need to implement the DataReciverDelegate in the class you need to get the data, and When you create the object of your static library class, set the delegate.
Related
I'm trying to use delegate methods from NMSSH library in iOS but could not get it working. Let's take an example.
CustomViewController.h
#import <UIKit/UIKit.h>
#import <NMSSH/NMSSH.h>
#interface CustomViewController : UIViewController<NMSSHSessionDelegate, NMSSHChannelDelegate>
- (IBAction)connectButton:(UIButton *)sender;
#end
CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
-(void)viewDidLoad{
[super viewDidLoad];
}
- (IBAction)connectButton:(UIButton *)sender {
[self serverConnect:#"10.0.0.1"];
}
-(void)serverConnect:(NSString *)address{
NMSSHSession *session = [NMSSHSession connectToHost:address withUsername:#"username"];
NMSSHChannel *myChannel = [[NMSSHChannel alloc]init];
if (session.isConnected) {
[session authenticateByPassword:#"password"];
if (session.isAuthorized) {
NSLog(#"Authentication succeeded");
[session setDelegate:self];
[myChannel setDelegate:self];
}
}
NSError *error = nil;
//session.channel.requestPty = YES; (tried and later ignored)
NSString *response = [session.channel execute:#"mkdir" error:&error];
NSLog(#"Response from device: %#", response);
}
- (void)session:(NMSSHSession *)session didDisconnectWithError:(NSError *)error{
NSLog(#"log if session disconnects...Delegate method");
}
- (void)channel:(NMSSHChannel *)channel didReadError:(NSString *)error{
NSLog(#"Error received...Delegate method");
}
- (void)channel:(NMSSHChannel *)channel didReadRawData:(NSData *)data{
NSLog(#"Read Raw Data...Delegate method");
}
Connection to the server, sending a single line command and acknowledgement back from the server in Console is OK.
I have decent idea how to pass values from one View Controller to another using delegate (went through few tutorials with practical implementation).
With the same knowledge I am attempting to get response from delegate methods parts of NMSSH library but it's driving me round and round. I've found http://cocoadocs.org/docsets/NMSSH/2.2.1/ pretty nice API of this library but with my limited knowledge of iOS, I'm bit stuck.
Please help me.
My search finally came to an end with NMSSH AsyncAPI (branch) which supports multithreading.
I have a class, "WebAPI", that handles all web API calls, the class uses NSURLConnection through its asynchronous delegate-based calls.
Whenever an object needs to communicate with the web API it will use an instance of WebAPI and call the required method as shown below in the case of signing in I make the folowing call from the AppDelegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
WebAPI *webAPI = [[WebAPI alloc] init];
[webAPI performLoginWithUserName:#"test1#myserver.com" andPassword:#"password"];
}
The problem is that once the performLoginWithUserName:andPassword call is made, the code progresses on and any/all response is received in the delegate methods that are implemented in WebAPI.m.
This is a real issue because I need to be able to get response codes and any data received within the class method from where the call to the WebAPI, originated . I would like to be able to this :
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
WebAPI *webAPI = [[WebAPI alloc] init];
WebAPIResponse * webAPIRespnse = [webAPI performLoginWithUserName:#"test1#myserver.com" andPassword:#"password"];
}
Where WebAPIResponse class is a custom class that will contain the HTTP Status code and any data that is received.
This is achievable if I change WebAPI.m to use NSURLConnection sendSynchronousRequest, but that doesnt enable me to receive all HTTP codes.
What would be the best way to fulfill this requirement?
Thank you for your help.
You could use blocks to handle responses.
For example:
WebApi.h
- (void)performLoginWithUsername:(NSString *)userName
andPassword:(NSString *)password
successBlock:(void(^)(NSData *response))successBlock
failureBlock:(void(^)(NSError *error))failureBlock;
WebApi.m
#interface WebAPI()
#property (nonatomic, copy) void(^authorizationSuccessBlock)(NSData *response);
#property (nonatomic, copy) void(^authorizationFailureBlock)(NSError *error);
#end
#implementation WebAPI
- (void)performLoginWithUsername:(NSString *)userName
andPassword:(NSString *)password
successBlock:(void(^)(NSData *response))successBlock
failureBlock:(void(^)(NSError *error))failureBlock {
self.authorizationSuccessBlock = successBlock;
self.authorizationFailureBlock = failureBlock;
// NSURLConnection call for authorization here
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (self.authorizationSuccessBlock != nil) {
self.authorizationSuccessBlock(data);
self.authorizationSuccessBlock = nil;
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if (self.authorizationFailureBlock != nil) {
self.authorizationFailureBlock(error);
self.authorizationFailureBlock = nil;
}
}
AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
WebAPI *webAPI = [[WebAPI alloc] init];
[webAPI performLoginWithUserName:#"test1#myserver.com" andPassword:#"password" successBlock:^(NSData *response) {
// Handle result here
} failureBlock:^(NSError *error) {
// Handle error here
}];
}
Change your WebAPI class to provide a delegate interface of its own, or to use completion blocks on the request which are called when the asynchronous connection completes.
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.
When the user presses a button, I need to know whether the device is connected to the internet at that very instant--not whether he was connected 3 seconds ago. The reachability (tonymillion) notifier takes about that long to update after there is a change in network reachability.
I thought that I would be able to check for actual access in real time using the following methods:
if (!([[Reachability reachabilityWithHostname:#"www.google.com"] currentReachabilityStatus] == NotReachable)) NSLog(#"reachable");
if ([[Reachability reachabilityWithHostname:#"www.google.com"] currentReachabilityStatus] == NotReachable) NSLog(#"not reachable");
But results indicated that in fact currentReachabilityStatus does not check for internet access; it only checks the same flag that is updated with ~3 seconds' delay.
What's an efficient way of actually checking for network access on the spot?
As you wished in the comments above here is a solution using a "HEAD" request.
Make your class conforming to the
NSURLConnectionDelegate.
Implement the connection:didReceiveResponse: delegate method
Optionally implement the connection:didFailWithError: delegate method
So your setup could look like this:
YourClass.m
#interface YourClass () <NSURLConnectionDelegate>
#property (strong, nonatomic) NSURLConnection *headerConnection;
#end
#implementation YourClass
- (void)viewDidLoad {
// You can do this in whatever method you want
NSMutableURLRequest *headerRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0];
headerRequest.HTTPMethod = #"HEAD";
self.headerConnection = [[NSURLConnection alloc] initWithRequest:headerRequest delegate:self];
}
#pragma mark - NSURLConnectionDelegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
if (connection == self.headerConnection) {
// Handle the case that you have Internet; if you receive a response you are definitely connected to the Internet
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// Note: Check the error using `error.localizedDescription` for getting the reason of failing
NSLog(#"Failed: %#", error.localizedDescription);
}
Have you tried putting an observer on the reachability status?
The Reachabilty extension (NPReachability) that I used to use, allows KVO on the status.
I have a UIWebView, and it would make my life a lot simpler if it could be a delegate for an NSURLConnection. I have made a category like this
#interface UIWebView (NSURLConnectionDelegate) <NSURLConnectionDelegate>
//these methods are used by the NSURLConnection, and are implemented in the .m
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
#end
the methods in the implementation do work, since i had another view that was the delegate before, but now i need to change it because of reasons.
i give the NSURLConnection a delegate like so
#import "UIWebView+NSURLConnectionDelegate.h"
[[NSURLConnection alloc] initWithRequest:request delegate:webview]; //used to be self but now i need the webview to know about its own connection because there are multiple webviews
but none of the delegate methods get called when its a category like this.
has anyone done something like this before or does this not work because the NSURLConnection thinks that webview isnt actually a delegate or something?
edit to show some more code:
- (BOOL)webView:(UIWebView *)webview shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(#"shouldStartLoadWithRequest %# %d", request.URL.absoluteURL.description, navigationType);
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
if (!authed) {
authed = NO; //gets set to yes when delegate methods work (also are some print outs in the delegate methods which are not printing at all)
urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:webview];
return NO;
}
return YES;
}