Using NSURLSession inside NSURLProtocol - ios

I'm trying to create a transparent NSURLProtocol for http:// and https:// connections using NSURLSession. However at the moment, even though the completion handler is being run, URL requests with the app (UIWebView) are coming back blank. Does anybody have any ideas? Code is below:
#import "MyURLProtocol.h"
// AppDelegate
#import "AppDelegate.h"
static NSString * const MyURLProtocolHandledKey = #"MyURLProtocolHandledKey";
#interface MyURLProtocol () <NSURLConnectionDelegate,NSURLSessionDelegate>
#property (nonatomic, strong) NSURLConnection *connection;
#property (nonatomic, strong) NSMutableData *mutableData;
#property (nonatomic, strong) NSURLResponse *response;
#end
#implementation MyURLProtocol
+(BOOL)canInitWithRequest:(NSURLRequest*)request
{
if ([NSURLProtocol propertyForKey:MyURLProtocolHandledKey inRequest:request])
return NO;
NSString *scheme = request.URL.scheme.lowercaseString;
return [scheme isEqualToString:#"http"] || [scheme isEqualToString:#"https"];
}
+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
-(void)startLoading
{
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:#YES forKey:#"MyURLProtocolHandledKey" inRequest:newRequest];
NSURLRequest *request = newRequest;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
if (error != nil) {
NSLog(#"There was an error");
}
NSLog(#"Completiio handler ran");
self.mutableData = [NSMutableData dataWithData:data];
self.response = response;
}];
[task resume];
}
- (void) stopLoading {
[self.connection cancel];
self.mutableData = nil;
}
// Delegate stuff
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
#end

Your code is using NSURLConnection delegates to pass data back to the caller, e.g. connectionDidFinishLoading:.
To fix this:
Replace these with NSURLSession delegate methods.
Create and retain a custom session whose delegate is your protocol class instance; the shared session doesn't have a delegate, so it won't call your class's delegate methods.
Remove the callback block so that the delegate method for request completion will be called correctly.

Related

Custom NSURLProtocol slower after switching NSURLConnection to NSURLSession

I have a custom NSURLProtocol ("UrlProtocol") written to intercept requests from a UIWebView when navigating to specific websites, and apply an extra HTTP header before being sent. I followed https://www.raywenderlich.com/59982/nsurlprotocol-tutorial for a working class. My problem comes with switching from the deprecated NSURLConnection to NSURLSession: I tested an extremely simple one-file html page, which loaded successfully. However, slightly more complex sites with resources like js files, images, etc. will timeout, whereas using NSURLConnection the entire site will load within a few seconds.
I'll paste the original UrlProtocol using NSURLConnection, then the new class using NSURLSession. The original:
#import "UrlProtocol.h"
#import "Globals.h"
#implementation UrlProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
if (![request.URL.absoluteString hasPrefix:#"http"]) return NO; //No need to intercept non-http requests
if ([NSURLProtocol propertyForKey:#"handled" inRequest:request]) {
return NO;
}
return YES;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
NSString* key = #"custom-auth-header";
Globals* globals = [Globals getInstance];
NSString* token = [globals token];
NSMutableURLRequest *newRequest = [request mutableCopy]; //Create a mutable copy that can be modified
[newRequest setValue:token forHTTPHeaderField:key];
return [newRequest copy]; //return a non-mutable copy
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
- (void)startLoading {
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:#YES forKey:#"handled" inRequest:newRequest];
self.connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}
- (void)stopLoading {
NSLog(#"stopLoading");
[self.connection cancel];
self.connection = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.client URLProtocol:self didLoadData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.client URLProtocolDidFinishLoading:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[self.client URLProtocol:self didFailWithError:error];
}
#end
The new UrlProtocol using NSURLSessionDataTasks for every request:
#import "UrlProtocol.h"
#import "Globals.h"
#implementation UrlProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest * _Nonnull) request {
if (![request.URL.absoluteString hasPrefix:#"http"]) return NO; //No need to intercept non-http requests
if ([NSURLProtocol propertyForKey:#"handled" inRequest:request]) {
return NO;
}
return YES;
}
+ (NSURLRequest * _Nonnull)canonicalRequestForRequest:(NSURLRequest * _Nonnull)request {
NSString* key = #"custom-auth-header";
Globals* globals = [Globals getInstance];
NSString* token = [globals token];
NSMutableURLRequest *newRequest = [request mutableCopy]; //Create a mutable copy that can be modified
[newRequest setValue:token forHTTPHeaderField:key];
return [newRequest copy]; //return a non-mutable copy
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest * _Nonnull)a toRequest:(NSURLRequest * _Nonnull)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
- (void)startLoading {
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:#YES forKey:#"handled" inRequest:newRequest];
[Globals setUrlSessionDelegate:self];
Globals* globals = [Globals getInstance];
self.dataTask = [globals.session dataTaskWithRequest:newRequest];
[self.dataTask resume];
}
- (void)URLSession:(NSURLSession * _Nonnull)session dataTask:(NSURLSessionDataTask * _Nullable)dataTask didReceiveData:(NSData * _Nullable)data{
[self.client URLProtocol:self didLoadData:data];
}
- (void)URLSession:(NSURLSession * _Nonnull)session dataTask:(NSURLSessionDataTask * _Nullable)dataTask didReceiveResponse:(NSURLResponse * _Nullable)response
completionHandler:(void (^ _Nullable)(NSURLSessionResponseDisposition))completionHandler{
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession * _Nonnull)session task:(NSURLSessionTask * _Nonnull)task didCompleteWithError:(NSError * _Nullable)error{
if (error){
[self.client URLProtocol:self didFailWithError:error];
} else {
[self.client URLProtocolDidFinishLoading:self];
}
}
- (void)URLSession:(NSURLSession * _Nonnull)session task:(NSURLSessionTask * _Nonnull)task willPerformHTTPRedirection:(NSHTTPURLResponse * _Nonnull)response
newRequest:(NSURLRequest * _Nonnull)request completionHandler:(void (^ _Nonnull)(NSURLRequest * _Nullable))completionHandler {
completionHandler(request);
}
- (void)stopLoading {
[self.dataTask cancel];
self.dataTask = nil;
}
#end
"Globals" is a singleton where I have initialized one NSURLSession meant to be used throughout the runtime of the app. It also contains the token I set as a custom HTTP header for all requests:
#import "Globals.h"
#import "UrlProtocol.h"
#implementation Globals
#synthesize token;
#synthesize session;
static Globals *instance = nil;
+(Globals*) getInstance
{
#synchronized(self)
{
if (instance == nil)
{
instance = [Globals new];
}
}
return instance;
}
//UrlProtocol class has no init method, so the NSURLSession delegate is being set on url load. We will ensure only one NSURLSession is created.
+(void) setUrlSessionDelegate:(UrlProtocol*) urlProtocol{
Globals* globals = [Globals getInstance];
if (!globals.session){
globals.session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.defaultSessionConfiguration delegate:urlProtocol delegateQueue:nil];
}
}
#end
Solved my problem by creating a new default NSURLSession for each NSURLSessionDataTask. Something was wrong with the way I was trying to share one NSURLSession for all my tasks. URLProtocol's startLoading method is now as follows:
- (void)startLoading {
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:#YES forKey:#"handled" inRequest:newRequest];
NSURLSessionConfiguration* config = NSURLSessionConfiguration.defaultSessionConfiguration;
NSURLSession* session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
self.dataTask = [session dataTaskWithRequest:newRequest];
[self.dataTask resume];
}
The simple HTML page test before must have worked because only one task was needed to load the page

Passing response to controller while converting NSURLConnection to NSURLSession

I have something like service API class which implements NSURLConnectionDelegate methods. I noticed that some of the methods of this are deprecated and Apple now suggests to use NSURLSession. I created this service API's own delegate which the calling class would implement and that would be executed when a response is received. I am looking for how I can do something similar while using NSURLSession.
My present stuff with NSURLConnection:
#class ServiceAPI;
#protocol ServiceAPIDelegate <NSObject>
- (void)getResponseData:(NSData *)responseData;
#end
#interface ServiceAPI : NSObject<NSURLCoDelegate>
- (void)httpServiceRequest:(NSMutableURLRequest *)serviceRequest;
#property (nonatomic, weak) id <ServiceAPIDelegate> delegate;
#end
In ServiceAPI implementation file:
- (void)httpServiceRequest:(NSMutableURLRequest *)serviceRequest {
//[[NSURLSession sharedSession] dataTaskWithRequest:serviceRequest] resume];
self.requestConnection = [NSURLConnection connectionWithRequest:serviceRequest delegate:self];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[self.delegate getResponseData:self.responseData sender:self];
}
Class making request and getting response:
#interface CallingController : UITableViewController<ServiceAPIDelegate>
#end
Implementation file CallingController:
- (void)getResponseData:(NSData *)responseData {
// Do something with response.
}
How do I let the calling class handle the response method just like NSURLConnection while using NSURLSession. While reading NSURLSession looks like the request and response are handled together using completion handler something like this:
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:#"https://itunes.apple.com/search?term=apple&media=software"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(#"%#", json);
}];
I still want to have a service API class and my controller would just pass down the request to service API to make calls and once the response is back, it would be passed down to controller. How do I pass the response to the controller while using NSURLSession.
Something like this:
#class ServiceAPI;
#protocol ServiceAPIDelegate <NSObject>
- (void)getResponseData:(NSData *)responseData;
#end
#interface ServiceAPI : NSObject<NSURLSessionDelegate>
- (void)httpServiceRequest:(NSMutableURLRequest *)serviceRequest;
#property (nonatomic, weak) id <ServiceAPIDelegate> delegate;
#end
In Implementation
- (void)httpServiceRequest:(NSMutableURLRequest *)serviceRequest {
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *downloadTask =
[session dataTaskWithRequest:serviceRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
[self.delegate getResponseData:data sender:self];
}];
[downloadTask resume];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// no use
}
You can still achieve what you wish by initialising NSURLSessionDataTask without completion block. That way, it will call the delegate methods instead.
For example:
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:#"https://itunes.apple.com/search?term=apple&media=software"]];
[dataTask resume];
Implement NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data;
Also implement NSURLSessionTaskDelegate to know when the request is completed.
/* Sent as the last message related to a specific task. Error may be
* nil, which implies that no error occurred and this task is complete.
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error;

NSURLConnection and NSURLConnectionDataDelegate: connection callbacks are not fired

I want to implement file downloading with progress from my server.
I my code I'm using a custom class which is delegated by
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://example.com"]];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"6.0")) {
DownloadCallback *dc = [[DownloadCallback alloc] initWithCallbackProgress:^(long long res){
NSLog(#"%lld", res);
} withCallbackReady:^(long long res){
NSLog(#"READY %lld", res);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];
} withCallbackError:^(NSError * error) {
NSLog(#"READY %#", error.domain);
}];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:dc];
// [connection setDelegateQueue:[[NSOperationQueue alloc] init]];
[connection start];
header:
#interface DownloadCallback: NSObject<NSURLConnectionDataDelegate>{
#private void (^_progressHandler)(long long someParameter);
#private void (^_readyHandler)(long long someParameter);
#private void (^_errorHandler)(NSError *someParameter);
}
-(id) initWithCallbackProgress:(void(^)(long long))handler withCallbackReady:(void(^)(long long))handlerReady withCallbackError:(void(^)(NSError*))handlerError;
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
#end
body:
#implementation DownloadCallback
-(id) initWithCallbackProgress:(void(^)(long long))handler withCallbackReady:(void(^)(long long))handlerReady withCallbackError:(void(^)(NSError*))handlerError{
self = [super init];
if (self) {
_progressHandler = [handler copy];
_readyHandler = [handlerReady copy];
_errorHandler = [handlerError copy];
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// self.expectedTotalSize = response.expectedContentLength;
// Call completion handler.
if (_readyHandler != nil)
_readyHandler(response.expectedContentLength);
// Clean up.
// [_completionHandler release];
_readyHandler = nil;
_progressHandler = nil;
_errorHandler = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// self.recievedData += data.length;
if (_progressHandler != nil)
_progressHandler(data.length);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if (_errorHandler != nil)
_errorHandler(error);
}
#end
But the callback events are not fired! At all!
The simple synch code work prefectly:
// Send a synchronous request
NSURLRequest * urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://example.com"]];
NSURLResponse * response = nil;
NSError * error = nil;
NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
if (error == nil) {
// Parse data here
}
But I need a callback! How to resolve it? I've not found in stackoverflow a solution.
Futhermore, if I'm using a simple delegate to major class instead of DownloadCallback the same: the connection callbacks are not fired too.
Add the dealloc method to your callback class and out a breakpoint or log statement in it. See if it is deallocated before the callbacks are called.
If this is the case, your callback class instance is destroyed too soon. Make it a property of a class that will for sure live longer then the request.
Also, you should make sure that this code:
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:dc];
[connection start];
is called on a thread outlives the connection and has a runloop. The easiest way to achieve this is to call that code on the main-queue. Your code-example does not show on which queue that is called. If it is not working I assume it is because your calling it on a background queue.
You can dispatch to a background queue from the delegate callbacks of you want/need to.
As a sidenote, if you are building something new, you should try and use NSURLSession instead of NSURLConnection. NSURLSession is more secure, easier to use and not deprecated. NSURLConnection is deprecated.

can NSURLProtocol work with NSURLSession {upload, download}dataTask

I have a custom NSURLProtocol
#import <Foundation/Foundation.h>
#interface XXXURLProtocol : NSURLProtocol <NSURLSessionDataDelegate, NSURLSessionTaskDelegate>
#property (nonatomic, strong) NSURLSession *session;
#property (nonatomic, strong) NSURLSessionTask *task;
#end
#import "XXXURLProtocol.h"
#implementation DXYURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
if ([NSURLProtocol propertyForKey:YXURLProtocolHandled inRequest:request]) {
return NO;
}
NSString *scheme = [[request URL] scheme];
NSDictionary *dict = [request allHTTPHeaderFields];
return [dict objectForKey:#"custom_header"] == nil &&
([scheme caseInsensitiveCompare:#"http"] == NSOrderedSame ||
[scheme caseInsensitiveCompare:#"https"] == NSOrderedSame);
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
return request;
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a
toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
- (void)startLoading
{
NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
//add custom headers
[XXXURLProtocol addCustomHeaders:mutableReqeust];
[NSURLProtocol setProperty:#(YES)
forKey:YXURLProtocolHandled
inRequest:mutableReqeust];
NSURLSessionConfiguration *config;
config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.protocolClasses = #[ self ];
self.session = [NSURLSession sessionWithConfiguration:config];
self.task = [self.session dataTaskWithRequest:mutableReqeust];
[self.task resume];
}
- (void)stopLoading
{
[self.task cancel];
self.task = nil;
}
//and all other NSURLProtocolClient protocol method
#end
how to make this custom NSURLProtocol to support {upload, download}dataTask?
You have the right idea, but you need to implement some key URLSessionDelegate methods to pass the response and data up to the NSURLProtocol client:
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if( error ) {
[self.client URLProtocol:self didFailWithError:error];
} else {
[self.client URLProtocolDidFinishLoading:self];
}
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
completionHandler(NSURLSessionResponseAllow);
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
[self.client URLProtocol:self didLoadData:data];
}

IOS 7 how to print the url response data

I am trying to call a web service. I tried this
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.75.1:8082/projectname/public/tests"]];
NSURLSessionConfiguration *configuration = [ NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error) {
if(!error){
NSLog(#"no error");
}else{
NSLog(#"error");
}
}];
[task resume];
}
as you see there are two nslog statements. I got the no error one.
when I call that web service from my safari, I got a simple string which is index printed in the browser, how can I see that string in my xcode please?
Thanks
you can implement the delegate method
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
EDIT
Try This
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:YOUR URL]];
NSData *respData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"~~~~~ Status code: %d", [response statusCode]);
//Print your recived data here..
NSString *str = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
NSLog(#"str: %#", str);
You can use the delegate methods. When a NSURLSessionDownlaodTask is completed, it's delegates will be called if your class confirmed to it.
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
you can get your response by parsing the data in that delegate method. It will tell you the location that the URLSession stores the downloaded result.
what I would do If I were you is:
NOTE: it is based on you said you receive a simple string only from your back-end. if that is not a simple string, you may need to revise the –connectionDidFinishLoading: method's body.
.h
#interface UIRandomViewController : UIViewController {
NSURLConnection *_urlConnection;
NSMutableData *_receivedData;
// ...
}
// ...
#end
.m
#implementation UIRandomViewController {
// ...
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSURLRequest *_request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://192.168.75.1:8082/projectname/public/tests"]];
_urlConnection = [[NSURLConnection alloc] initWithRequest:_request delegate:self startImmediately:TRUE];
// ...
}
// ...
#pragma mark - <NSURLConnectionDelegate>
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_receivedData = nil;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (_receivedData == nil) _receivedData = [NSMutableData dataWithData:data];
else [_receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *_receivedString = [[NSString alloc] initWithData:_receivedData encoding:NSUTF8StringEncoding];
// hello beautiful...
NSLog(#"received data : %#", _receivedString);
}
}
#end

Resources